-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Minimal implementation of implicit deref patterns for Strings #98914
Changes from all commits
b2cb42d
0537d30
d1c6b79
c74f155
64a17a0
bc51f87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess here is what you would have to look for all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (Note to self: this would also be a place to provide suggestions if no impl matches, but there are some, potentially filtered using some heuristic, maybe limited only to std.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like you're missing a check here, even for this minimal case: you should be looking at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is previous typeck code that prevents this. Here's the output
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes me preemptively sad, knowing that we'll have to have deref checks in E0308 both ways to account for deref in patterns. :( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could just be a |
||
if !tcx.features().string_deref_patterns { | ||
bug!("matching on `String` went through without enabling string_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) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// MIR for `foo` after PreCodegen | ||
|
||
fn foo(_1: Option<String>) -> i32 { | ||
debug s => _1; // in scope 0 at $DIR/string.rs:+0:12: +0:13 | ||
let mut _0: i32; // return place in scope 0 at $DIR/string.rs:+0:34: +0:37 | ||
let mut _2: &std::string::String; // in scope 0 at $DIR/string.rs:+2:14: +2:17 | ||
let mut _3: &str; // in scope 0 at $DIR/string.rs:+2:14: +2:17 | ||
let mut _4: bool; // in scope 0 at $DIR/string.rs:+2:14: +2:17 | ||
let mut _5: isize; // in scope 0 at $DIR/string.rs:+2:9: +2:18 | ||
let _6: std::option::Option<std::string::String>; // in scope 0 at $DIR/string.rs:+3:9: +3:10 | ||
let mut _7: bool; // in scope 0 at $DIR/string.rs:+5:1: +5:2 | ||
scope 1 { | ||
debug s => _6; // in scope 1 at $DIR/string.rs:+3:9: +3:10 | ||
} | ||
|
||
bb0: { | ||
_7 = const false; // scope 0 at $DIR/string.rs:+1:11: +1:12 | ||
_7 = const true; // scope 0 at $DIR/string.rs:+1:11: +1:12 | ||
_5 = discriminant(_1); // scope 0 at $DIR/string.rs:+1:11: +1:12 | ||
switchInt(move _5) -> [1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/string.rs:+1:5: +1:12 | ||
} | ||
|
||
bb1: { | ||
StorageLive(_6); // scope 0 at $DIR/string.rs:+3:9: +3:10 | ||
_7 = const false; // scope 0 at $DIR/string.rs:+3:9: +3:10 | ||
_6 = move _1; // scope 0 at $DIR/string.rs:+3:9: +3:10 | ||
_0 = const 4321_i32; // scope 1 at $DIR/string.rs:+3:14: +3:18 | ||
drop(_6) -> bb6; // scope 0 at $DIR/string.rs:+3:17: +3:18 | ||
} | ||
|
||
bb2: { | ||
_2 = &((_1 as Some).0: std::string::String); // scope 0 at $DIR/string.rs:+2:14: +2:17 | ||
_3 = <String as Deref>::deref(move _2) -> bb3; // scope 0 at $DIR/string.rs:+2:14: +2:17 | ||
// mir::Constant | ||
// + span: $DIR/string.rs:9:14: 9:17 | ||
// + literal: Const { ty: for<'a> fn(&'a String) -> &'a <String as Deref>::Target {<String as Deref>::deref}, val: Value(<ZST>) } | ||
} | ||
|
||
bb3: { | ||
_4 = <str as PartialEq>::eq(_3, const "a") -> bb4; // scope 0 at $DIR/string.rs:+2:14: +2:17 | ||
// mir::Constant | ||
// + span: $DIR/string.rs:9:14: 9:17 | ||
// + literal: Const { ty: for<'a, 'b> fn(&'a str, &'b str) -> bool {<str as PartialEq>::eq}, val: Value(<ZST>) } | ||
// mir::Constant | ||
// + span: $DIR/string.rs:9:14: 9:17 | ||
// + literal: Const { ty: &str, val: Value(Slice(..)) } | ||
} | ||
|
||
bb4: { | ||
switchInt(move _4) -> [false: bb1, otherwise: bb5]; // scope 0 at $DIR/string.rs:+2:14: +2:17 | ||
} | ||
|
||
bb5: { | ||
_0 = const 1234_i32; // scope 0 at $DIR/string.rs:+2:22: +2:26 | ||
goto -> bb9; // scope 0 at $DIR/string.rs:+2:22: +2:26 | ||
} | ||
|
||
bb6: { | ||
StorageDead(_6); // scope 0 at $DIR/string.rs:+3:17: +3:18 | ||
goto -> bb9; // scope 0 at $DIR/string.rs:+3:17: +3:18 | ||
} | ||
|
||
bb7: { | ||
return; // scope 0 at $DIR/string.rs:+5:2: +5:2 | ||
} | ||
|
||
bb8: { | ||
drop(_1) -> bb7; // scope 0 at $DIR/string.rs:+5:1: +5:2 | ||
} | ||
|
||
bb9: { | ||
switchInt(_7) -> [false: bb7, otherwise: bb8]; // scope 0 at $DIR/string.rs:+5:1: +5:2 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// compile-flags: -Z mir-opt-level=0 -C panic=abort | ||
|
||
#![feature(string_deref_patterns)] | ||
#![crate_type = "lib"] | ||
|
||
// EMIT_MIR string.foo.PreCodegen.after.mir | ||
pub fn foo(s: Option<String>) -> i32 { | ||
match s { | ||
Some("a") => 1234, | ||
s => 4321, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// run-pass | ||
// check-run-results | ||
#![feature(string_deref_patterns)] | ||
|
||
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"), | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
the answer | ||
something else? | ||
nil |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// check-pass | ||
#![feature(string_deref_patterns)] | ||
|
||
fn main() { | ||
match <_ as Default>::default() { | ||
"" => (), | ||
_ => unreachable!(), | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// gate-test-string_deref_patterns | ||
fn main() { | ||
match String::new() { | ||
"" | _ => {} | ||
//~^ mismatched types | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
error[E0308]: mismatched types | ||
--> $DIR/gate.rs:4:9 | ||
| | ||
LL | match String::new() { | ||
| ------------- this expression has type `String` | ||
LL | "" | _ => {} | ||
| ^^ expected struct `String`, found `&str` | ||
|
||
error: aborting due to previous error | ||
|
||
For more information about this error, try `rustc --explain E0308`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// check-pass | ||
#![feature(string_deref_patterns)] | ||
|
||
fn foo(s: &String) -> i32 { | ||
match *s { | ||
"a" => 42, | ||
_ => -1, | ||
} | ||
} | ||
|
||
fn bar(s: Option<&&&&String>) -> i32 { | ||
match s { | ||
Some(&&&&"&&&&") => 1, | ||
_ => -1, | ||
} | ||
} | ||
|
||
fn main() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need to check the feature flag here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it falls through to the statements below it would ICE anyways. I will add a
bug!
statement just to make sure that the feature flag is enabled.