about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthew Jasper <mjjasper1@gmail.com>2024-01-03 16:32:13 +0000
committerMatthew Jasper <mjjasper1@gmail.com>2024-01-05 10:56:59 +0000
commit1a267e3f40c4c6e32482a7dd98c512f4664a329e (patch)
treea435dca89a742799998ceb8661bbead879bcdbc2
parenta549711f6e3dc804783652810a40653719dd0af7 (diff)
downloadrust-1a267e3f40c4c6e32482a7dd98c512f4664a329e.tar.gz
rust-1a267e3f40c4c6e32482a7dd98c512f4664a329e.zip
Restore if let guard temporary scoping difference
Match guards with an if let guard or an if let chain guard should have a
temporary scope of the whole arm. This is to allow ref bindings to
temporaries to borrow check.
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs13
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs6
-rw-r--r--tests/ui/rfcs/rfc-2294-if-let-guard/drop-scope.rs72
3 files changed, 89 insertions, 2 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index c3bdf94d30f..542e69a6c34 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -177,6 +177,14 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
 }
 
 fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
+    fn has_let_expr(expr: &Expr<'_>) -> bool {
+        match &expr.kind {
+            hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
+            hir::ExprKind::Let(..) => true,
+            _ => false,
+        }
+    }
+
     let prev_cx = visitor.cx;
 
     visitor.terminating_scopes.insert(arm.hir_id.local_id);
@@ -184,8 +192,9 @@ fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir
     visitor.enter_node_scope_with_dtor(arm.hir_id.local_id);
     visitor.cx.var_parent = visitor.cx.parent;
 
-    if let Some(expr) = arm.guard {
-        // Check for if??
+    if let Some(expr) = arm.guard
+        && !has_let_expr(expr)
+    {
         visitor.terminating_scopes.insert(expr.hir_id.local_id);
     }
 
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 483e70fd6f1..906b3205ca7 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -33,6 +33,12 @@ use std::borrow::Borrow;
 use std::mem;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
+    /// Lowers a condition in a way that ensures that variables bound in any let
+    /// expressions are definitely initialized in the if body.
+    ///
+    /// If `declare_bindings` is false then variables created in `let`
+    /// expressions will not be declared. This is for if let guards on arms with
+    /// an or pattern, where the guard is lowered multiple times.
     pub(crate) fn then_else_break(
         &mut self,
         mut block: BasicBlock,
diff --git a/tests/ui/rfcs/rfc-2294-if-let-guard/drop-scope.rs b/tests/ui/rfcs/rfc-2294-if-let-guard/drop-scope.rs
new file mode 100644
index 00000000000..9e6e23e8882
--- /dev/null
+++ b/tests/ui/rfcs/rfc-2294-if-let-guard/drop-scope.rs
@@ -0,0 +1,72 @@
+// Ensure that temporaries in if-let guards live for the arm
+// regression test for #118593
+
+// check-pass
+
+#![feature(if_let_guard)]
+#![feature(let_chains)]
+
+fn get_temp() -> Option<String> {
+    None
+}
+
+fn let_guard(num: u8) {
+    match num {
+        1 | 2 if let Some(ref a) = get_temp() => {
+            let _b = a;
+        }
+        _ => {}
+    }
+    match num {
+        3 | 4 if let Some(ref mut c) = get_temp() => {
+            let _d = c;
+        }
+        _ => {}
+    }
+}
+
+fn let_let_chain_guard(num: u8) {
+    match num {
+        5 | 6
+            if let Some(ref a) = get_temp()
+                && let Some(ref b) = get_temp() =>
+        {
+            let _x = a;
+            let _y = b;
+        }
+        _ => {}
+    }
+    match num {
+        7 | 8
+            if let Some(ref mut c) = get_temp()
+                && let Some(ref mut d) = get_temp() =>
+        {
+            let _w = c;
+            let _z = d;
+        }
+        _ => {}
+    }
+}
+
+fn let_cond_chain_guard(num: u8) {
+    match num {
+        9 | 10
+            if let Some(ref a) = get_temp()
+                && true =>
+        {
+            let _x = a;
+        }
+        _ => {}
+    }
+    match num {
+        11 | 12
+            if let Some(ref mut b) = get_temp()
+                && true =>
+        {
+            let _w = b;
+        }
+        _ => {}
+    }
+}
+
+fn main() {}