about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDeadbeef <ent3rm4n@gmail.com>2022-07-05 07:19:13 +0000
committerDeadbeef <ent3rm4n@gmail.com>2022-11-17 12:46:43 +0000
commitb2cb42d6a7b14b35e79f56228682148ecdd8a6a9 (patch)
treea017d92db5f6602805df299c3503a4f91a9e766c
parent7c75fe4c8547c276574cacb144919d67fd8ab302 (diff)
downloadrust-b2cb42d6a7b14b35e79f56228682148ecdd8a6a9.tar.gz
rust-b2cb42d6a7b14b35e79f56228682148ecdd8a6a9.zip
Minimal implementation of implicit deref patterns
-rw-r--r--compiler/rustc_hir/src/lang_items.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs10
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs33
-rw-r--r--library/alloc/src/string.rs1
-rw-r--r--src/test/ui/deref-patterns/basic.rs16
-rw-r--r--src/test/ui/deref-patterns/basic.run.stdout3
-rw-r--r--src/test/ui/deref-patterns/mir.rs10
-rw-r--r--src/test/ui/deref-patterns/mir.stdout99
8 files changed, 174 insertions, 0 deletions
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index a55224d1097..7e9d1078049 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -302,6 +302,8 @@ language_item_table! {
     Range,                   sym::Range,               range_struct,               Target::Struct,         GenericRequirement::None;
     RangeToInclusive,        sym::RangeToInclusive,    range_to_inclusive_struct,  Target::Struct,         GenericRequirement::None;
     RangeTo,                 sym::RangeTo,             range_to_struct,            Target::Struct,         GenericRequirement::None;
+
+    String,                  sym::String,              string,                     Target::Struct,         GenericRequirement::None;
 }
 
 pub enum GenericRequirement {
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index eb10f3e2c10..f2d5f754c6f 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -401,6 +401,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
+        if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind {
+            let tcx = self.tcx;
+            let expected = self.resolve_vars_if_possible(expected);
+            pat_ty = match expected.kind() {
+                ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected,
+                ty::Str => tcx.mk_static_str(),
+                _ => pat_ty,
+            };
+        }
+
         // Somewhat surprising: in this case, the subtyping relation goes the
         // opposite way as the other cases. Actually what we really want is not
         // a subtyping relation at all but rather that there exists a LUB
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index b597ecfaa4d..e6c01b72b1b 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -240,6 +240,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
 
             TestKind::Eq { value, ty } => {
+                let tcx = self.tcx;
+                if let ty::Adt(def, _) = ty.kind() && Some(def.did()) == tcx.lang_items().string() {
+                    if !tcx.features().deref_patterns {
+                        bug!("matching on `String` went through without enabling deref_patterns");
+                    }
+                    let re_erased = tcx.lifetimes.re_erased;
+                    let ref_string = self.temp(tcx.mk_imm_ref(re_erased, ty), test.span);
+                    let ref_str_ty = tcx.mk_imm_ref(re_erased, tcx.types.str_);
+                    let ref_str = self.temp(ref_str_ty, test.span);
+                    let deref = tcx.require_lang_item(LangItem::Deref, None);
+                    let method = trait_method(tcx, deref, sym::deref, ty, &[]);
+                    let eq_block = self.cfg.start_new_block();
+                    self.cfg.push_assign(block, source_info, ref_string, Rvalue::Ref(re_erased, BorrowKind::Shared, place));
+                    self.cfg.terminate(
+                        block,
+                        source_info,
+                        TerminatorKind::Call {
+                            func: Operand::Constant(Box::new(Constant {
+                                span: test.span,
+                                user_ty: None,
+                                literal: method,
+                            })),
+                            args: vec![Operand::Move(ref_string)],
+                            destination: ref_str,
+                            target: Some(eq_block),
+                            cleanup: None,
+                            from_hir_call: false,
+                            fn_span: source_info.span
+                        }
+                    );
+                    self.non_scalar_compare(eq_block, make_target_blocks, source_info, value, ref_str, ref_str_ty);
+                    return;
+                }
                 if !ty.is_scalar() {
                     // Use `PartialEq::eq` instead of `BinOp::Eq`
                     // (the binop can only handle primitives)
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index c9ba8921f6e..5799ef778c4 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -364,6 +364,7 @@ use crate::vec::Vec;
 #[derive(PartialOrd, Eq, Ord)]
 #[cfg_attr(not(test), rustc_diagnostic_item = "String")]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(bootstrap), lang = "String")]
 pub struct String {
     vec: Vec<u8>,
 }
diff --git a/src/test/ui/deref-patterns/basic.rs b/src/test/ui/deref-patterns/basic.rs
new file mode 100644
index 00000000000..aa854ed7532
--- /dev/null
+++ b/src/test/ui/deref-patterns/basic.rs
@@ -0,0 +1,16 @@
+// run-pass
+// check-run-results
+
+fn main() {
+    test(Some(String::from("42")));
+    test(Some(String::new()));
+    test(None);
+}
+
+fn test(o: Option<String>) {
+    match o {
+        Some("42") => println!("the answer"),
+        Some(_) => println!("something else?"),
+        None => println!("nil"),
+    }
+}
\ No newline at end of file
diff --git a/src/test/ui/deref-patterns/basic.run.stdout b/src/test/ui/deref-patterns/basic.run.stdout
new file mode 100644
index 00000000000..e50df058281
--- /dev/null
+++ b/src/test/ui/deref-patterns/basic.run.stdout
@@ -0,0 +1,3 @@
+the answer
+something else?
+nil
diff --git a/src/test/ui/deref-patterns/mir.rs b/src/test/ui/deref-patterns/mir.rs
new file mode 100644
index 00000000000..3be4a82d76c
--- /dev/null
+++ b/src/test/ui/deref-patterns/mir.rs
@@ -0,0 +1,10 @@
+// compile-flags: -Z unpretty=mir
+// build-pass
+fn main() {
+    let s = Some(String::new());
+    let a;
+    match s {
+        Some("a") => a = 1234,
+        s => a = 4321,
+    }
+}
\ No newline at end of file
diff --git a/src/test/ui/deref-patterns/mir.stdout b/src/test/ui/deref-patterns/mir.stdout
new file mode 100644
index 00000000000..211f50fa1a8
--- /dev/null
+++ b/src/test/ui/deref-patterns/mir.stdout
@@ -0,0 +1,99 @@
+// WARNING: This output format is intended for human consumers only
+// and is subject to change without notice. Knock yourself out.
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/mir.rs:3:11: 3:11
+    let _1: std::option::Option<std::string::String>; // in scope 0 at $DIR/mir.rs:4:9: 4:10
+    let mut _2: std::string::String;     // in scope 0 at $DIR/mir.rs:4:18: 4:31
+    let mut _4: &std::string::String;    // in scope 0 at $DIR/mir.rs:7:14: 7:17
+    let mut _5: &str;                    // in scope 0 at $DIR/mir.rs:7:14: 7:17
+    let mut _6: bool;                    // in scope 0 at $DIR/mir.rs:7:14: 7:17
+    let mut _7: isize;                   // in scope 0 at $DIR/mir.rs:7:9: 7:18
+    let mut _9: bool;                    // in scope 0 at $DIR/mir.rs:10:1: 10:2
+    scope 1 {
+        debug s => _1;                   // in scope 1 at $DIR/mir.rs:4:9: 4:10
+        let _3: i32;                     // in scope 1 at $DIR/mir.rs:5:9: 5:10
+        scope 2 {
+            debug a => _3;               // in scope 2 at $DIR/mir.rs:5:9: 5:10
+            let _8: std::option::Option<std::string::String>; // in scope 2 at $DIR/mir.rs:8:9: 8:10
+            scope 3 {
+                debug s => _8;           // in scope 3 at $DIR/mir.rs:8:9: 8:10
+            }
+        }
+    }
+
+    bb0: {
+        _9 = const false;                // scope 0 at $DIR/mir.rs:4:9: 4:10
+        _2 = String::new() -> bb1;       // scope 0 at $DIR/mir.rs:4:18: 4:31
+                                         // mir::Constant
+                                         // + span: $DIR/mir.rs:4:18: 4:29
+                                         // + literal: Const { ty: fn() -> String {String::new}, val: Value(Scalar(<ZST>)) }
+    }
+
+    bb1: {
+        _9 = const true;                 // scope 0 at $DIR/mir.rs:4:13: 4:32
+        Deinit(_1);                      // scope 0 at $DIR/mir.rs:4:13: 4:32
+        ((_1 as Some).0: std::string::String) = move _2; // scope 0 at $DIR/mir.rs:4:13: 4:32
+        discriminant(_1) = 1;            // scope 0 at $DIR/mir.rs:4:13: 4:32
+        _7 = discriminant(_1);           // scope 2 at $DIR/mir.rs:6:11: 6:12
+        switchInt(move _7) -> [1_isize: bb3, otherwise: bb2]; // scope 2 at $DIR/mir.rs:6:5: 6:12
+    }
+
+    bb2: {
+        _9 = const false;                // scope 2 at $DIR/mir.rs:8:9: 8:10
+        _8 = move _1;                    // scope 2 at $DIR/mir.rs:8:9: 8:10
+        _3 = const 4321_i32;             // scope 3 at $DIR/mir.rs:8:14: 8:22
+        drop(_8) -> [return: bb7, unwind: bb12]; // scope 2 at $DIR/mir.rs:8:21: 8:22
+    }
+
+    bb3: {
+        _4 = &((_1 as Some).0: std::string::String); // scope 2 at $DIR/mir.rs:7:14: 7:17
+        _5 = <String as Deref>::deref(move _4) -> bb4; // scope 2 at $DIR/mir.rs:7:14: 7:17
+                                         // mir::Constant
+                                         // + span: $DIR/mir.rs:7:14: 7:17
+                                         // + literal: Const { ty: for<'r> fn(&'r String) -> &'r <String as Deref>::Target {<String as Deref>::deref}, val: Value(Scalar(<ZST>)) }
+    }
+
+    bb4: {
+        _6 = <str as PartialEq>::eq(_5, const "a") -> [return: bb5, unwind: bb12]; // scope 2 at $DIR/mir.rs:7:14: 7:17
+                                         // mir::Constant
+                                         // + span: $DIR/mir.rs:7:14: 7:17
+                                         // + literal: Const { ty: for<'r, 's> fn(&'r str, &'s str) -> bool {<str as PartialEq>::eq}, val: Value(Scalar(<ZST>)) }
+                                         // mir::Constant
+                                         // + span: $DIR/mir.rs:7:14: 7:17
+                                         // + literal: Const { ty: &str, val: Value(Slice(..)) }
+    }
+
+    bb5: {
+        switchInt(move _6) -> [false: bb2, otherwise: bb6]; // scope 2 at $DIR/mir.rs:7:14: 7:17
+    }
+
+    bb6: {
+        _3 = const 1234_i32;             // scope 2 at $DIR/mir.rs:7:22: 7:30
+        goto -> bb7;                     // scope 2 at $DIR/mir.rs:7:22: 7:30
+    }
+
+    bb7: {
+        switchInt(_9) -> [false: bb8, otherwise: bb10]; // scope 0 at $DIR/mir.rs:10:1: 10:2
+    }
+
+    bb8: {
+        _9 = const false;                // scope 0 at $DIR/mir.rs:10:1: 10:2
+        return;                          // scope 0 at $DIR/mir.rs:10:2: 10:2
+    }
+
+    bb9 (cleanup): {
+        resume;                          // scope 0 at $DIR/mir.rs:3:1: 10:2
+    }
+
+    bb10: {
+        drop(_1) -> bb8;                 // scope 0 at $DIR/mir.rs:10:1: 10:2
+    }
+
+    bb11 (cleanup): {
+        drop(_1) -> bb9;                 // scope 0 at $DIR/mir.rs:10:1: 10:2
+    }
+
+    bb12 (cleanup): {
+        switchInt(_9) -> [false: bb9, otherwise: bb11]; // scope 0 at $DIR/mir.rs:10:1: 10:2
+    }
+}