Skip to content

Commit b9ccc46

Browse files
committed
Minimal implementation of implicit deref patterns
1 parent c1a859b commit b9ccc46

File tree

8 files changed

+174
-0
lines changed

8 files changed

+174
-0
lines changed

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ language_item_table! {
302302
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
303303
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
304304
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
305+
306+
String, sym::String, string, Target::Struct, GenericRequirement::None;
305307
}
306308

307309
pub enum GenericRequirement {

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
401401
}
402402
}
403403

404+
if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind {
405+
let tcx = self.tcx;
406+
let expected = self.resolve_vars_if_possible(expected);
407+
pat_ty = match expected.kind() {
408+
ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected,
409+
ty::Str => tcx.mk_static_str(),
410+
_ => pat_ty,
411+
};
412+
}
413+
404414
// Somewhat surprising: in this case, the subtyping relation goes the
405415
// opposite way as the other cases. Actually what we really want is not
406416
// a subtyping relation at all but rather that there exists a LUB

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
240240
}
241241

242242
TestKind::Eq { value, ty } => {
243+
let tcx = self.tcx;
244+
if let ty::Adt(def, _) = ty.kind() && Some(def.did()) == tcx.lang_items().string() {
245+
if !tcx.features().deref_patterns {
246+
bug!("matching on `String` went through without enabling deref_patterns");
247+
}
248+
let re_erased = tcx.lifetimes.re_erased;
249+
let ref_string = self.temp(tcx.mk_imm_ref(re_erased, ty), test.span);
250+
let ref_str_ty = tcx.mk_imm_ref(re_erased, tcx.types.str_);
251+
let ref_str = self.temp(ref_str_ty, test.span);
252+
let deref = tcx.require_lang_item(LangItem::Deref, None);
253+
let method = trait_method(tcx, deref, sym::deref, ty, &[]);
254+
let eq_block = self.cfg.start_new_block();
255+
self.cfg.push_assign(block, source_info, ref_string, Rvalue::Ref(re_erased, BorrowKind::Shared, place));
256+
self.cfg.terminate(
257+
block,
258+
source_info,
259+
TerminatorKind::Call {
260+
func: Operand::Constant(Box::new(Constant {
261+
span: test.span,
262+
user_ty: None,
263+
literal: method,
264+
})),
265+
args: vec![Operand::Move(ref_string)],
266+
destination: ref_str,
267+
target: Some(eq_block),
268+
cleanup: None,
269+
from_hir_call: false,
270+
fn_span: source_info.span
271+
}
272+
);
273+
self.non_scalar_compare(eq_block, make_target_blocks, source_info, value, ref_str, ref_str_ty);
274+
return;
275+
}
243276
if !ty.is_scalar() {
244277
// Use `PartialEq::eq` instead of `BinOp::Eq`
245278
// (the binop can only handle primitives)

library/alloc/src/string.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ use crate::vec::Vec;
364364
#[derive(PartialOrd, Eq, Ord)]
365365
#[cfg_attr(not(test), rustc_diagnostic_item = "String")]
366366
#[stable(feature = "rust1", since = "1.0.0")]
367+
#[cfg_attr(not(bootstrap), lang = "String")]
367368
pub struct String {
368369
vec: Vec<u8>,
369370
}

src/test/ui/deref-patterns/basic.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-pass
2+
// check-run-results
3+
4+
fn main() {
5+
test(Some(String::from("42")));
6+
test(Some(String::new()));
7+
test(None);
8+
}
9+
10+
fn test(o: Option<String>) {
11+
match o {
12+
Some("42") => println!("the answer"),
13+
Some(_) => println!("something else?"),
14+
None => println!("nil"),
15+
}
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
the answer
2+
something else?
3+
nil

src/test/ui/deref-patterns/mir.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// compile-flags: -Z unpretty=mir
2+
// build-pass
3+
fn main() {
4+
let s = Some(String::new());
5+
let a;
6+
match s {
7+
Some("a") => a = 1234,
8+
s => a = 4321,
9+
}
10+
}

src/test/ui/deref-patterns/mir.stdout

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// WARNING: This output format is intended for human consumers only
2+
// and is subject to change without notice. Knock yourself out.
3+
fn main() -> () {
4+
let mut _0: (); // return place in scope 0 at $DIR/mir.rs:3:11: 3:11
5+
let _1: std::option::Option<std::string::String>; // in scope 0 at $DIR/mir.rs:4:9: 4:10
6+
let mut _2: std::string::String; // in scope 0 at $DIR/mir.rs:4:18: 4:31
7+
let mut _4: &std::string::String; // in scope 0 at $DIR/mir.rs:7:14: 7:17
8+
let mut _5: &str; // in scope 0 at $DIR/mir.rs:7:14: 7:17
9+
let mut _6: bool; // in scope 0 at $DIR/mir.rs:7:14: 7:17
10+
let mut _7: isize; // in scope 0 at $DIR/mir.rs:7:9: 7:18
11+
let mut _9: bool; // in scope 0 at $DIR/mir.rs:10:1: 10:2
12+
scope 1 {
13+
debug s => _1; // in scope 1 at $DIR/mir.rs:4:9: 4:10
14+
let _3: i32; // in scope 1 at $DIR/mir.rs:5:9: 5:10
15+
scope 2 {
16+
debug a => _3; // in scope 2 at $DIR/mir.rs:5:9: 5:10
17+
let _8: std::option::Option<std::string::String>; // in scope 2 at $DIR/mir.rs:8:9: 8:10
18+
scope 3 {
19+
debug s => _8; // in scope 3 at $DIR/mir.rs:8:9: 8:10
20+
}
21+
}
22+
}
23+
24+
bb0: {
25+
_9 = const false; // scope 0 at $DIR/mir.rs:4:9: 4:10
26+
_2 = String::new() -> bb1; // scope 0 at $DIR/mir.rs:4:18: 4:31
27+
// mir::Constant
28+
// + span: $DIR/mir.rs:4:18: 4:29
29+
// + literal: Const { ty: fn() -> String {String::new}, val: Value(Scalar(<ZST>)) }
30+
}
31+
32+
bb1: {
33+
_9 = const true; // scope 0 at $DIR/mir.rs:4:13: 4:32
34+
Deinit(_1); // scope 0 at $DIR/mir.rs:4:13: 4:32
35+
((_1 as Some).0: std::string::String) = move _2; // scope 0 at $DIR/mir.rs:4:13: 4:32
36+
discriminant(_1) = 1; // scope 0 at $DIR/mir.rs:4:13: 4:32
37+
_7 = discriminant(_1); // scope 2 at $DIR/mir.rs:6:11: 6:12
38+
switchInt(move _7) -> [1_isize: bb3, otherwise: bb2]; // scope 2 at $DIR/mir.rs:6:5: 6:12
39+
}
40+
41+
bb2: {
42+
_9 = const false; // scope 2 at $DIR/mir.rs:8:9: 8:10
43+
_8 = move _1; // scope 2 at $DIR/mir.rs:8:9: 8:10
44+
_3 = const 4321_i32; // scope 3 at $DIR/mir.rs:8:14: 8:22
45+
drop(_8) -> [return: bb7, unwind: bb12]; // scope 2 at $DIR/mir.rs:8:21: 8:22
46+
}
47+
48+
bb3: {
49+
_4 = &((_1 as Some).0: std::string::String); // scope 2 at $DIR/mir.rs:7:14: 7:17
50+
_5 = <String as Deref>::deref(move _4) -> bb4; // scope 2 at $DIR/mir.rs:7:14: 7:17
51+
// mir::Constant
52+
// + span: $DIR/mir.rs:7:14: 7:17
53+
// + literal: Const { ty: for<'r> fn(&'r String) -> &'r <String as Deref>::Target {<String as Deref>::deref}, val: Value(Scalar(<ZST>)) }
54+
}
55+
56+
bb4: {
57+
_6 = <str as PartialEq>::eq(_5, const "a") -> [return: bb5, unwind: bb12]; // scope 2 at $DIR/mir.rs:7:14: 7:17
58+
// mir::Constant
59+
// + span: $DIR/mir.rs:7:14: 7:17
60+
// + literal: Const { ty: for<'r, 's> fn(&'r str, &'s str) -> bool {<str as PartialEq>::eq}, val: Value(Scalar(<ZST>)) }
61+
// mir::Constant
62+
// + span: $DIR/mir.rs:7:14: 7:17
63+
// + literal: Const { ty: &str, val: Value(Slice(..)) }
64+
}
65+
66+
bb5: {
67+
switchInt(move _6) -> [false: bb2, otherwise: bb6]; // scope 2 at $DIR/mir.rs:7:14: 7:17
68+
}
69+
70+
bb6: {
71+
_3 = const 1234_i32; // scope 2 at $DIR/mir.rs:7:22: 7:30
72+
goto -> bb7; // scope 2 at $DIR/mir.rs:7:22: 7:30
73+
}
74+
75+
bb7: {
76+
switchInt(_9) -> [false: bb8, otherwise: bb10]; // scope 0 at $DIR/mir.rs:10:1: 10:2
77+
}
78+
79+
bb8: {
80+
_9 = const false; // scope 0 at $DIR/mir.rs:10:1: 10:2
81+
return; // scope 0 at $DIR/mir.rs:10:2: 10:2
82+
}
83+
84+
bb9 (cleanup): {
85+
resume; // scope 0 at $DIR/mir.rs:3:1: 10:2
86+
}
87+
88+
bb10: {
89+
drop(_1) -> bb8; // scope 0 at $DIR/mir.rs:10:1: 10:2
90+
}
91+
92+
bb11 (cleanup): {
93+
drop(_1) -> bb9; // scope 0 at $DIR/mir.rs:10:1: 10:2
94+
}
95+
96+
bb12 (cleanup): {
97+
switchInt(_9) -> [false: bb9, otherwise: bb11]; // scope 0 at $DIR/mir.rs:10:1: 10:2
98+
}
99+
}

0 commit comments

Comments
 (0)