1
Fork 0

Rollup merge of #121716 - Nadrieril:simple-binding-order, r=matthewjasper

match lowering: Lower bindings in a predictable order

After the recent refactorings, we can now lower bindings in a truly predictable order. The order in https://github.com/rust-lang/rust/pull/120214 was an improvement but not very clear. With this PR, we lower bindings from left to right, with the special case that `x @ pat` is traversed as `pat @ x` (i.e. `x` is lowered after any bindings in `pat`).

This description only applies in the absence of or-patterns. Or-patterns make everything complicated, because the binding place depends on the subpattern. Until I have a better idea I leave them to be handled in whatever weird order arises from today's code.

r? `@matthewjasper`
This commit is contained in:
Guillaume Boisseau 2024-03-02 20:13:23 +01:00 committed by GitHub
commit 30976fbe2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 118 additions and 100 deletions

View file

@ -701,7 +701,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.bind_pattern(
self.source_info(irrefutable_pat.span),
candidate,
&fake_borrow_temps,
fake_borrow_temps.as_slice(),
irrefutable_pat.span,
None,
false,
@ -1938,7 +1938,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let post_guard_block = self.bind_pattern(
self.source_info(pat.span),
guard_candidate,
&fake_borrow_temps,
fake_borrow_temps.as_slice(),
expr_span,
None,
false,
@ -2425,7 +2425,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let matching = this.bind_pattern(
this.source_info(pattern.span),
candidate,
&fake_borrow_temps,
fake_borrow_temps.as_slice(),
initializer_span,
None,
true,
@ -2434,7 +2434,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let failure = this.bind_pattern(
this.source_info(else_block_span),
wildcard,
&fake_borrow_temps,
fake_borrow_temps.as_slice(),
initializer_span,
None,
true,

View file

@ -41,60 +41,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// let y = x;
// }
//
// We can't just reverse the binding order, because we must preserve pattern-order
// otherwise, e.g. in `let (Some(a), Some(b)) = (x, y)`. Our rule then is: deepest-first,
// and bindings at the same depth stay in source order.
//
// To do this, every time around the loop we prepend the newly found bindings to the
// bindings we already had.
//
// example:
// candidate.bindings = [1, 2, 3]
// bindings in iter 1: [4, 5]
// bindings in iter 2: [6, 7]
//
// final bindings: [6, 7, 4, 5, 1, 2, 3]
let mut accumulated_bindings = mem::take(candidate_bindings);
let mut simplified_match_pairs = Vec::new();
// Repeatedly simplify match pairs until we're left with only unsimplifiable ones.
loop {
for mut match_pair in mem::take(match_pairs) {
if let TestCase::Irrefutable { binding, ascription } = match_pair.test_case {
if let Some(binding) = binding {
candidate_bindings.push(binding);
}
if let Some(ascription) = ascription {
candidate_ascriptions.push(ascription);
}
// Simplifiable pattern; we replace it with its subpairs and simplify further.
match_pairs.append(&mut match_pair.subpairs);
} else {
// Unsimplifiable pattern; we recursively simplify its subpairs and don't
// process it further.
self.simplify_match_pairs(
&mut match_pair.subpairs,
candidate_bindings,
candidate_ascriptions,
);
simplified_match_pairs.push(match_pair);
// We therefore lower bindings from left-to-right, except we lower the `x` in `x @ pat`
// after any bindings in `pat`. This doesn't work for or-patterns: the current structure of
// match lowering forces us to lower bindings inside or-patterns last.
for mut match_pair in mem::take(match_pairs) {
self.simplify_match_pairs(
&mut match_pair.subpairs,
candidate_bindings,
candidate_ascriptions,
);
if let TestCase::Irrefutable { binding, ascription } = match_pair.test_case {
if let Some(binding) = binding {
candidate_bindings.push(binding);
}
}
// This does: accumulated_bindings = candidate.bindings.take() ++ accumulated_bindings
candidate_bindings.extend_from_slice(&accumulated_bindings);
mem::swap(candidate_bindings, &mut accumulated_bindings);
candidate_bindings.clear();
if match_pairs.is_empty() {
break;
if let Some(ascription) = ascription {
candidate_ascriptions.push(ascription);
}
// Simplifiable pattern; we replace it with its already simplified subpairs.
match_pairs.append(&mut match_pair.subpairs);
} else {
// Unsimplifiable pattern; we keep it.
match_pairs.push(match_pair);
}
}
// Store computed bindings back in `candidate_bindings`.
mem::swap(candidate_bindings, &mut accumulated_bindings);
// Store simplified match pairs back in `match_pairs`.
mem::swap(match_pairs, &mut simplified_match_pairs);
// Move or-patterns to the end, because they can result in us
// creating additional candidates, so we want to test them as
// late as possible.