-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Fix scala/bug#10490: Allow Java nested class selection #6053
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
| qual match { | ||
| case id: Ident if context.unit.isJava => | ||
| val qualTyped1 = context.withinJavaTypeSelection(typeQual) | ||
| (qualTyped1, qualTyped1.isType) |
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's really interesting that this is all we need to do in typedSelectOrSuperCall, since the whole method will return Select(qualTyped1, name) whereas the more correct tree would have been SelectFromTypeTree(qualTyped1, name). I have not done this change because of simplicity. But it does make me wonder what is the crucial difference between Select and SelectFromTypeTree... It seems that Select(Ident(TypeName("Foo")), _) == SelectFromTypeTree(Ident(TypeName("Foo")), _), no?
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.
I think for consistency it would be better to generate the SelectFromTypeTree, even if a Select doesn't violate any of the current assumptions in the rest of the compiler.
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.
I can do that, but I'm afraid to modify too much of the logic of this method since it's really hot. I also think that for consistency it's better to generate a SelectFromTypeTree. Doing that requires me to wrap almost all the rest of the body that follows this statement with an if.
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 should also get a comment that explains why context.unit.isJava here needs to be reified as a context.withinJavaTypeSelection for consumption down the line in typedIdent.
| qual match { | ||
| case id: Ident if context.unit.isJava => | ||
| val qualTyped1 = context.withinJavaTypeSelection(typeQual) | ||
| (qualTyped1, qualTyped1.isType) |
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.
I think for consistency it would be better to generate the SelectFromTypeTree, even if a Select doesn't violate any of the current assumptions in the rest of the compiler.
| if (name.isTermName) (typeQual, false) | ||
| else { | ||
| qual match { | ||
| case id: Ident if context.unit.isJava => |
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.
A comment would be good here, e.g.
scala/bug#10490: given a Scala class A { class B }, in mixed compilation, a reference A.B in a Java source
would cause a "not found: value A". The `JavaTypeSelection` mode makes `typedIdent` type check A as
a type if no value is found. In case A has a companion, it works thanks to the fix of scala/bug#3120 in
typedSelectInternal.
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.
I'll add it 👍
|
It would be worth looking at #5762, which overhauls typechecking of selections to remove inconsistencies. I wasn't brave enough to merge that into 2.12.3, but will likely be merged soon. |
Isn't there already logic in the typer to do this in scala/src/compiler/scala/tools/nsc/typechecker/Typers.scala Lines 4919 to 4926 in e1e8d05
|
|
I saw that logic, I don't know why it was added but it doesn't seem to work; this path in I would also argue that aside from working, my fix is more elegant and correct. The real fix should happen in |
|
@retronym I've read the code in that PR. It doesn't seem to interact with the bug that I fix here, let me know if I'm wrong. I'm happy to rebase this when that gets merged. |
|
The existing logic in For each package-level Java class, the Scala compiler creates a class and a module symbol, static members are added to the module symbol. When the Scala compiler type-checks The fix in this PR is when selecting So there must be some other code path than https://github.com/lampepfl/dotty/blob/851c3c2212b7457dedc9ad1e59ff688b17458db5/compiler/src/dotty/tools/dotc/typer/Typer.scala#L418-L421 in dotty that makes the example work. |
|
I don't see why not. I'm making my way through the PR queue. Will look at this one next (in the next few hours). |
|
I assume you meant whether it can be merged once the review feedback has been addressed? I +1 the more comments as requested by Lukas -- this is tricky stuff and there's some good knowledge around this in this PR that should end up in the code. I'll look at the actual code after lunch, but I would also second the other request in directly emitting the right tree (SelectFromType), if we can find an elegant way to do it. |
|
When you rebase, please also drop "Fix scala/bug#10490: " from the commit title. That should go into the PR description. Thanks for the great commit message. |
|
I've pushed a tree with a sketch that rebases your work on my earlier PR and also boyscouts a bit in the area. Could you verify that it makes sense and absorb this into your PR? Now that I've looked at the code, I feel this area has hit a critical point where we need to do a bit of reinforcement before we tack on more stuff. |
Indeed.
Deal.
Excellent. I'm absorbing these commits into this PR. |
|
This PR has been updated according to feedback and is ready for another pass. I haven't scheduled benchmarks because I'm not sure this PR requires them! |
|
/rebuild |
|
Weird -- I cannot reproduce the error locally. I'm trying tomorrow again. |
|
I'll take a look as well |
|
I think we should just update the check file. It doesn't make sense to me to expand to |
Java type selections, for example `Foo.Bar`, can represent two different
trees depending on the context. On the one hand, it can be a type
selection over an object `Foo`. On the other hand, it can be a type
selection over a type (that is, a selection of a nested class).
However, when we parse the Scala signatures is not possible to know
which case the user meant. For example, `Foo.Bar` could be either:
1. `Select(Ident(TermName("Foo")), TypeName("Bar"))`; or
2. `SelectFromTypeTree(Ident(TypeName("Foo")), TypeName("Bar"))`.
By default, the Scala compiler parses `Foo.Bar` in a type position as
the first option, but it could very well be the case that the user meant
the second one, as specified by the tests in this commit.
To address the issue, then, there's nothing but the ugly solution of
injecting special logic into typer so that we try to typecheck both
trees for those different scenarios.
To handle this specific logic and try to make it as isolated as
possible, I've introduced a new context mode called `JavaTypeSelection`.
This context mode is set by `typedSelectOrSuperCall` when the
compilation unit is Java, the tree at hands select a type name and the
qualifier is an identifier. `typedIdent` detects when this mode is on,
and if the default term symbol lookup fails it tries to find the type
symbol. If that succeeds, it then delegates to `typedType`. The
different returning trees are handled in `typedSelectOrSuperCall`.
Run tests in this commit illustrate both accesses. They are independent
because if t10490 had the `Foo` companion, the bug that this commit
fixes wouldn't have been required (i.e. it's a very subtle workaround).
Fixes scala/bug#10490.
|
@adriaanm Makes sense. PR has been updated with the updated check file, agreed that the new error message is better than exposing the last try with the dynamic call typechecking. |
|
I'll add it.
Sorry, I missed this one.
It's been added, but it's shorter: |
yes please -- let's stick to the convention (although the original design decision is not immediately clear to me) |
| def typedTypeSelectionQualifier(tree: Tree, pt: Type = AnyRefTpe) = { | ||
| def typedQual = context.withImplicitsDisabled { typed(tree, MonoQualifierModes | mode.onlyTypePat, pt) } | ||
| tree match { | ||
| case id: Ident if context.unit.isJava => context.withinJavaTypeSelection { typedQual } |
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.
Talking this through with @retronym, it occurred to us that this withinJavaTypeSelection context bit would also be useful in @hrhino's quest to improve error messages in #6042. With that insight, should it just be withinTypeSelection (and should we have one for withinPattern?)
Also, why is this is only done when we reach an Ident? What are the varying combos of A, A.B and A.B.C.
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.
why is this is only done when we reach an Ident
For selections there's an existing fix / workaround, see my comment here #6053 (comment).
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.
Would it be feasible to combine the two fixes?
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.
Yes, I think so.
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.
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.
combining sounds good to me
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.
Alright, I'll cherry-pick @hrhino's changes into this PR 👍
|
@jvican interested in trying to finish this up in time for 2.12.5...? I've changed the milestone to 2.12.6, but only tentatively. |
|
@SethTisue Let's leave it for 2.12.6, since I believe that integrating Harrison's changes will take me a little bit of time and there are things with higher priority on my scala/scala queue 😄. |
|
At this point I think we should move this work to 2.13.0-M5. I would also encourage you to integrate the other efforts in this area. |
|
Can i reopen this PR targeting 2.13.0-M5? |
|
sure, you can retarget and reopen |
|
I'm taking a look at reviving this in 2.13.x...retronym:topic/java-inherited-type-ident |
(cherry picked from commit 81fff93)
- Inherited type declarations are in scope in Java code - For static innner classes, we need to check in the companion module of each base classes. - Incorporate and accomodate test case from #6053 - Tests to java code referring to module-class owned classes via companion class prefix
- Inherited type declarations are in scope in Java code - For static innner classes, we need to check in the companion module of each base classes. - Incorporate and accomodate test case from scala#6053 - Tests to java code referring to module-class owned classes via companion class prefix Backport of scala#7671
- Inherited type declarations are in scope in Java code - For static innner classes, we need to check in the companion module of each base classes. - Incorporate and accomodate test case from scala#6053 - Tests to java code referring to module-class owned classes via companion class prefix Backport of scala#7671
Java type selections, for example
Foo.Bar, can represent two differenttrees depending on the context. On the one hand, it can be a type
selection over an object
Foo. On the other hand, it can be a typeselection over a type (that is, a selection of a nested class).
However, when we parse the Scala signatures is not possible to know
which case the user meant. For example,
Foo.Barcould be either:Select(Ident(TermName("Foo")), TypeName("Bar")); orSelectFromTypeTree(Ident(TypeName("Foo")), TypeName("Bar")).By default, the Scala compiler parses
Foo.Barin a type position asthe first option, but it could very well be the case that the user meant
the second one, as specified by the tests in this commit.
To address the issue, then, there's nothing but the ugly solution of
injecting special logic into typer so that we try to typecheck both
trees for those different scenarios.
To handle this specific logic and try to make it as isolated as
possible, I've introduced a new context mode called
JavaTypeSelection.This context mode is set by
typedSelectOrSuperCallwhen thecompilation unit is Java, the tree at hands selects a type name and the
qualifier is an identifier.
typedIdentdetects when this mode is on,and if the default term symbol lookup fails it tries to find the type
symbol. If that succeeds, it then delegates to
typedType. Thedifferent returning trees are handled in
typedSelectOrSuperCall.Run tests in this commit illustrate both accesses. They are independent
because if t10490 had the
Foocompanion, the bug that this commitfixes wouldn't have been required (i.e. it's a very subtle workaround).