about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Holk <ericholk@microsoft.com>2024-09-20 12:09:18 -0700
committerEric Holk <ericholk@microsoft.com>2024-10-07 11:15:04 -0700
commitae698f819935aae8623f41aa3837cc0a07903c1a (patch)
treec13cb2f6aa92365689a10cbc4cbe8a297a5ba382
parent7caad6925314911eefe54b040d4bc5be940e8f92 (diff)
downloadrust-ae698f819935aae8623f41aa3837cc0a07903c1a.tar.gz
rust-ae698f819935aae8623f41aa3837cc0a07903c1a.zip
Add sugar for &pin (const|mut) types
-rw-r--r--compiler/rustc_ast/src/ast.rs11
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs2
-rw-r--r--compiler/rustc_ast/src/util/classify.rs4
-rw-r--r--compiler/rustc_ast/src/visit.rs3
-rw-r--r--compiler/rustc_ast_ir/src/lib.rs7
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs5
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs51
-rw-r--r--compiler/rustc_ast_lowering/src/lifetime_collector.rs2
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs6
-rw-r--r--compiler/rustc_hir/src/hir.rs5
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs39
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs50
-rw-r--r--compiler/rustc_resolve/src/late.rs4
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs3
-rw-r--r--src/tools/rustfmt/src/types.rs5
-rw-r--r--tests/ui/async-await/pin-sugar-ambiguity.rs15
-rw-r--r--tests/ui/async-await/pin-sugar-no-const.rs8
-rw-r--r--tests/ui/async-await/pin-sugar-no-const.stderr15
-rw-r--r--tests/ui/async-await/pin-sugar.rs51
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.rs11
-rw-r--r--tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr45
23 files changed, 283 insertions, 62 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index f2b52eea12f..88db97b66e4 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -23,7 +23,7 @@ use std::{cmp, fmt, mem};
 
 pub use GenericArgs::*;
 pub use UnsafeSource::*;
-pub use rustc_ast_ir::{Movability, Mutability};
+pub use rustc_ast_ir::{Movability, Mutability, Pinnedness};
 use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -2161,6 +2161,10 @@ pub enum TyKind {
     Ptr(MutTy),
     /// A reference (`&'a T` or `&'a mut T`).
     Ref(Option<Lifetime>, MutTy),
+    /// A pinned reference (`&'a pin const T` or `&'a pin mut T`).
+    ///
+    /// Desugars into `Pin<&'a T>` or `Pin<&'a mut T>`.
+    PinnedRef(Option<Lifetime>, MutTy),
     /// A bare function (e.g., `fn(usize) -> bool`).
     BareFn(P<BareFnTy>),
     /// The never type (`!`).
@@ -2509,7 +2513,10 @@ impl Param {
             if ident.name == kw::SelfLower {
                 return match self.ty.kind {
                     TyKind::ImplicitSelf => Some(respan(self.pat.span, SelfKind::Value(mutbl))),
-                    TyKind::Ref(lt, MutTy { ref ty, mutbl }) if ty.kind.is_implicit_self() => {
+                    TyKind::Ref(lt, MutTy { ref ty, mutbl })
+                    | TyKind::PinnedRef(lt, MutTy { ref ty, mutbl })
+                        if ty.kind.is_implicit_self() =>
+                    {
                         Some(respan(self.pat.span, SelfKind::Region(lt, mutbl)))
                     }
                     _ => Some(respan(
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 6ce23a1479d..1c57dd948fc 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -485,7 +485,7 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
         }
         TyKind::Slice(ty) => vis.visit_ty(ty),
         TyKind::Ptr(mt) => vis.visit_mt(mt),
-        TyKind::Ref(lt, mt) => {
+        TyKind::Ref(lt, mt) | TyKind::PinnedRef(lt, mt) => {
             visit_opt(lt, |lt| vis.visit_lifetime(lt));
             vis.visit_mt(mt);
         }
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 1a80a9ccdbf..8fdaf995850 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -247,7 +247,9 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
                 break (mac.args.delim == Delimiter::Brace).then_some(mac);
             }
 
-            ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => {
+            ast::TyKind::Ptr(mut_ty)
+            | ast::TyKind::Ref(_, mut_ty)
+            | ast::TyKind::PinnedRef(_, mut_ty) => {
                 ty = &mut_ty.ty;
             }
 
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 9f9c3d8c392..1ab88e8c02e 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -499,7 +499,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
     match kind {
         TyKind::Slice(ty) | TyKind::Paren(ty) => try_visit!(visitor.visit_ty(ty)),
         TyKind::Ptr(MutTy { ty, mutbl: _ }) => try_visit!(visitor.visit_ty(ty)),
-        TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ }) => {
+        TyKind::Ref(opt_lifetime, MutTy { ty, mutbl: _ })
+        | TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl: _ }) => {
             visit_opt!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
             try_visit!(visitor.visit_ty(ty));
         }
diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs
index eeed5d36151..ff9d940ce9f 100644
--- a/compiler/rustc_ast_ir/src/lib.rs
+++ b/compiler/rustc_ast_ir/src/lib.rs
@@ -79,3 +79,10 @@ impl Mutability {
         matches!(self, Self::Not)
     }
 }
+
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
+#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
+pub enum Pinnedness {
+    Not,
+    Pinned,
+}
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 52372bbf991..ae1e1b3f8a2 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -640,7 +640,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     self.lower_span(span),
                     Some(self.allow_gen_future.clone()),
                 );
-                let resume_ty = self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span);
+                let resume_ty =
+                    self.make_lang_item_qpath(hir::LangItem::ResumeTy, unstable_span, None);
                 let input_ty = hir::Ty {
                     hir_id: self.next_id(),
                     kind: hir::TyKind::Path(resume_ty),
@@ -2065,7 +2066,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         lang_item: hir::LangItem,
         name: Symbol,
     ) -> hir::Expr<'hir> {
-        let qpath = self.make_lang_item_qpath(lang_item, self.lower_span(span));
+        let qpath = self.make_lang_item_qpath(lang_item, self.lower_span(span), None);
         let path = hir::ExprKind::Path(hir::QPath::TypeRelative(
             self.arena.alloc(self.ty(span, hir::TyKind::Path(qpath))),
             self.arena.alloc(hir::PathSegment::new(
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index b26797f4203..bd20a70f325 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -55,8 +55,8 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdMap};
 use rustc_hir::{
-    self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, MissingLifetimeKind, ParamName,
-    TraitCandidate,
+    self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, MissingLifetimeKind,
+    ParamName, TraitCandidate,
 };
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_macros::extension;
@@ -765,8 +765,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         res
     }
 
-    fn make_lang_item_qpath(&mut self, lang_item: hir::LangItem, span: Span) -> hir::QPath<'hir> {
-        hir::QPath::Resolved(None, self.make_lang_item_path(lang_item, span, None))
+    fn make_lang_item_qpath(
+        &mut self,
+        lang_item: hir::LangItem,
+        span: Span,
+        args: Option<&'hir hir::GenericArgs<'hir>>,
+    ) -> hir::QPath<'hir> {
+        hir::QPath::Resolved(None, self.make_lang_item_path(lang_item, span, args))
     }
 
     fn make_lang_item_path(
@@ -1317,6 +1322,32 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let lifetime = self.lower_lifetime(&region);
                 hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx))
             }
+            TyKind::PinnedRef(region, mt) => {
+                let region = region.unwrap_or_else(|| {
+                    let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
+                        self.resolver.get_lifetime_res(t.id)
+                    {
+                        debug_assert_eq!(start.plus(1), end);
+                        start
+                    } else {
+                        self.next_node_id()
+                    };
+                    let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
+                    Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }
+                });
+                let lifetime = self.lower_lifetime(&region);
+                let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx));
+                let span = self.lower_span(t.span);
+                let arg = hir::Ty { kind, span, hir_id: self.next_id() };
+                let args = self.arena.alloc(hir::GenericArgs {
+                    args: self.arena.alloc([hir::GenericArg::Type(self.arena.alloc(arg))]),
+                    constraints: &[],
+                    parenthesized: hir::GenericArgsParentheses::No,
+                    span_ext: span,
+                });
+                let path = self.make_lang_item_qpath(LangItem::Pin, span, Some(args));
+                hir::TyKind::Path(path)
+            }
             TyKind::BareFn(f) => {
                 let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params);
                 hir::TyKind::BareFn(self.arena.alloc(hir::BareFnTy {
@@ -1882,10 +1913,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     // Given we are only considering `ImplicitSelf` types, we needn't consider
                     // the case where we have a mutable pattern to a reference as that would
                     // no longer be an `ImplicitSelf`.
-                    TyKind::Ref(_, mt) if mt.ty.kind.is_implicit_self() => match mt.mutbl {
-                        hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
-                        hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
-                    },
+                    TyKind::Ref(_, mt) | TyKind::PinnedRef(_, mt)
+                        if mt.ty.kind.is_implicit_self() =>
+                    {
+                        match mt.mutbl {
+                            hir::Mutability::Not => hir::ImplicitSelfKind::RefImm,
+                            hir::Mutability::Mut => hir::ImplicitSelfKind::RefMut,
+                        }
+                    }
                     _ => hir::ImplicitSelfKind::None,
                 }
             }),
diff --git a/compiler/rustc_ast_lowering/src/lifetime_collector.rs b/compiler/rustc_ast_lowering/src/lifetime_collector.rs
index 1e82ba5db8a..fe64160fb4d 100644
--- a/compiler/rustc_ast_lowering/src/lifetime_collector.rs
+++ b/compiler/rustc_ast_lowering/src/lifetime_collector.rs
@@ -95,7 +95,7 @@ impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
                 visit::walk_ty(self, t);
                 self.current_binders.pop();
             }
-            TyKind::Ref(None, _) => {
+            TyKind::Ref(None, _) | TyKind::PinnedRef(None, _) => {
                 self.record_elided_anchor(t.id, t.span);
                 visit::walk_ty(self, t);
             }
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 42d1ef5c558..5daaa840e13 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -547,6 +547,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(mut_ref, "mutable by-reference bindings are experimental");
     gate_all!(global_registration, "global registration is experimental");
     gate_all!(return_type_notation, "return type notation is experimental");
+    gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
 
     if !visitor.features.never_patterns {
         if let Some(spans) = spans.get(&sym::never_patterns) {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 884cc413bd5..8b3653ab097 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1163,6 +1163,12 @@ impl<'a> State<'a> {
                 self.print_opt_lifetime(lifetime);
                 self.print_mt(mt, false);
             }
+            ast::TyKind::PinnedRef(lifetime, mt) => {
+                self.word("&");
+                self.print_opt_lifetime(lifetime);
+                self.word("pin ");
+                self.print_mt(mt, true);
+            }
             ast::TyKind::Never => {
                 self.word("!");
             }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 2ef6fa53f4e..bc5ed249559 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1,10 +1,9 @@
 use std::fmt;
 
-use rustc_ast as ast;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_ast::{
-    Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitKind,
-    TraitObjectSyntax, UintTy,
+    self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
+    LitKind, TraitObjectSyntax, UintTy,
 };
 pub use rustc_ast::{
     BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy, ImplPolarity, IsAuto, Movability,
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index a8ed8b5df9c..ccd6dfda797 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -2,9 +2,7 @@ use rustc_ast::ptr::P;
 use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token, TokenKind};
 use rustc_ast::util::case::Case;
 use rustc_ast::{
-    self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy,
-    GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability,
-    PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind,
+    self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID
 };
 use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::{Ident, kw, sym};
@@ -529,7 +527,10 @@ impl<'a> Parser<'a> {
     fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> {
         let and_span = self.prev_token.span;
         let mut opt_lifetime = self.check_lifetime().then(|| self.expect_lifetime());
-        let mut mutbl = self.parse_mutability();
+        let (pinned, mut mutbl) = match self.parse_pin_and_mut() {
+            Some(pin_mut) => pin_mut,
+            None => (Pinnedness::Not, self.parse_mutability()),
+        };
         if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() {
             // A lifetime is invalid here: it would be part of a bare trait bound, which requires
             // it to be followed by a plus, but we disallow plus in the pointee type.
@@ -565,7 +566,35 @@ impl<'a> Parser<'a> {
             self.bump_with((dyn_tok, dyn_tok_sp));
         }
         let ty = self.parse_ty_no_plus()?;
-        Ok(TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }))
+        Ok(match pinned {
+            Pinnedness::Not => TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }),
+            Pinnedness::Pinned => TyKind::PinnedRef(opt_lifetime, MutTy { ty, mutbl }),
+        })
+    }
+
+    /// Parses `pin` and `mut` annotations on references.
+    ///
+    /// It must be either `pin const` or `pin mut`.
+    pub(crate) fn parse_pin_and_mut(&mut self) -> Option<(Pinnedness, Mutability)> {
+        if self.token.is_ident_named(sym::pin) {
+            let result = self.look_ahead(1, |token| {
+                if token.is_keyword(kw::Const) {
+                    Some((Pinnedness::Pinned, Mutability::Not))
+                } else if token.is_keyword(kw::Mut) {
+                    Some((Pinnedness::Pinned, Mutability::Mut))
+                } else {
+                    None
+                }
+            });
+            if result.is_some() {
+                self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
+                self.bump();
+                self.bump();
+            }
+            result
+        } else {
+            None
+        }
     }
 
     // Parses the `typeof(EXPR)`.
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 8ad14b6eb74..fc9d548d1fb 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -574,29 +574,33 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
     }
 
     fn visit_ty(&mut self, t: &'v ast::Ty) {
-        record_variants!((self, t, t.kind, Id::None, ast, Ty, TyKind), [
-            Slice,
-            Array,
-            Ptr,
-            Ref,
-            BareFn,
-            Never,
-            Tup,
-            AnonStruct,
-            AnonUnion,
-            Path,
-            Pat,
-            TraitObject,
-            ImplTrait,
-            Paren,
-            Typeof,
-            Infer,
-            ImplicitSelf,
-            MacCall,
-            CVarArgs,
-            Dummy,
-            Err
-        ]);
+        record_variants!(
+            (self, t, t.kind, Id::None, ast, Ty, TyKind),
+            [
+                Slice,
+                Array,
+                Ptr,
+                Ref,
+                PinnedRef,
+                BareFn,
+                Never,
+                Tup,
+                AnonStruct,
+                AnonUnion,
+                Path,
+                Pat,
+                TraitObject,
+                ImplTrait,
+                Paren,
+                Typeof,
+                Infer,
+                ImplicitSelf,
+                MacCall,
+                CVarArgs,
+                Dummy,
+                Err
+            ]
+        );
 
         ast_visit::walk_ty(self, t)
     }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 66c1ff93ce1..b84cbf9c629 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -779,7 +779,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
         let prev = self.diag_metadata.current_trait_object;
         let prev_ty = self.diag_metadata.current_type_path;
         match &ty.kind {
-            TyKind::Ref(None, _) => {
+            TyKind::Ref(None, _) | TyKind::PinnedRef(None, _) => {
                 // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with
                 // NodeId `ty.id`.
                 // This span will be used in case of elision failure.
@@ -2326,7 +2326,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
         impl<'ra> Visitor<'ra> for FindReferenceVisitor<'_, '_, '_> {
             fn visit_ty(&mut self, ty: &'ra Ty) {
                 trace!("FindReferenceVisitor considering ty={:?}", ty);
-                if let TyKind::Ref(lt, _) = ty.kind {
+                if let TyKind::Ref(lt, _) | TyKind::PinnedRef(lt, _) = ty.kind {
                     // See if anything inside the &thing contains Self
                     let mut visitor =
                         SelfVisitor { r: self.r, impl_self: self.impl_self, self_found: false };
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index fce5ec36c66..aced05ca351 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -3482,7 +3482,7 @@ struct LifetimeFinder<'ast> {
 
 impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> {
     fn visit_ty(&mut self, t: &'ast Ty) {
-        if let TyKind::Ref(_, mut_ty) = &t.kind {
+        if let TyKind::Ref(_, mut_ty) | TyKind::PinnedRef(_, mut_ty) = &t.kind {
             self.seen.push(t);
             if t.span.lo() == self.lifetime.lo() {
                 self.found = Some(&mut_ty.ty);
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 68f74e52ed7..187f7fb4417 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -753,6 +753,9 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
         (Ref(ll, l), Ref(rl, r)) => {
             both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
         },
+        (PinnedRef(ll, l), PinnedRef(rl, r)) => {
+            both(ll.as_ref(), rl.as_ref(), |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
+        },
         (BareFn(l), BareFn(r)) => {
             l.safety == r.safety
                 && eq_ext(&l.ext, &r.ext)
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 07b483b2b37..b64e069e096 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -827,7 +827,8 @@ impl Rewrite for ast::Ty {
 
                 rewrite_unary_prefix(context, prefix, &*mt.ty, shape)
             }
-            ast::TyKind::Ref(ref lifetime, ref mt) => {
+            ast::TyKind::Ref(ref lifetime, _pinned, ref mt) => {
+                // FIXME: format pinnedness
                 let mut_str = format_mutability(mt.mutbl);
                 let mut_len = mut_str.len();
                 let mut result = String::with_capacity(128);
@@ -1262,7 +1263,7 @@ pub(crate) fn can_be_overflowed_type(
 ) -> bool {
     match ty.kind {
         ast::TyKind::Tup(..) => context.use_block_indent() && len == 1,
-        ast::TyKind::Ref(_, ref mutty) | ast::TyKind::Ptr(ref mutty) => {
+        ast::TyKind::Ref(_, _, ref mutty) | ast::TyKind::Ptr(ref mutty) => {
             can_be_overflowed_type(context, &*mutty.ty, len)
         }
         _ => false,
diff --git a/tests/ui/async-await/pin-sugar-ambiguity.rs b/tests/ui/async-await/pin-sugar-ambiguity.rs
new file mode 100644
index 00000000000..d183000931e
--- /dev/null
+++ b/tests/ui/async-await/pin-sugar-ambiguity.rs
@@ -0,0 +1,15 @@
+//@ check-pass
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+// Handle the case where there's ambiguity between pin as a contextual keyword and pin as a path.
+
+struct Foo;
+
+mod pin {
+    pub struct Foo;
+}
+
+fn main() {
+    let _x: &pin ::Foo = &pin::Foo;
+}
diff --git a/tests/ui/async-await/pin-sugar-no-const.rs b/tests/ui/async-await/pin-sugar-no-const.rs
new file mode 100644
index 00000000000..dd6456b6034
--- /dev/null
+++ b/tests/ui/async-await/pin-sugar-no-const.rs
@@ -0,0 +1,8 @@
+#![feature(pin_ergonomics)]
+#![allow(incomplete_features)]
+
+// Makes sure we don't accidentally accept `&pin Foo` without the `const` keyword.
+
+fn main() {
+    let _x: &pin i32 = todo!(); //~ ERROR found `i32`
+}
diff --git a/tests/ui/async-await/pin-sugar-no-const.stderr b/tests/ui/async-await/pin-sugar-no-const.stderr
new file mode 100644
index 00000000000..5f01156c1f0
--- /dev/null
+++ b/tests/ui/async-await/pin-sugar-no-const.stderr
@@ -0,0 +1,15 @@
+error: expected one of `!`, `(`, `::`, `;`, `<`, or `=`, found `i32`
+  --> $DIR/pin-sugar-no-const.rs:7:18
+   |
+LL |     let _x: &pin i32 = todo!();
+   |           -      ^^^ expected one of `!`, `(`, `::`, `;`, `<`, or `=`
+   |           |
+   |           while parsing the type for `_x`
+   |
+help: there is a keyword `in` with a similar name
+   |
+LL |     let _x: &in i32 = todo!();
+   |              ~~
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/async-await/pin-sugar.rs b/tests/ui/async-await/pin-sugar.rs
new file mode 100644
index 00000000000..8dbdec418b1
--- /dev/null
+++ b/tests/ui/async-await/pin-sugar.rs
@@ -0,0 +1,51 @@
+//@ check-pass
+
+#![feature(pin_ergonomics)]
+#![allow(dead_code, incomplete_features)]
+
+// Makes sure we can handle `&pin mut T` and `&pin const T` as sugar for `Pin<&mut T>` and
+// `Pin<&T>`.
+
+use std::pin::Pin;
+
+struct Foo;
+
+impl Foo {
+    fn baz(self: &pin mut Self) {
+    }
+
+    fn baz_const(self: &pin const Self) {
+    }
+
+    fn baz_lt<'a>(self: &'a pin mut Self) {
+    }
+
+    fn baz_const_lt(self: &'_ pin const Self) {
+    }
+}
+
+fn foo(_: &pin mut Foo) {
+}
+
+fn foo_const(x: &pin const Foo) {
+}
+
+fn bar(x: &pin mut Foo) {
+    foo(x);
+    foo(x); // for this to work we need to automatically reborrow,
+            // as if the user had written `foo(x.as_mut())`.
+
+    Foo::baz(x);
+    Foo::baz(x);
+
+    // make sure we can reborrow &mut as &.
+    foo_const(x);
+    Foo::baz_const(x);
+
+    let x: &pin const _ = Pin::new(&Foo);
+
+    foo_const(x); // make sure reborrowing from & to & works.
+    foo_const(x);
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
index 3382504af9d..4624faf1e53 100644
--- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs
@@ -1,4 +1,4 @@
-#![allow(dead_code, incomplete_features)]
+#![allow(dead_code)]
 
 use std::pin::Pin;
 
@@ -9,10 +9,13 @@ impl Foo {
     }
 }
 
-fn foo(_: Pin<&mut Foo>) {
+fn foo(x: Pin<&mut Foo>) {
+    let _y: &pin mut Foo = x; //~ ERROR pinned reference syntax is experimental
 }
 
-fn bar(mut x: Pin<&mut Foo>) {
+fn foo_sugar(_: &pin mut Foo) {} //~ ERROR pinned reference syntax is experimental
+
+fn bar(x: Pin<&mut Foo>) {
     foo(x);
     foo(x); //~ ERROR use of moved value: `x`
 }
@@ -22,4 +25,6 @@ fn baz(mut x: Pin<&mut Foo>) {
     x.foo(); //~ ERROR use of moved value: `x`
 }
 
+fn baz_sugar(_: &pin const Foo) {} //~ ERROR pinned reference syntax is experimental
+
 fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
index 430b7866241..dd93a7be1ad 100644
--- a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
+++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr
@@ -1,8 +1,38 @@
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:13:14
+   |
+LL |     let _y: &pin mut Foo = x;
+   |              ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:16:18
+   |
+LL | fn foo_sugar(_: &pin mut Foo) {}
+   |                  ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: pinned reference syntax is experimental
+  --> $DIR/feature-gate-pin_ergonomics.rs:28:18
+   |
+LL | fn baz_sugar(_: &pin const Foo) {}
+   |                  ^^^
+   |
+   = note: see issue #130494 <https://github.com/rust-lang/rust/issues/130494> for more information
+   = help: add `#![feature(pin_ergonomics)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
 error[E0382]: use of moved value: `x`
-  --> $DIR/feature-gate-pin_ergonomics.rs:17:9
+  --> $DIR/feature-gate-pin_ergonomics.rs:20:9
    |
-LL | fn bar(mut x: Pin<&mut Foo>) {
-   |        ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
+LL | fn bar(x: Pin<&mut Foo>) {
+   |        - move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
 LL |     foo(x);
    |         - value moved here
 LL |     foo(x);
@@ -11,13 +41,13 @@ LL |     foo(x);
 note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary
   --> $DIR/feature-gate-pin_ergonomics.rs:12:11
    |
-LL | fn foo(_: Pin<&mut Foo>) {
+LL | fn foo(x: Pin<&mut Foo>) {
    |    ---    ^^^^^^^^^^^^^ this parameter takes ownership of the value
    |    |
    |    in this function
 
 error[E0382]: use of moved value: `x`
-  --> $DIR/feature-gate-pin_ergonomics.rs:22:5
+  --> $DIR/feature-gate-pin_ergonomics.rs:25:5
    |
 LL | fn baz(mut x: Pin<&mut Foo>) {
    |        ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait
@@ -36,6 +66,7 @@ help: consider reborrowing the `Pin` instead of moving it
 LL |     x.as_mut().foo();
    |      +++++++++
 
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0382`.
+Some errors have detailed explanations: E0382, E0658.
+For more information about an error, try `rustc --explain E0382`.