diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 0359264d3f787..5bf4bbe79a9a0 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -65,6 +65,10 @@ impl EffectiveVisibility { } } + pub fn public_at_level(&self) -> Option { + Level::all_levels().into_iter().find(|&level| self.is_public_at_level(level)) + } + pub fn is_public_at_level(&self, level: Level) -> bool { self.at_level(level).is_public() } @@ -120,9 +124,7 @@ impl EffectiveVisibilities { } pub fn public_at_level(&self, id: LocalDefId) -> Option { - self.effective_vis(id).and_then(|effective_vis| { - Level::all_levels().into_iter().find(|&level| effective_vis.is_public_at_level(level)) - }) + self.effective_vis(id).and_then(|effective_vis| effective_vis.public_at_level()) } pub fn update_root(&mut self) { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 93233efae6650..9091391a17ace 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -118,7 +118,7 @@ pub use self::typeck_results::{ }; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::{AmbigModChild, ModChild}; -use crate::middle::privacy::EffectiveVisibilities; +use crate::middle::privacy::{EffectiveVisibilities, EffectiveVisibility}; use crate::mir::{Body, CoroutineLayout, CoroutineSavedLocal, SourceInfo}; use crate::query::{IntoQueryKey, Providers}; use crate::ty; @@ -179,6 +179,12 @@ pub struct ResolverGlobalCtxt { /// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`. pub expn_that_defined: UnordMap, pub effective_visibilities: EffectiveVisibilities, + // FIXME: This table contains ADTs reachable from macro 2.0. + // Currently, reachability of a definition from a macro is determined by nominal visibility + // (see `compute_effective_visibilities`). This is incorrect and leads to the necessity + // of traversing ADT fields in `rustc_privacy`. Remove this workaround once the + // correct reachability logic is implemented for macros. + pub macro_reachable_adts: FxIndexMap, pub extern_crate_map: UnordMap, pub maybe_unused_trait_imports: FxIndexSet, pub module_children: LocalDefIdMap>, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index d92944d1b31fa..c93cf2c52bc29 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -15,7 +15,6 @@ use errors::{ ItemIsPrivate, PrivateInterfacesOrBoundsLint, ReportEffectiveVisibility, UnnameableTypesLint, UnnamedItemIsPrivate, }; -use rustc_ast::MacroDef; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; @@ -34,7 +33,6 @@ use rustc_middle::ty::{ }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::hygiene::Transparency; use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; @@ -419,22 +417,8 @@ impl VisibilityLike for EffectiveVisibility { /// The embargo visitor, used to determine the exports of the AST. struct EmbargoVisitor<'tcx> { tcx: TyCtxt<'tcx>, - /// Effective visibilities for reachable nodes. effective_visibilities: EffectiveVisibilities, - /// A set of pairs corresponding to modules, where the first module is - /// reachable via a macro that's defined in the second module. This cannot - /// be represented as reachable because it can't handle the following case: - /// - /// pub mod n { // Should be `Public` - /// pub(crate) mod p { // Should *not* be accessible - /// pub fn f() -> i32 { 12 } // Must be `Reachable` - /// } - /// } - /// pub macro m() { - /// n::p::f() - /// } - macro_reachable: FxHashSet<(LocalModDefId, LocalModDefId)>, /// Has something changed in the level map? changed: bool, } @@ -509,161 +493,6 @@ impl<'tcx> EmbargoVisitor<'tcx> { level: Level::ReachableThroughImplTrait, } } - - // We have to make sure that the items that macros might reference - // are reachable, since they might be exported transitively. - fn update_reachability_from_macro( - &mut self, - local_def_id: LocalDefId, - md: &MacroDef, - macro_ev: EffectiveVisibility, - ) { - // Non-opaque macros cannot make other items more accessible than they already are. - let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id); - let attrs = self.tcx.hir_attrs(hir_id); - - if find_attr!(attrs, RustcMacroTransparency(x) => *x) - .unwrap_or(Transparency::fallback(md.macro_rules)) - != Transparency::Opaque - { - return; - } - - let macro_module_def_id = self.tcx.local_parent(local_def_id); - if self.tcx.def_kind(macro_module_def_id) != DefKind::Mod { - // The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252). - return; - } - // FIXME(typed_def_id): Introduce checked constructors that check def_kind. - let macro_module_def_id = LocalModDefId::new_unchecked(macro_module_def_id); - - if self.effective_visibilities.public_at_level(local_def_id).is_none() { - return; - } - - // Since we are starting from an externally visible module, - // all the parents in the loop below are also guaranteed to be modules. - let mut module_def_id = macro_module_def_id; - loop { - let changed_reachability = - self.update_macro_reachable(module_def_id, macro_module_def_id, macro_ev); - if changed_reachability || module_def_id == LocalModDefId::CRATE_DEF_ID { - break; - } - module_def_id = LocalModDefId::new_unchecked(self.tcx.local_parent(module_def_id)); - } - } - - /// Updates the item as being reachable through a macro defined in the given - /// module. Returns `true` if the level has changed. - fn update_macro_reachable( - &mut self, - module_def_id: LocalModDefId, - defining_mod: LocalModDefId, - macro_ev: EffectiveVisibility, - ) -> bool { - if self.macro_reachable.insert((module_def_id, defining_mod)) { - for child in self.tcx.module_children_local(module_def_id.to_local_def_id()) { - if let Res::Def(def_kind, def_id) = child.res - && let Some(def_id) = def_id.as_local() - && child.vis.is_accessible_from(defining_mod, self.tcx) - { - let vis = self.tcx.local_visibility(def_id); - self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod, macro_ev); - } - } - true - } else { - false - } - } - - fn update_macro_reachable_def( - &mut self, - def_id: LocalDefId, - def_kind: DefKind, - vis: ty::Visibility, - module: LocalModDefId, - macro_ev: EffectiveVisibility, - ) { - self.update(def_id, macro_ev, Level::Reachable); - match def_kind { - // No type privacy, so can be directly marked as reachable. - DefKind::Const { .. } - | DefKind::Static { .. } - | DefKind::TraitAlias - | DefKind::TyAlias => { - if vis.is_accessible_from(module, self.tcx) { - self.update(def_id, macro_ev, Level::Reachable); - } - } - - // Hygiene isn't really implemented for `macro_rules!` macros at the - // moment. Accordingly, marking them as reachable is unwise. `macro` macros - // have normal hygiene, so we can treat them like other items without type - // privacy and mark them reachable. - DefKind::Macro(_) => { - let item = self.tcx.hir_expect_item(def_id); - if let hir::ItemKind::Macro(_, MacroDef { macro_rules: false, .. }, _) = item.kind { - if vis.is_accessible_from(module, self.tcx) { - self.update(def_id, macro_ev, Level::Reachable); - } - } - } - - // We can't use a module name as the final segment of a path, except - // in use statements. Since re-export checking doesn't consider - // hygiene these don't need to be marked reachable. The contents of - // the module, however may be reachable. - DefKind::Mod => { - if vis.is_accessible_from(module, self.tcx) { - self.update_macro_reachable( - LocalModDefId::new_unchecked(def_id), - module, - macro_ev, - ); - } - } - - DefKind::Struct | DefKind::Union => { - // While structs and unions have type privacy, their fields do not. - let struct_def = self.tcx.adt_def(def_id); - for field in &struct_def.non_enum_variant().fields { - let def_id = field.did.expect_local(); - let field_vis = self.tcx.local_visibility(def_id); - if field_vis.is_accessible_from(module, self.tcx) { - self.reach(def_id, macro_ev).ty(); - } - } - } - - // These have type privacy, so are not reachable unless they're - // public, or are not namespaced at all. - DefKind::AssocConst { .. } - | DefKind::AssocTy - | DefKind::ConstParam - | DefKind::Ctor(_, _) - | DefKind::Enum - | DefKind::ForeignTy - | DefKind::Fn - | DefKind::OpaqueTy - | DefKind::AssocFn - | DefKind::Trait - | DefKind::TyParam - | DefKind::Variant - | DefKind::LifetimeParam - | DefKind::ExternCrate - | DefKind::Use - | DefKind::ForeignMod - | DefKind::AnonConst - | DefKind::InlineConst - | DefKind::Field - | DefKind::GlobalAsm - | DefKind::Impl { .. } - | DefKind::Closure - | DefKind::SyntheticCoroutineBody => (), - } - } } impl<'tcx> EmbargoVisitor<'tcx> { @@ -689,13 +518,8 @@ impl<'tcx> EmbargoVisitor<'tcx> { DefKind::Use | DefKind::ExternCrate | DefKind::GlobalAsm => {} // The interface is empty, and all nested items are processed by `check_def_id`. DefKind::Mod => {} - DefKind::Macro { .. } => { - if let Some(item_ev) = item_ev { - let (_, macro_def, _) = - self.tcx.hir_expect_item(owner_id.def_id).expect_macro(); - self.update_reachability_from_macro(owner_id.def_id, macro_def, item_ev); - } - } + // Effective visibilities for macros are processed earlier. + DefKind::Macro { .. } => {} DefKind::ForeignTy | DefKind::Const { .. } | DefKind::Static { .. } @@ -1815,7 +1639,6 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { let mut visitor = EmbargoVisitor { tcx, effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(), - macro_reachable: Default::default(), changed: false, }; @@ -1872,6 +1695,17 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { visitor.changed = false; } + for (&adt_def_id, &(macro_module, adt_eff_vis)) in &tcx.resolutions(()).macro_reachable_adts { + let struct_def = tcx.adt_def(adt_def_id); + for field in &struct_def.non_enum_variant().fields { + let def_id = field.did.expect_local(); + let field_vis = tcx.local_visibility(def_id); + if field_vis.is_accessible_from(macro_module, tcx) { + visitor.reach(def_id, adt_eff_vis).ty(); + } + } + } + let crate_items = tcx.hir_crate_items(()); loop { for id in crate_items.free_items() { diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index f3b47f04c90a6..dfc00ed6f3674 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -1,11 +1,13 @@ use std::mem; use rustc_ast::visit::Visitor; -use rustc_ast::{Crate, EnumDef, ast, visit}; +use rustc_ast::{Attribute, Crate, EnumDef, ast, visit}; use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::ty::Visibility; +use rustc_span::sym; use tracing::info; use crate::{Decl, DeclKind, Resolver}; @@ -34,6 +36,19 @@ pub(crate) struct EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { import_effective_visibilities: EffectiveVisibilities>, // It's possible to recalculate this at any point, but it's relatively expensive. current_private_vis: Visibility, + /// A set of pairs corresponding to modules, where the first module is + /// reachable via a macro that's defined in the second module. This cannot + /// be represented as reachable because it can't handle the following case: + /// + /// pub mod n { // Should be `Public` + /// pub(crate) mod p { // Should *not* be accessible + /// pub fn f() -> i32 { 12 } // Must be `Reachable` + /// } + /// } + /// pub macro m() { + /// n::p::f() + /// } + macro_reachable: FxHashSet<(LocalDefId, LocalDefId)>, changed: bool, } @@ -71,6 +86,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { def_effective_visibilities: Default::default(), import_effective_visibilities: Default::default(), current_private_vis: Visibility::Restricted(CRATE_DEF_ID), + macro_reachable: Default::default(), changed: true, }; @@ -210,6 +226,121 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { let nominal_vis = self.r.tcx.local_visibility(def_id); self.update_def(def_id, nominal_vis, ParentId::Def(parent_id), self.current_private_vis); } + + fn update_macro(&mut self, def_id: LocalDefId, inherited_effective_vis: EffectiveVisibility) { + let max_vis = Some(self.r.tcx.local_visibility(def_id)); + let priv_vis = if def_id == CRATE_DEF_ID { + Visibility::Restricted(CRATE_DEF_ID) + } else { + self.r.private_vis_def(def_id) + }; + self.changed |= self.def_effective_visibilities.update( + def_id, + max_vis, + priv_vis, + inherited_effective_vis, + Level::Reachable, + self.r.tcx, + ); + } + + // We have to make sure that the items that macros might reference + // are reachable, since they might be exported transitively. + fn update_reachability_from_macro( + &mut self, + local_def_id: LocalDefId, + md: &ast::MacroDef, + attrs: &[Attribute], + ) { + // Non-opaque macros cannot make other items more accessible than they already are. + let is_non_opaque_transparency = + rustc_ast::attr::find_by_name(attrs, sym::rustc_macro_transparency) + .map_or(false, |attr| attr.value_str() != Some(sym::opaque)); + + if is_non_opaque_transparency || md.macro_rules { + return; + } + + let macro_module_def_id = self.r.tcx.local_parent(local_def_id); + if self.r.tcx.def_kind(macro_module_def_id) != DefKind::Mod { + // The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252). + return; + } + + let Some(macro_ev) = self + .def_effective_visibilities + .effective_vis(local_def_id) + .filter(|ev| ev.public_at_level().is_some()) + .copied() + else { + return; + }; + + // Since we are starting from an externally visible module, + // all the parents in the loop below are also guaranteed to be modules. + let mut module_def_id = macro_module_def_id; + loop { + let changed_reachability = + self.update_macro_reachable(module_def_id, macro_module_def_id, macro_ev); + if changed_reachability || module_def_id == CRATE_DEF_ID { + break; + } + module_def_id = self.r.tcx.local_parent(module_def_id); + } + } + + /// Updates the item as being reachable through a macro defined in the given + /// module. Returns `true` if the level has changed. + fn update_macro_reachable( + &mut self, + module_def_id: LocalDefId, + defining_mod: LocalDefId, + macro_ev: EffectiveVisibility, + ) -> bool { + if self.macro_reachable.insert((module_def_id, defining_mod)) { + let module = self.r.expect_module(module_def_id.to_def_id()); + for (_, name_resolution) in self.r.resolutions(module).borrow().iter() { + let Some(decl) = name_resolution.borrow().best_decl() else { + continue; + }; + + if let Res::Def(def_kind, def_id) = decl.res() + && let Some(def_id) = def_id.as_local() + // FIXME: defs should be checked with `EffectiveVisibilities::is_reachable`. + && decl.vis().is_accessible_from(defining_mod, self.r.tcx) + { + let vis = self.r.tcx.local_visibility(def_id); + self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod, macro_ev); + } + } + true + } else { + false + } + } + + fn update_macro_reachable_def( + &mut self, + def_id: LocalDefId, + def_kind: DefKind, + vis: Visibility, + module: LocalDefId, + macro_ev: EffectiveVisibility, + ) { + self.update_macro(def_id, macro_ev); + + match def_kind { + DefKind::Mod => { + if vis.is_accessible_from(module, self.r.tcx) { + self.update_macro_reachable(def_id, module, macro_ev); + } + } + DefKind::Struct | DefKind::Union => { + self.r.macro_reachable_adts.insert(def_id, (module, macro_ev)); + } + _ => {} + } + } } impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { @@ -217,7 +348,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> let def_id = self.r.local_def_id(item.id); // Update effective visibilities of nested items. // If it's a mod, also make the visitor walk all of its items - match item.kind { + match &item.kind { // Resolved in rustc_privacy when types are available ast::ItemKind::Impl(..) => return, @@ -234,7 +365,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> self.current_private_vis = prev_private_vis; } - ast::ItemKind::Enum(_, _, EnumDef { ref variants }) => { + ast::ItemKind::Enum(_, _, EnumDef { variants }) => { self.set_bindings_effective_visibilities(def_id); for variant in variants { let variant_def_id = self.r.local_def_id(variant.id); @@ -244,7 +375,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> } } - ast::ItemKind::Struct(_, _, ref def) | ast::ItemKind::Union(_, _, ref def) => { + ast::ItemKind::Struct(_, _, def) | ast::ItemKind::Union(_, _, def) => { for field in def.fields() { self.update_field(self.r.local_def_id(field.id), def_id); } @@ -254,6 +385,10 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> self.set_bindings_effective_visibilities(def_id); } + ast::ItemKind::MacroDef(_, macro_def) => { + self.update_reachability_from_macro(def_id, macro_def, &item.attrs); + } + ast::ItemKind::ExternCrate(..) | ast::ItemKind::Use(..) | ast::ItemKind::Static(..) @@ -262,7 +397,6 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> | ast::ItemKind::GlobalAsm(..) | ast::ItemKind::TyAlias(..) | ast::ItemKind::TraitAlias(..) - | ast::ItemKind::MacroDef(..) | ast::ItemKind::ForeignMod(..) | ast::ItemKind::Fn(..) | ast::ItemKind::Delegation(..) => return, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 44a72fec56b95..c8ce04554f000 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -62,7 +62,7 @@ use rustc_hir::{PrimTy, TraitCandidate, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_metadata::creader::CStore; use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; -use rustc_middle::middle::privacy::EffectiveVisibilities; +use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility}; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, DelegationInfo, MainDefinition, RegisteredTools, ResolverAstLowering, ResolverGlobalCtxt, @@ -1531,6 +1531,8 @@ pub struct Resolver<'ra, 'tcx> { stripped_cfg_items: Vec> = Vec::new(), effective_visibilities: EffectiveVisibilities, + macro_reachable_adts: FxIndexMap, + doc_link_resolutions: FxIndexMap, doc_link_traits_in_scope: FxIndexMap>, all_macro_rules: UnordSet = Default::default(), @@ -1853,6 +1855,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { confused_type_with_std_module: Default::default(), stripped_cfg_items: Default::default(), effective_visibilities: Default::default(), + macro_reachable_adts: Default::default(), doc_link_resolutions: Default::default(), doc_link_traits_in_scope: Default::default(), current_crate_outer_attr_insert_span, @@ -1963,6 +1966,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { expn_that_defined, visibilities_for_hashing: self.visibilities_for_hashing, effective_visibilities, + macro_reachable_adts: self.macro_reachable_adts, extern_crate_map, module_children: self.module_children, ambig_module_children: self.ambig_module_children,