diff options
| author | dianne <diannes.gm@gmail.com> | 2025-07-10 16:38:53 -0700 |
|---|---|---|
| committer | dianne <diannes.gm@gmail.com> | 2025-08-06 12:13:40 -0700 |
| commit | b7de53980572cfec3dadaf869fd554c755794d27 (patch) | |
| tree | 59341a54a162a992435844e144044f824e961f26 /compiler/rustc_mir_build | |
| parent | ea1eca5e3b6e5300696d06c56190f8d617104edf (diff) | |
| download | rust-b7de53980572cfec3dadaf869fd554c755794d27.tar.gz rust-b7de53980572cfec3dadaf869fd554c755794d27.zip | |
lower bindings in the order they're written
Diffstat (limited to 'compiler/rustc_mir_build')
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/matches/match_pair.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/matches/mod.rs | 82 | ||||
| -rw-r--r-- | compiler/rustc_mir_build/src/builder/matches/util.rs | 8 |
3 files changed, 95 insertions, 15 deletions
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 3a7854a5e11..7a848536d0e 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -124,9 +124,19 @@ impl<'tcx> MatchPairTree<'tcx> { let test_case = match pattern.kind { PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, - PatKind::Or { ref pats } => Some(TestCase::Or { - pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(), - }), + PatKind::Or { ref pats } => { + 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() { + // Hold a place for any bindings established in (possibly-nested) or-patterns. + // By only holding a place when bindings are present, we skip over any + // 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); + } + Some(TestCase::Or { pats }) + } PatKind::Range(ref range) => { if range.is_full_range(cx.tcx) == Some(true) { @@ -194,12 +204,12 @@ impl<'tcx> MatchPairTree<'tcx> { // Then push this binding, after any bindings in the subpattern. if let Some(source) = place { - extra_data.bindings.push(super::Binding { + extra_data.bindings.push(super::SubpatternBindings::One(super::Binding { span: pattern.span, source, var_id: var, binding_mode: mode, - }); + })); } None diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 29c5670b0bf..4a60ff30dba 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -990,7 +990,7 @@ struct PatternExtraData<'tcx> { span: Span, /// Bindings that must be established. - bindings: Vec<Binding<'tcx>>, + bindings: Vec<SubpatternBindings<'tcx>>, /// Types that must be asserted. ascriptions: Vec<Ascription<'tcx>>, @@ -1005,6 +1005,15 @@ impl<'tcx> PatternExtraData<'tcx> { } } +#[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. + FromOrPattern, +} + /// A pattern in a form suitable for lowering the match tree, with all irrefutable /// patterns simplified away. /// @@ -1220,7 +1229,7 @@ fn traverse_candidate<'tcx, C, T, I>( } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] struct Binding<'tcx> { span: Span, source: Place<'tcx>, @@ -1446,12 +1455,7 @@ impl<'tcx> MatchTreeSubBranch<'tcx> { span: candidate.extra_data.span, success_block: candidate.pre_binding_block.unwrap(), otherwise_block: candidate.otherwise_block.unwrap(), - bindings: parent_data - .iter() - .flat_map(|d| &d.bindings) - .chain(&candidate.extra_data.bindings) - .cloned() - .collect(), + bindings: sub_branch_bindings(parent_data, &candidate.extra_data.bindings), ascriptions: parent_data .iter() .flat_map(|d| &d.ascriptions) @@ -1484,6 +1488,68 @@ impl<'tcx> MatchTreeBranch<'tcx> { } } +/// Collects the bindings 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<Binding<'tcx>> { + // 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); + } + // 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. + // FIXME(@dianne): prevent ill-formed patterns from getting here + while let Some(candidate_bindings) = remainder.next() { + ty::tls::with(|tcx| { + tcx.dcx().delayed_bug("mismatched or-pattern bindings 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); + } + all_bindings +} + +/// 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<Binding<'tcx>>, + candidate_bindings: &'c [SubpatternBindings<'tcx>], + remainder: &mut impl Iterator<Item = &'c [SubpatternBindings<'tcx>]>, +) { + 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 + // 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); + } 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") + }); + }; + } + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum HasMatchGuard { Yes, diff --git a/compiler/rustc_mir_build/src/builder/matches/util.rs b/compiler/rustc_mir_build/src/builder/matches/util.rs index 589e350a03f..2c8ad95b6af 100644 --- a/compiler/rustc_mir_build/src/builder/matches/util.rs +++ b/compiler/rustc_mir_build/src/builder/matches/util.rs @@ -138,7 +138,9 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> { fn visit_candidate(&mut self, candidate: &Candidate<'tcx>) { for binding in &candidate.extra_data.bindings { - self.visit_binding(binding); + if let super::SubpatternBindings::One(binding) = binding { + self.visit_binding(binding); + } } for match_pair in &candidate.match_pairs { self.visit_match_pair(match_pair); @@ -147,7 +149,9 @@ 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 { - self.visit_binding(binding); + if let super::SubpatternBindings::One(binding) = binding { + self.visit_binding(binding); + } } for match_pair in &flat_pat.match_pairs { self.visit_match_pair(match_pair); |
