Skip to content

Commit 4fff543

Browse files
committed
Add flag to mir::LocalDecl to track whether its a temp from some subexpr a block tail expression.
Slightly refactored the `LocalDecl` construction API in the process.
1 parent b8bea5a commit 4fff543

File tree

11 files changed

+207
-23
lines changed

11 files changed

+207
-23
lines changed

src/librustc/ich/impls_mir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ impl_stable_hash_for!(struct mir::LocalDecl<'tcx> {
2929
source_info,
3030
visibility_scope,
3131
internal,
32+
is_block_tail,
3233
is_user_variable
3334
});
3435
impl_stable_hash_for!(struct mir::UpvarDecl { debug_name, var_hir_id, by_ref, mutability });

src/librustc/mir/mod.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,18 @@ mod binding_form_impl {
638638
}
639639
}
640640

641+
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
642+
pub struct BlockTailInfo {
643+
/// If `true`, then the value resulting from evaluating this tail
644+
/// expression is ignored by the block's expression context.
645+
///
646+
/// Examples include `{ ...; tail };` and `let _ = { ...; tail };`
647+
/// but not e.g. `let _x = { ...; tail };`
648+
pub tail_result_is_ignored: bool,
649+
}
650+
651+
impl_stable_hash_for!(struct BlockTailInfo { tail_result_is_ignored });
652+
641653
/// A MIR local.
642654
///
643655
/// This can be a binding declared by the user, a temporary inserted by the compiler, a function
@@ -677,6 +689,12 @@ pub struct LocalDecl<'tcx> {
677689
/// generator.
678690
pub internal: bool,
679691

692+
/// If this local is a temporary and `is_block_tail` is `Some`,
693+
/// then it is a temporary created for evaluation of some
694+
/// subexpression of some block's tail expression (with no
695+
/// intervening statement context).
696+
pub is_block_tail: Option<BlockTailInfo>,
697+
680698
/// Type of this local.
681699
pub ty: Ty<'tcx>,
682700

@@ -825,10 +843,19 @@ impl<'tcx> LocalDecl<'tcx> {
825843
Self::new_local(ty, Mutability::Mut, false, span)
826844
}
827845

828-
/// Create a new immutable `LocalDecl` for a temporary.
846+
/// Converts `self` into same `LocalDecl` except tagged as immutable.
829847
#[inline]
830-
pub fn new_immutable_temp(ty: Ty<'tcx>, span: Span) -> Self {
831-
Self::new_local(ty, Mutability::Not, false, span)
848+
pub fn immutable(mut self) -> Self {
849+
self.mutability = Mutability::Not;
850+
self
851+
}
852+
853+
/// Converts `self` into same `LocalDecl` except tagged as internal temporary.
854+
#[inline]
855+
pub fn block_tail(mut self, info: BlockTailInfo) -> Self {
856+
assert!(self.is_block_tail.is_none());
857+
self.is_block_tail = Some(info);
858+
self
832859
}
833860

834861
/// Create a new `LocalDecl` for a internal temporary.
@@ -856,6 +883,7 @@ impl<'tcx> LocalDecl<'tcx> {
856883
visibility_scope: OUTERMOST_SOURCE_SCOPE,
857884
internal,
858885
is_user_variable: None,
886+
is_block_tail: None,
859887
}
860888
}
861889

@@ -874,6 +902,7 @@ impl<'tcx> LocalDecl<'tcx> {
874902
},
875903
visibility_scope: OUTERMOST_SOURCE_SCOPE,
876904
internal: false,
905+
is_block_tail: None,
877906
name: None, // FIXME maybe we do want some name here?
878907
is_user_variable: None,
879908
}
@@ -2668,6 +2697,7 @@ pub enum ClosureOutlivesSubject<'tcx> {
26682697
*/
26692698

26702699
CloneTypeFoldableAndLiftImpls! {
2700+
BlockTailInfo,
26712701
Mutability,
26722702
SourceInfo,
26732703
UpvarDecl,
@@ -2711,6 +2741,7 @@ BraceStructTypeFoldableImpl! {
27112741
user_ty,
27122742
name,
27132743
source_info,
2744+
is_block_tail,
27142745
visibility_scope,
27152746
}
27162747
}

src/librustc/mir/visit.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,7 @@ macro_rules! make_mir_visitor {
728728
ref $($mutability)* visibility_scope,
729729
internal: _,
730730
is_user_variable: _,
731+
is_block_tail: _,
731732
} = *local_decl;
732733

733734
self.visit_ty(ty, TyContext::LocalDecl {

src/librustc_mir/build/block.rs

Lines changed: 34 additions & 5 deletions
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, BlockAndExtension, Builder};
11+
use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
1212
use build::ForGuard::OutsideGuard;
1313
use build::matches::ArmHasGuard;
1414
use hair::*;
@@ -93,6 +93,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
9393
let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
9494
match kind {
9595
StmtKind::Expr { scope, expr } => {
96+
this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
9697
unpack!(block = this.in_opt_scope(
9798
opt_destruction_scope.map(|de|(de, source_info)), block, |this| {
9899
let si = (scope, source_info);
@@ -109,6 +110,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
109110
initializer,
110111
lint_level
111112
} => {
113+
let ignores_expr_result = if let PatternKind::Wild = *pattern.kind {
114+
true
115+
} else {
116+
false
117+
};
118+
this.block_context.push(BlockFrame::Statement { ignores_expr_result });
119+
112120
// Enter the remainder scope, i.e. the bindings' destruction scope.
113121
this.push_scope((remainder_scope, source_info));
114122
let_scope_stack.push(remainder_scope);
@@ -155,19 +163,40 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
155163
}
156164
}
157165
}
166+
167+
let popped = this.block_context.pop();
168+
assert!(popped.map_or(false, |bf|bf.is_statement()));
158169
}
170+
159171
// Then, the block may have an optional trailing expression which is a “return” value
160-
// of the block.
172+
// of the block, which is stored into `destination`.
173+
let tcx = this.hir.tcx();
174+
let destination_ty = destination.ty(&this.local_decls, tcx).to_ty(tcx);
161175
if let Some(expr) = expr {
176+
let tail_result_is_ignored = destination_ty.is_unit() ||
177+
match this.block_context.last() {
178+
// no context: conservatively assume result is read
179+
None => false,
180+
181+
// sub-expression: block result feeds into some computation
182+
Some(BlockFrame::SubExpr) => false,
183+
184+
// otherwise: use accumualated is_ignored state.
185+
Some(BlockFrame::TailExpr { tail_result_is_ignored: ignored }) |
186+
Some(BlockFrame::Statement { ignores_expr_result: ignored }) => *ignored,
187+
};
188+
this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored });
189+
162190
unpack!(block = this.into(destination, block, expr));
191+
let popped = this.block_context.pop();
192+
193+
assert!(popped.map_or(false, |bf|bf.is_tail_expr()));
163194
} else {
164195
// If a block has no trailing expression, then it is given an implicit return type.
165196
// This return type is usually `()`, unless the block is diverging, in which case the
166197
// return type is `!`. For the unit type, we need to actually return the unit, but in
167198
// the case of `!`, no return value is required, as the block will never return.
168-
let tcx = this.hir.tcx();
169-
let ty = destination.ty(&this.local_decls, tcx).to_ty(tcx);
170-
if ty.is_unit() {
199+
if destination_ty.is_unit() {
171200
// We only want to assign an implicit `()` as the return value of the block if the
172201
// block does not diverge. (Otherwise, we may try to assign a unit to a `!`-type.)
173202
this.cfg.push_assign_unit(block, source_info, destination);

src/librustc_mir/build/expr/as_temp.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
//! See docs in build/expr/mod.rs
1212
13-
use build::{BlockAnd, BlockAndExtension, Builder};
13+
use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
1414
use hair::*;
1515
use rustc::middle::region;
1616
use rustc::mir::*;
@@ -59,14 +59,30 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
5959
}
6060

6161
let expr_ty = expr.ty;
62-
let temp = if mutability == Mutability::Not {
63-
this.local_decls
64-
.push(LocalDecl::new_immutable_temp(expr_ty, expr_span))
65-
} else {
66-
this.local_decls
67-
.push(LocalDecl::new_temp(expr_ty, expr_span))
68-
};
62+
let temp = {
63+
let mut local_decl = LocalDecl::new_temp(expr_ty, expr_span);
64+
if mutability == Mutability::Not {
65+
local_decl = local_decl.immutable();
66+
}
67+
68+
debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
69+
// Find out whether this temp is being created within the
70+
// tail expression of a block whose result is ignored.
71+
for bf in this.block_context.iter().rev() {
72+
match bf {
73+
BlockFrame::SubExpr => continue,
74+
BlockFrame::Statement { .. } => break,
75+
&BlockFrame::TailExpr { tail_result_is_ignored } => {
76+
local_decl = local_decl.block_tail(BlockTailInfo {
77+
tail_result_is_ignored
78+
});
79+
break;
80+
}
81+
}
82+
}
6983

84+
this.local_decls.push(local_decl)
85+
};
7086
if !expr_ty.is_never() {
7187
this.cfg.push(
7288
block,

src/librustc_mir/build/expr/into.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! See docs in build/expr/mod.rs
1212
1313
use build::expr::category::{Category, RvalueFunc};
14-
use build::{BlockAnd, BlockAndExtension, Builder};
14+
use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
1515
use hair::*;
1616
use rustc::mir::*;
1717
use rustc::ty;
@@ -39,7 +39,17 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
3939
let expr_span = expr.span;
4040
let source_info = this.source_info(expr_span);
4141

42-
match expr.kind {
42+
let expr_is_block_or_scope = match expr.kind {
43+
ExprKind::Block { .. } => true,
44+
ExprKind::Scope { .. } => true,
45+
_ => false,
46+
};
47+
48+
if !expr_is_block_or_scope {
49+
this.block_context.push(BlockFrame::SubExpr);
50+
}
51+
52+
let block_and = match expr.kind {
4353
ExprKind::Scope {
4454
region_scope,
4555
lint_level,
@@ -302,6 +312,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
302312
visibility_scope: source_info.scope,
303313
internal: true,
304314
is_user_variable: None,
315+
is_block_tail: None,
305316
});
306317
let ptr_temp = Place::Local(ptr_temp);
307318
let block = unpack!(this.into(&ptr_temp, block, ptr));
@@ -414,6 +425,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
414425
.push_assign(block, source_info, destination, rvalue);
415426
block.unit()
416427
}
428+
};
429+
430+
if !expr_is_block_or_scope {
431+
let popped = this.block_context.pop();
432+
assert!(popped.is_some());
417433
}
434+
435+
block_and
418436
}
419437
}

src/librustc_mir/build/expr/stmt.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use build::scope::BreakableScope;
12-
use build::{BlockAnd, BlockAndExtension, Builder};
12+
use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
1313
use hair::*;
1414
use rustc::mir::*;
1515

@@ -40,19 +40,22 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
4040
// is better for borrowck interaction with overloaded
4141
// operators like x[j] = x[i].
4242

43+
this.block_context.push(BlockFrame::SubExpr);
44+
4345
// Generate better code for things that don't need to be
4446
// dropped.
4547
if this.hir.needs_drop(lhs.ty) {
4648
let rhs = unpack!(block = this.as_local_operand(block, rhs));
4749
let lhs = unpack!(block = this.as_place(block, lhs));
4850
unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs));
49-
block.unit()
5051
} else {
5152
let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
5253
let lhs = unpack!(block = this.as_place(block, lhs));
5354
this.cfg.push_assign(block, source_info, &lhs, rhs);
54-
block.unit()
5555
}
56+
57+
this.block_context.pop();
58+
block.unit()
5659
}
5760
ExprKind::AssignOp { op, lhs, rhs } => {
5861
// FIXME(#28160) there is an interesting semantics
@@ -66,6 +69,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6669
let lhs = this.hir.mirror(lhs);
6770
let lhs_ty = lhs.ty;
6871

72+
this.block_context.push(BlockFrame::SubExpr);
73+
6974
// As above, RTL.
7075
let rhs = unpack!(block = this.as_local_operand(block, rhs));
7176
let lhs = unpack!(block = this.as_place(block, lhs));
@@ -85,6 +90,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
8590
);
8691
this.cfg.push_assign(block, source_info, &lhs, result);
8792

93+
this.block_context.pop();
8894
block.unit()
8995
}
9096
ExprKind::Continue { label } => {
@@ -114,7 +120,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
114120
(break_block, region_scope, break_destination.clone())
115121
};
116122
if let Some(value) = value {
117-
unpack!(block = this.into(&destination, block, value))
123+
this.block_context.push(BlockFrame::SubExpr);
124+
unpack!(block = this.into(&destination, block, value));
125+
this.block_context.pop();
118126
} else {
119127
this.cfg.push_assign_unit(block, source_info, &destination)
120128
}
@@ -123,7 +131,12 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
123131
}
124132
ExprKind::Return { value } => {
125133
block = match value {
126-
Some(value) => unpack!(this.into(&Place::Local(RETURN_PLACE), block, value)),
134+
Some(value) => {
135+
this.block_context.push(BlockFrame::SubExpr);
136+
let result = unpack!(this.into(&Place::Local(RETURN_PLACE), block, value));
137+
this.block_context.pop();
138+
result
139+
}
127140
None => {
128141
this.cfg
129142
.push_assign_unit(block, source_info, &Place::Local(RETURN_PLACE));
@@ -140,6 +153,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
140153
outputs,
141154
inputs,
142155
} => {
156+
this.block_context.push(BlockFrame::SubExpr);
143157
let outputs = outputs
144158
.into_iter()
145159
.map(|output| unpack!(block = this.as_place(block, output)))
@@ -161,6 +175,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
161175
},
162176
},
163177
);
178+
this.block_context.pop();
164179
block.unit()
165180
}
166181
_ => {

src/librustc_mir/build/matches/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
14851485
source_info,
14861486
visibility_scope,
14871487
internal: false,
1488+
is_block_tail: None,
14881489
is_user_variable: Some(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm {
14891490
binding_mode,
14901491
// hypothetically, `visit_bindings` could try to unzip
@@ -1518,6 +1519,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
15181519
visibility_scope,
15191520
// FIXME: should these secretly injected ref_for_guard's be marked as `internal`?
15201521
internal: false,
1522+
is_block_tail: None,
15211523
is_user_variable: Some(ClearCrossCrate::Set(BindingForm::RefForGuard)),
15221524
});
15231525
LocalsForNode::ForGuard {

0 commit comments

Comments
 (0)