Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add &pin (mut|const) T type position sugar #130635

Merged
merged 3 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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`).
eholk marked this conversation as resolved.
Show resolved Hide resolved
///
/// 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 (`!`).
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_ast_ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
5 changes: 3 additions & 2 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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(
Expand Down
51 changes: 43 additions & 8 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
}
}),
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ast_lowering/src/lifetime_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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("!");
}
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
38 changes: 35 additions & 3 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ 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,
Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty,
TyKind,
};
use rustc_errors::{Applicability, PResult};
use rustc_span::symbol::{Ident, kw, sym};
Expand Down Expand Up @@ -529,7 +530,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.
Expand Down Expand Up @@ -565,7 +569,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)`.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/hir_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
Array,
Ptr,
Ref,
PinnedRef,
BareFn,
Never,
Tup,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 };
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/tools/clippy/clippy_utils/src/ast_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
16 changes: 12 additions & 4 deletions src/tools/rustfmt/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, ref mt)
| ast::TyKind::PinnedRef(ref lifetime, ref mt) => {
let mut_str = format_mutability(mt.mutbl);
let mut_len = mut_str.len();
let mut result = String::with_capacity(128);
Expand Down Expand Up @@ -861,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));
Expand Down Expand Up @@ -1262,9 +1270,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,
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/tools/rustfmt/tests/source/pin_sugar.rs
Original file line number Diff line number Diff line change
@@ -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) {}
9 changes: 9 additions & 0 deletions src/tools/rustfmt/tests/target/pin_sugar.rs
Original file line number Diff line number Diff line change
@@ -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) {}
15 changes: 15 additions & 0 deletions tests/ui/async-await/pin-sugar-ambiguity.rs
Original file line number Diff line number Diff line change
@@ -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;
}
Loading
Loading