Skip to content

Commit 3692ab6

Browse files
committed
[MIR] Set dest ∀ expr with optional value
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.
1 parent 1509637 commit 3692ab6

File tree

4 files changed

+53
-33
lines changed

4 files changed

+53
-33
lines changed

src/librustc_mir/build/block.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use build::{BlockAnd, Builder};
11+
use build::{BlockAnd, BlockAndExtension, Builder};
1212
use hair::*;
1313
use rustc::mir::repr::*;
1414
use rustc_front::hir;
@@ -19,11 +19,16 @@ impl<'a,'tcx> Builder<'a,'tcx> {
1919
mut block: BasicBlock,
2020
ast_block: &'tcx hir::Block)
2121
-> BlockAnd<()> {
22-
let this = self;
23-
let Block { extent, span: _, stmts, expr } = this.hir.mirror(ast_block);
24-
this.in_scope(extent, block, |this| {
22+
let Block { extent, span, stmts, expr } = self.hir.mirror(ast_block);
23+
self.in_scope(extent, block, move |this| {
2524
unpack!(block = this.stmts(block, stmts));
26-
this.into(destination, block, expr)
25+
match expr {
26+
Some(expr) => this.into(destination, block, expr),
27+
None => {
28+
this.cfg.push_assign_unit(block, span, destination);
29+
block.unit()
30+
}
31+
}
2732
})
2833
}
2934
}

src/librustc_mir/build/cfg.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,6 @@ impl<'tcx> CFG<'tcx> {
3737
self.block_data_mut(block).statements.push(statement);
3838
}
3939

40-
pub fn push_assign_constant(&mut self,
41-
block: BasicBlock,
42-
span: Span,
43-
temp: &Lvalue<'tcx>,
44-
constant: Constant<'tcx>) {
45-
self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
46-
}
47-
4840
pub fn push_drop(&mut self, block: BasicBlock, span: Span,
4941
kind: DropKind, lvalue: &Lvalue<'tcx>) {
5042
self.push(block, Statement {
@@ -64,6 +56,23 @@ impl<'tcx> CFG<'tcx> {
6456
});
6557
}
6658

59+
pub fn push_assign_constant(&mut self,
60+
block: BasicBlock,
61+
span: Span,
62+
temp: &Lvalue<'tcx>,
63+
constant: Constant<'tcx>) {
64+
self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
65+
}
66+
67+
pub fn push_assign_unit(&mut self,
68+
block: BasicBlock,
69+
span: Span,
70+
lvalue: &Lvalue<'tcx>) {
71+
self.push_assign(block, span, lvalue, Rvalue::Aggregate(
72+
AggregateKind::Tuple, vec![]
73+
));
74+
}
75+
6776
pub fn terminate(&mut self,
6877
block: BasicBlock,
6978
terminator: Terminator<'tcx>) {

src/librustc_mir/build/expr/into.rs

+25-6
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,14 @@ impl<'a,'tcx> Builder<'a,'tcx> {
5858
});
5959

6060
unpack!(then_block = this.into(destination, then_block, then_expr));
61-
unpack!(else_block = this.into(destination, else_block, else_expr));
61+
else_block = if let Some(else_expr) = else_expr {
62+
unpack!(this.into(destination, else_block, else_expr))
63+
} else {
64+
// Body of the `if` expression without an `else` clause must return `()`, thus
65+
// we implicitly generate a `else {}` if it is not specified.
66+
this.cfg.push_assign_unit(else_block, expr_span, &Lvalue::ReturnPointer);
67+
else_block
68+
};
6269

6370
let join_block = this.cfg.start_new_block();
6471
this.cfg.terminate(then_block, Terminator::Goto { target: join_block });
@@ -157,12 +164,18 @@ impl<'a,'tcx> Builder<'a,'tcx> {
157164
}
158165

159166
// execute the body, branching back to the test
160-
// FIXME(#30636): this should not create or request any sort of destination at
161-
// all.
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.
162177
let body_block_end = unpack!(this.into(destination, body_block, body));
163178
this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
164-
165-
// final point is exit_block
166179
exit_block.unit()
167180
})
168181
}
@@ -207,7 +220,13 @@ impl<'a,'tcx> Builder<'a,'tcx> {
207220
this.break_or_continue(expr_span, label, block, |loop_scope| loop_scope.break_block)
208221
}
209222
ExprKind::Return { value } => {
210-
unpack!(block = this.into(&Lvalue::ReturnPointer, block, value));
223+
block = match value {
224+
Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
225+
None => {
226+
this.cfg.push_assign_unit(block, expr_span, &Lvalue::ReturnPointer);
227+
block
228+
}
229+
};
211230
let extent = this.extent_of_outermost_scope();
212231
this.exit_scope(expr_span, extent, block, END_BLOCK);
213232
this.cfg.start_new_block().unit()

src/librustc_mir/build/into.rs

+1-14
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
//! wrapped up as expressions (e.g. blocks). To make this ergonomic, we use this
1515
//! latter `EvalInto` trait.
1616
17-
use build::{BlockAnd, BlockAndExtension, Builder};
17+
use build::{BlockAnd, Builder};
1818
use hair::*;
1919
use rustc::mir::repr::*;
2020

@@ -58,16 +58,3 @@ impl<'tcx> EvalInto<'tcx> for Expr<'tcx> {
5858
builder.into_expr(destination, block, self)
5959
}
6060
}
61-
62-
impl<'tcx> EvalInto<'tcx> for Option<ExprRef<'tcx>> {
63-
fn eval_into<'a>(self,
64-
builder: &mut Builder<'a, 'tcx>,
65-
destination: &Lvalue<'tcx>,
66-
block: BasicBlock)
67-
-> BlockAnd<()> {
68-
match self {
69-
Some(expr) => builder.into(destination, block, expr),
70-
None => block.unit(),
71-
}
72-
}
73-
}

0 commit comments

Comments
 (0)