diff --git a/Cargo.lock b/Cargo.lock index 9f1561b503d8e..330791691b219 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3490,6 +3490,7 @@ dependencies = [ "rustc_privacy", "rustc_query_system", "rustc_resolve", + "rustc_restrictions", "rustc_session", "rustc_span", "rustc_symbol_mangling", @@ -3777,6 +3778,7 @@ dependencies = [ "rustc_query_impl", "rustc_query_system", "rustc_resolve", + "rustc_restrictions", "rustc_session", "rustc_span", "rustc_symbol_mangling", @@ -4189,6 +4191,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "rustc_restrictions" +version = "0.0.0" +dependencies = [ + "rustc_errors", + "rustc_fluent_macro", + "rustc_hir", + "rustc_macros", + "rustc_middle", + "rustc_session", + "rustc_span", + "tracing", +] + [[package]] name = "rustc_serialize" version = "0.0.0" diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a7198fbf88768..f83ee8d007a29 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -35,6 +35,7 @@ use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use std::fmt; +use std::marker::PhantomData; use std::mem; use thin_vec::{thin_vec, ThinVec}; @@ -2721,6 +2722,148 @@ impl VisibilityKind { } } +pub mod restriction_kind { + use super::*; + + /// Helper macro to generate the type, derive the relevant bounds, and implement the sealed + /// trait. + macro_rules! define_restrictions { + ($(Restriction { + requires_explicit_path: $requires_explicit_path:ident, + name: $name:ident, + keyword: $keyword_sym:path => $keyword_str:literal, + feature_gate: $feature_gate:expr $(,)? + }),* $(,)?) => {$( + #[derive(Debug, Clone, Copy, Encodable, Decodable)] + pub enum $name {} + + impl RestrictionKind for $name { + const REQUIRES_EXPLICIT_PATH: bool = $requires_explicit_path; + const KEYWORD_SYM: Symbol = $keyword_sym; + const KEYWORD_STR: &'static str = $keyword_str; + const FEATURE_GATE: Option = $feature_gate; + + const MIDDLE_KIND: u8 = ${index()}; + } + + impl sealed::Sealed for $name {} + )*}; + } + + // FIXME(jhpratt) After a bootstrap, replace this with the now-implemented native syntax. + mod sealed { + pub trait Sealed {} + } + + pub trait RestrictionKind: sealed::Sealed { + const REQUIRES_EXPLICIT_PATH: bool; + const KEYWORD_SYM: Symbol; + const KEYWORD_STR: &'static str; + const FEATURE_GATE: Option; + + const MIDDLE_KIND: u8; + } + + define_restrictions![ + Restriction { + requires_explicit_path: false, + name: Visibility, + keyword: kw::Pub => "pub", + feature_gate: None, + }, + Restriction { + requires_explicit_path: true, + name: Impl, + keyword: kw::Impl => "impl", + feature_gate: Some(sym::impl_restriction), + }, + Restriction { + requires_explicit_path: true, + name: Mut, + keyword: kw::Mut => "mut", + feature_gate: Some(sym::mut_restriction), + }, + ]; +} +pub use restriction_kind::RestrictionKind; + +// FIXME(jhpratt) Replace the generic with an enum once ADT const generics are not incomplete. +#[derive(Clone, Encodable, Decodable, Debug)] +pub struct Restriction { + pub level: RestrictionLevel, + pub span: Span, + pub kind: PhantomData, + pub tokens: Option, +} + +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum RestrictionLevel { + // kw + Unrestricted, + // kw(path) + Restricted { path: P, id: NodeId, shorthand: bool }, + // nothing + Implied, +} + +pub type VisibilityRestriction = Restriction; +pub type ImplRestriction = Restriction; +pub type MutRestriction = Restriction; + +impl From for Visibility { + fn from(restriction: VisibilityRestriction) -> Self { + match restriction.level { + RestrictionLevel::Unrestricted => Self { + kind: VisibilityKind::Public, + span: restriction.span, + tokens: restriction.tokens, + }, + RestrictionLevel::Restricted { path, id, shorthand } => Self { + kind: VisibilityKind::Restricted { path, id, shorthand }, + span: restriction.span, + tokens: restriction.tokens, + }, + RestrictionLevel::Implied => Self { + kind: VisibilityKind::Inherited, + span: restriction.span, + tokens: restriction.tokens, + }, + } + } +} + +impl Restriction { + pub fn unrestricted() -> Self { + Self { + level: RestrictionLevel::Unrestricted, + span: DUMMY_SP, + tokens: None, + kind: PhantomData, + } + } + + pub fn restricted(path: P, id: NodeId, shorthand: bool) -> Self { + Self { + level: RestrictionLevel::Restricted { path, id, shorthand }, + span: DUMMY_SP, + tokens: None, + kind: PhantomData, + } + } + + pub fn implied() -> Self { + Self { level: RestrictionLevel::Implied, span: DUMMY_SP, tokens: None, kind: PhantomData } + } + + pub fn with_span(self, span: Span) -> Self { + Self { span, ..self } + } + + pub fn with_tokens(self, tokens: LazyAttrTokenStream) -> Self { + Self { tokens: Some(tokens), ..self } + } +} + /// Field definition in a struct, variant or union. /// /// E.g., `bar: usize` as in `struct Foo { bar: usize }`. @@ -2730,6 +2873,7 @@ pub struct FieldDef { pub id: NodeId, pub span: Span, pub vis: Visibility, + pub mut_restriction: MutRestriction, pub ident: Option, pub ty: P, @@ -2869,6 +3013,7 @@ impl Default for FnHeader { #[derive(Clone, Encodable, Decodable, Debug)] pub struct Trait { + pub impl_restriction: ImplRestriction, pub unsafety: Unsafe, pub is_auto: IsAuto, pub generics: Generics, diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 4dc9c30a2c807..fc7cbf79ea08f 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -7,7 +7,7 @@ use crate::token::Nonterminal; use crate::tokenstream::LazyAttrTokenStream; use crate::{Arm, Crate, ExprField, FieldDef, GenericParam, Param, PatField, Variant}; use crate::{AssocItem, Expr, ForeignItem, Item, NodeId}; -use crate::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; +use crate::{AttrItem, AttrKind, Block, Pat, Path, Restriction, Ty, Visibility}; use crate::{AttrVec, Attribute, Stmt, StmtKind}; use rustc_span::Span; @@ -110,6 +110,12 @@ macro_rules! impl_has_span { impl_has_span!(AssocItem, Block, Expr, ForeignItem, Item, Pat, Path, Stmt, Ty, Visibility); +impl HasSpan for Restriction { + fn span(&self) -> Span { + self.span + } +} + impl> HasSpan for T { fn span(&self) -> Span { self.ast_deref().span() @@ -326,6 +332,14 @@ impl_has_attrs!( ); impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility); +impl HasAttrs for Restriction { + const SUPPORTS_CUSTOM_INNER_ATTRS: bool = false; + fn attrs(&self) -> &[Attribute] { + &[] + } + fn visit_attrs(&mut self, _f: impl FnOnce(&mut AttrVec)) {} +} + impl> HasAttrs for T { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::Target::SUPPORTS_CUSTOM_INNER_ATTRS; fn attrs(&self) -> &[Attribute] { diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index b07ed1d1c741e..40a03d3e1c1fb 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -13,6 +13,7 @@ #![feature(const_trait_impl)] #![feature(if_let_guard)] #![feature(let_chains)] +#![feature(macro_metavar_expr)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(stmt_expr_attributes)] diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 53a9c9a046ea0..7f7c2a0bc6b77 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -278,6 +278,13 @@ pub trait MutVisitor: Sized { noop_visit_vis(vis, self); } + fn visit_restriction( + &mut self, + restriction: &mut Restriction, + ) { + noop_visit_restriction(restriction, self); + } + fn visit_id(&mut self, _id: &mut NodeId) { // Do nothing. } @@ -990,9 +997,10 @@ pub fn noop_flat_map_field_def( mut fd: FieldDef, visitor: &mut T, ) -> SmallVec<[FieldDef; 1]> { - let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd; + let FieldDef { span, ident, vis, mut_restriction, id, ty, attrs, is_placeholder: _ } = &mut fd; visitor.visit_span(span); visit_opt(ident, |ident| visitor.visit_ident(ident)); + visitor.visit_restriction(mut_restriction); visitor.visit_vis(vis); visitor.visit_id(id); visitor.visit_ty(ty); @@ -1092,7 +1100,15 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { vis.visit_ty(self_ty); items.flat_map_in_place(|item| vis.flat_map_impl_item(item)); } - ItemKind::Trait(box Trait { unsafety, is_auto: _, generics, bounds, items }) => { + ItemKind::Trait(box Trait { + impl_restriction, + unsafety, + is_auto: _, + generics, + bounds, + items, + }) => { + vis.visit_restriction(impl_restriction); visit_unsafety(unsafety, vis); vis.visit_generics(generics); visit_bounds(bounds, vis); @@ -1554,6 +1570,17 @@ pub fn noop_visit_vis(visibility: &mut Visibility, vis: &mut T) { vis.visit_span(&mut visibility.span); } +pub fn noop_visit_restriction( + restriction: &mut Restriction, + vis: &mut T, +) { + if let RestrictionLevel::Restricted { path, id, shorthand: _ } = &mut restriction.level { + vis.visit_path(path); + vis.visit_id(id); + } + vis.visit_span(&mut restriction.span); +} + /// Some value for the AST node that is valid but possibly meaningless. pub trait DummyAstNode { fn dummy() -> Self; diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index d9de5b8e197db..8136aea01f988 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -227,6 +227,12 @@ pub trait Visitor<'ast>: Sized { fn visit_vis(&mut self, vis: &'ast Visibility) { walk_vis(self, vis) } + fn visit_restriction( + &mut self, + restriction: &'ast Restriction, + ) { + walk_restriction(self, restriction) + } fn visit_fn_ret_ty(&mut self, ret_ty: &'ast FnRetTy) { walk_fn_ret_ty(self, ret_ty) } @@ -357,10 +363,18 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_generics(generics); visitor.visit_variant_data(struct_definition); } - ItemKind::Trait(box Trait { unsafety: _, is_auto: _, generics, bounds, items }) => { + ItemKind::Trait(box Trait { + impl_restriction, + unsafety: _, + is_auto: _, + generics, + bounds, + items, + }) => { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits); walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait); + visitor.visit_restriction(impl_restriction); } ItemKind::TraitAlias(generics, bounds) => { visitor.visit_generics(generics); @@ -705,6 +719,7 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) if let Some(ident) = field.ident { visitor.visit_ident(ident); } + visitor.visit_restriction(&field.mut_restriction); visitor.visit_ty(&field.ty); walk_list!(visitor, visit_attribute, &field.attrs); } @@ -949,6 +964,17 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) { } } +pub fn walk_restriction<'a, V: Visitor<'a>, Kind: crate::RestrictionKind>( + visitor: &mut V, + restriction: &'a Restriction, +) { + match restriction.level { + RestrictionLevel::Unrestricted => {} + RestrictionLevel::Restricted { ref path, id, shorthand: _ } => visitor.visit_path(path, id), + RestrictionLevel::Implied => {} + } +} + pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) { match &attr.kind { AttrKind::Normal(normal) => walk_attr_args(visitor, &normal.item.args), diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ab68436c0939d..8263939f1ede5 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -415,7 +415,14 @@ impl<'hir> LoweringContext<'_, 'hir> { items: new_impl_items, })) } - ItemKind::Trait(box Trait { is_auto, unsafety, generics, bounds, items }) => { + ItemKind::Trait(box Trait { + impl_restriction: _, + is_auto, + unsafety, + generics, + bounds, + items, + }) => { // FIXME(const_trait_impl, effects, fee1-dead) this should be simplified if possible let constness = attrs .unwrap_or(&[]) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index b0dbc2c23403e..306963887e66e 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -556,6 +556,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_closures, "const closures are experimental"); gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); + gate_all!(impl_restriction, "restrictions are experimental"); + gate_all!(mut_restriction, "restrictions are experimental"); if !visitor.features.negative_bounds { for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 59239b49eddd8..66d83a1d63274 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -883,6 +883,13 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere Self::to_string(|s| s.print_visibility(v)) } + fn restriction_to_string( + &self, + restriction: &ast::Restriction, + ) -> String { + Self::to_string(|s| s.print_restriction(restriction)) + } + fn block_to_string(&self, blk: &ast::Block) -> String { Self::to_string(|s| { // Containing cbox, will be closed by `print_block` at `}`. diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 5c01b7ea70a13..e28da52b7cf0a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -314,6 +314,7 @@ impl<'a> State<'a> { self.bclose(item.span, empty); } ast::ItemKind::Trait(box ast::Trait { + impl_restriction, is_auto, unsafety, generics, @@ -323,6 +324,7 @@ impl<'a> State<'a> { }) => { self.head(""); self.print_visibility(&item.vis); + self.print_restriction(impl_restriction); self.print_unsafety(*unsafety); self.print_is_auto(*is_auto); self.word_nbsp("trait"); @@ -428,6 +430,25 @@ impl<'a> State<'a> { } } + pub(crate) fn print_restriction( + &mut self, + restriction: &ast::Restriction, + ) { + match restriction.level { + ast::RestrictionLevel::Unrestricted => self.word_nbsp(Kind::KEYWORD_STR), + ast::RestrictionLevel::Restricted { ref path, id: _, shorthand } => { + let kw = Kind::KEYWORD_STR; + 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::RestrictionLevel::Implied => {} + } + } + fn print_defaultness(&mut self, defaultness: ast::Defaultness) { if let ast::Defaultness::Default(_) = defaultness { self.word_nbsp("default"); @@ -447,6 +468,7 @@ impl<'a> State<'a> { self.maybe_print_comment(field.span.lo()); self.print_outer_attributes(&field.attrs); self.print_visibility(&field.vis); + self.print_restriction(&field.mut_restriction); self.print_ident(field.ident.unwrap()); self.word_nbsp(":"); self.print_type(&field.ty); diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs index 5c52c9c18adfd..12e90b5841034 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -5,7 +5,7 @@ //! [`codegen_static`]: crate::constant::codegen_static use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_middle::mir::mono::{Linkage as RLinkage, MonoItem, Visibility}; +use rustc_middle::mir::mono::{MonoItem, MonoItemData}; use crate::prelude::*; @@ -16,11 +16,11 @@ pub(crate) mod jit; fn predefine_mono_items<'tcx>( tcx: TyCtxt<'tcx>, module: &mut dyn Module, - mono_items: &[(MonoItem<'tcx>, (RLinkage, Visibility))], + mono_items: &[(MonoItem<'tcx>, MonoItemData)], ) { tcx.prof.generic_activity("predefine functions").run(|| { let is_compiler_builtins = tcx.is_compiler_builtins(LOCAL_CRATE); - for &(mono_item, (linkage, visibility)) in mono_items { + for &(mono_item, data) in mono_items { match mono_item { MonoItem::Fn(instance) => { let name = tcx.symbol_name(instance).name; @@ -29,8 +29,8 @@ fn predefine_mono_items<'tcx>( get_function_sig(tcx, module.target_config().default_call_conv, instance); let linkage = crate::linkage::get_clif_linkage( mono_item, - linkage, - visibility, + data.linkage, + data.visibility, is_compiler_builtins, ); module.declare_function(name, linkage, &sig).unwrap(); diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index dcd560b3dcd95..9e614ca4ace0b 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -159,8 +159,8 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_i let cx = CodegenCx::new(&context, cgu, tcx, supports_128bit_integers); let mono_items = cgu.items_in_deterministic_order(tcx); - for &(mono_item, (linkage, visibility)) in &mono_items { - mono_item.predefine::>(&cx, linkage, visibility); + for &(mono_item, data) in &mono_items { + mono_item.predefine::>(&cx, data.linkage, data.visibility); } // ... and now that we have everything pre-defined, fill out those definitions. diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 5b2bbdb4bde1e..5b5f81c032940 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -86,8 +86,8 @@ pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol) -> (ModuleCodegen { let cx = CodegenCx::new(tcx, cgu, &llvm_module); let mono_items = cx.codegen_unit.items_in_deterministic_order(cx.tcx); - for &(mono_item, (linkage, visibility)) in &mono_items { - mono_item.predefine::>(&cx, linkage, visibility); + for &(mono_item, data) in &mono_items { + mono_item.predefine::>(&cx, data.linkage, data.visibility); } // ... and now that we have everything pre-defined, fill out those definitions. diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 406048bfe0598..cbe7e5190791c 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -328,14 +328,14 @@ fn exported_symbols_provider_local( let (_, cgus) = tcx.collect_and_partition_mono_items(()); - for (mono_item, &(linkage, visibility)) in cgus.iter().flat_map(|cgu| cgu.items().iter()) { - if linkage != Linkage::External { + for (mono_item, data) in cgus.iter().flat_map(|cgu| cgu.items().iter()) { + if data.linkage != Linkage::External { // We can only re-use things with external linkage, otherwise // we'll get a linker error continue; } - if need_visibility && visibility == Visibility::Hidden { + if need_visibility && data.visibility == Visibility::Hidden { // If we potentially share things from Rust dylibs, they must // not be hidden continue; diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 67352c55c9019..e755285ba643f 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -28,6 +28,7 @@ rustc_passes = { path = "../rustc_passes" } rustc_privacy = { path = "../rustc_privacy" } rustc_query_system = { path = "../rustc_query_system" } rustc_resolve = { path = "../rustc_resolve" } +rustc_restrictions = { path = "../rustc_restrictions" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 2e26ce111f01d..f3e12be9a8ada 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -117,6 +117,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_privacy::DEFAULT_LOCALE_RESOURCE, rustc_query_system::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, + rustc_restrictions::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_symbol_mangling::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_error_codes/src/error_codes/E0577.md b/compiler/rustc_error_codes/src/error_codes/E0577.md index eba2d3b14175b..72a0cbdb8cc0c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0577.md +++ b/compiler/rustc_error_codes/src/error_codes/E0577.md @@ -1,4 +1,4 @@ -Something other than a module was found in visibility scope. +Something other than a module was found in visibility or restriction scope. Erroneous code example: @@ -13,7 +13,8 @@ fn main() {} `Sea` is not a module, therefore it is invalid to use it in a visibility path. To fix this error we need to ensure `sea` is a module. -Please note that the visibility scope can only be applied on ancestors! +Please note that the visibility and restriction scope can only be applied on +ancestors! ```edition2018 pub mod sea { diff --git a/compiler/rustc_error_codes/src/error_codes/E0578.md b/compiler/rustc_error_codes/src/error_codes/E0578.md index fca89757287f5..eff9b61c0905a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0578.md +++ b/compiler/rustc_error_codes/src/error_codes/E0578.md @@ -1,4 +1,5 @@ -A module cannot be found and therefore, the visibility cannot be determined. +A module cannot be found and therefore, the visibility or restriction cannot be +determined. Erroneous code example: diff --git a/compiler/rustc_error_codes/src/error_codes/E0704.md b/compiler/rustc_error_codes/src/error_codes/E0704.md index c22b274fb223e..a3a5e41430c2f 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0704.md +++ b/compiler/rustc_error_codes/src/error_codes/E0704.md @@ -1,4 +1,4 @@ -An incorrect visibility restriction was specified. +An incorrect restriction was specified. Erroneous code example: @@ -25,3 +25,4 @@ mod foo { For more information see the Rust Reference on [Visibility]. [Visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html +[//]: FIXME(jhpratt) add link to Restriction page in reference once it's created diff --git a/compiler/rustc_error_codes/src/error_codes/E0742.md b/compiler/rustc_error_codes/src/error_codes/E0742.md index e10c1639dd38a..1058a9ba792f8 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0742.md +++ b/compiler/rustc_error_codes/src/error_codes/E0742.md @@ -1,5 +1,5 @@ -Visibility is restricted to a module which isn't an ancestor of the current -item. +Visibility or restriction is restricted to a module which isn't an ancestor of +the current item. Erroneous code example: diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index e9af688ee2b61..948cf9fb4c243 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -169,6 +169,7 @@ pub fn placeholder( span, ty: ty(), vis, + mut_restriction: ast::Restriction::implied(), is_placeholder: true, }]), AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant { diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 56a2c5eff3d07..c2dfea5b05ecd 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -426,6 +426,8 @@ declare_features! ( (active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None), /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), + /// Allows restricting the implementation of traits. + (active, impl_restriction, "CURRENT_RUSTC_VERSION", Some(105077), None), /// Allows `impl Trait` to be used inside associated types (RFC 2515). (active, impl_trait_in_assoc_type, "1.70.0", Some(63063), None), /// Allows `impl Trait` as output type in `Fn` traits in return position of functions. @@ -462,6 +464,8 @@ declare_features! ( (active, more_qualified_paths, "1.54.0", Some(86935), None), /// Allows the `#[must_not_suspend]` attribute. (active, must_not_suspend, "1.57.0", Some(83310), None), + /// Allows restricting the mutation of fields. + (active, mut_restriction, "CURRENT_RUSTC_VERSION", Some(105077), None), /// Allows using `#[naked]` on functions. (active, naked_functions, "1.9.0", Some(32408), None), /// Allows specifying the as-needed link modifier diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 7826d42dcb2d7..a6b307cdf1580 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -48,6 +48,7 @@ rustc_privacy = { path = "../rustc_privacy" } rustc_query_system = { path = "../rustc_query_system" } rustc_query_impl = { path = "../rustc_query_impl" } rustc_resolve = { path = "../rustc_resolve" } +rustc_restrictions = { path = "../rustc_restrictions" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 6b3facd041c28..ca894e2674923 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -670,6 +670,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock = LazyLock::new(|| { rustc_resolve::provide(providers); rustc_hir_analysis::provide(providers); rustc_hir_typeck::provide(providers); + rustc_restrictions::provide(providers); ty::provide(providers); traits::provide(providers); rustc_passes::provide(providers); @@ -859,6 +860,9 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { tcx.ensure().check_mod_privacy(module); }); }); + }, + { + sess.time("check_impl_restriction", || tcx.check_impl_restriction(())); } ); diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 848535fb39521..678a09d69ea92 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -271,6 +271,8 @@ provide! { tcx, def_id, other, cdata, associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array } visibility => { cdata.get_visibility(def_id.index) } + impl_restriction => { table } + mut_restriction => { table } adt_def => { cdata.get_adt_def(def_id.index, tcx) } adt_destructor => { let _ = cdata; diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b5f955d14fbc8..4bc85fe61a8bc 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1336,6 +1336,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tcx.local_visibility(local_id).map_id(|def_id| def_id.local_def_index); record!(self.tables.visibility[def_id] <- vis); } + if let DefKind::Trait = def_kind { + let impl_restriction = self.tcx.impl_restriction(def_id); + record!(self.tables.impl_restriction[def_id] <- impl_restriction); + } + if let DefKind::Field = def_kind { + let mut_restriction = self.tcx.mut_restriction(def_id); + record!(self.tables.mut_restriction[def_id] <- mut_restriction); + } if should_encode_stability(def_kind) { self.encode_stability(def_id); self.encode_const_stability(def_id); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 9cffd96f4a396..e9b35deb45f76 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -395,6 +395,8 @@ define_tables! { associated_item_or_field_def_ids: Table>, opt_def_kind: Table, visibility: Table>>, + impl_restriction: Table>, + mut_restriction: Table>, def_span: Table>, def_ident_span: Table>, lookup_stability: Table>, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 1fd68dc5cb28e..ffb4a4730f54b 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1220,9 +1220,11 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { } tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher); tcx.sess.local_stable_crate_id().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. resolutions.visibilities.hash_stable(&mut hcx, &mut stable_hasher); resolutions.has_pub_restricted.hash_stable(&mut hcx, &mut stable_hasher); + resolutions.impl_restrictions.hash_stable(&mut hcx, &mut stable_hasher); + resolutions.mut_restrictions.hash_stable(&mut hcx, &mut stable_hasher); stable_hasher.finish() }); diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index ca3cd943d3d5d..16addc2dc1e75 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -59,12 +59,19 @@ impl<'tcx> MonoItem<'tcx> { pub fn size_estimate(&self, tcx: TyCtxt<'tcx>) -> usize { match *self { MonoItem::Fn(instance) => { - // Estimate the size of a function based on how many statements - // it contains. - tcx.instance_def_size_estimate(instance.def) + match instance.def { + // "Normal" functions size estimate: the number of + // statements, plus one for the terminator. + InstanceDef::Item(..) | InstanceDef::DropGlue(..) => { + let mir = tcx.instance_mir(instance.def); + mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum() + } + // Other compiler-generated shims size estimate: 1 + _ => 1, + } } - // Conservatively estimate the size of a static declaration - // or assembly to be 1. + // Conservatively estimate the size of a static declaration or + // assembly item to be 1. MonoItem::Static(_) | MonoItem::GlobalAsm(_) => 1, } } @@ -230,7 +237,7 @@ pub struct CodegenUnit<'tcx> { /// contain something unique to this crate (e.g., a module path) /// as well as the crate name and disambiguator. name: Symbol, - items: FxHashMap, (Linkage, Visibility)>, + items: FxHashMap, MonoItemData>, size_estimate: usize, primary: bool, /// True if this is CGU is used to hold code coverage information for dead code, @@ -238,6 +245,14 @@ pub struct CodegenUnit<'tcx> { is_code_coverage_dead_code_cgu: bool, } +/// Auxiliary info about a `MonoItem`. +#[derive(Copy, Clone, PartialEq, Debug, HashStable)] +pub struct MonoItemData { + pub linkage: Linkage, + pub visibility: Visibility, + pub size_estimate: usize, +} + /// Specifies the linkage type for a `MonoItem`. /// /// See for more details about these variants. @@ -292,12 +307,12 @@ impl<'tcx> CodegenUnit<'tcx> { } /// The order of these items is non-determinstic. - pub fn items(&self) -> &FxHashMap, (Linkage, Visibility)> { + pub fn items(&self) -> &FxHashMap, MonoItemData> { &self.items } /// The order of these items is non-determinstic. - pub fn items_mut(&mut self) -> &mut FxHashMap, (Linkage, Visibility)> { + pub fn items_mut(&mut self) -> &mut FxHashMap, MonoItemData> { &mut self.items } @@ -320,16 +335,16 @@ impl<'tcx> CodegenUnit<'tcx> { base_n::encode(hash, base_n::CASE_INSENSITIVE) } - pub fn compute_size_estimate(&mut self, tcx: TyCtxt<'tcx>) { - // Estimate the size of a codegen unit as (approximately) the number of MIR - // statements it corresponds to. - self.size_estimate = self.items.keys().map(|mi| mi.size_estimate(tcx)).sum(); + pub fn compute_size_estimate(&mut self) { + // The size of a codegen unit as the sum of the sizes of the items + // within it. + self.size_estimate = self.items.values().map(|data| data.size_estimate).sum(); } - #[inline] /// Should only be called if [`compute_size_estimate`] has previously been called. /// /// [`compute_size_estimate`]: Self::compute_size_estimate + #[inline] pub fn size_estimate(&self) -> usize { // Items are never zero-sized, so if we have items the estimate must be // non-zero, unless we forgot to call `compute_size_estimate` first. @@ -355,7 +370,7 @@ impl<'tcx> CodegenUnit<'tcx> { pub fn items_in_deterministic_order( &self, tcx: TyCtxt<'tcx>, - ) -> Vec<(MonoItem<'tcx>, (Linkage, Visibility))> { + ) -> Vec<(MonoItem<'tcx>, MonoItemData)> { // The codegen tests rely on items being process in the same order as // they appear in the file, so for local items, we sort by node_id first #[derive(PartialEq, Eq, PartialOrd, Ord)] @@ -390,7 +405,7 @@ impl<'tcx> CodegenUnit<'tcx> { ) } - let mut items: Vec<_> = self.items().iter().map(|(&i, &l)| (i, l)).collect(); + let mut items: Vec<_> = self.items().iter().map(|(&i, &data)| (i, data)).collect(); items.sort_by_cached_key(|&(i, _)| item_sort_key(tcx, i)); items } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index f79697936d28c..bb359801ed61e 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -51,6 +51,22 @@ impl<'tcx> PlaceTy<'tcx> { } } + /// `place_ty.field_def(f)` obtains the `FieldDef` of the provided ADT field. + pub fn field_def(self, f: FieldIdx) -> &'tcx ty::FieldDef { + let ty::Adt(adt_def, _) = self.ty.kind() else { + bug!("extracting field def of non-ADT: {self:?}") + }; + + let variant_def = match self.variant_index { + None => adt_def.non_enum_variant(), + Some(variant_index) => { + assert!(adt_def.is_enum()); + &adt_def.variant(variant_index) + } + }; + &variant_def.fields[f] + } + /// Convenience wrapper around `projection_ty_core` for /// `PlaceElem`, where we can just use the `Ty` that is already /// stored inline on field projection elems. diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 2c481745d987a..88bb1ab0639bb 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -274,6 +274,10 @@ trivial! { rustc_middle::ty::UnusedGenericParams, rustc_middle::ty::util::AlwaysRequiresDrop, rustc_middle::ty::Visibility, + // FIXME(jhpratt) replace with a generic impl over ty::RestrictionKind when + // #![feature(generic_const_exprs)] is no longer incomplete. + rustc_middle::ty::ImplRestriction, + rustc_middle::ty::MutRestriction, rustc_session::config::CrateType, rustc_session::config::EntryFnType, rustc_session::config::OptLevel, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 45fa82ba68ad5..fe1bea4f1edd2 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1695,6 +1695,15 @@ rustc_queries! { feedable } + query impl_restriction(def_id: DefId) -> ty::ImplRestriction { + desc { |tcx| "computing impl restriction for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query check_impl_restriction(_: ()) { + desc { "checking impl restrictions" } + } + query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> { desc { "computing the uninhabited predicate of `{:?}`", key } } @@ -1704,6 +1713,23 @@ rustc_queries! { desc { "computing the uninhabited predicate of `{}`", key } } + query mut_restriction(def_id: DefId) -> ty::MutRestriction { + desc { |tcx| "computing mut restriction for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + + query check_mut_restriction(def_id: LocalDefId) { + desc { "checking mut restrictions" } + } + + query adt_expression_restriction(variant_def_id: DefId) -> ty::MutRestriction { + desc { + "computing where `{}` can be constructed via an ADT expression", + tcx.def_path_str(variant_def_id) + } + cache_on_disk_if { true } + } + query dep_kind(_: CrateNum) -> CrateDepKind { eval_always desc { "fetching what a dependency looks like" } @@ -2080,12 +2106,6 @@ rustc_queries! { desc { "looking up supported target features" } } - /// Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning. - query instance_def_size_estimate(def: ty::InstanceDef<'tcx>) - -> usize { - desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) } - } - query features_query(_: ()) -> &'tcx rustc_feature::Features { feedable desc { "looking up enabled feature gates" } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 0411890ab5139..1bbaf0810091b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -162,6 +162,8 @@ pub struct ResolverOutputs { #[derive(Debug)] pub struct ResolverGlobalCtxt { pub visibilities: FxHashMap, + pub impl_restrictions: FxHashMap, + pub mut_restrictions: FxHashMap, /// This field is used to decide whether we should make `PRIVATE_IN_PUBLIC` a hard error. pub has_pub_restricted: bool, /// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`. @@ -290,6 +292,91 @@ pub enum Visibility { Restricted(Id), } +// FIXME(jhpratt) Once #![feature(adt_const_params)] is no longer incomplete, replace this with an +// enum of the various kinds of restrictions. +pub type RestrictionKind = u8; +pub type MutRestriction = + Restriction<{ ::MIDDLE_KIND }>; +pub type ImplRestriction = + Restriction<{ ::MIDDLE_KIND }>; + +#[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(Id, Span), +} + +impl + Copy> 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 => rustc_span::DUMMY_SP, + Restriction::Restricted(_, span) => *span, + } + } + + /// Obtain the path of the restriction. If unrestricted, an empty string is returned. + pub fn restriction_path(&self, tcx: TyCtxt<'_>, krate: rustc_span::def_id::CrateNum) -> String { + let Restriction::Restricted(def_id, _) = self else { + return String::new(); + }; + + let def_id: DefId = (*def_id).into(); + + if krate == def_id.krate { + tcx.def_path_str(def_id) + } else { + tcx.crate_name(def_id.krate).to_ident_string() + } + } + + /// Obtain the stricter of the two restrictions. If the two restrictions are the same, returns + /// `left`. Panics if the restrictions do not reference the same crate. + pub fn stricter_of(left: Self, right: Self, tcx: TyCtxt<'_>) -> Self { + match (left, right) { + (Restriction::Unrestricted, Restriction::Unrestricted) => Restriction::Unrestricted, + (Restriction::Unrestricted, Restriction::Restricted(..)) => right, + (Restriction::Restricted(..), Restriction::Unrestricted) => left, + (Restriction::Restricted(left_did, _), Restriction::Restricted(right_did, _)) => { + let left_did = left_did.into(); + let right_did = right_did.into(); + + if left_did.krate != right_did.krate { + bug!("stricter_of: left and right restriction do not reference the same crate"); + } + + if tcx.is_descendant_of(left_did, right_did) { left } else { right } + } + } + } + + /// Obtain the strictest of the provided restrictions. If multiple restrictions are the same, + /// the first is returned. Panics if all restrictions do not reference the same crate. + pub fn strictest_of(iter: impl Iterator, tcx: TyCtxt<'_>) -> Self { + iter.fold(Restriction::Unrestricted, |left, right| Self::stricter_of(left, right, tcx)) + } + + pub fn map_id(self, f: impl FnOnce(Id) -> OutId) -> Restriction { + match self { + Restriction::Unrestricted => Restriction::Unrestricted, + Restriction::Restricted(id, span) => Restriction::Restricted(f(id), span), + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)] pub enum BoundConstness { /// `T: Trait` diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index cc2b26a5e1457..1214844087965 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -105,6 +105,10 @@ trivially_parameterized_over_tcx! { rustc_type_ir::Variance, } +impl ty::ParameterizedOverTcx for ty::Restriction { + type Value<'tcx> = Self; +} + // HACK(compiler-errors): This macro rule can only take a fake path, // not a real, due to parsing ambiguity reasons. #[macro_export] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index d419329f2d6bf..a8f9454459fbb 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -265,6 +265,8 @@ fn mir_const(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { tcx.ensure_with_value().unsafety_check_result(def); } + tcx.ensure().check_mut_restriction(def); + // has_ffi_unwind_calls query uses the raw mir, so make sure it is run. tcx.ensure_with_value().has_ffi_unwind_calls(def); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 391666554bb65..54096abb2e0c8 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -107,7 +107,8 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_middle::mir; use rustc_middle::mir::mono::{ - CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, Visibility, + CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, + Visibility, }; use rustc_middle::query::Providers; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; @@ -130,11 +131,6 @@ struct PlacedMonoItems<'tcx> { codegen_units: Vec>, internalization_candidates: FxHashSet>, - - /// These must be obtained when the iterator in `partition` runs. They - /// can't be obtained later because some inlined functions might not be - /// reachable. - unique_inlined_stats: (usize, usize), } // The output CGUs are sorted by name. @@ -152,11 +148,11 @@ where // Place all mono items into a codegen unit. `place_mono_items` is // responsible for initializing the CGU size estimates. - let PlacedMonoItems { mut codegen_units, internalization_candidates, unique_inlined_stats } = { + let PlacedMonoItems { mut codegen_units, internalization_candidates } = { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_items"); let placed = place_mono_items(cx, mono_items); - debug_dump(tcx, "PLACE", &placed.codegen_units, placed.unique_inlined_stats); + debug_dump(tcx, "PLACE", &placed.codegen_units); placed }; @@ -167,7 +163,7 @@ where { let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus"); merge_codegen_units(cx, &mut codegen_units); - debug_dump(tcx, "MERGE", &codegen_units, unique_inlined_stats); + debug_dump(tcx, "MERGE", &codegen_units); } // Make as many symbols "internal" as possible, so LLVM has more freedom to @@ -176,7 +172,7 @@ where let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols"); internalize_symbols(cx, &mut codegen_units, internalization_candidates); - debug_dump(tcx, "INTERNALIZE", &codegen_units, unique_inlined_stats); + debug_dump(tcx, "INTERNALIZE", &codegen_units); } // Mark one CGU for dead code, if necessary. @@ -216,18 +212,12 @@ where let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx); let cgu_name_cache = &mut FxHashMap::default(); - let mut num_unique_inlined_items = 0; - let mut unique_inlined_items_size = 0; for mono_item in mono_items { // Handle only root items directly here. Inlined items are handled at // the bottom of the loop based on reachability. match mono_item.instantiation_mode(cx.tcx) { InstantiationMode::GloballyShared { .. } => {} - InstantiationMode::LocalCopy => { - num_unique_inlined_items += 1; - unique_inlined_items_size += mono_item.size_estimate(cx.tcx); - continue; - } + InstantiationMode::LocalCopy => continue, } let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item); @@ -256,8 +246,9 @@ where if visibility == Visibility::Hidden && can_be_internalized { internalization_candidates.insert(mono_item); } + let size_estimate = mono_item.size_estimate(cx.tcx); - cgu.items_mut().insert(mono_item, (linkage, visibility)); + cgu.items_mut().insert(mono_item, MonoItemData { linkage, visibility, size_estimate }); // Get all inlined items that are reachable from `mono_item` without // going via another root item. This includes drop-glue, functions from @@ -271,7 +262,11 @@ where // the `insert` will be a no-op. for inlined_item in reachable_inlined_items { // This is a CGU-private copy. - cgu.items_mut().insert(inlined_item, (Linkage::Internal, Visibility::Default)); + cgu.items_mut().entry(inlined_item).or_insert_with(|| MonoItemData { + linkage: Linkage::Internal, + visibility: Visibility::Default, + size_estimate: inlined_item.size_estimate(cx.tcx), + }); } } @@ -286,14 +281,10 @@ where codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str())); for cgu in codegen_units.iter_mut() { - cgu.compute_size_estimate(cx.tcx); + cgu.compute_size_estimate(); } - return PlacedMonoItems { - codegen_units, - internalization_candidates, - unique_inlined_stats: (num_unique_inlined_items, unique_inlined_items_size), - }; + return PlacedMonoItems { codegen_units, internalization_candidates }; fn get_reachable_inlined_items<'tcx>( tcx: TyCtxt<'tcx>, @@ -349,7 +340,7 @@ fn merge_codegen_units<'tcx>( && codegen_units.iter().any(|cgu| cgu.size_estimate() < NON_INCR_MIN_CGU_SIZE)) { // Sort small cgus to the back. - codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); + codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate())); let mut smallest = codegen_units.pop().unwrap(); let second_smallest = codegen_units.last_mut().unwrap(); @@ -358,7 +349,7 @@ fn merge_codegen_units<'tcx>( // may be duplicate inlined items, in which case the destination CGU is // unaffected. Recalculate size estimates afterwards. second_smallest.items_mut().extend(smallest.items_mut().drain()); - second_smallest.compute_size_estimate(cx.tcx); + second_smallest.compute_size_estimate(); // Record that `second_smallest` now contains all the stuff that was // in `smallest` before. @@ -492,7 +483,7 @@ fn internalize_symbols<'tcx>( for cgu in codegen_units { let home_cgu = MonoItemPlacement::SingleCgu(cgu.name()); - for (item, linkage_and_visibility) in cgu.items_mut() { + for (item, data) in cgu.items_mut() { if !internalization_candidates.contains(item) { // This item is no candidate for internalizing, so skip it. continue; @@ -520,7 +511,8 @@ fn internalize_symbols<'tcx>( // If we got here, we did not find any uses from other CGUs, so // it's fine to make this monomorphization internal. - *linkage_and_visibility = (Linkage::Internal, Visibility::Default); + data.linkage = Linkage::Internal; + data.visibility = Visibility::Default; } } } @@ -537,7 +529,7 @@ fn mark_code_coverage_dead_code_cgu<'tcx>(codegen_units: &mut [CodegenUnit<'tcx> // function symbols to be included via `-u` or `/include` linker args. let dead_code_cgu = codegen_units .iter_mut() - .filter(|cgu| cgu.items().iter().any(|(_, (linkage, _))| *linkage == Linkage::External)) + .filter(|cgu| cgu.items().iter().any(|(_, data)| data.linkage == Linkage::External)) .min_by_key(|cgu| cgu.size_estimate()); // If there are no CGUs that have externally linked items, then we just @@ -851,12 +843,7 @@ fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibilit } } -fn debug_dump<'a, 'tcx: 'a>( - tcx: TyCtxt<'tcx>, - label: &str, - cgus: &[CodegenUnit<'tcx>], - (unique_inlined_items, unique_inlined_size): (usize, usize), -) { +fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<'tcx>]) { let dump = move || { use std::fmt::Write; @@ -865,28 +852,36 @@ fn debug_dump<'a, 'tcx: 'a>( // Note: every unique root item is placed exactly once, so the number // of unique root items always equals the number of placed root items. + // + // Also, unreached inlined items won't be counted here. This is fine. + + let mut inlined_items = FxHashSet::default(); let mut root_items = 0; - // unique_inlined_items is passed in above. + let mut unique_inlined_items = 0; let mut placed_inlined_items = 0; let mut root_size = 0; - // unique_inlined_size is passed in above. + let mut unique_inlined_size = 0; let mut placed_inlined_size = 0; for cgu in cgus.iter() { num_cgus += 1; all_cgu_sizes.push(cgu.size_estimate()); - for (item, _) in cgu.items() { + for (item, data) in cgu.items() { match item.instantiation_mode(tcx) { InstantiationMode::GloballyShared { .. } => { root_items += 1; - root_size += item.size_estimate(tcx); + root_size += data.size_estimate; } InstantiationMode::LocalCopy => { + if inlined_items.insert(item) { + unique_inlined_items += 1; + unique_inlined_size += data.size_estimate; + } placed_inlined_items += 1; - placed_inlined_size += item.size_estimate(tcx); + placed_inlined_size += data.size_estimate; } } } @@ -928,7 +923,7 @@ fn debug_dump<'a, 'tcx: 'a>( let mean_size = size as f64 / num_items as f64; let mut placed_item_sizes: Vec<_> = - cgu.items().iter().map(|(item, _)| item.size_estimate(tcx)).collect(); + cgu.items().values().map(|data| data.size_estimate).collect(); placed_item_sizes.sort_unstable_by_key(|&n| cmp::Reverse(n)); let sizes = list(&placed_item_sizes); @@ -937,15 +932,16 @@ fn debug_dump<'a, 'tcx: 'a>( let _ = writeln!(s, " - items: {num_items}, mean size: {mean_size:.1}, sizes: {sizes}",); - for (item, linkage) in cgu.items_in_deterministic_order(tcx) { + for (item, data) in cgu.items_in_deterministic_order(tcx) { + let linkage = data.linkage; let symbol_name = item.symbol_name(tcx).name; let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map_or("", |i| &symbol_name[i..]); - let size = item.size_estimate(tcx); let kind = match item.instantiation_mode(tcx) { InstantiationMode::GloballyShared { .. } => "root", InstantiationMode::LocalCopy => "inlined", }; + let size = data.size_estimate; let _ = with_no_trimmed_paths!(writeln!( s, " - {item} [{linkage:?}] [{symbol_hash}] ({kind}, size: {size})" @@ -1100,8 +1096,8 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default(); for cgu in codegen_units { - for (&mono_item, &linkage) in cgu.items() { - item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage)); + for (&mono_item, &data) in cgu.items() { + item_to_cgus.entry(mono_item).or_default().push((cgu.name(), data.linkage)); } } @@ -1114,7 +1110,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); cgus.sort_by_key(|(name, _)| *name); cgus.dedup(); - for &(ref cgu_name, (linkage, _)) in cgus.iter() { + for &(ref cgu_name, linkage) in cgus.iter() { output.push(' '); output.push_str(cgu_name.as_str()); diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9787d98c1a49a..7e1d24817db20 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -286,6 +286,8 @@ parse_if_expression_missing_then_block = this `if` expression is missing a block .add_then_block = add a block here .condition_possibly_unfinished = this binary operation is possibly unfinished +parse_impl_restriction_on_trait_alias = trait aliases cannot be implemented + parse_in_in_typo = expected iterable, found keyword `in` .suggestion = remove the duplicated `in` @@ -309,6 +311,13 @@ parse_inclusive_range_no_end = inclusive range with no end parse_incorrect_braces_trait_bounds = incorrect braces around trait bounds .suggestion = remove 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 @@ -319,13 +328,6 @@ parse_incorrect_use_of_await = .parentheses_suggestion = `await` is not a method call, remove the parentheses .postfix_suggestion = `await` is a postfix operation -parse_incorrect_visibility_restriction = incorrect visibility restriction - .help = some possible visibility restrictions are: - `pub(crate)`: visible only on the current crate - `pub(super)`: visible only in the current module's parent - `pub(in path::to::module)`: visible only on the specified path - .suggestion = make this visible only to module `{$inner_str}` with `in` - parse_inner_attr_explanation = inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files parse_inner_attr_not_permitted = an inner attribute is not permitted in this context .label_does_not_annotate_this = {parse_label_inner_attr_does_not_annotate_this} @@ -668,6 +670,29 @@ parse_require_colon_after_labeled_expression = labeled expression must be follow .label = the label .suggestion = add `:` after the label +# internal use only +parse_restriction_adjective = { $keyword -> + [impl] implementable + [mut] mutable + [pub] visible + *[DEFAULT_IS_BUG] BUG +} + +parse_restriction_missing_path = 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 the current crate + +# internal use only +parse_restriction_noun = { $keyword -> + [impl] impl + [mut] mut + [pub] visibility + *[DEFAULT_IS_BUG] BUG +} + parse_return_types_use_thin_arrow = return types are denoted using `->` .suggestion = use `->` instead diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 96e1c0e3c6d9e..70743cb1ad37b 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -812,13 +812,31 @@ pub(crate) struct MismatchedClosingDelimiter { } #[derive(Diagnostic)] -#[diag(parse_incorrect_visibility_restriction, code = "E0704")] +#[diag(parse_incorrect_restriction, code = "E0704")] #[help] -pub(crate) struct IncorrectVisibilityRestriction { +pub(crate) struct IncorrectRestriction { + #[primary_span] + #[suggestion(code = "in {path}", applicability = "machine-applicable")] + pub span: Span, + pub path: String, + pub keyword: &'static str, +} + +#[derive(Diagnostic)] +#[diag(parse_restriction_missing_path, code = "E0704")] +#[help] +pub(crate) struct RestrictionMissingPath { + #[primary_span] + #[suggestion(code = "{keyword}(crate)", applicability = "maybe-incorrect")] + pub span: Span, + pub keyword: &'static str, +} + +#[derive(Diagnostic)] +#[diag(parse_impl_restriction_on_trait_alias)] +pub(crate) struct ImplRestrictionOnTraitAlias { #[primary_span] - #[suggestion(code = "in {inner_str}", applicability = "machine-applicable")] pub span: Span, - pub inner_str: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1470180dea714..d321bbaa75d36 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -239,7 +239,7 @@ impl<'a> Parser<'a> { let (ident, ty, expr) = self.parse_item_global(None)?; (ident, ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ty, expr }))) } - } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { + } else if self.check_keyword(kw::Trait) || self.is_trait_item_with_modifiers() { // TRAIT ITEM self.parse_item_trait(attrs, lo)? } else if self.check_keyword(kw::Impl) @@ -344,7 +344,7 @@ impl<'a> Parser<'a> { /// When parsing a statement, would the start of a path be an item? 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.check_auto_or_unsafe_trait_item() // no: `auto::b`, yes: `auto trait X { .. }` + || self.is_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` } @@ -789,16 +789,49 @@ 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(kw::Auto) && self.is_keyword_ahead(1, &[kw::Trait]) - // unsafe auto trait - || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) + /// Is this item an unsafe trait, an auto trait, have an impl restriction, + /// or any combination thereof? + fn is_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.tree_cursor.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; + } + 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; + } + + (has_impl_restriction || has_unsafe || has_auto) && has_trait } - /// 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, ItemInfo> { + let impl_restriction = self.parse_restriction(FollowedByType::No)?; let unsafety = self.parse_unsafety(Case::Sensitive); // Parse optional `auto` prefix. let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No }; @@ -825,6 +858,9 @@ impl<'a> Parser<'a> { self.expect_semi()?; let whole_span = lo.to(self.prev_token.span); + if !matches!(impl_restriction.level, RestrictionLevel::Implied) { + self.sess.emit_err(errors::ImplRestrictionOnTraitAlias { span: whole_span }); + } if is_auto == IsAuto::Yes { self.sess.emit_err(errors::TraitAliasCannotBeAuto { span: whole_span }); } @@ -841,7 +877,14 @@ impl<'a> Parser<'a> { let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; Ok(( ident, - ItemKind::Trait(Box::new(Trait { is_auto, unsafety, generics, bounds, items })), + ItemKind::Trait(Box::new(Trait { + impl_restriction, + is_auto, + unsafety, + generics, + bounds, + items, + })), )) } } @@ -1544,6 +1587,15 @@ impl<'a> Parser<'a> { return Err(err); } }; + let mut_restriction = match p.parse_restriction(FollowedByType::Yes) { + Ok(mut_restriction) => mut_restriction, + Err(err) => { + if let Some(ref mut snapshot) = snapshot { + snapshot.recover_diff_marker(); + } + return Err(err); + } + }; let ty = match p.parse_ty() { Ok(ty) => ty, Err(err) => { @@ -1558,6 +1610,7 @@ impl<'a> Parser<'a> { FieldDef { span: lo.to(ty.span), vis, + mut_restriction, ident: None, id: DUMMY_NODE_ID, ty, @@ -1579,7 +1632,11 @@ impl<'a> Parser<'a> { self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; let vis = this.parse_visibility(FollowedByType::No)?; - Ok((this.parse_single_struct_field(adt_ty, lo, vis, attrs)?, TrailingToken::None)) + let mut_restriction = this.parse_restriction(FollowedByType::No)?; + Ok(( + this.parse_single_struct_field(adt_ty, lo, vis, mut_restriction, attrs)?, + TrailingToken::None, + )) }) } @@ -1589,10 +1646,11 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + mut_restriction: MutRestriction, attrs: AttrVec, ) -> PResult<'a, FieldDef> { let mut seen_comma: bool = false; - let a_var = self.parse_name_and_ty(adt_ty, lo, vis, attrs)?; + let a_var = self.parse_name_and_ty(adt_ty, lo, vis, mut_restriction, attrs)?; if self.token == token::Comma { seen_comma = true; } @@ -1728,6 +1786,7 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + mut_restriction: MutRestriction, attrs: AttrVec, ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; @@ -1746,6 +1805,7 @@ impl<'a> Parser<'a> { span: lo.to(self.prev_token.span), ident: Some(name), vis, + mut_restriction, id: DUMMY_NODE_ID, ty, attrs, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index e4d843b7c8bcc..51e60fe0e34c2 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -26,7 +26,8 @@ use rustc_ast::AttrId; use rustc_ast::DUMMY_NODE_ID; use rustc_ast::{self as ast, AnonConst, AttrStyle, Const, DelimArgs, Extern}; use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, MacDelimiter, Mutability, StrLit}; -use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind}; +use rustc_ast::{HasAttrs, HasTokens, Unsafe}; +use rustc_ast::{Restriction, RestrictionKind, Visibility}; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Ordering; @@ -43,7 +44,8 @@ use thin_vec::ThinVec; use tracing::debug; use crate::errors::{ - self, IncorrectVisibilityRestriction, MismatchedClosingDelimiter, NonStringAbiLiteral, + self, IncorrectRestriction, MismatchedClosingDelimiter, NonStringAbiLiteral, + RestrictionMissingPath, }; bitflags::bitflags! { @@ -1384,76 +1386,82 @@ impl<'a> Parser<'a> { // Public for rustfmt usage. pub fn parse_visibility(&mut self, fbt: FollowedByType) -> PResult<'a, Visibility> { maybe_whole!(self, NtVis, |x| x.into_inner()); + self.parse_restriction(fbt).map(Into::into) + } - if !self.eat_keyword(kw::Pub) { - // We need a span for our `Spanned`, but there's inherently no - // keyword to grab a span from for inherited visibility; an empty span at the - // beginning of the current token would seem to be the "Schelling span". - return Ok(Visibility { - span: self.token.span.shrink_to_lo(), - kind: VisibilityKind::Inherited, - tokens: None, - }); + /// Parses `kw`, `kw(in path)`, and 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, + fbt: FollowedByType, + ) -> PResult<'a, Restriction> { + if !self.eat_keyword(Kind::KEYWORD_SYM) { + // 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 lo = self.prev_token.span; + + let gate = |span| { + if let Some(feature_gate) = Kind::FEATURE_GATE { + self.sess.gated_spans.gate(feature_gate, span); + } + span + }; + + let kw_span = self.prev_token.span; if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { // 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(pub (), pub (usize));`. - // Because of this, we only `bump` the `(` if we're assured it is appropriate to do so + // 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 `pub(in path)`. + // Parse `kw(in path)`. self.bump(); // `(` self.bump(); // `in` let path = self.parse_path(PathStyle::Mod)?; // `path` self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` - let vis = VisibilityKind::Restricted { - path: P(path), - id: ast::DUMMY_NODE_ID, - shorthand: false, - }; - return Ok(Visibility { - span: lo.to(self.prev_token.span), - kind: vis, - tokens: None, - }); + return Ok(Restriction::restricted(P(path), ast::DUMMY_NODE_ID, false) + .with_span(gate(kw_span.to(self.prev_token.span)))); } else if self.look_ahead(2, |t| t == &token::CloseDelim(Delimiter::Parenthesis)) && self.is_keyword_ahead(1, &[kw::Crate, kw::Super, kw::SelfLower]) { - // Parse `pub(crate)`, `pub(self)`, or `pub(super)`. + // Parse `kw(crate)`, `kw(self)`, or `kw(super)`. self.bump(); // `(` let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` - let vis = VisibilityKind::Restricted { - path: P(path), - id: ast::DUMMY_NODE_ID, - shorthand: true, - }; - return Ok(Visibility { - span: lo.to(self.prev_token.span), - kind: vis, - tokens: None, - }); - } 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_vis_restriction()?; - // Emit diagnostic, but continue with public visibility. + return Ok(Restriction::restricted(P(path), ast::DUMMY_NODE_ID, true) + .with_span(gate(kw_span.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::()?; + // Emit diagnostic, but continue with no restriction. + } + Ok(Restriction::unrestricted().with_span(gate(kw_span))) + } + } else { + if Kind::REQUIRES_EXPLICIT_PATH { + self.sess + .emit_err(RestrictionMissingPath { span: kw_span, keyword: Kind::KEYWORD_STR }); } + Ok(Restriction::unrestricted().with_span(gate(kw_span))) } - - Ok(Visibility { span: lo, kind: VisibilityKind::Public, tokens: None }) } - /// Recovery for e.g. `pub(something) fn ...` or `struct X { pub(something) y: Z }` - fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> { + /// Recovery for e.g. `kw(something) fn ...` or `struct X { kw(something) y: Z }` + fn recover_incorrect_restriction(&mut self) -> PResult<'a, ()> { self.bump(); // `(` let path = self.parse_path(PathStyle::Mod)?; self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)` - let path_str = pprust::path_to_string(&path); - self.sess.emit_err(IncorrectVisibilityRestriction { span: path.span, inner_str: path_str }); + self.sess.emit_err(IncorrectRestriction { + span: path.span, + path: pprust::path_to_string(&path), + keyword: Kind::KEYWORD_STR, + }); Ok(()) } diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index f98918cba8800..a076a8f27fec0 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -221,6 +221,18 @@ resolve_relative_2018 = resolve_remove_surrounding_derive = remove from the surrounding `derive()` +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 ff63e4e33cb9d..45fc23828cd5a 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -12,7 +12,8 @@ use crate::Namespace::{self, MacroNS, TypeNS, ValueNS}; use crate::{errors, BindingKey, MacroData, NameBindingData}; use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot}; use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError}; -use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError}; +use crate::{Resolver, ResolverArenas, Segment, ToNameBinding}; +use crate::{RestrictionResolutionError, VisResolutionError}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind}; @@ -324,6 +325,111 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r.field_def_ids.insert(def_id, self.r.tcx.arena.alloc_from_iter(def_ids)); } + fn resolve_restriction< + AstKind: ast::RestrictionKind, + const MIDDLE_KIND: ty::RestrictionKind, + >( + &mut self, + restriction: &ast::Restriction, + ) -> ty::Restriction<{ MIDDLE_KIND }> { + const { assert!(AstKind::MIDDLE_KIND == MIDDLE_KIND) }; + + self.try_resolve_restriction(restriction, true).unwrap_or_else(|err| { + self.r.report_restriction_error(err); + ty::Restriction::Unrestricted + }) + } + + fn try_resolve_restriction< + 'ast, + AstKind: ast::RestrictionKind, + const MIDDLE_KIND: ty::RestrictionKind, + >( + &mut self, + restriction: &'ast ast::Restriction, + finalize: bool, + ) -> Result, RestrictionResolutionError<'ast>> { + const { assert!(AstKind::MIDDLE_KIND == MIDDLE_KIND) }; + + let parent_scope = &self.parent_scope; + match restriction.level { + ast::RestrictionLevel::Unrestricted => Ok(ty::Restriction::Unrestricted), + // FIXME(jhpratt) create an "implied" level in rustc_middle. This will be useful for + // lints in addition to conversion to visibility. + // If the restriction is implied, it has no effect when the item is otherwise visible. + ast::RestrictionLevel::Implied => Ok(ty::Restriction::Unrestricted), + ast::RestrictionLevel::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, + ) { + 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, vdata: &ast::VariantData) { let field_vis = vdata .fields() @@ -702,7 +808,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); } - ItemKind::Enum(_, _) => { + ItemKind::Enum(..) => { let module = self.r.new_module( Some(parent), ModuleKind::Def(DefKind::Enum, def_id, ident.name), @@ -779,7 +885,10 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { self.insert_field_visibilities_local(def_id, vdata); } - ItemKind::Trait(..) => { + ItemKind::Trait(ref trait_def) => { + let impl_restriction = self.resolve_restriction(&trait_def.impl_restriction); + self.r.impl_restrictions.insert(local_def_id, impl_restriction); + // Add all the items within to a new module. let module = self.r.new_module( Some(parent), @@ -1452,6 +1561,8 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> { } else { let vis = self.resolve_visibility(&sf.vis); self.r.visibilities.insert(self.r.local_def_id(sf.id), vis); + let mut_restriction = self.resolve_restriction(&sf.mut_restriction); + self.r.mut_restrictions.insert(self.r.local_def_id(sf.id), mut_restriction); visit::walk_field_def(self, sf); } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index d3dcdfa427542..39639b966bd7c 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -38,7 +38,10 @@ use crate::path_names_to_string; use crate::{errors as errs, BindingKey}; use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, Finalize}; use crate::{HasGenericParams, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot}; -use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, VisResolutionError}; +use crate::{ + LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, RestrictionResolutionError, + VisResolutionError, +}; use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet}; use crate::{Segment, UseError}; @@ -999,6 +1002,46 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .emit() } + pub(crate) fn report_restriction_error( + &mut self, + vis_resolution_error: RestrictionResolutionError<'_>, + ) -> ErrorGuaranteed { + match vis_resolution_error { + RestrictionResolutionError::Relative2018(span, path) => { + self.tcx.sess.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.tcx.sess.create_err(errs::RestrictionAncestorOnly(span)) + } + RestrictionResolutionError::FailedToResolve(span, label, suggestion) => self + .into_struct_error( + span, + ResolutionError::FailedToResolve { + last_segment: None, + label, + suggestion, + module: None, + }, + ), + RestrictionResolutionError::ExpectedFound(span, path_str, res) => { + self.tcx.sess.create_err(errs::ExpectedFound { span, res, path_str }) + } + RestrictionResolutionError::Indeterminate(span) => { + self.tcx.sess.create_err(errs::RestrictionIndeterminate(span)) + } + RestrictionResolutionError::ModuleOnly(span) => { + self.tcx.sess.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 e4b89c65853d0..0a2edbf9bcea1 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -727,3 +727,25 @@ pub(crate) struct IsNotDirectlyImportable { pub(crate) span: Span, pub(crate) target: Ident, } + +#[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 faa672db59ccf..8865ca5e1cd12 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -11,6 +11,7 @@ #![feature(box_patterns)] #![feature(extract_if)] #![feature(if_let_guard)] +#![feature(inline_const)] #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] @@ -275,6 +276,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)] @@ -971,6 +981,10 @@ pub struct Resolver<'a, 'tcx> { /// Visibilities in "lowered" form, for all entities that have them. visibilities: FxHashMap, has_pub_restricted: bool, + /// trait def -> restriction scope + impl_restrictions: FxHashMap, + /// field def -> restriction scope + mut_restrictions: FxHashMap, used_imports: FxHashSet, maybe_unused_trait_imports: FxIndexSet, @@ -1309,6 +1323,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { glob_map: Default::default(), visibilities, + impl_restrictions: FxHashMap::default(), + mut_restrictions: FxHashMap::default(), has_pub_restricted: false, used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), @@ -1424,6 +1440,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect(); let expn_that_defined = self.expn_that_defined; let visibilities = self.visibilities; + let impl_restrictions = self.impl_restrictions; + let mut_restrictions = self.mut_restrictions; let has_pub_restricted = self.has_pub_restricted; let extern_crate_map = self.extern_crate_map; let maybe_unused_trait_imports = self.maybe_unused_trait_imports; @@ -1442,6 +1460,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let global_ctxt = ResolverGlobalCtxt { expn_that_defined, visibilities, + impl_restrictions, + mut_restrictions, has_pub_restricted, effective_visibilities, extern_crate_map, diff --git a/compiler/rustc_restrictions/Cargo.toml b/compiler/rustc_restrictions/Cargo.toml new file mode 100644 index 0000000000000..6bf7445ba7d8e --- /dev/null +++ b/compiler/rustc_restrictions/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rustc_restrictions" +version = "0.0.0" +edition = "2021" + +[dependencies] +rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_macros = { path = "../rustc_macros" } +rustc_middle = { path = "../rustc_middle" } +rustc_session = { path = "../rustc_session" } +rustc_span = { path = "../rustc_span" } +tracing = "0.1" diff --git a/compiler/rustc_restrictions/messages.ftl b/compiler/rustc_restrictions/messages.ftl new file mode 100644 index 0000000000000..eb54df1cec093 --- /dev/null +++ b/compiler/rustc_restrictions/messages.ftl @@ -0,0 +1,11 @@ +restrictions_construction_of_ty_with_mut_restricted_field = + `{$name}` cannot be constructed using {$article} {$description} expression outside `{$restriction_path}` + .note = {$article} {$description} containing fields with a mutability restriction cannot be constructed using {$article} {$description} expression + .label = mutability restricted here +restrictions_impl_of_restricted_trait = + trait cannot be implemented outside `{$restriction_path}` + .label = trait restricted here + +restrictions_mut_of_restricted_field = + field cannot be mutated outside `{$restriction_path}` + .label = mutability restricted here diff --git a/compiler/rustc_restrictions/src/errors.rs b/compiler/rustc_restrictions/src/errors.rs new file mode 100644 index 0000000000000..24e3a74682c77 --- /dev/null +++ b/compiler/rustc_restrictions/src/errors.rs @@ -0,0 +1,37 @@ +use rustc_macros::Diagnostic; +use rustc_span::Span; + +#[derive(Diagnostic)] +#[diag(restrictions_impl_of_restricted_trait)] +pub struct ImplOfRestrictedTrait { + #[primary_span] + pub impl_span: Span, + #[label] + pub restriction_span: Span, + pub restriction_path: String, +} + +#[derive(Diagnostic)] +#[diag(restrictions_mut_of_restricted_field)] +pub struct MutOfRestrictedField { + #[primary_span] + pub mut_span: Span, + #[label] + pub restriction_span: Span, + pub restriction_path: String, +} + +#[derive(Diagnostic)] +#[diag(restrictions_construction_of_ty_with_mut_restricted_field)] +pub struct ConstructionOfTyWithMutRestrictedField { + #[primary_span] + pub construction_span: Span, + #[label] + pub restriction_span: Span, + pub restriction_path: String, + #[note] + pub note: (), + pub article: &'static str, + pub description: &'static str, + pub name: String, +} diff --git a/compiler/rustc_restrictions/src/impl_restriction.rs b/compiler/rustc_restrictions/src/impl_restriction.rs new file mode 100644 index 0000000000000..7bd4bf3502f6f --- /dev/null +++ b/compiler/rustc_restrictions/src/impl_restriction.rs @@ -0,0 +1,55 @@ +use rustc_hir::def_id::LocalDefId; +use rustc_hir::{self as hir, intravisit::Visitor}; +use rustc_middle::bug; +use rustc_middle::query::Providers; +use rustc_middle::ty::{self, TyCtxt}; + +use crate::errors; + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { impl_restriction, check_impl_restriction, ..*providers }; +} + +struct ImplOfRestrictedTraitVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'v> Visitor<'v> for ImplOfRestrictedTraitVisitor<'v> { + type NestedFilter = rustc_middle::hir::nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_item(&mut self, item: &'v hir::Item<'v>) { + if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(trait_ref), .. }) = &item.kind { + let trait_def_id = trait_ref.trait_def_id().expect("item is known to be a trait"); + + let restriction = self.tcx.impl_restriction(trait_def_id); + + if !restriction.is_allowed_in(item.owner_id.to_def_id(), self.tcx) { + let impl_span = + self.tcx.span_of_impl(item.owner_id.to_def_id()).expect("impl should be local"); + let restriction_span = restriction.span(); + let restriction_path = + restriction.restriction_path(self.tcx, hir::def_id::LOCAL_CRATE); + let diag = + errors::ImplOfRestrictedTrait { impl_span, restriction_span, restriction_path }; + self.tcx.sess.emit_err(diag); + } + } + + hir::intravisit::walk_item(self, item) + } +} + +fn impl_restriction(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::ImplRestriction { + match tcx.resolutions(()).impl_restrictions.get(&def_id) { + Some(restriction) => *restriction, + None => bug!("impl restriction not found for {def_id:?}"), + } +} + +fn check_impl_restriction(tcx: TyCtxt<'_>, _: ()) { + tcx.hir().walk_toplevel_module(&mut ImplOfRestrictedTraitVisitor { tcx }); +} diff --git a/compiler/rustc_restrictions/src/lib.rs b/compiler/rustc_restrictions/src/lib.rs new file mode 100644 index 0000000000000..7f9eb7051471d --- /dev/null +++ b/compiler/rustc_restrictions/src/lib.rs @@ -0,0 +1,19 @@ +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] +#![feature(box_patterns)] + +mod errors; +mod impl_restriction; +mod mut_restriction; + +use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; +use rustc_fluent_macro::fluent_messages; +use rustc_middle::query::Providers; + +fluent_messages! { "../messages.ftl" } + +pub fn provide(providers: &mut Providers) { + impl_restriction::provide(providers); + mut_restriction::provide(providers); +} diff --git a/compiler/rustc_restrictions/src/mut_restriction.rs b/compiler/rustc_restrictions/src/mut_restriction.rs new file mode 100644 index 0000000000000..661feef4099f3 --- /dev/null +++ b/compiler/rustc_restrictions/src/mut_restriction.rs @@ -0,0 +1,133 @@ +use rustc_hir::def::Res; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::Node; +use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::{AggregateKind, Rvalue}; +use rustc_middle::mir::{Body, Location, Place, ProjectionElem, Statement, Terminator}; +use rustc_middle::query::Providers; +use rustc_middle::span_bug; +use rustc_middle::ty::{MutRestriction, Restriction, TyCtxt}; +use rustc_span::Span; + +use crate::errors; + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { + mut_restriction, + check_mut_restriction, + adt_expression_restriction, + ..*providers + }; +} + +fn mut_restriction(tcx: TyCtxt<'_>, def_id: LocalDefId) -> MutRestriction { + tracing::debug!("mut_restriction({def_id:?})"); + + match tcx.resolutions(()).mut_restrictions.get(&def_id) { + Some(restriction) => *restriction, + None => span_bug!(tcx.def_span(def_id), "mut restriction not found for {def_id:?}"), + } +} + +fn check_mut_restriction(tcx: TyCtxt<'_>, def_id: LocalDefId) { + tracing::debug!("check_mut_restriction({def_id:?})"); + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + // FIXME(jhpratt) Does this need to be handled somehow? + if matches!(tcx.hir().get(hir_id), Node::AnonConst(_)) { + return; + } + + let body = &tcx.mir_built(def_id).borrow(); + let mut checker = MutRestrictionChecker { tcx, body, span: body.span }; + checker.visit_body(body); +} + +/// Obtain the restriction on ADT expressions. This occurs when an ADT field has its mutability +/// restricted. +// This is a query to allow the compiler to cache the output. This avoids the need to recompute the +// same information for every ADT expression. +fn adt_expression_restriction(tcx: TyCtxt<'_>, variant_def_id: DefId) -> MutRestriction { + let res = Res::Def(tcx.def_kind(variant_def_id), variant_def_id); + let variant = tcx.expect_variant_res(res); + + Restriction::strictest_of( + variant.fields.iter().map(|field| tcx.mut_restriction(field.did)), + tcx, + ) +} + +struct MutRestrictionChecker<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, + span: Span, +} + +impl<'tcx> Visitor<'tcx> for MutRestrictionChecker<'_, 'tcx> { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + self.span = terminator.source_info.span; + self.super_terminator(terminator, location); + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + self.span = statement.source_info.span; + self.super_statement(statement, location); + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) { + if context.is_mutating_use() { + let body_did = self.body.source.instance.def_id(); + + for (place_base, elem) in place.iter_projections() { + match elem { + ProjectionElem::Field(field, _ty) => { + let field_ty = place_base.ty(self.body, self.tcx); + if !field_ty.ty.is_adt() { + continue; + } + let field_def = field_ty.field_def(field); + let field_mut_restriction = self.tcx.mut_restriction(field_def.did); + + if !field_mut_restriction.is_allowed_in(body_did, self.tcx) { + self.tcx.sess.emit_err(errors::MutOfRestrictedField { + mut_span: self.span, + restriction_span: field_mut_restriction.span(), + restriction_path: field_mut_restriction + .restriction_path(self.tcx, body_did.krate), + }); + } + } + _ => {} + } + } + } + + self.super_place(place, context, location) + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + if let Rvalue::Aggregate(box AggregateKind::Adt(def_id, variant_idx, _, _, _), _) = rvalue { + let adt_def = self.tcx.type_of(def_id).skip_binder().ty_adt_def().unwrap(); + let variant = adt_def.variant(*variant_idx); + + let construction_restriction = self.tcx.adt_expression_restriction(variant.def_id); + + let body_did = self.body.source.instance.def_id(); + if !construction_restriction.is_allowed_in(body_did, self.tcx) { + self.tcx.sess.emit_err(errors::ConstructionOfTyWithMutRestrictedField { + construction_span: self.span, + restriction_span: construction_restriction.span(), + restriction_path: construction_restriction + .restriction_path(self.tcx, body_did.krate), + note: (), + article: "a", + description: adt_def.variant_descr(), + name: variant.name.to_ident_string(), + }); + } + } + + self.super_rvalue(rvalue, location); + } +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4fc440ef94702..c6e2a2541fdc6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -814,6 +814,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, @@ -978,6 +979,7 @@ symbols! { multiple_supertrait_upcastable, must_not_suspend, must_use, + mut_restriction, naked, naked_functions, name, diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index c228938126e57..f6302113e2a25 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -311,22 +311,6 @@ fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamE tcx.param_env(def_id).with_reveal_all_normalized(tcx) } -fn instance_def_size_estimate<'tcx>( - tcx: TyCtxt<'tcx>, - instance_def: ty::InstanceDef<'tcx>, -) -> usize { - use ty::InstanceDef; - - match instance_def { - InstanceDef::Item(..) | InstanceDef::DropGlue(..) => { - let mir = tcx.instance_mir(instance_def); - mir.basic_blocks.iter().map(|bb| bb.statements.len() + 1).sum() - } - // Estimate the size of other compiler-generated shims to be 1. - _ => 1, - } -} - /// If `def_id` is an issue 33140 hack impl, returns its self type; otherwise, returns `None`. /// /// See [`ty::ImplOverlapKind::Issue33140`] for more details. @@ -432,7 +416,6 @@ pub fn provide(providers: &mut Providers) { adt_sized_constraint, param_env, param_env_reveal_all_normalized, - instance_def_size_estimate, issue33140_self_ty, defaultness, unsizing_params_for_adt, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 8cc01f1ef9740..52549f72c01ed 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -366,6 +366,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, unsafety: lu, generics: lg, @@ -373,6 +374,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { items: li, }), Trait(box ast::Trait { + impl_restriction: _, is_auto: ra, unsafety: ru, generics: rg, diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index d5bc38303e004..84f89210e9a0e 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1120,6 +1120,7 @@ pub(crate) fn format_trait( ) -> Option { if let ast::ItemKind::Trait(trait_kind) = &item.kind { let ast::Trait { + ref impl_restriction, is_auto, unsafety, ref generics, @@ -1128,8 +1129,9 @@ pub(crate) fn format_trait( } = **trait_kind; let mut result = String::with_capacity(128); let header = format!( - "{}{}{}trait ", + "{}{}{}{}trait ", format_visibility(context, &item.vis), + format_restriction(context, &impl_restriction), format_unsafety(unsafety), format_auto(is_auto), ); @@ -1826,15 +1828,15 @@ pub(crate) fn rewrite_struct_field_prefix( field: &ast::FieldDef, ) -> Option { let vis = format_visibility(context, &field.vis); + let mut_restriction = format_restriction(context, &field.mut_restriction); let type_annotation_spacing = type_annotation_spacing(context.config); Some(match field.ident { Some(name) => format!( - "{}{}{}:", - vis, + "{vis}{mut_restriction}{}{}:", rewrite_ident(context, name), type_annotation_spacing.0 ), - None => vis.to_string(), + None => format!("{vis}{mut_restriction}"), }) } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 890a05b8c8259..d4c461a1748ce 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use rustc_ast::ast::{ - self, Attribute, MetaItem, MetaItemKind, NestedMetaItem, NodeId, Path, Visibility, - VisibilityKind, + self, Attribute, MetaItem, MetaItemKind, NestedMetaItem, NodeId, Path, Restriction, + RestrictionLevel, Visibility, VisibilityKind, }; use rustc_ast::ptr; use rustc_ast_pretty::pprust; @@ -74,6 +74,35 @@ pub(crate) fn format_visibility( } } +// Does not allocate in the common, implied case. +pub(crate) fn format_restriction( + context: &RewriteContext<'_>, + restriction: &Restriction, +) -> Cow<'static, str> { + match restriction.level { + RestrictionLevel::Unrestricted => Kind::KEYWORD_STR.into(), + RestrictionLevel::Restricted { + ref path, + id: _, + shorthand, + } => { + let kw = Kind::KEYWORD_STR; + 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}) ").into() + } + RestrictionLevel::Implied => "".into(), + } +} + #[inline] pub(crate) fn format_async(is_async: &ast::Async) -> &'static str { match is_async { diff --git a/src/tools/rustfmt/tests/source/restriction.rs b/src/tools/rustfmt/tests/source/restriction.rs new file mode 100644 index 0000000000000..58d266a425291 --- /dev/null +++ b/src/tools/rustfmt/tests/source/restriction.rs @@ -0,0 +1,70 @@ +pub +impl(crate) +trait Foo {} + +pub +impl ( in foo +:: +bar ) +trait Baz {} + +struct FooS { + pub + mut(crate) + field: (), +} + +struct BazS { + pub + mut ( in foo + :: + bar ) + field: (), +} + +struct FooS2( + pub + mut(crate) + (), +); + +struct BazS2( + pub + mut ( in foo + :: + bar ) + (), +); + +enum Enum { + Foo { + mut(crate) + field: (), + }, + Baz { + mut ( in foo + :: + bar ) + field: (), + }, + FooT( + mut(crate) + (), + ), + BazT( + mut ( in foo + :: + bar ) + (), + ), + +} + +union Union { + mut(crate) + field1: (), + mut ( in foo + :: + bar ) + field3: (), +} diff --git a/src/tools/rustfmt/tests/target/restriction.rs b/src/tools/rustfmt/tests/target/restriction.rs new file mode 100644 index 0000000000000..a64a9b5bbef6e --- /dev/null +++ b/src/tools/rustfmt/tests/target/restriction.rs @@ -0,0 +1,27 @@ +pub impl(crate) trait Foo {} + +pub impl(in foo::bar) trait Baz {} + +struct FooS { + pub mut(crate) field: (), +} + +struct BazS { + pub mut(in foo::bar) field: (), +} + +struct FooS2(pub mut(crate) ()); + +struct BazS2(pub mut(in foo::bar) ()); + +enum Enum { + Foo { mut(crate) field: () }, + Baz { mut(in foo::bar) field: () }, + FooT(mut(crate) ()), + BazT(mut(in foo::bar) ()), +} + +union Union { + mut(crate) field1: (), + mut(in foo::bar) field3: (), +} diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index c3a639528413b..a8bf097751636 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -9,9 +9,8 @@ use std::fs; use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; -// FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1894; -const ROOT_ENTRY_LIMIT: usize = 870; +const ROOT_ENTRY_LIMIT: usize = 871; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index 816f99baa8495..ef0029d4d2968 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -882,3 +882,23 @@ fn test_vis() { } assert_eq!(stringify_inherited_vis!(struct), ""); } + +#[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 {}"); +} + +#[test] +fn test_mut_restriction() { + assert_eq!(stringify!(pub mut(crate) struct Foo {}), "pub mut(crate) struct Foo {}"); + assert_eq!(stringify!(pub mut(in crate) struct Foo {}), "pub mut(in crate) struct Foo {}"); + assert_eq!(stringify!(pub mut(super) struct Foo {}), "pub mut(super) struct Foo {}"); + assert_eq!(stringify!(pub mut(in super) struct Foo {}), "pub mut(in super) struct Foo {}"); + assert_eq!(stringify!(pub mut(self) struct Foo {}), "pub mut(self) struct Foo {}"); + assert_eq!(stringify!(pub mut(in self) struct Foo {}), "pub mut(in self) struct Foo {}"); +} diff --git a/tests/ui/pub/pub-restricted.stderr b/tests/ui/pub/pub-restricted.stderr index 4694530e54863..390b0d593455a 100644 --- a/tests/ui/pub/pub-restricted.stderr +++ b/tests/ui/pub/pub-restricted.stderr @@ -5,9 +5,9 @@ LL | pub (a) fn afn() {} | ^ help: make this visible only to module `a` with `in`: `in a` | = help: some possible visibility restrictions are: - `pub(crate)`: visible only on the current crate + `pub(crate)`: visible only in the current crate `pub(super)`: visible only in the current module's parent - `pub(in path::to::module)`: visible only on the specified path + `pub(in path::to::module)`: visible only in the specified path error[E0704]: incorrect visibility restriction --> $DIR/pub-restricted.rs:4:6 @@ -16,9 +16,9 @@ LL | pub (b) fn bfn() {} | ^ help: make this visible only to module `b` with `in`: `in b` | = help: some possible visibility restrictions are: - `pub(crate)`: visible only on the current crate + `pub(crate)`: visible only in the current crate `pub(super)`: visible only in the current module's parent - `pub(in path::to::module)`: visible only on the specified path + `pub(in path::to::module)`: visible only in the specified path error[E0704]: incorrect visibility restriction --> $DIR/pub-restricted.rs:5:6 @@ -27,9 +27,9 @@ LL | pub (crate::a) fn cfn() {} | ^^^^^^^^ help: make this visible only to module `crate::a` with `in`: `in crate::a` | = help: some possible visibility restrictions are: - `pub(crate)`: visible only on the current crate + `pub(crate)`: visible only in the current crate `pub(super)`: visible only in the current module's parent - `pub(in path::to::module)`: visible only on the specified path + `pub(in path::to::module)`: visible only in the specified path error[E0704]: incorrect visibility restriction --> $DIR/pub-restricted.rs:22:14 @@ -38,9 +38,9 @@ LL | pub (a) invalid: usize, | ^ help: make this visible only to module `a` with `in`: `in a` | = help: some possible visibility restrictions are: - `pub(crate)`: visible only on the current crate + `pub(crate)`: visible only in the current crate `pub(super)`: visible only in the current module's parent - `pub(in path::to::module)`: visible only on the specified path + `pub(in path::to::module)`: visible only in the specified path error[E0704]: incorrect visibility restriction --> $DIR/pub-restricted.rs:31:6 @@ -49,9 +49,9 @@ LL | pub (xyz) fn xyz() {} | ^^^ help: make this visible only to module `xyz` with `in`: `in xyz` | = help: some possible visibility restrictions are: - `pub(crate)`: visible only on the current crate + `pub(crate)`: visible only in the current crate `pub(super)`: visible only in the current module's parent - `pub(in path::to::module)`: visible only on the specified path + `pub(in path::to::module)`: visible only in the specified path error[E0742]: visibilities can only be restricted to ancestor modules --> $DIR/pub-restricted.rs:23:17 diff --git a/tests/ui/restrictions/auxiliary/external-impl-restriction.rs b/tests/ui/restrictions/auxiliary/external-impl-restriction.rs new file mode 100644 index 0000000000000..fa3814add9d38 --- /dev/null +++ b/tests/ui/restrictions/auxiliary/external-impl-restriction.rs @@ -0,0 +1,7 @@ +#![feature(impl_restriction)] + +pub impl(crate) trait TopLevel {} + +pub mod inner { + pub impl(self) trait Inner {} +} diff --git a/tests/ui/restrictions/auxiliary/external-mut-restriction.rs b/tests/ui/restrictions/auxiliary/external-mut-restriction.rs new file mode 100644 index 0000000000000..43406810352ac --- /dev/null +++ b/tests/ui/restrictions/auxiliary/external-mut-restriction.rs @@ -0,0 +1,29 @@ +#![feature(mut_restriction)] + +#[derive(Default)] +pub struct TopLevelStruct { + pub mut(self) field: u8, +} + +#[derive(Default)] +pub enum TopLevelEnum { + #[default] + Default, + A(mut(self) u8), + B { mut(self) field: u8 }, +} + +pub mod inner { + #[derive(Default)] + pub struct InnerStruct { + pub mut(self) field: u8, + } + + #[derive(Default)] + pub enum InnerEnum { + #[default] + Default, + A(mut(self) u8), + B { mut(self) field: u8 }, + } +} diff --git a/tests/ui/restrictions/impl-feature-gate.rs b/tests/ui/restrictions/impl-feature-gate.rs new file mode 100644 index 0000000000000..439d754b7d41e --- /dev/null +++ b/tests/ui/restrictions/impl-feature-gate.rs @@ -0,0 +1,12 @@ +// gate-test-impl_restriction +// revisions: with_gate without_gate +//[with_gate] check-pass + +#![crate_type = "lib"] +#![cfg_attr(with_gate, feature(impl_restriction))] + +pub impl(crate) trait Bar {} //[without_gate]~ ERROR + +mod foo { + pub impl(in foo) trait Baz {} //[without_gate]~ ERROR +} diff --git a/tests/ui/restrictions/impl-feature-gate.without_gate.stderr b/tests/ui/restrictions/impl-feature-gate.without_gate.stderr new file mode 100644 index 0000000000000..f4924d8c71ba5 --- /dev/null +++ b/tests/ui/restrictions/impl-feature-gate.without_gate.stderr @@ -0,0 +1,21 @@ +error[E0658]: restrictions are experimental + --> $DIR/impl-feature-gate.rs:8: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 + +error[E0658]: restrictions are experimental + --> $DIR/impl-feature-gate.rs:11:9 + | +LL | pub impl(in foo) trait Baz {} + | ^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(impl_restriction)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/restrictions/impl-restriction.rs b/tests/ui/restrictions/impl-restriction.rs new file mode 100644 index 0000000000000..391dcf627d87d --- /dev/null +++ b/tests/ui/restrictions/impl-restriction.rs @@ -0,0 +1,21 @@ +// aux-build: external-impl-restriction.rs + +#![crate_type = "lib"] +#![feature(impl_restriction)] + +extern crate external_impl_restriction as external; + +struct LocalType; // needed to avoid orphan rule errors + +impl external::TopLevel for LocalType {} //~ ERROR trait cannot be implemented outside `external_impl_restriction` +impl external::inner::Inner for LocalType {} //~ ERROR trait cannot be implemented outside `external_impl_restriction` + +pub mod foo { + pub mod bar { + pub(crate) impl(super) trait Foo {} + } + + impl bar::Foo for i8 {} +} + +impl foo::bar::Foo for u8 {} //~ ERROR trait cannot be implemented outside `foo` diff --git a/tests/ui/restrictions/impl-restriction.stderr b/tests/ui/restrictions/impl-restriction.stderr new file mode 100644 index 0000000000000..a09d4d23e55a1 --- /dev/null +++ b/tests/ui/restrictions/impl-restriction.stderr @@ -0,0 +1,33 @@ +error: trait cannot be implemented outside `external_impl_restriction` + --> $DIR/impl-restriction.rs:10:1 + | +LL | impl external::TopLevel for LocalType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/external-impl-restriction.rs:3:5 + | +LL | pub impl(crate) trait TopLevel {} + | ----------- trait restricted here + +error: trait cannot be implemented outside `external_impl_restriction` + --> $DIR/impl-restriction.rs:11:1 + | +LL | impl external::inner::Inner for LocalType {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/external-impl-restriction.rs:6:9 + | +LL | pub impl(self) trait Inner {} + | ---------- trait restricted here + +error: trait cannot be implemented outside `foo` + --> $DIR/impl-restriction.rs:21:1 + | +LL | pub(crate) impl(super) trait Foo {} + | ----------- trait restricted here +... +LL | impl foo::bar::Foo for u8 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/restrictions/in-path-rustfix.fixed b/tests/ui/restrictions/in-path-rustfix.fixed new file mode 100644 index 0000000000000..7a664376c107f --- /dev/null +++ b/tests/ui/restrictions/in-path-rustfix.fixed @@ -0,0 +1,10 @@ +// run-rustfix + +#![crate_type = "lib"] +#![feature(mut_restriction)] + +pub mod a { + pub struct Foo { + mut(in crate::a) _foo: u8, //~ ERROR incorrect mut restriction + } +} diff --git a/tests/ui/restrictions/in-path-rustfix.rs b/tests/ui/restrictions/in-path-rustfix.rs new file mode 100644 index 0000000000000..3f127e371e529 --- /dev/null +++ b/tests/ui/restrictions/in-path-rustfix.rs @@ -0,0 +1,10 @@ +// run-rustfix + +#![crate_type = "lib"] +#![feature(mut_restriction)] + +pub mod a { + pub struct Foo { + mut(crate::a) _foo: u8, //~ ERROR incorrect mut restriction + } +} diff --git a/tests/ui/restrictions/in-path-rustfix.stderr b/tests/ui/restrictions/in-path-rustfix.stderr new file mode 100644 index 0000000000000..5140922d493fb --- /dev/null +++ b/tests/ui/restrictions/in-path-rustfix.stderr @@ -0,0 +1,14 @@ +error[E0704]: incorrect mut restriction + --> $DIR/in-path-rustfix.rs:8:13 + | +LL | mut(crate::a) _foo: u8, + | ^^^^^^^^ help: make this mutable only to module `crate::a` with `in`: `in crate::a` + | + = help: some possible mut restrictions are: + `mut(crate)`: mutable only in the current crate + `mut(super)`: mutable only in the current module's parent + `mut(in path::to::module)`: mutable only in the specified path + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0704`. diff --git a/tests/ui/restrictions/mut-feature-gate.rs b/tests/ui/restrictions/mut-feature-gate.rs new file mode 100644 index 0000000000000..5ec3b39ea6b29 --- /dev/null +++ b/tests/ui/restrictions/mut-feature-gate.rs @@ -0,0 +1,10 @@ +// gate-test-mut_restriction +// revisions: with_gate without_gate +//[with_gate] check-pass + +#![crate_type = "lib"] +#![cfg_attr(with_gate, feature(mut_restriction))] + +pub struct Foo { + pub mut(self) alpha: u8, //[without_gate]~ ERROR +} diff --git a/tests/ui/restrictions/mut-feature-gate.without_gate.stderr b/tests/ui/restrictions/mut-feature-gate.without_gate.stderr new file mode 100644 index 0000000000000..b000ba90c843a --- /dev/null +++ b/tests/ui/restrictions/mut-feature-gate.without_gate.stderr @@ -0,0 +1,12 @@ +error[E0658]: restrictions are experimental + --> $DIR/mut-feature-gate.rs:9:9 + | +LL | pub mut(self) alpha: u8, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/restrictions/mut-restriction-construction.rs b/tests/ui/restrictions/mut-restriction-construction.rs new file mode 100644 index 0000000000000..361af0ed87743 --- /dev/null +++ b/tests/ui/restrictions/mut-restriction-construction.rs @@ -0,0 +1,16 @@ +#![feature(mut_restriction)] + +pub mod foo { + pub struct Foo { + pub mut(self) alpha: u8, + } + + pub enum Bar { + Beta(mut(self) u8), + } +} + +fn main() { + let foo = foo::Foo { alpha: 0 }; //~ ERROR cannot be constructed + let bar = foo::Bar::Beta(0); //~ ERROR cannot be constructed +} diff --git a/tests/ui/restrictions/mut-restriction-construction.stderr b/tests/ui/restrictions/mut-restriction-construction.stderr new file mode 100644 index 0000000000000..f09f6b6976ae6 --- /dev/null +++ b/tests/ui/restrictions/mut-restriction-construction.stderr @@ -0,0 +1,24 @@ +error: `Foo` cannot be constructed using a struct expression outside `foo` + --> $DIR/mut-restriction-construction.rs:14:15 + | +LL | pub mut(self) alpha: u8, + | --------- mutability restricted here +... +LL | let foo = foo::Foo { alpha: 0 }; + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: a struct containing fields with a mutability restriction cannot be constructed using a struct expression + +error: `Beta` cannot be constructed using a variant expression outside `foo` + --> $DIR/mut-restriction-construction.rs:15:15 + | +LL | Beta(mut(self) u8), + | --------- mutability restricted here +... +LL | let bar = foo::Bar::Beta(0); + | ^^^^^^^^^^^^^^^^^ + | + = note: a variant containing fields with a mutability restriction cannot be constructed using a variant expression + +error: aborting due to 2 previous errors + diff --git a/tests/ui/restrictions/mut-restriction.rs b/tests/ui/restrictions/mut-restriction.rs new file mode 100644 index 0000000000000..ce2a00952b164 --- /dev/null +++ b/tests/ui/restrictions/mut-restriction.rs @@ -0,0 +1,102 @@ +// aux-build: external-mut-restriction.rs + +#![feature(mut_restriction)] + +extern crate external_mut_restriction as external; + +pub mod local { + #[derive(Default)] + pub struct TupleStruct(pub mut(self) u8); + + #[derive(Default)] + pub struct FieldStruct { + pub mut(self) field: u8, + } + + #[derive(Default)] + pub enum Enum { + #[default] + Default, + Tuple(mut(self) u8), + Field { mut(self) field: u8 }, + } +} + +fn mut_ref( + local_tuple_struct: &mut local::TupleStruct, + local_field_struct: &mut local::FieldStruct, + local_enum: &mut local::Enum +) { + local_tuple_struct.0 = 1; //~ ERROR field cannot be mutated outside `local` + local_field_struct.field = 1; //~ ERROR field cannot be mutated outside `local` + match local_enum { + local::Enum::Default => {} + local::Enum::Tuple(ref mut a) => {} //~ ERROR field cannot be mutated outside `local` + local::Enum::Field { ref mut field } => {} //~ ERROR field cannot be mutated outside `local` + } +} + +fn mut_ptr(a: *mut local::TupleStruct, b: *mut local::FieldStruct) { + // unsafe doesn't matter + unsafe { + (*a).0 = 1; //~ ERROR field cannot be mutated outside `local` + (*b).field = 1; //~ ERROR field cannot be mutated outside `local` + } +} + +fn main() { + let mut local_tuple_struct = local::TupleStruct::default(); + let mut local_field_struct = local::FieldStruct::default(); + let mut local_enum = local::Enum::default(); + + local_tuple_struct.0 = 1; //~ ERROR field cannot be mutated outside `local` + local_field_struct.field = 1; //~ ERROR field cannot be mutated outside `local` + match local_enum { + local::Enum::Default => {} + local::Enum::Tuple(ref mut a) => {} //~ ERROR field cannot be mutated outside `local` + local::Enum::Field { ref mut field } => {} //~ ERROR field cannot be mutated outside `local` + } + std::ptr::addr_of_mut!(local_tuple_struct.0); //~ ERROR field cannot be mutated outside `local` + std::ptr::addr_of_mut!(local_field_struct.field); //~ ERROR field cannot be mutated outside `local` + + &mut local_tuple_struct.0; //~ ERROR field cannot be mutated outside `local` + &mut local_field_struct.field; //~ ERROR field cannot be mutated outside `local` + + let mut closure = || { + local_tuple_struct.0 = 1; //~ ERROR field cannot be mutated outside `local` + local_field_struct.field = 1; //~ ERROR field cannot be mutated outside `local` + }; + + // okay: the mutation occurs inside the function + closure(); + mut_ref(&mut local_tuple_struct, &mut local_field_struct, &mut local_enum); + mut_ptr(&mut local_tuple_struct as *mut _, &mut local_field_struct as *mut _); + + // undefined behavior, but not a compile error (it is the same as turning &T into &mut T) + unsafe { *(&local_tuple_struct.0 as *const _ as *mut _) = 1; } + unsafe { *(&local_field_struct.field as *const _ as *mut _) = 1; } + + // Check that external items have mut restrictions enforced. We are also checking that the + // name of the internal module is not present in the error message, as it is not relevant to the + // user. + + let mut external_top_level_struct = external::TopLevelStruct::default(); + external_top_level_struct.field = 1; //~ ERROR field cannot be mutated outside `external_mut_restriction` + + let mut external_top_level_enum = external::TopLevelEnum::default(); + match external_top_level_enum { + external::TopLevelEnum::Default => {} + external::TopLevelEnum::A(ref mut a) => {} //~ ERROR field cannot be mutated outside `external_mut_restriction` + external::TopLevelEnum::B { ref mut field } => {} //~ ERROR field cannot be mutated outside `external_mut_restriction` + } + + let mut external_inner_struct = external::inner::InnerStruct::default(); + external_inner_struct.field = 1; //~ ERROR field cannot be mutated outside `external_mut_restriction` + + let mut external_inner_enum = external::inner::InnerEnum::default(); + match external_inner_enum { + external::inner::InnerEnum::Default => {} + external::inner::InnerEnum::A(ref mut a) => {} //~ ERROR field cannot be mutated outside `external_mut_restriction` + external::inner::InnerEnum::B { ref mut field } => {} //~ ERROR field cannot be mutated outside `external_mut_restriction` + } +} diff --git a/tests/ui/restrictions/mut-restriction.stderr b/tests/ui/restrictions/mut-restriction.stderr new file mode 100644 index 0000000000000..95f8c01182f5a --- /dev/null +++ b/tests/ui/restrictions/mut-restriction.stderr @@ -0,0 +1,216 @@ +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:30:5 + | +LL | pub struct TupleStruct(pub mut(self) u8); + | --------- mutability restricted here +... +LL | local_tuple_struct.0 = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:31:5 + | +LL | pub mut(self) field: u8, + | --------- mutability restricted here +... +LL | local_field_struct.field = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:35:30 + | +LL | Field { mut(self) field: u8 }, + | --------- mutability restricted here +... +LL | local::Enum::Field { ref mut field } => {} + | ^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:34:28 + | +LL | Tuple(mut(self) u8), + | --------- mutability restricted here +... +LL | local::Enum::Tuple(ref mut a) => {} + | ^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:42:9 + | +LL | pub struct TupleStruct(pub mut(self) u8); + | --------- mutability restricted here +... +LL | (*a).0 = 1; + | ^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:43:9 + | +LL | pub mut(self) field: u8, + | --------- mutability restricted here +... +LL | (*b).field = 1; + | ^^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:52:5 + | +LL | pub struct TupleStruct(pub mut(self) u8); + | --------- mutability restricted here +... +LL | local_tuple_struct.0 = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:53:5 + | +LL | pub mut(self) field: u8, + | --------- mutability restricted here +... +LL | local_field_struct.field = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:57:30 + | +LL | Field { mut(self) field: u8 }, + | --------- mutability restricted here +... +LL | local::Enum::Field { ref mut field } => {} + | ^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:56:28 + | +LL | Tuple(mut(self) u8), + | --------- mutability restricted here +... +LL | local::Enum::Tuple(ref mut a) => {} + | ^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:59:5 + | +LL | pub struct TupleStruct(pub mut(self) u8); + | --------- mutability restricted here +... +LL | std::ptr::addr_of_mut!(local_tuple_struct.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `std::ptr::addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:60:5 + | +LL | pub mut(self) field: u8, + | --------- mutability restricted here +... +LL | std::ptr::addr_of_mut!(local_field_struct.field); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `std::ptr::addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:62:5 + | +LL | pub struct TupleStruct(pub mut(self) u8); + | --------- mutability restricted here +... +LL | &mut local_tuple_struct.0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:63:5 + | +LL | pub mut(self) field: u8, + | --------- mutability restricted here +... +LL | &mut local_field_struct.field; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field cannot be mutated outside `external_mut_restriction` + --> $DIR/mut-restriction.rs:84:5 + | +LL | external_top_level_struct.field = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/external-mut-restriction.rs:5:9 + | +LL | pub mut(self) field: u8, + | --------- mutability restricted here + +error: field cannot be mutated outside `external_mut_restriction` + --> $DIR/mut-restriction.rs:90:37 + | +LL | external::TopLevelEnum::B { ref mut field } => {} + | ^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/external-mut-restriction.rs:13:9 + | +LL | B { mut(self) field: u8 }, + | --------- mutability restricted here + +error: field cannot be mutated outside `external_mut_restriction` + --> $DIR/mut-restriction.rs:89:35 + | +LL | external::TopLevelEnum::A(ref mut a) => {} + | ^^^^^^^^^ + | + ::: $DIR/auxiliary/external-mut-restriction.rs:12:7 + | +LL | A(mut(self) u8), + | --------- mutability restricted here + +error: field cannot be mutated outside `external_mut_restriction` + --> $DIR/mut-restriction.rs:94:5 + | +LL | external_inner_struct.field = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/external-mut-restriction.rs:19:13 + | +LL | pub mut(self) field: u8, + | --------- mutability restricted here + +error: field cannot be mutated outside `external_mut_restriction` + --> $DIR/mut-restriction.rs:100:41 + | +LL | external::inner::InnerEnum::B { ref mut field } => {} + | ^^^^^^^^^^^^^ + | + ::: $DIR/auxiliary/external-mut-restriction.rs:27:13 + | +LL | B { mut(self) field: u8 }, + | --------- mutability restricted here + +error: field cannot be mutated outside `external_mut_restriction` + --> $DIR/mut-restriction.rs:99:39 + | +LL | external::inner::InnerEnum::A(ref mut a) => {} + | ^^^^^^^^^ + | + ::: $DIR/auxiliary/external-mut-restriction.rs:26:11 + | +LL | A(mut(self) u8), + | --------- mutability restricted here + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:66:9 + | +LL | pub struct TupleStruct(pub mut(self) u8); + | --------- mutability restricted here +... +LL | local_tuple_struct.0 = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: field cannot be mutated outside `local` + --> $DIR/mut-restriction.rs:67:9 + | +LL | pub mut(self) field: u8, + | --------- mutability restricted here +... +LL | local_field_struct.field = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 22 previous errors + diff --git a/tests/ui/restrictions/naked-impl-restriction.rs b/tests/ui/restrictions/naked-impl-restriction.rs new file mode 100644 index 0000000000000..a29913389a95b --- /dev/null +++ b/tests/ui/restrictions/naked-impl-restriction.rs @@ -0,0 +1,4 @@ +#![crate_type = "lib"] +#![feature(impl_restriction)] + +pub impl trait Foo {} //~ ERROR incorrect impl restriction diff --git a/tests/ui/restrictions/naked-impl-restriction.stderr b/tests/ui/restrictions/naked-impl-restriction.stderr new file mode 100644 index 0000000000000..f99d097353ea6 --- /dev/null +++ b/tests/ui/restrictions/naked-impl-restriction.stderr @@ -0,0 +1,14 @@ +error[E0704]: incorrect impl restriction + --> $DIR/naked-impl-restriction.rs:4:5 + | +LL | pub impl trait Foo {} + | ^^^^ help: make this implementable only to the current crate: `impl(crate)` + | + = help: some possible impl restrictions are: + `impl(crate)`: implementable only in the current crate + `impl(super)`: implementable only in the current module's parent + `impl(in path::to::module)`: implementable only in the specified path + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0704`. diff --git a/tests/ui/restrictions/naked-mut-restriction.rs b/tests/ui/restrictions/naked-mut-restriction.rs new file mode 100644 index 0000000000000..200fd0380cffe --- /dev/null +++ b/tests/ui/restrictions/naked-mut-restriction.rs @@ -0,0 +1,6 @@ +#![crate_type = "lib"] +#![feature(mut_restriction)] + +pub struct Foo { + pub mut x: u32, //~ ERROR incorrect mut restriction +} diff --git a/tests/ui/restrictions/naked-mut-restriction.stderr b/tests/ui/restrictions/naked-mut-restriction.stderr new file mode 100644 index 0000000000000..a1e7c54312123 --- /dev/null +++ b/tests/ui/restrictions/naked-mut-restriction.stderr @@ -0,0 +1,14 @@ +error[E0704]: incorrect mut restriction + --> $DIR/naked-mut-restriction.rs:5:9 + | +LL | pub mut x: u32, + | ^^^ help: make this mutable only to the current crate: `mut(crate)` + | + = help: some possible mut restrictions are: + `mut(crate)`: mutable only in the current crate + `mut(super)`: mutable only in the current module's parent + `mut(in path::to::module)`: mutable only in the specified path + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0704`. diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr index d723ff538a886..a40d3168abc71 100644 --- a/tests/ui/stats/hir-stats.stderr +++ b/tests/ui/stats/hir-stats.stderr @@ -15,45 +15,45 @@ ast-stats-1 Arm 96 ( 1.5%) 2 48 ast-stats-1 ForeignItem 96 ( 1.5%) 1 96 ast-stats-1 - Fn 96 ( 1.5%) 1 ast-stats-1 FnDecl 120 ( 1.8%) 5 24 -ast-stats-1 FieldDef 160 ( 2.5%) 2 80 -ast-stats-1 Stmt 160 ( 2.5%) 5 32 +ast-stats-1 Stmt 160 ( 2.4%) 5 32 ast-stats-1 - Local 32 ( 0.5%) 1 ast-stats-1 - MacCall 32 ( 0.5%) 1 ast-stats-1 - Expr 96 ( 1.5%) 3 -ast-stats-1 Param 160 ( 2.5%) 4 40 -ast-stats-1 Block 192 ( 3.0%) 6 32 +ast-stats-1 Param 160 ( 2.4%) 4 40 +ast-stats-1 Block 192 ( 2.9%) 6 32 ast-stats-1 Variant 208 ( 3.2%) 2 104 -ast-stats-1 GenericBound 224 ( 3.5%) 4 56 -ast-stats-1 - Trait 224 ( 3.5%) 4 +ast-stats-1 GenericBound 224 ( 3.4%) 4 56 +ast-stats-1 - Trait 224 ( 3.4%) 4 +ast-stats-1 FieldDef 224 ( 3.4%) 2 112 ast-stats-1 AssocItem 352 ( 5.4%) 4 88 ast-stats-1 - Type 176 ( 2.7%) 2 ast-stats-1 - Fn 176 ( 2.7%) 2 -ast-stats-1 GenericParam 480 ( 7.4%) 5 96 -ast-stats-1 Pat 504 ( 7.8%) 7 72 +ast-stats-1 GenericParam 480 ( 7.3%) 5 96 +ast-stats-1 Pat 504 ( 7.7%) 7 72 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Wild 72 ( 1.1%) 1 ast-stats-1 - Ident 360 ( 5.5%) 5 -ast-stats-1 Expr 576 ( 8.9%) 8 72 +ast-stats-1 Expr 576 ( 8.8%) 8 72 ast-stats-1 - Path 72 ( 1.1%) 1 ast-stats-1 - Match 72 ( 1.1%) 1 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Lit 144 ( 2.2%) 2 ast-stats-1 - Block 216 ( 3.3%) 3 -ast-stats-1 PathSegment 720 (11.1%) 30 24 -ast-stats-1 Ty 896 (13.8%) 14 64 +ast-stats-1 PathSegment 720 (11.0%) 30 24 +ast-stats-1 Ty 896 (13.7%) 14 64 ast-stats-1 - Ptr 64 ( 1.0%) 1 ast-stats-1 - Ref 64 ( 1.0%) 1 ast-stats-1 - ImplicitSelf 128 ( 2.0%) 2 -ast-stats-1 - Path 640 ( 9.9%) 10 -ast-stats-1 Item 1_224 (18.9%) 9 136 +ast-stats-1 - Path 640 ( 9.8%) 10 +ast-stats-1 Item 1_224 (18.7%) 9 136 ast-stats-1 - Trait 136 ( 2.1%) 1 ast-stats-1 - Enum 136 ( 2.1%) 1 ast-stats-1 - ForeignMod 136 ( 2.1%) 1 ast-stats-1 - Impl 136 ( 2.1%) 1 ast-stats-1 - Fn 272 ( 4.2%) 2 -ast-stats-1 - Use 408 ( 6.3%) 3 +ast-stats-1 - Use 408 ( 6.2%) 3 ast-stats-1 ---------------------------------------------------------------- -ast-stats-1 Total 6_488 +ast-stats-1 Total 6_552 ast-stats-1 ast-stats-2 POST EXPANSION AST STATS ast-stats-2 Name Accumulated Size Count Item Size @@ -65,32 +65,32 @@ ast-stats-2 ExprField 48 ( 0.7%) 1 48 ast-stats-2 WherePredicate 56 ( 0.8%) 1 56 ast-stats-2 - BoundPredicate 56 ( 0.8%) 1 ast-stats-2 Local 72 ( 1.0%) 1 72 -ast-stats-2 Arm 96 ( 1.4%) 2 48 -ast-stats-2 ForeignItem 96 ( 1.4%) 1 96 -ast-stats-2 - Fn 96 ( 1.4%) 1 +ast-stats-2 Arm 96 ( 1.3%) 2 48 +ast-stats-2 ForeignItem 96 ( 1.3%) 1 96 +ast-stats-2 - Fn 96 ( 1.3%) 1 ast-stats-2 InlineAsm 120 ( 1.7%) 1 120 ast-stats-2 FnDecl 120 ( 1.7%) 5 24 ast-stats-2 Attribute 128 ( 1.8%) 4 32 -ast-stats-2 - DocComment 32 ( 0.5%) 1 -ast-stats-2 - Normal 96 ( 1.4%) 3 -ast-stats-2 FieldDef 160 ( 2.3%) 2 80 -ast-stats-2 Stmt 160 ( 2.3%) 5 32 -ast-stats-2 - Local 32 ( 0.5%) 1 -ast-stats-2 - Semi 32 ( 0.5%) 1 -ast-stats-2 - Expr 96 ( 1.4%) 3 -ast-stats-2 Param 160 ( 2.3%) 4 40 +ast-stats-2 - DocComment 32 ( 0.4%) 1 +ast-stats-2 - Normal 96 ( 1.3%) 3 +ast-stats-2 Stmt 160 ( 2.2%) 5 32 +ast-stats-2 - Local 32 ( 0.4%) 1 +ast-stats-2 - Semi 32 ( 0.4%) 1 +ast-stats-2 - Expr 96 ( 1.3%) 3 +ast-stats-2 Param 160 ( 2.2%) 4 40 ast-stats-2 Block 192 ( 2.7%) 6 32 ast-stats-2 Variant 208 ( 2.9%) 2 104 -ast-stats-2 GenericBound 224 ( 3.2%) 4 56 -ast-stats-2 - Trait 224 ( 3.2%) 4 -ast-stats-2 AssocItem 352 ( 5.0%) 4 88 +ast-stats-2 GenericBound 224 ( 3.1%) 4 56 +ast-stats-2 - Trait 224 ( 3.1%) 4 +ast-stats-2 FieldDef 224 ( 3.1%) 2 112 +ast-stats-2 AssocItem 352 ( 4.9%) 4 88 ast-stats-2 - Type 176 ( 2.5%) 2 ast-stats-2 - Fn 176 ( 2.5%) 2 -ast-stats-2 GenericParam 480 ( 6.8%) 5 96 -ast-stats-2 Pat 504 ( 7.1%) 7 72 +ast-stats-2 GenericParam 480 ( 6.7%) 5 96 +ast-stats-2 Pat 504 ( 7.0%) 7 72 ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - Wild 72 ( 1.0%) 1 -ast-stats-2 - Ident 360 ( 5.1%) 5 +ast-stats-2 - Ident 360 ( 5.0%) 5 ast-stats-2 Expr 648 ( 9.1%) 9 72 ast-stats-2 - Path 72 ( 1.0%) 1 ast-stats-2 - Match 72 ( 1.0%) 1 @@ -98,22 +98,22 @@ ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - InlineAsm 72 ( 1.0%) 1 ast-stats-2 - Lit 144 ( 2.0%) 2 ast-stats-2 - Block 216 ( 3.0%) 3 -ast-stats-2 PathSegment 792 (11.2%) 33 24 -ast-stats-2 Ty 896 (12.6%) 14 64 +ast-stats-2 PathSegment 792 (11.1%) 33 24 +ast-stats-2 Ty 896 (12.5%) 14 64 ast-stats-2 - Ptr 64 ( 0.9%) 1 ast-stats-2 - Ref 64 ( 0.9%) 1 ast-stats-2 - ImplicitSelf 128 ( 1.8%) 2 -ast-stats-2 - Path 640 ( 9.0%) 10 -ast-stats-2 Item 1_496 (21.1%) 11 136 +ast-stats-2 - Path 640 ( 8.9%) 10 +ast-stats-2 Item 1_496 (20.9%) 11 136 ast-stats-2 - Trait 136 ( 1.9%) 1 ast-stats-2 - Enum 136 ( 1.9%) 1 ast-stats-2 - ExternCrate 136 ( 1.9%) 1 ast-stats-2 - ForeignMod 136 ( 1.9%) 1 ast-stats-2 - Impl 136 ( 1.9%) 1 ast-stats-2 - Fn 272 ( 3.8%) 2 -ast-stats-2 - Use 544 ( 7.7%) 4 +ast-stats-2 - Use 544 ( 7.6%) 4 ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Total 7_088 +ast-stats-2 Total 7_152 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size