rustc_privacy: visit Ty instead of HIR types in EmbargoVisitor.

This commit is contained in:
Eduard-Mihai Burtescu 2016-11-28 05:09:28 +02:00
parent 9aaf26e7aa
commit 36d33d6b71
8 changed files with 227 additions and 167 deletions

View file

@ -298,7 +298,8 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
// Nothing to recurse on for these
ast_map::NodeForeignItem(_) |
ast_map::NodeVariant(_) |
ast_map::NodeStructCtor(_) => {}
ast_map::NodeStructCtor(_) |
ast_map::NodeTy(_) => {}
_ => {
bug!("found unexpected thingy in worklist: {}",
self.tcx.map.node_to_string(search_item))

View file

@ -191,6 +191,10 @@ pub trait TypeVisitor<'tcx> : Sized {
t.super_visit_with(self)
}
fn visit_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool {
trait_ref.super_visit_with(self)
}
fn visit_region(&mut self, r: &'tcx ty::Region) -> bool {
r.super_visit_with(self)
}

View file

@ -1280,8 +1280,13 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> {
}
Some(ast_map::NodeExpr(expr)) => {
// This is a convenience to allow closures to work.
if let hir::ExprClosure(..) = expr.node {
ParameterEnvironment::for_item(tcx, tcx.map.get_parent(id))
if let hir::ExprClosure(.., ref body, _) = expr.node {
let def_id = tcx.map.local_def_id(id);
let base_def_id = tcx.closure_base_def_id(def_id);
tcx.construct_parameter_environment(
expr.span,
base_def_id,
tcx.region_maps.call_site_extent(id, body.id))
} else {
tcx.empty_parameter_environment()
}

View file

@ -599,6 +599,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> {
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.substs.visit_with(visitor)
}
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
visitor.visit_trait_ref(*self)
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::ExistentialTraitRef<'tcx> {
@ -766,6 +770,36 @@ impl<'tcx> TypeFoldable<'tcx> for ty::RegionParameterDef<'tcx> {
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::Generics<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
ty::Generics {
parent: self.parent,
parent_regions: self.parent_regions,
parent_types: self.parent_types,
regions: self.regions.fold_with(folder),
types: self.types.fold_with(folder),
has_self: self.has_self,
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.regions.visit_with(visitor) || self.types.visit_with(visitor)
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::GenericPredicates<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
ty::GenericPredicates {
parent: self.parent,
predicates: self.predicates.fold_with(folder),
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
self.predicates.visit_with(visitor)
}
}
impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
match *self {

View file

@ -35,7 +35,8 @@ use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
use rustc::lint;
use rustc::middle::privacy::{AccessLevel, AccessLevels};
use rustc::ty::{self, TyCtxt};
use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
use rustc::ty::fold::TypeVisitor;
use rustc::util::nodemap::NodeSet;
use syntax::ast;
use syntax_pos::Span;
@ -62,40 +63,32 @@ struct EmbargoVisitor<'a, 'tcx: 'a> {
}
struct ReachEverythingInTheInterfaceVisitor<'b, 'a: 'b, 'tcx: 'a> {
item_def_id: DefId,
ev: &'b mut EmbargoVisitor<'a, 'tcx>,
}
impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
fn ty_level(&self, ty: &hir::Ty) -> Option<AccessLevel> {
if let hir::TyPath(ref qpath) = ty.node {
let def = match *qpath {
hir::QPath::Resolved(_, ref path) => path.def,
hir::QPath::TypeRelative(..) => self.tcx.tables().type_relative_path_defs[&ty.id]
};
match def {
Def::PrimTy(..) | Def::SelfTy(..) | Def::TyParam(..) => {
Some(AccessLevel::Public)
}
def => {
if let Some(node_id) = self.tcx.map.as_local_node_id(def.def_id()) {
self.get(node_id)
} else {
Some(AccessLevel::Public)
}
}
}
fn item_ty_level(&self, item_def_id: DefId) -> Option<AccessLevel> {
let ty_def_id = match self.tcx.item_type(item_def_id).sty {
ty::TyAdt(adt, _) => adt.did,
ty::TyTrait(ref obj) => obj.principal.def_id(),
ty::TyProjection(ref proj) => proj.trait_ref.def_id,
_ => return Some(AccessLevel::Public)
};
if let Some(node_id) = self.tcx.map.as_local_node_id(ty_def_id) {
self.get(node_id)
} else {
Some(AccessLevel::Public)
}
}
fn trait_level(&self, trait_ref: &hir::TraitRef) -> Option<AccessLevel> {
let did = trait_ref.path.def.def_id();
if let Some(node_id) = self.tcx.map.as_local_node_id(did) {
self.get(node_id)
} else {
Some(AccessLevel::Public)
fn impl_trait_level(&self, impl_def_id: DefId) -> Option<AccessLevel> {
if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) {
if let Some(node_id) = self.tcx.map.as_local_node_id(trait_ref.def_id) {
return self.get(node_id);
}
}
Some(AccessLevel::Public)
}
fn get(&self, id: ast::NodeId) -> Option<AccessLevel> {
@ -115,8 +108,12 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
}
}
fn reach<'b>(&'b mut self) -> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
ReachEverythingInTheInterfaceVisitor { ev: self }
fn reach<'b>(&'b mut self, item_id: ast::NodeId)
-> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
ReachEverythingInTheInterfaceVisitor {
item_def_id: self.tcx.map.local_def_id(item_id),
ev: self,
}
}
}
@ -130,14 +127,13 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item) {
let inherited_item_level = match item.node {
// Impls inherit level from their types and traits
hir::ItemImpl(.., None, ref ty, _) => {
self.ty_level(&ty)
hir::ItemImpl(..) => {
let def_id = self.tcx.map.local_def_id(item.id);
cmp::min(self.item_ty_level(def_id), self.impl_trait_level(def_id))
}
hir::ItemImpl(.., Some(ref trait_ref), ref ty, _) => {
cmp::min(self.ty_level(&ty), self.trait_level(trait_ref))
}
hir::ItemDefaultImpl(_, ref trait_ref) => {
self.trait_level(trait_ref)
hir::ItemDefaultImpl(..) => {
let def_id = self.tcx.map.local_def_id(item.id);
self.impl_trait_level(def_id)
}
// Foreign mods inherit level from parents
hir::ItemForeignMod(..) => {
@ -209,22 +205,54 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
hir::ItemMod(..) => {}
// Reexports are handled in visit_mod
hir::ItemUse(..) => {}
// The interface is empty
hir::ItemDefaultImpl(..) => {}
// Visit everything
hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) |
hir::ItemTrait(..) | hir::ItemTy(..) | hir::ItemImpl(.., Some(..), _, _) => {
hir::ItemConst(..) | hir::ItemStatic(..) |
hir::ItemFn(..) | hir::ItemTy(..) => {
if item_level.is_some() {
self.reach().visit_item(item);
self.reach(item.id).generics().predicates().item_type();
}
}
// Visit everything, but enum variants have their own levels
hir::ItemEnum(ref def, ref generics) => {
hir::ItemTrait(.., ref trait_items) => {
if item_level.is_some() {
self.reach().visit_generics(generics);
self.reach(item.id).generics().predicates();
for trait_item in trait_items {
let mut reach = self.reach(trait_item.id);
reach.generics().predicates();
if let hir::TypeTraitItem(_, None) = trait_item.node {
// No type to visit.
} else {
reach.item_type();
}
}
}
}
// Visit everything except for private impl items
hir::ItemImpl(.., ref trait_ref, _, ref impl_items) => {
if item_level.is_some() {
self.reach(item.id).generics().predicates().impl_trait_ref();
for impl_item in impl_items {
let id = impl_item.id.node_id;
if trait_ref.is_some() || self.get(id).is_some() {
self.reach(id).generics().predicates().item_type();
}
}
}
}
// Visit everything, but enum variants have their own levels
hir::ItemEnum(ref def, _) => {
if item_level.is_some() {
self.reach(item.id).generics().predicates();
}
for variant in &def.variants {
if self.get(variant.node.data.id()).is_some() {
for field in variant.node.data.fields() {
self.reach().visit_struct_field(field);
self.reach(field.id).item_type();
}
// Corner case: if the variant is reachable, but its
// enum is not, make the enum reachable as well.
@ -236,32 +264,18 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
hir::ItemForeignMod(ref foreign_mod) => {
for foreign_item in &foreign_mod.items {
if self.get(foreign_item.id).is_some() {
self.reach().visit_foreign_item(foreign_item);
self.reach(foreign_item.id).generics().predicates().item_type();
}
}
}
// Visit everything except for private fields
hir::ItemStruct(ref struct_def, ref generics) |
hir::ItemUnion(ref struct_def, ref generics) => {
hir::ItemStruct(ref struct_def, _) |
hir::ItemUnion(ref struct_def, _) => {
if item_level.is_some() {
self.reach().visit_generics(generics);
self.reach(item.id).generics().predicates();
for field in struct_def.fields() {
if self.get(field.id).is_some() {
self.reach().visit_struct_field(field);
}
}
}
}
// The interface is empty
hir::ItemDefaultImpl(..) => {}
// Visit everything except for private impl items
hir::ItemImpl(.., ref generics, None, _, ref impl_item_refs) => {
if item_level.is_some() {
self.reach().visit_generics(generics);
for impl_item_ref in impl_item_refs {
if self.get(impl_item_ref.id.node_id).is_some() {
let impl_item = self.tcx.map.impl_item(impl_item_ref.id);
self.reach().visit_impl_item(impl_item);
self.reach(field.id).item_type();
}
}
}
@ -306,87 +320,69 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef) {
self.update(md.id, Some(AccessLevel::Public));
}
}
impl<'b, 'a, 'tcx: 'a> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
// Make the type hidden under a type alias reachable
fn reach_aliased_type(&mut self, item: &'tcx hir::Item, segment: &'tcx hir::PathSegment) {
if let hir::ItemTy(ref ty, ref generics) = item.node {
// See `fn is_public_type_alias` for details
self.visit_ty(ty);
let provided_params = segment.parameters.types().len();
for ty_param in &generics.ty_params[provided_params..] {
if let Some(ref default_ty) = ty_param.default {
self.visit_ty(default_ty);
}
}
}
}
}
impl<'b, 'a, 'tcx: 'a> Visitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
fn visit_nested_impl_item(&mut self, item_id: hir::ImplItemId) {
// when we visit an impl, its methods and items are part of its "interface"
let impl_item = self.ev.tcx.map.impl_item(item_id);
self.visit_impl_item(impl_item)
}
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
let def_and_segment = match ty.node {
hir::TyPath(hir::QPath::Resolved(_, ref path)) => {
Some((path.def, path.segments.last().unwrap()))
}
hir::TyPath(hir::QPath::TypeRelative(_, ref segment)) => {
Some((self.ev.tcx.tables().type_relative_path_defs[&ty.id], &**segment))
}
_ => None
};
if let Some((def, segment)) = def_and_segment {
match def {
Def::Struct(def_id) | Def::Union(def_id) | Def::Enum(def_id) |
Def::TyAlias(def_id) | Def::Trait(def_id) | Def::AssociatedTy(def_id) => {
if let Some(mut node_id) = self.ev.tcx.map.as_local_node_id(def_id) {
// Check the trait for associated types.
if let hir::map::NodeTraitItem(_) = self.ev.tcx.map.get(node_id) {
node_id = self.ev.tcx.map.get_parent(node_id);
}
let item = self.ev.tcx.map.expect_item(node_id);
if let Def::TyAlias(..) = def {
// Type aliases are substituted. Associated type aliases are not
// substituted yet, but ideally they should be.
if self.ev.get(item.id).is_none() {
self.reach_aliased_type(item, segment);
}
} else {
self.ev.update(item.id, Some(AccessLevel::Reachable));
}
}
}
_ => {}
if let hir::TyImplTrait(..) = ty.node {
if self.get(ty.id).is_some() {
// Reach the (potentially private) type and the API being exposed.
self.reach(ty.id).item_type().predicates();
}
}
intravisit::walk_ty(self, ty);
}
}
fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef) {
let def_id = trait_ref.path.def.def_id();
if let Some(node_id) = self.ev.tcx.map.as_local_node_id(def_id) {
impl<'b, 'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
fn generics(&mut self) -> &mut Self {
self.ev.tcx.item_generics(self.item_def_id).visit_with(self);
self
}
fn predicates(&mut self) -> &mut Self {
self.ev.tcx.item_predicates(self.item_def_id).visit_with(self);
self
}
fn item_type(&mut self) -> &mut Self {
self.ev.tcx.item_type(self.item_def_id).visit_with(self);
self
}
fn impl_trait_ref(&mut self) -> &mut Self {
self.ev.tcx.impl_trait_ref(self.item_def_id).visit_with(self);
self
}
}
impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
let ty_def_id = match ty.sty {
ty::TyAdt(adt, _) => Some(adt.did),
ty::TyTrait(ref obj) => Some(obj.principal.def_id()),
ty::TyProjection(ref proj) => Some(proj.trait_ref.def_id),
ty::TyFnDef(def_id, ..) |
ty::TyAnon(def_id, _) => Some(def_id),
_ => None
};
if let Some(def_id) = ty_def_id {
if let Some(node_id) = self.ev.tcx.map.as_local_node_id(def_id) {
self.ev.update(node_id, Some(AccessLevel::Reachable));
}
}
ty.super_visit_with(self)
}
fn visit_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool {
if let Some(node_id) = self.ev.tcx.map.as_local_node_id(trait_ref.def_id) {
let item = self.ev.tcx.map.expect_item(node_id);
self.ev.update(item.id, Some(AccessLevel::Reachable));
}
intravisit::walk_trait_ref(self, trait_ref);
trait_ref.super_visit_with(self)
}
// Don't recurse into function bodies
fn visit_block(&mut self, _: &hir::Block) {}
// Don't recurse into expressions in array sizes or const initializers
fn visit_expr(&mut self, _: &hir::Expr) {}
// Don't recurse into patterns in function arguments
fn visit_pat(&mut self, _: &hir::Pat) {}
}
////////////////////////////////////////////////////////////////////////////////

View file

@ -136,7 +136,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CollectItemTypesVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &hir::Expr) {
if let hir::ExprClosure(..) = expr.node {
convert_closure(self.ccx, expr.id);
let def_id = self.ccx.tcx.map.local_def_id(expr.id);
generics_of_def_id(self.ccx, def_id);
type_of_def_id(self.ccx, def_id);
}
intravisit::walk_expr(self, expr);
}
@ -570,40 +572,6 @@ fn convert_field<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
ccx.tcx.predicates.borrow_mut().insert(def_id, struct_predicates.clone());
}
fn convert_closure<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
node_id: ast::NodeId)
{
let tcx = ccx.tcx;
let def_id = tcx.map.local_def_id(node_id);
let base_def_id = tcx.closure_base_def_id(def_id);
let base_generics = generics_of_def_id(ccx, base_def_id);
// provide junk type parameter defs - the only place that
// cares about anything but the length is instantiation,
// and we don't do that for closures.
let upvar_decls : Vec<_> = tcx.with_freevars(node_id, |fv| {
fv.iter().enumerate().map(|(i, _)| ty::TypeParameterDef {
index: (base_generics.count() as u32) + (i as u32),
name: Symbol::intern("<upvar>"),
def_id: def_id,
default_def_id: base_def_id,
default: None,
object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault,
pure_wrt_drop: false,
}).collect()
});
tcx.generics.borrow_mut().insert(def_id, tcx.alloc_generics(ty::Generics {
parent: Some(base_def_id),
parent_regions: base_generics.parent_regions + (base_generics.regions.len() as u32),
parent_types: base_generics.parent_types + (base_generics.types.len() as u32),
regions: vec![],
types: upvar_decls,
has_self: base_generics.has_self,
}));
type_of_def_id(ccx, def_id);
}
fn convert_method<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
container: AssociatedItemContainer,
id: ast::NodeId,
@ -1320,6 +1288,9 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let parent_id = tcx.map.get_parent(node_id);
Some(tcx.map.local_def_id(parent_id))
}
NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) => {
Some(tcx.closure_base_def_id(def_id))
}
NodeTy(&hir::Ty { node: hir::TyImplTrait(..), .. }) => {
let mut parent_id = node_id;
loop {
@ -1437,7 +1408,24 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let i = type_start + i as u32;
get_or_create_type_parameter_def(ccx, ast_generics, i, p, allow_defaults)
});
let types: Vec<_> = opt_self.into_iter().chain(types).collect();
let mut types: Vec<_> = opt_self.into_iter().chain(types).collect();
// provide junk type parameter defs - the only place that
// cares about anything but the length is instantiation,
// and we don't do that for closures.
if let NodeExpr(&hir::Expr { node: hir::ExprClosure(..), .. }) = node {
tcx.with_freevars(node_id, |fv| {
types.extend(fv.iter().enumerate().map(|(i, _)| ty::TypeParameterDef {
index: type_start + i as u32,
name: Symbol::intern("<upvar>"),
def_id: def_id,
default_def_id: parent_def_id.unwrap(),
default: None,
object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault,
pure_wrt_drop: false,
}));
});
}
// Debugging aid.
if tcx.has_attr(def_id, "rustc_object_lifetime_default") {

View file

@ -0,0 +1,15 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(conservative_impl_trait)]
pub fn fourway_add(a: i32) -> impl Fn(i32) -> impl Fn(i32) -> impl Fn(i32) -> i32 {
move |b| move |c| move |d| a + b + c + d
}

View file

@ -0,0 +1,17 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:xcrate.rs
extern crate xcrate;
fn main() {
assert_eq!(xcrate::fourway_add(1)(2)(3)(4), 10);
}