Rename TestCase to TestableCase

This commit is contained in:
Zalathar 2025-12-15 18:26:44 +11:00
parent 6e7dd2cd99
commit 7f6f522761
5 changed files with 64 additions and 54 deletions

View file

@ -7,7 +7,7 @@ use tracing::debug;
use crate::builder::Builder;
use crate::builder::matches::test::is_switch_ty;
use crate::builder::matches::{Candidate, Test, TestBranch, TestCase, TestKind};
use crate::builder::matches::{Candidate, Test, TestBranch, TestKind, TestableCase};
/// Output of [`Builder::partition_candidates_into_buckets`].
pub(crate) struct PartitionedCandidates<'tcx, 'b, 'c> {
@ -140,12 +140,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// branch, so it can be removed. If false, the match pair is _compatible_
// with its test branch, but still needs a more specific test.
let fully_matched;
let ret = match (&test.kind, &match_pair.test_case) {
let ret = match (&test.kind, &match_pair.testable_case) {
// If we are performing a variant switch, then this
// informs variant patterns, but nothing else.
(
&TestKind::Switch { adt_def: tested_adt_def },
&TestCase::Variant { adt_def, variant_index },
&TestableCase::Variant { adt_def, variant_index },
) => {
assert_eq!(adt_def, tested_adt_def);
fully_matched = true;
@ -159,7 +159,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// things out here, in some cases.
//
// FIXME(Zalathar): Is the `is_switch_ty` test unnecessary?
(TestKind::SwitchInt, &TestCase::Constant { value })
(TestKind::SwitchInt, &TestableCase::Constant { value })
if is_switch_ty(match_pair.pattern_ty) =>
{
// An important invariant of candidate bucketing is that a candidate
@ -167,16 +167,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// a new value might invalidate that property for range patterns that
// have already been partitioned into the failure arm, so we must take care
// not to add such values here.
let is_covering_range = |test_case: &TestCase<'tcx>| {
test_case.as_range().is_some_and(|range| {
let is_covering_range = |testable_case: &TestableCase<'tcx>| {
testable_case.as_range().is_some_and(|range| {
matches!(range.contains(value, self.tcx), None | Some(true))
})
};
let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| {
candidate
.match_pairs
.iter()
.any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case))
candidate.match_pairs.iter().any(|mp| {
mp.place == Some(test_place) && is_covering_range(&mp.testable_case)
})
};
if prior_candidates
.get(&TestBranch::Failure)
@ -189,7 +188,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Some(TestBranch::Constant(value))
}
}
(TestKind::SwitchInt, TestCase::Range(range)) => {
(TestKind::SwitchInt, TestableCase::Range(range)) => {
// When performing a `SwitchInt` test, a range pattern can be
// sorted into the failure arm if it doesn't contain _any_ of
// the values being tested. (This restricts what values can be
@ -207,7 +206,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
})
}
(TestKind::If, TestCase::Constant { value }) => {
(TestKind::If, TestableCase::Constant { value }) => {
fully_matched = true;
let value = value.try_to_bool().unwrap_or_else(|| {
span_bug!(test.span, "expected boolean value but got {value:?}")
@ -217,7 +216,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
(
&TestKind::Len { len: test_len, op: BinOp::Eq },
&TestCase::Slice { len, variable_length },
&TestableCase::Slice { len, variable_length },
) => {
match (test_len.cmp(&(len as u64)), variable_length) {
(Ordering::Equal, false) => {
@ -249,7 +248,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
(
&TestKind::Len { len: test_len, op: BinOp::Ge },
&TestCase::Slice { len, variable_length },
&TestableCase::Slice { len, variable_length },
) => {
// the test is `$actual_len >= test_len`
match (test_len.cmp(&(len as u64)), variable_length) {
@ -281,7 +280,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
(TestKind::Range(test), TestCase::Range(pat)) => {
(TestKind::Range(test), TestableCase::Range(pat)) => {
if test == pat {
fully_matched = true;
Some(TestBranch::Success)
@ -292,7 +291,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if !test.overlaps(pat, self.tcx)? { Some(TestBranch::Failure) } else { None }
}
}
(TestKind::Range(range), &TestCase::Constant { value }) => {
(TestKind::Range(range), &TestableCase::Constant { value }) => {
fully_matched = false;
if !range.contains(value, self.tcx)? {
// `value` is not contained in the testing range,
@ -303,7 +302,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
(TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => {
(TestKind::Eq { value: test_val, .. }, TestableCase::Constant { value: case_val }) => {
if test_val == case_val {
fully_matched = true;
Some(TestBranch::Success)
@ -313,7 +312,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
(TestKind::Deref { temp: test_temp, .. }, TestCase::Deref { temp, .. })
(TestKind::Deref { temp: test_temp, .. }, TestableCase::Deref { temp, .. })
if test_temp == temp =>
{
fully_matched = true;

View file

@ -8,7 +8,7 @@ use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt};
use crate::builder::Builder;
use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestCase};
use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestableCase};
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
@ -132,7 +132,7 @@ impl<'tcx> MatchPairTree<'tcx> {
let place = place_builder.try_to_place(cx);
let mut subpairs = Vec::new();
let test_case = match pattern.kind {
let testable_case = match pattern.kind {
PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
PatKind::Or { ref pats } => {
@ -146,18 +146,18 @@ impl<'tcx> MatchPairTree<'tcx> {
// FIXME(@dianne): this needs updating/removing if we always merge or-patterns
extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
}
Some(TestCase::Or { pats })
Some(TestableCase::Or { pats })
}
PatKind::Range(ref range) => {
if range.is_full_range(cx.tcx) == Some(true) {
None
} else {
Some(TestCase::Range(Arc::clone(range)))
Some(TestableCase::Range(Arc::clone(range)))
}
}
PatKind::Constant { value } => Some(TestCase::Constant { value }),
PatKind::Constant { value } => Some(TestableCase::Constant { value }),
PatKind::AscribeUserType {
ascription: Ascription { ref annotation, variance },
@ -256,7 +256,7 @@ impl<'tcx> MatchPairTree<'tcx> {
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
None
} else {
Some(TestCase::Slice {
Some(TestableCase::Slice {
len: prefix.len() + suffix.len(),
variable_length: slice.is_some(),
})
@ -275,7 +275,11 @@ impl<'tcx> MatchPairTree<'tcx> {
cx.def_id.into(),
)
}) && !adt_def.variant_list_has_applicable_non_exhaustive();
if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) }
if irrefutable {
None
} else {
Some(TestableCase::Variant { adt_def, variant_index })
}
}
PatKind::Leaf { ref subpatterns } => {
@ -331,17 +335,17 @@ impl<'tcx> MatchPairTree<'tcx> {
&mut subpairs,
extra_data,
);
Some(TestCase::Deref { temp, mutability })
Some(TestableCase::Deref { temp, mutability })
}
PatKind::Never => Some(TestCase::Never),
PatKind::Never => Some(TestableCase::Never),
};
if let Some(test_case) = test_case {
if let Some(testable_case) = testable_case {
// This pattern is refutable, so push a new match-pair node.
match_pairs.push(MatchPairTree {
place,
test_case,
testable_case,
subpairs,
pattern_ty: pattern.ty,
pattern_span: pattern.span,

View file

@ -1078,7 +1078,7 @@ struct Candidate<'tcx> {
/// (see [`Builder::test_remaining_match_pairs_after_or`]).
///
/// Invariants:
/// - All or-patterns ([`TestCase::Or`]) have been sorted to the end.
/// - All or-patterns ([`TestableCase::Or`]) have been sorted to the end.
match_pairs: Vec<MatchPairTree<'tcx>>,
/// ...and if this is non-empty, one of these subcandidates also has to match...
@ -1164,12 +1164,15 @@ impl<'tcx> Candidate<'tcx> {
/// Restores the invariant that or-patterns must be sorted to the end.
fn sort_match_pairs(&mut self) {
self.match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. }));
self.match_pairs.sort_by_key(|pair| matches!(pair.testable_case, TestableCase::Or { .. }));
}
/// Returns whether the first match pair of this candidate is an or-pattern.
fn starts_with_or_pattern(&self) -> bool {
matches!(&*self.match_pairs, [MatchPairTree { test_case: TestCase::Or { .. }, .. }, ..])
matches!(
&*self.match_pairs,
[MatchPairTree { testable_case: TestableCase::Or { .. }, .. }, ..]
)
}
/// Visit the leaf candidates (those with no subcandidates) contained in
@ -1261,7 +1264,7 @@ struct Ascription<'tcx> {
/// Instead they participate in or-pattern expansion, where they are transformed into
/// subcandidates. See [`Builder::expand_and_match_or_candidates`].
#[derive(Debug, Clone)]
enum TestCase<'tcx> {
enum TestableCase<'tcx> {
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
Constant { value: ty::Value<'tcx> },
Range(Arc<PatRange<'tcx>>),
@ -1271,7 +1274,7 @@ enum TestCase<'tcx> {
Or { pats: Box<[FlatPat<'tcx>]> },
}
impl<'tcx> TestCase<'tcx> {
impl<'tcx> TestableCase<'tcx> {
fn as_range(&self) -> Option<&PatRange<'tcx>> {
if let Self::Range(v) = self { Some(v.as_ref()) } else { None }
}
@ -1289,12 +1292,12 @@ pub(crate) struct MatchPairTree<'tcx> {
/// ---
/// This can be `None` if it referred to a non-captured place in a closure.
///
/// Invariant: Can only be `None` when `test_case` is `Or`.
/// Invariant: Can only be `None` when `testable_case` is `Or`.
/// Therefore this must be `Some(_)` after or-pattern expansion.
place: Option<Place<'tcx>>,
/// ... must pass this test...
test_case: TestCase<'tcx>,
testable_case: TestableCase<'tcx>,
/// ... and these subpairs must match.
///
@ -1317,7 +1320,7 @@ enum TestKind<'tcx> {
/// Test what enum variant a value is.
///
/// The subset of expected variants is not stored here; instead they are
/// extracted from the [`TestCase`]s of the candidates participating in the
/// extracted from the [`TestableCase`]s of the candidates participating in the
/// test.
Switch {
/// The enum type being tested.
@ -1327,7 +1330,7 @@ enum TestKind<'tcx> {
/// Test what value an integer or `char` has.
///
/// The test's target values are not stored here; instead they are extracted
/// from the [`TestCase`]s of the candidates participating in the test.
/// from the [`TestableCase`]s of the candidates participating in the test.
SwitchInt,
/// Test whether a `bool` is `true` or `false`.
@ -1948,7 +1951,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
candidate: &mut Candidate<'tcx>,
match_pair: MatchPairTree<'tcx>,
) {
let TestCase::Or { pats } = match_pair.test_case else { bug!() };
let TestableCase::Or { pats } = match_pair.testable_case else { bug!() };
debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats);
candidate.or_span = Some(match_pair.pattern_span);
candidate.subcandidates = pats
@ -2116,7 +2119,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
debug_assert!(
remaining_match_pairs
.iter()
.all(|match_pair| matches!(match_pair.test_case, TestCase::Or { .. }))
.all(|match_pair| matches!(match_pair.testable_case, TestableCase::Or { .. }))
);
// Visit each leaf candidate within this subtree, add a copy of the remaining

View file

@ -19,7 +19,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use tracing::{debug, instrument};
use crate::builder::Builder;
use crate::builder::matches::{MatchPairTree, Test, TestBranch, TestCase, TestKind};
use crate::builder::matches::{MatchPairTree, Test, TestBranch, TestKind, TestableCase};
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Identifies what test is needed to decide if `match_pair` is applicable.
@ -29,30 +29,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
match_pair: &MatchPairTree<'tcx>,
) -> Test<'tcx> {
let kind = match match_pair.test_case {
TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
let kind = match match_pair.testable_case {
TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
TestCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If,
TestCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => TestKind::SwitchInt,
TestCase::Constant { value } => TestKind::Eq { value, cast_ty: match_pair.pattern_ty },
TestableCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If,
TestableCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => {
TestKind::SwitchInt
}
TestableCase::Constant { value } => {
TestKind::Eq { value, cast_ty: match_pair.pattern_ty }
}
TestCase::Range(ref range) => {
TestableCase::Range(ref range) => {
assert_eq!(range.ty, match_pair.pattern_ty);
TestKind::Range(Arc::clone(range))
}
TestCase::Slice { len, variable_length } => {
TestableCase::Slice { len, variable_length } => {
let op = if variable_length { BinOp::Ge } else { BinOp::Eq };
TestKind::Len { len: len as u64, op }
}
TestCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
TestableCase::Deref { temp, mutability } => TestKind::Deref { temp, mutability },
TestCase::Never => TestKind::Never,
TestableCase::Never => TestKind::Never,
// Or-patterns are not tested directly; instead they are expanded into subcandidates,
// which are then distinguished by testing whatever non-or patterns they contain.
TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
TestableCase::Or { .. } => bug!("or-patterns should have already been handled"),
};
Test { span: match_pair.pattern_span, kind }

View file

@ -6,7 +6,7 @@ use tracing::debug;
use crate::builder::Builder;
use crate::builder::expr::as_place::PlaceBase;
use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestCase};
use crate::builder::matches::{Binding, Candidate, FlatPat, MatchPairTree, TestableCase};
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Creates a false edge to `imaginary_target` and a real edge to
@ -159,11 +159,11 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
}
fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'tcx>) {
if let TestCase::Or { pats, .. } = &match_pair.test_case {
if let TestableCase::Or { pats, .. } = &match_pair.testable_case {
for flat_pat in pats.iter() {
self.visit_flat_pat(flat_pat)
}
} else if matches!(match_pair.test_case, TestCase::Deref { .. }) {
} else if matches!(match_pair.testable_case, TestableCase::Deref { .. }) {
// The subpairs of a deref pattern are all places relative to the deref temporary, so we
// don't fake borrow them. Problem is, if we only shallowly fake-borrowed
// `match_pair.place`, this would allow: