diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index e31beca390837..1dc655f372313 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -213,6 +213,13 @@ fn resolve_arm<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, arm: &'tcx hir: #[tracing::instrument(level = "debug", skip(visitor))] fn resolve_pat<'tcx>(visitor: &mut ScopeResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { + // We walk the whole pattern here to avoid walking `cond` expr another time on `walk_pat` + if let PatKind::Guard(pat, cond) = pat.kind { + resolve_cond(visitor, cond); + resolve_pat(visitor, pat); + return; + } + // If this is a binding then record the lifetime of that binding. if let PatKind::Binding(..) = pat.kind { record_var_lifetime(visitor, pat.hir_id.local_id); diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index f3d0b4d000c28..ab0c000a056c8 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -947,13 +947,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx read_discriminant(); } } + PatKind::Or(_) | PatKind::Box(_) | PatKind::Ref(..) - | PatKind::Guard(..) | PatKind::Tuple(..) | PatKind::Wild | PatKind::Missing + | PatKind::Guard(..) | PatKind::Err(_) => { // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses // are made later as these patterns contains subpatterns. diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index bd18a215aea70..a57257608dda5 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -138,6 +138,8 @@ impl<'tcx> MatchPairTree<'tcx> { PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, PatKind::Or { ref pats } => { + use super::OrderedPatternData; + let pats: Box<[FlatPat<'tcx>]> = pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(); if !pats[0].extra_data.bindings.is_empty() { @@ -146,7 +148,10 @@ impl<'tcx> MatchPairTree<'tcx> { // or-patterns that will be simplified by `merge_trivial_subcandidates`. In // other words, we can assume this expands into subcandidates. // FIXME(@dianne): this needs updating/removing if we always merge or-patterns - extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); + extra_data.bindings.push(OrderedPatternData::FromOrPattern); + } + if pats.iter().any(|pat| !pat.extra_data.guard_patterns.is_empty()) { + extra_data.guard_patterns.push(super::OrderedPatternData::FromOrPattern); } Some(TestableCase::Or { pats }) } @@ -217,7 +222,7 @@ impl<'tcx> MatchPairTree<'tcx> { // Then push this binding, after any bindings in the subpattern. if let Some(source) = place { - extra_data.bindings.push(super::SubpatternBindings::One(super::Binding { + extra_data.bindings.push(super::OrderedPatternData::One(super::Binding { span: pattern.span, source, var_id: var, @@ -361,9 +366,10 @@ impl<'tcx> MatchPairTree<'tcx> { Some(TestableCase::Deref { temp, mutability }) } - PatKind::Guard { .. } => { - // FIXME(guard_patterns) - None + PatKind::Guard { ref subpattern, condition } => { + MatchPairTree::for_pattern(place_builder, subpattern, cx, match_pairs, extra_data); + extra_data.guard_patterns.push(super::OrderedPatternData::One(condition)); + return; } PatKind::Never => Some(TestableCase::Never), diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 5604e86e06722..02882fa831bee 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -14,7 +14,7 @@ use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node}; -use rustc_middle::middle::region::{self, TempLifetime}; +use rustc_middle::middle::region::{self, Scope, TempLifetime}; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind}; @@ -365,8 +365,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .iter() .map(|&arm| { let arm = &self.thir[arm]; - let has_match_guard = - if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No }; + let has_match_guard = if let Some(guard) = arm.guard { + HasMatchGuard::Yes(guard) + } else { + HasMatchGuard::No + }; (&*arm.pattern, has_match_guard) }) .collect(); @@ -430,9 +433,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_source_info = self.source_info(arm.span); let arm_scope = (arm.scope, arm_source_info); let match_scope = self.local_scope(); - let guard_scope = arm - .guard - .map(|_| region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }); + let guard_scope = if arm.guard.is_some() || self.pat_has_guard(&arm.pattern) { + Some(region::Scope { data: region::ScopeData::MatchGuard, ..arm.scope }) + } else { + None + }; self.in_scope(arm_scope, LintLevel::Explicit(arm.hir_id), |this| { this.opt_in_scope(guard_scope.map(|scope| (scope, arm_source_info)), |this| { // `if let` guard temps needing deduplicating will be in the guard scope. @@ -466,7 +471,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { branch, &built_match_tree.fake_borrow_temps, scrutinee_span, - Some((arm, match_scope)), + Some(arm), + match_scope, ); this.fixed_temps_scope = old_dedup_scope; @@ -501,7 +507,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// For a top-level `match` arm or a `let` binding, binds the variables and - /// ascribes types, and also checks the match arm guard (if present). + /// ascribes types, and also checks the match arm/pattern guards (if present). /// /// `arm_scope` should be `Some` if and only if this is called for a /// `match` arm. @@ -520,7 +526,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { branch: MatchTreeBranch<'tcx>, fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, - arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, + arm: Option<&Arm<'tcx>>, + match_scope: region::Scope, ) -> BasicBlock { if branch.sub_branches.len() == 1 { let [sub_branch] = branch.sub_branches.try_into().unwrap(); @@ -529,7 +536,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { sub_branch, fake_borrow_temps, scrutinee_span, - arm_match_scope, + arm, + match_scope, ScheduleDrops::Yes, ) } else { @@ -558,7 +566,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { sub_branch, fake_borrow_temps, scrutinee_span, - arm_match_scope, + arm, + match_scope, schedule_drops, ); self.cfg.goto(binding_end, outer_source_info, target_block); @@ -689,12 +698,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + // FIXME(guard_patterns) + let match_scope = self.local_scope(); self.bind_pattern( self.source_info(irrefutable_pat.span), branch, &[], irrefutable_pat.span, None, + match_scope, ) .unit() } @@ -712,6 +724,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard: Option, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option { + let has_guard = guard.is_some() || self.pat_has_guard(pattern); self.visit_primary_bindings_special( pattern, &ProjectedUserTypesNode::None, @@ -731,13 +744,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { var, ty, user_tys, - ArmHasGuard(guard.is_some()), + ArmHasGuard(has_guard), opt_match_place.map(|(x, y)| (x.cloned(), y)), pattern.span, ); this.source_scope = saved_scope; }, ); + // FIXME(guard_patterns): same kind of handling will be need to support `if let` guards. if let Some(guard_expr) = guard { self.declare_guard_bindings(guard_expr, scope_span, visibility_scope); } @@ -772,6 +786,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + fn pat_has_guard(&self, pat: &Pat<'tcx>) -> bool { + let mut has_guard = false; + pat.walk(|pat| { + if matches!(pat.kind, PatKind::Guard { .. }) { + has_guard = true; + }; + // If has_guard is true, we just stop walking other patterns + !has_guard + }); + has_guard + } + /// Emits a [`StatementKind::StorageLive`] for the given var, and also /// schedules a drop if requested (and possible). pub(crate) fn storage_live_binding( @@ -959,27 +985,34 @@ struct PatternExtraData<'tcx> { span: Span, /// Bindings that must be established. - bindings: Vec>, + bindings: Vec>>, /// Types that must be asserted. ascriptions: Vec>, /// Whether this corresponds to a never pattern. is_never: bool, + + /// [`ExprId`]s of subpattern conditions + guard_patterns: Vec>, } impl<'tcx> PatternExtraData<'tcx> { fn is_empty(&self) -> bool { - self.bindings.is_empty() && self.ascriptions.is_empty() + self.bindings.is_empty() && self.ascriptions.is_empty() && self.guard_patterns.is_empty() } } +/// This is used for patterns where order-preserving behavior related with `|` matters +/// such as bindings or guard patterns +/// +/// See [`sub_branch_ordered_pat_data`] #[derive(Debug, Clone)] -enum SubpatternBindings<'tcx> { - /// A single binding. - One(Binding<'tcx>), - /// Holds the place for an or-pattern's bindings. This ensures their drops are scheduled in the - /// order the primary bindings appear. See rust-lang/rust#142163 for more information. +enum OrderedPatternData { + /// A single guard pat/binding. + One(T), + /// Holds the place for an or-pattern's guard pat/binding. This ensures their drops are scheduled in the + /// order those items appear. See rust-lang/rust#142163 for more information. FromOrPattern, } @@ -1010,6 +1043,7 @@ impl<'tcx> FlatPat<'tcx> { bindings: Vec::new(), ascriptions: Vec::new(), is_never: pattern.is_never_pattern(), + guard_patterns: Vec::new(), }; MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data); @@ -1068,12 +1102,12 @@ struct Candidate<'tcx> { /// - See [`Builder::remove_never_subcandidates`]. subcandidates: Vec>, - /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`. + /// ...and if there are arm guards they must be evaluated; if they're `false` then branch to + /// `otherwise_block`. /// /// --- - /// For subcandidates, this is copied from the parent candidate, so it indicates - /// whether the enclosing match arm has a guard. - has_guard: bool, + /// For subcandidates, this is copied from the parent candidate + guards: Vec, /// Holds extra pattern data that was prepared by [`FlatPat`], including bindings and /// ascriptions that must be established if this candidate succeeds. @@ -1105,23 +1139,25 @@ impl<'tcx> Candidate<'tcx> { fn new( place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, - has_guard: HasMatchGuard, + guard: HasMatchGuard, cx: &mut Builder<'_, 'tcx>, ) -> Self { // Use `FlatPat` to build simplified match pairs, then immediately // incorporate them into a new candidate. - Self::from_flat_pat( - FlatPat::new(place, pattern, cx), - matches!(has_guard, HasMatchGuard::Yes), - ) + let flat_pat = FlatPat::new(place, pattern, cx); + let mut guards = Vec::new(); + if let HasMatchGuard::Yes(g) = guard { + guards.push(g); + } + Self::from_flat_pat(flat_pat, guards) } /// Incorporates an already-simplified [`FlatPat`] into a new candidate. - fn from_flat_pat(flat_pat: FlatPat<'tcx>, has_guard: bool) -> Self { + fn from_flat_pat(flat_pat: FlatPat<'tcx>, guards: Vec) -> Self { let mut this = Candidate { match_pairs: flat_pat.match_pairs, extra_data: flat_pat.extra_data, - has_guard, + guards, subcandidates: Vec::new(), or_span: None, otherwise_block: None, @@ -1420,6 +1456,8 @@ struct MatchTreeSubBranch<'tcx> { bindings: Vec>, /// The ascriptions to set up in this sub-branch. ascriptions: Vec>, + /// The guards from arms and patterns combined present in this sub-branch. + guards: Vec, /// Whether the sub-branch corresponds to a never pattern. is_never: bool, } @@ -1464,13 +1502,23 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { span: candidate.extra_data.span, success_block: candidate.pre_binding_block.unwrap(), otherwise_block: candidate.otherwise_block.unwrap(), - bindings: sub_branch_bindings(parent_data, &candidate.extra_data.bindings), + bindings: sub_branch_ordered_pat_data( + parent_data.iter().map(|parent| parent.bindings.as_slice()), + &candidate.extra_data.bindings, + ), ascriptions: parent_data .iter() .flat_map(|d| &d.ascriptions) .cloned() .chain(candidate.extra_data.ascriptions) .collect(), + guards: sub_branch_ordered_pat_data( + parent_data.iter().map(|parent| parent.guard_patterns.as_slice()), + &candidate.extra_data.guard_patterns, + ) + .into_iter() + .chain(candidate.guards) + .collect(), is_never: candidate.extra_data.is_never, } } @@ -1497,61 +1545,65 @@ impl<'tcx> MatchTreeBranch<'tcx> { } } -/// Collects the bindings for a [`MatchTreeSubBranch`], preserving the order they appear in the +/// Collects the bindings/guard patterns for a [`MatchTreeSubBranch`], preserving the order they appear in the /// pattern, as though the or-alternatives chosen in this sub-branch were inlined. -fn sub_branch_bindings<'tcx>( - parents: &[PatternExtraData<'tcx>], - leaf_bindings: &[SubpatternBindings<'tcx>], -) -> Vec> { - // In the common case, all bindings will be in leaves. Allocate to fit the leaf's bindings. - let mut all_bindings = Vec::with_capacity(leaf_bindings.len()); - let mut remainder = parents - .iter() - .map(|parent| parent.bindings.as_slice()) - .chain([leaf_bindings]) - // Skip over unsimplified or-patterns without bindings. - .filter(|bindings| !bindings.is_empty()); - if let Some(candidate_bindings) = remainder.next() { - push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder); +/// +/// *Note: this was introduced in [#143764](https://github.com/rust-lang/rust/pull/143764) to be used for bindings, +/// but has been generalized later to also be utilized for guard patterns* +fn sub_branch_ordered_pat_data<'a, T: Copy>( + remainder: impl Iterator]>, + leaf_items: &'a [OrderedPatternData], +) -> Vec { + // In the common case, all bindings/guard patterns patterns will be in leaves. Allocate to fit the leaf's items. + let mut all_items = Vec::with_capacity(leaf_items.len()); + let mut remainder = remainder + .chain([leaf_items]) + // Skip over unsimplified or-patterns without bindings/guard patterns. + .filter(|item| !item.is_empty()); + if let Some(candidate_item) = remainder.next() { + push_sub_branch_ordered_pat_data(&mut all_items, candidate_item, &mut remainder); } - // Make sure we've included all bindings. For ill-formed patterns like `(x, _ | y)`, we may not - // have collected all bindings yet, since we only check the first alternative when determining - // whether to inline subcandidates' bindings. + // Make sure we've included all bindings/guard patterns. For ill-formed patterns like `(x, _ | y)`, we may not + // have collected all bindings/guard patterns yet, since we only check the first alternative when determining + // whether to inline subcandidates' bindings/guard patterns. // FIXME(@dianne): prevent ill-formed patterns from getting here - while let Some(candidate_bindings) = remainder.next() { + while let Some(candidate_items) = remainder.next() { ty::tls::with(|tcx| { - tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted") + tcx.dcx() + .delayed_bug("mismatched or-pattern bindings/guard patterns but no error emitted") }); // To recover, we collect the rest in an arbitrary order. - push_sub_branch_bindings(&mut all_bindings, candidate_bindings, &mut remainder); + push_sub_branch_ordered_pat_data(&mut all_items, candidate_items, &mut remainder); } - all_bindings + all_items } -/// Helper for [`sub_branch_bindings`]. Collects bindings from `candidate_bindings` into -/// `flattened`. Bindings in or-patterns are collected recursively from `remainder`. -fn push_sub_branch_bindings<'c, 'tcx: 'c>( - flattened: &mut Vec>, - candidate_bindings: &'c [SubpatternBindings<'tcx>], - remainder: &mut impl Iterator]>, +/// Helper for [`sub_branch_ordered_pat_data`]. Collects bindings/guard patterns from `candidate_items` into +/// `flattened`. Those items in or-patterns are collected recursively from `remainder`. +fn push_sub_branch_ordered_pat_data<'c, T: Copy>( + flattened: &mut Vec, + candidate_items: &'c [OrderedPatternData], + remainder: &mut impl Iterator]>, ) { - for subpat_bindings in candidate_bindings { - match subpat_bindings { - SubpatternBindings::One(binding) => flattened.push(*binding), - SubpatternBindings::FromOrPattern => { - // Inline bindings from an or-pattern. By construction, this always + for subpat_items in candidate_items { + match subpat_items { + OrderedPatternData::One(item) => flattened.push(*item), + OrderedPatternData::FromOrPattern => { + // Inline bindings/guard patterns from an or-pattern. By construction, this always // corresponds to a subcandidate and its closest descendants (i.e. those // from nested or-patterns, but not adjacent or-patterns). To handle // adjacent or-patterns, e.g. `(x | x, y | y)`, we update the `remainder` to // point to the first descendant candidate from outside this or-pattern. - if let Some(subcandidate_bindings) = remainder.next() { - push_sub_branch_bindings(flattened, subcandidate_bindings, remainder); + if let Some(subcandidate_items) = remainder.next() { + push_sub_branch_ordered_pat_data(flattened, subcandidate_items, remainder); } else { // For ill-formed patterns like `x | _`, we may not have any subcandidates left // to inline bindings from. // FIXME(@dianne): prevent ill-formed patterns from getting here ty::tls::with(|tcx| { - tcx.dcx().delayed_bug("mismatched or-pattern bindings but no error emitted") + tcx.dcx().delayed_bug( + "mismatched or-pattern bindings/guard patterns but no error emitted", + ) }); }; } @@ -1561,7 +1613,7 @@ fn push_sub_branch_bindings<'c, 'tcx: 'c>( #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum HasMatchGuard { - Yes, + Yes(ExprId), No, } @@ -1615,7 +1667,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // false edge to the final failure block. let mut next_candidate_start_block = if refutable { Some(otherwise_block) } else { None }; for candidate in candidates.iter_mut().rev() { - let has_guard = candidate.has_guard; + let has_guard = !candidate.guards.is_empty(); candidate.visit_leaves_rev(|leaf_candidate| { if let Some(next_candidate_start_block) = next_candidate_start_block { let source_info = self.source_info(leaf_candidate.extra_data.span); @@ -1962,7 +2014,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.or_span = Some(match_pair.pattern_span); candidate.subcandidates = pats .into_iter() - .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) + .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.guards.clone())) .collect(); candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; } @@ -2026,7 +2078,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// in match tree lowering. fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'tcx>) { assert!(!candidate.subcandidates.is_empty()); - if candidate.has_guard { + if !candidate.guards.is_empty() || !candidate.extra_data.guard_patterns.is_empty() { // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; } @@ -2146,7 +2198,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // directly to `last_otherwise`. If there is a guard, // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we // can't skip `Q`. - let or_otherwise = if leaf_candidate.has_guard { + let or_otherwise = if !leaf_candidate.guards.is_empty() { leaf_candidate.otherwise_block.unwrap() } else { last_otherwise.unwrap() @@ -2386,7 +2438,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - let success = self.bind_pattern(self.source_info(pat.span), branch, &[], expr_span, None); + // FIXME(guard_patterns) + let match_scope = self.local_scope(); + let success = self.bind_pattern( + self.source_info(pat.span), + branch, + &[], + expr_span, + None, + match_scope, + ); // If branch coverage is enabled, record this branch. self.visit_coverage_conditional_let(pat, success, built_tree.otherwise_block); @@ -2395,7 +2456,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } /// Initializes each of the bindings from the candidate by - /// moving/copying/ref'ing the source as appropriate. Tests the guard, if + /// moving/copying/ref'ing the source as appropriate. Tests the guards, if /// any, and then branches to the arm. Returns the block for the case where /// the guard succeeds. /// @@ -2407,7 +2468,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { sub_branch: MatchTreeSubBranch<'tcx>, fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)], scrutinee_span: Span, - arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>, + arm: Option<&Arm<'tcx>>, + match_scope: region::Scope, schedule_drops: ScheduleDrops, ) -> BasicBlock { debug!("bind_and_guard_matched_candidate(subbranch={:?})", sub_branch); @@ -2424,13 +2486,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.ascribe_types(block, sub_branch.ascriptions); - // Lower an instance of the arm guard (if present) for this candidate, + let guards = sub_branch.guards; + + // Lower guards of this sub-branch (if present) for this candidate, // and then perform bindings for the arm body. - if let Some((arm, match_scope)) = arm_match_scope - && let Some(guard) = arm.guard - { + if !guards.is_empty() { let tcx = self.tcx; + let (arm_span, arm_scope) = self.extract_arm_span_scope(sub_branch.span, arm); + // Bindings for guards require some extra handling to automatically // insert implicit references/dereferences. // This always schedules storage drops, so we may need to unschedule them below. @@ -2452,29 +2516,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow); } - let mut guard_span = rustc_span::DUMMY_SP; - let (post_guard_block, otherwise_post_guard_block) = - self.in_if_then_scope(match_scope, guard_span, |this| { - guard_span = this.thir[guard].span; - this.then_else_break( - block, - guard, - None, // Use `self.local_scope()` as the temp scope - this.source_info(arm.span), - DeclareLetBindings::No, // For guards, `let` bindings are declared separately - ) + self.in_if_then_scope(match_scope, arm_span, |this| { + guards.into_iter().fold(block.unit(), |block, guard| { + this.then_else_break( + block.0, + guard, + None, // Use `self.local_scope()` as the temp scope + this.source_info(arm_span), + DeclareLetBindings::No, // For guards, `let` bindings are declared separately + ) + }) }); // If this isn't the final sub-branch being lowered, we need to unschedule drops of // 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_match_arm_and_guard_scopes(arm.scope); + self.clear_match_arm_and_guard_scopes(arm_scope); } - let source_info = self.source_info(guard_span); - let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span)); + let source_info = self.source_info(arm_span); + let guard_end = self.source_info(tcx.sess.source_map().end_point(arm_span)); let guard_frame = self.guard_context.pop().unwrap(); debug!("Exiting guard building context with locals: {:?}", guard_frame); @@ -2543,6 +2606,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + fn extract_arm_span_scope( + &self, + sub_branch_span: Span, + arm: Option<&Arm<'tcx>>, + ) -> (Span, Scope) { + arm.map(|arm| { + let span = arm.guard.map(|guard| self.thir[guard].span).unwrap_or(arm.span); + (span, arm.scope) + }) + // FIXME(guard_patterns) + .unwrap_or_else(|| (sub_branch_span, self.local_scope())) + } + /// Append `AscribeUserType` statements onto the end of `block` /// for each ascription fn ascribe_types( diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index f1df90f93fd63..1dc7cc7ca1810 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -72,7 +72,7 @@ pub(super) fn collect_fake_borrows<'tcx>( temp_span: Span, scrutinee_base: PlaceBase, ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> { - if candidates.iter().all(|candidate| !candidate.has_guard) { + if candidates.iter().all(|candidate| candidate.guards.is_empty()) { // Fake borrows are only used when there is a guard. return Vec::new(); } @@ -138,7 +138,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) { for binding in &candidate.extra_data.bindings { - if let super::SubpatternBindings::One(binding) = binding { + if let super::OrderedPatternData::One(binding) = binding { self.visit_binding(binding); } } @@ -149,7 +149,7 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'tcx>) { for binding in &flat_pat.extra_data.bindings { - if let super::SubpatternBindings::One(binding) = binding { + if let super::OrderedPatternData::One(binding) = binding { self.visit_binding(binding); } } diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs index 21edb9ceeff48..83ad8c76bb1cf 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs @@ -6,7 +6,6 @@ #![expect(incomplete_features)] fn good_fn_item(((x if x) | x): bool) -> bool { x } -//~^ ERROR: used binding `x` is possibly-uninitialized [E0381] fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} //~^ ERROR cannot find value `x` in this scope diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr index a95ffdd42532f..44e42f1427074 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `y` is not bound in all patterns - --> $DIR/name-resolution.rs:38:10 + --> $DIR/name-resolution.rs:37:10 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^^^^^^^^^^^^ - variable not in all patterns @@ -13,7 +13,7 @@ LL + ((Ok(x) if y) | (Err(x) if x),) => x && y, | error[E0408]: variable `x` is not bound in all patterns - --> $DIR/name-resolution.rs:38:25 + --> $DIR/name-resolution.rs:37:25 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | - ^^^^^^^^^^^^^ pattern doesn't bind `x` @@ -27,7 +27,7 @@ LL + ((Ok(y) if y) | (Err(y) if x),) => x && y, | error[E0408]: variable `x` is not bound in all patterns - --> $DIR/name-resolution.rs:64:28 + --> $DIR/name-resolution.rs:63:28 | LL | Some(x if x > 0) | None => {} | - ^^^^ pattern doesn't bind `x` @@ -35,7 +35,7 @@ LL | Some(x if x > 0) | None => {} | variable not in all patterns error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:11:34 + --> $DIR/name-resolution.rs:10:34 | LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} | ^ @@ -47,7 +47,7 @@ LL + fn bad_fn_item_1(x: bool, ((y if y) | y): bool) {} | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:13:25 + --> $DIR/name-resolution.rs:12:25 | LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} | ^ @@ -59,7 +59,7 @@ LL + fn bad_fn_item_2(((x if x) | x): bool, y: bool) {} | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:21:18 + --> $DIR/name-resolution.rs:20:18 | LL | (x, y if x) => x && y, | ^ @@ -71,7 +71,7 @@ LL + (x, y if y) => x && y, | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:23:15 + --> $DIR/name-resolution.rs:22:15 | LL | (x if y, y) => x && y, | ^ @@ -83,7 +83,7 @@ LL + (x if x, y) => x && y, | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:30:20 + --> $DIR/name-resolution.rs:29:20 | LL | (x @ (y if x),) => x && y, | ^ @@ -95,7 +95,7 @@ LL + (x @ (y if y),) => x && y, | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:38:20 + --> $DIR/name-resolution.rs:37:20 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^ @@ -107,7 +107,7 @@ LL + ((Ok(x) if x) | (Err(y) if x),) => x && y, | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:38:36 + --> $DIR/name-resolution.rs:37:36 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^ @@ -119,13 +119,13 @@ LL + ((Ok(x) if y) | (Err(y) if y),) => x && y, | error[E0425]: cannot find value `nonexistent` in this scope - --> $DIR/name-resolution.rs:45:15 + --> $DIR/name-resolution.rs:44:15 | LL | let (_ if nonexistent) = true; | ^^^^^^^^^^^ not found in this scope error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:47:22 + --> $DIR/name-resolution.rs:46:22 | LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -137,7 +137,7 @@ LL + if let ((x, y if y) | (x if y, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:47:33 + --> $DIR/name-resolution.rs:46:33 | LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -149,7 +149,7 @@ LL + if let ((x, y if x) | (x if x, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:50:25 + --> $DIR/name-resolution.rs:49:25 | LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -161,7 +161,7 @@ LL + while let ((x, y if y) | (x if y, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:50:36 + --> $DIR/name-resolution.rs:49:36 | LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -173,7 +173,7 @@ LL + while let ((x, y if x) | (x if x, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:53:19 + --> $DIR/name-resolution.rs:52:19 | LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } | ^ @@ -185,7 +185,7 @@ LL + for ((x, y if y) | (x if y, y)) in [(true, true)] { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:53:30 + --> $DIR/name-resolution.rs:52:30 | LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } | ^ @@ -197,7 +197,7 @@ LL + for ((x, y if x) | (x if x, y)) in [(true, true)] { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:58:13 + --> $DIR/name-resolution.rs:57:13 | LL | (|(x if y), (y if x)| x && y)(true, true); | ^ @@ -209,7 +209,7 @@ LL + (|(x if x), (y if x)| x && y)(true, true); | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:58:23 + --> $DIR/name-resolution.rs:57:23 | LL | (|(x if y), (y if x)| x && y)(true, true); | ^ @@ -221,7 +221,7 @@ LL + (|(x if y), (y if y)| x && y)(true, true); | error[E0308]: mismatched types - --> $DIR/name-resolution.rs:76:18 + --> $DIR/name-resolution.rs:75:18 | LL | local if local => 0, | ^^^^^ expected `bool`, found `({integer}, {integer})` @@ -229,16 +229,7 @@ LL | local if local => 0, = note: expected type `bool` found tuple `({integer}, {integer})` -error[E0381]: used binding `x` is possibly-uninitialized - --> $DIR/name-resolution.rs:8:49 - | -LL | fn good_fn_item(((x if x) | x): bool) -> bool { x } - | - - ^ `x` used here but it is possibly-uninitialized - | | | - | | binding initialized here in some conditions - | binding declared here but left uninitialized - -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors -Some errors have detailed explanations: E0308, E0381, E0408, E0425. +Some errors have detailed explanations: E0308, E0408, E0425. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/arm-and-pat-guards.rs b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/arm-and-pat-guards.rs new file mode 100644 index 0000000000000..22c00cae2a56f --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/arm-and-pat-guards.rs @@ -0,0 +1,22 @@ +//! Check for correct runtime behavior when using guard patterns combined with arm guards + +//@ compile-flags: -Zvalidate-mir -Zlint-mir +//@ run-pass + +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +fn main() { + assert!(guard_arm_pats(true, false, true)); + assert!(guard_arm_pats(false, true, true)); +} + +fn guard_arm_pats(x: bool, y: bool, z: bool) -> bool { + match (x, y) { + // (true, false, true) + (true if x, false if x) if z => true, + // (false, true, true) + (false if z, true if !x) if y => true, + _ => false + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/or-pats.rs b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/or-pats.rs new file mode 100644 index 0000000000000..edaa55d4b2832 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/or-pats.rs @@ -0,0 +1,48 @@ +//! Check for correct runtime behavior when using guard patterns with or-patterns + +//@ compile-flags: -Zvalidate-mir -Zlint-mir +//@ run-pass + +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +fn main() { + assert!(arr_with_or_pats_and_arm_guard([1, 2, 4], true, true, false, true, true)); + assert!(arr_with_or_pats_and_arm_guard([1, 3, 4], true, false, true, true, true)); + + assert!(arr_with_or_pats([1, 2, 4], true, true, false, true)); + assert!(arr_with_or_pats([1, 3, 4], true, false, true, true)); +} + +fn arr_with_or_pats_and_arm_guard( + arr: [u8; 3], + a: bool, + b: bool, + c: bool, + d: bool, + e: bool, +) -> bool { + const A: u8 = 1; + const B: u8 = 2; + const C: u8 = 3; + const D: u8 = 4; + + match arr { + // [1, 2 | 3, 4] + [A if a, (B if b) | (C if c), D if d] if e => true, + _ => false + } +} + +fn arr_with_or_pats(arr: [u8; 3], a: bool, b: bool, c: bool, d: bool) -> bool { + const A: u8 = 1; + const B: u8 = 2; + const C: u8 = 3; + const D: u8 = 4; + + match arr { + // [1, 2 | 3, 4] + [A if a, (B if b) | (C if c), D if d] => true, + _ => false + } +} diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/with-bools.rs b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/with-bools.rs new file mode 100644 index 0000000000000..44e58fad38510 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/runtime-behavior/with-bools.rs @@ -0,0 +1,34 @@ +//! Check for correct runtime behavior when using bools in guard patterns + +//@ compile-flags: -Zvalidate-mir -Zlint-mir +//@ run-pass + +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +fn main() { + assert!(generic_usage(true, false, true)); + assert!(!generic_usage(false, true, true)); + assert!(with_ops(true, false, true)); + assert!(with_ops(false, true, true)) +} + +fn generic_usage(x: bool, y: bool, z: bool) -> bool { + match (x, y) { + // (true, false, true) + (true if z, false if z) => true, + // (false, true, true) + (false if z, true if z) => false, + _ => false + } +} + +fn with_ops(x: bool, y: bool, z: bool) -> bool { + match (x, y) { + // (true, false, true) + (true if y || z, false if x && z) => true, + // (false, true, true) + (false if y && z, true if y || z) => true, + _ => false + } + }