about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDing Xiang Fei <dingxiangfei2009@protonmail.ch>2022-07-21 00:35:12 +0800
committerDing Xiang Fei <dingxiangfei2009@protonmail.ch>2022-07-21 00:35:43 +0800
commit9b566401068cb8450912f6ab48f3d0e60f5cb482 (patch)
treef9eb8b8408b103afa5590f0c78d16d9412b4681e
parentd60d88fe5cd55496b9ccb1511a9af4994b7c43d0 (diff)
downloadrust-9b566401068cb8450912f6ab48f3d0e60f5cb482.tar.gz
rust-9b566401068cb8450912f6ab48f3d0e60f5cb482.zip
break out scopes when let-else fails to match
-rw-r--r--compiler/rustc_mir_build/src/build/block.rs1
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs86
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs4
-rw-r--r--src/test/ui/let-else/let-else-temp-borrowck.rs26
-rw-r--r--src/test/ui/let-else/let-else-temporary-lifetime.rs35
5 files changed, 110 insertions, 42 deletions
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs
index cb8be51a085..6875600129a 100644
--- a/compiler/rustc_mir_build/src/build/block.rs
+++ b/compiler/rustc_mir_build/src/build/block.rs
@@ -132,6 +132,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                                 initializer_span,
                                                 else_block,
                                                 visibility_scope,
+                                                *remainder_scope,
                                                 remainder_span,
                                                 pattern,
                                             )
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 7067a48b783..58b1564cc5d 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -2282,49 +2282,55 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         initializer_span: Span,
         else_block: &Block,
         visibility_scope: Option<SourceScope>,
+        remainder_scope: region::Scope,
         remainder_span: Span,
         pattern: &Pat<'tcx>,
     ) -> BlockAnd<()> {
-        let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span));
-        let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
-        let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
-        self.declare_bindings(
-            visibility_scope,
-            remainder_span,
-            pattern,
-            ArmHasGuard(false),
-            Some((None, initializer_span)),
-        );
-        let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
-        let fake_borrow_temps = self.lower_match_tree(
-            block,
-            initializer_span,
-            pattern.span,
-            false,
-            &mut [&mut candidate, &mut wildcard],
-        );
-        // This block is for the matching case
-        let matching = self.bind_pattern(
-            self.source_info(pattern.span),
-            candidate,
-            None,
-            &fake_borrow_temps,
-            initializer_span,
-            None,
-            None,
-            None,
-        );
-        // This block is for the failure case
-        let failure = self.bind_pattern(
-            self.source_info(else_block.span),
-            wildcard,
-            None,
-            &fake_borrow_temps,
-            initializer_span,
-            None,
-            None,
-            None,
-        );
+        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 mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
+            this.declare_bindings(
+                visibility_scope,
+                remainder_span,
+                pattern,
+                ArmHasGuard(false),
+                Some((None, initializer_span)),
+            );
+            let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
+            let fake_borrow_temps = this.lower_match_tree(
+                block,
+                initializer_span,
+                pattern.span,
+                false,
+                &mut [&mut candidate, &mut wildcard],
+            );
+            // This block is for the matching case
+            let matching = this.bind_pattern(
+                this.source_info(pattern.span),
+                candidate,
+                None,
+                &fake_borrow_temps,
+                initializer_span,
+                None,
+                None,
+                None,
+            );
+            // This block is for the failure case
+            let failure = this.bind_pattern(
+                this.source_info(else_block.span),
+                wildcard,
+                None,
+                &fake_borrow_temps,
+                initializer_span,
+                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
         // should never be used to take values at the end of the failure
         // block.
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index b9fd8c50e6a..b2fd9f25bdd 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -690,7 +690,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
         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
         // because MIR type checking will panic if it hasn't been overwritten.
         self.cfg.terminate(block, source_info, TerminatorKind::Resume);
@@ -722,7 +722,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
         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
         // because MIR type checking will panic if it hasn't been overwritten.
         self.cfg.terminate(block, source_info, TerminatorKind::Resume);
diff --git a/src/test/ui/let-else/let-else-temp-borrowck.rs b/src/test/ui/let-else/let-else-temp-borrowck.rs
new file mode 100644
index 00000000000..3910d35e776
--- /dev/null
+++ b/src/test/ui/let-else/let-else-temp-borrowck.rs
@@ -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();
+}
diff --git a/src/test/ui/let-else/let-else-temporary-lifetime.rs b/src/test/ui/let-else/let-else-temporary-lifetime.rs
index 624c2ea37a7..28c69ba1ce6 100644
--- a/src/test/ui/let-else/let-else-temporary-lifetime.rs
+++ b/src/test/ui/let-else/let-else-temporary-lifetime.rs
@@ -1,6 +1,7 @@
 // run-pass
 #![feature(let_else)]
 
+use std::rc::Rc;
 use std::sync::atomic::{AtomicU8, Ordering};
 
 static TRACKER: AtomicU8 = AtomicU8::new(0);
@@ -22,4 +23,38 @@ fn main() {
     let 0 = Droppy::default().inner else { return };
     assert_eq!(TRACKER.load(Ordering::Acquire), 1);
     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!();
+    }
 }