Skip to content
14 changes: 14 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3542,6 +3542,19 @@ impl VisibilityKind {
}
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct ImplRestriction {
pub kind: RestrictionKind,
pub span: Span,
pub tokens: Option<LazyAttrTokenStream>,
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum RestrictionKind {
Unrestricted,
Restricted { path: Box<Path>, id: NodeId, shorthand: bool },
}

/// Field definition in a struct, variant or union.
///
/// E.g., `bar: usize` as in `struct Foo { bar: usize }`.
Expand Down Expand Up @@ -3741,6 +3754,7 @@ pub struct Trait {
pub constness: Const,
pub safety: Safety,
pub is_auto: IsAuto,
pub impl_restriction: ImplRestriction,
pub ident: Ident,
pub generics: Generics,
#[visitable(extra = BoundKind::SuperTraits)]
Expand Down
20 changes: 16 additions & 4 deletions compiler/rustc_ast/src/ast_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use std::marker::PhantomData;
use crate::tokenstream::LazyAttrTokenStream;
use crate::{
Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField,
FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind,
Ty, Variant, Visibility, WherePredicate,
FieldDef, ForeignItem, GenericParam, ImplRestriction, Item, NodeId, Param, Pat, PatField, Path,
Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate,
};

/// A trait for AST nodes having an ID.
Expand Down Expand Up @@ -97,7 +97,19 @@ macro_rules! impl_has_tokens_none {
};
}

impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility);
impl_has_tokens!(
AssocItem,
AttrItem,
Block,
Expr,
ForeignItem,
Item,
Pat,
Path,
Ty,
Visibility,
ImplRestriction
);
impl_has_tokens_none!(
Arm,
ExprField,
Expand Down Expand Up @@ -242,7 +254,7 @@ impl_has_attrs!(
Variant,
WherePredicate,
);
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility, ImplRestriction);

impl<T: HasAttrs> HasAttrs for Box<T> {
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS;
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ macro_rules! common_visitor_and_walkers {
RangeEnd,
RangeSyntax,
Recovered,
RestrictionKind,
Safety,
StaticItem,
StrLit,
Expand Down Expand Up @@ -595,6 +596,7 @@ macro_rules! common_visitor_and_walkers {
fn visit_poly_trait_ref(PolyTraitRef);
fn visit_precise_capturing_arg(PreciseCapturingArg);
fn visit_qself(QSelf);
fn visit_impl_restriction(ImplRestriction);
fn visit_trait_ref(TraitRef);
fn visit_ty_pat(TyPat);
fn visit_ty(Ty);
Expand Down Expand Up @@ -1115,6 +1117,7 @@ macro_rules! common_visitor_and_walkers {
pub fn walk_poly_trait_ref(PolyTraitRef);
pub fn walk_precise_capturing_arg(PreciseCapturingArg);
pub fn walk_qself(QSelf);
pub fn walk_impl_restriction(ImplRestriction);
pub fn walk_trait_ref(TraitRef);
pub fn walk_ty_pat(TyPat);
pub fn walk_ty(Ty);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
constness,
is_auto,
safety,
// FIXME(impl_restrictions): lower to HIR
impl_restriction: _,
ident,
generics,
bounds,
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 @@ -576,6 +576,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(coroutines, "coroutine syntax is experimental");
gate_all!(const_block_items, "const block items are experimental");
gate_all!(final_associated_functions, "`final` on trait functions is experimental");
gate_all!(impl_restriction, "`impl` restrictions are experimental");

if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ pub fn vis_to_string(v: &ast::Visibility) -> String {
State::new().vis_to_string(v)
}

pub fn impl_restriction_to_string(r: &ast::ImplRestriction) -> String {
State::new().impl_restriction_to_string(r)
}

pub fn meta_list_item_to_string(li: &ast::MetaItemInner) -> String {
State::new().meta_list_item_to_string(li)
}
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
Self::to_string(|s| s.print_visibility(v))
}

fn impl_restriction_to_string(&self, r: &ast::ImplRestriction) -> String {
Self::to_string(|s| s.print_impl_restriction(r))
}

fn block_to_string(&self, blk: &ast::Block) -> String {
Self::to_string(|s| {
let (cb, ib) = s.head("");
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ impl<'a> State<'a> {
constness,
safety,
is_auto,
impl_restriction,
ident,
generics,
bounds,
Expand All @@ -375,6 +376,7 @@ impl<'a> State<'a> {
self.print_constness(*constness);
self.print_safety(*safety);
self.print_is_auto(*is_auto);
self.print_impl_restriction(impl_restriction);
self.word_nbsp("trait");
self.print_ident(*ident);
self.print_generic_params(&generics.params);
Expand Down Expand Up @@ -483,6 +485,20 @@ impl<'a> State<'a> {
}
}

pub(crate) fn print_impl_restriction(&mut self, impl_restriction: &ast::ImplRestriction) {
match &impl_restriction.kind {
ast::RestrictionKind::Restricted { path, shorthand, .. } => {
let path = Self::to_string(|s| s.print_path(path, false, 0));
if *shorthand {
self.word_nbsp(format!("impl({path})"))
} else {
self.word_nbsp(format!("impl(in {path})"))
}
}
ast::RestrictionKind::Unrestricted => {}
}
}

fn print_defaultness(&mut self, defaultness: ast::Defaultness) {
if let ast::Defaultness::Default(_) = defaultness {
self.word_nbsp("default");
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,8 @@ declare_features! (
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
/// Target features on hexagon.
(unstable, hexagon_target_feature, "1.27.0", Some(150250)),
/// Allows `impl(crate) trait Foo` restrictions.
(incomplete, impl_restriction, "CURRENT_RUSTC_VERSION", Some(105077)),
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
/// Allows `impl Trait` in bindings (`let`).
Expand Down
28 changes: 28 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1268,6 +1268,26 @@ pub(crate) struct IncorrectVisibilityRestriction {
pub inner_str: String,
}

#[derive(Diagnostic)]
#[diag("incorrect `impl` restriction")]
#[help(
"some possible `impl` restrictions are:
`impl(crate)`: can only be implemented in the current crate
`impl(super)`: can only be implemented in the parent module
`impl(self)`: can only be implemented in current module
`impl(in path::to::module)`: can only be implemented in the specified path"
)]
pub(crate) struct IncorrectImplRestriction {
#[primary_span]
#[suggestion(
"help: use `in` to restrict implementations to the path `{$inner_str}`",
code = "in {inner_str}",
applicability = "machine-applicable"
)]
pub span: Span,
pub inner_str: String,
}

#[derive(Diagnostic)]
#[diag("<assignment> ... else {\"{\"} ... {\"}\"} is not allowed")]
pub(crate) struct AssignmentElseNotAllowed {
Expand Down Expand Up @@ -2403,6 +2423,14 @@ pub(crate) struct TraitAliasCannotBeUnsafe {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("trait aliases cannot be `impl`-restricted")]
pub(crate) struct TraitAliasCannotBeImplRestricted {
#[primary_span]
#[label("trait aliases cannot be `impl`-restricted")]
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("associated `static` items are not allowed")]
pub(crate) struct AssociatedStaticItemNotAllowed {
Expand Down
88 changes: 78 additions & 10 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,17 +1025,79 @@ impl<'a> Parser<'a> {
}
}

/// Is this an `(const unsafe? auto?| unsafe auto? | auto) trait` item?
/// Is there an `[ impl(in? path) ]? trait` item `dist` tokens ahead?
fn is_trait_with_maybe_impl_restriction_in_front(&self, dist: usize) -> bool {
// `trait`
if self.is_keyword_ahead(dist, &[kw::Trait]) {
return true;
}
// `impl(`
if !self.is_keyword_ahead(dist, &[kw::Impl])
|| !self.look_ahead(dist + 1, |t| t == &token::OpenParen)
{
return false;
}
// `crate | super | self) trait`
if self.is_keyword_ahead(dist + 2, &[kw::Crate, kw::Super, kw::SelfLower])
&& self.look_ahead(dist + 3, |t| t == &token::CloseParen)
&& self.is_keyword_ahead(dist + 4, &[kw::Trait])
{
return true;
}
// `impl(in? something) trait`
// We catch cases where the `in` keyword is missing to provide a
// better error message. This is handled later in
// `self.recover_incorrect_impl_restriction`.
self.tree_look_ahead(dist + 2, |t| {
if let TokenTree::Token(token, _) = t { token.is_keyword(kw::Trait) } else { false }
})
.unwrap_or(false)
}

/// Is this an `(const unsafe? auto? [ impl(in? path) ]? | unsafe auto? [ impl(in? path) ]? | auto [ impl(in? path) ]? | [ impl(in? path) ]?) trait` item?
fn check_trait_front_matter(&mut self) -> bool {
// auto trait
self.check_keyword(exp!(Auto)) && self.is_keyword_ahead(1, &[kw::Trait])
// unsafe auto trait
|| self.check_keyword(exp!(Unsafe)) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto])
|| self.check_keyword(exp!(Const)) && ((self.is_keyword_ahead(1, &[kw::Trait]) || self.is_keyword_ahead(1, &[kw::Auto]) && self.is_keyword_ahead(2, &[kw::Trait]))
|| self.is_keyword_ahead(1, &[kw::Unsafe]) && self.is_keyword_ahead(2, &[kw::Trait, kw::Auto]))
// `[ impl(in? path) ]? trait`
if self.is_trait_with_maybe_impl_restriction_in_front(0) {
return true;
}
// `auto [ impl(in? path) ]? trait`
if self.check_keyword(exp!(Auto)) && self.is_trait_with_maybe_impl_restriction_in_front(1) {
return true;
}
// `unsafe auto? [ impl(in? path) ]? trait`
if self.check_keyword(exp!(Unsafe))
&& (self.is_trait_with_maybe_impl_restriction_in_front(1)
|| self.is_keyword_ahead(1, &[kw::Auto])
&& self.is_trait_with_maybe_impl_restriction_in_front(2))
{
return true;
}
// `const` ...
if !self.check_keyword(exp!(Const)) {
return false;
}
// `const [ impl(in? path) ]? trait`
if self.is_trait_with_maybe_impl_restriction_in_front(1) {
return true;
}
// `const (unsafe | auto) [ impl(in? path) ]? trait`
if self.is_keyword_ahead(1, &[kw::Unsafe, kw::Auto])
&& self.is_trait_with_maybe_impl_restriction_in_front(2)
{
return true;
}
// `const unsafe auto [ impl(in? path) ]? trait`
self.is_keyword_ahead(1, &[kw::Unsafe])
&& self.is_keyword_ahead(2, &[kw::Auto])
&& self.is_trait_with_maybe_impl_restriction_in_front(3)
}

/// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
/// Parses `const? unsafe? auto? [impl(in? path)]? trait Foo { ... }` or `trait Foo = Bar;`.
///
/// FIXME(restrictions): The current keyword order follows the grammar specified in RFC 3323.
/// However, whether the restriction should be grouped closer to the visibility modifier
/// (e.g., `pub impl(crate) const unsafe auto trait`) remains an unresolved design question.
/// This ordering must be kept in sync with the logic in `check_trait_front_matter`.
fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> {
let constness = self.parse_constness(Case::Sensitive);
if let Const::Yes(span) = constness {
Expand All @@ -1050,6 +1112,8 @@ impl<'a> Parser<'a> {
IsAuto::No
};

let impl_restriction = self.parse_impl_restriction()?;

self.expect_keyword(exp!(Trait))?;
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
Expand Down Expand Up @@ -1078,6 +1142,9 @@ impl<'a> Parser<'a> {
if let Safety::Unsafe(_) = safety {
self.dcx().emit_err(errors::TraitAliasCannotBeUnsafe { span: whole_span });
}
if let RestrictionKind::Restricted { .. } = impl_restriction.kind {
self.dcx().emit_err(errors::TraitAliasCannotBeImplRestricted { span: whole_span });
}

self.psess.gated_spans.gate(sym::trait_alias, whole_span);

Expand All @@ -1090,6 +1157,7 @@ impl<'a> Parser<'a> {
constness,
is_auto,
safety,
impl_restriction,
ident,
generics,
bounds,
Expand Down Expand Up @@ -2843,8 +2911,8 @@ impl<'a> Parser<'a> {
&& !self.is_unsafe_foreign_mod()
// Rule out `async gen {` and `async gen move {`
&& !self.is_async_gen_block()
// Rule out `const unsafe auto` and `const unsafe trait`.
&& !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait])
// Rule out `const unsafe auto` and `const unsafe trait` and `const unsafe impl`.
&& !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait, kw::Impl])
)
})
// `extern ABI fn`
Expand Down
Loading
Loading