]> git.lizzy.rs Git - rust.git/commitdiff
[MIR] Set dest ∀ expr with optional value
authorSimonas Kazlauskas <git@kazlauskas.me>
Wed, 30 Dec 2015 18:00:26 +0000 (20:00 +0200)
committerSimonas Kazlauskas <git@kazlauskas.me>
Thu, 7 Jan 2016 00:12:36 +0000 (02:12 +0200)
Assign a default unit value to the destinations of block expressions without trailing expression,
return expressions without return value (i.e. `return;`) and conditionals without else clause.

src/librustc_mir/build/block.rs
src/librustc_mir/build/cfg.rs
src/librustc_mir/build/expr/into.rs
src/librustc_mir/build/into.rs

index 5f7f87cb86234ecbe7eb73c5dda309b693d716ec..12b9130b48c612c9081914e2af66f6597a819951 100644 (file)
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use build::{BlockAnd, Builder};
+use build::{BlockAnd, BlockAndExtension, Builder};
 use hair::*;
 use rustc::mir::repr::*;
 use rustc_front::hir;
@@ -19,11 +19,16 @@ pub fn ast_block(&mut self,
                      mut block: BasicBlock,
                      ast_block: &'tcx hir::Block)
                      -> BlockAnd<()> {
-        let this = self;
-        let Block { extent, span: _, stmts, expr } = this.hir.mirror(ast_block);
-        this.in_scope(extent, block, |this| {
+        let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
+        self.in_scope(extent, block, move |this| {
             unpack!(block = this.stmts(block, stmts));
-            this.into(destination, block, expr)
+            match expr {
+                Some(expr) => this.into(destination, block, expr),
+                None => {
+                    this.cfg.push_assign_unit(block, span, destination);
+                    block.unit()
+                }
+            }
         })
     }
 }
index 2e70e6bb5ae0499e1a56eada967ae9258114459f..523ac85cdc5090414e3d93f812ec1669380d6fd3 100644 (file)
@@ -37,14 +37,6 @@ pub fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) {
         self.block_data_mut(block).statements.push(statement);
     }
 
-    pub fn push_assign_constant(&mut self,
-                                block: BasicBlock,
-                                span: Span,
-                                temp: &Lvalue<'tcx>,
-                                constant: Constant<'tcx>) {
-        self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
-    }
-
     pub fn push_drop(&mut self, block: BasicBlock, span: Span,
                      kind: DropKind, lvalue: &Lvalue<'tcx>) {
         self.push(block, Statement {
@@ -64,6 +56,23 @@ pub fn push_assign(&mut self,
         });
     }
 
+    pub fn push_assign_constant(&mut self,
+                                block: BasicBlock,
+                                span: Span,
+                                temp: &Lvalue<'tcx>,
+                                constant: Constant<'tcx>) {
+        self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
+    }
+
+    pub fn push_assign_unit(&mut self,
+                            block: BasicBlock,
+                            span: Span,
+                            lvalue: &Lvalue<'tcx>) {
+        self.push_assign(block, span, lvalue, Rvalue::Aggregate(
+            AggregateKind::Tuple, vec![]
+        ));
+    }
+
     pub fn terminate(&mut self,
                      block: BasicBlock,
                      terminator: Terminator<'tcx>) {
index 53ac4d854ed08917022a370fb7ee03ab5d3e172d..63eb760720479a750d90779fa96a21c135332975 100644 (file)
@@ -58,7 +58,14 @@ pub fn into_expr(&mut self,
                 });
 
                 unpack!(then_block = this.into(destination, then_block, then_expr));
-                unpack!(else_block = this.into(destination, else_block, else_expr));
+                else_block = if let Some(else_expr) = else_expr {
+                    unpack!(this.into(destination, else_block, else_expr))
+                } else {
+                    // Body of the `if` expression without an `else` clause must return `()`, thus
+                    // we implicitly generate a `else {}` if it is not specified.
+                    this.cfg.push_assign_unit(else_block, expr_span, &Lvalue::ReturnPointer);
+                    else_block
+                };
 
                 let join_block = this.cfg.start_new_block();
                 this.cfg.terminate(then_block, Terminator::Goto { target: join_block });
@@ -157,12 +164,18 @@ pub fn into_expr(&mut self,
                     }
 
                     // execute the body, branching back to the test
-                    // FIXME(#30636): this should not create or request any sort of destination at
-                    // all.
+                    // We write body’s “return value” into the destination of loop. This is fine,
+                    // because:
+                    //
+                    // * In Rust both loop expression and its body are required to have `()`
+                    //   as the “return value”;
+                    // * The destination will be considered uninitialised (given it was
+                    //   uninitialised before the loop) during the first iteration, thus
+                    //   disallowing its use inside the body. Alternatively, if it was already
+                    //   initialised, the `destination` can only possibly have a value of `()`,
+                    //   therefore, “mutating” the destination during iteration is fine.
                     let body_block_end = unpack!(this.into(destination, body_block, body));
                     this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
-
-                    // final point is exit_block
                     exit_block.unit()
                 })
             }
@@ -207,7 +220,13 @@ pub fn into_expr(&mut self,
                 this.break_or_continue(expr_span, label, block, |loop_scope| loop_scope.break_block)
             }
             ExprKind::Return { value } => {
-                unpack!(block = this.into(&Lvalue::ReturnPointer, block, value));
+                block = match value {
+                    Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
+                    None => {
+                        this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer);
+                        block
+                    }
+                };
                 let extent = this.extent_of_outermost_scope();
                 this.exit_scope(expr_span, extent, block, END_BLOCK);
                 this.cfg.start_new_block().unit()
index 66d6c49ef12aaf3b49b0eb5d49ae9a1152fb029b..77d9d926328fc5535bf26967ad79797c2d7a07b9 100644 (file)
@@ -14,7 +14,7 @@
 //! wrapped up as expressions (e.g. blocks). To make this ergonomic, we use this
 //! latter `EvalInto` trait.
 
-use build::{BlockAnd, BlockAndExtension, Builder};
+use build::{BlockAnd, Builder};
 use hair::*;
 use rustc::mir::repr::*;
 
@@ -58,16 +58,3 @@ fn eval_into<'a>(self,
         builder.into_expr(destination, block, self)
     }
 }
-
-impl<'tcx> EvalInto<'tcx> for Option<ExprRef<'tcx>> {
-    fn eval_into<'a>(self,
-                     builder: &mut Builder<'a, 'tcx>,
-                     destination: &Lvalue<'tcx>,
-                     block: BasicBlock)
-                     -> BlockAnd<()> {
-        match self {
-            Some(expr) => builder.into(destination, block, expr),
-            None => block.unit(),
-        }
-    }
-}