From b2cb42d6a7b14b35e79f56228682148ecdd8a6a9 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Tue, 5 Jul 2022 07:19:13 +0000 Subject: [PATCH] Minimal implementation of implicit deref patterns --- compiler/rustc_hir/src/lang_items.rs | 2 + compiler/rustc_hir_typeck/src/pat.rs | 10 ++ .../rustc_mir_build/src/build/matches/test.rs | 33 +++++++ library/alloc/src/string.rs | 1 + src/test/ui/deref-patterns/basic.rs | 16 +++ src/test/ui/deref-patterns/basic.run.stdout | 3 + src/test/ui/deref-patterns/mir.rs | 10 ++ src/test/ui/deref-patterns/mir.stdout | 99 +++++++++++++++++++ 8 files changed, 174 insertions(+) create mode 100644 src/test/ui/deref-patterns/basic.rs create mode 100644 src/test/ui/deref-patterns/basic.run.stdout create mode 100644 src/test/ui/deref-patterns/mir.rs create mode 100644 src/test/ui/deref-patterns/mir.stdout 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, } 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) { + 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; // 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; // 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()) } + } + + 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 = ::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 ::Target {::deref}, val: Value(Scalar()) } + } + + bb4: { + _6 = ::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 {::eq}, val: Value(Scalar()) } + // 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 + } +}