Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22292 from rust-lang/deref-pat
feat: Support deref patterns
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 |