Skip to content

Commit

Permalink
Rollup merge of #121393 - Nadrieril:match-lowering-testcase, r=matthe…
Browse files Browse the repository at this point in the history
…wjasper

match lowering: Introduce a `TestCase` enum to replace most matching on `PatKind`

Introduces `TestCase` that represents the specific outcome of a test. It complements `TestKind` which represents a test. In `MatchPair::new()` we select the appropriate `TestCase` for the pattern, and after that we almost never have to inspect the pattern directly during match lowering.

Together with #120904, this makes `MatchPair` into a standalone abstraction that hides the details of `thir::Pat`. This will become even truer in the next PR where I make `TestCase` handle or patterns. This opens the door to a lot of future simplifications.

r? `@matthewjasper`
  • Loading branch information
matthiaskrgr authored Feb 22, 2024
2 parents 379ef9b + 0610f59 commit 5a87e13
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 260 deletions.
27 changes: 20 additions & 7 deletions compiler/rustc_mir_build/src/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_data_structures::{
};
use rustc_index::bit_set::BitSet;
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::mir::{self, *};
use rustc_middle::thir::{self, *};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc_span::symbol::Symbol;
Expand Down Expand Up @@ -1052,18 +1052,31 @@ struct Ascription<'tcx> {
variance: ty::Variance,
}

#[derive(Debug, Clone)]
enum TestCase<'pat, 'tcx> {
Irrefutable { binding: Option<Binding<'tcx>>, ascription: Option<Ascription<'tcx>> },
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
Constant { value: mir::Const<'tcx> },
Range(&'pat PatRange<'tcx>),
Slice { len: usize, variable_length: bool },
Or,
}

#[derive(Debug, Clone)]
pub(crate) struct MatchPair<'pat, 'tcx> {
// This place...
/// This place...
place: PlaceBuilder<'tcx>,

// ... must match this pattern.
// Invariant: after creation and simplification in `Candidate::new()`, all match pairs must be
// simplified, i.e. require a test.
pattern: &'pat Pat<'tcx>,
/// ... must pass this test...
// Invariant: after creation and simplification in `Candidate::new()`, this must not be
// `Irrefutable`.
test_case: TestCase<'pat, 'tcx>,

/// Precomputed sub-match pairs of `pattern`.
/// ... and these subpairs must match.
subpairs: Vec<Self>,

/// The pattern this was created from.
pattern: &'pat Pat<'tcx>,
}

/// See [`Test`] for more.
Expand Down
160 changes: 20 additions & 140 deletions compiler/rustc_mir_build/src/build/matches/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
//! testing a value against a constant.
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair, TestCase};
use crate::build::Builder;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::thir::{self, *};
use rustc_middle::ty;
use rustc_middle::thir::{Pat, PatKind};

use std::mem;

Expand Down Expand Up @@ -62,13 +60,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let mut simplified_match_pairs = Vec::new();
// Repeatedly simplify match pairs until we're left with only unsimplifiable ones.
loop {
for match_pair in mem::take(match_pairs) {
if let Err(match_pair) = self.simplify_match_pair(
match_pair,
candidate_bindings,
candidate_ascriptions,
match_pairs,
) {
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);
}
}
Expand Down Expand Up @@ -117,133 +126,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
})
.collect()
}

/// Tries to simplify `match_pair`, returning `Ok(())` if successful. If successful, new match
/// pairs and bindings will have been pushed into the respective `Vec`s. If no simplification is
/// possible, `Err` is returned.
fn simplify_match_pair<'pat>(
&mut self,
mut match_pair: MatchPair<'pat, 'tcx>,
bindings: &mut Vec<Binding<'tcx>>,
ascriptions: &mut Vec<Ascription<'tcx>>,
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
) -> Result<(), MatchPair<'pat, 'tcx>> {
match match_pair.pattern.kind {
PatKind::Leaf { .. }
| PatKind::Deref { .. }
| PatKind::Array { .. }
| PatKind::Never
| PatKind::Wild
| PatKind::Error(_) => {}

PatKind::AscribeUserType {
ascription: thir::Ascription { ref annotation, variance },
..
} => {
// Apply the type ascription to the value at `match_pair.place`
if let Some(source) = match_pair.place.try_to_place(self) {
ascriptions.push(Ascription {
annotation: annotation.clone(),
source,
variance,
});
}
}

PatKind::Binding {
name: _,
mutability: _,
mode,
var,
ty: _,
subpattern: _,
is_primary: _,
} => {
if let Some(source) = match_pair.place.try_to_place(self) {
bindings.push(Binding {
span: match_pair.pattern.span,
source,
var_id: var,
binding_mode: mode,
});
}
}

PatKind::InlineConstant { subpattern: ref pattern, def } => {
// Apply a type ascription for the inline constant to the value at `match_pair.place`
if let Some(source) = match_pair.place.try_to_place(self) {
let span = match_pair.pattern.span;
let parent_id = self.tcx.typeck_root_def_id(self.def_id.to_def_id());
let args = ty::InlineConstArgs::new(
self.tcx,
ty::InlineConstArgsParts {
parent_args: ty::GenericArgs::identity_for_item(self.tcx, parent_id),
ty: self.infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span,
}),
},
)
.args;
let user_ty =
self.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf(
def.to_def_id(),
ty::UserArgs { args, user_self_ty: None },
));
let annotation = ty::CanonicalUserTypeAnnotation {
inferred_ty: pattern.ty,
span,
user_ty: Box::new(user_ty),
};
ascriptions.push(Ascription {
annotation,
source,
variance: ty::Contravariant,
});
}
}

PatKind::Constant { .. } => {
// FIXME normalize patterns when possible
return Err(match_pair);
}

PatKind::Range(ref range) => {
if range.is_full_range(self.tcx) != Some(true) {
return Err(match_pair);
}
}

PatKind::Slice { ref prefix, ref slice, ref suffix } => {
if !(prefix.is_empty() && slice.is_some() && suffix.is_empty()) {
self.simplify_match_pairs(&mut match_pair.subpairs, bindings, ascriptions);
return Err(match_pair);
}
}

PatKind::Variant { adt_def, args, variant_index, subpatterns: _ } => {
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
i == variant_index || {
(self.tcx.features().exhaustive_patterns
|| self.tcx.features().min_exhaustive_patterns)
&& !v
.inhabited_predicate(self.tcx, adt_def)
.instantiate(self.tcx, args)
.apply_ignore_module(self.tcx, self.param_env)
}
}) && (adt_def.did().is_local()
|| !adt_def.is_variant_list_non_exhaustive());
if !irrefutable {
self.simplify_match_pairs(&mut match_pair.subpairs, bindings, ascriptions);
return Err(match_pair);
}
}

PatKind::Or { .. } => return Err(match_pair),
}

// Simplifiable pattern; we replace it with its subpairs.
match_pairs.append(&mut match_pair.subpairs);
Ok(())
}
}
Loading

0 comments on commit 5a87e13

Please sign in to comment.