From 36d33d6b71cb99ad9d9403295daec8d8a8f5c976 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 28 Nov 2016 05:09:28 +0200 Subject: [PATCH] rustc_privacy: visit Ty instead of HIR types in EmbargoVisitor. --- src/librustc/middle/reachable.rs | 3 +- src/librustc/ty/fold.rs | 4 + src/librustc/ty/mod.rs | 9 +- src/librustc/ty/structural_impls.rs | 34 +++ src/librustc_privacy/lib.rs | 252 +++++++++--------- src/librustc_typeck/collect.rs | 60 ++--- .../run-pass/impl-trait/auxiliary/xcrate.rs | 15 ++ src/test/run-pass/impl-trait/xcrate.rs | 17 ++ 8 files changed, 227 insertions(+), 167 deletions(-) create mode 100644 src/test/run-pass/impl-trait/auxiliary/xcrate.rs create mode 100644 src/test/run-pass/impl-trait/xcrate.rs diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index e9b731ebaf2b..767886a23157 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -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)) diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 354658ec4397..10754825a8c1 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -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) } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3719090e922d..844fc58cec37 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -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() } diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index 877da7ee3b51..c8618cd15478 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -599,6 +599,10 @@ impl<'tcx> TypeFoldable<'tcx> for ty::TraitRef<'tcx> { fn super_visit_with>(&self, visitor: &mut V) -> bool { self.substs.visit_with(visitor) } + + fn visit_with>(&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>(&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>(&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 { diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 29bcb73602ea..797371001d8d 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -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 { - 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 { + 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 { - 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 { + 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 { @@ -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) {} } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 493f9d96fe02..0dcc0bcc316c 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -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(""), - 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(""), + 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") { diff --git a/src/test/run-pass/impl-trait/auxiliary/xcrate.rs b/src/test/run-pass/impl-trait/auxiliary/xcrate.rs new file mode 100644 index 000000000000..be353f6d563a --- /dev/null +++ b/src/test/run-pass/impl-trait/auxiliary/xcrate.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/run-pass/impl-trait/xcrate.rs b/src/test/run-pass/impl-trait/xcrate.rs new file mode 100644 index 000000000000..fe3ed7b3465f --- /dev/null +++ b/src/test/run-pass/impl-trait/xcrate.rs @@ -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 or the MIT license +// , 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); +}