@@ -146,11 +146,13 @@ impl<'a,'tcx> Builder<'a,'tcx> {
146146 // start the loop
147147 this. cfg . terminate ( block, Terminator :: Goto { target : loop_block } ) ;
148148
149- this. in_loop_scope ( loop_block, exit_block, |this| {
149+ let might_break = this. in_loop_scope ( loop_block, exit_block, move |this| {
150150 // conduct the test, if necessary
151151 let body_block;
152- let opt_cond_expr = opt_cond_expr; // FIXME rustc bug
153152 if let Some ( cond_expr) = opt_cond_expr {
153+ // This loop has a condition, ergo its exit_block is reachable.
154+ this. find_loop_scope ( expr_span, None ) . might_break = true ;
155+
154156 let loop_block_end;
155157 let cond = unpack ! ( loop_block_end = this. as_operand( loop_block, cond_expr) ) ;
156158 body_block = this. cfg . start_new_block ( ) ;
@@ -163,21 +165,22 @@ impl<'a,'tcx> Builder<'a,'tcx> {
163165 body_block = loop_block;
164166 }
165167
166- // execute the body, branching back to the test
167- // We write body’s “return value” into the destination of loop. This is fine,
168- // because:
169- //
170- // * In Rust both loop expression and its body are required to have `()`
171- // as the “return value”;
172- // * The destination will be considered uninitialised (given it was
173- // uninitialised before the loop) during the first iteration, thus
174- // disallowing its use inside the body. Alternatively, if it was already
175- // initialised, the `destination` can only possibly have a value of `()`,
176- // therefore, “mutating” the destination during iteration is fine.
177- let body_block_end = unpack ! ( this. into( destination, body_block, body) ) ;
168+ // The “return” value of the loop body must always be an unit, but we cannot
169+ // reuse that as a “return” value of the whole loop expressions, because some
170+ // loops are diverging (e.g. `loop {}`). Thus, we introduce a unit temporary as
171+ // the destination for the loop body and assign the loop’s own “return” value
172+ // immediately after the iteration is finished.
173+ let tmp = this. get_unit_temp ( ) ;
174+ // Execute the body, branching back to the test.
175+ let body_block_end = unpack ! ( this. into( & tmp, body_block, body) ) ;
178176 this. cfg . terminate ( body_block_end, Terminator :: Goto { target : loop_block } ) ;
179- exit_block. unit ( )
180- } )
177+ } ) ;
178+ // If the loop may reach its exit_block, we assign an empty tuple to the
179+ // destination to keep the MIR well-formed.
180+ if might_break {
181+ this. cfg . push_assign_unit ( exit_block, expr_span, destination) ;
182+ }
183+ exit_block. unit ( )
181184 }
182185 ExprKind :: Assign { lhs, rhs } => {
183186 // Note: we evaluate assignments right-to-left. This
@@ -217,7 +220,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
217220 |loop_scope| loop_scope. continue_block )
218221 }
219222 ExprKind :: Break { label } => {
220- this. break_or_continue ( expr_span, label, block, |loop_scope| loop_scope. break_block )
223+ this. break_or_continue ( expr_span, label, block, |loop_scope| {
224+ loop_scope. might_break = true ;
225+ loop_scope. break_block
226+ } )
221227 }
222228 ExprKind :: Return { value } => {
223229 block = match value {
@@ -303,11 +309,13 @@ impl<'a,'tcx> Builder<'a,'tcx> {
303309 block : BasicBlock ,
304310 exit_selector : F )
305311 -> BlockAnd < ( ) >
306- where F : FnOnce ( & LoopScope ) -> BasicBlock
312+ where F : FnOnce ( & mut LoopScope ) -> BasicBlock
307313 {
308- let loop_scope = self . find_loop_scope ( span, label) ;
309- let exit_block = exit_selector ( & loop_scope) ;
310- self . exit_scope ( span, loop_scope. extent , block, exit_block) ;
314+ let ( exit_block, extent) = {
315+ let loop_scope = self . find_loop_scope ( span, label) ;
316+ ( exit_selector ( loop_scope) , loop_scope. extent )
317+ } ;
318+ self . exit_scope ( span, extent, block, exit_block) ;
311319 self . cfg . start_new_block ( ) . unit ( )
312320 }
313321}
0 commit comments