From ae698f819935aae8623f41aa3837cc0a07903c1a Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 20 Sep 2024 12:09:18 -0700 Subject: [PATCH 1/3] Add sugar for &pin (const|mut) types --- compiler/rustc_ast/src/ast.rs | 11 +++- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/util/classify.rs | 4 +- compiler/rustc_ast/src/visit.rs | 3 +- compiler/rustc_ast_ir/src/lib.rs | 7 +++ compiler/rustc_ast_lowering/src/expr.rs | 5 +- compiler/rustc_ast_lowering/src/lib.rs | 51 ++++++++++++++++--- .../src/lifetime_collector.rs | 2 +- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 6 +++ compiler/rustc_hir/src/hir.rs | 5 +- compiler/rustc_parse/src/parser/ty.rs | 39 ++++++++++++-- compiler/rustc_passes/src/hir_stats.rs | 50 +++++++++--------- compiler/rustc_resolve/src/late.rs | 4 +- .../rustc_resolve/src/late/diagnostics.rs | 2 +- .../clippy/clippy_utils/src/ast_utils.rs | 3 ++ src/tools/rustfmt/src/types.rs | 5 +- tests/ui/async-await/pin-sugar-ambiguity.rs | 15 ++++++ tests/ui/async-await/pin-sugar-no-const.rs | 8 +++ .../ui/async-await/pin-sugar-no-const.stderr | 15 ++++++ tests/ui/async-await/pin-sugar.rs | 51 +++++++++++++++++++ .../feature-gate-pin_ergonomics.rs | 11 ++-- .../feature-gate-pin_ergonomics.stderr | 45 +++++++++++++--- 23 files changed, 283 insertions(+), 62 deletions(-) create mode 100644 tests/ui/async-await/pin-sugar-ambiguity.rs create mode 100644 tests/ui/async-await/pin-sugar-no-const.rs create mode 100644 tests/ui/async-await/pin-sugar-no-const.stderr create mode 100644 tests/ui/async-await/pin-sugar.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f2b52eea12f21..88db97b66e496 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, 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, MutTy), /// A bare function (e.g., `fn(usize) -> bool`). BareFn(P), /// 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 6ce23a1479d05..1c57dd948fc86 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(vis: &mut T, ty: &mut P) { } 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 1a80a9ccdbf54..8fdaf99585089 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 9f9c3d8c39239..1ab88e8c02ef3 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 eeed5d3615139..ff9d940ce9f28 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 52372bbf991c9..ae1e1b3f8a21d 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 b26797f42032f..bd20a70f325e1 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(®ion); 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(®ion); + 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 1e82ba5db8a2a..fe64160fb4dcf 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 42d1ef5c558b0..5daaa840e13ba 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 884cc413bd583..8b3653ab097c7 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 2ef6fa53f4edd..bc5ed249559a5 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 a8ed8b5df9c00..ccd6dfda79703 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 8ad14b6eb74b2..fc9d548d1fbea 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 66c1ff93ce1ce..b84cbf9c62941 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 fce5ec36c661b..aced05ca351af 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 68f74e52ed7b7..187f7fb441761 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 07b483b2b3721..b64e069e09665 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 0000000000000..d183000931ec1 --- /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 0000000000000..dd6456b603481 --- /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 0000000000000..5f01156c1f0a4 --- /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 0000000000000..8dbdec418b1fa --- /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 3382504af9d61..4624faf1e53cf 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 430b78662414a..dd93a7be1ada1 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 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 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 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`. From b490bf56b7da3a689f0ea7a46f56d7cd2339efb0 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 20 Sep 2024 15:44:43 -0700 Subject: [PATCH 2/3] Fix clippy and rustfmt compilation --- compiler/rustc_parse/src/parser/ty.rs | 5 ++- compiler/rustc_passes/src/hir_stats.rs | 51 ++++++++++++-------------- src/tools/rustfmt/src/types.rs | 11 +++--- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index ccd6dfda79703..b0ff033cf8011 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -2,7 +2,10 @@ 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, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, TyKind, DUMMY_NODE_ID + self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy, + GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, + Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, + TyKind, }; use rustc_errors::{Applicability, PResult}; use rustc_span::symbol::{Ident, kw, sym}; diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index fc9d548d1fbea..daf1bd8b19d00 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -574,33 +574,30 @@ 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, - PinnedRef, - 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/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index b64e069e09665..999deb5dd4aa7 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -827,8 +827,9 @@ impl Rewrite for ast::Ty { rewrite_unary_prefix(context, prefix, &*mt.ty, shape) } - ast::TyKind::Ref(ref lifetime, _pinned, ref mt) => { - // FIXME: format pinnedness + ast::TyKind::Ref(ref lifetime, ref mt) + | ast::TyKind::PinnedRef(ref lifetime, ref mt) => { + // FIXME(pin_ergonomics): correctly format pinned reference syntax let mut_str = format_mutability(mt.mutbl); let mut_len = mut_str.len(); let mut result = String::with_capacity(128); @@ -1263,9 +1264,9 @@ 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) => { - can_be_overflowed_type(context, &*mutty.ty, len) - } + ast::TyKind::Ref(_, ref mutty) + | ast::TyKind::PinnedRef(_, ref mutty) + | ast::TyKind::Ptr(ref mutty) => can_be_overflowed_type(context, &*mutty.ty, len), _ => false, } } From 3aabe1e4a3518f943a20b692a0db90189fc5898c Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Mon, 23 Sep 2024 16:07:31 -0700 Subject: [PATCH 3/3] Add basic pin sugar support to rustfmt --- src/tools/rustfmt/src/types.rs | 8 +++++++- src/tools/rustfmt/tests/source/pin_sugar.rs | 10 ++++++++++ src/tools/rustfmt/tests/target/pin_sugar.rs | 9 +++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/tools/rustfmt/tests/source/pin_sugar.rs create mode 100644 src/tools/rustfmt/tests/target/pin_sugar.rs diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 999deb5dd4aa7..f7177c7f85464 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -829,7 +829,6 @@ impl Rewrite for ast::Ty { } ast::TyKind::Ref(ref lifetime, ref mt) | ast::TyKind::PinnedRef(ref lifetime, ref mt) => { - // FIXME(pin_ergonomics): correctly format pinned reference syntax let mut_str = format_mutability(mt.mutbl); let mut_len = mut_str.len(); let mut result = String::with_capacity(128); @@ -863,6 +862,13 @@ impl Rewrite for ast::Ty { cmnt_lo = lifetime.ident.span.hi(); } + if let ast::TyKind::PinnedRef(..) = self.kind { + result.push_str("pin "); + if ast::Mutability::Not == mt.mutbl { + result.push_str("const "); + } + } + if ast::Mutability::Mut == mt.mutbl { let mut_hi = context.snippet_provider.span_after(self.span(), "mut"); let before_mut_span = mk_sp(cmnt_lo, mut_hi - BytePos::from_usize(3)); diff --git a/src/tools/rustfmt/tests/source/pin_sugar.rs b/src/tools/rustfmt/tests/source/pin_sugar.rs new file mode 100644 index 0000000000000..0eb3c0770c482 --- /dev/null +++ b/src/tools/rustfmt/tests/source/pin_sugar.rs @@ -0,0 +1,10 @@ +// See #130494 + +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +fn f(x: &pin const i32) {} +fn g<'a>(x: & 'a pin const i32) {} +fn h<'a>(x: & 'a pin +mut i32) {} +fn i(x: &pin mut i32) {} diff --git a/src/tools/rustfmt/tests/target/pin_sugar.rs b/src/tools/rustfmt/tests/target/pin_sugar.rs new file mode 100644 index 0000000000000..c9fa883e238fd --- /dev/null +++ b/src/tools/rustfmt/tests/target/pin_sugar.rs @@ -0,0 +1,9 @@ +// See #130494 + +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +fn f(x: &pin const i32) {} +fn g<'a>(x: &'a pin const i32) {} +fn h<'a>(x: &'a pin mut i32) {} +fn i(x: &pin mut i32) {}