Skip to content

Commit 9a8c8a3

Browse files
committed
Auto merge of #141754 - jhpratt:restrictions-pr1, r=<try>
Introduce `impl` restrictions to AST, lower to `rustc_middle` RFC approved in [RFC 3323](https://rust-lang.github.io/rfcs/3323-restrictions.html); part of tracking issue #105077 and a [2025H1 project goal](rust-lang/rust-project-goals#257). This is likely easiest to review commit-by-commit. First, the `impl` restriction is introduced to the AST, followed by actually parsing it into the AST on trait definitions (the only place it's permitted). Next we add the ability for `rustfmt` to handle it in the most logical manner; unstable features are permitted to choose a sane format without a team decision. After placing it behind a feature gate, the restriction is lowered to `rustc_middle` with identical limitations to visibility. Finally, the diagnostics are partially moved into Fluent to make them a bit more translatable. This is the groundwork for the restriction and does not actually enforce anything; that is coming in another PR soon. r? `@Urgau` as discussed
2 parents 1c0849d + 12ce9a9 commit 9a8c8a3

File tree

29 files changed

+649
-30
lines changed

29 files changed

+649
-30
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use rustc_data_structures::tagged_ptr::Tag;
3232
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
3333
pub use rustc_span::AttrId;
3434
use rustc_span::source_map::{Spanned, respan};
35-
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
35+
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
3636
use thin_vec::{ThinVec, thin_vec};
3737

3838
pub use crate::format::*;
@@ -3316,6 +3316,42 @@ impl VisibilityKind {
33163316
}
33173317
}
33183318

3319+
#[derive(Clone, Encodable, Decodable, Debug)]
3320+
pub struct Restriction {
3321+
pub kind: RestrictionKind,
3322+
pub span: Span,
3323+
pub tokens: Option<LazyAttrTokenStream>,
3324+
}
3325+
3326+
#[derive(Clone, Encodable, Decodable, Debug)]
3327+
pub enum RestrictionKind {
3328+
Unrestricted,
3329+
Restricted { path: P<Path>, id: NodeId, shorthand: bool },
3330+
Implied,
3331+
}
3332+
3333+
impl Restriction {
3334+
pub fn unrestricted() -> Self {
3335+
Restriction { kind: RestrictionKind::Unrestricted, span: DUMMY_SP, tokens: None }
3336+
}
3337+
3338+
pub fn restricted(path: P<Path>, id: NodeId, shorthand: bool) -> Self {
3339+
Restriction {
3340+
kind: RestrictionKind::Restricted { path, id, shorthand },
3341+
span: DUMMY_SP,
3342+
tokens: None,
3343+
}
3344+
}
3345+
3346+
pub fn implied() -> Self {
3347+
Restriction { kind: RestrictionKind::Implied, span: DUMMY_SP, tokens: None }
3348+
}
3349+
3350+
pub fn with_span(self, span: Span) -> Self {
3351+
Restriction { span, ..self }
3352+
}
3353+
}
3354+
33193355
/// Field definition in a struct, variant or union.
33203356
///
33213357
/// E.g., `bar: usize` as in `struct Foo { bar: usize }`.
@@ -3494,6 +3530,7 @@ impl Default for FnHeader {
34943530

34953531
#[derive(Clone, Encodable, Decodable, Debug)]
34963532
pub struct Trait {
3533+
pub impl_restriction: Restriction,
34973534
pub safety: Safety,
34983535
pub is_auto: IsAuto,
34993536
pub ident: Ident,

compiler/rustc_ast/src/ast_traits.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use crate::ptr::P;
99
use crate::tokenstream::LazyAttrTokenStream;
1010
use crate::{
1111
Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField,
12-
FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind,
13-
Ty, Variant, Visibility, WherePredicate,
12+
FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Restriction,
13+
Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate,
1414
};
1515

1616
/// A trait for AST nodes having an ID.
@@ -98,7 +98,19 @@ macro_rules! impl_has_tokens_none {
9898
};
9999
}
100100

101-
impl_has_tokens!(AssocItem, AttrItem, Block, Expr, ForeignItem, Item, Pat, Path, Ty, Visibility);
101+
impl_has_tokens!(
102+
AssocItem,
103+
AttrItem,
104+
Block,
105+
Expr,
106+
ForeignItem,
107+
Item,
108+
Pat,
109+
Path,
110+
Restriction,
111+
Ty,
112+
Visibility
113+
);
102114
impl_has_tokens_none!(
103115
Arm,
104116
ExprField,
@@ -243,7 +255,7 @@ impl_has_attrs!(
243255
Variant,
244256
WherePredicate,
245257
);
246-
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility);
258+
impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Restriction, Ty, Visibility);
247259

248260
impl<T: HasAttrs> HasAttrs for P<T> {
249261
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS;

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ pub trait MutVisitor: Sized {
327327
walk_vis(self, vis);
328328
}
329329

330+
fn visit_restriction(&mut self, restriction: &mut Restriction) {
331+
walk_restriction(self, restriction);
332+
}
333+
330334
fn visit_id(&mut self, _id: &mut NodeId) {
331335
// Do nothing.
332336
}
@@ -1405,6 +1409,18 @@ fn walk_vis<T: MutVisitor>(vis: &mut T, visibility: &mut Visibility) {
14051409
vis.visit_span(span);
14061410
}
14071411

1412+
fn walk_restriction<T: MutVisitor>(vis: &mut T, restriction: &mut Restriction) {
1413+
let Restriction { kind, span, tokens: _ } = restriction;
1414+
match kind {
1415+
RestrictionKind::Unrestricted | RestrictionKind::Implied => {}
1416+
RestrictionKind::Restricted { path, id, shorthand: _ } => {
1417+
vis.visit_id(id);
1418+
vis.visit_path(path);
1419+
}
1420+
}
1421+
vis.visit_span(span);
1422+
}
1423+
14081424
fn walk_capture_by<T: MutVisitor>(vis: &mut T, capture_by: &mut CaptureBy) {
14091425
match capture_by {
14101426
CaptureBy::Ref => {}

compiler/rustc_ast/src/visit.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ pub trait Visitor<'ast>: Sized {
268268
fn visit_vis(&mut self, vis: &'ast Visibility) -> Self::Result {
269269
walk_vis(self, vis)
270270
}
271+
fn visit_restriction(&mut self, restriction: &'ast Restriction) -> Self::Result {
272+
walk_restriction(self, restriction)
273+
}
271274
fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) -> Self::Result {
272275
walk_fn_ret_ty(self, ret_ty)
273276
}
@@ -554,7 +557,16 @@ macro_rules! common_visitor_and_walkers {
554557
<V as Visitor<$lt>>::Result::output()
555558
)?
556559
}
557-
ItemKind::Trait(box Trait { safety, is_auto: _, ident, generics, bounds, items }) => {
560+
ItemKind::Trait(box Trait {
561+
impl_restriction,
562+
safety,
563+
is_auto: _,
564+
ident,
565+
generics,
566+
bounds,
567+
items,
568+
}) => {
569+
try_visit!(vis.visit_restriction(impl_restriction));
558570
try_visit!(visit_safety(vis, safety));
559571
try_visit!(vis.visit_ident(ident));
560572
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::
15161528
V::Result::output()
15171529
}
15181530

1531+
pub fn walk_restriction<'a, V: Visitor<'a>>(
1532+
visitor: &mut V,
1533+
restriction: &'a Restriction,
1534+
) -> V::Result {
1535+
let Restriction { kind, span: _, tokens: _ } = restriction;
1536+
match kind {
1537+
RestrictionKind::Unrestricted | RestrictionKind::Implied => {}
1538+
RestrictionKind::Restricted { path, id, shorthand: _ } => {
1539+
try_visit!(visitor.visit_path(path, *id));
1540+
}
1541+
}
1542+
V::Result::output()
1543+
}
1544+
15191545
pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) -> V::Result {
15201546
let Attribute { kind, id: _, style: _, span: _ } = attr;
15211547
match kind {

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
415415
items: new_impl_items,
416416
}))
417417
}
418-
ItemKind::Trait(box Trait { is_auto, safety, ident, generics, bounds, items }) => {
418+
ItemKind::Trait(box Trait {
419+
impl_restriction: _,
420+
is_auto,
421+
safety,
422+
ident,
423+
generics,
424+
bounds,
425+
items,
426+
}) => {
419427
let ident = self.lower_ident(*ident);
420428
let (generics, (safety, items, bounds)) = self.lower_generics(
421429
generics,

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
515515
gate_all!(where_clause_attrs, "attributes in `where` clause are unstable");
516516
gate_all!(super_let, "`super let` is experimental");
517517
gate_all!(frontmatter, "frontmatters are experimental");
518+
gate_all!(impl_restriction, "impl restrictions are experimental");
518519

519520
if !visitor.features.never_patterns() {
520521
if let Some(spans) = spans.get(&sym::never_patterns) {

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,10 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
10711071
Self::to_string(|s| s.print_visibility(v))
10721072
}
10731073

1074+
fn restriction_to_string(&self, kw: &'static str, r: &ast::Restriction) -> String {
1075+
Self::to_string(|s| s.print_restriction(kw, r))
1076+
}
1077+
10741078
fn block_to_string(&self, blk: &ast::Block) -> String {
10751079
Self::to_string(|s| {
10761080
let (cb, ib) = s.head("");

compiler/rustc_ast_pretty/src/pprust/state/item.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ impl<'a> State<'a> {
357357
self.bclose(item.span, empty, cb);
358358
}
359359
ast::ItemKind::Trait(box ast::Trait {
360+
impl_restriction,
360361
safety,
361362
is_auto,
362363
ident,
@@ -366,6 +367,7 @@ impl<'a> State<'a> {
366367
}) => {
367368
let (cb, ib) = self.head("");
368369
self.print_visibility(&item.vis);
370+
self.print_restriction("impl", impl_restriction);
369371
self.print_safety(*safety);
370372
self.print_is_auto(*is_auto);
371373
self.word_nbsp("trait");
@@ -473,6 +475,22 @@ impl<'a> State<'a> {
473475
}
474476
}
475477

478+
// FIXME(jhpratt) make `kw` into a const generic once permitted
479+
pub(crate) fn print_restriction(&mut self, kw: &'static str, restriction: &ast::Restriction) {
480+
match restriction.kind {
481+
ast::RestrictionKind::Unrestricted => self.word_nbsp(kw),
482+
ast::RestrictionKind::Restricted { ref path, shorthand, id: _ } => {
483+
let path = Self::to_string(|s| s.print_path(path, false, 0));
484+
if shorthand {
485+
self.word_nbsp(format!("{kw}({path})"))
486+
} else {
487+
self.word_nbsp(format!("{kw}(in {path})"))
488+
}
489+
}
490+
ast::RestrictionKind::Implied => {}
491+
}
492+
}
493+
476494
fn print_defaultness(&mut self, defaultness: ast::Defaultness) {
477495
if let ast::Defaultness::Default(_) = defaultness {
478496
self.word_nbsp("default");

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,8 @@ declare_features! (
534534
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
535535
/// Allows `if let` guard in match arms.
536536
(unstable, if_let_guard, "1.47.0", Some(51114)),
537+
/// Allows `impl(crate) trait Foo` restrictions
538+
(unstable, impl_restriction, "CURRENT_RUSTC_VERSION", Some(105077)),
537539
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
538540
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
539541
/// Allows `impl Trait` in bindings (`let`).

compiler/rustc_middle/src/hir/map.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1179,14 +1179,15 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh {
11791179
}
11801180
tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
11811181
tcx.stable_crate_id(LOCAL_CRATE).hash_stable(&mut hcx, &mut stable_hasher);
1182-
// Hash visibility information since it does not appear in HIR.
1182+
// Hash visibility and restriction information since it does not appear in HIR.
11831183
// FIXME: Figure out how to remove `visibilities_for_hashing` by hashing visibilities on
11841184
// the fly in the resolver, storing only their accumulated hash in `ResolverGlobalCtxt`,
11851185
// and combining it with other hashes here.
11861186
resolutions.visibilities_for_hashing.hash_stable(&mut hcx, &mut stable_hasher);
11871187
with_metavar_spans(|mspans| {
11881188
mspans.freeze_and_get_read_spans().hash_stable(&mut hcx, &mut stable_hasher);
11891189
});
1190+
resolutions.impl_restrictions.hash_stable(&mut hcx, &mut stable_hasher);
11901191
stable_hasher.finish()
11911192
});
11921193

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ pub struct ResolverOutputs {
178178
#[derive(Debug)]
179179
pub struct ResolverGlobalCtxt {
180180
pub visibilities_for_hashing: Vec<(LocalDefId, Visibility)>,
181+
pub impl_restrictions: FxIndexMap<LocalDefId, Restriction>,
181182
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
182183
pub expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
183184
pub effective_visibilities: EffectiveVisibilities,
@@ -322,6 +323,35 @@ impl Visibility {
322323
}
323324
}
324325

326+
#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, Encodable, Decodable, HashStable)]
327+
pub enum Restriction {
328+
/// The restriction does not affect the item.
329+
Unrestricted,
330+
/// The restriction only applies outside of this path.
331+
Restricted(DefId, Span),
332+
}
333+
334+
impl Restriction {
335+
/// Returns `true` if the behavior is allowed/unrestricted in the given module. A value of
336+
/// `false` indicates that the behavior is prohibited.
337+
pub fn is_allowed_in(self, module: DefId, tcx: TyCtxt<'_>) -> bool {
338+
let restricted_to = match self {
339+
Restriction::Unrestricted => return true,
340+
Restriction::Restricted(module, _) => module,
341+
};
342+
343+
tcx.is_descendant_of(module, restricted_to.into())
344+
}
345+
346+
/// Obtain the [`Span`] of the restriction. If unrestricted, an empty span is returned.
347+
pub fn span(&self) -> Span {
348+
match self {
349+
Restriction::Unrestricted => DUMMY_SP,
350+
Restriction::Restricted(_, span) => *span,
351+
}
352+
}
353+
}
354+
325355
#[derive(Clone, Debug, PartialEq, Eq, Copy, Hash, TyEncodable, TyDecodable, HashStable)]
326356
#[derive(TypeFoldable, TypeVisitable)]
327357
pub struct ClosureSizeProfileData<'tcx> {

compiler/rustc_parse/messages.ftl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,13 @@ parse_inclusive_range_no_end = inclusive range with no end
377377
parse_incorrect_parens_trait_bounds = incorrect parentheses around trait bounds
378378
parse_incorrect_parens_trait_bounds_sugg = fix the parentheses
379379
380+
parse_incorrect_restriction = incorrect {parse_restriction_noun} restriction
381+
.help = some possible {parse_restriction_noun} restrictions are:
382+
`{$keyword}(crate)`: {parse_restriction_adjective} only in the current crate
383+
`{$keyword}(super)`: {parse_restriction_adjective} only in the current module's parent
384+
`{$keyword}(in path::to::module)`: {parse_restriction_adjective} only in the specified path
385+
.suggestion = make this {parse_restriction_adjective} only to module `{$path}` with `in`
386+
380387
parse_incorrect_semicolon =
381388
expected item, found `;`
382389
.suggestion = remove this semicolon
@@ -783,6 +790,18 @@ parse_reserved_string = invalid string literal
783790
.note = unprefixed guarded string literals are reserved for future use since Rust 2024
784791
.suggestion_whitespace = consider inserting whitespace here
785792
793+
# internal use only
794+
parse_restriction_adjective = { $keyword ->
795+
[impl] implementable
796+
*[DEFAULT_IS_BUG] BUG
797+
}
798+
799+
# internal use only
800+
parse_restriction_noun = { $keyword ->
801+
[impl] impl
802+
*[DEFAULT_IS_BUG] BUG
803+
}
804+
786805
parse_return_types_use_thin_arrow = return types are denoted using `->`
787806
.suggestion = use `->` instead
788807
@@ -853,6 +872,7 @@ parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern
853872
parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto`
854873
parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe`
855874
875+
parse_trait_alias_cannot_have_impl_restriction = trait alias cannot have `impl` restriction
856876
parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before
857877
.suggestion = move `{$kw}` before the `for<...>`
858878

0 commit comments

Comments
 (0)