Auto merge of #151155 - Zalathar:str, r=Nadrieril
THIR patterns: Always use type `str` for string-constant-value nodes Historically, constants and literals of type `&str` have been represented in THIR patterns as `PatKind::Const` nodes with type `&str`. That's fine for stable Rust, but `feature(deref_patterns)` also created a need to have string literal patterns of type `str` in some cases, which resulted in a number of additional special cases and inconsistencies in typechecking and in HIR-to-THIR-to-MIR lowering of patterns. We can avoid several of those special cases by having THIR treat string-constant-values as fundamentally being of type `str`, and then using `PatKind::Deref` to represent the additional `&` layer in the common case where it is needed. This allows bare `str` patterns to require very little special treatment. Existing tests should already do a good job of demonstrating that this implementation change does not affect the stable language.
This commit is contained in:
commit
d2015e2359
8 changed files with 423 additions and 122 deletions
|
|
@ -2,7 +2,6 @@ use std::sync::Arc;
|
|||
|
||||
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 @@ impl<'tcx> MatchPairTree<'tcx> {
|
|||
}
|
||||
|
||||
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 @@ impl<'tcx> MatchPairTree<'tcx> {
|
|||
} 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,19 +1339,13 @@ 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.
|
||||
ScalarEq {
|
||||
value: ty::Value<'tcx>,
|
||||
/// Type of the corresponding pattern node.
|
||||
pat_ty: Ty<'tcx>,
|
||||
},
|
||||
ScalarEq { value: ty::Value<'tcx> },
|
||||
|
||||
/// Test whether the value falls within an inclusive or exclusive range.
|
||||
Range(Arc<PatRange<'tcx>>),
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ use std::sync::Arc;
|
|||
|
||||
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,10 +39,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
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 }
|
||||
TestKind::ScalarEq { value }
|
||||
}
|
||||
|
||||
TestableCase::Range(ref range) => {
|
||||
|
|
@ -141,47 +141,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
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,11 +178,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fail_block,
|
||||
source_info,
|
||||
expected_value_operand,
|
||||
Operand::Copy(actual_value_place),
|
||||
Operand::Copy(actual_value_ref_place),
|
||||
);
|
||||
}
|
||||
|
||||
TestKind::ScalarEq { value, pat_ty } => {
|
||||
TestKind::ScalarEq { value } => {
|
||||
let tcx = self.tcx;
|
||||
let success_block = target_block(TestBranch::Success);
|
||||
let fail_block = target_block(TestBranch::Failure);
|
||||
|
|
@ -205,12 +191,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let mut expected_value_operand =
|
||||
self.literal_operand(test.span, Const::from_ty_value(tcx, value));
|
||||
|
||||
let mut actual_value_ty = pat_ty;
|
||||
let mut actual_value_place = place;
|
||||
|
||||
match pat_ty.kind() {
|
||||
match value.ty.kind() {
|
||||
&ty::Pat(base, _) => {
|
||||
assert_eq!(pat_ty, value.ty);
|
||||
assert!(base.is_trivially_pure_clone_copy());
|
||||
|
||||
let transmuted_place = self.temp(base, test.span);
|
||||
|
|
@ -234,15 +218,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
);
|
||||
|
||||
actual_value_place = transmuted_place;
|
||||
actual_value_ty = base;
|
||||
expected_value_operand = Operand::Copy(transmuted_expect);
|
||||
expected_value_ty = base;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
assert_eq!(expected_value_ty, actual_value_ty);
|
||||
assert!(actual_value_ty.is_scalar());
|
||||
assert!(expected_value_ty.is_scalar());
|
||||
|
||||
self.compare(
|
||||
block,
|
||||
|
|
|
|||
|
|
@ -289,32 +289,29 @@ impl<'tcx> ConstToPat<'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 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
|||
17
tests/ui/thir-print/str-patterns.rs
Normal file
17
tests/ui/thir-print/str-patterns.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#![crate_type = "rlib"]
|
||||
//@ edition: 2024
|
||||
//@ compile-flags: -Zunpretty=thir-flat
|
||||
//@ check-pass
|
||||
|
||||
// Snapshot test capturing the THIR pattern structure produced by
|
||||
// string-literal and string-constant patterns.
|
||||
|
||||
pub fn hello_world(x: &str) {
|
||||
match x {
|
||||
"hello" => {}
|
||||
CONSTANT => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
const CONSTANT: &str = "constant";
|
||||
324
tests/ui/thir-print/str-patterns.stdout
Normal file
324
tests/ui/thir-print/str-patterns.stdout
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
DefId(0:3 ~ str_patterns[fc71]::hello_world):
|
||||
Thir {
|
||||
body_type: Fn(
|
||||
fn(&'{erased} str),
|
||||
),
|
||||
arms: [
|
||||
Arm {
|
||||
pattern: Pat {
|
||||
ty: &'{erased} str,
|
||||
span: $DIR/str-patterns.rs:11:9: 11:16 (#0),
|
||||
extra: None,
|
||||
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,
|
||||
],
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
guard: None,
|
||||
body: e3,
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).9),
|
||||
scope: Node(9),
|
||||
span: $DIR/str-patterns.rs:11:9: 11:22 (#0),
|
||||
},
|
||||
Arm {
|
||||
pattern: Pat {
|
||||
ty: &'{erased} str,
|
||||
span: $DIR/str-patterns.rs:12:9: 12:17 (#0),
|
||||
extra: Some(
|
||||
PatExtra {
|
||||
expanded_const: Some(
|
||||
DefId(0:4 ~ str_patterns[fc71]::CONSTANT),
|
||||
),
|
||||
ascriptions: [],
|
||||
},
|
||||
),
|
||||
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,
|
||||
],
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
guard: None,
|
||||
body: e5,
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).15),
|
||||
scope: Node(15),
|
||||
span: $DIR/str-patterns.rs:12:9: 12:23 (#0),
|
||||
},
|
||||
Arm {
|
||||
pattern: Pat {
|
||||
ty: &'{erased} str,
|
||||
span: $DIR/str-patterns.rs:13:9: 13:10 (#0),
|
||||
extra: None,
|
||||
kind: Wild,
|
||||
},
|
||||
guard: None,
|
||||
body: e7,
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).19),
|
||||
scope: Node(19),
|
||||
span: $DIR/str-patterns.rs:13:9: 13:16 (#0),
|
||||
},
|
||||
],
|
||||
blocks: [
|
||||
Block {
|
||||
targeted_by_break: false,
|
||||
region_scope: Node(11),
|
||||
span: $DIR/str-patterns.rs:11:20: 11:22 (#0),
|
||||
stmts: [],
|
||||
expr: None,
|
||||
safety_mode: Safe,
|
||||
},
|
||||
Block {
|
||||
targeted_by_break: false,
|
||||
region_scope: Node(17),
|
||||
span: $DIR/str-patterns.rs:12:21: 12:23 (#0),
|
||||
stmts: [],
|
||||
expr: None,
|
||||
safety_mode: Safe,
|
||||
},
|
||||
Block {
|
||||
targeted_by_break: false,
|
||||
region_scope: Node(21),
|
||||
span: $DIR/str-patterns.rs:13:14: 13:16 (#0),
|
||||
stmts: [],
|
||||
expr: None,
|
||||
safety_mode: Safe,
|
||||
},
|
||||
Block {
|
||||
targeted_by_break: false,
|
||||
region_scope: Node(3),
|
||||
span: $DIR/str-patterns.rs:9:29: 15:2 (#0),
|
||||
stmts: [],
|
||||
expr: Some(
|
||||
e9,
|
||||
),
|
||||
safety_mode: Safe,
|
||||
},
|
||||
],
|
||||
exprs: [
|
||||
Expr {
|
||||
kind: VarRef {
|
||||
id: LocalVarId(
|
||||
HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).2),
|
||||
),
|
||||
},
|
||||
ty: &'{erased} str,
|
||||
temp_scope_id: 5,
|
||||
span: $DIR/str-patterns.rs:10:11: 10:12 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Scope {
|
||||
region_scope: Node(5),
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).5),
|
||||
value: e0,
|
||||
},
|
||||
ty: &'{erased} str,
|
||||
temp_scope_id: 5,
|
||||
span: $DIR/str-patterns.rs:10:11: 10:12 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Block {
|
||||
block: b0,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 10,
|
||||
span: $DIR/str-patterns.rs:11:20: 11:22 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Scope {
|
||||
region_scope: Node(10),
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).10),
|
||||
value: e2,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 10,
|
||||
span: $DIR/str-patterns.rs:11:20: 11:22 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Block {
|
||||
block: b1,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 16,
|
||||
span: $DIR/str-patterns.rs:12:21: 12:23 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Scope {
|
||||
region_scope: Node(16),
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).16),
|
||||
value: e4,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 16,
|
||||
span: $DIR/str-patterns.rs:12:21: 12:23 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Block {
|
||||
block: b2,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 20,
|
||||
span: $DIR/str-patterns.rs:13:14: 13:16 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Scope {
|
||||
region_scope: Node(20),
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).20),
|
||||
value: e6,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 20,
|
||||
span: $DIR/str-patterns.rs:13:14: 13:16 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Match {
|
||||
scrutinee: e1,
|
||||
arms: [
|
||||
a0,
|
||||
a1,
|
||||
a2,
|
||||
],
|
||||
match_source: Normal,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 4,
|
||||
span: $DIR/str-patterns.rs:10:5: 14:6 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Scope {
|
||||
region_scope: Node(4),
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).4),
|
||||
value: e8,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 4,
|
||||
span: $DIR/str-patterns.rs:10:5: 14:6 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Block {
|
||||
block: b3,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 22,
|
||||
span: $DIR/str-patterns.rs:9:29: 15:2 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Scope {
|
||||
region_scope: Node(22),
|
||||
hir_id: HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).22),
|
||||
value: e10,
|
||||
},
|
||||
ty: (),
|
||||
temp_scope_id: 22,
|
||||
span: $DIR/str-patterns.rs:9:29: 15:2 (#0),
|
||||
},
|
||||
],
|
||||
stmts: [],
|
||||
params: [
|
||||
Param {
|
||||
pat: Some(
|
||||
Pat {
|
||||
ty: &'{erased} str,
|
||||
span: $DIR/str-patterns.rs:9:20: 9:21 (#0),
|
||||
extra: None,
|
||||
kind: Binding {
|
||||
name: "x",
|
||||
mode: BindingMode(
|
||||
No,
|
||||
Not,
|
||||
),
|
||||
var: LocalVarId(
|
||||
HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).2),
|
||||
),
|
||||
ty: &'{erased} str,
|
||||
subpattern: None,
|
||||
is_primary: true,
|
||||
is_shorthand: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
ty: &'{erased} str,
|
||||
ty_span: Some(
|
||||
$DIR/str-patterns.rs:9:23: 9:27 (#0),
|
||||
),
|
||||
self_kind: None,
|
||||
hir_id: Some(
|
||||
HirId(DefId(0:3 ~ str_patterns[fc71]::hello_world).1),
|
||||
),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
DefId(0:4 ~ str_patterns[fc71]::CONSTANT):
|
||||
Thir {
|
||||
body_type: Const(
|
||||
&'{erased} str,
|
||||
),
|
||||
arms: [],
|
||||
blocks: [],
|
||||
exprs: [
|
||||
Expr {
|
||||
kind: Literal {
|
||||
lit: Spanned {
|
||||
node: Str(
|
||||
"constant",
|
||||
Cooked,
|
||||
),
|
||||
span: $DIR/str-patterns.rs:17:24: 17:34 (#0),
|
||||
},
|
||||
neg: false,
|
||||
},
|
||||
ty: &'{erased} str,
|
||||
temp_scope_id: 5,
|
||||
span: $DIR/str-patterns.rs:17:24: 17:34 (#0),
|
||||
},
|
||||
Expr {
|
||||
kind: Scope {
|
||||
region_scope: Node(5),
|
||||
hir_id: HirId(DefId(0:4 ~ str_patterns[fc71]::CONSTANT).5),
|
||||
value: e0,
|
||||
},
|
||||
ty: &'{erased} str,
|
||||
temp_scope_id: 5,
|
||||
span: $DIR/str-patterns.rs:17:24: 17:34 (#0),
|
||||
},
|
||||
],
|
||||
stmts: [],
|
||||
params: [],
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue