Skip to content

Commit 604d124

Browse files
Rollup merge of #156500 - Bryanskiy:macros_vis, r=petrochenkov
Privacy: move macros handling to early stage The patch moves effective visibility computation for macros from `rustc_privacy` to `rustc_resolve`. It will enable this optimization: #156228. However, I found some problems with macro handling while I was doing this. The current implementation was written ~6 years ago and checks the reachability of a definition from a macro by nominal visibility. In general this is incorrect. For example, in the current implementation modules are not traversed if their nominal visibility is less then the nominal visibility of a module defining macro: https://github.com/rust-lang/rust/blob/29b7590130c83542a095cdf1323ed0f78eec2bb8/compiler/rustc_privacy/src/lib.rs#L618-L626 As a result, in order to compile code like `tests/ui/definition-reachable/auxiliary/field-method-macro.rs`. we have to additionally traverse types of adt fields: https://github.com/rust-lang/rust/blob/29b7590130c83542a095cdf1323ed0f78eec2bb8/compiler/rustc_privacy/src/lib.rs#L628-L638 This is a hack and the proper solution would be to check definitions with `EffectiveVisibilities::is_reachable`. I haven’t done this yet, as it would start to trigger many lints as more items become reachable. I think it’s better to leave the change to another commit. r? @petrochenkov
2 parents f24b05a + 3d0ee52 commit 604d124

5 files changed

Lines changed: 178 additions & 187 deletions

File tree

compiler/rustc_middle/src/middle/privacy.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ impl EffectiveVisibility {
6565
}
6666
}
6767

68+
pub fn public_at_level(&self) -> Option<Level> {
69+
Level::all_levels().into_iter().find(|&level| self.is_public_at_level(level))
70+
}
71+
6872
pub fn is_public_at_level(&self, level: Level) -> bool {
6973
self.at_level(level).is_public()
7074
}
@@ -120,9 +124,7 @@ impl EffectiveVisibilities {
120124
}
121125

122126
pub fn public_at_level(&self, id: LocalDefId) -> Option<Level> {
123-
self.effective_vis(id).and_then(|effective_vis| {
124-
Level::all_levels().into_iter().find(|&level| effective_vis.is_public_at_level(level))
125-
})
127+
self.effective_vis(id).and_then(|effective_vis| effective_vis.public_at_level())
126128
}
127129

128130
pub fn update_root(&mut self) {

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ pub struct ResolverGlobalCtxt {
179179
/// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
180180
pub expn_that_defined: UnordMap<LocalDefId, ExpnId>,
181181
pub effective_visibilities: EffectiveVisibilities,
182+
// FIXME: This table contains ADTs reachable from macro 2.0.
183+
// Currently, reachability of a definition from a macro is determined by nominal visibility
184+
// (see `compute_effective_visibilities`). This is incorrect and leads to the necessity
185+
// of traversing ADT fields in `rustc_privacy`. Remove this workaround once the
186+
// correct reachability logic is implemented for macros.
187+
pub macro_reachable_adts: FxIndexMap<LocalDefId, FxIndexSet<LocalDefId>>,
182188
pub extern_crate_map: UnordMap<LocalDefId, CrateNum>,
183189
pub maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
184190
pub module_children: LocalDefIdMap<Vec<ModChild>>,

compiler/rustc_privacy/src/lib.rs

Lines changed: 22 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use errors::{
1515
ItemIsPrivate, PrivateInterfacesOrBoundsLint, ReportEffectiveVisibility, UnnameableTypesLint,
1616
UnnamedItemIsPrivate,
1717
};
18-
use rustc_ast::MacroDef;
1918
use rustc_ast::visit::{VisitorResult, try_visit};
2019
use rustc_data_structures::fx::FxHashSet;
2120
use rustc_data_structures::intern::Interned;
@@ -34,7 +33,6 @@ use rustc_middle::ty::{
3433
};
3534
use rustc_middle::{bug, span_bug};
3635
use rustc_session::lint;
37-
use rustc_span::hygiene::Transparency;
3836
use rustc_span::{Ident, Span, Symbol, sym};
3937
use tracing::debug;
4038

@@ -419,22 +417,8 @@ impl VisibilityLike for EffectiveVisibility {
419417
/// The embargo visitor, used to determine the exports of the AST.
420418
struct EmbargoVisitor<'tcx> {
421419
tcx: TyCtxt<'tcx>,
422-
423420
/// Effective visibilities for reachable nodes.
424421
effective_visibilities: EffectiveVisibilities,
425-
/// A set of pairs corresponding to modules, where the first module is
426-
/// reachable via a macro that's defined in the second module. This cannot
427-
/// be represented as reachable because it can't handle the following case:
428-
///
429-
/// pub mod n { // Should be `Public`
430-
/// pub(crate) mod p { // Should *not* be accessible
431-
/// pub fn f() -> i32 { 12 } // Must be `Reachable`
432-
/// }
433-
/// }
434-
/// pub macro m() {
435-
/// n::p::f()
436-
/// }
437-
macro_reachable: FxHashSet<(LocalModDefId, LocalModDefId)>,
438422
/// Has something changed in the level map?
439423
changed: bool,
440424
}
@@ -509,161 +493,6 @@ impl<'tcx> EmbargoVisitor<'tcx> {
509493
level: Level::ReachableThroughImplTrait,
510494
}
511495
}
512-
513-
// We have to make sure that the items that macros might reference
514-
// are reachable, since they might be exported transitively.
515-
fn update_reachability_from_macro(
516-
&mut self,
517-
local_def_id: LocalDefId,
518-
md: &MacroDef,
519-
macro_ev: EffectiveVisibility,
520-
) {
521-
// Non-opaque macros cannot make other items more accessible than they already are.
522-
let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id);
523-
let attrs = self.tcx.hir_attrs(hir_id);
524-
525-
if find_attr!(attrs, RustcMacroTransparency(x) => *x)
526-
.unwrap_or(Transparency::fallback(md.macro_rules))
527-
!= Transparency::Opaque
528-
{
529-
return;
530-
}
531-
532-
let macro_module_def_id = self.tcx.local_parent(local_def_id);
533-
if self.tcx.def_kind(macro_module_def_id) != DefKind::Mod {
534-
// The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
535-
return;
536-
}
537-
// FIXME(typed_def_id): Introduce checked constructors that check def_kind.
538-
let macro_module_def_id = LocalModDefId::new_unchecked(macro_module_def_id);
539-
540-
if self.effective_visibilities.public_at_level(local_def_id).is_none() {
541-
return;
542-
}
543-
544-
// Since we are starting from an externally visible module,
545-
// all the parents in the loop below are also guaranteed to be modules.
546-
let mut module_def_id = macro_module_def_id;
547-
loop {
548-
let changed_reachability =
549-
self.update_macro_reachable(module_def_id, macro_module_def_id, macro_ev);
550-
if changed_reachability || module_def_id == LocalModDefId::CRATE_DEF_ID {
551-
break;
552-
}
553-
module_def_id = LocalModDefId::new_unchecked(self.tcx.local_parent(module_def_id));
554-
}
555-
}
556-
557-
/// Updates the item as being reachable through a macro defined in the given
558-
/// module. Returns `true` if the level has changed.
559-
fn update_macro_reachable(
560-
&mut self,
561-
module_def_id: LocalModDefId,
562-
defining_mod: LocalModDefId,
563-
macro_ev: EffectiveVisibility,
564-
) -> bool {
565-
if self.macro_reachable.insert((module_def_id, defining_mod)) {
566-
for child in self.tcx.module_children_local(module_def_id.to_local_def_id()) {
567-
if let Res::Def(def_kind, def_id) = child.res
568-
&& let Some(def_id) = def_id.as_local()
569-
&& child.vis.is_accessible_from(defining_mod, self.tcx)
570-
{
571-
let vis = self.tcx.local_visibility(def_id);
572-
self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod, macro_ev);
573-
}
574-
}
575-
true
576-
} else {
577-
false
578-
}
579-
}
580-
581-
fn update_macro_reachable_def(
582-
&mut self,
583-
def_id: LocalDefId,
584-
def_kind: DefKind,
585-
vis: ty::Visibility,
586-
module: LocalModDefId,
587-
macro_ev: EffectiveVisibility,
588-
) {
589-
self.update(def_id, macro_ev, Level::Reachable);
590-
match def_kind {
591-
// No type privacy, so can be directly marked as reachable.
592-
DefKind::Const { .. }
593-
| DefKind::Static { .. }
594-
| DefKind::TraitAlias
595-
| DefKind::TyAlias => {
596-
if vis.is_accessible_from(module, self.tcx) {
597-
self.update(def_id, macro_ev, Level::Reachable);
598-
}
599-
}
600-
601-
// Hygiene isn't really implemented for `macro_rules!` macros at the
602-
// moment. Accordingly, marking them as reachable is unwise. `macro` macros
603-
// have normal hygiene, so we can treat them like other items without type
604-
// privacy and mark them reachable.
605-
DefKind::Macro(_) => {
606-
let item = self.tcx.hir_expect_item(def_id);
607-
if let hir::ItemKind::Macro(_, MacroDef { macro_rules: false, .. }, _) = item.kind {
608-
if vis.is_accessible_from(module, self.tcx) {
609-
self.update(def_id, macro_ev, Level::Reachable);
610-
}
611-
}
612-
}
613-
614-
// We can't use a module name as the final segment of a path, except
615-
// in use statements. Since re-export checking doesn't consider
616-
// hygiene these don't need to be marked reachable. The contents of
617-
// the module, however may be reachable.
618-
DefKind::Mod => {
619-
if vis.is_accessible_from(module, self.tcx) {
620-
self.update_macro_reachable(
621-
LocalModDefId::new_unchecked(def_id),
622-
module,
623-
macro_ev,
624-
);
625-
}
626-
}
627-
628-
DefKind::Struct | DefKind::Union => {
629-
// While structs and unions have type privacy, their fields do not.
630-
let struct_def = self.tcx.adt_def(def_id);
631-
for field in &struct_def.non_enum_variant().fields {
632-
let def_id = field.did.expect_local();
633-
let field_vis = self.tcx.local_visibility(def_id);
634-
if field_vis.is_accessible_from(module, self.tcx) {
635-
self.reach(def_id, macro_ev).ty();
636-
}
637-
}
638-
}
639-
640-
// These have type privacy, so are not reachable unless they're
641-
// public, or are not namespaced at all.
642-
DefKind::AssocConst { .. }
643-
| DefKind::AssocTy
644-
| DefKind::ConstParam
645-
| DefKind::Ctor(_, _)
646-
| DefKind::Enum
647-
| DefKind::ForeignTy
648-
| DefKind::Fn
649-
| DefKind::OpaqueTy
650-
| DefKind::AssocFn
651-
| DefKind::Trait
652-
| DefKind::TyParam
653-
| DefKind::Variant
654-
| DefKind::LifetimeParam
655-
| DefKind::ExternCrate
656-
| DefKind::Use
657-
| DefKind::ForeignMod
658-
| DefKind::AnonConst
659-
| DefKind::InlineConst
660-
| DefKind::Field
661-
| DefKind::GlobalAsm
662-
| DefKind::Impl { .. }
663-
| DefKind::Closure
664-
| DefKind::SyntheticCoroutineBody => (),
665-
}
666-
}
667496
}
668497

669498
impl<'tcx> EmbargoVisitor<'tcx> {
@@ -689,13 +518,8 @@ impl<'tcx> EmbargoVisitor<'tcx> {
689518
DefKind::Use | DefKind::ExternCrate | DefKind::GlobalAsm => {}
690519
// The interface is empty, and all nested items are processed by `check_def_id`.
691520
DefKind::Mod => {}
692-
DefKind::Macro { .. } => {
693-
if let Some(item_ev) = item_ev {
694-
let (_, macro_def, _) =
695-
self.tcx.hir_expect_item(owner_id.def_id).expect_macro();
696-
self.update_reachability_from_macro(owner_id.def_id, macro_def, item_ev);
697-
}
698-
}
521+
// Effective visibilities for macros are processed earlier.
522+
DefKind::Macro { .. } => {}
699523
DefKind::ForeignTy
700524
| DefKind::Const { .. }
701525
| DefKind::Static { .. }
@@ -1815,7 +1639,6 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
18151639
let mut visitor = EmbargoVisitor {
18161640
tcx,
18171641
effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(),
1818-
macro_reachable: Default::default(),
18191642
changed: false,
18201643
};
18211644

@@ -1872,6 +1695,26 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
18721695
visitor.changed = false;
18731696
}
18741697

1698+
// FIXME: remove this once proper support for defs reachability from macros is implemented.
1699+
// See `ResolverGlobalCtxt::macro_reachable_adts` comment.
1700+
for (&adt_def_id, macro_mods) in &tcx.resolutions(()).macro_reachable_adts {
1701+
let struct_def = tcx.adt_def(adt_def_id);
1702+
let Some(struct_ev) = visitor.effective_visibilities.effective_vis(adt_def_id).copied()
1703+
else {
1704+
continue;
1705+
};
1706+
for field in &struct_def.non_enum_variant().fields {
1707+
let def_id = field.did.expect_local();
1708+
let field_vis = tcx.local_visibility(def_id);
1709+
1710+
for &macro_mod in macro_mods {
1711+
if field_vis.is_accessible_from(macro_mod, tcx) {
1712+
visitor.reach(def_id, struct_ev).ty();
1713+
}
1714+
}
1715+
}
1716+
}
1717+
18751718
let crate_items = tcx.hir_crate_items(());
18761719
loop {
18771720
for id in crate_items.free_items() {

0 commit comments

Comments
 (0)