Auto merge of #104602 - petrochenkov:effvisperf5, r=oli-obk

privacy: Fix more (potential) issues with effective visibilities

Continuation of https://github.com/rust-lang/rust/pull/103965.
See individual commits for more detailed description of the changes.

The shortcuts removed in 4eb63f618e and c7c7d16727 could actually be correct (or correct after some tweaks), but they used global reasoning like "we can skip this update because if the code compiles then some other update should do the same thing eventually".
I have some expertise in this area, but I still have doubt whether such global reasoning was correct or not, especially in presence of all possible exotic cases with imports.
After this PR all table changes should be "locally correct" after every update, even if it may be overcautious.
If similar optimizations are introduced again they will need detailed comments explaining why it's legal to do what they do and providing proofs.

Fixes https://github.com/rust-lang/rust/issues/104249.
Fixes https://github.com/rust-lang/rust/issues/104539.
This commit is contained in:
bors
2022-11-25 06:14:42 +00:00
6 changed files with 178 additions and 101 deletions

View File

@@ -4,7 +4,6 @@
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;
@@ -142,13 +141,13 @@ impl EffectiveVisibilities {
pub fn set_public_at_level(
&mut self,
id: LocalDefId,
default_vis: impl FnOnce() -> Visibility,
lazy_private_vis: impl FnOnce() -> Visibility,
level: Level,
) {
let mut effective_vis = self
.effective_vis(id)
.copied()
.unwrap_or_else(|| EffectiveVisibility::from_vis(default_vis()));
.unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
for l in Level::all_levels() {
if l <= level {
*effective_vis.at_level_mut(l) = Visibility::Public;
@@ -185,7 +184,6 @@ impl EffectiveVisibilities {
);
}
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 {
@@ -197,15 +195,15 @@ impl EffectiveVisibilities {
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);
}
}
}
}
pub trait IntoDefIdTree {
type Tree: DefIdTree;
fn tree(self) -> Self::Tree;
}
impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
self.map.iter()
@@ -215,56 +213,65 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
self.map.get(&id)
}
// `parent_id` is not necessarily a parent in source code tree,
// it is the node from which the maximum effective visibility is inherited.
pub fn update(
// FIXME: Share code with `fn update`.
pub fn effective_vis_or_private(
&mut self,
id: Id,
lazy_private_vis: impl FnOnce() -> Visibility,
) -> &EffectiveVisibility {
self.map.entry(id).or_insert_with(|| EffectiveVisibility::from_vis(lazy_private_vis()))
}
pub fn update<T: IntoDefIdTree>(
&mut self,
id: Id,
nominal_vis: Visibility,
default_vis: Visibility,
inherited_eff_vis: Option<EffectiveVisibility>,
lazy_private_vis: impl FnOnce(T) -> (Visibility, T),
inherited_effective_vis: EffectiveVisibility,
level: Level,
tree: impl DefIdTree,
mut into_tree: T,
) -> bool {
let mut changed = false;
let mut current_effective_vis = self
.map
.get(&id)
.copied()
.unwrap_or_else(|| EffectiveVisibility::from_vis(default_vis));
if let Some(inherited_effective_vis) = inherited_eff_vis {
let mut inherited_effective_vis_at_prev_level =
*inherited_effective_vis.at_level(level);
let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
for l in Level::all_levels() {
if level >= l {
let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l);
let current_effective_vis_at_level = current_effective_vis.at_level_mut(l);
// effective visibility for id shouldn't be recalculated if
// inherited from parent_id effective visibility isn't changed at next level
if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
&& level != l)
{
calculated_effective_vis =
if nominal_vis.is_at_least(inherited_effective_vis_at_level, tree) {
inherited_effective_vis_at_level
} else {
nominal_vis
};
}
// effective visibility can't be decreased at next update call for the
// same id
if *current_effective_vis_at_level != calculated_effective_vis
&& calculated_effective_vis
.is_at_least(*current_effective_vis_at_level, tree)
{
changed = true;
*current_effective_vis_at_level = calculated_effective_vis;
}
inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
let mut current_effective_vis = match self.map.get(&id).copied() {
Some(eff_vis) => eff_vis,
None => {
let private_vis;
(private_vis, into_tree) = lazy_private_vis(into_tree);
EffectiveVisibility::from_vis(private_vis)
}
};
let tree = into_tree.tree();
let mut inherited_effective_vis_at_prev_level = *inherited_effective_vis.at_level(level);
let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
for l in Level::all_levels() {
if level >= l {
let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l);
let current_effective_vis_at_level = current_effective_vis.at_level_mut(l);
// effective visibility for id shouldn't be recalculated if
// inherited from parent_id effective visibility isn't changed at next level
if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
&& level != l)
{
calculated_effective_vis =
if nominal_vis.is_at_least(inherited_effective_vis_at_level, tree) {
inherited_effective_vis_at_level
} else {
nominal_vis
};
}
// effective visibility can't be decreased at next update call for the
// same id
if *current_effective_vis_at_level != calculated_effective_vis
&& calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tree)
{
changed = true;
*current_effective_vis_at_level = calculated_effective_vis;
}
inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
}
}
self.map.insert(id, current_effective_vis);
changed
}