diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2ececee87510d..082b8aa25d021 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -32,7 +32,7 @@ use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; pub use crate::format::*; @@ -3316,6 +3316,42 @@ impl VisibilityKind { } } +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct Restriction { + pub kind: RestrictionKind, + pub span: Span, + pub tokens: Option, +} + +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum RestrictionKind { + Unrestricted, + Restricted { path: P, id: NodeId, shorthand: bool }, + Implied, +} + +impl Restriction { + pub fn unrestricted() -> Self { + Restriction { kind: RestrictionKind::Unrestricted, span: DUMMY_SP, tokens: None } + } + + pub fn restricted(path: P, id: NodeId, shorthand: bool) -> Self { + Restriction { + kind: RestrictionKind::Restricted { path, id, shorthand }, + span: DUMMY_SP, + tokens: None, + } + } + + pub fn implied() -> Self { + Restriction { kind: RestrictionKind::Implied, span: DUMMY_SP, tokens: None } + } + + pub fn with_span(self, span: Span) -> Self { + Restriction { span, ..self } + } +} + /// Field definition in a struct, variant or union. /// /// E.g., `bar: usize` as in `struct Foo { bar: usize }`. @@ -3494,6 +3530,7 @@ impl Default for FnHeader { #[derive(Clone, Encodable, Decodable, Debug)] pub struct Trait { + pub impl_restriction: Restriction, pub safety: Safety, pub is_auto: IsAuto, pub ident: Ident, diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 21de7ff7719b6..c14461ca0d970 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -9,8 +9,8 @@ use crate::ptr::P; 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, Item, NodeId, Param, Pat, PatField, Path, Restriction, + Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate, }; /// A trait for AST nodes having an ID. @@ -98,7 +98,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, + Restriction, + Ty, + Visibility +); impl_has_tokens_none!( Arm, ExprField, @@ -243,7 +255,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, Restriction, Ty, Visibility); impl HasAttrs for P { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 1700c701e8e05..613bd5b2f9eaf 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -327,6 +327,10 @@ pub trait MutVisitor: Sized { walk_vis(self, vis); } + fn visit_restriction(&mut self, restriction: &mut Restriction) { + walk_restriction(self, restriction); + } + fn visit_id(&mut self, _id: &mut NodeId) { // Do nothing. } @@ -1405,6 +1409,18 @@ fn walk_vis(vis: &mut T, visibility: &mut Visibility) { vis.visit_span(span); } +fn walk_restriction(vis: &mut T, restriction: &mut Restriction) { + let Restriction { kind, span, tokens: _ } = restriction; + match kind { + RestrictionKind::Unrestricted | RestrictionKind::Implied => {} + RestrictionKind::Restricted { path, id, shorthand: _ } => { + vis.visit_id(id); + vis.visit_path(path); + } + } + vis.visit_span(span); +} + fn walk_capture_by(vis: &mut T, capture_by: &mut CaptureBy) { match capture_by { CaptureBy::Ref => {} diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index e1c2dd053242c..b28b4672f8e03 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -268,6 +268,9 @@ pub trait Visitor<'ast>: Sized { fn visit_vis(&mut self, vis: &'ast Visibility) -> Self::Result { walk_vis(self, vis) } + fn visit_restriction(&mut self, restriction: &'ast Restriction) -> Self::Result { + walk_restriction(self, restriction) + } fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) -> Self::Result { walk_fn_ret_ty(self, ret_ty) } @@ -554,7 +557,16 @@ macro_rules! common_visitor_and_walkers { >::Result::output() )? } - ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => { + ItemKind::Trait(box Trait { + impl_restriction, + safety, + is_auto: _, + ident, + generics, + bounds, + items, + }) => { + try_visit!(vis.visit_restriction(impl_restriction)); try_visit!(visit_safety(vis, safety)); try_visit!(vis.visit_ident(ident)); try_visit!(vis.visit_generics(generics)); @@ -1516,6 +1528,20 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) -> V:: V::Result::output() } +pub fn walk_restriction<'a, V: Visitor<'a>>( + visitor: &mut V, + restriction: &'a Restriction, +) -> V::Result { + let Restriction { kind, span: _, tokens: _ } = restriction; + match kind { + RestrictionKind::Unrestricted | RestrictionKind::Implied => {} + RestrictionKind::Restricted { path, id, shorthand: _ } => { + try_visit!(visitor.visit_path(path, *id)); + } + } + V::Result::output() +} + pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) -> V::Result { let Attribute { kind, id: _, style: _, span: _ } = attr; match kind { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 7f7d45790ee26..f8fe1e9432ced 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -415,7 +415,15 @@ impl<'hir> LoweringContext<'_, 'hir> { items: new_impl_items, })) } - ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => { + ItemKind::Trait(box Trait { + impl_restriction: _, + is_auto, + safety, + ident, + generics, + bounds, + items, + }) => { let ident = self.lower_ident(*ident); let (generics, (safety, items, bounds)) = self.lower_generics( generics, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 915613a391374..65773e4d2229b 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -515,6 +515,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(where_clause_attrs, "attributes in `where` clause are unstable"); gate_all!(super_let, "`super let` is experimental"); gate_all!(frontmatter, "frontmatters are experimental"); + gate_all!(impl_restriction, "impl restrictions are 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 0990c9b27eb09..28ebc3ca1facc 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1071,6 +1071,10 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere Self::to_string(|s| s.print_visibility(v)) } + fn restriction_to_string(&self, kw: &'static str, r: &ast::Restriction) -> String { + Self::to_string(|s| s.print_restriction(kw, r)) + } + fn block_to_string(&self, blk: &ast::Block) -> String { Self::to_string(|s| { let (cb, ib) = s.head(""); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 3638eb31c6186..e092b0b4946ec 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -357,6 +357,7 @@ impl<'a> State<'a> { self.bclose(item.span, empty, cb); } ast::ItemKind::Trait(box ast::Trait { + impl_restriction, safety, is_auto, ident, @@ -366,6 +367,7 @@ impl<'a> State<'a> { }) => { let (cb, ib) = self.head(""); self.print_visibility(&item.vis); + self.print_restriction("impl", impl_restriction); self.print_safety(*safety); self.print_is_auto(*is_auto); self.word_nbsp("trait"); @@ -473,6 +475,22 @@ impl<'a> State<'a> { } } + // FIXME(jhpratt) make `kw` into a const generic once permitted + pub(crate) fn print_restriction(&mut self, kw: &'static str, restriction: &ast::Restriction) { + match restriction.kind { + ast::RestrictionKind::Unrestricted => self.word_nbsp(kw), + ast::RestrictionKind::Restricted { ref path, shorthand, id: _ } => { + let path = Self::to_string(|s| s.print_path(path, false, 0)); + if shorthand { + self.word_nbsp(format!("{kw}({path})")) + } else { + self.word_nbsp(format!("{kw}(in {path})")) + } + } + ast::RestrictionKind::Implied => {} + } + } + fn print_defaultness(&mut self, defaultness: ast::Defaultness) { if let ast::Defaultness::Default(_) = defaultness { self.word_nbsp("default"); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index b46eac6d8a602..2a4d3c74736a4 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -534,6 +534,8 @@ declare_features! ( (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. (unstable, if_let_guard, "1.47.0", Some(51114)), + /// Allows `impl(crate) trait Foo` restrictions + (unstable, 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`). diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 9c8f1c9eccf30..3f3eaaaf73fa6 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -1179,7 +1179,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { } tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher); tcx.stable_crate_id(LOCAL_CRATE).hash_stable(&mut hcx, &mut stable_hasher); - // Hash visibility information since it does not appear in HIR. + // Hash visibility and restriction information since it does not appear in HIR. // FIXME: Figure out how to remove `visibilities_for_hashing` by hashing visibilities on // the fly in the resolver, storing only their accumulated hash in `ResolverGlobalCtxt`, // and combining it with other hashes here. @@ -1187,6 +1187,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { with_metavar_spans(|mspans| { mspans.freeze_and_get_read_spans().hash_stable(&mut hcx, &mut stable_hasher); }); + resolutions.impl_restrictions.hash_stable(&mut hcx, &mut stable_hasher); stable_hasher.finish() }); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 78c0812b08f80..7d7d3fe1cac02 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -178,6 +178,7 @@ pub struct ResolverOutputs { #[derive(Debug)] pub struct ResolverGlobalCtxt { pub visibilities_for_hashing: Vec<(LocalDefId, Visibility)>, + pub impl_restrictions: FxIndexMap, /// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`. pub expn_that_defined: FxHashMap, pub effective_visibilities: EffectiveVisibilities, @@ -322,6 +323,35 @@ impl Visibility { } } +#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Encodable, Decodable, HashStable)] +pub enum Restriction { + /// The restriction does not affect the item. + Unrestricted, + /// The restriction only applies outside of this path. + Restricted(DefId, Span), +} + +impl Restriction { + /// Returns `true` if the behavior is allowed/unrestricted in the given module. A value of + /// `false` indicates that the behavior is prohibited. + pub fn is_allowed_in(self, module: DefId, tcx: TyCtxt<'_>) -> bool { + let restricted_to = match self { + Restriction::Unrestricted => return true, + Restriction::Restricted(module, _) => module, + }; + + tcx.is_descendant_of(module, restricted_to.into()) + } + + /// Obtain the [`Span`] of the restriction. If unrestricted, an empty span is returned. + pub fn span(&self) -> Span { + match self { + Restriction::Unrestricted => DUMMY_SP, + Restriction::Restricted(_, span) => *span, + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)] #[derive(TypeFoldable, TypeVisitable)] pub struct ClosureSizeProfileData<'tcx> { diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 1f221b4bf78c2..70e2e31b61521 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -377,6 +377,13 @@ parse_inclusive_range_no_end = inclusive range with no end parse_incorrect_parens_trait_bounds = incorrect parentheses around trait bounds parse_incorrect_parens_trait_bounds_sugg = fix the parentheses +parse_incorrect_restriction = incorrect {parse_restriction_noun} restriction + .help = some possible {parse_restriction_noun} restrictions are: + `{$keyword}(crate)`: {parse_restriction_adjective} only in the current crate + `{$keyword}(super)`: {parse_restriction_adjective} only in the current module's parent + `{$keyword}(in path::to::module)`: {parse_restriction_adjective} only in the specified path + .suggestion = make this {parse_restriction_adjective} only to module `{$path}` with `in` + parse_incorrect_semicolon = expected item, found `;` .suggestion = remove this semicolon @@ -783,6 +790,18 @@ parse_reserved_string = invalid string literal .note = unprefixed guarded string literals are reserved for future use since Rust 2024 .suggestion_whitespace = consider inserting whitespace here +# internal use only +parse_restriction_adjective = { $keyword -> + [impl] implementable + *[DEFAULT_IS_BUG] BUG +} + +# internal use only +parse_restriction_noun = { $keyword -> + [impl] impl + *[DEFAULT_IS_BUG] BUG +} + parse_return_types_use_thin_arrow = return types are denoted using `->` .suggestion = use `->` instead @@ -853,6 +872,7 @@ parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto` parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe` +parse_trait_alias_cannot_have_impl_restriction = trait alias cannot have `impl` restriction parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before .suggestion = move `{$kw}` before the `for<...>` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2dba568a258a2..9f213fa4ecc56 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1041,6 +1041,17 @@ pub(crate) struct IncorrectVisibilityRestriction { pub inner_str: String, } +#[derive(Diagnostic)] +#[diag(parse_incorrect_restriction, code = E0704)] +#[help] +pub(crate) struct IncorrectRestriction<'kw> { + #[primary_span] + #[suggestion(code = "in {path}", applicability = "machine-applicable", style = "verbose")] + pub span: Span, + pub path: String, + pub keyword: &'kw str, +} + #[derive(Diagnostic)] #[diag(parse_assignment_else_not_allowed)] pub(crate) struct AssignmentElseNotAllowed { @@ -1946,6 +1957,16 @@ pub(crate) struct BoundsNotAllowedOnTraitAliases { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_trait_alias_cannot_have_impl_restriction)] +pub(crate) struct TraitAliasCannotHaveImplRestriction { + #[primary_span] + #[label(parse_trait_alias_cannot_have_impl_restriction)] + pub span: Span, + #[suggestion(code = "", applicability = "machine-applicable", style = "short")] + pub restriction: Span, +} + #[derive(Diagnostic)] #[diag(parse_trait_alias_cannot_be_auto)] pub(crate) struct TraitAliasCannotBeAuto { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index c7b0eb11e5a03..94bb788479b3a 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -262,7 +262,7 @@ impl<'a> Parser<'a> { define_opaque: None, })) } - } else if self.check_keyword(exp!(Trait)) || self.check_auto_or_unsafe_trait_item() { + } else if self.check_keyword(exp!(Trait)) || self.check_trait_item_with_modifiers() { // TRAIT ITEM self.parse_item_trait(attrs, lo)? } else if self.check_keyword(exp!(Impl)) @@ -373,7 +373,7 @@ impl<'a> Parser<'a> { pub(super) fn is_path_start_item(&mut self) -> bool { self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` || self.is_reuse_path_item() - || self.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }` + || self.check_trait_item_with_modifiers() // no: `auto::b`, yes: `auto trait X { .. }` || self.is_async_fn() // no(2015): `async::b`, yes: `async fn` || matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac` } @@ -872,16 +872,67 @@ impl<'a> Parser<'a> { } } - /// Is this an `(unsafe auto? | auto) trait` item? - fn check_auto_or_unsafe_trait_item(&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]) + /// Is this an `[ impl(in path) ]? unsafe? auto? trait` item? + fn check_trait_item_with_modifiers(&mut self) -> bool { + let current_token = TokenTree::Token(self.token.clone(), self.token_spacing); + let look_ahead = |index| match index { + 0 => Some(¤t_token), + _ => self.token_cursor.curr.look_ahead(index - 1), + }; + + let mut has_impl_restriction = false; + let mut has_unsafe = false; + let mut has_auto = false; + let mut has_trait = false; + + let mut index = 0; + + if let Some(TokenTree::Token(token, _)) = look_ahead(index) + && token.is_keyword(kw::Impl) + { + has_impl_restriction = true; + index += 1; + } + // impl restrictions require parens, but we enforce this later. + if has_impl_restriction + && matches!( + look_ahead(index), + Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, _)) + ) + { + index += 1; + } + if let Some(TokenTree::Token(token, _)) = look_ahead(index) + && token.is_keyword(kw::Unsafe) + { + has_unsafe = true; + index += 1; + } + if let Some(TokenTree::Token(token, _)) = look_ahead(index) + && token.is_keyword(kw::Auto) + { + has_auto = true; + index += 1; + } + if let Some(TokenTree::Token(token, _)) = look_ahead(index) + && token.is_keyword(kw::Trait) + { + has_trait = true; + } + + let ret = (has_impl_restriction || has_unsafe || has_auto) && has_trait; + if !ret { + self.expected_token_types.insert(exp!(Impl).token_type); + self.expected_token_types.insert(exp!(Unsafe).token_type); + self.expected_token_types.insert(exp!(Auto).token_type); + } + ret } - /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. + /// Parses `[ impl(in path) ]? unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> { + let impl_restriction = + self.parse_restriction(exp!(Impl), Some(sym::impl_restriction), FollowedByType::No)?; let safety = self.parse_safety(Case::Sensitive); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(exp!(Auto)) { @@ -913,6 +964,12 @@ impl<'a> Parser<'a> { self.expect_semi()?; let whole_span = lo.to(self.prev_token.span); + if !matches!(impl_restriction.kind, RestrictionKind::Implied) { + self.dcx().emit_err(errors::TraitAliasCannotHaveImplRestriction { + span: whole_span, + restriction: impl_restriction.span, + }); + } if is_auto == IsAuto::Yes { self.dcx().emit_err(errors::TraitAliasCannotBeAuto { span: whole_span }); } @@ -927,7 +984,15 @@ impl<'a> Parser<'a> { // It's a normal trait. generics.where_clause = self.parse_where_clause()?; let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; - Ok(ItemKind::Trait(Box::new(Trait { is_auto, safety, ident, generics, bounds, items }))) + Ok(ItemKind::Trait(Box::new(Trait { + impl_restriction, + is_auto, + safety, + ident, + generics, + bounds, + items, + }))) } } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index b2e902513672d..7db066fb8f2c0 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -31,8 +31,8 @@ use rustc_ast::tokenstream::{ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, - DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, - Visibility, VisibilityKind, + DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Restriction, + Safety, StrLit, Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -45,7 +45,9 @@ use token_type::TokenTypeSet; pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType}; use tracing::debug; -use crate::errors::{self, IncorrectVisibilityRestriction, NonStringAbiLiteral}; +use crate::errors::{ + self, IncorrectRestriction, IncorrectVisibilityRestriction, NonStringAbiLiteral, +}; use crate::exp; #[cfg(test)] @@ -1514,6 +1516,75 @@ impl<'a> Parser<'a> { Ok(()) } + /// Parses `kw`, `kw(in path)` plus shortcuts `kw(crate)` for `kw(in crate)`, `kw(self)` for + /// `kw(in self)` and `kw(super)` for `kw(in super)`. + fn parse_restriction( + &mut self, + kw: ExpKeywordPair, + feature_gate: Option, + fbt: FollowedByType, + ) -> PResult<'a, Restriction> { + if !self.eat_keyword(kw) { + // We need a span, but there's inherently no keyword to grab a span from for an implied + // restriction. An empty span at the beginning of the current token is a reasonable + // fallback. + return Ok(Restriction::implied().with_span(self.token.span.shrink_to_lo())); + } + + let gate = |span| { + if let Some(feature_gate) = feature_gate { + self.psess.gated_spans.gate(feature_gate, span); + } + span + }; + + let lo = self.prev_token.span; + + if self.check(exp!(OpenParen)) { + // We don't `self.bump()` the `(` yet because this might be a struct definition where + // `()` or a tuple might be allowed. For example, `struct Struct(kw (), kw (usize));`. + // Because of this, we only `bump` the `(` if we're assured it is appropriate to do so + // by the following tokens. + if self.is_keyword_ahead(1, &[kw::In]) { + // Parse `kw(in path)`. + self.bump(); // `(` + self.bump(); // `in` + let path = self.parse_path(PathStyle::Mod)?; // `path` + self.expect(exp!(CloseParen))?; // `)` + return Ok(Restriction::restricted(P(path), ast::DUMMY_NODE_ID, false) + .with_span(gate(lo.to(self.prev_token.span)))); + } else if self.look_ahead(2, |t| t == &TokenKind::CloseParen) + && self.is_keyword_ahead(1, &[kw::Crate, kw::Super, kw::SelfLower]) + { + // Parse `kw(crate)`, `kw(self)`, or `kw(super)`. + self.bump(); // `(` + let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` + self.expect(exp!(CloseParen))?; // `)` + return Ok(Restriction::restricted(P(path), ast::DUMMY_NODE_ID, true) + .with_span(gate(lo.to(self.prev_token.span)))); + } else if let FollowedByType::No = fbt { + // Provide this diagnostic if a type cannot follow; + // in particular, if this is not a tuple struct. + self.recover_incorrect_restriction(kw.kw.as_str())?; + // Emit diagnostic, but continue unrestricted. + } + } + + Ok(Restriction::unrestricted().with_span(gate(lo))) + } + + /// Recovery for e.g. `kw(something) fn ...` or `struct X { kw(something) y: Z }` + fn recover_incorrect_restriction<'kw>(&mut self, kw: &'kw str) -> PResult<'a, ()> { + self.bump(); // `(` + let path = self.parse_path(PathStyle::Mod)?; + self.expect(exp!(CloseParen))?; // `)` + + let path_str = pprust::path_to_string(&path); + self.dcx().emit_err(IncorrectRestriction { span: path.span, path: path_str, keyword: kw }); + + Ok(()) + } + /// Parses `extern string_literal?`. fn parse_extern(&mut self, case: Case) -> Extern { if self.eat_keyword_case(exp!(Extern), case) { diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 38cdfa72a14dd..1f1f95d8184a9 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -353,6 +353,16 @@ resolve_remove_surrounding_derive = resolve_remove_unnecessary_import = remove unnecessary import +resolve_restriction_ancestor_only = restrictions can only be restricted to ancestor modules + +resolve_restriction_indeterminate = cannot determine resolution for the restriction + +resolve_restriction_module_only = restriction must resolve to a module + +resolve_restriction_relative_2018 = + relative paths are not supported in restrictions in 2018 edition or later + .suggestion = try + resolve_self_import_can_only_appear_once_in_the_list = `self` import can only appear once in an import list .label = can only appear once in an import list diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c30ed781f35f3..bdfd4eac4f637 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -33,8 +33,8 @@ use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use crate::{ BindingKey, Determinacy, ExternPreludeEntry, Finalize, MacroData, Module, ModuleKind, ModuleOrUniformRoot, NameBinding, NameBindingData, NameBindingKind, ParentScope, PathResult, - ResolutionError, Resolver, ResolverArenas, Segment, ToNameBinding, Used, VisResolutionError, - errors, + ResolutionError, Resolver, ResolverArenas, RestrictionResolutionError, Segment, ToNameBinding, + Used, VisResolutionError, errors, }; type Res = def::Res; @@ -322,9 +322,9 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { }) } ast::VisibilityKind::Restricted { ref path, id, .. } => { - // For visibilities we are not ready to provide correct implementation of "uniform + // For restrictions we are not ready to provide correct implementation of "uniform // paths" right now, so on 2018 edition we only allow module-relative paths for now. - // On 2015 edition visibilities are resolved as crate-relative by default, + // On 2015 edition restrictions are resolved as crate-relative by default, // so we are prepending a root segment if necessary. let ident = path.segments.get(0).expect("empty path in visibility").ident; let crate_root = if ident.is_path_segment_keyword() { @@ -406,6 +406,97 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.field_names.insert(def_id, fields); } + fn resolve_restriction(&mut self, restriction: &ast::Restriction) -> ty::Restriction { + self.try_resolve_restriction(restriction, true).unwrap_or_else(|err| { + self.r.report_restriction_error(err); + ty::Restriction::Unrestricted + }) + } + + fn try_resolve_restriction<'ast>( + &mut self, + restriction: &'ast ast::Restriction, + finalize: bool, + ) -> Result> { + let parent_scope = &self.parent_scope; + match restriction.kind { + // If the restriction is implied, it has no effect when the item is otherwise visible. + ast::RestrictionKind::Unrestricted | ast::RestrictionKind::Implied => { + Ok(ty::Restriction::Unrestricted) + } + ast::RestrictionKind::Restricted { ref path, id, shorthand: _ } => { + // For restrictions we are not ready to provide correct implementation of "uniform + // paths" right now, so on 2018 edition we only allow module-relative paths for now. + // On 2015 edition visibilities are resolved as crate-relative by default, + // so we are prepending a root segment if necessary. + let ident = path.segments.get(0).expect("empty path in restriction").ident; + let crate_root = if ident.is_path_segment_keyword() { + None + } else if ident.span.is_rust_2015() { + Some(Segment::from_ident(Ident::new( + kw::PathRoot, + path.span.shrink_to_lo().with_ctxt(ident.span.ctxt()), + ))) + } else { + return Err(RestrictionResolutionError::Relative2018(ident.span, path)); + }; + + let segments = crate_root + .into_iter() + .chain(path.segments.iter().map(|seg| seg.into())) + .collect::>(); + let expected_found_error = |res| { + Err(RestrictionResolutionError::ExpectedFound( + path.span, + Segment::names_to_string(&segments), + res, + )) + }; + match self.r.resolve_path( + &segments, + Some(TypeNS), + parent_scope, + finalize.then(|| Finalize::new(id, path.span)), + None, + None, + ) { + PathResult::Module(ModuleOrUniformRoot::Module(module)) => { + let res = module.res().expect("restriction resolved to unnamed block"); + if finalize { + self.r.record_partial_res(id, PartialRes::new(res)); + } + if module.is_normal() { + if res == Res::Err { + Ok(ty::Restriction::Unrestricted) + } else { + let vis = ty::Visibility::Restricted(res.def_id()); + if self.r.is_accessible_from(vis, parent_scope.module) { + Ok(ty::Restriction::Restricted(res.def_id(), restriction.span)) + } else { + Err(RestrictionResolutionError::AncestorOnly(path.span)) + } + } + } else { + expected_found_error(res) + } + } + PathResult::Module(..) => { + Err(RestrictionResolutionError::ModuleOnly(path.span)) + } + PathResult::NonModule(partial_res) => { + expected_found_error(partial_res.base_res()) + } + PathResult::Failed { span, label, suggestion, .. } => { + Err(RestrictionResolutionError::FailedToResolve(span, label, suggestion)) + } + PathResult::Indeterminate => { + Err(RestrictionResolutionError::Indeterminate(path.span)) + } + } + } + } + } + fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) { let field_vis = fields .iter() @@ -809,8 +900,21 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { ItemKind::TyAlias(box TyAlias { ident, .. }) | ItemKind::TraitAlias(ident, ..) => { self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); } + ItemKind::Trait(box ast::Trait { ident, ref impl_restriction, .. }) => { + let impl_restriction = self.resolve_restriction(impl_restriction); + self.r.impl_restrictions.insert(local_def_id, impl_restriction); - ItemKind::Enum(ident, _, _) | ItemKind::Trait(box ast::Trait { ident, .. }) => { + let module = self.r.new_module( + Some(parent), + ModuleKind::Def(def_kind, def_id, Some(ident.name)), + expansion.to_expn_id(), + item.span, + parent.no_implicit_prelude, + ); + self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion)); + self.parent_scope.module = module; + } + ItemKind::Enum(ident, _, _) => { let module = self.r.new_module( Some(parent), ModuleKind::Def(def_kind, def_id, Some(ident.name)), diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 201b1c0a493bc..a093de213327d 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -46,8 +46,8 @@ use crate::{ AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, BindingKey, Finalize, ForwardGenericParamBanReason, HasGenericParams, LexicalScopeBinding, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, NameBinding, NameBindingKind, ParentScope, PathResult, - PrivacyError, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, - VisResolutionError, errors as errs, path_names_to_string, + PrivacyError, ResolutionError, Resolver, RestrictionResolutionError, Scope, ScopeSet, Segment, + UseError, Used, VisResolutionError, errors as errs, path_names_to_string, }; type Res = def::Res; @@ -1018,6 +1018,46 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .emit() } + pub(crate) fn report_restriction_error( + &mut self, + resolution_error: RestrictionResolutionError<'_>, + ) -> ErrorGuaranteed { + match resolution_error { + RestrictionResolutionError::Relative2018(span, path) => { + self.dcx().create_err(errs::RestrictionRelative2018 { + span, + path_span: path.span, + // intentionally converting to String, as the text would also be used as + // in suggestion context + path_str: pprust::path_to_string(&path), + }) + } + RestrictionResolutionError::AncestorOnly(span) => { + self.dcx().create_err(errs::RestrictionAncestorOnly(span)) + } + RestrictionResolutionError::FailedToResolve(span, label, suggestion) => self + .into_struct_error( + span, + ResolutionError::FailedToResolve { + segment: None, + label, + suggestion, + module: None, + }, + ), + RestrictionResolutionError::ExpectedFound(span, path_str, res) => { + self.dcx().create_err(errs::ExpectedModuleFound { span, res, path_str }) + } + RestrictionResolutionError::Indeterminate(span) => { + self.dcx().create_err(errs::RestrictionIndeterminate(span)) + } + RestrictionResolutionError::ModuleOnly(span) => { + self.dcx().create_err(errs::RestrictionModuleOnly(span)) + } + } + .emit() + } + /// Lookup typo candidate in scope for a macro or import. fn early_lookup_typo_candidate( &mut self, diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 7fe74378b6748..290d0be358626 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1244,3 +1244,25 @@ pub(crate) struct TraitImplMismatch { #[label(resolve_trait_impl_mismatch_label_item)] pub(crate) trait_item_span: Span, } + +#[derive(Diagnostic)] +#[diag(resolve_restriction_relative_2018)] +pub(crate) struct RestrictionRelative2018 { + #[primary_span] + pub(crate) span: Span, + #[suggestion(code = "crate::{path_str}", applicability = "maybe-incorrect")] + pub(crate) path_span: Span, + pub(crate) path_str: String, +} + +#[derive(Diagnostic)] +#[diag(resolve_restriction_ancestor_only, code = E0742)] +pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_restriction_indeterminate, code = E0578)] +pub(crate) struct RestrictionIndeterminate(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag(resolve_restriction_module_only)] +pub(crate) struct RestrictionModuleOnly(#[primary_span] pub(crate) Span); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 9ba70abd4d933..432553f98fbfd 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -321,6 +321,15 @@ enum VisResolutionError<'a> { ModuleOnly(Span), } +enum RestrictionResolutionError<'a> { + Relative2018(Span, &'a ast::Path), + AncestorOnly(Span), + FailedToResolve(Span, String, Option), + ExpectedFound(Span, String, Res), + Indeterminate(Span), + ModuleOnly(Span), +} + /// A minimal representation of a path segment. We use this in resolve because we synthesize 'path /// segments' which don't have the rest of an AST or HIR `PathSegment`. #[derive(Clone, Copy, Debug)] @@ -1101,6 +1110,7 @@ pub struct Resolver<'ra, 'tcx> { glob_map: FxIndexMap>, glob_error: Option, visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>, + impl_restrictions: FxIndexMap, used_imports: FxHashSet, maybe_unused_trait_imports: FxIndexSet, @@ -1492,6 +1502,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { glob_map: Default::default(), glob_error: None, visibilities_for_hashing: Default::default(), + impl_restrictions: Default::default(), used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), @@ -1661,6 +1672,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let global_ctxt = ResolverGlobalCtxt { expn_that_defined, visibilities_for_hashing: self.visibilities_for_hashing, + impl_restrictions: self.impl_restrictions, effective_visibilities, extern_crate_map, module_children: self.module_children, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 29c3d58f93583..d26d8ca3e2a92 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1154,6 +1154,7 @@ symbols! { ignore, impl_header_lifetime_elision, impl_lint_pass, + impl_restriction, impl_trait_in_assoc_type, impl_trait_in_bindings, impl_trait_in_fn_trait_return, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index a0503a699e691..6a8522c9365f7 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -444,6 +444,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { }, ( Trait(box ast::Trait { + impl_restriction: _, // does not affect item kind is_auto: la, safety: lu, ident: li, @@ -452,6 +453,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { items: lis, }), Trait(box ast::Trait { + impl_restriction: _, // does not affect item kind is_auto: ra, safety: ru, ident: ri, diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 1a3897b51cb8f..266fae8e7d57f 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1172,6 +1172,7 @@ pub(crate) fn format_trait( unreachable!(); }; let ast::Trait { + ref impl_restriction, is_auto, safety, ident, @@ -1182,8 +1183,9 @@ pub(crate) fn format_trait( let mut result = String::with_capacity(128); let header = format!( - "{}{}{}trait ", + "{}{}{}{}trait ", format_visibility(context, &item.vis), + format_restriction("impl", context, impl_restriction), format_safety(safety), format_auto(is_auto), ); diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index fcd475b1784f5..cd26b5dfce7f1 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -4,7 +4,7 @@ use rustc_ast::ast::{ self, Attribute, MetaItem, MetaItemInner, MetaItemKind, NodeId, Path, Visibility, VisibilityKind, }; -use rustc_ast::{YieldKind, ptr}; +use rustc_ast::{Restriction, RestrictionKind, YieldKind, ptr}; use rustc_ast_pretty::pprust; use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol}; use unicode_width::UnicodeWidthStr; @@ -74,6 +74,35 @@ pub(crate) fn format_visibility( } } +// Does not allocate in the common (implied) case. +pub(crate) fn format_restriction( + kw: &'static str, + context: &RewriteContext<'_>, + restriction: &Restriction, +) -> String { + match restriction.kind { + RestrictionKind::Unrestricted => format!("{kw} "), + RestrictionKind::Restricted { + ref path, + id: _, + shorthand, + } => { + let Path { ref segments, .. } = **path; + let mut segments_iter = segments.iter().map(|seg| rewrite_ident(context, seg.ident)); + if path.is_global() && segments_iter.next().is_none() { + panic!("non-global path in {kw}(restricted)?"); + } + // FIXME use `segments_iter.intersperse("::").collect::()` once + // `#![feature(iter_intersperse)]` is re-stabilized. + let path = itertools::join(segments_iter, "::"); + let in_str = if shorthand { "" } else { "in " }; + + format!("{kw}({in_str}{path}) ") + } + RestrictionKind::Implied => String::new(), + } +} + #[inline] pub(crate) fn format_coro(coroutine_kind: &ast::CoroutineKind) -> &'static str { match coroutine_kind { diff --git a/src/tools/rustfmt/tests/source/restriction.rs b/src/tools/rustfmt/tests/source/restriction.rs new file mode 100644 index 0000000000000..d0ed1d13bbe4c --- /dev/null +++ b/src/tools/rustfmt/tests/source/restriction.rs @@ -0,0 +1,16 @@ +pub +impl(crate) +trait Foo {} + +pub impl +( in +crate ) +trait +Bar +{} + +pub +impl ( in foo +:: +bar ) +trait Baz {} diff --git a/src/tools/rustfmt/tests/target/restriction.rs b/src/tools/rustfmt/tests/target/restriction.rs new file mode 100644 index 0000000000000..6912386b5094a --- /dev/null +++ b/src/tools/rustfmt/tests/target/restriction.rs @@ -0,0 +1,5 @@ +pub impl(crate) trait Foo {} + +pub impl(in crate) trait Bar {} + +pub impl(in foo::bar) trait Baz {} diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 3f3d9252adbe8..eadf95df41ae3 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -793,6 +793,16 @@ fn test_vis() { // Attributes are not allowed on visibilities. } +#[test] +fn test_impl_restriction() { + assert_eq!(stringify!(pub impl(crate) trait Foo {}), "pub impl(crate) trait Foo {}"); + assert_eq!(stringify!(pub impl(in crate) trait Foo {}), "pub impl(in crate) trait Foo {}"); + assert_eq!(stringify!(pub impl(super) trait Foo {}), "pub impl(super) trait Foo {}"); + assert_eq!(stringify!(pub impl(in super) trait Foo {}), "pub impl(in super) trait Foo {}"); + assert_eq!(stringify!(pub impl(self) trait Foo {}), "pub impl(self) trait Foo {}"); + assert_eq!(stringify!(pub impl(in self) trait Foo {}), "pub impl(in self) trait Foo {}"); +} + macro_rules! p { ([$($tt:tt)*], $s:literal) => { assert_eq!(stringify!($($tt)*), $s); diff --git a/tests/ui/restrictions/feature-gate-impl-restriction.rs b/tests/ui/restrictions/feature-gate-impl-restriction.rs new file mode 100644 index 0000000000000..b8f2c7bcee3ef --- /dev/null +++ b/tests/ui/restrictions/feature-gate-impl-restriction.rs @@ -0,0 +1,11 @@ +//@ compile-flags: --crate-type=lib +//@ revisions: with_gate without_gate +//@[with_gate] check-pass + +#![cfg_attr(with_gate, feature(impl_restriction))] + +pub impl(crate) trait Bar {} //[without_gate]~ ERROR + +mod foo { + pub impl(in crate::foo) trait Baz {} //[without_gate]~ ERROR +} diff --git a/tests/ui/restrictions/feature-gate-impl-restriction.without_gate.stderr b/tests/ui/restrictions/feature-gate-impl-restriction.without_gate.stderr new file mode 100644 index 0000000000000..2279be3aae862 --- /dev/null +++ b/tests/ui/restrictions/feature-gate-impl-restriction.without_gate.stderr @@ -0,0 +1,23 @@ +error[E0658]: impl restrictions are experimental + --> $DIR/feature-gate-impl-restriction.rs:7:5 + | +LL | pub impl(crate) trait Bar {} + | ^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(impl_restriction)]` 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]: impl restrictions are experimental + --> $DIR/feature-gate-impl-restriction.rs:10:9 + | +LL | pub impl(in crate::foo) trait Baz {} + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(impl_restriction)]` 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: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`.