-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #4947: Do not replace positions of inlined arguments #4949
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
pos = last | ||
res | ||
case tree => | ||
if (pos.exists) super.transform(tree).withPos(pos) |
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.
@nicolasstucki says !pos.exists
is only possible if inlined.call
is emptyTree
, which means that this is not a call to an inline method but an argument to it — and then bindings
should be empty.
We should update docs for Inlined
about this.
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 does not look like the right algorithm. We have enclosingInlineds, which is interpreted as follows:
- An Inlined node with a non-empty call is a plain inlined node
- An inlined node with an empty tree as call in an inlined argument which cancels the enclosing inlined call.
So one should start with the outermost InlinedCall and update all positions in its expansion to the call position, except in those regions where there are as many empty as non-empty calls in enclosingInlineds
.
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 sounds like the algorithm I implemented though I use the pos state to know if it has been cancelled or not. I will change it to use enclosingInlineds
directly.
Please add documentation, I have no idea what this does from looking at it. |
pos = last | ||
res | ||
case tree => | ||
if (pos.exists) super.transform(tree).withPos(pos) |
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 does not look like the right algorithm. We have enclosingInlineds, which is interpreted as follows:
- An Inlined node with a non-empty call is a plain inlined node
- An inlined node with an empty tree as call in an inlined argument which cancels the enclosing inlined call.
So one should start with the outermost InlinedCall and update all positions in its expansion to the call position, except in those regions where there are as many empty as non-empty calls in enclosingInlineds
.
I think this also needs more extensive tests covering various nesting levels of inlined/non-inlined across one or more files and probably more low-level tests too (like https://github.com/scala/scala/blob/2.13.x/test/junit/scala/tools/nsc/backend/jvm/BytecodeTest.scala#L32-L77, https://github.com/scala/scala/blob/2.13.x/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala#L1515, etc) |
2b4d85b
to
21f7aa3
Compare
Only reposition positions of inlined trees when they come from another file.
9b96b78
to
a11db37
Compare
@Blaisorblade I when a bit than #4947 and kept the positions of trees inlined in the same file. Those already have the correct position and can be used in the debugger or stack traces. |
Sounds like what I hoped for!* Line numbers in debugging now work like a charm (minus, of course, setting breakpoints inside *EDIT: oh, I see that you preserve line numbers even for |
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.
Cool, but I'm taking a look and changing a couple things which can be improved, and will reassign you for re-review.
@@ -0,0 +1,4 @@ | |||
track: Test$.main(i4947.scala:4) |
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.
These testcases are good and go beyond #4947.
@Test def i4947 = { | ||
val source = """class Foo { | ||
| transparent def track[T](f: => T): T = { | ||
| println("tracking") // line 3 |
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.
What about we call a new virtual method m1(String)
in Foo
, rather than println
, a bit like the existing test? This test will break if any code figures that Predef$.MODULE$
needn't be reloaded.
Do these tests ever break? (Guess: yes). I don't know and I'm curious about the existing guidelines, but people maintaining them might like info on what bits matter and which don't (beyond what's already in this PR).
Nicolas mentioned he already tried this but the resulting test was more complex (which... well, ALOAD 0 seems pretty stable to me), and it's easy to fix the test by pasting the output.
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.
Changed
val transformed = super.transform(tree) | ||
enclosingInlineds match { | ||
case call :: _ if call.symbol.topLevelClass != ctx.owner.topLevelClass => | ||
// reposition tree inlined from some other file |
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 comment makes sense, but the code is stricter, and resets the position even if the toplevel class is different but the source file is the same. In that case, it appears we don't need to reset the position. Fixing.
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.
Also, this is an expensive (O(n)) way to test if enclosingInlineds.nonEmpty
and get the head, and that could be expensive, but that's really enclosingInlineds
's fault and I think can be fixed separately without affecting this code.
val transformed = super.transform(tree) | ||
enclosingInlineds match { | ||
case call :: _ if call.symbol.sourceFile != ctx.owner.sourceFile => | ||
// reposition tree inlined from some other file |
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 the documentation is too complicated. We're dropping Inlined nodes, and repositioning nodes from other files to the call position because we can't express their position otherwise in their debug info, pending JSR-45. The stuff about "empty call" is confusing and expressed in terms of the implementation.
Other issues that I think are there and that I'm trying to address:
-
The comment makes sense, but the code is stricter, and resets the position even if the toplevel class is different but the source file is the same. In that case, it appears we don't need to reset the position. Addressed by one commit I pushed.
-
Also, this is an expensive (O(n)) way to test if enclosingInlineds.nonEmpty and get the head, and that could be expensive, but that's really enclosingInlineds's fault and I think can be fixed separately without affecting this code.
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.
So #4990 addresses performance.
@nicolasstucki The current algorithm is fishy; if I keep the full stack as described in #4990 (comment) (not the PR but the comment), the new tests break. What we need is to compare the current source file I'm not sure Martin's suggested algorithm is correct either, or if so I don't get why:
It seems to keep unchanged the position of parameters (as many empty as non-empty calls), but that's wrong for parameters from another file (also featuring in the latest testcases). EDIT: BTW, tests keep working with #4990 in. EDIT2: I conjecture this algorithm works only assuming that |
All the requested changes where made
@Blaisorblade I'll let you merge if all is in order for you. |
@nicolasstucki I think I figured what's going on and why the code is correct. I'll update the comments and merge everything. |
I'm merging the PRs as-is and pushing comment clarifications to followup PRs. |
No description provided.