Skip to content

Commit

Permalink
rustc_privacy: avoid using TypeckTables::empty for {Name,Type}Privacy…
Browse files Browse the repository at this point in the history
…Visitor.
  • Loading branch information
eddyb committed Jul 2, 2020
1 parent 883ece4 commit 780d6cb
Showing 1 changed file with 58 additions and 98 deletions.
156 changes: 58 additions & 98 deletions src/librustc_privacy/lib.rs
Expand Up @@ -2,6 +2,7 @@
#![feature(in_band_lifetimes)]
#![feature(nll)]
#![feature(or_patterns)]
#![cfg_attr(bootstrap, feature(track_caller))]
#![recursion_limit = "256"]

use rustc_attr as attr;
Expand Down Expand Up @@ -345,17 +346,6 @@ fn def_id_visibility<'tcx>(
}
}

// Set the correct `TypeckTables` for the given `item_id` (or an empty table if
// there is no `TypeckTables` for the item).
fn item_tables<'a, 'tcx>(
tcx: TyCtxt<'tcx>,
hir_id: hir::HirId,
empty_tables: &'a ty::TypeckTables<'tcx>,
) -> &'a ty::TypeckTables<'tcx> {
let def_id = tcx.hir().local_def_id(hir_id);
if tcx.has_typeck_tables(def_id) { tcx.typeck_tables_of(def_id) } else { empty_tables }
}

fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visibility {
if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 }
}
Expand Down Expand Up @@ -1029,14 +1019,21 @@ impl DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
/// This pass performs remaining checks for fields in struct expressions and patterns.
//////////////////////////////////////////////////////////////////////////////////////

struct NamePrivacyVisitor<'a, 'tcx> {
struct NamePrivacyVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>,
current_item: Option<hir::HirId>,
empty_tables: &'a ty::TypeckTables<'tcx>,
}

impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
impl<'tcx> NamePrivacyVisitor<'tcx> {
/// Gets the type-checking side-tables for the current body.
/// As this will ICE if called outside bodies, only call when working with
/// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
#[track_caller]
fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
self.maybe_typeck_tables.expect("`NamePrivacyVisitor::tables` called outside of body")
}

// Checks that a field in a struct constructor (expression or pattern) is accessible.
fn check_field(
&mut self,
Expand Down Expand Up @@ -1072,7 +1069,7 @@ impl<'a, 'tcx> NamePrivacyVisitor<'a, 'tcx> {
}
}

impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
type Map = Map<'tcx>;

/// We want to visit items in the context of their containing
Expand All @@ -1087,39 +1084,22 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
}

fn visit_nested_body(&mut self, body: hir::BodyId) {
let orig_tables = mem::replace(&mut self.tables, self.tcx.body_tables(body));
let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body));
let body = self.tcx.hir().body(body);
self.visit_body(body);
self.tables = orig_tables;
self.maybe_typeck_tables = old_maybe_typeck_tables;
}

fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
let orig_current_item = mem::replace(&mut self.current_item, Some(item.hir_id));
let orig_tables =
mem::replace(&mut self.tables, item_tables(self.tcx, item.hir_id, self.empty_tables));
let orig_current_item = self.current_item.replace(item.hir_id);
intravisit::walk_item(self, item);
self.current_item = orig_current_item;
self.tables = orig_tables;
}

fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
let orig_tables =
mem::replace(&mut self.tables, item_tables(self.tcx, ti.hir_id, self.empty_tables));
intravisit::walk_trait_item(self, ti);
self.tables = orig_tables;
}

fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
let orig_tables =
mem::replace(&mut self.tables, item_tables(self.tcx, ii.hir_id, self.empty_tables));
intravisit::walk_impl_item(self, ii);
self.tables = orig_tables;
}

fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
if let hir::ExprKind::Struct(ref qpath, fields, ref base) = expr.kind {
let res = self.tables.qpath_res(qpath, expr.hir_id);
let adt = self.tables.expr_ty(expr).ty_adt_def().unwrap();
let res = self.tables().qpath_res(qpath, expr.hir_id);
let adt = self.tables().expr_ty(expr).ty_adt_def().unwrap();
let variant = adt.variant_of_res(res);
if let Some(ref base) = *base {
// If the expression uses FRU we need to make sure all the unmentioned fields
Expand All @@ -1128,7 +1108,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
for (vf_index, variant_field) in variant.fields.iter().enumerate() {
let field = fields
.iter()
.find(|f| self.tcx.field_index(f.hir_id, self.tables) == vf_index);
.find(|f| self.tcx.field_index(f.hir_id, self.tables()) == vf_index);
let (use_ctxt, span) = match field {
Some(field) => (field.ident.span, field.span),
None => (base.span, base.span),
Expand All @@ -1138,7 +1118,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
} else {
for field in fields {
let use_ctxt = field.ident.span;
let index = self.tcx.field_index(field.hir_id, self.tables);
let index = self.tcx.field_index(field.hir_id, self.tables());
self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false);
}
}
Expand All @@ -1149,12 +1129,12 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {

fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
if let PatKind::Struct(ref qpath, fields, _) = pat.kind {
let res = self.tables.qpath_res(qpath, pat.hir_id);
let adt = self.tables.pat_ty(pat).ty_adt_def().unwrap();
let res = self.tables().qpath_res(qpath, pat.hir_id);
let adt = self.tables().pat_ty(pat).ty_adt_def().unwrap();
let variant = adt.variant_of_res(res);
for field in fields {
let use_ctxt = field.ident.span;
let index = self.tcx.field_index(field.hir_id, self.tables);
let index = self.tcx.field_index(field.hir_id, self.tables());
self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false);
}
}
Expand All @@ -1169,16 +1149,22 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
/// Checks are performed on "semantic" types regardless of names and their hygiene.
////////////////////////////////////////////////////////////////////////////////////////////

struct TypePrivacyVisitor<'a, 'tcx> {
struct TypePrivacyVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
maybe_typeck_tables: Option<&'tcx ty::TypeckTables<'tcx>>,
current_item: LocalDefId,
in_body: bool,
span: Span,
empty_tables: &'a ty::TypeckTables<'tcx>,
}

impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
impl<'tcx> TypePrivacyVisitor<'tcx> {
/// Gets the type-checking side-tables for the current body.
/// As this will ICE if called outside bodies, only call when working with
/// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
#[track_caller]
fn tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
self.maybe_typeck_tables.expect("`TypePrivacyVisitor::tables` called outside of body")
}

fn item_is_accessible(&self, did: DefId) -> bool {
def_id_visibility(self.tcx, did)
.0
Expand All @@ -1188,10 +1174,11 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
// Take node-id of an expression or pattern and check its type for privacy.
fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool {
self.span = span;
if self.visit(self.tables.node_type(id)) || self.visit(self.tables.node_substs(id)) {
let tables = self.tables();
if self.visit(tables.node_type(id)) || self.visit(tables.node_substs(id)) {
return true;
}
if let Some(adjustments) = self.tables.adjustments().get(id) {
if let Some(adjustments) = tables.adjustments().get(id) {
for adjustment in adjustments {
if self.visit(adjustment.target) {
return true;
Expand All @@ -1214,7 +1201,7 @@ impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
}
}

impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
type Map = Map<'tcx>;

/// We want to visit items in the context of their containing
Expand All @@ -1229,19 +1216,17 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
}

fn visit_nested_body(&mut self, body: hir::BodyId) {
let orig_tables = mem::replace(&mut self.tables, self.tcx.body_tables(body));
let orig_in_body = mem::replace(&mut self.in_body, true);
let old_maybe_typeck_tables = self.maybe_typeck_tables.replace(self.tcx.body_tables(body));
let body = self.tcx.hir().body(body);
self.visit_body(body);
self.tables = orig_tables;
self.in_body = orig_in_body;
self.maybe_typeck_tables = old_maybe_typeck_tables;
}

fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
self.span = hir_ty.span;
if self.in_body {
if let Some(tables) = self.maybe_typeck_tables {
// Types in bodies.
if self.visit(self.tables.node_type(hir_ty.hir_id)) {
if self.visit(tables.node_type(hir_ty.hir_id)) {
return;
}
} else {
Expand All @@ -1258,7 +1243,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {

fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef<'tcx>) {
self.span = trait_ref.path.span;
if !self.in_body {
if self.maybe_typeck_tables.is_none() {
// Avoid calling `hir_trait_to_predicates` in bodies, it will ICE.
// The traits' privacy in bodies is already checked as a part of trait object types.
let bounds = rustc_typeck::hir_trait_to_predicates(
Expand Down Expand Up @@ -1304,7 +1289,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
hir::ExprKind::MethodCall(_, span, _, _) => {
// Method calls have to be checked specially.
self.span = span;
if let Some(def_id) = self.tables.type_dependent_def_id(expr.hir_id) {
if let Some(def_id) = self.tables().type_dependent_def_id(expr.hir_id) {
if self.visit(self.tcx.type_of(def_id)) {
return;
}
Expand All @@ -1327,9 +1312,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
// more code internal visibility at link time. (Access to private functions
// is already prohibited by type privacy for function types.)
fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: hir::HirId, span: Span) {
let def = match self.tables.qpath_res(qpath, id) {
Res::Def(kind, def_id) => Some((kind, def_id)),
_ => None,
let def = match qpath {
hir::QPath::Resolved(_, path) => match path.res {
Res::Def(kind, def_id) => Some((kind, def_id)),
_ => None,
},
hir::QPath::TypeRelative(..) => {
self.maybe_typeck_tables.and_then(|tables| tables.type_dependent_def(id))
}
};
let def = def.filter(|(kind, _)| match kind {
DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static => true,
Expand Down Expand Up @@ -1385,31 +1375,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
let orig_current_item =
mem::replace(&mut self.current_item, self.tcx.hir().local_def_id(item.hir_id));
let orig_in_body = mem::replace(&mut self.in_body, false);
let orig_tables =
mem::replace(&mut self.tables, item_tables(self.tcx, item.hir_id, self.empty_tables));
let old_maybe_typeck_tables = self.maybe_typeck_tables.take();
intravisit::walk_item(self, item);
self.tables = orig_tables;
self.in_body = orig_in_body;
self.maybe_typeck_tables = old_maybe_typeck_tables;
self.current_item = orig_current_item;
}

fn visit_trait_item(&mut self, ti: &'tcx hir::TraitItem<'tcx>) {
let orig_tables =
mem::replace(&mut self.tables, item_tables(self.tcx, ti.hir_id, self.empty_tables));
intravisit::walk_trait_item(self, ti);
self.tables = orig_tables;
}

fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) {
let orig_tables =
mem::replace(&mut self.tables, item_tables(self.tcx, ii.hir_id, self.empty_tables));
intravisit::walk_impl_item(self, ii);
self.tables = orig_tables;
}
}

impl DefIdVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
impl DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
Expand Down Expand Up @@ -2066,29 +2039,16 @@ pub fn provide(providers: &mut Providers<'_>) {
}

fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
let empty_tables = ty::TypeckTables::empty(None);

// Check privacy of names not checked in previous compilation stages.
let mut visitor = NamePrivacyVisitor {
tcx,
tables: &empty_tables,
current_item: None,
empty_tables: &empty_tables,
};
let mut visitor = NamePrivacyVisitor { tcx, maybe_typeck_tables: None, current_item: None };
let (module, span, hir_id) = tcx.hir().get_module(module_def_id);

intravisit::walk_mod(&mut visitor, module, hir_id);

// Check privacy of explicitly written types and traits as well as
// inferred types of expressions and patterns.
let mut visitor = TypePrivacyVisitor {
tcx,
tables: &empty_tables,
current_item: module_def_id,
in_body: false,
span,
empty_tables: &empty_tables,
};
let mut visitor =
TypePrivacyVisitor { tcx, maybe_typeck_tables: None, current_item: module_def_id, span };
intravisit::walk_mod(&mut visitor, module, hir_id);
}

Expand Down

0 comments on commit 780d6cb

Please sign in to comment.