Skip to content

Commit f9e3c61

Browse files
authored
Rollup merge of #150238 - Zalathar:pat-const-kind, r=Nadrieril
mir_build: Classify `TestableCase::Constant` into multiple sub-kinds In match lowering, when choosing a test for a `TestableCase::Constant`, there is some ad-hoc logic for inspecting the pattern type and deciding what kind of test is suitable. There is also some very similar logic later, when partitioning cases into buckets based on the chosen test. Instead of having that ad-hoc logic in multiple places, I think it's better to perform an up-front classification when lowering `thir::PatKind::Constant` to `TestableCase::Constant`, and then have the later steps simply match on an enum variant. There should be no change to the resulting built MIR. (I will note that the logic/invariants involved are a bit unclear, so there is a risk of accidental minor differences.)
2 parents ef4f2d6 + 1296925 commit f9e3c61

4 files changed

Lines changed: 78 additions & 22 deletions

File tree

compiler/rustc_mir_build/src/builder/matches/buckets.rs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use rustc_middle::span_bug;
66
use tracing::debug;
77

88
use crate::builder::Builder;
9-
use crate::builder::matches::test::is_switch_ty;
10-
use crate::builder::matches::{Candidate, Test, TestBranch, TestKind, TestableCase};
9+
use crate::builder::matches::{Candidate, PatConstKind, Test, TestBranch, TestKind, TestableCase};
1110

1211
/// Output of [`Builder::partition_candidates_into_buckets`].
1312
pub(crate) struct PartitionedCandidates<'tcx, 'b, 'c> {
@@ -157,11 +156,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
157156
//
158157
// FIXME(#29623) we could use PatKind::Range to rule
159158
// things out here, in some cases.
160-
//
161-
// FIXME(Zalathar): Is the `is_switch_ty` test unnecessary?
162-
(TestKind::SwitchInt, &TestableCase::Constant { value })
163-
if is_switch_ty(match_pair.pattern_ty) =>
164-
{
159+
(
160+
TestKind::SwitchInt,
161+
&TestableCase::Constant { value, kind: PatConstKind::IntOrChar },
162+
) => {
165163
// An important invariant of candidate bucketing is that a candidate
166164
// must not match in multiple branches. For `SwitchInt` tests, adding
167165
// a new value might invalidate that property for range patterns that
@@ -206,7 +204,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
206204
})
207205
}
208206

209-
(TestKind::If, TestableCase::Constant { value }) => {
207+
(TestKind::If, TestableCase::Constant { value, kind: PatConstKind::Bool }) => {
210208
fully_matched = true;
211209
let value = value.try_to_bool().unwrap_or_else(|| {
212210
span_bug!(test.span, "expected boolean value but got {value:?}")
@@ -291,7 +289,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
291289
if !test.overlaps(pat, self.tcx)? { Some(TestBranch::Failure) } else { None }
292290
}
293291
}
294-
(TestKind::Range(range), &TestableCase::Constant { value }) => {
292+
(
293+
TestKind::Range(range),
294+
&TestableCase::Constant {
295+
value,
296+
kind: PatConstKind::Bool | PatConstKind::IntOrChar | PatConstKind::Float,
297+
},
298+
) => {
295299
fully_matched = false;
296300
if !range.contains(value, self.tcx)? {
297301
// `value` is not contained in the testing range,
@@ -302,7 +306,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
302306
}
303307
}
304308

305-
(TestKind::Eq { value: test_val, .. }, TestableCase::Constant { value: case_val }) => {
309+
(
310+
TestKind::Eq { value: test_val, .. },
311+
TestableCase::Constant {
312+
value: case_val,
313+
kind: PatConstKind::Float | PatConstKind::Other,
314+
},
315+
) => {
306316
if test_val == case_val {
307317
fully_matched = true;
308318
Some(TestBranch::Success)

compiler/rustc_mir_build/src/builder/matches/match_pair.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt};
77

88
use crate::builder::Builder;
99
use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
10-
use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestableCase};
10+
use crate::builder::matches::{
11+
FlatPat, MatchPairTree, PatConstKind, PatternExtraData, TestableCase,
12+
};
1113

1214
impl<'a, 'tcx> Builder<'a, 'tcx> {
1315
/// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
@@ -156,7 +158,29 @@ impl<'tcx> MatchPairTree<'tcx> {
156158
}
157159
}
158160

159-
PatKind::Constant { value } => Some(TestableCase::Constant { value }),
161+
PatKind::Constant { value } => {
162+
// CAUTION: The type of the pattern node (`pattern.ty`) is
163+
// _often_ the same as the type of the const value (`value.ty`),
164+
// but there are some cases where those types differ
165+
// (e.g. when `deref!(..)` patterns interact with `String`).
166+
167+
// Classify the constant-pattern into further kinds, to
168+
// reduce the number of ad-hoc type tests needed later on.
169+
let pat_ty = pattern.ty;
170+
let const_kind = if pat_ty.is_bool() {
171+
PatConstKind::Bool
172+
} else if pat_ty.is_integral() || pat_ty.is_char() {
173+
PatConstKind::IntOrChar
174+
} else if pat_ty.is_floating_point() {
175+
PatConstKind::Float
176+
} else {
177+
// FIXME(Zalathar): This still covers several different
178+
// categories (e.g. raw pointer, string, pattern-type)
179+
// which could be split out into their own kinds.
180+
PatConstKind::Other
181+
};
182+
Some(TestableCase::Constant { value, kind: const_kind })
183+
}
160184

161185
PatKind::AscribeUserType {
162186
ascription: Ascription { ref annotation, variance },

compiler/rustc_mir_build/src/builder/matches/mod.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,7 @@ struct Ascription<'tcx> {
12621262
#[derive(Debug, Clone)]
12631263
enum TestableCase<'tcx> {
12641264
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
1265-
Constant { value: ty::Value<'tcx> },
1265+
Constant { value: ty::Value<'tcx>, kind: PatConstKind },
12661266
Range(Arc<PatRange<'tcx>>),
12671267
Slice { len: u64, variable_length: bool },
12681268
Deref { temp: Place<'tcx>, mutability: Mutability },
@@ -1276,6 +1276,28 @@ impl<'tcx> TestableCase<'tcx> {
12761276
}
12771277
}
12781278

1279+
/// Sub-classification of [`TestableCase::Constant`], which helps to avoid
1280+
/// some redundant ad-hoc checks when preparing and lowering tests.
1281+
#[derive(Debug, Clone)]
1282+
enum PatConstKind {
1283+
/// The primitive `bool` type, which is like an integer but simpler,
1284+
/// having only two values.
1285+
Bool,
1286+
/// Primitive unsigned/signed integer types, plus `char`.
1287+
/// These types interact nicely with `SwitchInt`.
1288+
IntOrChar,
1289+
/// Floating-point primitives, e.g. `f32`, `f64`.
1290+
/// These types don't support `SwitchInt` and require an equality test,
1291+
/// but can also interact with range pattern tests.
1292+
Float,
1293+
/// Any other constant-pattern is usually tested via some kind of equality
1294+
/// check. Types that might be encountered here include:
1295+
/// - `&str`
1296+
/// - raw pointers derived from integer values
1297+
/// - pattern types, e.g. `pattern_type!(u32 is 1..)`
1298+
Other,
1299+
}
1300+
12791301
/// Node in a tree of "match pairs", where each pair consists of a place to be
12801302
/// tested, and a test to perform on that place.
12811303
///

compiler/rustc_mir_build/src/builder/matches/test.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym};
1919
use tracing::{debug, instrument};
2020

2121
use crate::builder::Builder;
22-
use crate::builder::matches::{MatchPairTree, Test, TestBranch, TestKind, TestableCase};
22+
use crate::builder::matches::{
23+
MatchPairTree, PatConstKind, Test, TestBranch, TestKind, TestableCase,
24+
};
2325

2426
impl<'a, 'tcx> Builder<'a, 'tcx> {
2527
/// Identifies what test is needed to decide if `match_pair` is applicable.
@@ -32,11 +34,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
3234
let kind = match match_pair.testable_case {
3335
TestableCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
3436

35-
TestableCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If,
36-
TestableCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => {
37+
TestableCase::Constant { value: _, kind: PatConstKind::Bool } => TestKind::If,
38+
TestableCase::Constant { value: _, kind: PatConstKind::IntOrChar } => {
3739
TestKind::SwitchInt
3840
}
39-
TestableCase::Constant { value } => {
41+
TestableCase::Constant { value, kind: PatConstKind::Float } => {
42+
TestKind::Eq { value, cast_ty: match_pair.pattern_ty }
43+
}
44+
TestableCase::Constant { value, kind: PatConstKind::Other } => {
4045
TestKind::Eq { value, cast_ty: match_pair.pattern_ty }
4146
}
4247

@@ -491,11 +496,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
491496
}
492497
}
493498

494-
/// Returns true if this type be used with [`TestKind::SwitchInt`].
495-
pub(crate) fn is_switch_ty(ty: Ty<'_>) -> bool {
496-
ty.is_integral() || ty.is_char()
497-
}
498-
499499
fn trait_method<'tcx>(
500500
tcx: TyCtxt<'tcx>,
501501
trait_def_id: DefId,

0 commit comments

Comments
 (0)