Skip to content

Commit 24093fc

Browse files
committed
resolve: More detailed effective visibility tracking for imports
Also drop `extern` blocks from the effective visibility table, they are nominally private and it doesn't make sense to keep them there.
1 parent 452cf4f commit 24093fc

File tree

7 files changed

+212
-106
lines changed

7 files changed

+212
-106
lines changed

compiler/rustc_middle/src/middle/privacy.rs

+25-18
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
77
use rustc_macros::HashStable;
88
use rustc_query_system::ich::StableHashingContext;
99
use rustc_span::def_id::LocalDefId;
10+
use std::hash::Hash;
1011

1112
/// Represents the levels of effective visibility an item can have.
1213
///
@@ -74,9 +75,9 @@ impl EffectiveVisibility {
7475
}
7576

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

8283
impl EffectiveVisibilities {
@@ -111,10 +112,6 @@ impl EffectiveVisibilities {
111112
})
112113
}
113114

114-
pub fn effective_vis(&self, id: LocalDefId) -> Option<&EffectiveVisibility> {
115-
self.map.get(&id)
116-
}
117-
118115
pub fn iter(&self) -> impl Iterator<Item = (&LocalDefId, &EffectiveVisibility)> {
119116
self.map.iter()
120117
}
@@ -136,27 +133,31 @@ impl EffectiveVisibilities {
136133
}
137134
self.map.insert(id, effective_vis);
138135
}
136+
}
137+
138+
impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
139+
pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
140+
self.map.get(&id)
141+
}
139142

140143
// `parent_id` is not necessarily a parent in source code tree,
141144
// it is the node from which the maximum effective visibility is inherited.
142145
pub fn update(
143146
&mut self,
144-
id: LocalDefId,
147+
id: Id,
145148
nominal_vis: Visibility,
146-
default_vis: impl FnOnce() -> Visibility,
147-
parent_id: LocalDefId,
149+
default_vis: Visibility,
150+
inherited_eff_vis: Option<EffectiveVisibility>,
148151
level: Level,
149152
tree: impl DefIdTree,
150153
) -> bool {
151154
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) {
155+
let mut current_effective_vis = self
156+
.map
157+
.get(&id)
158+
.copied()
159+
.unwrap_or_else(|| EffectiveVisibility::from_vis(default_vis));
160+
if let Some(inherited_effective_vis) = inherited_eff_vis {
160161
let mut inherited_effective_vis_at_prev_level =
161162
*inherited_effective_vis.at_level(level);
162163
let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
@@ -194,6 +195,12 @@ impl EffectiveVisibilities {
194195
}
195196
}
196197

198+
impl<Id> Default for EffectiveVisibilities<Id> {
199+
fn default() -> Self {
200+
EffectiveVisibilities { map: Default::default() }
201+
}
202+
}
203+
197204
impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
198205
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
199206
let EffectiveVisibilities { ref map } = *self;

compiler/rustc_resolve/src/effective_visibilities.rs

+122-83
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,38 @@
1-
use crate::{ImportKind, NameBindingKind, Resolver};
1+
use crate::{ImportKind, NameBinding, NameBindingKind, Resolver, ResolverTree};
22
use rustc_ast::ast;
33
use rustc_ast::visit;
44
use rustc_ast::visit::Visitor;
55
use rustc_ast::Crate;
66
use rustc_ast::EnumDef;
7+
use rustc_data_structures::intern::Interned;
78
use rustc_hir::def_id::LocalDefId;
89
use rustc_hir::def_id::CRATE_DEF_ID;
9-
use rustc_middle::middle::privacy::Level;
10-
use rustc_middle::ty::{DefIdTree, Visibility};
10+
use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
11+
use rustc_middle::ty::Visibility;
12+
13+
type ImportId<'a> = Interned<'a, NameBinding<'a>>;
14+
15+
#[derive(Clone, Copy)]
16+
enum ParentId<'a> {
17+
Def(LocalDefId),
18+
Import(ImportId<'a>),
19+
}
20+
21+
impl ParentId<'_> {
22+
fn level(self) -> Level {
23+
match self {
24+
ParentId::Def(_) => Level::Direct,
25+
ParentId::Import(_) => Level::Reexported,
26+
}
27+
}
28+
}
1129

1230
pub struct EffectiveVisibilitiesVisitor<'r, 'a> {
1331
r: &'r mut Resolver<'a>,
32+
/// While walking import chains we need to track effective visibilities per-binding, and def id
33+
/// keys in `Resolver::effective_visibilities` are not enough for that, because multiple
34+
/// bindings can correspond to a single def id in imports. So we keep a separate table.
35+
import_effective_visibilities: EffectiveVisibilities<ImportId<'a>>,
1436
changed: bool,
1537
}
1638

@@ -19,21 +41,25 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
1941
/// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we
2042
/// need access to a TyCtxt for that.
2143
pub fn compute_effective_visibilities<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
22-
let mut visitor = EffectiveVisibilitiesVisitor { r, changed: false };
44+
let mut visitor = EffectiveVisibilitiesVisitor {
45+
r,
46+
import_effective_visibilities: Default::default(),
47+
changed: false,
48+
};
2349

24-
visitor.update(CRATE_DEF_ID, Visibility::Public, CRATE_DEF_ID, Level::Direct);
50+
visitor.update(CRATE_DEF_ID, CRATE_DEF_ID);
2551
visitor.set_bindings_effective_visibilities(CRATE_DEF_ID);
2652

2753
while visitor.changed {
28-
visitor.reset();
54+
visitor.changed = false;
2955
visit::walk_crate(&mut visitor, krate);
3056
}
3157

3258
info!("resolve::effective_visibilities: {:#?}", r.effective_visibilities);
3359
}
3460

35-
fn reset(&mut self) {
36-
self.changed = false;
61+
fn nearest_normal_mod(&mut self, def_id: LocalDefId) -> LocalDefId {
62+
self.r.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local()
3763
}
3864

3965
/// Update effective visibilities of bindings in the given module,
@@ -48,92 +74,114 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
4874
// Set the given effective visibility level to `Level::Direct` and
4975
// sets the rest of the `use` chain to `Level::Reexported` until
5076
// we hit the actual exported item.
51-
52-
// FIXME: tag and is_public() condition should be removed, but assertions occur.
53-
let tag = if binding.is_import() { Level::Reexported } else { Level::Direct };
54-
if binding.vis.is_public() {
55-
let mut prev_parent_id = module_id;
56-
let mut level = Level::Direct;
57-
while let NameBindingKind::Import { binding: nested_binding, import, .. } =
58-
binding.kind
59-
{
77+
let mut parent_id = ParentId::Def(module_id);
78+
while let NameBindingKind::Import { binding: nested_binding, import, .. } =
79+
binding.kind
80+
{
81+
let binding_id = ImportId::new_unchecked(binding);
82+
self.update_import(binding_id, parent_id);
83+
84+
// Update visibilities for import ids. These are not used during this pass,
85+
// because we have more detailed binding-based information, but are used by
86+
// later passes. Effective visibility of an import def id is the maximum value
87+
// among visibilities of bindings corresponding to that def id.
88+
if let Some(node_id) = import.id() {
6089
let mut update = |node_id| {
61-
self.update(
90+
self.update_def(
6291
self.r.local_def_id(node_id),
6392
binding.vis.expect_local(),
64-
prev_parent_id,
65-
level,
93+
parent_id,
6694
)
6795
};
68-
match import.kind {
69-
ImportKind::Single { id, additional_ids, .. } => {
70-
// In theory all the import IDs have individual visibilities and
71-
// effective visibilities, but in practice these IDs go straigth to
72-
// HIR where all their few uses assume that their (effective)
73-
// visibility applies to the whole syntactic `use` item. So we
74-
// update them all to the maximum value among the potential
75-
// individual effective visibilities. Maybe HIR for imports
76-
// shouldn't use three IDs at all.
77-
update(id);
78-
update(additional_ids.0);
79-
update(additional_ids.1);
80-
prev_parent_id = self.r.local_def_id(id);
81-
}
82-
ImportKind::Glob { id, .. } | ImportKind::ExternCrate { id, .. } => {
83-
update(id);
84-
prev_parent_id = self.r.local_def_id(id);
96+
update(node_id);
97+
if let ImportKind::Single { additional_ids: (id1, id2), .. } = import.kind {
98+
// In theory all the single import IDs have individual visibilities and
99+
// effective visibilities, but in practice these IDs go straigth to HIR
100+
// where all their few uses assume that their (effective) visibility
101+
// applies to the whole syntactic `use` item. So they all get the same
102+
// value which is the maximum of all bindings. Maybe HIR for imports
103+
// shouldn't use three IDs at all.
104+
if id1 != ast::DUMMY_NODE_ID {
105+
update(id1);
85106
}
86-
ImportKind::MacroUse => {
87-
// In theory we should reset the parent id to something private
88-
// here, but `macro_use` imports always refer to external items,
89-
// so it doesn't matter and we can just do nothing.
90-
}
91-
ImportKind::MacroExport => {
92-
// In theory we should reset the parent id to something public
93-
// here, but it has the same effect as leaving the previous parent,
94-
// so we can just do nothing.
107+
if id2 != ast::DUMMY_NODE_ID {
108+
update(id2);
95109
}
96110
}
97-
98-
level = Level::Reexported;
99-
binding = nested_binding;
100111
}
112+
113+
parent_id = ParentId::Import(binding_id);
114+
binding = nested_binding;
101115
}
102116

103117
if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) {
104-
self.update(def_id, binding.vis.expect_local(), module_id, tag);
118+
self.update_def(def_id, binding.vis.expect_local(), parent_id);
105119
}
106120
}
107121
}
108122
}
109123

110-
fn update(
111-
&mut self,
112-
def_id: LocalDefId,
124+
fn effective_vis(&self, parent_id: ParentId<'a>) -> Option<EffectiveVisibility> {
125+
match parent_id {
126+
ParentId::Def(def_id) => self.r.effective_visibilities.effective_vis(def_id),
127+
ParentId::Import(binding) => self.import_effective_visibilities.effective_vis(binding),
128+
}
129+
.copied()
130+
}
131+
132+
/// The update is guaranteed to not change the table and we can skip it.
133+
fn is_noop_update(
134+
&self,
135+
parent_id: ParentId<'a>,
113136
nominal_vis: Visibility,
114-
parent_id: LocalDefId,
115-
tag: Level,
116-
) {
117-
let module_id = self
118-
.r
119-
.get_nearest_non_block_module(def_id.to_def_id())
120-
.nearest_parent_mod()
121-
.expect_local();
122-
if nominal_vis == Visibility::Restricted(module_id)
123-
|| self.r.visibilities[&parent_id] == Visibility::Restricted(module_id)
124-
{
137+
default_vis: Visibility,
138+
) -> bool {
139+
nominal_vis == default_vis
140+
|| match parent_id {
141+
ParentId::Def(def_id) => self.r.visibilities[&def_id],
142+
ParentId::Import(binding) => binding.vis.expect_local(),
143+
} == default_vis
144+
}
145+
146+
fn update_import(&mut self, binding: ImportId<'a>, parent_id: ParentId<'a>) {
147+
let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
148+
let nominal_vis = binding.vis.expect_local();
149+
let default_vis = Visibility::Restricted(
150+
import
151+
.id()
152+
.map(|id| self.nearest_normal_mod(self.r.local_def_id(id)))
153+
.unwrap_or(CRATE_DEF_ID),
154+
);
155+
if self.is_noop_update(parent_id, nominal_vis, default_vis) {
156+
return;
157+
}
158+
self.changed |= self.import_effective_visibilities.update(
159+
binding,
160+
nominal_vis,
161+
default_vis,
162+
self.effective_vis(parent_id),
163+
parent_id.level(),
164+
ResolverTree(&self.r.definitions, &self.r.crate_loader),
165+
);
166+
}
167+
168+
fn update_def(&mut self, def_id: LocalDefId, nominal_vis: Visibility, parent_id: ParentId<'a>) {
169+
let default_vis = Visibility::Restricted(self.nearest_normal_mod(def_id));
170+
if self.is_noop_update(parent_id, nominal_vis, default_vis) {
125171
return;
126172
}
127-
let mut effective_visibilities = std::mem::take(&mut self.r.effective_visibilities);
128-
self.changed |= effective_visibilities.update(
173+
self.changed |= self.r.effective_visibilities.update(
129174
def_id,
130175
nominal_vis,
131-
|| Visibility::Restricted(module_id),
132-
parent_id,
133-
tag,
134-
&*self.r,
176+
if def_id == CRATE_DEF_ID { Visibility::Public } else { default_vis },
177+
self.effective_vis(parent_id),
178+
parent_id.level(),
179+
ResolverTree(&self.r.definitions, &self.r.crate_loader),
135180
);
136-
self.r.effective_visibilities = effective_visibilities;
181+
}
182+
183+
fn update(&mut self, def_id: LocalDefId, parent_id: LocalDefId) {
184+
self.update_def(def_id, self.r.visibilities[&def_id], ParentId::Def(parent_id));
137185
}
138186
}
139187

@@ -151,12 +199,6 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
151199
"ast::ItemKind::MacCall encountered, this should not anymore appear at this stage"
152200
),
153201

154-
// Foreign modules inherit level from parents.
155-
ast::ItemKind::ForeignMod(..) => {
156-
let parent_id = self.r.local_parent(def_id);
157-
self.update(def_id, Visibility::Public, parent_id, Level::Direct);
158-
}
159-
160202
ast::ItemKind::Mod(..) => {
161203
self.set_bindings_effective_visibilities(def_id);
162204
visit::walk_item(self, item);
@@ -167,18 +209,14 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
167209
for variant in variants {
168210
let variant_def_id = self.r.local_def_id(variant.id);
169211
for field in variant.data.fields() {
170-
let field_def_id = self.r.local_def_id(field.id);
171-
let vis = self.r.visibilities[&field_def_id];
172-
self.update(field_def_id, vis, variant_def_id, Level::Direct);
212+
self.update(self.r.local_def_id(field.id), variant_def_id);
173213
}
174214
}
175215
}
176216

177217
ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
178218
for field in def.fields() {
179-
let field_def_id = self.r.local_def_id(field.id);
180-
let vis = self.r.visibilities[&field_def_id];
181-
self.update(field_def_id, vis, def_id, Level::Direct);
219+
self.update(self.r.local_def_id(field.id), def_id);
182220
}
183221
}
184222

@@ -194,6 +232,7 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
194232
| ast::ItemKind::TyAlias(..)
195233
| ast::ItemKind::TraitAlias(..)
196234
| ast::ItemKind::MacroDef(..)
235+
| ast::ItemKind::ForeignMod(..)
197236
| ast::ItemKind::Fn(..) => return,
198237
}
199238
}

0 commit comments

Comments
 (0)