Skip to content

Commit e07425d

Browse files
committed
Auto merge of #98914 - fee1-dead-contrib:min-deref-patterns, r=compiler-errors
Minimal implementation of implicit deref patterns for Strings cc `@compiler-errors` `@BoxyUwU` rust-lang/lang-team#88 #87121 ~~I forgot to add a feature gate, will do so in a minute~~ Done
2 parents 2ed65da + bc51f87 commit e07425d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+302
-99
lines changed

compiler/rustc_feature/src/active.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,8 @@ declare_features! (
506506
(active, stmt_expr_attributes, "1.6.0", Some(15701), None),
507507
/// Allows lints part of the strict provenance effort.
508508
(active, strict_provenance, "1.61.0", Some(95228), None),
509+
/// Allows string patterns to dereference values to match them.
510+
(active, string_deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121), None),
509511
/// Allows the use of `#[target_feature]` on safe functions.
510512
(active, target_feature_11, "1.45.0", Some(69098), None),
511513
/// Allows using `#[thread_local]` on `static` items.

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,8 @@ language_item_table! {
312312
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
313313
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
314314
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
315+
316+
String, sym::String, string, Target::Struct, GenericRequirement::None;
315317
}
316318

317319
pub enum GenericRequirement {

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
464464
ref_cnt += 1;
465465
}
466466
if let ty::Adt(adt, _) = peeled.kind()
467-
&& self.tcx.is_diagnostic_item(sym::String, adt.did())
467+
&& Some(adt.did()) == self.tcx.lang_items().string()
468468
{
469469
err.span_suggestion_verbose(
470470
expr.span.shrink_to_hi(),

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -821,10 +821,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
821821
ty.is_str()
822822
|| matches!(
823823
ty.kind(),
824-
ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did())
824+
ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string()
825825
)
826826
}
827-
ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()),
827+
ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(),
828828
_ => false,
829829
};
830830
if is_string_or_ref_str && item_name.name == sym::iter {

compiler/rustc_hir_typeck/src/op.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -556,9 +556,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
556556
let rm_borrow_msg = "remove the borrow to obtain an owned `String`";
557557
let to_owned_msg = "create an owned `String` from a string reference";
558558

559+
let string_type = self.tcx.lang_items().string();
559560
let is_std_string = |ty: Ty<'tcx>| {
560-
ty.ty_adt_def()
561-
.map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did()))
561+
ty.ty_adt_def().map_or(false, |ty_def| Some(ty_def.did()) == string_type)
562562
};
563563

564564
match (lhs_ty.kind(), rhs_ty.kind()) {

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 self.tcx.features().string_deref_patterns && 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_lint/src/non_fmt_panic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
148148
ty::Ref(_, r, _) if *r.kind() == ty::Str,
149149
) || matches!(
150150
ty.ty_adt_def(),
151-
Some(ty_def) if cx.tcx.is_diagnostic_item(sym::String, ty_def.did()),
151+
Some(ty_def) if Some(ty_def.did()) == cx.tcx.lang_items().string(),
152152
);
153153

154154
let infcx = cx.tcx.infer_ctxt().build();

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().string_deref_patterns {
246+
bug!("matching on `String` went through without enabling string_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)

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,7 @@ symbols! {
14071407
str_trim_end,
14081408
str_trim_start,
14091409
strict_provenance,
1410+
string_deref_patterns,
14101411
stringify,
14111412
struct_field_attributes,
14121413
struct_inherit,

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1729,7 +1729,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
17291729
ty::Bool => Some(0),
17301730
ty::Char => Some(1),
17311731
ty::Str => Some(2),
1732-
ty::Adt(def, _) if tcx.is_diagnostic_item(sym::String, def.did()) => Some(2),
1732+
ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2),
17331733
ty::Int(..)
17341734
| ty::Uint(..)
17351735
| ty::Float(..)

0 commit comments

Comments
 (0)