Skip to content

Commit ff8c0ef

Browse files
committed
Fix drop handling for if let expressions
MIR lowering for `if let` expressions is now more complicated now that `if let` exists in HIR. This PR adds a scope for the variables bound in an `if let` expression and then uses an approach similar to how we handle loops to ensure that we reliably drop the correct variables.
1 parent 50171c3 commit ff8c0ef

File tree

56 files changed

+524
-464
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+524
-464
lines changed

compiler/rustc_hir/src/hir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,7 @@ pub struct Arm<'hir> {
11871187
#[derive(Debug, HashStable_Generic)]
11881188
pub enum Guard<'hir> {
11891189
If(&'hir Expr<'hir>),
1190+
// FIXME use ExprKind::Let for this.
11901191
IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>),
11911192
}
11921193

compiler/rustc_middle/src/middle/region.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ impl fmt::Debug for Scope {
9494
ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.id),
9595
ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id),
9696
ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id),
97+
ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.id),
9798
ScopeData::Remainder(fsi) => write!(
9899
fmt,
99100
"Remainder {{ block: {:?}, first_statement_index: {}}}",
@@ -120,6 +121,10 @@ pub enum ScopeData {
120121
/// Scope of destructors for temporaries of node-id.
121122
Destruction,
122123

124+
/// Scope of the condition and then block of an if expression
125+
/// Used for variables introduced in an if-let expression.
126+
IfThen,
127+
123128
/// Scope following a `let id = expr;` binding in a block.
124129
Remainder(FirstStatementIndex),
125130
}

compiler/rustc_middle/src/thir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ pub enum ExprKind<'tcx> {
223223
},
224224
/// An `if` expression.
225225
If {
226+
if_then_scope: region::Scope,
226227
cond: ExprId,
227228
then: ExprId,
228229
else_opt: Option<ExprId>,

compiler/rustc_mir_build/src/build/expr/into.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
5252
ExprKind::Match { scrutinee, ref arms } => {
5353
this.match_expr(destination, expr_span, block, &this.thir[scrutinee], arms)
5454
}
55-
ExprKind::If { cond, then, else_opt } => {
56-
let local_scope = this.local_scope();
57-
let (mut then_blk, mut else_blk) =
58-
this.then_else_blocks(block, &this.thir[cond], local_scope, source_info);
59-
unpack!(then_blk = this.expr_into_dest(destination, then_blk, &this.thir[then]));
55+
ExprKind::If { cond, then, else_opt, if_then_scope } => {
56+
let then_blk;
57+
let then_expr = &this.thir[then];
58+
let then_source_info = this.source_info(then_expr.span);
59+
let condition_scope = this.local_scope();
60+
61+
let mut else_blk = unpack!(
62+
then_blk = this.in_scope(
63+
(if_then_scope, then_source_info),
64+
LintLevel::Inherited,
65+
|this| {
66+
let (then_block, else_block) =
67+
this.in_if_then_scope(condition_scope, |this| {
68+
let then_blk = unpack!(this.then_else_break(
69+
block,
70+
&this.thir[cond],
71+
condition_scope,
72+
condition_scope,
73+
then_expr.span,
74+
));
75+
this.expr_into_dest(destination, then_blk, then_expr)
76+
});
77+
then_block.and(else_block)
78+
},
79+
)
80+
);
81+
6082
else_blk = if let Some(else_opt) = else_opt {
6183
unpack!(this.expr_into_dest(destination, else_blk, &this.thir[else_opt]))
6284
} else {
@@ -81,9 +103,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
81103

82104
join_block.unit()
83105
}
84-
ExprKind::Let { ref pat, expr } => {
85-
let (true_block, false_block) =
86-
this.lower_let(block, &this.thir[expr], pat, expr_span);
106+
ExprKind::Let { expr, ref pat } => {
107+
let scope = this.local_scope();
108+
let (true_block, false_block) = this.in_if_then_scope(scope, |this| {
109+
this.lower_let_else(block, &this.thir[expr], pat, scope, expr_span)
110+
});
87111

88112
let join_block = this.cfg.start_new_block();
89113

compiler/rustc_mir_build/src/build/matches/mod.rs

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,42 +35,49 @@ use std::convert::TryFrom;
3535
use std::mem;
3636

3737
impl<'a, 'tcx> Builder<'a, 'tcx> {
38-
pub(crate) fn then_else_blocks(
38+
pub(crate) fn then_else_break(
3939
&mut self,
4040
mut block: BasicBlock,
4141
expr: &Expr<'tcx>,
42-
scope: region::Scope,
43-
source_info: SourceInfo,
44-
) -> (BasicBlock, BasicBlock) {
42+
temp_scope: region::Scope,
43+
break_scope: region::Scope,
44+
variable_scope_span: Span,
45+
) -> BlockAnd<()> {
4546
let this = self;
4647
let expr_span = expr.span;
4748

4849
match expr.kind {
4950
ExprKind::Scope { region_scope, lint_level, value } => {
50-
let region_scope = (region_scope, source_info);
51-
let then_block;
52-
let else_block = unpack!(
53-
then_block = this.in_scope(region_scope, lint_level, |this| {
54-
let (then_block, else_block) =
55-
this.then_else_blocks(block, &this.thir[value], scope, source_info);
56-
then_block.and(else_block)
57-
})
58-
);
59-
(then_block, else_block)
51+
let region_scope = (region_scope, this.source_info(expr_span));
52+
this.in_scope(region_scope, lint_level, |this| {
53+
this.then_else_break(
54+
block,
55+
&this.thir[value],
56+
temp_scope,
57+
break_scope,
58+
variable_scope_span,
59+
)
60+
})
6061
}
6162
ExprKind::Let { expr, ref pat } => {
62-
// FIXME: Use correct span.
63-
this.lower_let(block, &this.thir[expr], pat, expr_span)
63+
this.lower_let_else(block, &this.thir[expr], pat, break_scope, variable_scope_span)
6464
}
6565
_ => {
66+
// TODO `as_temp`?
6667
let mutability = Mutability::Mut;
67-
let place = unpack!(block = this.as_temp(block, Some(scope), expr, mutability));
68+
let place =
69+
unpack!(block = this.as_temp(block, Some(temp_scope), expr, mutability));
6870
let operand = Operand::Move(Place::from(place));
71+
6972
let then_block = this.cfg.start_new_block();
7073
let else_block = this.cfg.start_new_block();
7174
let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block);
75+
76+
let source_info = this.source_info(expr_span);
7277
this.cfg.terminate(block, source_info, term);
73-
(then_block, else_block)
78+
this.break_for_else(else_block, break_scope, source_info);
79+
80+
then_block.unit()
7481
}
7582
}
7683
}
@@ -302,6 +309,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
302309

303310
let arm_source_info = self.source_info(arm.span);
304311
let arm_scope = (arm.scope, arm_source_info);
312+
let match_scope = self.local_scope();
305313
self.in_scope(arm_scope, arm.lint_level, |this| {
306314
// `try_upvars_resolved` may fail if it is unable to resolve the given
307315
// `PlaceBuilder` inside a closure. In this case, we don't want to include
@@ -340,6 +348,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
340348
scrutinee_span,
341349
Some(arm.span),
342350
Some(arm.scope),
351+
Some(match_scope),
343352
);
344353

345354
if let Some(source_scope) = scope {
@@ -384,6 +393,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
384393
scrutinee_span: Span,
385394
arm_span: Option<Span>,
386395
arm_scope: Option<region::Scope>,
396+
match_scope: Option<region::Scope>,
387397
) -> BasicBlock {
388398
if candidate.subcandidates.is_empty() {
389399
// Avoid generating another `BasicBlock` when we only have one
@@ -395,6 +405,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
395405
fake_borrow_temps,
396406
scrutinee_span,
397407
arm_span,
408+
match_scope,
398409
true,
399410
)
400411
} else {
@@ -431,6 +442,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
431442
&fake_borrow_temps,
432443
scrutinee_span,
433444
arm_span,
445+
match_scope,
434446
schedule_drops,
435447
);
436448
if arm_scope.is_none() {
@@ -616,6 +628,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
616628
irrefutable_pat.span,
617629
None,
618630
None,
631+
None,
619632
)
620633
.unit()
621634
}
@@ -1742,13 +1755,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17421755
// Pat binding - used for `let` and function parameters as well.
17431756

17441757
impl<'a, 'tcx> Builder<'a, 'tcx> {
1745-
pub fn lower_let(
1758+
crate fn lower_let_else(
17461759
&mut self,
17471760
mut block: BasicBlock,
17481761
expr: &Expr<'tcx>,
17491762
pat: &Pat<'tcx>,
1763+
else_target: region::Scope,
17501764
span: Span,
1751-
) -> (BasicBlock, BasicBlock) {
1765+
) -> BlockAnd<()> {
17521766
let expr_span = expr.span;
17531767
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span));
17541768
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false);
@@ -1769,6 +1783,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17691783
expr_place = expr_builder.into_place(self.tcx, self.typeck_results);
17701784
opt_expr_place = Some((Some(&expr_place), expr_span));
17711785
}
1786+
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
1787+
self.break_for_else(otherwise_post_guard_block, else_target, self.source_info(expr_span));
1788+
17721789
self.declare_bindings(None, pat.span.to(span), pat, ArmHasGuard(false), opt_expr_place);
17731790
let post_guard_block = self.bind_pattern(
17741791
self.source_info(pat.span),
@@ -1778,9 +1795,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17781795
expr.span,
17791796
None,
17801797
None,
1798+
None,
17811799
);
1782-
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
1783-
(post_guard_block, otherwise_post_guard_block)
1800+
1801+
post_guard_block.unit()
17841802
}
17851803

17861804
/// Initializes each of the bindings from the candidate by
@@ -1799,6 +1817,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17991817
fake_borrows: &Vec<(Place<'tcx>, Local)>,
18001818
scrutinee_span: Span,
18011819
arm_span: Option<Span>,
1820+
match_scope: Option<region::Scope>,
18021821
schedule_drops: bool,
18031822
) -> BasicBlock {
18041823
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
@@ -1929,17 +1948,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
19291948
self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
19301949
}
19311950

1932-
let (guard_span, (post_guard_block, otherwise_post_guard_block)) = match *guard {
1933-
Guard::If(e) => {
1934-
let e = &self.thir[e];
1935-
let source_info = self.source_info(e.span);
1936-
(e.span, self.test_bool(block, e, source_info))
1937-
}
1938-
Guard::IfLet(ref pat, scrutinee) => {
1939-
let s = &self.thir[scrutinee];
1940-
(s.span, self.lower_let(block, s, pat, arm_span.unwrap()))
1941-
}
1942-
};
1951+
let arm_span = arm_span.unwrap();
1952+
let arm_scope = self.local_scope();
1953+
let match_scope = match_scope.unwrap();
1954+
let mut guard_span = rustc_span::DUMMY_SP;
1955+
1956+
let (post_guard_block, otherwise_post_guard_block) =
1957+
self.in_if_then_scope(match_scope, |this| match *guard {
1958+
Guard::If(e) => {
1959+
let e = &this.thir[e];
1960+
guard_span = e.span;
1961+
this.then_else_break(block, e, arm_scope, match_scope, arm_span)
1962+
}
1963+
Guard::IfLet(ref pat, scrutinee) => {
1964+
let s = &this.thir[scrutinee];
1965+
guard_span = s.span;
1966+
this.lower_let_else(block, s, pat, match_scope, arm_span)
1967+
}
1968+
});
1969+
19431970
let source_info = self.source_info(guard_span);
19441971
let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span));
19451972
let guard_frame = self.guard_context.pop().unwrap();
@@ -1955,10 +1982,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
19551982
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
19561983
unreachable
19571984
});
1958-
let outside_scope = self.cfg.start_new_block();
1959-
self.exit_top_scope(otherwise_post_guard_block, outside_scope, source_info);
19601985
self.false_edges(
1961-
outside_scope,
1986+
otherwise_post_guard_block,
19621987
otherwise_block,
19631988
candidate.next_candidate_pre_binding_block,
19641989
source_info,

0 commit comments

Comments
 (0)