about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_mir_build/src/build/block.rs21
-rw-r--r--src/test/ui/let-else/let-else-temporary-lifetime.rs11
2 files changed, 30 insertions, 2 deletions
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs
index 6875600129a..280b6aad12c 100644
--- a/compiler/rustc_mir_build/src/build/block.rs
+++ b/compiler/rustc_mir_build/src/build/block.rs
@@ -1,6 +1,7 @@
 use crate::build::matches::ArmHasGuard;
 use crate::build::ForGuard::OutsideGuard;
 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
+use rustc_middle::middle::region::Scope;
 use rustc_middle::thir::*;
 use rustc_middle::{mir::*, ty};
 use rustc_span::Span;
@@ -34,10 +35,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             &stmts,
                             expr,
                             safety_mode,
+                            region_scope,
                         ))
                     })
                 } else {
-                    this.ast_block_stmts(destination, block, span, &stmts, expr, safety_mode)
+                    this.ast_block_stmts(
+                        destination,
+                        block,
+                        span,
+                        &stmts,
+                        expr,
+                        safety_mode,
+                        region_scope,
+                    )
                 }
             })
         })
@@ -51,6 +61,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         stmts: &[StmtId],
         expr: Option<&Expr<'tcx>>,
         safety_mode: BlockSafety,
+        region_scope: Scope,
     ) -> BlockAnd<()> {
         let this = self;
 
@@ -73,6 +84,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let mut let_scope_stack = Vec::with_capacity(8);
         let outer_source_scope = this.source_scope;
         let outer_in_scope_unsafe = this.in_scope_unsafe;
+        // This scope information is kept for breaking out of the parent remainder scope in case
+        // one let-else pattern matching fails.
+        // By doing so, we can be sure that even temporaries that receive extended lifetime
+        // assignments are dropped, too.
+        let mut last_remainder_scope = region_scope;
         this.update_source_scope_for_safety_mode(span, safety_mode);
 
         let source_info = this.source_info(span);
@@ -132,7 +148,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                                 initializer_span,
                                                 else_block,
                                                 visibility_scope,
-                                                *remainder_scope,
+                                                last_remainder_scope,
                                                 remainder_span,
                                                 pattern,
                                             )
@@ -178,6 +194,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     if let Some(source_scope) = visibility_scope {
                         this.source_scope = source_scope;
                     }
+                    last_remainder_scope = *remainder_scope;
                 }
             }
 
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 9c86901b97f..07fcc16e7bb 100644
--- a/src/test/ui/let-else/let-else-temporary-lifetime.rs
+++ b/src/test/ui/let-else/let-else-temporary-lifetime.rs
@@ -75,6 +75,17 @@ fn main() {
         }
     }
     {
+        fn must_pass() {
+            let rc = Rc::new(());
+            let &None = &Some(Rc::clone(&rc)) else {
+                Rc::try_unwrap(rc).unwrap();
+                return;
+            };
+            unreachable!();
+        }
+        must_pass();
+    }
+    {
         // test let-else drops temps before else block
         // NOTE: this test has to be the last block in the `main`
         // body.