Skip to content

Commit ea14acd

Browse files
Implement macro-based deref!() syntax for deref patterns
Stop using `box PAT` syntax for deref patterns, as it's misleading and also causes their semantics being tangled up.
1 parent fa9862b commit ea14acd

File tree

33 files changed

+125
-37
lines changed

33 files changed

+125
-37
lines changed

compiler/rustc_ast/src/ast.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,9 @@ impl Pat {
621621
| PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
622622

623623
// Trivial wrappers over inner patterns.
624-
PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it),
624+
PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => {
625+
s.walk(it)
626+
}
625627

626628
// These patterns do not contain subpatterns, skip.
627629
PatKind::Wild
@@ -792,6 +794,9 @@ pub enum PatKind {
792794
/// A `box` pattern.
793795
Box(P<Pat>),
794796

797+
/// A `deref` pattern (currently `deref!()` macro-based syntax).
798+
Deref(P<Pat>),
799+
795800
/// A reference pattern (e.g., `&mut (a, b)`).
796801
Ref(P<Pat>, Mutability),
797802

compiler/rustc_ast/src/mut_visit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,7 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
12951295
fields.flat_map_in_place(|field| vis.flat_map_pat_field(field));
12961296
}
12971297
PatKind::Box(inner) => vis.visit_pat(inner),
1298+
PatKind::Deref(inner) => vis.visit_pat(inner),
12981299
PatKind::Ref(inner, _mutbl) => vis.visit_pat(inner),
12991300
PatKind::Range(e1, e2, Spanned { span: _, node: _ }) => {
13001301
visit_opt(e1, |e| vis.visit_expr(e));

compiler/rustc_ast/src/visit.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res
576576
try_visit!(visitor.visit_path(path, pattern.id));
577577
walk_list!(visitor, visit_pat_field, fields);
578578
}
579-
PatKind::Box(subpattern) | PatKind::Ref(subpattern, _) | PatKind::Paren(subpattern) => {
579+
PatKind::Box(subpattern)
580+
| PatKind::Deref(subpattern)
581+
| PatKind::Ref(subpattern, _)
582+
| PatKind::Paren(subpattern) => {
580583
try_visit!(visitor.visit_pat(subpattern));
581584
}
582585
PatKind::Ident(_, ident, optional_subpattern) => {

compiler/rustc_ast_lowering/src/pat.rs

+3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
9191
PatKind::Box(inner) => {
9292
break hir::PatKind::Box(self.lower_pat(inner));
9393
}
94+
PatKind::Deref(inner) => {
95+
break hir::PatKind::Deref(self.lower_pat(inner));
96+
}
9497
PatKind::Ref(inner, mutbl) => {
9598
break hir::PatKind::Ref(self.lower_pat(inner), *mutbl);
9699
}

compiler/rustc_ast_passes/src/feature_gate.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -413,10 +413,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
413413
}
414414
}
415415
PatKind::Box(..) => {
416-
if !self.features.deref_patterns {
417-
// Allow box patterns under `deref_patterns`.
418-
gate!(&self, box_patterns, pattern.span, "box pattern syntax is experimental");
419-
}
416+
gate!(&self, box_patterns, pattern.span, "box pattern syntax is experimental");
420417
}
421418
PatKind::Range(_, Some(_), Spanned { node: RangeEnd::Excluded, .. }) => {
422419
gate!(
@@ -610,10 +607,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
610607
};
611608
}
612609

613-
if !visitor.features.deref_patterns {
614-
// Allow box patterns under `deref_patterns`.
615-
gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
616-
}
610+
gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
617611
gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental");
618612
// Despite being a new feature, `where T: Trait<Assoc(): Sized>`, which is RTN syntax now,
619613
// used to be gated under associated_type_bounds, which are right above, so RTN needs to

compiler/rustc_ast_pretty/src/pprust/state.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,12 @@ impl<'a> State<'a> {
16261626
self.word("box ");
16271627
self.print_pat(inner);
16281628
}
1629+
PatKind::Deref(inner) => {
1630+
self.word("deref!");
1631+
self.popen();
1632+
self.print_pat(inner);
1633+
self.pclose();
1634+
}
16291635
PatKind::Ref(inner, mutbl) => {
16301636
self.word("&");
16311637
if mutbl.is_mut() {

compiler/rustc_hir/src/hir.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1003,7 +1003,7 @@ impl<'hir> Pat<'hir> {
10031003
use PatKind::*;
10041004
match self.kind {
10051005
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true,
1006-
Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
1006+
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_short_(it),
10071007
Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)),
10081008
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)),
10091009
Slice(before, slice, after) => {
@@ -1030,7 +1030,7 @@ impl<'hir> Pat<'hir> {
10301030
use PatKind::*;
10311031
match self.kind {
10321032
Wild | Never | Lit(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {}
1033-
Box(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
1033+
Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) => s.walk_(it),
10341034
Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)),
10351035
TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)),
10361036
Slice(before, slice, after) => {
@@ -1173,6 +1173,9 @@ pub enum PatKind<'hir> {
11731173
/// A `box` pattern.
11741174
Box(&'hir Pat<'hir>),
11751175

1176+
/// A `deref` pattern (currently `deref!()` macro-based syntax).
1177+
Deref(&'hir Pat<'hir>),
1178+
11761179
/// A reference pattern (e.g., `&mut (a, b)`).
11771180
Ref(&'hir Pat<'hir>, Mutability),
11781181

compiler/rustc_hir/src/intravisit.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,9 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V:
660660
PatKind::Tuple(tuple_elements, _) => {
661661
walk_list!(visitor, visit_pat, tuple_elements);
662662
}
663-
PatKind::Box(ref subpattern) | PatKind::Ref(ref subpattern, _) => {
663+
PatKind::Box(ref subpattern)
664+
| PatKind::Deref(ref subpattern)
665+
| PatKind::Ref(ref subpattern, _) => {
664666
try_visit!(visitor.visit_pat(subpattern));
665667
}
666668
PatKind::Binding(_, _hir_id, ident, ref optional_subpattern) => {

compiler/rustc_hir_analysis/src/check/region.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ fn resolve_local<'tcx>(
668668
| PatKind::TupleStruct(_, subpats, _)
669669
| PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(p)),
670670

671-
PatKind::Box(subpat) => is_binding_pat(subpat),
671+
PatKind::Box(subpat) | PatKind::Deref(subpat) => is_binding_pat(subpat),
672672

673673
PatKind::Ref(_, _)
674674
| PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..)

compiler/rustc_hir_pretty/src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,12 @@ impl<'a> State<'a> {
18081808
self.pclose();
18091809
}
18101810
}
1811+
PatKind::Deref(inner) => {
1812+
self.word("deref!");
1813+
self.popen();
1814+
self.print_pat(inner);
1815+
self.pclose();
1816+
}
18111817
PatKind::Ref(inner, mutbl) => {
18121818
let is_range_inner = matches!(inner.kind, PatKind::Range(..));
18131819
self.word("&");

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+1
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
463463
}
464464
PatKind::Or(_)
465465
| PatKind::Box(_)
466+
| PatKind::Deref(_)
466467
| PatKind::Ref(..)
467468
| PatKind::Wild
468469
| PatKind::Err(_) => {

compiler/rustc_hir_typeck/src/mem_categorization.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
719719
self.cat_pattern_(place_with_id, subpat, op)?;
720720
}
721721

722-
PatKind::Box(subpat) | PatKind::Ref(subpat, _) => {
722+
PatKind::Box(subpat) | PatKind::Ref(subpat, _) | PatKind::Deref(subpat) => {
723723
// box p1, &p1, &mut p1. we can ignore the mutability of
724724
// PatKind::Ref since that information is already contained
725725
// in the type.

compiler/rustc_hir_typeck/src/pat.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
210210
PatKind::Tuple(elements, ddpos) => {
211211
self.check_pat_tuple(pat.span, elements, ddpos, expected, pat_info)
212212
}
213-
PatKind::Box(inner) if self.tcx.features().deref_patterns => {
214-
self.check_pat_deref(pat.span, inner, expected, pat_info)
215-
}
216213
PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info),
214+
PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info),
217215
PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info),
218216
PatKind::Slice(before, slice, after) => {
219217
self.check_pat_slice(pat.span, before, slice, after, expected, pat_info)
@@ -297,6 +295,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
297295
| PatKind::TupleStruct(..)
298296
| PatKind::Tuple(..)
299297
| PatKind::Box(_)
298+
| PatKind::Deref(_)
300299
| PatKind::Range(..)
301300
| PatKind::Slice(..) => AdjustMode::Peel,
302301
// A never pattern behaves somewhat like a literal or unit variant.
@@ -762,6 +761,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
762761
| PatKind::Binding(..)
763762
| PatKind::Path(..)
764763
| PatKind::Box(..)
764+
| PatKind::Deref(_)
765765
| PatKind::Ref(..)
766766
| PatKind::Lit(..)
767767
| PatKind::Range(..)

compiler/rustc_lint/src/unused.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,7 @@ impl EarlyLintPass for UnusedParens {
11811181
self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
11821182
},
11831183
// Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
1184-
Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1184+
Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
11851185
// Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
11861186
// Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
11871187
Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),

compiler/rustc_middle/src/thir.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
11791179
write!(f, "{subpattern}")
11801180
}
11811181
PatKind::DerefPattern { ref subpattern } => {
1182-
write!(f, "k#deref {subpattern}")
1182+
write!(f, "deref!({subpattern})")
11831183
}
11841184
PatKind::Constant { value } => write!(f, "{value}"),
11851185
PatKind::InlineConstant { def: _, ref subpattern } => {

compiler/rustc_mir_build/src/thir/pattern/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
257257
return self.lower_path(qpath, pat.hir_id, pat.span);
258258
}
259259

260-
hir::PatKind::Box(subpattern) if self.tcx.features().deref_patterns => {
260+
hir::PatKind::Deref(subpattern) => {
261261
PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern) }
262262
}
263263
hir::PatKind::Ref(subpattern, _) | hir::PatKind::Box(subpattern) => {

compiler/rustc_parse/src/parser/expr.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -1939,11 +1939,10 @@ impl<'a> Parser<'a> {
19391939
/// Parse `builtin # ident(args,*)`.
19401940
fn parse_expr_builtin(&mut self) -> PResult<'a, P<Expr>> {
19411941
self.parse_builtin(|this, lo, ident| {
1942-
if ident.name == sym::offset_of {
1943-
return Ok(Some(this.parse_expr_offset_of(lo)?));
1944-
}
1945-
1946-
Ok(None)
1942+
Ok(match ident.name {
1943+
sym::offset_of => Some(this.parse_expr_offset_of(lo)?),
1944+
_ => None,
1945+
})
19471946
})
19481947
}
19491948

compiler/rustc_parse/src/parser/pat.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -498,11 +498,14 @@ impl<'a> Parser<'a> {
498498
} else {
499499
PatKind::Lit(const_expr)
500500
}
501+
} else if self.is_builtin() {
502+
self.parse_pat_builtin()?
503+
}
501504
// Don't eagerly error on semantically invalid tokens when matching
502505
// declarative macros, as the input to those doesn't have to be
503506
// semantically valid. For attribute/derive proc macros this is not the
504507
// case, so doing the recovery for them is fine.
505-
} else if self.can_be_ident_pat()
508+
else if self.can_be_ident_pat()
506509
|| (self.is_lit_bad_ident().is_some() && self.may_recover())
507510
{
508511
// Parse `ident @ pat`
@@ -1119,6 +1122,21 @@ impl<'a> Parser<'a> {
11191122
.contains(&self.token.kind)
11201123
}
11211124

1125+
fn parse_pat_builtin(&mut self) -> PResult<'a, PatKind> {
1126+
self.parse_builtin(|self_, _lo, ident| {
1127+
Ok(match ident.name {
1128+
// builtin#deref(PAT)
1129+
sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt(
1130+
None,
1131+
RecoverComma::Yes,
1132+
RecoverColon::Yes,
1133+
CommaRecoveryMode::LikelyTuple,
1134+
)?)),
1135+
_ => None,
1136+
})
1137+
})
1138+
}
1139+
11221140
/// Parses `box pat`
11231141
fn parse_pat_box(&mut self) -> PResult<'a, PatKind> {
11241142
let box_span = self.prev_token.span;

compiler/rustc_passes/src/hir_stats.rs

+2
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
300300
Path,
301301
Tuple,
302302
Box,
303+
Deref,
303304
Ref,
304305
Lit,
305306
Range,
@@ -566,6 +567,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
566567
Path,
567568
Tuple,
568569
Box,
570+
Deref,
569571
Ref,
570572
Lit,
571573
Range,

compiler/rustc_pattern_analysis/src/rustc.rs

+1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
465465
PatKind::DerefPattern { .. } => {
466466
// FIXME(deref_patterns): At least detect that `box _` is irrefutable.
467467
fields = vec![];
468+
arity = 0;
468469
ctor = Opaque(OpaqueId::new());
469470
}
470471
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {

library/core/src/macros/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1714,6 +1714,18 @@ pub(crate) mod builtin {
17141714
/* compiler built-in */
17151715
}
17161716

1717+
#[cfg(not(bootstrap))]
1718+
/// Unstable placeholder for type ascription.
1719+
#[allow_internal_unstable(builtin_syntax)]
1720+
#[unstable(
1721+
feature = "deref_patterns",
1722+
issue = "87121",
1723+
reason = "placeholder syntax for deref patterns"
1724+
)]
1725+
pub macro deref($pat:pat) {
1726+
builtin # deref($pat)
1727+
}
1728+
17171729
/// Unstable implementation detail of the `rustc` compiler, do not use.
17181730
#[rustc_builtin_macro]
17191731
#[stable(feature = "rust1", since = "1.0.0")]

library/core/src/prelude/v1.rs

+8
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,11 @@ pub use crate::macros::builtin::cfg_eval;
103103
reason = "placeholder syntax for type ascription"
104104
)]
105105
pub use crate::macros::builtin::type_ascribe;
106+
107+
#[cfg(not(bootstrap))]
108+
#[unstable(
109+
feature = "deref_patterns",
110+
issue = "87121",
111+
reason = "placeholder syntax for deref patterns"
112+
)]
113+
pub use crate::macros::builtin::deref;

library/std/src/prelude/v1.rs

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ pub use core::prelude::v1::cfg_eval;
9191
)]
9292
pub use core::prelude::v1::type_ascribe;
9393

94+
#[cfg(not(bootstrap))]
95+
// Do not `doc(no_inline)` either.
96+
#[unstable(
97+
feature = "deref_patterns",
98+
issue = "87121",
99+
reason = "placeholder syntax for deref patterns"
100+
)]
101+
pub use core::prelude::v1::deref;
102+
94103
// The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated
95104
// rather than glob imported because we want docs to show these re-exports as
96105
// pointing to within `std`.

src/librustdoc/clean/utils.rs

+1
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
329329
elts.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(", ")
330330
),
331331
PatKind::Box(p) => return name_from_pat(&*p),
332+
PatKind::Deref(p) => format!("deref!({})", name_from_pat(&*p)),
332333
PatKind::Ref(p, _) => return name_from_pat(&*p),
333334
PatKind::Lit(..) => {
334335
warn!(

src/tools/clippy/clippy_lints/src/equatable_if_let.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
5555
| PatKind::Err(_) => false,
5656
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
5757
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
58-
PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
58+
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) => unary_pattern(x),
5959
PatKind::Path(_) | PatKind::Lit(_) => true,
6060
}
6161
}

src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ impl<'a> NormalizedPat<'a> {
243243
fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self {
244244
match pat.kind {
245245
PatKind::Wild | PatKind::Binding(.., None) => Self::Wild,
246-
PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => {
246+
PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => {
247247
Self::from_pat(cx, arena, pat)
248248
},
249249
PatKind::Never => Self::Never,

src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs

+2
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
242242
|k| matches!(k, Box(_)),
243243
|k| always_pat!(k, Box(p) => p),
244244
),
245+
// FIXME(deref_patterns): Should we merge patterns here?
246+
Deref(_) => false,
245247
// Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
246248
Ref(target, Mutability::Mut) => extend_with_matching(
247249
target, start, alternatives,

src/tools/clippy/clippy_lints/src/utils/author.rs

+5
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
689689
kind!("Box({pat})");
690690
self.pat(pat);
691691
},
692+
PatKind::Deref(pat) => {
693+
bind!(self, pat);
694+
kind!("Deref({pat})");
695+
self.pat(pat);
696+
},
692697
PatKind::Ref(pat, muta) => {
693698
bind!(self, pat);
694699
kind!("Ref({pat}, Mutability::{muta:?})");

0 commit comments

Comments
 (0)