diff --git a/compiler/rustc_mir_build/src/builder/matches/buckets.rs b/compiler/rustc_mir_build/src/builder/matches/buckets.rs index 8cbbb8e14095a..5275a1bb620ed 100644 --- a/compiler/rustc_mir_build/src/builder/matches/buckets.rs +++ b/compiler/rustc_mir_build/src/builder/matches/buckets.rs @@ -6,8 +6,7 @@ use rustc_middle::span_bug; use tracing::debug; use crate::builder::Builder; -use crate::builder::matches::test::is_switch_ty; -use crate::builder::matches::{Candidate, Test, TestBranch, TestKind, TestableCase}; +use crate::builder::matches::{Candidate, PatConstKind, Test, TestBranch, TestKind, TestableCase}; /// Output of [`Builder::partition_candidates_into_buckets`]. pub(crate) struct PartitionedCandidates<'tcx, 'b, 'c> { @@ -157,11 +156,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // // FIXME(#29623) we could use PatKind::Range to rule // things out here, in some cases. - // - // FIXME(Zalathar): Is the `is_switch_ty` test unnecessary? - (TestKind::SwitchInt, &TestableCase::Constant { value }) - if is_switch_ty(match_pair.pattern_ty) => - { + ( + TestKind::SwitchInt, + &TestableCase::Constant { value, kind: PatConstKind::IntOrChar }, + ) => { // An important invariant of candidate bucketing is that a candidate // must not match in multiple branches. For `SwitchInt` tests, adding // a new value might invalidate that property for range patterns that @@ -206,7 +204,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) } - (TestKind::If, TestableCase::Constant { value }) => { + (TestKind::If, TestableCase::Constant { value, kind: PatConstKind::Bool }) => { fully_matched = true; let value = value.try_to_bool().unwrap_or_else(|| { span_bug!(test.span, "expected boolean value but got {value:?}") @@ -291,7 +289,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if !test.overlaps(pat, self.tcx)? { Some(TestBranch::Failure) } else { None } } } - (TestKind::Range(range), &TestableCase::Constant { value }) => { + (TestKind::Range(range), &TestableCase::Constant { value, kind: _ }) => { fully_matched = false; if !range.contains(value, self.tcx)? { // `value` is not contained in the testing range, @@ -302,7 +300,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - (TestKind::Eq { value: test_val, .. }, TestableCase::Constant { value: case_val }) => { + ( + TestKind::Eq { value: test_val, .. }, + TestableCase::Constant { value: case_val, kind: _ }, + ) => { if test_val == case_val { fully_matched = true; Some(TestBranch::Success) 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 9b9ee18369564..48811fbed093c 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -7,7 +7,9 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use crate::builder::Builder; use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; -use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestableCase}; +use crate::builder::matches::{ + FlatPat, MatchPairTree, PatConstKind, PatternExtraData, TestableCase, +}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in @@ -156,7 +158,27 @@ impl<'tcx> MatchPairTree<'tcx> { } } - PatKind::Constant { value } => Some(TestableCase::Constant { value }), + PatKind::Constant { value } => { + // CAUTION: The type of the pattern node (`pattern.ty`) is + // _often_ the same as the type of the const value (`value.ty`), + // but there are some cases where those types differ + // (e.g. when `deref!(..)` patterns interact with `String`). + + // Classify the constant-pattern into further kinds, to + // reduce the number of ad-hoc type tests needed later on. + let pat_ty = pattern.ty; + let const_kind = if pat_ty.is_bool() { + PatConstKind::Bool + } else if pat_ty.is_integral() || pat_ty.is_char() { + PatConstKind::IntOrChar + } else { + // FIXME(Zalathar): This still covers several different + // categories (e.g. float, pointer, string, pattern-type) + // which could be split out into their own kinds. + PatConstKind::Eq + }; + Some(TestableCase::Constant { value, kind: const_kind }) + } PatKind::AscribeUserType { ascription: Ascription { ref annotation, variance }, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 8897ca7c72107..7b45c23a0806e 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1262,7 +1262,7 @@ struct Ascription<'tcx> { #[derive(Debug, Clone)] enum TestableCase<'tcx> { Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx }, - Constant { value: ty::Value<'tcx> }, + Constant { value: ty::Value<'tcx>, kind: PatConstKind }, Range(Arc>), Slice { len: u64, variable_length: bool }, Deref { temp: Place<'tcx>, mutability: Mutability }, @@ -1276,6 +1276,25 @@ impl<'tcx> TestableCase<'tcx> { } } +/// Sub-classification of [`TestableCase::Constant`], which helps to avoid +/// some redundant ad-hoc checks when preparing and lowering tests. +#[derive(Debug, Clone)] +enum PatConstKind { + /// The primitive `bool` type, which is like an integer but simpler, + /// having only two values. + Bool, + /// Primitive unsigned/signed integer types, plus `char`. + /// These types interact nicely with `SwitchInt`. + IntOrChar, + /// Any other constant-pattern is usually tested via some kind of equality + /// check. Types that might be encountered here include: + /// - `&str` + /// - floating-point primitives, e.g. `f32`, `f64` + /// - raw pointers derived from integer values + /// - pattern types, e.g. `pattern_type!(u32 is 1..)` + Eq, +} + /// Node in a tree of "match pairs", where each pair consists of a place to be /// tested, and a test to perform on that place. /// diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 402587bff7e86..0c85de0aa9a8d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -19,7 +19,9 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use tracing::{debug, instrument}; use crate::builder::Builder; -use crate::builder::matches::{MatchPairTree, Test, TestBranch, TestKind, TestableCase}; +use crate::builder::matches::{ + MatchPairTree, PatConstKind, Test, TestBranch, TestKind, TestableCase, +}; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. @@ -32,11 +34,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let kind = match match_pair.testable_case { TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def }, - TestableCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If, - TestableCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => { + TestableCase::Constant { value: _, kind: PatConstKind::Bool } => TestKind::If, + TestableCase::Constant { value: _, kind: PatConstKind::IntOrChar } => { TestKind::SwitchInt } - TestableCase::Constant { value } => { + TestableCase::Constant { value, kind: PatConstKind::Eq } => { TestKind::Eq { value, cast_ty: match_pair.pattern_ty } } @@ -491,11 +493,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } -/// Returns true if this type be used with [`TestKind::SwitchInt`]. -pub(crate) fn is_switch_ty(ty: Ty<'_>) -> bool { - ty.is_integral() || ty.is_char() -} - fn trait_method<'tcx>( tcx: TyCtxt<'tcx>, trait_def_id: DefId,