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.
// 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;
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()
+ }
+ }
})
}
}
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 {
});
}
+ 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>) {
});
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 });
}
// 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()
})
}
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()
//! 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::*;
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(),
- }
- }
-}