Skip to content
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

Add original tree attachment #6013

Merged
merged 2 commits into from
Sep 26, 2017
Merged

Conversation

jvican
Copy link
Member

@jvican jvican commented Jul 29, 2017

Adds an original tree attachment that allows external tools (compiler
plugins like scalameta & zinc) to keep track of the previous, unadapted
tree. The reason why this is required is explained in SD-340: scala/scala-dev#340.

This change enables the incremental compiler to detect changes in final vals: sbt/zinc#227.

It also enables a fix for scala/bug#10426 by allowing the scaladoc
compiler to let the compiler adapt constants without losing the tree that
will be shown in the Scaladoc UI.

To maintainers: I was thinking of the best way to test this, but
couldn't come up with an elegant one. Do you suggest a way I could write
a test for this? Is there a precedent in testing information carried in
the trees?

I think @lrytz is the right person to review this, since he suggested
this fix in the first place.

Fixes scala/bug#7173

Copy link
Member

@lrytz lrytz left a comment

Choose a reason for hiding this comment

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

I'd be interested to see how many such attachments are created, say when compiling the std lib. Could you do the experiment?

Also, should we eliminate the attachment at some phase? Maybe in erasure?

@@ -27,7 +27,7 @@ import scala.reflect.macros.whitebox
* @author Martin Odersky
* @version 1.0
*/
trait Typers extends Adaptations with Tags with TypersTracking with PatternTypers {
trait Typers extends Adaptations with Tags with TypersTracking with PatternTypers with StdAttachments {
Copy link
Member

Choose a reason for hiding this comment

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

I that necessary? The self type (Analyzer) includes StdAttachments.

case class OriginalAdaptedTreeAttachment(original: Tree)
def markOriginal(tree: Tree, original: Tree) = tree.updateAttachment(OriginalAdaptedTreeAttachment(original))
def unmarkOriginal(tree: Tree): Tree = tree.removeAttachment[OriginalAdaptedTreeAttachment]
def isAdaptedTree(tree: Tree): Boolean = tree.attachments.get[OriginalAdaptedTreeAttachment].isDefined
Copy link
Member

Choose a reason for hiding this comment

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

I personally prefer not to have those methods, they just make you go through an indirection when reading the code.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've done them to follow the convention. Honestly, I like it. I was hoping that there was a place in the compiler where the utils and definitions of attachments in the compiler was defined.

If we remove the attachment after erasure, we'll use both unmarkOriginal and isAdaptedTree, right? In any case, I'm happy to remove them if you think they should go away.

Copy link
Contributor

@adriaanm adriaanm Sep 20, 2017

Choose a reason for hiding this comment

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

I also prefer the more direct style (i.e., drop the additional methods). The method names are all pretty ambiguous. Also, why Original*Adapted* -- OriginalTreeAttachment captures the same info (the presence of the attachment implies the tree is no longer the original).

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll use the more direct style, rename methods/names and update this PR.

Copy link
Contributor

@adriaanm adriaanm Sep 20, 2017

Choose a reason for hiding this comment

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

Ok, as a reminder: please do not include the ticket in the commit title/body. It should be in the PR description. (Title should be short, referencing the issue in the commit message causes noise on every push.)

Copy link
Member

Choose a reason for hiding this comment

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

Our CONTRIBUTING.md says

At the end of the commit message, include "Fixes scala/bug#NNNN"

Should that be changed? I think it's good to have the reference in the commit message.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm fine with keeping it, I'll get over my OCD that hates seeing the stale commit references pile up until the git GC kicks in at github :-)

@lrytz
Copy link
Member

lrytz commented Aug 2, 2017

Related to #4880, which WIPs the idea of keeping all trees (with ConstantType) until erasure.

@jvican
Copy link
Member Author

jvican commented Aug 2, 2017

Related to #4880, which WIPs the idea of keeping all trees (with ConstantType) until erasure.

I'm aware of this effort, hope it gets to the finish line at some point. Until that gets merged, I think this is the perfect solution for Zinc -- I would like to support incremental changes in constants for 2.12.4 codebases.

@jvican
Copy link
Member Author

jvican commented Aug 2, 2017

I'd be interested to see how many such attachments are created, say when compiling the std lib. Could you do the experiment?

How should I do this? Can you point me to some docs or previous examples?

Also, should we eliminate the attachment at some phase? Maybe in erasure?

Yes, this is indeed a good idea.

@lrytz
Copy link
Member

lrytz commented Aug 2, 2017

How should I do this? Can you point me to some docs or previous examples?

For example just a HashSet in some top-level object, add to it in the constructor of OriginalAdaptedTreeAttachment, and print its content at the end of the run. Then build/quick/bin/scalac -d /tmp $(find src/library -name '*.scala')

Also, should we eliminate the attachment at some phase? Maybe in erasure?
Yes, this is indeed a good idea.

I'm not sure, I was just asking the question.

@jvican
Copy link
Member Author

jvican commented Aug 2, 2017

For example just a HashSet in some top-level object, add to it in the constructor of OriginalAdaptedTreeAttachment, and print its content at the end of the run. Then build/quick/bin/scalac -d /tmp $(find src/library -name '*.scala')

Thanks, will try that.

I'm not sure, I was just asking the question.

I think it's a good idea, neither Zinc, scaladoc or Scalameta traverse trees after typer, so it's a good tradeoff.

@adriaanm
Copy link
Contributor

Ping! 👋 For this to ship as part of 2.12.4, it will have to be mergeable by Sep 27.

Adds an original tree attachment that allows external tools (compiler
plugins like scalameta & zinc) to keep track of the previous, unadapted
tree. The reason why this is required is explained in SD-340: scala/scala-dev#340.

This change enables the incremental compiler to detect changes in `final
val`s: sbt/zinc#227.

It also enables a fix for scala/bug#10426 by allowing the scaladoc
compiler to let the compiler adapt literals without losing the tree that
will be shown in the Scaladoc UI.

To maintainers: I was thinking of the best way to test this, but
couldn't come up with an elegant one. Do you suggest a way I could write
a test for this? Is there a precedent in testing information carried in
the trees?

I think @lrytz is the right person to review this, since he suggested
this fix in the first place.

Fixes scala/bug#7173.
@jvican
Copy link
Member Author

jvican commented Sep 25, 2017

@lrytz Our little experiment gives us the following data about adapted trees:

Phases Number
typer 12718
patmat 1300
fields 479
specialize 469
total 15063

The total includes a few adapted trees that were done by other phases but were not important enough to add to the table.

@jvican
Copy link
Member Author

jvican commented Sep 25, 2017

I've added a second commit that cleans the original tree attachments in pre erasure so that we free up pointers to stale trees sooner in the pipeline. I think this hits the right trade-off, so thanks for the suggestion Lukas.

The PR is now updated and ready for another review.

@jvican
Copy link
Member Author

jvican commented Sep 25, 2017

/rebuild

@adriaanm
Copy link
Contributor

Seems like a real (though strange) error.

case tpe => specialScalaErasure(tpe)
case Literal(ct) =>
val cleanLiteral = {
if (tree.attachments.get[OriginalTreeAttachment].isEmpty) tree
Copy link
Contributor

Choose a reason for hiding this comment

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

Use contains instead?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or better yet, just use remove directly. If this pattern of checking whether the attachment is there before removing is faster, we should implement it once in remove (one method call that requires a class tag is also better than two)

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in last commit!

@adriaanm adriaanm changed the title Fixes scala/bug#7173: Add original tree attachment Add original tree attachment Sep 25, 2017
The original tree attachment is useful for compiler plugins and macros.
However, this commit constraints compiler plugins to run before erasure
if they want to inspect the original trees so that we free up memory as
soon as possible.

This commit removes the attachment in pre erasure, taking advantage that
we modified literals before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants