Skip to content

[MIR] Reintroduce the unit temporary #30945

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
Jan 20, 2016

Conversation

nagisa
Copy link
Member

@nagisa nagisa commented Jan 15, 2016

As an attempt to make loop body destination be optional, author implemented a pretty self contained
change and deemed it to be (much) uglier than the alternative of just keeping the unit temporary.
Having the temporary created lazily also has a nice property of not figuring in the MIR of
functions which do not use loops of any sort.

r? @nikomatsakis

// Even if we assign unconditionally, if the loop turns out to be diverging,
// the loop’s “return” value will not appear in the MIR (removed along with the
// exit_block by cfg simplification passes later).
this.cfg.push_assign_unit(exit_block, expr_span, destination);
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 have some doubts about suitability of this here. It relies on CFG passes removing the exit_block in order to not introduce ill-typed MIR (see e.g. #30876) in case of loops with diverging types. I’m not sure if having that ill-typedness for a little while in an unreachable block is fine. Any thoughts?

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 actually a little bit surprised by the behavior of the type checker here. It seems like loop { } has type unit -- but, if you never break out, we consider the loop non-terminating, and hence we allow it to be used in any context. This is probably not how I would want it to work in an ideal world, but it may not be worth trying to change anything here. :)

Therefore, to match the compiler as today, I suspect it should work like this:

  • if you break out of the loop, then we will push_assign_unit
  • if you do not, we will not push any assignment at all

Of course we don't know whether you break out of the loop in advance. We could either analyze this, or maybe come back after the fact and check.

Copy link
Member Author

Choose a reason for hiding this comment

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

The trivial way to check would be to simply check if desination type is () or not. Provided typechecking/inference run before lowering into MIR, like they do now.

Copy link
Contributor

Choose a reason for hiding this comment

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

It occurs to me we could also add this assignment as an "exiting the scope" action, much like drops. That is, we could record it in the LoopScope, and when we are translating a break, we can insert the store.

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 not sure if having that ill-typedness for a little while in an unreachable block is fine.

I never answered this directly -- I'd prefer to avoid it, since I plan to be running various type-checking steps on the MIR before we do optimizations and cleanup.

@nagisa
Copy link
Member Author

nagisa commented Jan 15, 2016

@nagisa nagisa force-pushed the mir-optional-block-dest branch from 6a704a1 to 04895a0 Compare January 19, 2016 17:29
@nagisa
Copy link
Member Author

nagisa commented Jan 19, 2016

@nikomatsakis I updated the PR with the approach of storing a flag inside LoopScope.

self.exit_scope(span, loop_scope.extent, block, exit_block);
let (exit_block, extent) = {
let loop_scope = self.find_loop_scope(span, label);
loop_scope.might_terminate = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is only true for break, not continue.

Copy link
Member Author

Choose a reason for hiding this comment

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

@nikomatsakis

Something like

'a: while 1 != 2 {
     loop { continue 'a }
}

is a valid code and essentially terminates the loop inside of which the expression resides?

Copy link
Contributor

Choose a reason for hiding this comment

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

@nagisa yes, you're right, it depends on where you continue to I suppose

Copy link
Contributor

Choose a reason for hiding this comment

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

@nagisa seems like we should toggle this flag in exit_scope, not here, and then return the result (as I wrote elsewhere)

@nikomatsakis
Copy link
Contributor

@nagisa left some nit-like comments. :)

@nagisa nagisa force-pushed the mir-optional-block-dest branch from 04895a0 to 132b263 Compare January 19, 2016 19:58

// We merge iterators of regular scopes and loop scopes based on their extents. This code
// relies on the assumption that every loop scope will have an extent of some non-popped
// scope.
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 hope this comment is a good enough explanation for the overly-smart code below.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is some awfully clever code, I hope it's correct :) (I get nervous about iterators being off by one)

I used to have each scope have an Option<LoopScope> embedded within, but I removed it at some point. Wonder if it'd be better that way. It was nice to be able to separate them, though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Another option would be to allow a kind of marker to be pushed into the scopes array, so that as we iterate backwards we'd encounter a "track_break" marker and set the flag to true.

@nagisa
Copy link
Member Author

nagisa commented Jan 19, 2016

@nikomatsakis nit-like comments addressed.


// start the loop
this.cfg.terminate(block, Terminator::Goto { target: loop_block });

this.in_loop_scope(loop_block, exit_block, |this| {
let may_term = unpack!(exit_block = this.in_loop_scope(loop_block,
exit_block,
Copy link
Contributor

Choose a reason for hiding this comment

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

sorry to keep adding nits, but it seems like exit_block never changes, it just flows around and around. Maybe we should just change the closure's return type to (), and then we can make in_loop_scope return bool and drop this awkward unpack. The code will also be clearer in that it's clear that exit_block never changes (and I can't see why you would want it to?)

@nagisa nagisa force-pushed the mir-optional-block-dest branch from 132b263 to 57b6ace Compare January 19, 2016 20:50
An attempt to make loop body destination be optional, author implemented a pretty self contained
change and deemed it to be (much) uglier than the alternative of just keeping the unit temporary.
Having the temporary created lazily also has a nice property of not figuring in the MIR of
functions which do not use loops of any sort.
@nagisa nagisa force-pushed the mir-optional-block-dest branch from 57b6ace to f9f6e3a Compare January 19, 2016 20:53
if let Some(cond_expr) = opt_cond_expr {
// This loop has a condition, ergo its exit_block is reachable.
this.find_loop_scope(expr_span, None).might_break = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

this seems fine; we could also check in the if below and say if may_break or opt_cond_expr.is_some()

Copy link
Member Author

Choose a reason for hiding this comment

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

To me it sounds like it is better to keep state consistent in case somebody else wants to use it later for whatever reasons.

@nikomatsakis
Copy link
Contributor

This version looks good. I wonder if it's worth adding some tests (basically, the various cases we discussed), just to cement that this is the expected result of the compiler. Such tests may of course exist, I don't know. :(

e.g.:

(also, I know that these tests wouldn't test mir, at least not until we port borrowck etc to mir.)

@nikomatsakis
Copy link
Contributor

well, r=me, I'll leave it to your discretion to consider whether adding tests seems to make sense, since I cannot decide :)

@nagisa
Copy link
Member Author

nagisa commented Jan 19, 2016

@bors r=nikomatsakis 28638ed

@bors
Copy link
Collaborator

bors commented Jan 20, 2016

⌛ Testing commit 28638ed with merge 6271092...

@bors
Copy link
Collaborator

bors commented Jan 20, 2016

💔 Test failed - auto-linux-32-opt

@alexcrichton
Copy link
Member

@bors: retry

On Tuesday, January 19, 2016, bors notifications@github.com wrote:

[image: 💔] Test failed - auto-linux-32-opt
http://buildbot.rust-lang.org/builders/auto-linux-32-opt/builds/7778


Reply to this email directly or view it on GitHub
#30945 (comment).

Mostly testing borrowck and typeck behaviour in presence of `loop` expression
@nagisa nagisa force-pushed the mir-optional-block-dest branch from 28638ed to 8877cca Compare January 20, 2016 11:28
@nagisa
Copy link
Member Author

nagisa commented Jan 20, 2016

@bors r=nikomatsakis 8877cca

@bors
Copy link
Collaborator

bors commented Jan 20, 2016

⌛ Testing commit 8877cca with merge d8c2360...

@bors
Copy link
Collaborator

bors commented Jan 20, 2016

💔 Test failed - auto-linux-64-nopt-t

@nagisa
Copy link
Member Author

nagisa commented Jan 20, 2016

@bors retry

bors added a commit that referenced this pull request Jan 20, 2016
As an attempt to make loop body destination be optional, author implemented a pretty self contained
change and deemed it to be (much) uglier than the alternative of just keeping the unit temporary.
Having the temporary created lazily also has a nice property of not figuring in the MIR of
functions which do not use loops of any sort.

r? @nikomatsakis
@bors
Copy link
Collaborator

bors commented Jan 20, 2016

⌛ Testing commit 8877cca with merge 4bb9d45...

@bors bors merged commit 8877cca into rust-lang:master Jan 20, 2016
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.

4 participants