Skip to content

Commit 9b56640

Browse files
break out scopes when let-else fails to match
1 parent d60d88f commit 9b56640

File tree

5 files changed

+110
-42
lines changed

5 files changed

+110
-42
lines changed

compiler/rustc_mir_build/src/build/block.rs

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
132132
initializer_span,
133133
else_block,
134134
visibility_scope,
135+
*remainder_scope,
135136
remainder_span,
136137
pattern,
137138
)

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

+46-40
Original file line numberDiff line numberDiff line change
@@ -2282,49 +2282,55 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22822282
initializer_span: Span,
22832283
else_block: &Block,
22842284
visibility_scope: Option<SourceScope>,
2285+
remainder_scope: region::Scope,
22852286
remainder_span: Span,
22862287
pattern: &Pat<'tcx>,
22872288
) -> BlockAnd<()> {
2288-
let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span));
2289-
let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
2290-
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
2291-
self.declare_bindings(
2292-
visibility_scope,
2293-
remainder_span,
2294-
pattern,
2295-
ArmHasGuard(false),
2296-
Some((None, initializer_span)),
2297-
);
2298-
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
2299-
let fake_borrow_temps = self.lower_match_tree(
2300-
block,
2301-
initializer_span,
2302-
pattern.span,
2303-
false,
2304-
&mut [&mut candidate, &mut wildcard],
2305-
);
2306-
// This block is for the matching case
2307-
let matching = self.bind_pattern(
2308-
self.source_info(pattern.span),
2309-
candidate,
2310-
None,
2311-
&fake_borrow_temps,
2312-
initializer_span,
2313-
None,
2314-
None,
2315-
None,
2316-
);
2317-
// This block is for the failure case
2318-
let failure = self.bind_pattern(
2319-
self.source_info(else_block.span),
2320-
wildcard,
2321-
None,
2322-
&fake_borrow_temps,
2323-
initializer_span,
2324-
None,
2325-
None,
2326-
None,
2327-
);
2289+
let (matching, failure) = self.in_if_then_scope(remainder_scope, |this| {
2290+
let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span));
2291+
let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
2292+
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
2293+
this.declare_bindings(
2294+
visibility_scope,
2295+
remainder_span,
2296+
pattern,
2297+
ArmHasGuard(false),
2298+
Some((None, initializer_span)),
2299+
);
2300+
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
2301+
let fake_borrow_temps = this.lower_match_tree(
2302+
block,
2303+
initializer_span,
2304+
pattern.span,
2305+
false,
2306+
&mut [&mut candidate, &mut wildcard],
2307+
);
2308+
// This block is for the matching case
2309+
let matching = this.bind_pattern(
2310+
this.source_info(pattern.span),
2311+
candidate,
2312+
None,
2313+
&fake_borrow_temps,
2314+
initializer_span,
2315+
None,
2316+
None,
2317+
None,
2318+
);
2319+
// This block is for the failure case
2320+
let failure = this.bind_pattern(
2321+
this.source_info(else_block.span),
2322+
wildcard,
2323+
None,
2324+
&fake_borrow_temps,
2325+
initializer_span,
2326+
None,
2327+
None,
2328+
None,
2329+
);
2330+
this.break_for_else(failure, remainder_scope, this.source_info(initializer_span));
2331+
matching.unit()
2332+
});
2333+
23282334
// This place is not really used because this destination place
23292335
// should never be used to take values at the end of the failure
23302336
// block.

compiler/rustc_mir_build/src/build/scope.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
690690
}
691691
drops.add_entry(block, drop_idx);
692692

693-
// `build_drop_tree` doesn't have access to our source_info, so we
693+
// `build_drop_trees` doesn't have access to our source_info, so we
694694
// create a dummy terminator now. `TerminatorKind::Resume` is used
695695
// because MIR type checking will panic if it hasn't been overwritten.
696696
self.cfg.terminate(block, source_info, TerminatorKind::Resume);
@@ -722,7 +722,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
722722
}
723723
drops.add_entry(block, drop_idx);
724724

725-
// `build_drop_tree` doesn't have access to our source_info, so we
725+
// `build_drop_trees` doesn't have access to our source_info, so we
726726
// create a dummy terminator now. `TerminatorKind::Resume` is used
727727
// because MIR type checking will panic if it hasn't been overwritten.
728728
self.cfg.terminate(block, source_info, TerminatorKind::Resume);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// run-pass
2+
//
3+
// from issue #93951, where borrowck complained the temporary that `foo(&x)` was stored in was to
4+
// be dropped sometime after `x` was. It then suggested adding a semicolon that was already there.
5+
6+
#![feature(let_else)]
7+
use std::fmt::Debug;
8+
9+
fn foo<'a>(x: &'a str) -> Result<impl Debug + 'a, ()> {
10+
Ok(x)
11+
}
12+
13+
fn let_else() {
14+
let x = String::from("Hey");
15+
let Ok(_) = foo(&x) else { return };
16+
}
17+
18+
fn if_let() {
19+
let x = String::from("Hey");
20+
let _ = if let Ok(s) = foo(&x) { s } else { return };
21+
}
22+
23+
fn main() {
24+
let_else();
25+
if_let();
26+
}

src/test/ui/let-else/let-else-temporary-lifetime.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// run-pass
22
#![feature(let_else)]
33

4+
use std::rc::Rc;
45
use std::sync::atomic::{AtomicU8, Ordering};
56

67
static TRACKER: AtomicU8 = AtomicU8::new(0);
@@ -22,4 +23,38 @@ fn main() {
2223
let 0 = Droppy::default().inner else { return };
2324
assert_eq!(TRACKER.load(Ordering::Acquire), 1);
2425
println!("Should have dropped 👆");
26+
27+
{
28+
// test let-else drops temps after statement
29+
let rc = Rc::new(0);
30+
let 0 = *rc.clone() else { unreachable!() };
31+
Rc::try_unwrap(rc).unwrap();
32+
}
33+
{
34+
let mut rc = Rc::new(0);
35+
let mut i = 0;
36+
loop {
37+
if i > 3 {
38+
break;
39+
}
40+
let 1 = *rc.clone() else {
41+
if let Ok(v) = Rc::try_unwrap(rc) {
42+
rc = Rc::new(v);
43+
} else {
44+
panic!()
45+
}
46+
i += 1;
47+
continue
48+
};
49+
}
50+
}
51+
{
52+
// test let-else drops temps before else block
53+
let rc = Rc::new(0);
54+
let 1 = *rc.clone() else {
55+
Rc::try_unwrap(rc).unwrap();
56+
return;
57+
};
58+
unreachable!();
59+
}
2560
}

0 commit comments

Comments
 (0)