Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22292 from rust-lang/deref-pat
feat: Support deref patterns
Lukas Wirth 10 days ago
parent 43e333a · parent 8f678f2 · commit 6137693
-rw-r--r--crates/hir-def/src/expr_store.rs5
-rw-r--r--crates/hir-def/src/expr_store/lower.rs4
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs5
-rw-r--r--crates/hir-def/src/hir.rs5
-rw-r--r--crates/hir-expand/src/inert_attr_macro.rs1
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs1
-rw-r--r--crates/hir-ty/src/infer.rs22
-rw-r--r--crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs97
-rw-r--r--crates/hir-ty/src/infer/expr.rs1
-rw-r--r--crates/hir-ty/src/infer/pat.rs7
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs1
-rw-r--r--crates/hir-ty/src/next_solver/ty.rs4
-rw-r--r--crates/hir-ty/src/tests/patterns.rs56
-rw-r--r--crates/ide-assists/src/handlers/convert_let_else_to_match.rs4
-rw-r--r--crates/ide-assists/src/utils.rs1
-rw-r--r--crates/parser/src/grammar/patterns.rs21
-rw-r--r--crates/parser/src/syntax_kind/generated.rs8
-rw-r--r--crates/parser/test_data/generated/runner.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/deref_pat.rast36
-rw-r--r--crates/parser/test_data/parser/inline/ok/deref_pat.rs1
-rw-r--r--crates/syntax/rust.ungram4
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs62
-rw-r--r--crates/syntax/src/ast/make.rs4
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs4
-rw-r--r--crates/test-utils/src/minicore.rs23
-rw-r--r--xtask/src/codegen/grammar/ast_src.rs1
26 files changed, 365 insertions, 15 deletions
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs
index 2c2b477115..af3d28d174 100644
--- a/crates/hir-def/src/expr_store.rs
+++ b/crates/hir-def/src/expr_store.rs
@@ -587,7 +587,7 @@ impl ExpressionStore {
Pat::Record { args, ellipsis: _, path: _ } => {
args.iter().for_each(|RecordFieldPat { pat, name: _ }| f(*pat));
}
- Pat::Box { inner } => f(*inner),
+ Pat::Box { inner } | Pat::Deref { inner } => f(*inner),
}
}
@@ -803,7 +803,8 @@ impl ExpressionStore {
| Pat::Bind { .. }
| Pat::TupleStruct { .. }
| Pat::Ref { .. }
- | Pat::Box { .. } => {}
+ | Pat::Box { .. }
+ | Pat::Deref { .. } => {}
});
}
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 93f8304230..f4842700f7 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -2794,6 +2794,10 @@ impl<'db> ExprCollector<'db> {
let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
}
+ ast::Pat::DerefPat(inner) => {
+ let inner = self.collect_pat_opt(inner.pat(), binding_list);
+ Pat::Deref { inner }
+ }
ast::Pat::ConstBlockPat(const_block_pat) => {
if let Some(block) = const_block_pat.block_expr() {
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index 5d90191503..3bf6a8c6a0 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -1031,6 +1031,11 @@ impl Printer<'_> {
w!(self, "box ");
self.print_pat(*inner);
}
+ Pat::Deref { inner } => {
+ w!(self, "deref!(");
+ self.print_pat(*inner);
+ w!(self, ")");
+ }
Pat::ConstBlock(c) => {
w!(self, "const ");
self.print_expr(*c);
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 125a393bd9..6eba859264 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -714,6 +714,9 @@ pub enum Pat {
Box {
inner: PatId,
},
+ Deref {
+ inner: PatId,
+ },
ConstBlock(ExprId),
/// An expression inside a pattern. That can only occur inside assignments.
///
@@ -746,7 +749,7 @@ impl Pat {
Pat::Record { args, .. } => {
args.iter().map(|f| f.pat).for_each(f);
}
- Pat::Box { inner } => f(*inner),
+ Pat::Box { inner } | Pat::Deref { inner } => f(*inner),
}
}
}
diff --git a/crates/hir-expand/src/inert_attr_macro.rs b/crates/hir-expand/src/inert_attr_macro.rs
index 53b624d9a6..4185b7b018 100644
--- a/crates/hir-expand/src/inert_attr_macro.rs
+++ b/crates/hir-expand/src/inert_attr_macro.rs
@@ -702,6 +702,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing),
+ rustc_attr!(TEST, rustc_dyn_incompatible_trait, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk),
gated!(
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index ad9909a204..4db6066a56 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -253,6 +253,7 @@ impl<'db> UnsafeVisitor<'db> {
| Pat::TupleStruct { .. }
| Pat::Ref { .. }
| Pat::Box { .. }
+ | Pat::Deref { .. }
| Pat::Expr(..)
| Pat::ConstBlock(..) => {
self.on_unsafe_op(current.into(), UnsafetyReason::UnionField)
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index d28ee4ab44..14da4288b5 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -1205,6 +1205,12 @@ impl InferenceResult {
}
}
+#[derive(Debug, Clone, Copy)]
+enum DerefPatBorrowMode {
+ Borrow(Mutability),
+ Box,
+}
+
/// The inference context contains all information needed during type inference.
#[derive(Clone, Debug)]
pub(crate) struct InferenceContext<'body, 'db> {
@@ -1367,6 +1373,22 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
(target_features, *target_feature_is_safe)
}
+ /// How should a deref pattern find the place for its inner pattern to match on?
+ ///
+ /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner
+ /// pattern's scrutinee by calling `DerefMut::deref_mut`, and otherwise we call `Deref::deref`.
+ /// However, for boxes we can use a built-in deref instead, which doesn't borrow the scrutinee;
+ /// in this case, we return `DerefPatBorrowMode::Box`.
+ fn deref_pat_borrow_mode(&self, pointer_ty: Ty<'_>, inner: PatId) -> DerefPatBorrowMode {
+ if pointer_ty.is_box() {
+ DerefPatBorrowMode::Box
+ } else {
+ let mutability =
+ if self.pat_has_ref_mut_binding(inner) { Mutability::Mut } else { Mutability::Not };
+ DerefPatBorrowMode::Borrow(mutability)
+ }
+ }
+
#[inline]
fn set_tainted_by_errors(&mut self) {
self.result.has_errors = true;
diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
index 44dc1a63d1..b42127cce6 100644
--- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
+++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
@@ -17,13 +17,15 @@ use hir_def::{
use macros::{TypeFoldable, TypeVisitable};
use rustc_type_ir::inherent::{IntoKind, Ty as _};
use smallvec::{SmallVec, smallvec};
+use stdx::impl_from;
use syntax::ast::{BinaryOp, UnaryOp};
use tracing::{debug, instrument, trace};
use crate::{
Adjust, Adjustment, AutoBorrow, Span,
infer::{
- ByRef, CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind,
+ ByRef, CaptureSourceStack, DerefPatBorrowMode, InferenceContext, PatAdjust, PatAdjustment,
+ UpvarCapture, closure::analysis::BorrowKind,
},
method_resolution::CandidateId,
next_solver::{ErrorGuaranteed, StoredTy, Ty, TyKind},
@@ -795,6 +797,11 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
self.cx.result.expr_adjustment(expr).unwrap_or_default().into()
}
+ fn pat_adjustments(&self, pat: PatId) -> SmallVec<[PatAdjustment; 5]> {
+ // Due to borrowck problems, we cannot borrow the adjustments, unfortunately.
+ self.cx.result.pat_adjustment(pat).unwrap_or_default().into()
+ }
+
/// Invoke the appropriate delegate calls for anything that gets
/// consumed or borrowed as part of the automatic adjustment
/// process.
@@ -877,6 +884,31 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
#[instrument(skip(self), level = "debug")]
fn walk_pat(&mut self, discr_place: PlaceWithOrigin, pat: PatId, has_guard: bool) -> Result {
self.cat_pattern(discr_place.clone(), pat, &mut |this, place, pat| {
+ let walk_deref_pat = |this: &mut Self, subpattern: PatId, place: PlaceWithOrigin| {
+ // A deref pattern is a bit special: the binding mode of its inner bindings
+ // determines whether to borrow *at the level of the deref pattern* rather than
+ // borrowing the bound place (since that inner place is inside the temporary that
+ // stores the result of calling `deref()`/`deref_mut()` so can't be captured).
+ // Deref patterns on boxes don't borrow, so we ignore them here.
+ // HACK: this could be a fake pattern corresponding to a deref inserted by match
+ // ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
+ if let DerefPatBorrowMode::Borrow(mutability) =
+ this.cx.deref_pat_borrow_mode(place.place.ty(), subpattern)
+ {
+ let bk = BorrowKind::from_mutbl(mutability);
+ this.delegate.borrow(place, bk, this.cx);
+ }
+ };
+
+ let pat = match pat {
+ CatPatternPat::PatId(pat) => pat,
+ CatPatternPat::DerefPat { inner } => {
+ debug!("walk_pat: Deref {{ inner: {:?} }}", inner);
+ walk_deref_pat(this, inner, place);
+ return Ok(());
+ }
+ };
+
debug!("walk_pat: pat.kind={:?}", this.cx.store[pat]);
let read_discriminant = {
let place = place.clone();
@@ -921,6 +953,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
}
}
}
+ Pat::Deref { inner: subpattern } => walk_deref_pat(this, subpattern, place),
Pat::Path(ref path) => {
// A `Path` pattern is just a name like `Foo`. This is either a
// named constant or else it refers to an ADT variant
@@ -1110,6 +1143,13 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
}
}
+#[derive(Debug, Clone, Copy)]
+enum CatPatternPat {
+ PatId(PatId),
+ DerefPat { inner: PatId },
+}
+impl_from!(PatId for CatPatternPat);
+
/// The job of the methods whose name starts with `cat_` is to analyze
/// expressions and construct the corresponding [`Place`]s. The `cat`
/// stands for "categorize", this is a leftover from long ago when
@@ -1460,7 +1500,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
op: &mut F,
) -> Result
where
- F: FnMut(&mut Self, PlaceWithOrigin, PatId) -> Result,
+ F: FnMut(&mut Self, PlaceWithOrigin, CatPatternPat) -> Result,
{
// If (pattern) adjustments are active for this pattern, adjust the `PlaceWithId` correspondingly.
// `PlaceWithId`s are constructed differently from patterns. For example, in
@@ -1494,11 +1534,26 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
// Then we see that to get the same result, we must start with
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
- let adjustments_len = self.cx.result.pat_adjustment(pat).map_or(0, |it| it.len());
- for _ in 0..adjustments_len {
+ let adjustments = self.pat_adjustments(pat);
+ let mut adjusts = adjustments.iter().peekable();
+ while let Some(adjust) = adjusts.next() {
debug!("applying adjustment to place_with_id={:?}", place_with_id);
- // FIXME: We need to adjust this once we implement deref patterns (or pin ergonomics, for that matter).
- place_with_id = self.cat_deref(pat.into(), place_with_id)?;
+ place_with_id = match adjust.kind {
+ PatAdjust::BuiltinDeref => self.cat_deref(pat.into(), place_with_id)?,
+ PatAdjust::OverloadedDeref => {
+ // This adjustment corresponds to an overloaded deref; unless it's on a box, it
+ // borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke
+ // the callback before setting `place_with_id` to the temporary storing the
+ // result of the deref.
+ op(self, place_with_id.clone(), CatPatternPat::DerefPat { inner: pat })?;
+ let target_ty = match adjusts.peek() {
+ Some(next_adjust) => next_adjust.source.as_ref(),
+ // At the end of the deref chain, we get `pat`'s scrutinee.
+ None => self.pat_ty_unadjusted(pat)?,
+ };
+ self.pat_deref_place(pat.into(), place_with_id, pat, target_ty)?
+ }
+ };
}
let place_with_id = place_with_id; // lose mutability
debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
@@ -1512,7 +1567,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
// `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)`
// result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with
// that (where the `ref` on `x` is implied).
- op(self, place_with_id.clone(), pat)?;
+ op(self, place_with_id.clone(), pat.into())?;
match self.cx.store[pat] {
Pat::Tuple { args: ref subpats, ellipsis: dots_pos } => {
@@ -1592,6 +1647,11 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
let subplace = self.cat_deref(pat.into(), place_with_id)?;
self.cat_pattern(subplace, subpat, op)?;
}
+ Pat::Deref { inner: subpat } => {
+ let ty = self.pat_ty_adjusted(subpat)?;
+ let place = self.pat_deref_place(pat.into(), place_with_id, subpat, ty)?;
+ self.cat_pattern(place, subpat, op)?;
+ }
Pat::Slice { prefix: ref before, slice, suffix: ref after } => {
let Some(element_ty) = self
@@ -1642,6 +1702,29 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
Ok(())
}
+ /// Represents the place matched on by a deref pattern's interior.
+ fn pat_deref_place(
+ &mut self,
+ node: ExprOrPatId,
+ base_place: PlaceWithOrigin,
+ inner: PatId,
+ target_ty: Ty<'db>,
+ ) -> Result<PlaceWithOrigin> {
+ match self.cx.deref_pat_borrow_mode(base_place.place.ty(), inner) {
+ // Deref patterns on boxes are lowered using a built-in deref.
+ DerefPatBorrowMode::Box => self.cat_deref(node, base_place),
+ // For other types, we create a temporary to match on.
+ DerefPatBorrowMode::Borrow(mutability) => {
+ let re_erased = self.cx.types.regions.erased;
+ let ty = Ty::new_ref(self.cx.interner(), re_erased, target_ty, mutability);
+ // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
+ let base = self.cat_rvalue(node, ty);
+ // ... and the inner pattern matches on the place behind that reference.
+ self.cat_deref(node, base)
+ }
+ }
+ }
+
/// Checks whether a type has multiple variants, and therefore, whether a
/// read of the discriminant might be necessary. Note that the actual MIR
/// builder code does a more specific check, filtering out variants that
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index b1e983f274..3ccf7e5cc8 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -194,6 +194,7 @@ impl<'db> InferenceContext<'_, 'db> {
| Pat::Path(_)
| Pat::Tuple { .. }
| Pat::Box { .. }
+ | Pat::Deref { .. }
| Pat::Ref { .. }
| Pat::Lit(_)
| Pat::Range { .. }
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index b9ca34559c..fd4f7a7997 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -517,6 +517,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
self.infer_tuple_pat(pat, elements, ddpos, expected, pat_info)
}
Pat::Box { inner } => self.infer_box_pat(pat, inner, expected, pat_info),
+ Pat::Deref { inner } => self.infer_deref_pat(pat, inner, expected, pat_info),
// Pat::Deref(inner) => self.infer_deref_pat(pat.span, inner, expected, pat_info),
Pat::Ref { pat: inner, mutability: mutbl } => self.infer_ref_pat(
pat,
@@ -616,7 +617,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// When checking an explicit deref pattern, only peel reference types.
// FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
// patterns may want `PeelKind::Implicit`, stopping on encountering a box.
- Pat::Box { .. } /* | Pat::Deref(_) */ => {
+ Pat::Box { .. } | Pat::Deref { .. } => {
AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat }
}
// A never pattern behaves somewhat like a literal or unit variant.
@@ -1270,7 +1271,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
box_ty
}
- fn _infer_deref_pat(
+ fn infer_deref_pat(
&mut self,
pat: PatId,
inner: PatId,
@@ -1332,7 +1333,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
/// This is computed from the typeck results since we want to make
/// sure to apply any match-ergonomics adjustments, which we cannot
/// determine from the HIR alone.
- fn pat_has_ref_mut_binding(&self, pat: PatId) -> bool {
+ pub(super) fn pat_has_ref_mut_binding(&self, pat: PatId) -> bool {
let mut has_ref_mut = false;
self.store.walk_pats(pat, &mut |pat| {
if let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) =
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 03c608f4b2..e7fa036f23 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -504,6 +504,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
(current, current_else)
}
Pat::Box { .. } => not_supported!("box pattern"),
+ Pat::Deref { .. } => not_supported!("deref pattern"),
Pat::ConstBlock(_) => not_supported!("const block pattern"),
})
}
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs
index 8a3866d5ac..46bfd1cc4a 100644
--- a/crates/hir-ty/src/next_solver/ty.rs
+++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -501,6 +501,10 @@ impl<'db> Ty<'db> {
}
}
+ pub fn is_box(self) -> bool {
+ matches!(self.kind(), TyKind::Adt(adt_def, _) if adt_def.is_box())
+ }
+
#[inline]
pub fn as_adt(self) -> Option<(AdtId, GenericArgs<'db>)> {
match self.kind() {
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 9449f531a6..ea94725df8 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -1305,3 +1305,59 @@ fn bar() {
"#,
);
}
+
+#[test]
+fn deref_pattern() {
+ check_infer(
+ r#"
+//- minicore: deref_pat
+use core::ops::{Deref, DerefPure};
+
+#[lang = "owned_box"]
+pub struct Box<T>(T);
+impl<T> Deref for Box<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ loop {}
+ }
+}
+impl<T> DerefPure for Box<T> {}
+
+pub struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+ type Target = [T];
+ fn deref(&self) -> &Self::Target {
+ loop {}
+ }
+}
+impl<T> DerefPure for Foo<T> {}
+
+fn foo(v: &Box<Foo<i32>>) {
+ match v {
+ deref!(deref!(inner)) => {}
+ _ => {}
+ }
+}
+ "#,
+ expect![[r#"
+ 142..146 'self': &'? Box<T>
+ 165..188 '{ ... }': &'? T
+ 175..182 'loop {}': !
+ 180..182 '{}': ()
+ 310..314 'self': &'? Foo<T>
+ 333..356 '{ ... }': &'? [T]
+ 343..350 'loop {}': !
+ 348..350 '{}': ()
+ !0..20 'builti...inner)': Foo<i32>
+ !0..28 'builti...nner))': Box<Foo<i32>>
+ !14..19 'inner': &'? [i32]
+ 399..400 'v': &'? Box<Foo<i32>>
+ 418..493 '{ ... } }': ()
+ 424..491 'match ... }': ()
+ 430..431 'v': &'? Box<Foo<i32>>
+ 467..469 '{}': ()
+ 478..479 '_': &'? Box<Foo<i32>>
+ 483..485 '{}': ()
+ "#]],
+ );
+}
diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
index 721e2d8789..07e12f0320 100644
--- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
+++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
@@ -146,6 +146,10 @@ fn remove_mut_and_collect_idents(
let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
make.box_pat(pat).into()
}
+ ast::Pat::DerefPat(p) => {
+ let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
+ make.deref_pat(pat)
+ }
ast::Pat::OrPat(p) => {
let pats = p
.pats()
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 35f28aed53..096f6678a5 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -416,6 +416,7 @@ fn check_pat_variant_nested_or_literal_with_depth(
| ast::Pat::MacroPat(_)
| ast::Pat::PathPat(_)
| ast::Pat::BoxPat(_)
+ | ast::Pat::DerefPat(_)
| ast::Pat::ConstBlockPat(_) => true,
ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| {
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 4dd44c030f..5726f085a0 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -199,6 +199,23 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
}
}
+fn builtin_pat(p: &mut Parser<'_>) -> Option<CompletedMarker> {
+ let m = p.start();
+ p.bump_remap(T![builtin]);
+ p.bump(T![#]);
+ if p.eat_contextual_kw(T![deref]) {
+ // test deref_pat
+ // fn foo() { let builtin # deref(_) = 1; }
+ p.expect(T!['(']);
+ pattern(p);
+ p.expect(T![')']);
+ Some(m.complete(p, DEREF_PAT))
+ } else {
+ m.abandon(p);
+ None
+ }
+}
+
const PAT_RECOVERY_SET: TokenSet = TokenSet::new(&[
T![let],
T![if],
@@ -214,6 +231,10 @@ const PAT_RECOVERY_SET: TokenSet = TokenSet::new(&[
]);
fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarker> {
+ if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) {
+ return builtin_pat(p);
+ }
+
let m = match p.current() {
T![box] => box_pat(p),
T![ref] | T![mut] => ident_pat(p, true),
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 59fa3ee773..b1867275ce 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -120,6 +120,7 @@ pub enum SyntaxKind {
CFG_KW,
CLOBBER_ABI_KW,
DEFAULT_KW,
+ DEREF_KW,
DYN_KW,
FORMAT_ARGS_KW,
GEN_KW,
@@ -198,6 +199,7 @@ pub enum SyntaxKind {
CONST_BLOCK_PAT,
CONST_PARAM,
CONTINUE_EXPR,
+ DEREF_PAT,
DYN_TRAIT_TYPE,
ENUM,
EXPR_STMT,
@@ -382,6 +384,7 @@ impl SyntaxKind {
| CONST_BLOCK_PAT
| CONST_PARAM
| CONTINUE_EXPR
+ | DEREF_PAT
| DYN_TRAIT_TYPE
| ENUM
| EXPR_STMT
@@ -627,6 +630,7 @@ impl SyntaxKind {
CFG_ATTR_KW => "cfg_attr",
CLOBBER_ABI_KW => "clobber_abi",
DEFAULT_KW => "default",
+ DEREF_KW => "deref",
DYN_KW => "dyn",
FORMAT_ARGS_KW => "format_args",
GLOBAL_ASM_KW => "global_asm",
@@ -732,6 +736,7 @@ impl SyntaxKind {
CFG_ATTR_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
+ DEREF_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
GLOBAL_ASM_KW => true,
@@ -825,6 +830,7 @@ impl SyntaxKind {
CFG_ATTR_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
+ DEREF_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
GLOBAL_ASM_KW => true,
@@ -981,6 +987,7 @@ impl SyntaxKind {
"cfg_attr" => CFG_ATTR_KW,
"clobber_abi" => CLOBBER_ABI_KW,
"default" => DEFAULT_KW,
+ "deref" => DEREF_KW,
"dyn" if edition < Edition::Edition2018 => DYN_KW,
"format_args" => FORMAT_ARGS_KW,
"global_asm" => GLOBAL_ASM_KW,
@@ -1155,6 +1162,7 @@ macro_rules ! T_ {
[cfg_attr] => { $ crate :: SyntaxKind :: CFG_ATTR_KW };
[clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW };
[default] => { $ crate :: SyntaxKind :: DEFAULT_KW };
+ [deref] => { $ crate :: SyntaxKind :: DEREF_KW };
[dyn] => { $ crate :: SyntaxKind :: DYN_KW };
[format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW };
[global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW };
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 845119d922..7aaf270a77 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -195,6 +195,8 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/default_unsafe_item.rs");
}
#[test]
+ fn deref_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/deref_pat.rs"); }
+ #[test]
fn destructuring_assignment_struct_rest_pattern() {
run_and_expect_no_errors(
"test_data/parser/inline/ok/destructuring_assignment_struct_rest_pattern.rs",
diff --git a/crates/parser/test_data/parser/inline/ok/deref_pat.rast b/crates/parser/test_data/parser/inline/ok/deref_pat.rast
new file mode 100644
index 0000000000..ab4073c081
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/deref_pat.rast
@@ -0,0 +1,36 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ DEREF_PAT
+ BUILTIN_KW "builtin"
+ WHITESPACE " "
+ POUND "#"
+ WHITESPACE " "
+ DEREF_KW "deref"
+ L_PAREN "("
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "1"
+ SEMICOLON ";"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/deref_pat.rs b/crates/parser/test_data/parser/inline/ok/deref_pat.rs
new file mode 100644
index 0000000000..c5c9d9f059
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/deref_pat.rs
@@ -0,0 +1 @@
+fn foo() { let builtin # deref(_) = 1; }
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 768cf2013d..6bcf8ba743 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -748,6 +748,10 @@ Pat =
| TuplePat
| TupleStructPat
| ConstBlockPat
+| DerefPat
+
+DerefPat =
+ 'builtin' '#' 'deref' '(' Pat ')'
LiteralPat =
'-'? Literal
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 9a2bba9ebf..e3e5c499d4 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -532,6 +532,23 @@ impl ContinueExpr {
support::token(&self.syntax, T![continue])
}
}
+pub struct DerefPat {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DerefPat {
+ #[inline]
+ pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
+ #[inline]
+ pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
+ #[inline]
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ #[inline]
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+ #[inline]
+ pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ #[inline]
+ pub fn deref_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![deref]) }
+}
pub struct DynTraitType {
pub(crate) syntax: SyntaxNode,
}
@@ -2254,6 +2271,7 @@ pub enum Meta {
pub enum Pat {
BoxPat(BoxPat),
ConstBlockPat(ConstBlockPat),
+ DerefPat(DerefPat),
IdentPat(IdentPat),
LiteralPat(LiteralPat),
MacroPat(MacroPat),
@@ -3585,6 +3603,38 @@ impl fmt::Debug for ContinueExpr {
f.debug_struct("ContinueExpr").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for DerefPat {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ DEREF_PAT
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == DEREF_PAT }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for DerefPat {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for DerefPat {}
+impl PartialEq for DerefPat {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for DerefPat {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for DerefPat {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("DerefPat").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for DynTraitType {
#[inline]
fn kind() -> SyntaxKind
@@ -8515,6 +8565,10 @@ impl From<ConstBlockPat> for Pat {
#[inline]
fn from(node: ConstBlockPat) -> Pat { Pat::ConstBlockPat(node) }
}
+impl From<DerefPat> for Pat {
+ #[inline]
+ fn from(node: DerefPat) -> Pat { Pat::DerefPat(node) }
+}
impl From<IdentPat> for Pat {
#[inline]
fn from(node: IdentPat) -> Pat { Pat::IdentPat(node) }
@@ -8578,6 +8632,7 @@ impl AstNode for Pat {
kind,
BOX_PAT
| CONST_BLOCK_PAT
+ | DEREF_PAT
| IDENT_PAT
| LITERAL_PAT
| MACRO_PAT
@@ -8599,6 +8654,7 @@ impl AstNode for Pat {
let res = match syntax.kind() {
BOX_PAT => Pat::BoxPat(BoxPat { syntax }),
CONST_BLOCK_PAT => Pat::ConstBlockPat(ConstBlockPat { syntax }),
+ DEREF_PAT => Pat::DerefPat(DerefPat { syntax }),
IDENT_PAT => Pat::IdentPat(IdentPat { syntax }),
LITERAL_PAT => Pat::LiteralPat(LiteralPat { syntax }),
MACRO_PAT => Pat::MacroPat(MacroPat { syntax }),
@@ -8622,6 +8678,7 @@ impl AstNode for Pat {
match self {
Pat::BoxPat(it) => &it.syntax,
Pat::ConstBlockPat(it) => &it.syntax,
+ Pat::DerefPat(it) => &it.syntax,
Pat::IdentPat(it) => &it.syntax,
Pat::LiteralPat(it) => &it.syntax,
Pat::MacroPat(it) => &it.syntax,
@@ -10121,6 +10178,11 @@ impl std::fmt::Display for ContinueExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for DerefPat {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for DynTraitType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 718e5e2dca..95ff3aebd8 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -873,6 +873,10 @@ pub fn box_pat(pat: ast::Pat) -> ast::BoxPat {
ast_from_text(&format!("fn f(box {pat}: ())"))
}
+pub fn deref_pat(pat: ast::Pat) -> ast::Pat {
+ ast_from_text(&format!("fn f(deref!({pat}): ())"))
+}
+
pub fn paren_pat(pat: ast::Pat) -> ast::ParenPat {
ast_from_text(&format!("fn f(({pat}): ())"))
}
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index f5a88e40ea..51d833d39e 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -915,6 +915,10 @@ impl SyntaxFactory {
ast
}
+ pub fn deref_pat(&self, pat: ast::Pat) -> ast::Pat {
+ make::deref_pat(pat.clone()).clone_for_update()
+ }
+
pub fn paren_pat(&self, pat: ast::Pat) -> ast::ParenPat {
let ast = make::paren_pat(pat.clone()).clone_for_update();
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 802e6ab8ce..0d9b866cdf 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -28,6 +28,7 @@
//! default: sized
//! deref_mut: deref
//! deref: sized
+//! deref_pat: deref
//! derive:
//! discriminant:
//! drop: sized
@@ -617,6 +618,15 @@ pub mod ops {
}
// endregion:deref_mut
+ // region:deref_pat
+ #[lang = "deref_pure"]
+ #[rustc_dyn_incompatible_trait]
+ pub unsafe trait DerefPure: PointeeSized {}
+
+ unsafe impl<T: ?Sized> DerefPure for &T {}
+ unsafe impl<T: ?Sized> DerefPure for &mut T {}
+ // endregion:deref_pat
+
// region:receiver
#[lang = "receiver"]
pub trait Receiver: PointeeSized {
@@ -634,8 +644,9 @@ pub mod ops {
}
pub use self::deref::{
Deref,
- DerefMut, // :deref_mut
- Receiver, // :receiver
+ DerefMut, // :deref_mut
+ DerefPure, // :deref_pat
+ Receiver, // :receiver
};
// endregion:deref
@@ -2258,6 +2269,13 @@ mod macros {
#[macro_export]
macro_rules! option_env {}
// endregion:env
+
+ // region:deref_pat
+ #[allow_internal_unstable(builtin_syntax)]
+ pub macro deref($pat:pat) {
+ builtin # deref($pat)
+ }
+ // endregion:deref_pat
}
// region:non_zero
@@ -2387,6 +2405,7 @@ pub mod prelude {
panic, // :panic
result::Result::{self, Err, Ok}, // :result
str::FromStr, // :str
+ macros::deref, // :deref_pat
};
}
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index a0abdf09d3..43462d1c6e 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -150,6 +150,7 @@ const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
// "raw",
"readonly",
"sym",
+ "deref",
];
// keywords that are keywords depending on the edition