break out scopes when let-else fails to match

This commit is contained in:
Ding Xiang Fei 2022-07-21 00:35:12 +08:00
parent d60d88fe5c
commit 9b56640106
No known key found for this signature in database
GPG Key ID: 3CD748647EEF6359
5 changed files with 110 additions and 42 deletions

View File

@ -132,6 +132,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
initializer_span, initializer_span,
else_block, else_block,
visibility_scope, visibility_scope,
*remainder_scope,
remainder_span, remainder_span,
pattern, pattern,
) )

View File

@ -2282,13 +2282,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
initializer_span: Span, initializer_span: Span,
else_block: &Block, else_block: &Block,
visibility_scope: Option<SourceScope>, visibility_scope: Option<SourceScope>,
remainder_scope: region::Scope,
remainder_span: Span, remainder_span: Span,
pattern: &Pat<'tcx>, pattern: &Pat<'tcx>,
) -> BlockAnd<()> { ) -> BlockAnd<()> {
let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span)); let (matching, failure) = self.in_if_then_scope(remainder_scope, |this| {
let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span));
let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) }; let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false); let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
self.declare_bindings( this.declare_bindings(
visibility_scope, visibility_scope,
remainder_span, remainder_span,
pattern, pattern,
@ -2296,7 +2298,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Some((None, initializer_span)), Some((None, initializer_span)),
); );
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false); let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
let fake_borrow_temps = self.lower_match_tree( let fake_borrow_temps = this.lower_match_tree(
block, block,
initializer_span, initializer_span,
pattern.span, pattern.span,
@ -2304,8 +2306,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut [&mut candidate, &mut wildcard], &mut [&mut candidate, &mut wildcard],
); );
// This block is for the matching case // This block is for the matching case
let matching = self.bind_pattern( let matching = this.bind_pattern(
self.source_info(pattern.span), this.source_info(pattern.span),
candidate, candidate,
None, None,
&fake_borrow_temps, &fake_borrow_temps,
@ -2315,8 +2317,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None, None,
); );
// This block is for the failure case // This block is for the failure case
let failure = self.bind_pattern( let failure = this.bind_pattern(
self.source_info(else_block.span), this.source_info(else_block.span),
wildcard, wildcard,
None, None,
&fake_borrow_temps, &fake_borrow_temps,
@ -2325,6 +2327,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
None, None,
None, None,
); );
this.break_for_else(failure, remainder_scope, this.source_info(initializer_span));
matching.unit()
});
// This place is not really used because this destination place // This place is not really used because this destination place
// should never be used to take values at the end of the failure // should never be used to take values at the end of the failure
// block. // block.

View File

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

View File

@ -0,0 +1,26 @@
// run-pass
//
// from issue #93951, where borrowck complained the temporary that `foo(&x)` was stored in was to
// be dropped sometime after `x` was. It then suggested adding a semicolon that was already there.
#![feature(let_else)]
use std::fmt::Debug;
fn foo<'a>(x: &'a str) -> Result<impl Debug + 'a, ()> {
Ok(x)
}
fn let_else() {
let x = String::from("Hey");
let Ok(_) = foo(&x) else { return };
}
fn if_let() {
let x = String::from("Hey");
let _ = if let Ok(s) = foo(&x) { s } else { return };
}
fn main() {
let_else();
if_let();
}

View File

@ -1,6 +1,7 @@
// run-pass // run-pass
#![feature(let_else)] #![feature(let_else)]
use std::rc::Rc;
use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::atomic::{AtomicU8, Ordering};
static TRACKER: AtomicU8 = AtomicU8::new(0); static TRACKER: AtomicU8 = AtomicU8::new(0);
@ -22,4 +23,38 @@ fn main() {
let 0 = Droppy::default().inner else { return }; let 0 = Droppy::default().inner else { return };
assert_eq!(TRACKER.load(Ordering::Acquire), 1); assert_eq!(TRACKER.load(Ordering::Acquire), 1);
println!("Should have dropped 👆"); println!("Should have dropped 👆");
{
// test let-else drops temps after statement
let rc = Rc::new(0);
let 0 = *rc.clone() else { unreachable!() };
Rc::try_unwrap(rc).unwrap();
}
{
let mut rc = Rc::new(0);
let mut i = 0;
loop {
if i > 3 {
break;
}
let 1 = *rc.clone() else {
if let Ok(v) = Rc::try_unwrap(rc) {
rc = Rc::new(v);
} else {
panic!()
}
i += 1;
continue
};
}
}
{
// test let-else drops temps before else block
let rc = Rc::new(0);
let 1 = *rc.clone() else {
Rc::try_unwrap(rc).unwrap();
return;
};
unreachable!();
}
} }