-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Bullet-proofing companion objects #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Companion class/module computations now also work for local classes and modules. For this to work, either one of two conditions must be met: (1) some enclosing context refers to a scope that contains the companions. (2) the context's compilation unit has a typed tree that contains the companions. (1) is usually true when type-checking, (2) when transforming trees. Local companions are searched as follows: If (2) holds, we locate the statement sequence containing the companions by searching down from the root stored in the compilation unit. Otherwise, we search outwards in the enclosing contexts for a scope containing the companions.
Review by @DarkDimius @retronym |
Dotty currently cannot parse @unchecked annotations in pattern types. That's why the previous commit failed. We need to come back to this and fix it. For the moment, to make on the current branch, the annotation is removed.
case tree: Tree => // Dotty problem: cannot write Tree @ unchecked, this currently gives a syntax error | ||
if (definedSym(tree) == sym) tree :: Nil | ||
else if (tree.envelope.contains(sym.pos)) { | ||
val p = search(tree.productIterator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks a bit dicey to me. Could this search be expressed with a regular TreeAccumulator
, customized to prune based on positions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an interesting idea. But it would affect more code than just this one. I.e. compare with Positioned#contains, which is used in Inferencing to determine whether a type variable may be instantiated. So I prefer delegating a change like this to a separate commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The duplication with Positioned#contains
makes it all the more worthwhile to either factor out this productIterator
based approach or replace it with a TreeAccumulator
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a stab at the refactoring: #76
What about a
class C[@specialized(Int) X] {
def foo(x: X) = {
class C {
private def y: Any = C.x
}
object C {
private def x: Any = new C().y
}
(y: Int) => x
}
}
|
LGTM |
Bullet-proofing companion objects
@retronym why would they, AFAIK specialization creates new symbols with custom suffix, and they enter scope of their owner, amirite? |
Compiling with -optimise eliminates the closure, we rely on that in scalac. |
* if no such path exists. | ||
* Pre: `sym` must have a position. | ||
*/ | ||
def defPath(sym: Symbol, root: Tree)(implicit ctx: Context): List[Tree] = ctx.debugTraceIndented(s"defpath($sym with position ${sym.pos}, ${root.show})") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that this code is never executed by current tests. I guess that's expected as we don't have transformer phases calling companionModule
right now, but it would be good to mention that sort of thing explicitly in the pull request.
@DarkDimius The part of specialization I was referring to is I can't actually think of any places in |
FYI: I'm implementing |
Okay. So long as the symbols for the class/module exist in the tree that was fed into the phase, this approach ought to work. But if you ended up with new symbols for some reason, I think it could be brittle. |
Oh, I guess you might have to will the companion module into existence during that phase, if one doesn't already exist. So you might get into interesting territory. |
Come to think of it, you might be better adding the offsets as static members of the class, rather than to the companion. See the reflection cache added for structural types as an example. |
Companion class/module computations now also work for local classes and modules. For this to work,
either one of two conditions must be met: (1) some enclosing context refers to a scope that
contains the companions. (2) the context's compilation unit has a typed tree that contains the
companions. (1) is usually true when type-checking, (2) when transforming trees.
Local companions are searched as follows: If (2) holds, we locate the statement sequence containing
the companions by searching down from the root stored in the compilation unit. Otherwise, we search
outwards in the enclosing contexts for a scope containing the
companions.