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

Promote WhileDo loops to typed trees. #5113

Merged
merged 2 commits into from
Sep 21, 2018

Conversation

sjrd
Copy link
Member

@sjrd sjrd commented Sep 15, 2018

They remain as such, without desugaring, until the back-end. We also use them to desugar DoWhile loops, instead of label-defs.

This is the second step towards getting rid of label-defs.

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! ❤️

Commit Messages

We want to keep history, but for that to actually be useful we have
some rules on how to format our commit messages (relevant xkcd).

Please stick to these guidelines for commit messages:

  1. Separate subject from body with a blank line
  2. When fixing an issue, start your commit message with Fix #<ISSUE-NBR>:
  3. Limit the subject line to 72 characters
  4. Capitalize the subject line
  5. Do not end the subject line with a period
  6. Use the imperative mood in the subject line ("Add" instead of "Added")
  7. Wrap the body at 80 characters
  8. Use the body to explain what and why vs. how

adapted from https://chris.beams.io/posts/git-commit

Have an awesome day! ☀️

@sjrd
Copy link
Member Author

sjrd commented Sep 15, 2018

Everything seems to work fine, except the source display. One command that reproduces the issue:

dotty-compiler-bootstrapped/testCompilation tests/run-with-compiler/quote-inline-function

Apparently the typer (or some other early phase) collapses nested Blocks, which means that my efforts in TreeOpsImpl.scala to recover the shapes of do..while loops are useless.

@nicolasstucki Any idea to fix this?

@nicolasstucki
Copy link
Contributor

There is some logic taking care of the collapsed Blocks in normalizedLoops, you probably shoul addapt it. Maybe you just need to change this line to case _: tpd.WhileDo => true.

@sjrd
Copy link
Member Author

sjrd commented Sep 16, 2018

Hum, I don't think that's going to help me. normalizeLoops can recover lost Blocks because it can infer their presence from the call to the label-def. But the blocks in the WhileDo that come from a desugared DoWhile leave no such trace. They're just normal Blocks.

I wonder if maybe the best thing to do would simply be to remove DoWhile from tasty-reflect. Don't pretend they exist, but rather always expose While loops instead. We would then formalize the desugaring of do { body } while (cond) into while ({ { body }; { cond } }) {} in the specification of the language/tasty-reflect.

// while ({ { body }; { cond } }) { () }
// the inner blocks are there to protect the scopes of body and cond from each other
val protectedBody = Block(Nil, body).withPos(body)
val protectedCond = Block(Nil, cond).withPos(cond)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the block for cond needed?

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 am not 100% sure. I can't really come up with a problematic source code, but at the same time I cannot convince myself that there are no problematic source code. It is definitely possible to come up with a problematic AST (with cond being a ValDef for example), but the source syntax doesn't allow that, AFAICT. IMO it's better to play it safe.

labelDefAndCall(nme.DO_WHILE_PREFIX, rhs, call)
// while ({ { body }; { cond } }) { () }
// the inner blocks are there to protect the scopes of body and cond from each other
val protectedBody = Block(Nil, body).withPos(body)
Copy link
Member

Choose a reason for hiding this comment

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

The withPos calls should be unnecessary: a tree position is the union of the positions of its children.

@nicolasstucki
Copy link
Contributor

We can remove the do while from tasty reflect. We would then simply do a best effort in the decompiler to recover do while.

@sjrd
Copy link
Member Author

sjrd commented Sep 17, 2018

We would then simply do a best effort in the decompiler to recover do while.

I'm already doing the best-effort to recover the DoWhile at the tasty-reflect level, and it's not enough even for the most basic do..while loops. The decompiler won't be able to do any better, I'm afraid.

The only approach I can see involves deep inspection: if the cond is a Block(stats, expr), then

  1. Shallow-inspect stats to find the symbols it defines (vals, defs, etc.)
  2. Deep-inspect expr to test whether it refers to any of those symbols.

If it doesn't, we can display a do { stats } while (expr).

@nicolasstucki
Copy link
Contributor

I was also considering the deep inspection. I would say we should just decompile it as a while loop. If someone actually cares about them in the future, at that moment we would implement the best effort decompilation of do while loops. Let's not waste time more time now and keep it simple.

@sjrd sjrd force-pushed the real-while-loops branch 2 times, most recently from c59877a to 26c7fdc Compare September 17, 2018 18:09
@sjrd
Copy link
Member Author

sjrd commented Sep 17, 2018

So, turns out I was wrong: Blocks are not flattened by typer, and my efforts to recover DoWhiles were fruitful. It just doesn't work when showing quoted trees. But for decompilation, it works like a charm.

@sjrd
Copy link
Member Author

sjrd commented Sep 17, 2018

It's green now :) Ready for a full review.

case Trees.Block((ddef: tpd.DefDef) :: Nil, expr) if expr.symbol.is(Flags.Label) && expr.symbol.name == nme.WHILE_PREFIX =>
val Trees.If(cond, Trees.Block(bodyStats, _), _) = ddef.rhs
Some((cond, loopBody(bodyStats)))
case x: tpd.WhileDo => Some((x.cond, x.body))
Copy link
Contributor

Choose a reason for hiding this comment

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

Before this case we should check that this is not a do while.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or maybe it would be better to remove the do while extractor and place that condition in the decompiler.

case Term.While(cond, body) =>
this += "while "
inParens(printTree(cond)) += " "
printTree(body)

case Term.DoWhile(body, cond) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

This should not be changed. The order should not matter.

@sjrd
Copy link
Member Author

sjrd commented Sep 18, 2018

@nicolasstucki I have added a commit that removes DoWhile from tasty-reflect, and enhances the decompiler instead, as suggested.

@nicolasstucki
Copy link
Contributor

Submodule reference can be updated now that lampepfl/scala#38 is merged.

@sjrd
Copy link
Member Author

sjrd commented Sep 21, 2018

Submodule reference can be updated now that lampepfl/scala#38 is merged.

Done.

@nicolasstucki
Copy link
Contributor

@sjrd you may need to rebase

@sjrd
Copy link
Member Author

sjrd commented Sep 21, 2018

Hum, I ... did rebase, without conflict, but apparently there was a semantic conflict. I'll fix it later today, or tomorrow.

They remain as such, without desugaring, until the back-end. We
also use them to desugar `DoWhile` loops, instead of label-defs:

  do { body } while (cond)

is now desugared as

  while { { body }; { cond } } ()

the inner blocks protecting the respective scopes of `body` and
`cond` from each other.

This is the second step towards getting rid of label-defs.
This avoids the awkward situation where `WhileDo` needs to be
tested before `While` for it to meaningful, or suffering some more
performance hit by rejecting `do-while`-like stuff in the `While`
deconstructor.

It will probably also simplify most usage patterns of the
tasty-reflect API, since users will only have to worry about one
kind of loop.

The decompiler takes it upon itself to deconstruct `While` loops
that look like the product of `do..while` to print a nice
decompilation output.
@sjrd
Copy link
Member Author

sjrd commented Sep 21, 2018

Back to green.

@nicolasstucki nicolasstucki merged commit a0b429b into scala:master Sep 21, 2018
@nicolasstucki nicolasstucki deleted the real-while-loops branch September 21, 2018 20:29
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.

5 participants