Skip to content

Commit

Permalink
change AccessLevels representation +
Browse files Browse the repository at this point in the history
  • Loading branch information
Bryanskiy authored and petrochenkov committed Sep 13, 2022
1 parent 7098c18 commit 3af17a0
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 136 deletions.
134 changes: 128 additions & 6 deletions compiler/rustc_middle/src/middle/privacy.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! 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 rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_macros::HashStable;
Expand All @@ -27,26 +27,148 @@ pub enum AccessLevel {
Public,
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Default)]
pub struct EffectiveVisibility {
public: Option<Visibility>,
exported: Option<Visibility>,
reachable: Option<Visibility>,
reachable_from_impl_trait: Option<Visibility>,
}

impl EffectiveVisibility {
fn get_field(&self, tag: AccessLevel) -> &Option<Visibility> {
match tag {
AccessLevel::Public => &self.public,
AccessLevel::Exported => &self.exported,
AccessLevel::Reachable => &self.reachable,
AccessLevel::ReachableFromImplTrait => &self.reachable_from_impl_trait,
}
}

fn get_mut_field(&mut self, tag: AccessLevel) -> &mut Option<Visibility> {
match tag {
AccessLevel::Public => &mut self.public,
AccessLevel::Exported => &mut self.exported,
AccessLevel::Reachable => &mut self.reachable,
AccessLevel::ReachableFromImplTrait => &mut self.reachable_from_impl_trait,
}
}

pub fn get(&self, tag: AccessLevel) -> Option<&Visibility> {
(*self.get_field(tag)).as_ref()
}

fn get_mut(&mut self, tag: AccessLevel) -> Option<&mut Visibility> {
(*self.get_mut_field(tag)).as_mut()
}

fn set(&mut self, effective_vis: Option<Visibility>, tag: AccessLevel) {
*self.get_mut_field(tag) = effective_vis;
}

fn merge(
&mut self,
effective_vis_opt: Option<Visibility>,
tag: AccessLevel,
tree: impl DefIdTree,
) {
let current_effective_vis = self.get_mut(tag);
if let Some(effective_vis) = effective_vis_opt {
if let Some(current_effective_vis) = current_effective_vis {
if current_effective_vis.is_at_least(effective_vis, tree) {
*current_effective_vis = effective_vis;
}
} else {
self.set(effective_vis_opt, tag);
}
}
}

pub fn update(
&mut self,
effective_vis_opt: Option<Visibility>,
tag: AccessLevel,
tree: impl DefIdTree,
) {
for level in [
AccessLevel::Public,
AccessLevel::Exported,
AccessLevel::Reachable,
AccessLevel::ReachableFromImplTrait,
] {
if level <= tag {
self.merge(effective_vis_opt, level, tree);
}
}
}

fn is_public(&self, tag: AccessLevel) -> bool {
self.get(tag).map_or(false, |vis| vis.is_public())
}
}

/// Holds a map of accessibility levels for reachable HIR nodes.
#[derive(Debug, Clone)]
pub struct AccessLevels<Id = LocalDefId> {
pub map: FxHashMap<Id, AccessLevel>,
pub map: FxHashMap<Id, EffectiveVisibility>,
}

impl<Id: Hash + Eq> AccessLevels<Id> {
impl<Id: Hash + Eq + Copy> AccessLevels<Id> {
fn is_smth(&self, id: Id, tag: AccessLevel) -> bool {
self.get_effective_vis(id).map_or(false, |effective_vis| effective_vis.is_public(tag))
}

/// See `AccessLevel::Reachable`.
pub fn is_reachable(&self, id: Id) -> bool {
self.map.get(&id) >= Some(&AccessLevel::Reachable)
self.is_smth(id, AccessLevel::Reachable)
}

/// See `AccessLevel::Exported`.
pub fn is_exported(&self, id: Id) -> bool {
self.map.get(&id) >= Some(&AccessLevel::Exported)
self.is_smth(id, AccessLevel::Exported)
}

/// See `AccessLevel::Public`.
pub fn is_public(&self, id: Id) -> bool {
self.map.get(&id) >= Some(&AccessLevel::Public)
self.is_smth(id, AccessLevel::Public)
}

pub fn get_access_level(&self, id: Id) -> Option<AccessLevel> {
self.get_effective_vis(id).and_then(|effective_vis| {
for level in [
AccessLevel::Public,
AccessLevel::Exported,
AccessLevel::Reachable,
AccessLevel::ReachableFromImplTrait,
] {
if effective_vis.is_public(level) {
return Some(level);
}
}
None
})
}

pub fn set_access_level(
&mut self,
id: Id,
access_level: Option<AccessLevel>,
tree: impl DefIdTree,
) -> Option<AccessLevel> {
if let Some(tag) = access_level {
let mut effective_vis = self.get_effective_vis(id).copied().unwrap_or_default();
effective_vis.update(Some(Visibility::Public), tag, tree);
self.set_effective_vis(id, effective_vis);
}
self.get_access_level(id)
}

pub fn set_effective_vis(&mut self, id: Id, effective_vis: EffectiveVisibility) {
self.map.insert(id, effective_vis);
}

pub fn get_effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
self.map.get(&id)
}
}

Expand Down
7 changes: 1 addition & 6 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Node, PatKind, TyKind};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::privacy;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_session::lint;
Expand Down Expand Up @@ -621,11 +620,7 @@ fn create_and_seed_worklist<'tcx>(
let mut worklist = access_levels
.map
.iter()
.filter_map(
|(&id, &level)| {
if level >= privacy::AccessLevel::Reachable { Some(id) } else { None }
},
)
.filter_map(|(&id, _)| if access_levels.is_reachable(id) { Some(id) } else { None })
// Seed entry point
.chain(tcx.entry_fn(()).and_then(|(def_id, _)| def_id.as_local()))
.collect::<Vec<_>>();
Expand Down
40 changes: 34 additions & 6 deletions compiler/rustc_privacy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ impl VisibilityLike for Option<AccessLevel> {
// (which require reaching the `DefId`s in them).
const SHALLOW: bool = true;
fn new_min(find: &FindMin<'_, '_, Self>, def_id: LocalDefId) -> Self {
cmp::min(find.access_levels.map.get(&def_id).copied(), find.min)
cmp::min(find.access_levels.get_access_level(def_id), find.min)
}
}

Expand Down Expand Up @@ -416,7 +416,7 @@ struct ReachEverythingInTheInterfaceVisitor<'a, 'tcx> {

impl<'tcx> EmbargoVisitor<'tcx> {
fn get(&self, def_id: LocalDefId) -> Option<AccessLevel> {
self.access_levels.map.get(&def_id).copied()
self.access_levels.get_access_level(def_id)
}

fn update_with_hir_id(
Expand All @@ -433,7 +433,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
let old_level = self.get(def_id);
// Accessibility levels can only grow.
if level > old_level {
self.access_levels.map.insert(def_id, level.unwrap());
self.access_levels.set_access_level(def_id, level, self.tcx);
self.changed = true;
level
} else {
Expand Down Expand Up @@ -914,10 +914,38 @@ pub struct TestReachabilityVisitor<'tcx, 'a> {

impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> {
fn access_level_diagnostic(&mut self, def_id: LocalDefId) {
let span = self.tcx.def_span(def_id.to_def_id());
if self.tcx.has_attr(def_id.to_def_id(), sym::rustc_access_level) {
let access_level = format!("{:?}", self.access_levels.map.get(&def_id));
let span = self.tcx.def_span(def_id.to_def_id());
self.tcx.sess.emit_err(ReportAccessLevel { span, descr: access_level });
let mut error_msg = String::new();

let effective_vis =
self.access_levels.get_effective_vis(def_id).copied().unwrap_or_default();
let mut process_effective_vis = |tag: AccessLevel| {
let vis = effective_vis.get(tag);

let vis_str = match vis {
Some(ty::Visibility::Restricted(restricted_id)) => {
format!("pub({})", self.tcx.item_name(restricted_id.to_def_id()).as_str())
}
Some(ty::Visibility::Public) => "pub".to_string(),
None => format!("{:?}", vis),
};
if tag != AccessLevel::Public {
error_msg.push(',');
error_msg.push(' ');
}
error_msg.push_str(format!("{:?}: {}", tag, vis_str).as_str());
};

for level in [
AccessLevel::Public,
AccessLevel::Exported,
AccessLevel::Reachable,
AccessLevel::ReachableFromImplTrait,
] {
process_effective_vis(level);
}
self.tcx.sess.emit_err(ReportAccessLevel { span, descr: error_msg });
}
}
}
Expand Down
28 changes: 22 additions & 6 deletions compiler/rustc_resolve/src/access_levels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_hir::def_id::LocalDefId;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_middle::middle::privacy::AccessLevel;
use rustc_middle::ty::DefIdTree;
use rustc_middle::ty::Visibility;
use rustc_span::sym;

pub struct AccessLevelsVisitor<'r, 'a> {
Expand Down Expand Up @@ -46,7 +47,7 @@ impl<'r, 'a> AccessLevelsVisitor<'r, 'a> {
/// This will also follow `use` chains (see PrivacyVisitor::set_import_binding_access_level).
fn set_bindings_access_level(&mut self, module_id: LocalDefId) {
assert!(self.r.module_map.contains_key(&&module_id.to_def_id()));
let module_level = self.r.access_levels.map.get(&module_id).copied();
let module_level = self.r.access_levels.get_access_level(module_id);
if !module_level.is_some() {
return;
}
Expand Down Expand Up @@ -103,15 +104,30 @@ impl<'r, 'a> AccessLevelsVisitor<'r, 'a> {
def_id: LocalDefId,
access_level: Option<AccessLevel>,
) -> Option<AccessLevel> {
let old_level = self.r.access_levels.map.get(&def_id).copied();
let old_level = self.r.access_levels.get_access_level(def_id);
if old_level < access_level {
self.r.access_levels.map.insert(def_id, access_level.unwrap());
self.set_access_level_wrapper(def_id, access_level);
self.changed = true;
access_level
} else {
old_level
}
}

fn set_access_level_wrapper(
&mut self,
id: LocalDefId,
access_level: Option<AccessLevel>,
) -> Option<AccessLevel> {
if let Some(tag) = access_level {
let mut effective_vis =
self.r.access_levels.get_effective_vis(id).copied().unwrap_or_default();

effective_vis.update(Some(Visibility::Public), tag, &*self.r);
self.r.access_levels.set_effective_vis(id, effective_vis);
}
self.r.access_levels.get_access_level(id)
}
}

impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
Expand All @@ -131,7 +147,7 @@ impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
// Foreign modules inherit level from parents.
ast::ItemKind::ForeignMod(..) => {
let parent_level =
self.r.access_levels.map.get(&self.r.local_parent(def_id)).copied();
self.r.access_levels.get_access_level(self.r.local_parent(def_id));
self.set_access_level(item.id, parent_level);
}

Expand All @@ -151,15 +167,15 @@ impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
self.set_bindings_access_level(def_id);
for variant in variants {
let variant_def_id = self.r.local_def_id(variant.id);
let variant_level = self.r.access_levels.map.get(&variant_def_id).copied();
let variant_level = self.r.access_levels.get_access_level(variant_def_id);
for field in variant.data.fields() {
self.set_access_level(field.id, variant_level);
}
}
}

ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
let inherited_level = self.r.access_levels.map.get(&def_id).copied();
let inherited_level = self.r.access_levels.get_access_level(def_id);
for field in def.fields() {
if field.vis.kind.is_pub() {
self.set_access_level(field.id, inherited_level);
Expand Down
6 changes: 5 additions & 1 deletion src/librustdoc/visit_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
} else {
// All items need to be handled here in case someone wishes to link
// to them with intra-doc links
self.cx.cache.access_levels.map.insert(did, AccessLevel::Public);
self.cx.cache.access_levels.set_access_level(
did,
Some(AccessLevel::Public),
self.cx.tcx,
);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/visit_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ impl<'a, 'tcx> LibEmbargoVisitor<'a, 'tcx> {
fn update(&mut self, did: DefId, level: Option<AccessLevel>) -> Option<AccessLevel> {
let is_hidden = self.tcx.is_doc_hidden(did);

let old_level = self.access_levels.map.get(&did).cloned();
let old_level = self.access_levels.get_access_level(did);
// Accessibility levels can only grow
if level > old_level && !is_hidden {
self.access_levels.map.insert(did, level.unwrap());
self.access_levels.set_access_level(did, level, self.tcx);
level
} else {
old_level
Expand Down
Loading

0 comments on commit 3af17a0

Please sign in to comment.