Skip to content

Commit 39888c4

Browse files
committed
resolve/effective-visibility: Stop recalculating current normal module
It becomes relatively expensive if done often and shows up during perf profiling 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 33b55ac commit 39888c4

File tree

3 files changed

+73
-44
lines changed

3 files changed

+73
-44
lines changed

compiler/rustc_resolve/src/effective_visibilities.rs

+71-42
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ use rustc_ast::visit;
44
use rustc_ast::visit::Visitor;
55
use rustc_ast::Crate;
66
use rustc_ast::EnumDef;
7+
use rustc_hir::def::{DefKind, Res};
78
use rustc_hir::def_id::LocalDefId;
89
use rustc_hir::def_id::CRATE_DEF_ID;
910
use rustc_middle::middle::privacy::Level;
1011
use rustc_middle::ty::{DefIdTree, Visibility};
12+
use std::mem;
1113

1214
pub struct EffectiveVisibilitiesVisitor<'r, 'a> {
1315
r: &'r mut Resolver<'a>,
16+
// It's possible to recalculate this at any point, but it's relatively expensive.
17+
current_normal_mod: LocalDefId,
1418
changed: bool,
1519
}
1620

@@ -19,21 +23,22 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
1923
/// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we
2024
/// need access to a TyCtxt for that.
2125
pub fn compute_effective_visibilities<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
22-
let mut visitor = EffectiveVisibilitiesVisitor { r, changed: false };
26+
let mut visitor =
27+
EffectiveVisibilitiesVisitor { r, current_normal_mod: CRATE_DEF_ID, changed: false };
2328

24-
visitor.update(CRATE_DEF_ID, Visibility::Public, CRATE_DEF_ID, Level::Direct);
29+
visitor.update(CRATE_DEF_ID, CRATE_DEF_ID);
2530
visitor.set_bindings_effective_visibilities(CRATE_DEF_ID);
2631

2732
while visitor.changed {
28-
visitor.reset();
33+
visitor.changed = false;
2934
visit::walk_crate(&mut visitor, krate);
3035
}
3136

3237
info!("resolve::effective_visibilities: {:#?}", r.effective_visibilities);
3338
}
3439

35-
fn reset(&mut self) {
36-
self.changed = false;
40+
fn nearest_normal_mod(&mut self, def_id: LocalDefId) -> LocalDefId {
41+
self.r.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local()
3742
}
3843

3944
/// Update effective visibilities of bindings in the given module,
@@ -49,20 +54,30 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
4954
// sets the rest of the `use` chain to `Level::Reexported` until
5055
// we hit the actual exported item.
5156

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 };
57+
// FIXME: level and is_public() condition should be removed, but assertions occur.
58+
let level = if binding.is_import() { Level::Reexported } else { Level::Direct };
59+
let mut normal_mod_id = self.current_normal_mod;
5460
if binding.vis.is_public() {
5561
let mut prev_parent_id = module_id;
5662
let mut level = Level::Direct;
5763
while let NameBindingKind::Import { binding: nested_binding, import, .. } =
5864
binding.kind
5965
{
60-
let mut update = |node_id| self.update(
61-
self.r.local_def_id(node_id),
62-
binding.vis.expect_local(),
63-
prev_parent_id,
64-
level,
65-
);
66+
let def_id = self.r.local_def_id(import.id);
67+
if level != Level::Direct {
68+
// Not a first binding in the chain, recalculate parent module.
69+
normal_mod_id = self.nearest_normal_mod(def_id);
70+
}
71+
72+
let mut update = |node_id| {
73+
self.update_ext(
74+
self.r.local_def_id(node_id),
75+
binding.vis.expect_local(),
76+
prev_parent_id,
77+
normal_mod_id,
78+
level,
79+
)
80+
};
6681
// In theory all the import IDs have individual visibilities and effective
6782
// visibilities, but in practice these IDs go straigth to HIR where all
6883
// their few uses assume that their (effective) visibility applies to the
@@ -76,46 +91,69 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
7691
}
7792

7893
level = Level::Reexported;
79-
prev_parent_id = self.r.local_def_id(import.id);
94+
prev_parent_id = def_id;
8095
binding = nested_binding;
8196
}
8297
}
8398

84-
if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) {
85-
self.update(def_id, binding.vis.expect_local(), module_id, tag);
99+
let res = binding.res();
100+
if let Some(def_id) = res.opt_def_id().and_then(|id| id.as_local()) {
101+
if level != Level::Direct
102+
|| matches!(binding.kind, NameBindingKind::Res(_, _is_macro_export @ true))
103+
// FIXME: This condition is incorrect, but it preserves pre-existing logic.
104+
// Rewrite update logic to support parent nodes that are fully
105+
// private and not in the table (which makes sense for `mod` items).
106+
|| matches!(res, Res::Def(DefKind::Mod, _))
107+
{
108+
// Not a first binding in the chain, recalculate parent module.
109+
normal_mod_id = self.nearest_normal_mod(def_id);
110+
}
111+
self.update_ext(
112+
def_id,
113+
binding.vis.expect_local(),
114+
module_id,
115+
normal_mod_id,
116+
level,
117+
);
86118
}
87119
}
88120
}
89121
}
90122

91-
fn update(
123+
fn update_ext(
92124
&mut self,
93125
def_id: LocalDefId,
94126
nominal_vis: Visibility,
95127
parent_id: LocalDefId,
96-
tag: Level,
128+
normal_mod_id: LocalDefId,
129+
level: Level,
97130
) {
98-
let module_id = self
99-
.r
100-
.get_nearest_non_block_module(def_id.to_def_id())
101-
.nearest_parent_mod()
102-
.expect_local();
103-
if nominal_vis == Visibility::Restricted(module_id)
104-
|| self.r.visibilities[&parent_id] == Visibility::Restricted(module_id)
131+
if nominal_vis == Visibility::Restricted(normal_mod_id)
132+
|| self.r.visibilities[&parent_id] == Visibility::Restricted(normal_mod_id)
105133
{
106134
return;
107135
}
108136
let mut effective_visibilities = std::mem::take(&mut self.r.effective_visibilities);
109137
self.changed |= effective_visibilities.update(
110138
def_id,
111139
nominal_vis,
112-
|| Visibility::Restricted(module_id),
140+
|| Visibility::Restricted(normal_mod_id),
113141
parent_id,
114-
tag,
142+
level,
115143
&*self.r,
116144
);
117145
self.r.effective_visibilities = effective_visibilities;
118146
}
147+
148+
fn update(&mut self, def_id: LocalDefId, parent_id: LocalDefId) {
149+
self.update_ext(
150+
def_id,
151+
self.r.visibilities[&def_id],
152+
parent_id,
153+
self.current_normal_mod,
154+
Level::Direct,
155+
);
156+
}
119157
}
120158

121159
impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
@@ -132,41 +170,31 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
132170
"ast::ItemKind::MacCall encountered, this should not anymore appear at this stage"
133171
),
134172

135-
// Foreign modules inherit level from parents.
136-
ast::ItemKind::ForeignMod(..) => {
137-
let parent_id = self.r.local_parent(def_id);
138-
self.update(def_id, Visibility::Public, parent_id, Level::Direct);
139-
}
140-
141173
// Only exported `macro_rules!` items are public, but they always are
142174
ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => {
143-
let parent_id = self.r.local_parent(def_id);
144-
let vis = self.r.visibilities[&def_id];
145-
self.update(def_id, vis, parent_id, Level::Direct);
175+
self.update(def_id, self.r.local_parent(def_id));
146176
}
147177

148178
ast::ItemKind::Mod(..) => {
179+
let prev_normal_mod = mem::replace(&mut self.current_normal_mod, def_id);
149180
self.set_bindings_effective_visibilities(def_id);
150181
visit::walk_item(self, item);
182+
self.current_normal_mod = prev_normal_mod;
151183
}
152184

153185
ast::ItemKind::Enum(EnumDef { ref variants }, _) => {
154186
self.set_bindings_effective_visibilities(def_id);
155187
for variant in variants {
156188
let variant_def_id = self.r.local_def_id(variant.id);
157189
for field in variant.data.fields() {
158-
let field_def_id = self.r.local_def_id(field.id);
159-
let vis = self.r.visibilities[&field_def_id];
160-
self.update(field_def_id, vis, variant_def_id, Level::Direct);
190+
self.update(self.r.local_def_id(field.id), variant_def_id);
161191
}
162192
}
163193
}
164194

165195
ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
166196
for field in def.fields() {
167-
let field_def_id = self.r.local_def_id(field.id);
168-
let vis = self.r.visibilities[&field_def_id];
169-
self.update(field_def_id, vis, def_id, Level::Direct);
197+
self.update(self.r.local_def_id(field.id), def_id);
170198
}
171199
}
172200

@@ -182,6 +210,7 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
182210
| ast::ItemKind::TyAlias(..)
183211
| ast::ItemKind::TraitAlias(..)
184212
| ast::ItemKind::MacroDef(..)
213+
| ast::ItemKind::ForeignMod(..)
185214
| ast::ItemKind::Fn(..) => return,
186215
}
187216
}

src/test/ui/privacy/effective_visibilities.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod outer { //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub
66
pub mod inner1 { //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
77

88
#[rustc_effective_visibility]
9-
extern "C" {} //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
9+
extern "C" {} //~ ERROR not in the table
1010

1111
#[rustc_effective_visibility]
1212
pub trait PubTrait { //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub

src/test/ui/privacy/effective_visibilities.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImpl
1010
LL | pub mod inner1 {
1111
| ^^^^^^^^^^^^^^
1212

13-
error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
13+
error: not in the table
1414
--> $DIR/effective_visibilities.rs:9:9
1515
|
1616
LL | extern "C" {}

0 commit comments

Comments
 (0)