Skip to content

Commit 9c6a4b0

Browse files
Rollup merge of #150581 - Zalathar:string, r=Nadrieril
mir_build: Separate match lowering for string-equality and scalar-equality - Follow-up to #150238 --- This PR takes some match-lowering code that is responsible for equality tests, and splits it into distinct code paths for string-equality and scalar-equality. The split results in more lines of code overall, but makes the separated code paths easier to understand individually. r? Nadrieril
2 parents 1354000 + 7749e75 commit 9c6a4b0

5 files changed

Lines changed: 110 additions & 66 deletions

File tree

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,12 @@ impl<'tcx> Ty<'tcx> {
12181218
*self.kind() == Str
12191219
}
12201220

1221+
/// Returns true if this type is `&str`. The reference's lifetime is ignored.
1222+
#[inline]
1223+
pub fn is_imm_ref_str(self) -> bool {
1224+
matches!(self.kind(), ty::Ref(_, inner, hir::Mutability::Not) if inner.is_str())
1225+
}
1226+
12211227
#[inline]
12221228
pub fn is_param(self, index: u32) -> bool {
12231229
match self.kind() {

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
314314
}
315315

316316
(
317-
TestKind::Eq { value: test_val, .. },
317+
TestKind::StringEq { value: test_val, .. },
318+
TestableCase::Constant { value: case_val, kind: PatConstKind::String },
319+
)
320+
| (
321+
TestKind::ScalarEq { value: test_val, .. },
318322
TestableCase::Constant {
319323
value: case_val,
320324
kind: PatConstKind::Float | PatConstKind::Other,
@@ -347,7 +351,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
347351
| TestKind::If
348352
| TestKind::SliceLen { .. }
349353
| TestKind::Range { .. }
350-
| TestKind::Eq { .. }
354+
| TestKind::StringEq { .. }
355+
| TestKind::ScalarEq { .. }
351356
| TestKind::Deref { .. },
352357
_,
353358
) => {

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::sync::Arc;
22

33
use rustc_abi::FieldIdx;
44
use rustc_middle::mir::*;
5+
use rustc_middle::span_bug;
56
use rustc_middle::thir::*;
67
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
78

@@ -173,9 +174,21 @@ impl<'tcx> MatchPairTree<'tcx> {
173174
PatConstKind::IntOrChar
174175
} else if pat_ty.is_floating_point() {
175176
PatConstKind::Float
177+
} else if pat_ty.is_str() {
178+
// Deref-patterns can cause string-literal patterns to have
179+
// type `str` instead of the usual `&str`.
180+
if !cx.tcx.features().deref_patterns() {
181+
span_bug!(
182+
pattern.span,
183+
"const pattern has type `str` but deref_patterns is not enabled"
184+
);
185+
}
186+
PatConstKind::String
187+
} else if pat_ty.is_imm_ref_str() {
188+
PatConstKind::String
176189
} else {
177190
// FIXME(Zalathar): This still covers several different
178-
// categories (e.g. raw pointer, string, pattern-type)
191+
// categories (e.g. raw pointer, pattern-type)
179192
// which could be split out into their own kinds.
180193
PatConstKind::Other
181194
};

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,9 +1290,10 @@ enum PatConstKind {
12901290
/// These types don't support `SwitchInt` and require an equality test,
12911291
/// but can also interact with range pattern tests.
12921292
Float,
1293+
/// Constant string values, tested via string equality.
1294+
String,
12931295
/// Any other constant-pattern is usually tested via some kind of equality
12941296
/// check. Types that might be encountered here include:
1295-
/// - `&str`
12961297
/// - raw pointers derived from integer values
12971298
/// - pattern types, e.g. `pattern_type!(u32 is 1..)`
12981299
Other,
@@ -1368,14 +1369,20 @@ enum TestKind<'tcx> {
13681369
/// Test whether a `bool` is `true` or `false`.
13691370
If,
13701371

1371-
/// Test for equality with value, possibly after an unsizing coercion to
1372-
/// `cast_ty`,
1373-
Eq {
1372+
/// Tests the place against a string constant using string equality.
1373+
StringEq {
1374+
/// Constant `&str` value to test against.
13741375
value: ty::Value<'tcx>,
1375-
// Integer types are handled by `SwitchInt`, and constants with ADT
1376-
// types and `&[T]` types are converted back into patterns, so this can
1377-
// only be `&str` or floats.
1378-
cast_ty: Ty<'tcx>,
1376+
/// Type of the corresponding pattern node. Usually `&str`, but could
1377+
/// be `str` for patterns like `deref!("..."): String`.
1378+
pat_ty: Ty<'tcx>,
1379+
},
1380+
1381+
/// Tests the place against a constant using scalar equality.
1382+
ScalarEq {
1383+
value: ty::Value<'tcx>,
1384+
/// Type of the corresponding pattern node.
1385+
pat_ty: Ty<'tcx>,
13791386
},
13801387

13811388
/// Test whether the value falls within an inclusive or exclusive range.

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

Lines changed: 68 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
3838
TestableCase::Constant { value: _, kind: PatConstKind::IntOrChar } => {
3939
TestKind::SwitchInt
4040
}
41-
TestableCase::Constant { value, kind: PatConstKind::Float } => {
42-
TestKind::Eq { value, cast_ty: match_pair.pattern_ty }
41+
TestableCase::Constant { value, kind: PatConstKind::String } => {
42+
TestKind::StringEq { value, pat_ty: match_pair.pattern_ty }
4343
}
44-
TestableCase::Constant { value, kind: PatConstKind::Other } => {
45-
TestKind::Eq { value, cast_ty: match_pair.pattern_ty }
44+
TestableCase::Constant { value, kind: PatConstKind::Float | PatConstKind::Other } => {
45+
TestKind::ScalarEq { value, pat_ty: match_pair.pattern_ty }
4646
}
4747

4848
TestableCase::Range(ref range) => {
@@ -141,17 +141,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
141141
self.cfg.terminate(block, self.source_info(match_start_span), terminator);
142142
}
143143

144-
TestKind::Eq { value, mut cast_ty } => {
144+
TestKind::StringEq { value, pat_ty } => {
145145
let tcx = self.tcx;
146146
let success_block = target_block(TestBranch::Success);
147147
let fail_block = target_block(TestBranch::Failure);
148148

149-
let mut expect_ty = value.ty;
150-
let mut expect = self.literal_operand(test.span, Const::from_ty_value(tcx, value));
149+
let expected_value_ty = value.ty;
150+
let expected_value_operand =
151+
self.literal_operand(test.span, Const::from_ty_value(tcx, value));
151152

152-
let mut place = place;
153+
let mut actual_value_ty = pat_ty;
154+
let mut actual_value_place = place;
153155

154-
match cast_ty.kind() {
156+
match pat_ty.kind() {
155157
ty::Str => {
156158
// String literal patterns may have type `str` if `deref_patterns` is
157159
// enabled, in order to allow `deref!("..."): String`. In this case, `value`
@@ -172,74 +174,85 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
172174
ref_place,
173175
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
174176
);
175-
place = ref_place;
176-
cast_ty = ref_str_ty;
177+
actual_value_place = ref_place;
178+
actual_value_ty = ref_str_ty;
177179
}
180+
_ => {}
181+
}
182+
183+
assert_eq!(expected_value_ty, actual_value_ty);
184+
assert!(actual_value_ty.is_imm_ref_str());
185+
186+
// Compare two strings using `<str as std::cmp::PartialEq>::eq`.
187+
// (Interestingly this means that exhaustiveness analysis relies, for soundness,
188+
// on the `PartialEq` impl for `str` to be correct!)
189+
self.string_compare(
190+
block,
191+
success_block,
192+
fail_block,
193+
source_info,
194+
expected_value_operand,
195+
Operand::Copy(actual_value_place),
196+
);
197+
}
198+
199+
TestKind::ScalarEq { value, pat_ty } => {
200+
let tcx = self.tcx;
201+
let success_block = target_block(TestBranch::Success);
202+
let fail_block = target_block(TestBranch::Failure);
203+
204+
let mut expected_value_ty = value.ty;
205+
let mut expected_value_operand =
206+
self.literal_operand(test.span, Const::from_ty_value(tcx, value));
207+
208+
let mut actual_value_ty = pat_ty;
209+
let mut actual_value_place = place;
210+
211+
match pat_ty.kind() {
178212
&ty::Pat(base, _) => {
179-
assert_eq!(cast_ty, value.ty);
213+
assert_eq!(pat_ty, value.ty);
180214
assert!(base.is_trivially_pure_clone_copy());
181215

182216
let transmuted_place = self.temp(base, test.span);
183217
self.cfg.push_assign(
184218
block,
185219
self.source_info(scrutinee_span),
186220
transmuted_place,
187-
Rvalue::Cast(CastKind::Transmute, Operand::Copy(place), base),
221+
Rvalue::Cast(
222+
CastKind::Transmute,
223+
Operand::Copy(actual_value_place),
224+
base,
225+
),
188226
);
189227

190228
let transmuted_expect = self.temp(base, test.span);
191229
self.cfg.push_assign(
192230
block,
193231
self.source_info(test.span),
194232
transmuted_expect,
195-
Rvalue::Cast(CastKind::Transmute, expect, base),
233+
Rvalue::Cast(CastKind::Transmute, expected_value_operand, base),
196234
);
197235

198-
place = transmuted_place;
199-
expect = Operand::Copy(transmuted_expect);
200-
cast_ty = base;
201-
expect_ty = base;
236+
actual_value_place = transmuted_place;
237+
actual_value_ty = base;
238+
expected_value_operand = Operand::Copy(transmuted_expect);
239+
expected_value_ty = base;
202240
}
203241
_ => {}
204242
}
205243

206-
assert_eq!(expect_ty, cast_ty);
207-
if !cast_ty.is_scalar() {
208-
// Use `PartialEq::eq` instead of `BinOp::Eq`
209-
// (the binop can only handle primitives)
210-
// Make sure that we do *not* call any user-defined code here.
211-
// The only type that can end up here is string literals, which have their
212-
// comparison defined in `core`.
213-
// (Interestingly this means that exhaustiveness analysis relies, for soundness,
214-
// on the `PartialEq` impl for `str` to b correct!)
215-
match *cast_ty.kind() {
216-
ty::Ref(_, deref_ty, _) if deref_ty == self.tcx.types.str_ => {}
217-
_ => {
218-
span_bug!(
219-
source_info.span,
220-
"invalid type for non-scalar compare: {cast_ty}"
221-
)
222-
}
223-
};
224-
self.string_compare(
225-
block,
226-
success_block,
227-
fail_block,
228-
source_info,
229-
expect,
230-
Operand::Copy(place),
231-
);
232-
} else {
233-
self.compare(
234-
block,
235-
success_block,
236-
fail_block,
237-
source_info,
238-
BinOp::Eq,
239-
expect,
240-
Operand::Copy(place),
241-
);
242-
}
244+
assert_eq!(expected_value_ty, actual_value_ty);
245+
assert!(actual_value_ty.is_scalar());
246+
247+
self.compare(
248+
block,
249+
success_block,
250+
fail_block,
251+
source_info,
252+
BinOp::Eq,
253+
expected_value_operand,
254+
Operand::Copy(actual_value_place),
255+
);
243256
}
244257

245258
TestKind::Range(ref range) => {

0 commit comments

Comments
 (0)