diff options
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/region.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/middle/region.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/rvalue_scopes.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/matches/mod.rs | 78 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/scope.rs | 23 | ||||
| -rw-r--r-- | tests/ui/drop/if-let-guards.rs | 5 | ||||
| -rw-r--r-- | tests/ui/thir-print/thir-tree-loop-match.stdout | 16 | ||||
| -rw-r--r-- | tests/ui/thir-print/thir-tree-match.stdout | 12 |
8 files changed, 84 insertions, 63 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 95f6fba6487..d43ac053758 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -198,6 +198,11 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: visitor.cx.var_parent = visitor.cx.parent; resolve_pat(visitor, arm.pat); + // We introduce a new scope to contain bindings and temporaries from `if let` guards, to + // ensure they're dropped before the arm's pattern's bindings. This extends to the end of + // the arm body and is the scope of its locals as well. + visitor.enter_scope(Scope { local_id: arm.hir_id.local_id, data: ScopeData::MatchGuard }); + visitor.cx.var_parent = visitor.cx.parent; if let Some(guard) = arm.guard { resolve_cond(visitor, guard); } diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 800c1af660a..857d041224f 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -96,6 +96,7 @@ impl fmt::Debug for Scope { ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.local_id), ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.local_id), ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.local_id), + ScopeData::MatchGuard => write!(fmt, "MatchGuard({:?})", self.local_id), ScopeData::Remainder(fsi) => write!( fmt, "Remainder {{ block: {:?}, first_statement_index: {}}}", @@ -131,6 +132,11 @@ pub enum ScopeData { /// whose lifetimes do not cross beyond this scope. IfThenRescope, + /// Scope of the condition and body of a match arm with a guard + /// Used for variables introduced in an if-let guard, + /// whose lifetimes do not cross beyond this scope. + MatchGuard, + /// Scope following a `let id = expr;` binding in a block. Remainder(FirstStatementIndex), } diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs index 9bf6e3a7590..7dfe2d28051 100644 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ b/compiler/rustc_middle/src/ty/rvalue_scopes.rs @@ -44,7 +44,7 @@ impl RvalueScopes { debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]"); return (Some(id), backwards_incompatible); } - ScopeData::IfThenRescope => { + ScopeData::IfThenRescope | ScopeData::MatchGuard => { debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]"); return (Some(p), backwards_incompatible); } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index aebd78515a2..c9a561fc54a 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -435,46 +435,50 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_scope = (arm.scope, arm_source_info); let match_scope = self.local_scope(); self.in_scope(arm_scope, arm.lint_level, |this| { - let old_dedup_scope = - mem::replace(&mut this.fixed_temps_scope, Some(arm.scope)); - - // `try_to_place` may fail if it is unable to resolve the given - // `PlaceBuilder` inside a closure. In this case, we don't want to include - // a scrutinee place. `scrutinee_place_builder` will fail to be resolved - // if the only match arm is a wildcard (`_`). - // Example: - // ``` - // let foo = (0, 1); - // let c = || { - // match foo { _ => () }; - // }; - // ``` - let scrutinee_place = scrutinee_place_builder.try_to_place(this); - let opt_scrutinee_place = - scrutinee_place.as_ref().map(|place| (Some(place), scrutinee_span)); - let scope = this.declare_bindings( - None, - arm.span, - &arm.pattern, - arm.guard, - opt_scrutinee_place, - ); + let guard_scope = + region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }; + this.in_scope((guard_scope, arm_source_info), LintLevel::Inherited, |this| { + let old_dedup_scope = + mem::replace(&mut this.fixed_temps_scope, Some(guard_scope)); + + // `try_to_place` may fail if it is unable to resolve the given + // `PlaceBuilder` inside a closure. In this case, we don't want to include + // a scrutinee place. `scrutinee_place_builder` will fail to be resolved + // if the only match arm is a wildcard (`_`). + // Example: + // ``` + // let foo = (0, 1); + // let c = || { + // match foo { _ => () }; + // }; + // ``` + let scrutinee_place = scrutinee_place_builder.try_to_place(this); + let opt_scrutinee_place = + scrutinee_place.as_ref().map(|place| (Some(place), scrutinee_span)); + let scope = this.declare_bindings( + None, + arm.span, + &arm.pattern, + arm.guard, + opt_scrutinee_place, + ); - let arm_block = this.bind_pattern( - outer_source_info, - branch, - &built_match_tree.fake_borrow_temps, - scrutinee_span, - Some((arm, match_scope)), - ); + let arm_block = this.bind_pattern( + outer_source_info, + branch, + &built_match_tree.fake_borrow_temps, + scrutinee_span, + Some((arm, match_scope)), + ); - this.fixed_temps_scope = old_dedup_scope; + this.fixed_temps_scope = old_dedup_scope; - if let Some(source_scope) = scope { - this.source_scope = source_scope; - } + if let Some(source_scope) = scope { + this.source_scope = source_scope; + } - this.expr_into_dest(destination, arm_block, arm.body) + this.expr_into_dest(destination, arm_block, arm.body) + }) }) .into_block() }) @@ -2523,7 +2527,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // bindings and temporaries created for and by the guard. As a result, the drop order // for the arm will correspond to the binding order of the final sub-branch lowered. if matches!(schedule_drops, ScheduleDrops::No) { - self.clear_top_scope(arm.scope); + self.clear_match_arm_and_guard_scopes(arm.scope); } let source_info = self.source_info(guard_span); diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 1240b34cf9d..403e7167a69 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -1750,17 +1750,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block } - /// Unschedules any drops in the top scope. + /// Unschedules any drops in the top two scopes. /// - /// This is only needed for `match` arm scopes, because they have one - /// entrance per pattern, but only one exit. - pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) { - let top_scope = self.scopes.scopes.last_mut().unwrap(); + /// This is only needed for pattern-matches combining guards and or-patterns: or-patterns lead + /// to guards being lowered multiple times before lowering the arm body, so we unschedle drops + /// for guards' temporaries and bindings between lowering each instance of an match arm's guard. + pub(crate) fn clear_match_arm_and_guard_scopes(&mut self, region_scope: region::Scope) { + let [.., arm_scope, guard_scope] = &mut *self.scopes.scopes else { + bug!("matches with guards should introduce separate scopes for the pattern and guard"); + }; - assert_eq!(top_scope.region_scope, region_scope); + assert_eq!(arm_scope.region_scope, region_scope); + assert_eq!(guard_scope.region_scope.data, region::ScopeData::MatchGuard); + assert_eq!(guard_scope.region_scope.local_id, region_scope.local_id); - top_scope.drops.clear(); - top_scope.invalidate_cache(); + arm_scope.drops.clear(); + arm_scope.invalidate_cache(); + guard_scope.drops.clear(); + guard_scope.invalidate_cache(); } } diff --git a/tests/ui/drop/if-let-guards.rs b/tests/ui/drop/if-let-guards.rs index 918245e029c..bd353112c09 100644 --- a/tests/ui/drop/if-let-guards.rs +++ b/tests/ui/drop/if-let-guards.rs @@ -14,13 +14,12 @@ use core::{cell::RefCell, ops::Drop}; fn main() { // Test that `let` guard bindings and temps are dropped before the arm's pattern's bindings. - // TODO: this is currently the old behavior (`let` bindings dropped after arm bindings). assert_drop_order(1..=6, |o| { // We move out of the scrutinee, so the drop order of the array's elements are based on // binding declaration order, and they're dropped in the arm's scope. - match [o.log(3), o.log(2)] { + match [o.log(6), o.log(5)] { // Partially move from the guard temporary to test drops both for temps and the binding. - [_x, _y] if let [_, _z, _] = [o.log(6), o.log(4), o.log(5)] + [_x, _y] if let [_, _z, _] = [o.log(4), o.log(2), o.log(3)] && true => { let _a = o.log(1); } _ => unreachable!(), } diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index 5c4c50cb156..86d37e987e2 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -129,7 +129,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: Scope { @@ -138,14 +138,14 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: NeverToAny { source: Expr { ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) kind: Block { @@ -227,7 +227,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: Scope { @@ -236,21 +236,21 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: NeverToAny { source: Expr { ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) kind: Return { value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) kind: Scope { @@ -259,7 +259,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(24)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) kind: VarRef { diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 2049c531abd..31b9b3b9995 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -123,7 +123,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Scope { @@ -132,7 +132,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false) @@ -175,7 +175,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Scope { @@ -184,7 +184,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false) @@ -219,7 +219,7 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Scope { @@ -228,7 +228,7 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(MatchGuard(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false) |
