Skip to content

Commit c5842b0

Browse files
committed
Auto merge of #103965 - petrochenkov:effvisperf3, r=oli-obk
resolve: More detailed effective visibility tracking for imports Per-`DefId` tracking is not enough, due to glob imports in particular, which have a single `DefId` for the whole glob import item. We need to track this stuff per every introduced name (`NameBinding`). Also drop `extern` blocks from the effective visibility table, they are nominally private and it doesn't make sense to keep them there. Later commits add some debug-only invariant checking and optimiaztions to mitigate regressions in #103965 (comment). This is a bugfix and continuation of #102026.
2 parents ddfe1e8 + 43bea6c commit c5842b0

File tree

8 files changed

+339
-133
lines changed

8 files changed

+339
-133
lines changed

Diff for: compiler/rustc_middle/src/middle/privacy.rs

+103-21
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
//! A pass that checks to make sure private fields and methods aren't used
22
//! outside their scopes. This pass will also generate a set of exported items
33
//! which are available for use externally when compiled as a library.
4-
use crate::ty::{DefIdTree, Visibility};
4+
use crate::ty::{DefIdTree, TyCtxt, Visibility};
55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
7+
use rustc_hir::def::DefKind;
78
use rustc_macros::HashStable;
89
use rustc_query_system::ich::StableHashingContext;
910
use rustc_span::def_id::LocalDefId;
11+
use std::hash::Hash;
1012

1113
/// Represents the levels of effective visibility an item can have.
1214
///
@@ -74,9 +76,9 @@ impl EffectiveVisibility {
7476
}
7577

7678
/// Holds a map of effective visibilities for reachable HIR nodes.
77-
#[derive(Default, Clone, Debug)]
78-
pub struct EffectiveVisibilities {
79-
map: FxHashMap<LocalDefId, EffectiveVisibility>,
79+
#[derive(Clone, Debug)]
80+
pub struct EffectiveVisibilities<Id = LocalDefId> {
81+
map: FxHashMap<Id, EffectiveVisibility>,
8082
}
8183

8284
impl EffectiveVisibilities {
@@ -111,12 +113,30 @@ impl EffectiveVisibilities {
111113
})
112114
}
113115

114-
pub fn effective_vis(&self, id: LocalDefId) -> Option<&EffectiveVisibility> {
115-
self.map.get(&id)
116-
}
117-
118-
pub fn iter(&self) -> impl Iterator<Item = (&LocalDefId, &EffectiveVisibility)> {
119-
self.map.iter()
116+
// FIXME: Share code with `fn update`.
117+
pub fn update_eff_vis(
118+
&mut self,
119+
def_id: LocalDefId,
120+
eff_vis: &EffectiveVisibility,
121+
tree: impl DefIdTree,
122+
) {
123+
use std::collections::hash_map::Entry;
124+
match self.map.entry(def_id) {
125+
Entry::Occupied(mut occupied) => {
126+
let old_eff_vis = occupied.get_mut();
127+
for l in Level::all_levels() {
128+
let vis_at_level = eff_vis.at_level(l);
129+
let old_vis_at_level = old_eff_vis.at_level_mut(l);
130+
if vis_at_level != old_vis_at_level
131+
&& vis_at_level.is_at_least(*old_vis_at_level, tree)
132+
{
133+
*old_vis_at_level = *vis_at_level
134+
}
135+
}
136+
old_eff_vis
137+
}
138+
Entry::Vacant(vacant) => vacant.insert(*eff_vis),
139+
};
120140
}
121141

122142
pub fn set_public_at_level(
@@ -137,26 +157,82 @@ impl EffectiveVisibilities {
137157
self.map.insert(id, effective_vis);
138158
}
139159

160+
pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
161+
if !cfg!(debug_assertions) {
162+
return;
163+
}
164+
for (&def_id, ev) in &self.map {
165+
// More direct visibility levels can never go farther than less direct ones,
166+
// neither of effective visibilities can go farther than nominal visibility,
167+
// and all effective visibilities are larger or equal than private visibility.
168+
let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
169+
let span = tcx.def_span(def_id.to_def_id());
170+
if !ev.direct.is_at_least(private_vis, tcx) {
171+
span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
172+
}
173+
if !ev.reexported.is_at_least(ev.direct, tcx) {
174+
span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
175+
}
176+
if !ev.reachable.is_at_least(ev.reexported, tcx) {
177+
span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
178+
}
179+
if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
180+
span_bug!(
181+
span,
182+
"reachable {:?} > reachable_through_impl_trait {:?}",
183+
ev.reachable,
184+
ev.reachable_through_impl_trait
185+
);
186+
}
187+
let nominal_vis = tcx.visibility(def_id);
188+
let def_kind = tcx.opt_def_kind(def_id);
189+
// FIXME: `rustc_privacy` is not yet updated for the new logic and can set
190+
// effective visibilities that are larger than the nominal one.
191+
if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
192+
span_bug!(
193+
span,
194+
"{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
195+
def_id,
196+
ev.reachable_through_impl_trait,
197+
nominal_vis
198+
);
199+
}
200+
// Fully private items are never put into the table, this is important for performance.
201+
// FIXME: Fully private `mod` items are currently put into the table.
202+
if ev.reachable_through_impl_trait == private_vis && def_kind != Some(DefKind::Mod) {
203+
span_bug!(span, "fully private item in the table {:?}: {:?}", def_id, ev.direct);
204+
}
205+
}
206+
}
207+
}
208+
209+
impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
210+
pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
211+
self.map.iter()
212+
}
213+
214+
pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
215+
self.map.get(&id)
216+
}
217+
140218
// `parent_id` is not necessarily a parent in source code tree,
141219
// it is the node from which the maximum effective visibility is inherited.
142220
pub fn update(
143221
&mut self,
144-
id: LocalDefId,
222+
id: Id,
145223
nominal_vis: Visibility,
146-
default_vis: impl FnOnce() -> Visibility,
147-
parent_id: LocalDefId,
224+
default_vis: Visibility,
225+
inherited_eff_vis: Option<EffectiveVisibility>,
148226
level: Level,
149227
tree: impl DefIdTree,
150228
) -> bool {
151229
let mut changed = false;
152-
let mut current_effective_vis = self.effective_vis(id).copied().unwrap_or_else(|| {
153-
if id.is_top_level_module() {
154-
EffectiveVisibility::from_vis(Visibility::Public)
155-
} else {
156-
EffectiveVisibility::from_vis(default_vis())
157-
}
158-
});
159-
if let Some(inherited_effective_vis) = self.effective_vis(parent_id) {
230+
let mut current_effective_vis = self
231+
.map
232+
.get(&id)
233+
.copied()
234+
.unwrap_or_else(|| EffectiveVisibility::from_vis(default_vis));
235+
if let Some(inherited_effective_vis) = inherited_eff_vis {
160236
let mut inherited_effective_vis_at_prev_level =
161237
*inherited_effective_vis.at_level(level);
162238
let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
@@ -194,6 +270,12 @@ impl EffectiveVisibilities {
194270
}
195271
}
196272

273+
impl<Id> Default for EffectiveVisibilities<Id> {
274+
fn default() -> Self {
275+
EffectiveVisibilities { map: Default::default() }
276+
}
277+
}
278+
197279
impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
198280
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
199281
let EffectiveVisibilities { ref map } = *self;

Diff for: compiler/rustc_privacy/src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -959,13 +959,21 @@ impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> {
959959
for variant in def.variants.iter() {
960960
let variant_id = self.tcx.hir().local_def_id(variant.id);
961961
self.effective_visibility_diagnostic(variant_id);
962+
if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
963+
let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
964+
self.effective_visibility_diagnostic(ctor_def_id);
965+
}
962966
for field in variant.data.fields() {
963967
let def_id = self.tcx.hir().local_def_id(field.hir_id);
964968
self.effective_visibility_diagnostic(def_id);
965969
}
966970
}
967971
}
968972
hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
973+
if let Some(ctor_hir_id) = def.ctor_hir_id() {
974+
let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
975+
self.effective_visibility_diagnostic(ctor_def_id);
976+
}
969977
for field in def.fields() {
970978
let def_id = self.tcx.hir().local_def_id(field.hir_id);
971979
self.effective_visibility_diagnostic(def_id);
@@ -2131,6 +2139,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
21312139
changed: false,
21322140
};
21332141

2142+
visitor.effective_visibilities.check_invariants(tcx, true);
21342143
loop {
21352144
tcx.hir().walk_toplevel_module(&mut visitor);
21362145
if visitor.changed {
@@ -2139,6 +2148,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
21392148
break;
21402149
}
21412150
}
2151+
visitor.effective_visibilities.check_invariants(tcx, false);
21422152

21432153
let mut check_visitor =
21442154
TestReachabilityVisitor { tcx, effective_visibilities: &visitor.effective_visibilities };

0 commit comments

Comments
 (0)