Skip to content

Commit c41740d

Browse files
committed
make str literal patterns usable in deref patterns
Specifically, this allows string literal patterns to be used where a `str` is expected when `deref_patterns` is enabled.
1 parent 191df20 commit c41740d

File tree

9 files changed

+103
-29
lines changed

9 files changed

+103
-29
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+11
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
775775
}
776776
}
777777

778+
// When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow
779+
// string literal patterns to have type `str`. This is accounted for when lowering to MIR.
780+
if self.tcx.features().deref_patterns()
781+
&& let hir::PatExprKind::Lit {
782+
lit: Spanned { node: ast::LitKind::Str(..), .. }, ..
783+
} = lt.kind
784+
&& self.try_structurally_resolve_type(span, expected).is_str()
785+
{
786+
pat_ty = self.tcx.types.str_;
787+
}
788+
778789
if self.tcx.features().string_deref_patterns()
779790
&& let hir::PatExprKind::Lit {
780791
lit: Spanned { node: ast::LitKind::Str(..), .. }, ..

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

+23
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
146146
let mut place = place;
147147
let mut block = block;
148148
match ty.kind() {
149+
ty::Str => {
150+
// String literal patterns may have type `str` if `deref_patterns` is
151+
// enabled, in order to allow `deref!("..."): String`. In this case, `value`
152+
// is of type `&str`, so we compare it to `&place`.
153+
if !tcx.features().deref_patterns() {
154+
span_bug!(
155+
test.span,
156+
"matching on `str` went through without enabling deref_patterns"
157+
);
158+
}
159+
let re_erased = tcx.lifetimes.re_erased;
160+
let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_);
161+
let ref_place = self.temp(ref_str_ty, test.span);
162+
// `let ref_place: &str = &place;`
163+
self.cfg.push_assign(
164+
block,
165+
self.source_info(test.span),
166+
ref_place,
167+
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
168+
);
169+
place = ref_place;
170+
ty = ref_str_ty;
171+
}
149172
ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => {
150173
if !tcx.features().string_deref_patterns() {
151174
span_bug!(

compiler/rustc_mir_build/src/thir/constant.rs

+6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ pub(crate) fn lit_to_const<'tcx>(
3737
let str_bytes = s.as_str().as_bytes();
3838
ty::ValTree::from_raw_bytes(tcx, str_bytes)
3939
}
40+
(ast::LitKind::Str(s, _), ty::Str) if tcx.features().deref_patterns() => {
41+
// String literal patterns may have type `str` if `deref_patterns` is enabled, in order
42+
// to allow `deref!("..."): String`.
43+
let str_bytes = s.as_str().as_bytes();
44+
ty::ValTree::from_raw_bytes(tcx, str_bytes)
45+
}
4046
(ast::LitKind::ByteStr(data, _), ty::Ref(_, inner_ty, _))
4147
if matches!(inner_ty.kind(), ty::Slice(_)) =>
4248
{

compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs

+9
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,15 @@ impl<'tcx> ConstToPat<'tcx> {
280280
slice: None,
281281
suffix: Box::new([]),
282282
},
283+
ty::Str => {
284+
// String literal patterns may have type `str` if `deref_patterns` is enabled, in
285+
// order to allow `deref!("..."): String`. Since we need a `&str` for the comparison
286+
// when lowering to MIR in `Builder::perform_test`, treat the constant as a `&str`.
287+
let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty);
288+
PatKind::Constant {
289+
value: mir::Const::Ty(ref_str_ty, ty::Const::new_value(tcx, cv, ref_str_ty)),
290+
}
291+
}
283292
ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
284293
// `&str` is represented as a valtree, let's keep using this
285294
// optimization for now.

tests/ui/pattern/deref-patterns/needs-gate.rs

+7
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,11 @@ fn main() {
1212
//~^ ERROR: mismatched types
1313
_ => {}
1414
}
15+
16+
// `deref_patterns` allows string and byte string literals to have non-ref types.
17+
match *"test" {
18+
"test" => {}
19+
//~^ ERROR: mismatched types
20+
_ => {}
21+
}
1522
}

tests/ui/pattern/deref-patterns/needs-gate.stderr

+9-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@ help: consider dereferencing to access the inner value using the Deref trait
2323
LL | match *Box::new(0) {
2424
| +
2525

26-
error: aborting due to 2 previous errors
26+
error[E0308]: mismatched types
27+
--> $DIR/needs-gate.rs:18:9
28+
|
29+
LL | match *"test" {
30+
| ------- this expression has type `str`
31+
LL | "test" => {}
32+
| ^^^^^^ expected `str`, found `&str`
33+
34+
error: aborting due to 3 previous errors
2735

2836
Some errors have detailed explanations: E0308, E0658.
2937
For more information about an error, try `rustc --explain E0308`.
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@ run-pass
2+
//! Test deref patterns using string and bytestring literals.
3+
4+
#![feature(deref_patterns)]
5+
#![allow(incomplete_features)]
6+
7+
fn main() {
8+
for (test_in, test_expect) in [("zero", 0), ("one", 1), ("two", 2)] {
9+
// Test string literal patterns having type `str`.
10+
let test_actual = match *test_in {
11+
"zero" => 0,
12+
"one" => 1,
13+
_ => 2,
14+
};
15+
assert_eq!(test_actual, test_expect);
16+
17+
// Test string literals in explicit `deref!(_)` patterns.
18+
let test_actual = match test_in.to_string() {
19+
deref!("zero") => 0,
20+
deref!("one") => 1,
21+
_ => 2,
22+
};
23+
assert_eq!(test_actual, test_expect);
24+
}
25+
26+
// Test that we can still mutate in the match arm after using a literal to test equality:
27+
let mut test = "test".to_string();
28+
if let deref!(s @ "test") = &mut test {
29+
s.make_ascii_uppercase();
30+
}
31+
assert_eq!(test, "TEST");
32+
}

tests/ui/pattern/deref-patterns/typeck_fail.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,14 @@
22
#![allow(incomplete_features)]
33

44
fn main() {
5-
// FIXME(deref_patterns): fails to typecheck because `"foo"` has type &str but deref creates a
6-
// place of type `str`.
5+
// FIXME(deref_patterns): fails to typecheck because string literal patterns don't peel
6+
// references from the scrutinee.
77
match "foo".to_string() {
8-
deref!("foo") => {}
9-
//~^ ERROR: mismatched types
108
"foo" => {}
119
//~^ ERROR: mismatched types
1210
_ => {}
1311
}
1412
match &"foo".to_string() {
15-
deref!("foo") => {}
16-
//~^ ERROR: mismatched types
1713
"foo" => {}
1814
//~^ ERROR: mismatched types
1915
_ => {}
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,24 @@
11
error[E0308]: mismatched types
2-
--> $DIR/typeck_fail.rs:8:16
2+
--> $DIR/typeck_fail.rs:8:9
33
|
44
LL | match "foo".to_string() {
55
| ----------------- this expression has type `String`
6-
LL | deref!("foo") => {}
7-
| ^^^^^ expected `str`, found `&str`
8-
9-
error[E0308]: mismatched types
10-
--> $DIR/typeck_fail.rs:10:9
11-
|
12-
LL | match "foo".to_string() {
13-
| ----------------- this expression has type `String`
14-
...
156
LL | "foo" => {}
167
| ^^^^^ expected `String`, found `&str`
178

189
error[E0308]: mismatched types
19-
--> $DIR/typeck_fail.rs:15:16
20-
|
21-
LL | match &"foo".to_string() {
22-
| ------------------ this expression has type `&String`
23-
LL | deref!("foo") => {}
24-
| ^^^^^ expected `str`, found `&str`
25-
26-
error[E0308]: mismatched types
27-
--> $DIR/typeck_fail.rs:17:9
10+
--> $DIR/typeck_fail.rs:13:9
2811
|
2912
LL | match &"foo".to_string() {
3013
| ------------------ this expression has type `&String`
31-
...
3214
LL | "foo" => {}
3315
| ^^^^^ expected `&String`, found `&str`
3416
|
3517
= note: expected reference `&String`
3618
found reference `&'static str`
3719

3820
error[E0308]: mismatched types
39-
--> $DIR/typeck_fail.rs:24:9
21+
--> $DIR/typeck_fail.rs:20:9
4022
|
4123
LL | match Some(0) {
4224
| ------- this expression has type `Option<{integer}>`
@@ -46,6 +28,6 @@ LL | Ok(0) => {}
4628
= note: expected enum `Option<{integer}>`
4729
found enum `Result<_, _>`
4830

49-
error: aborting due to 5 previous errors
31+
error: aborting due to 3 previous errors
5032

5133
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)