Minimal implementation of implicit deref patterns

This commit is contained in:
Deadbeef 2022-07-05 07:19:13 +00:00
parent 7c75fe4c85
commit b2cb42d6a7
8 changed files with 174 additions and 0 deletions

View File

@ -302,6 +302,8 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
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 {

View File

@ -401,6 +401,16 @@ fn check_pat_lit(
}
}
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

View File

@ -240,6 +240,39 @@ pub(super) fn perform_test(
}
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)

View File

@ -364,6 +364,7 @@
#[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>,
}

View File

@ -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"),
}
}

View File

@ -0,0 +1,3 @@
the answer
something else?
nil

View File

@ -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,
}
}

View File

@ -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
}
}