Skip to content

Commit b7404c8

Browse files
committed
Auto merge of #88572 - matthewjasper:if-let-scoping-fix, r=oli-obk
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. Closes #88307 cc `@flip1995` `@richkadel` `@c410-f3r`
2 parents 4878034 + 4e2fd4f commit b7404c8

File tree

62 files changed

+596
-562
lines changed

Some content is hidden

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

62 files changed

+596
-562
lines changed

compiler/rustc_hir/src/hir.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,7 @@ pub struct Arm<'hir> {
11771177
#[derive(Debug, HashStable_Generic)]
11781178
pub enum Guard<'hir> {
11791179
If(&'hir Expr<'hir>),
1180+
// FIXME use ExprKind::Let for this.
11801181
IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>),
11811182
}
11821183

compiler/rustc_middle/src/middle/region.rs

+5
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

+1
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

+32-8
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_expr(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

+60-36
Original file line numberDiff line numberDiff line change
@@ -35,42 +35,48 @@ 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_expr(block, &this.thir[expr], pat, break_scope, variable_scope_span)
6464
}
6565
_ => {
6666
let mutability = Mutability::Mut;
67-
let place = unpack!(block = this.as_temp(block, Some(scope), expr, mutability));
67+
let place =
68+
unpack!(block = this.as_temp(block, Some(temp_scope), expr, mutability));
6869
let operand = Operand::Move(Place::from(place));
70+
6971
let then_block = this.cfg.start_new_block();
7072
let else_block = this.cfg.start_new_block();
7173
let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block);
74+
75+
let source_info = this.source_info(expr_span);
7276
this.cfg.terminate(block, source_info, term);
73-
(then_block, else_block)
77+
this.break_for_else(else_block, break_scope, source_info);
78+
79+
then_block.unit()
7480
}
7581
}
7682
}
@@ -302,6 +308,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
302308

303309
let arm_source_info = self.source_info(arm.span);
304310
let arm_scope = (arm.scope, arm_source_info);
311+
let match_scope = self.local_scope();
305312
self.in_scope(arm_scope, arm.lint_level, |this| {
306313
// `try_upvars_resolved` may fail if it is unable to resolve the given
307314
// `PlaceBuilder` inside a closure. In this case, we don't want to include
@@ -340,6 +347,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
340347
scrutinee_span,
341348
Some(arm.span),
342349
Some(arm.scope),
350+
Some(match_scope),
343351
);
344352

345353
if let Some(source_scope) = scope {
@@ -384,6 +392,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
384392
scrutinee_span: Span,
385393
arm_span: Option<Span>,
386394
arm_scope: Option<region::Scope>,
395+
match_scope: Option<region::Scope>,
387396
) -> BasicBlock {
388397
if candidate.subcandidates.is_empty() {
389398
// Avoid generating another `BasicBlock` when we only have one
@@ -395,6 +404,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
395404
fake_borrow_temps,
396405
scrutinee_span,
397406
arm_span,
407+
match_scope,
398408
true,
399409
)
400410
} else {
@@ -431,6 +441,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
431441
&fake_borrow_temps,
432442
scrutinee_span,
433443
arm_span,
444+
match_scope,
434445
schedule_drops,
435446
);
436447
if arm_scope.is_none() {
@@ -616,6 +627,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
616627
irrefutable_pat.span,
617628
None,
618629
None,
630+
None,
619631
)
620632
.unit()
621633
}
@@ -1742,13 +1754,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17421754
// Pat binding - used for `let` and function parameters as well.
17431755

17441756
impl<'a, 'tcx> Builder<'a, 'tcx> {
1745-
pub fn lower_let(
1757+
crate fn lower_let_expr(
17461758
&mut self,
17471759
mut block: BasicBlock,
17481760
expr: &Expr<'tcx>,
17491761
pat: &Pat<'tcx>,
1762+
else_target: region::Scope,
17501763
span: Span,
1751-
) -> (BasicBlock, BasicBlock) {
1764+
) -> BlockAnd<()> {
17521765
let expr_span = expr.span;
17531766
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span));
17541767
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false);
@@ -1769,6 +1782,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17691782
expr_place = expr_builder.into_place(self.tcx, self.typeck_results);
17701783
opt_expr_place = Some((Some(&expr_place), expr_span));
17711784
}
1785+
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
1786+
self.break_for_else(otherwise_post_guard_block, else_target, self.source_info(expr_span));
1787+
17721788
self.declare_bindings(None, pat.span.to(span), pat, ArmHasGuard(false), opt_expr_place);
17731789
let post_guard_block = self.bind_pattern(
17741790
self.source_info(pat.span),
@@ -1778,9 +1794,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17781794
expr.span,
17791795
None,
17801796
None,
1797+
None,
17811798
);
1782-
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
1783-
(post_guard_block, otherwise_post_guard_block)
1799+
1800+
post_guard_block.unit()
17841801
}
17851802

17861803
/// Initializes each of the bindings from the candidate by
@@ -1799,6 +1816,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
17991816
fake_borrows: &Vec<(Place<'tcx>, Local)>,
18001817
scrutinee_span: Span,
18011818
arm_span: Option<Span>,
1819+
match_scope: Option<region::Scope>,
18021820
schedule_drops: bool,
18031821
) -> BasicBlock {
18041822
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
@@ -1929,17 +1947,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
19291947
self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
19301948
}
19311949

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-
};
1950+
let arm_span = arm_span.unwrap();
1951+
let arm_scope = self.local_scope();
1952+
let match_scope = match_scope.unwrap();
1953+
let mut guard_span = rustc_span::DUMMY_SP;
1954+
1955+
let (post_guard_block, otherwise_post_guard_block) =
1956+
self.in_if_then_scope(match_scope, |this| match *guard {
1957+
Guard::If(e) => {
1958+
let e = &this.thir[e];
1959+
guard_span = e.span;
1960+
this.then_else_break(block, e, arm_scope, match_scope, arm_span)
1961+
}
1962+
Guard::IfLet(ref pat, scrutinee) => {
1963+
let s = &this.thir[scrutinee];
1964+
guard_span = s.span;
1965+
this.lower_let_expr(block, s, pat, match_scope, arm_span)
1966+
}
1967+
});
1968+
19431969
let source_info = self.source_info(guard_span);
19441970
let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span));
19451971
let guard_frame = self.guard_context.pop().unwrap();
@@ -1955,10 +1981,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
19551981
self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable);
19561982
unreachable
19571983
});
1958-
let outside_scope = self.cfg.start_new_block();
1959-
self.exit_top_scope(otherwise_post_guard_block, outside_scope, source_info);
19601984
self.false_edges(
1961-
outside_scope,
1985+
otherwise_post_guard_block,
19621986
otherwise_block,
19631987
candidate.next_candidate_pre_binding_block,
19641988
source_info,

0 commit comments

Comments
 (0)