Skip to content

Fix #8748: Verify symbol is a param by querying the reftree #8805

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

Merged
merged 2 commits into from
Apr 27, 2020

Conversation

DieBauer
Copy link
Contributor

@DieBauer DieBauer commented Apr 26, 2020

Verify symbol is a param by querying the reftree

In the HoistSuperArgs phase we have established which tree should be
hoisted, and replace all references in the Tree with params that are
accessible in the hoisted tree.

When the Tree contains an anonymous class without parameters,
the paramSyms of that class refer to the created 'fresh' super symbol
(e.g. B$superArg$1), while the RefTree symbol belongs to the superclass B.
This meant that the conditional was never satisfied, and the type was
never replaced by the primary constructor argument of the generated
superArg class.
This is mitigated by replacing the check, to only take params or
accessors into account in the scope of the hoisted tree.

Closes #8748 and closes #8786

I've run dotty-compiler/test and testCompilation and all is green on my machine.

/cc @liufengyun

Copy link
Member

@dottybot dottybot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello, and thank you for opening this PR! 🎉

All contributors have signed the CLA, thank you! ❤️

Have an awesome day! ☀️

@DieBauer DieBauer changed the title Fix/issue 8748 Fix #8748: Verify symbol is a param by querying the reftree Apr 26, 2020
Copy link
Contributor

@liufengyun liufengyun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good diagnosis 👍

In the commit message, we can add that treeMap is called before typeMap on a given tree, thus previously the code in treeMap is never executed.

@@ -140,7 +140,7 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
}
},
treeMap = {
case tree: RefTree if paramSyms.contains(tree.symbol) =>
case tree: RefTree if tree.symbol.isParamOrAccessor =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to strengthen the condition a little:

-                case tree: RefTree if paramSyms.contains(tree.symbol) =>
+                case tree: RefTree if needsRewire(tree.tpe.asInstanceOf[NamedType]) =>

where needsRewire is the condition in the typeMap:

            def needsRewire(tp: NamedType) =
                (tp.symbol.owner == cls || tp.symbol.owner == constr) && tp.symbol.isParamOrAccessor

Copy link
Contributor Author

@DieBauer DieBauer Apr 27, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally agree, although all tests pass it is better to make it more strict. Thanks for the suggestion.

One question, why should we cast this to NamedType instead of pattern match like:

def needsRewire(t: Type): Boolean = t match {
        case tp: NamedType => (tp.symbol.owner == cls || tp.symbol.owner == constr) && tp.symbol.isParamOrAccessor
        case _             => false
      }

Can the RefTree type (Select, Ident) never be anything else than a TermRef or TypeRef? if that's the case then we can 'safely' cast this. And safe a pattern match :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's better to do a pattern match (the cast should be safe, though). And we can remove duplicate code in typeMap to reuse needsRewire.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I have applied your suggestion, and removed the duplicated code in the typeMap.

@DieBauer DieBauer force-pushed the fix/issue-8748 branch 2 times, most recently from 50104b1 to 1cc568f Compare April 27, 2020 12:29
In the HoistSuperArgs phase we have established which tree should be
hoisted, and replace all references in the Tree with params that are
accessible in the hoisted tree.

When the Tree contains an anonymous class without parameters,
the paramSyms of that class refer to the created 'fresh' super symbol
(e.g. `B$superArg$1`), while the RefTree symbol belongs to the
superclass `B`.
This meant that the conditional was never satisfied, and the type was
never replaced by the primary constructor argument of the generated
superArg class.

In the current design `treeMap` is called before `typeMap` on a given
tree, thus the code in `treeMap` was never executed.

This is mitigated by rewriting the check, to only take params or
accessors into account in the scope of the hoisted tree.
@odersky
Copy link
Contributor

odersky commented Apr 27, 2020

Nice! Thanks for the fix.

@odersky odersky merged commit 66f335c into scala:master Apr 27, 2020
@DieBauer DieBauer deleted the fix/issue-8748 branch April 28, 2020 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Compiler hangs forever in Erasure phase assertion failed: asTerm called on not-a-Term val <none>
4 participants