Skip to content

Commit 906c06a

Browse files
committed
make operands live to the end of their containing expression
In MIR construction, operands need to live exactly until they are used, which is during the (sub)expression that made the call to `as_operand`. Before this PR, operands lived until the end of the temporary scope, which was sometimes unnecessarily longer and sometimes too short. Fixes #38669.
1 parent 6755fb8 commit 906c06a

File tree

12 files changed

+257
-56
lines changed

12 files changed

+257
-56
lines changed

src/librustc_mir/build/expr/as_lvalue.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5656
let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
5757

5858
let slice = unpack!(block = this.as_lvalue(block, lhs));
59-
60-
let idx = unpack!(block = this.as_operand(block, index));
59+
// extent=None so lvalue indexes live forever. They are scalars so they
60+
// do not need storage annotations, and they are often copied between
61+
// places.
62+
let idx = unpack!(block = this.as_operand(block, None, index));
6163

6264
// bounds check:
6365
let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty));
@@ -121,7 +123,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
121123
Some(Category::Lvalue) => false,
122124
_ => true,
123125
});
124-
this.as_temp(block, expr)
126+
this.as_temp(block, expr.temp_lifetime, expr)
125127
}
126128
}
127129
}

src/librustc_mir/build/expr/as_operand.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,52 @@
1313
use build::{BlockAnd, BlockAndExtension, Builder};
1414
use build::expr::category::Category;
1515
use hair::*;
16+
use rustc::middle::region::CodeExtent;
1617
use rustc::mir::*;
1718

1819
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
20+
/// Returns an operand suitable for use until the end of the current
21+
/// scope expression.
22+
///
23+
/// The operand returned from this function will *not be valid* after
24+
/// an ExprKind::Scope is passed, so please do *not* return it from
25+
/// functions to avoid bad miscompiles.
26+
pub fn as_local_operand<M>(&mut self, block: BasicBlock, expr: M)
27+
-> BlockAnd<Operand<'tcx>>
28+
where M: Mirror<'tcx, Output = Expr<'tcx>>
29+
{
30+
let topmost_scope = self.topmost_scope(); // FIXME(#6393)
31+
self.as_operand(block, Some(topmost_scope), expr)
32+
}
33+
1934
/// Compile `expr` into a value that can be used as an operand.
2035
/// If `expr` is an lvalue like `x`, this will introduce a
2136
/// temporary `tmp = x`, so that we capture the value of `x` at
2237
/// this time.
23-
pub fn as_operand<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Operand<'tcx>>
38+
///
39+
/// The operand is known to be live until the end of `scope`.
40+
pub fn as_operand<M>(&mut self,
41+
block: BasicBlock,
42+
scope: Option<CodeExtent>,
43+
expr: M) -> BlockAnd<Operand<'tcx>>
2444
where M: Mirror<'tcx, Output = Expr<'tcx>>
2545
{
2646
let expr = self.hir.mirror(expr);
27-
self.expr_as_operand(block, expr)
47+
self.expr_as_operand(block, scope, expr)
2848
}
2949

3050
fn expr_as_operand(&mut self,
3151
mut block: BasicBlock,
52+
scope: Option<CodeExtent>,
3253
expr: Expr<'tcx>)
3354
-> BlockAnd<Operand<'tcx>> {
3455
debug!("expr_as_operand(block={:?}, expr={:?})", block, expr);
3556
let this = self;
3657

3758
if let ExprKind::Scope { extent, value } = expr.kind {
38-
return this.in_scope(extent, block, |this| this.as_operand(block, value));
59+
return this.in_scope(extent, block, |this| {
60+
this.as_operand(block, scope, value)
61+
});
3962
}
4063

4164
let category = Category::of(&expr.kind).unwrap();
@@ -47,7 +70,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
4770
}
4871
Category::Lvalue |
4972
Category::Rvalue(..) => {
50-
let operand = unpack!(block = this.as_temp(block, expr));
73+
let operand =
74+
unpack!(block = this.as_temp(block, scope, expr));
5175
block.and(Operand::Consume(operand))
5276
}
5377
}

src/librustc_mir/build/expr/as_rvalue.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,34 @@ use build::expr::category::{Category, RvalueFunc};
2121
use hair::*;
2222
use rustc_const_math::{ConstInt, ConstIsize};
2323
use rustc::middle::const_val::ConstVal;
24+
use rustc::middle::region::CodeExtent;
2425
use rustc::ty;
2526
use rustc::mir::*;
2627
use syntax::ast;
2728
use syntax_pos::Span;
2829

2930
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
31+
/// See comment on `as_local_operand`
32+
pub fn as_local_rvalue<M>(&mut self, block: BasicBlock, expr: M)
33+
-> BlockAnd<Rvalue<'tcx>>
34+
where M: Mirror<'tcx, Output = Expr<'tcx>>
35+
{
36+
let topmost_scope = self.topmost_scope(); // FIXME(#6393)
37+
self.as_rvalue(block, Some(topmost_scope), expr)
38+
}
39+
3040
/// Compile `expr`, yielding an rvalue.
31-
pub fn as_rvalue<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Rvalue<'tcx>>
41+
pub fn as_rvalue<M>(&mut self, block: BasicBlock, scope: Option<CodeExtent>, expr: M)
42+
-> BlockAnd<Rvalue<'tcx>>
3243
where M: Mirror<'tcx, Output = Expr<'tcx>>
3344
{
3445
let expr = self.hir.mirror(expr);
35-
self.expr_as_rvalue(block, expr)
46+
self.expr_as_rvalue(block, scope, expr)
3647
}
3748

3849
fn expr_as_rvalue(&mut self,
3950
mut block: BasicBlock,
51+
scope: Option<CodeExtent>,
4052
expr: Expr<'tcx>)
4153
-> BlockAnd<Rvalue<'tcx>> {
4254
debug!("expr_as_rvalue(block={:?}, expr={:?})", block, expr);
@@ -47,24 +59,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
4759

4860
match expr.kind {
4961
ExprKind::Scope { extent, value } => {
50-
this.in_scope(extent, block, |this| this.as_rvalue(block, value))
62+
this.in_scope(extent, block, |this| this.as_rvalue(block, scope, value))
5163
}
5264
ExprKind::Repeat { value, count } => {
53-
let value_operand = unpack!(block = this.as_operand(block, value));
65+
let value_operand = unpack!(block = this.as_operand(block, scope, value));
5466
block.and(Rvalue::Repeat(value_operand, count))
5567
}
5668
ExprKind::Borrow { region, borrow_kind, arg } => {
5769
let arg_lvalue = unpack!(block = this.as_lvalue(block, arg));
5870
block.and(Rvalue::Ref(region, borrow_kind, arg_lvalue))
5971
}
6072
ExprKind::Binary { op, lhs, rhs } => {
61-
let lhs = unpack!(block = this.as_operand(block, lhs));
62-
let rhs = unpack!(block = this.as_operand(block, rhs));
73+
let lhs = unpack!(block = this.as_operand(block, scope, lhs));
74+
let rhs = unpack!(block = this.as_operand(block, scope, rhs));
6375
this.build_binary_op(block, op, expr_span, expr.ty,
6476
lhs, rhs)
6577
}
6678
ExprKind::Unary { op, arg } => {
67-
let arg = unpack!(block = this.as_operand(block, arg));
79+
let arg = unpack!(block = this.as_operand(block, scope, arg));
6880
// Check for -MIN on signed integers
6981
if this.hir.check_overflow() && op == UnOp::Neg && expr.ty.is_signed() {
7082
let bool_ty = this.hir.bool_ty();
@@ -97,27 +109,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
97109
ExprKind::Cast { source } => {
98110
let source = this.hir.mirror(source);
99111

100-
let source = unpack!(block = this.as_operand(block, source));
112+
let source = unpack!(block = this.as_operand(block, scope, source));
101113
block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
102114
}
103115
ExprKind::Use { source } => {
104-
let source = unpack!(block = this.as_operand(block, source));
116+
let source = unpack!(block = this.as_operand(block, scope, source));
105117
block.and(Rvalue::Use(source))
106118
}
107119
ExprKind::ReifyFnPointer { source } => {
108-
let source = unpack!(block = this.as_operand(block, source));
120+
let source = unpack!(block = this.as_operand(block, scope, source));
109121
block.and(Rvalue::Cast(CastKind::ReifyFnPointer, source, expr.ty))
110122
}
111123
ExprKind::UnsafeFnPointer { source } => {
112-
let source = unpack!(block = this.as_operand(block, source));
124+
let source = unpack!(block = this.as_operand(block, scope, source));
113125
block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty))
114126
}
115127
ExprKind::ClosureFnPointer { source } => {
116-
let source = unpack!(block = this.as_operand(block, source));
128+
let source = unpack!(block = this.as_operand(block, scope, source));
117129
block.and(Rvalue::Cast(CastKind::ClosureFnPointer, source, expr.ty))
118130
}
119131
ExprKind::Unsize { source } => {
120-
let source = unpack!(block = this.as_operand(block, source));
132+
let source = unpack!(block = this.as_operand(block, scope, source));
121133
block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty))
122134
}
123135
ExprKind::Array { fields } => {
@@ -150,7 +162,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
150162
// first process the set of fields
151163
let fields: Vec<_> =
152164
fields.into_iter()
153-
.map(|f| unpack!(block = this.as_operand(block, f)))
165+
.map(|f| unpack!(block = this.as_operand(block, scope, f)))
154166
.collect();
155167

156168
block.and(Rvalue::Aggregate(AggregateKind::Array, fields))
@@ -159,15 +171,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
159171
// first process the set of fields
160172
let fields: Vec<_> =
161173
fields.into_iter()
162-
.map(|f| unpack!(block = this.as_operand(block, f)))
174+
.map(|f| unpack!(block = this.as_operand(block, scope, f)))
163175
.collect();
164176

165177
block.and(Rvalue::Aggregate(AggregateKind::Tuple, fields))
166178
}
167179
ExprKind::Closure { closure_id, substs, upvars } => { // see (*) above
168180
let upvars =
169181
upvars.into_iter()
170-
.map(|upvar| unpack!(block = this.as_operand(block, upvar)))
182+
.map(|upvar| unpack!(block = this.as_operand(block, scope, upvar)))
171183
.collect();
172184
block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars))
173185
}
@@ -179,10 +191,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
179191

180192
// first process the set of fields that were provided
181193
// (evaluating them in order given by user)
182-
let fields_map: FxHashMap<_, _> =
183-
fields.into_iter()
184-
.map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr))))
185-
.collect();
194+
let fields_map: FxHashMap<_, _> = fields.into_iter()
195+
.map(|f| (f.name, unpack!(block = this.as_operand(block, scope, f.expr))))
196+
.collect();
186197

187198
let field_names = this.hir.all_fields(adt_def, variant_index);
188199

@@ -235,7 +246,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
235246
Some(Category::Rvalue(RvalueFunc::AsRvalue)) => false,
236247
_ => true,
237248
});
238-
let operand = unpack!(block = this.as_operand(block, expr));
249+
let operand = unpack!(block = this.as_operand(block, scope, expr));
239250
block.and(Rvalue::Use(operand))
240251
}
241252
}

src/librustc_mir/build/expr/as_temp.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,38 @@
1313
use build::{BlockAnd, BlockAndExtension, Builder};
1414
use build::expr::category::Category;
1515
use hair::*;
16+
use rustc::middle::region::CodeExtent;
1617
use rustc::mir::*;
1718

1819
impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
1920
/// Compile `expr` into a fresh temporary. This is used when building
2021
/// up rvalues so as to freeze the value that will be consumed.
21-
pub fn as_temp<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Lvalue<'tcx>>
22+
pub fn as_temp<M>(&mut self,
23+
block: BasicBlock,
24+
temp_lifetime: Option<CodeExtent>,
25+
expr: M)
26+
-> BlockAnd<Lvalue<'tcx>>
2227
where M: Mirror<'tcx, Output = Expr<'tcx>>
2328
{
2429
let expr = self.hir.mirror(expr);
25-
self.expr_as_temp(block, expr)
30+
self.expr_as_temp(block, temp_lifetime, expr)
2631
}
2732

28-
fn expr_as_temp(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<Lvalue<'tcx>> {
33+
fn expr_as_temp(&mut self,
34+
mut block: BasicBlock,
35+
temp_lifetime: Option<CodeExtent>,
36+
expr: Expr<'tcx>)
37+
-> BlockAnd<Lvalue<'tcx>> {
2938
debug!("expr_as_temp(block={:?}, expr={:?})", block, expr);
3039
let this = self;
3140

32-
if let ExprKind::Scope { extent, value } = expr.kind {
33-
return this.in_scope(extent, block, |this| this.as_temp(block, value));
41+
if let ExprKind::Scope { .. } = expr.kind {
42+
span_bug!(expr.span, "unexpected scope expression in as_temp: {:?}",
43+
expr);
3444
}
3545

3646
let expr_ty = expr.ty.clone();
3747
let temp = this.temp(expr_ty.clone());
38-
let temp_lifetime = expr.temp_lifetime;
3948
let expr_span = expr.span;
4049
let source_info = this.source_info(expr_span);
4150

src/librustc_mir/build/expr/into.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5252
_ => false,
5353
};
5454

55-
unpack!(block = this.as_rvalue(block, source));
55+
unpack!(block = this.as_local_rvalue(block, source));
5656

5757
// This is an optimization. If the expression was a call then we already have an
5858
// unreachable block. Don't bother to terminate it and create a new one.
@@ -65,7 +65,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6565
}
6666
}
6767
ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
68-
let operand = unpack!(block = this.as_operand(block, cond_expr));
68+
let operand = unpack!(block = this.as_local_operand(block, cond_expr));
6969

7070
let mut then_block = this.cfg.start_new_block();
7171
let mut else_block = this.cfg.start_new_block();
@@ -107,15 +107,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
107107
(this.cfg.start_new_block(), this.cfg.start_new_block(),
108108
this.cfg.start_new_block(), this.cfg.start_new_block());
109109

110-
let lhs = unpack!(block = this.as_operand(block, lhs));
110+
let lhs = unpack!(block = this.as_local_operand(block, lhs));
111111
let blocks = match op {
112112
LogicalOp::And => (else_block, false_block),
113113
LogicalOp::Or => (true_block, else_block),
114114
};
115115
let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
116116
this.cfg.terminate(block, source_info, term);
117117

118-
let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
118+
let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
119119
let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
120120
this.cfg.terminate(else_block, source_info, term);
121121

@@ -173,7 +173,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
173173
if let Some(cond_expr) = opt_cond_expr {
174174
let loop_block_end;
175175
let cond = unpack!(
176-
loop_block_end = this.as_operand(loop_block, cond_expr));
176+
loop_block_end = this.as_local_operand(loop_block, cond_expr));
177177
body_block = this.cfg.start_new_block();
178178
let term = TerminatorKind::if_(this.hir.tcx(), cond,
179179
body_block, exit_block);
@@ -206,10 +206,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
206206
}
207207
_ => false
208208
};
209-
let fun = unpack!(block = this.as_operand(block, fun));
209+
let fun = unpack!(block = this.as_local_operand(block, fun));
210210
let args: Vec<_> =
211211
args.into_iter()
212-
.map(|arg| unpack!(block = this.as_operand(block, arg)))
212+
.map(|arg| unpack!(block = this.as_local_operand(block, arg)))
213213
.collect();
214214

215215
let success = this.cfg.start_new_block();
@@ -265,7 +265,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
265265
_ => true,
266266
});
267267

268-
let rvalue = unpack!(block = this.as_rvalue(block, expr));
268+
let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
269269
this.cfg.push_assign(block, source_info, destination, rvalue);
270270
block.unit()
271271
}

src/librustc_mir/build/expr/stmt.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
3838
// Generate better code for things that don't need to be
3939
// dropped.
4040
if this.hir.needs_drop(lhs.ty) {
41-
let rhs = unpack!(block = this.as_operand(block, rhs));
41+
let rhs = unpack!(block = this.as_local_operand(block, rhs));
4242
let lhs = unpack!(block = this.as_lvalue(block, lhs));
4343
unpack!(block = this.build_drop_and_replace(
4444
block, lhs_span, lhs, rhs
4545
));
4646
block.unit()
4747
} else {
48-
let rhs = unpack!(block = this.as_rvalue(block, rhs));
48+
let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
4949
let lhs = unpack!(block = this.as_lvalue(block, lhs));
5050
this.cfg.push_assign(block, source_info, &lhs, rhs);
5151
block.unit()
@@ -64,7 +64,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6464
let lhs_ty = lhs.ty;
6565

6666
// As above, RTL.
67-
let rhs = unpack!(block = this.as_operand(block, rhs));
67+
let rhs = unpack!(block = this.as_local_operand(block, rhs));
6868
let lhs = unpack!(block = this.as_lvalue(block, lhs));
6969

7070
// we don't have to drop prior contents or anything
@@ -122,7 +122,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
122122
unpack!(block = this.as_lvalue(block, output))
123123
}).collect();
124124
let inputs = inputs.into_iter().map(|input| {
125-
unpack!(block = this.as_operand(block, input))
125+
unpack!(block = this.as_local_operand(block, input))
126126
}).collect();
127127
this.cfg.push(block, Statement {
128128
source_info: source_info,

src/librustc_mir/build/matches/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
661661
// guard, this block is simply unreachable
662662
let guard = self.hir.mirror(guard);
663663
let source_info = self.source_info(guard.span);
664-
let cond = unpack!(block = self.as_operand(block, guard));
664+
let cond = unpack!(block = self.as_local_operand(block, guard));
665665
let otherwise = self.cfg.start_new_block();
666666
self.cfg.terminate(block, source_info,
667667
TerminatorKind::if_(self.hir.tcx(), cond, arm_block, otherwise));

0 commit comments

Comments
 (0)