diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 3d7a379c56c..11fbefefcc9 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -1,9 +1,10 @@ //! A pass that checks to make sure private fields and methods aren't used //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. -use crate::ty::{DefIdTree, Visibility}; +use crate::ty::{DefIdTree, TyCtxt, Visibility}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::def::DefKind; use rustc_macros::HashStable; use rustc_query_system::ich::StableHashingContext; use rustc_span::def_id::LocalDefId; @@ -133,6 +134,54 @@ impl EffectiveVisibilities { } self.map.insert(id, effective_vis); } + + pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) { + if !cfg!(debug_assertions) { + return; + } + for (&def_id, ev) in &self.map { + // More direct visibility levels can never go farther than less direct ones, + // neither of effective visibilities can go farther than nominal visibility, + // and all effective visibilities are larger or equal than private visibility. + let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id)); + let span = tcx.def_span(def_id.to_def_id()); + if !ev.direct.is_at_least(private_vis, tcx) { + span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct); + } + if !ev.reexported.is_at_least(ev.direct, tcx) { + span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported); + } + if !ev.reachable.is_at_least(ev.reexported, tcx) { + span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable); + } + if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) { + span_bug!( + span, + "reachable {:?} > reachable_through_impl_trait {:?}", + ev.reachable, + ev.reachable_through_impl_trait + ); + } + let nominal_vis = tcx.visibility(def_id); + let def_kind = tcx.opt_def_kind(def_id); + // FIXME: `rustc_privacy` is not yet updated for the new logic and can set + // effective visibilities that are larger than the nominal one. + if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early { + span_bug!( + span, + "{:?}: reachable_through_impl_trait {:?} > nominal {:?}", + def_id, + ev.reachable_through_impl_trait, + nominal_vis + ); + } + // Fully private items are never put into the table, this is important for performance. + // FIXME: Fully private `mod` items are currently put into the table. + if ev.reachable_through_impl_trait == private_vis && def_kind != Some(DefKind::Mod) { + span_bug!(span, "fully private item in the table {:?}: {:?}", def_id, ev.direct); + } + } + } } impl EffectiveVisibilities { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index bda3b55cfc3..e17f85c1aae 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -2139,6 +2139,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { changed: false, }; + visitor.effective_visibilities.check_invariants(tcx, true); loop { tcx.hir().walk_toplevel_module(&mut visitor); if visitor.changed { @@ -2147,6 +2148,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { break; } } + visitor.effective_visibilities.check_invariants(tcx, false); let mut check_visitor = TestReachabilityVisitor { tcx, effective_visibilities: &visitor.effective_visibilities };