mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
THIR patterns: Always use type str for string-constant-value nodes
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
|
||||
@@ -160,10 +159,7 @@ pub(super) fn for_pattern(
|
||||
}
|
||||
|
||||
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`).
|
||||
assert_eq!(pattern.ty, value.ty);
|
||||
|
||||
// Classify the constant-pattern into further kinds, to
|
||||
// reduce the number of ad-hoc type tests needed later on.
|
||||
@@ -175,16 +171,6 @@ pub(super) fn for_pattern(
|
||||
} else if pat_ty.is_floating_point() {
|
||||
PatConstKind::Float
|
||||
} else if pat_ty.is_str() {
|
||||
// Deref-patterns can cause string-literal patterns to have
|
||||
// type `str` instead of the usual `&str`.
|
||||
if !cx.tcx.features().deref_patterns() {
|
||||
span_bug!(
|
||||
pattern.span,
|
||||
"const pattern has type `str` but deref_patterns is not enabled"
|
||||
);
|
||||
}
|
||||
PatConstKind::String
|
||||
} else if pat_ty.is_imm_ref_str() {
|
||||
PatConstKind::String
|
||||
} else {
|
||||
// FIXME(Zalathar): This still covers several different
|
||||
|
||||
@@ -1339,11 +1339,9 @@ enum TestKind<'tcx> {
|
||||
|
||||
/// Tests the place against a string constant using string equality.
|
||||
StringEq {
|
||||
/// Constant `&str` value to test against.
|
||||
/// Constant string value to test against.
|
||||
/// Note that this value has type `str` (not `&str`).
|
||||
value: ty::Value<'tcx>,
|
||||
/// Type of the corresponding pattern node. Usually `&str`, but could
|
||||
/// be `str` for patterns like `deref!("..."): String`.
|
||||
pat_ty: Ty<'tcx>,
|
||||
},
|
||||
|
||||
/// Tests the place against a constant using scalar equality.
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::{LangItem, RangeEnd};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
@@ -39,7 +39,7 @@ pub(super) fn pick_test_for_match_pair(
|
||||
TestKind::SwitchInt
|
||||
}
|
||||
TestableCase::Constant { value, kind: PatConstKind::String } => {
|
||||
TestKind::StringEq { value, pat_ty: match_pair.pattern_ty }
|
||||
TestKind::StringEq { value }
|
||||
}
|
||||
TestableCase::Constant { value, kind: PatConstKind::Float | PatConstKind::Other } => {
|
||||
TestKind::ScalarEq { value, pat_ty: match_pair.pattern_ty }
|
||||
@@ -141,47 +141,33 @@ pub(super) fn perform_test(
|
||||
self.cfg.terminate(block, self.source_info(match_start_span), terminator);
|
||||
}
|
||||
|
||||
TestKind::StringEq { value, pat_ty } => {
|
||||
TestKind::StringEq { value } => {
|
||||
let tcx = self.tcx;
|
||||
let success_block = target_block(TestBranch::Success);
|
||||
let fail_block = target_block(TestBranch::Failure);
|
||||
|
||||
let expected_value_ty = value.ty;
|
||||
let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, tcx.types.str_);
|
||||
assert!(ref_str_ty.is_imm_ref_str(), "{ref_str_ty:?}");
|
||||
|
||||
// The string constant we're testing against has type `str`, but
|
||||
// calling `<str as PartialEq>::eq` requires `&str` operands.
|
||||
//
|
||||
// Because `str` and `&str` have the same valtree representation,
|
||||
// we can "cast" to the desired type by just replacing the type.
|
||||
assert!(value.ty.is_str(), "unexpected value type for StringEq test: {value:?}");
|
||||
let expected_value = ty::Value { ty: ref_str_ty, valtree: value.valtree };
|
||||
let expected_value_operand =
|
||||
self.literal_operand(test.span, Const::from_ty_value(tcx, value));
|
||||
self.literal_operand(test.span, Const::from_ty_value(tcx, expected_value));
|
||||
|
||||
let mut actual_value_ty = pat_ty;
|
||||
let mut actual_value_place = place;
|
||||
|
||||
match pat_ty.kind() {
|
||||
ty::Str => {
|
||||
// String literal patterns may have type `str` if `deref_patterns` is
|
||||
// enabled, in order to allow `deref!("..."): String`. In this case, `value`
|
||||
// is of type `&str`, so we compare it to `&place`.
|
||||
if !tcx.features().deref_patterns() {
|
||||
span_bug!(
|
||||
test.span,
|
||||
"matching on `str` went through without enabling deref_patterns"
|
||||
);
|
||||
}
|
||||
let re_erased = tcx.lifetimes.re_erased;
|
||||
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
|
||||
let ref_place = self.temp(ref_str_ty, test.span);
|
||||
// `let ref_place: &str = &place;`
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
self.source_info(test.span),
|
||||
ref_place,
|
||||
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
|
||||
);
|
||||
actual_value_place = ref_place;
|
||||
actual_value_ty = ref_str_ty;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
assert_eq!(expected_value_ty, actual_value_ty);
|
||||
assert!(actual_value_ty.is_imm_ref_str());
|
||||
// Similarly, the scrutinized place has type `str`, but we need `&str`.
|
||||
// Get a reference by doing `let actual_value_ref_place: &str = &place`.
|
||||
let actual_value_ref_place = self.temp(ref_str_ty, test.span);
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
self.source_info(test.span),
|
||||
actual_value_ref_place,
|
||||
Rvalue::Ref(tcx.lifetimes.re_erased, BorrowKind::Shared, place),
|
||||
);
|
||||
|
||||
// Compare two strings using `<str as std::cmp::PartialEq>::eq`.
|
||||
// (Interestingly this means that exhaustiveness analysis relies, for soundness,
|
||||
@@ -192,7 +178,7 @@ pub(super) fn perform_test(
|
||||
fail_block,
|
||||
source_info,
|
||||
expected_value_operand,
|
||||
Operand::Copy(actual_value_place),
|
||||
Operand::Copy(actual_value_ref_place),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -289,32 +289,29 @@ fn valtree_to_pat(&self, cv: ValTree<'tcx>, ty: Ty<'tcx>) -> Box<Pat<'tcx>> {
|
||||
suffix: Box::new([]),
|
||||
},
|
||||
ty::Str => {
|
||||
// String literal patterns may have type `str` if `deref_patterns` is enabled, in
|
||||
// order to allow `deref!("..."): String`. Since we need a `&str` for the comparison
|
||||
// when lowering to MIR in `Builder::perform_test`, treat the constant as a `&str`.
|
||||
// This works because `str` and `&str` have the same valtree representation.
|
||||
let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty);
|
||||
PatKind::Constant { value: ty::Value { ty: ref_str_ty, valtree: cv } }
|
||||
// Constant/literal patterns of type `&str` are lowered to a
|
||||
// `PatKind::Deref` wrapping a `PatKind::Constant` of type `str`.
|
||||
// This pattern node is the `str` constant part.
|
||||
//
|
||||
// Under `feature(deref_patterns)`, string literal patterns can also
|
||||
// have type `str` directly, without the `&`, in order to allow things
|
||||
// like `deref!("...")` to work when the scrutinee is `String`.
|
||||
PatKind::Constant { value: ty::Value { ty, valtree: cv } }
|
||||
}
|
||||
ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
|
||||
// `&str` is represented as a valtree, let's keep using this
|
||||
// optimization for now.
|
||||
ty::Str => PatKind::Constant { value: ty::Value { ty, valtree: cv } },
|
||||
// All other references are converted into deref patterns and then recursively
|
||||
// convert the dereferenced constant to a pattern that is the sub-pattern of the
|
||||
// deref pattern.
|
||||
_ => {
|
||||
if !pointee_ty.is_sized(tcx, self.typing_env) && !pointee_ty.is_slice() {
|
||||
return self.mk_err(
|
||||
tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
|
||||
ty,
|
||||
);
|
||||
} else {
|
||||
// References have the same valtree representation as their pointee.
|
||||
PatKind::Deref { subpattern: self.valtree_to_pat(cv, *pointee_ty) }
|
||||
}
|
||||
ty::Ref(_, pointee_ty, ..) => {
|
||||
if pointee_ty.is_str()
|
||||
|| pointee_ty.is_slice()
|
||||
|| pointee_ty.is_sized(tcx, self.typing_env)
|
||||
{
|
||||
// References have the same valtree representation as their pointee.
|
||||
PatKind::Deref { subpattern: self.valtree_to_pat(cv, *pointee_ty) }
|
||||
} else {
|
||||
return self.mk_err(
|
||||
tcx.dcx().create_err(UnsizedPattern { span, non_sm_ty: *pointee_ty }),
|
||||
ty,
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
ty::Float(flt) => {
|
||||
let v = cv.to_leaf();
|
||||
let is_nan = match flt {
|
||||
|
||||
@@ -583,19 +583,13 @@ pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Ref(_, t, _) if t.is_str() => {
|
||||
// We want a `&str` constant to behave like a `Deref` pattern, to be compatible
|
||||
// with other `Deref` patterns. This could have been done in `const_to_pat`,
|
||||
// but that causes issues with the rest of the matching code.
|
||||
// So here, the constructor for a `"foo"` pattern is `&` (represented by
|
||||
// `Ref`), and has one field. That field has constructor `Str(value)` and no
|
||||
// subfields.
|
||||
// Note: `t` is `str`, not `&str`.
|
||||
let ty = self.reveal_opaque_ty(*t);
|
||||
let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat);
|
||||
ctor = Ref;
|
||||
fields = vec![subpattern.at_index(0)];
|
||||
arity = 1;
|
||||
ty::Str => {
|
||||
// For constant/literal patterns of type `&str`, the THIR
|
||||
// pattern is a `PatKind::Deref` of type `&str` wrapping a
|
||||
// `PatKind::Const` of type `str`.
|
||||
ctor = Str(*value);
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
// All constants that can be structurally matched have already been expanded
|
||||
// into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
|
||||
|
||||
+22
-15
@@ -7,11 +7,14 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
let mut _3: (&str, bool);
|
||||
let mut _4: &str;
|
||||
let mut _5: bool;
|
||||
let mut _6: &&str;
|
||||
let mut _7: &bool;
|
||||
let mut _8: bool;
|
||||
let mut _9: bool;
|
||||
let mut _6: &str;
|
||||
let mut _7: &&str;
|
||||
let mut _8: &bool;
|
||||
let mut _9: &str;
|
||||
let mut _10: bool;
|
||||
let mut _11: &str;
|
||||
let mut _12: bool;
|
||||
let mut _13: bool;
|
||||
|
||||
bb0: {
|
||||
StorageLive(_3);
|
||||
@@ -23,7 +26,8 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
StorageDead(_5);
|
||||
StorageDead(_4);
|
||||
PlaceMention(_3);
|
||||
_9 = <str as PartialEq>::eq(copy (_3.0: &str), const "a") -> [return: bb9, unwind: bb19];
|
||||
_11 = &(*(_3.0: &str));
|
||||
_12 = <str as PartialEq>::eq(copy _11, const "a") -> [return: bb9, unwind: bb19];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@@ -43,7 +47,8 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb5: {
|
||||
_8 = <str as PartialEq>::eq(copy (_3.0: &str), const "b") -> [return: bb8, unwind: bb19];
|
||||
_9 = &(*(_3.0: &str));
|
||||
_10 = <str as PartialEq>::eq(copy _9, const "b") -> [return: bb8, unwind: bb19];
|
||||
}
|
||||
|
||||
bb6: {
|
||||
@@ -55,11 +60,11 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb8: {
|
||||
switchInt(move _8) -> [0: bb1, otherwise: bb6];
|
||||
switchInt(move _10) -> [0: bb1, otherwise: bb6];
|
||||
}
|
||||
|
||||
bb9: {
|
||||
switchInt(move _9) -> [0: bb5, otherwise: bb2];
|
||||
switchInt(move _12) -> [0: bb5, otherwise: bb2];
|
||||
}
|
||||
|
||||
bb10: {
|
||||
@@ -87,23 +92,25 @@ fn constant_eq(_1: &str, _2: bool) -> u32 {
|
||||
}
|
||||
|
||||
bb15: {
|
||||
_6 = &fake shallow (_3.0: &str);
|
||||
_7 = &fake shallow (_3.1: bool);
|
||||
StorageLive(_10);
|
||||
_10 = const true;
|
||||
switchInt(move _10) -> [0: bb17, otherwise: bb16];
|
||||
_6 = &fake shallow (*(_3.0: &str));
|
||||
_7 = &fake shallow (_3.0: &str);
|
||||
_8 = &fake shallow (_3.1: bool);
|
||||
StorageLive(_13);
|
||||
_13 = const true;
|
||||
switchInt(move _13) -> [0: bb17, otherwise: bb16];
|
||||
}
|
||||
|
||||
bb16: {
|
||||
StorageDead(_10);
|
||||
StorageDead(_13);
|
||||
FakeRead(ForMatchGuard, _6);
|
||||
FakeRead(ForMatchGuard, _7);
|
||||
FakeRead(ForMatchGuard, _8);
|
||||
_0 = const 1_u32;
|
||||
goto -> bb18;
|
||||
}
|
||||
|
||||
bb17: {
|
||||
StorageDead(_10);
|
||||
StorageDead(_13);
|
||||
falseEdge -> [real: bb3, imaginary: bb5];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,25 @@ Thir {
|
||||
ty: &'{erased} str,
|
||||
span: $DIR/str-patterns.rs:11:9: 11:16 (#0),
|
||||
extra: None,
|
||||
kind: Constant {
|
||||
value: Value {
|
||||
ty: &'{erased} str,
|
||||
valtree: Branch(
|
||||
[
|
||||
104_u8,
|
||||
101_u8,
|
||||
108_u8,
|
||||
108_u8,
|
||||
111_u8,
|
||||
],
|
||||
),
|
||||
kind: Deref {
|
||||
subpattern: Pat {
|
||||
ty: str,
|
||||
span: $DIR/str-patterns.rs:11:9: 11:16 (#0),
|
||||
extra: None,
|
||||
kind: Constant {
|
||||
value: Value {
|
||||
ty: str,
|
||||
valtree: Branch(
|
||||
[
|
||||
104_u8,
|
||||
101_u8,
|
||||
108_u8,
|
||||
108_u8,
|
||||
111_u8,
|
||||
],
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -42,21 +49,28 @@ Thir {
|
||||
ascriptions: [],
|
||||
},
|
||||
),
|
||||
kind: Constant {
|
||||
value: Value {
|
||||
ty: &'{erased} str,
|
||||
valtree: Branch(
|
||||
[
|
||||
99_u8,
|
||||
111_u8,
|
||||
110_u8,
|
||||
115_u8,
|
||||
116_u8,
|
||||
97_u8,
|
||||
110_u8,
|
||||
116_u8,
|
||||
],
|
||||
),
|
||||
kind: Deref {
|
||||
subpattern: Pat {
|
||||
ty: str,
|
||||
span: $DIR/str-patterns.rs:12:9: 12:17 (#0),
|
||||
extra: None,
|
||||
kind: Constant {
|
||||
value: Value {
|
||||
ty: str,
|
||||
valtree: Branch(
|
||||
[
|
||||
99_u8,
|
||||
111_u8,
|
||||
110_u8,
|
||||
115_u8,
|
||||
116_u8,
|
||||
97_u8,
|
||||
110_u8,
|
||||
116_u8,
|
||||
],
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user