diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 4b6d2ea5080..0c896007fc3 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -263,9 +263,10 @@ pub fn phase_3_run_analysis_passes(sess: Session, method_map, ty_cx)); let maps = (external_exports, last_private_map); - time(time_passes, "privacy checking", maps, |(a, b)| - middle::privacy::check_crate(ty_cx, &method_map, &exp_map2, - a, b, crate)); + let exported_items = + time(time_passes, "privacy checking", maps, |(a, b)| + middle::privacy::check_crate(ty_cx, &method_map, &exp_map2, + a, b, crate)); time(time_passes, "effect checking", (), |_| middle::effect::check_crate(ty_cx, method_map, crate)); @@ -300,7 +301,8 @@ pub fn phase_3_run_analysis_passes(sess: Session, let reachable_map = time(time_passes, "reachability checking", (), |_| - reachable::find_reachable(ty_cx, method_map, crate)); + reachable::find_reachable(ty_cx, method_map, exp_map2, + &exported_items)); time(time_passes, "lint checking", (), |_| lint::check_crate(ty_cx, crate)); diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index fb4b76c7c91..6f52ba5db4e 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -31,6 +31,10 @@ use syntax::visit::Visitor; type Context<'self> = (&'self method_map, &'self resolve::ExportMap2); +// A set of all nodes in the ast which can be considered "publicly exported" in +// the sense that they are accessible from anywhere in any hierarchy. +pub type ExportedItems = HashSet; + // This visitor is used to determine the parent of all nodes in question when it // comes to privacy. This is used to determine later on if a usage is actually // valid or not. @@ -137,7 +141,7 @@ impl<'self> Visitor<()> for ParentVisitor<'self> { // This visitor is used to determine which items of the ast are embargoed, // otherwise known as not exported. struct EmbargoVisitor<'self> { - exported_items: &'self mut HashSet, + exported_items: &'self mut ExportedItems, exp_map2: &'self resolve::ExportMap2, path_all_public: bool, } @@ -239,7 +243,7 @@ struct PrivacyVisitor<'self> { curitem: ast::NodeId, // Results of previous analyses necessary for privacy checking. - exported_items: &'self HashSet, + exported_items: &'self ExportedItems, method_map: &'self method_map, parents: &'self HashMap, external_exports: resolve::ExternalExports, @@ -785,7 +789,7 @@ pub fn check_crate(tcx: ty::ctxt, exp_map2: &resolve::ExportMap2, external_exports: resolve::ExternalExports, last_private_map: resolve::LastPrivateMap, - crate: &ast::Crate) { + crate: &ast::Crate) -> ExportedItems { let mut parents = HashMap::new(); let mut exported_items = HashSet::new(); @@ -802,7 +806,7 @@ pub fn check_crate(tcx: ty::ctxt, { // Initialize the exported items with resolve's id for the "root crate" // to resolve references to `super` leading to the root and such. - exported_items.insert(0); + exported_items.insert(ast::CRATE_NODE_ID); let mut visitor = EmbargoVisitor { exported_items: &mut exported_items, exp_map2: exp_map2, @@ -824,4 +828,5 @@ pub fn check_crate(tcx: ty::ctxt, }; visit::walk_crate(&mut visitor, crate, ()); } + return exported_items; } diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 79685543450..3c78c86d436 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -17,11 +17,13 @@ use middle::ty; use middle::typeck; +use middle::privacy; +use middle::resolve; use std::hashmap::HashSet; use syntax::ast::*; use syntax::ast_map; -use syntax::ast_util::def_id_of_def; +use syntax::ast_util::{def_id_of_def, is_local}; use syntax::attr; use syntax::parse::token; use syntax::visit::Visitor; @@ -71,15 +73,6 @@ fn trait_method_might_be_inlined(trait_method: &trait_method) -> bool { } } -// The context we're in. If we're in a public context, then public symbols are -// marked reachable. If we're in a private context, then only trait -// implementations are marked reachable. -#[deriving(Clone, Eq)] -enum PrivacyContext { - PublicContext, - PrivateContext, -} - // Information needed while computing reachability. struct ReachableContext { // The type context. @@ -92,108 +85,8 @@ struct ReachableContext { // A worklist of item IDs. Each item ID in this worklist will be inlined // and will be scanned for further references. worklist: @mut ~[NodeId], -} - -struct ReachableVisitor { - reachable_symbols: @mut HashSet, - worklist: @mut ~[NodeId], -} - -impl Visitor for ReachableVisitor { - - fn visit_item(&mut self, item:@item, privacy_context:PrivacyContext) { - - match item.node { - item_fn(*) => { - if privacy_context == PublicContext { - self.reachable_symbols.insert(item.id); - } - if item_might_be_inlined(item) { - self.worklist.push(item.id) - } - } - item_struct(ref struct_def, _) => { - match struct_def.ctor_id { - Some(ctor_id) if - privacy_context == PublicContext => { - self.reachable_symbols.insert(ctor_id); - } - Some(_) | None => {} - } - } - item_enum(ref enum_def, _) => { - if privacy_context == PublicContext { - for variant in enum_def.variants.iter() { - self.reachable_symbols.insert(variant.node.id); - } - } - } - item_impl(ref generics, ref trait_ref, _, ref methods) => { - // XXX(pcwalton): We conservatively assume any methods - // on a trait implementation are reachable, when this - // is not the case. We could be more precise by only - // treating implementations of reachable or cross- - // crate traits as reachable. - - let should_be_considered_public = |method: @method| { - (method.vis == public && - privacy_context == PublicContext) || - trait_ref.is_some() - }; - - // Mark all public methods as reachable. - for &method in methods.iter() { - if should_be_considered_public(method) { - self.reachable_symbols.insert(method.id); - } - } - - if generics_require_inlining(generics) { - // If the impl itself has generics, add all public - // symbols to the worklist. - for &method in methods.iter() { - if should_be_considered_public(method) { - self.worklist.push(method.id) - } - } - } else { - // Otherwise, add only public methods that have - // generics to the worklist. - for method in methods.iter() { - let generics = &method.generics; - let attrs = &method.attrs; - if generics_require_inlining(generics) || - attributes_specify_inlining(*attrs) || - should_be_considered_public(*method) { - self.worklist.push(method.id) - } - } - } - } - item_trait(_, _, ref trait_methods) => { - // Mark all provided methods as reachable. - if privacy_context == PublicContext { - for trait_method in trait_methods.iter() { - match *trait_method { - provided(method) => { - self.reachable_symbols.insert(method.id); - self.worklist.push(method.id) - } - required(_) => {} - } - } - } - } - _ => {} - } - - if item.vis == public && privacy_context == PublicContext { - visit::walk_item(self, item, PublicContext) - } else { - visit::walk_item(self, item, PrivateContext) - } - } - + // Known reexports of modules + exp_map2: resolve::ExportMap2, } struct MarkSymbolVisitor { @@ -256,31 +149,17 @@ impl Visitor<()> for MarkSymbolVisitor { impl ReachableContext { // Creates a new reachability computation context. - fn new(tcx: ty::ctxt, method_map: typeck::method_map) - -> ReachableContext { + fn new(tcx: ty::ctxt, method_map: typeck::method_map, + exp_map2: resolve::ExportMap2) -> ReachableContext { ReachableContext { tcx: tcx, method_map: method_map, reachable_symbols: @mut HashSet::new(), worklist: @mut ~[], + exp_map2: exp_map2, } } - // Step 1: Mark all public symbols, and add all public symbols that might - // be inlined to a worklist. - fn mark_public_symbols(&self, crate: &Crate) { - let reachable_symbols = self.reachable_symbols; - let worklist = self.worklist; - - let mut visitor = ReachableVisitor { - reachable_symbols: reachable_symbols, - worklist: worklist, - }; - - - visit::walk_crate(&mut visitor, crate, PublicContext); - } - // Returns true if the given def ID represents a local item that is // eligible for inlining and false otherwise. fn def_id_represents_local_inlined_item(tcx: ty::ctxt, def_id: DefId) @@ -352,6 +231,19 @@ impl ReachableContext { } } + fn propagate_mod(&self, id: NodeId) { + match self.exp_map2.find(&id) { + Some(l) => { + for reexport in l.iter() { + if reexport.reexport && is_local(reexport.def_id) { + self.worklist.push(reexport.def_id.node); + } + } + } + None => {} + } + } + // Step 2: Mark all symbols that the symbols on the worklist touch. fn propagate(&self) { let mut visitor = self.init_visitor(); @@ -373,6 +265,18 @@ impl ReachableContext { item_fn(_, _, _, _, ref search_block) => { visit::walk_block(&mut visitor, search_block, ()) } + // Our recursion into modules involves looking up their + // public reexports and the destinations of those + // exports. Privacy will put them in the worklist, but + // we won't find them in the ast_map, so this is where + // we deal with publicly re-exported items instead. + item_mod(*) => { self.propagate_mod(item.id); } + // These are normal, nothing reachable about these + // inherently and their children are already in the + // worklist + item_struct(*) | item_impl(*) | item_static(*) | + item_enum(*) | item_ty(*) | item_trait(*) | + item_foreign_mod(*) => {} _ => { self.tcx.sess.span_bug(item.span, "found non-function item \ @@ -382,10 +286,8 @@ impl ReachableContext { } Some(&ast_map::node_trait_method(trait_method, _, _)) => { match *trait_method { - required(ref ty_method) => { - self.tcx.sess.span_bug(ty_method.span, - "found required method in \ - worklist?!") + required(*) => { + // Keep going, nothing to get exported } provided(ref method) => { visit::walk_block(&mut visitor, &method.body, ()) @@ -395,6 +297,10 @@ impl ReachableContext { Some(&ast_map::node_method(ref method, _, _)) => { visit::walk_block(&mut visitor, &method.body, ()) } + // Nothing to recurse on for these + Some(&ast_map::node_foreign_item(*)) | + Some(&ast_map::node_variant(*)) | + Some(&ast_map::node_struct_ctor(*)) => {} Some(_) => { let ident_interner = token::get_ident_interner(); let desc = ast_map::node_id_to_str(self.tcx.items, @@ -404,6 +310,9 @@ impl ReachableContext { worklist: {}", desc)) } + None if search_item == CRATE_NODE_ID => { + self.propagate_mod(search_item); + } None => { self.tcx.sess.bug(format!("found unmapped ID in worklist: \ {}", @@ -429,7 +338,8 @@ impl ReachableContext { pub fn find_reachable(tcx: ty::ctxt, method_map: typeck::method_map, - crate: &Crate) + exp_map2: resolve::ExportMap2, + exported_items: &privacy::ExportedItems) -> @mut HashSet { // XXX(pcwalton): We only need to mark symbols that are exported. But this // is more complicated than just looking at whether the symbol is `pub`, @@ -442,11 +352,13 @@ pub fn find_reachable(tcx: ty::ctxt, // is to have the name resolution pass mark all targets of a `pub use` as // "must be reachable". - let reachable_context = ReachableContext::new(tcx, method_map); + let reachable_context = ReachableContext::new(tcx, method_map, exp_map2); - // Step 1: Mark all public symbols, and add all public symbols that might - // be inlined to a worklist. - reachable_context.mark_public_symbols(crate); + // Step 1: Seed the worklist with all nodes which were found to be public as + // a result of the privacy pass + for &id in exported_items.iter() { + reachable_context.worklist.push(id); + } // Step 2: Mark all symbols that the symbols on the worklist touch. reachable_context.propagate(); diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 025d1a18bd7..d0085afb3a7 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2535,7 +2535,6 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef { let (v, inlineable) = consts::const_expr(ccx, expr); ccx.const_values.insert(id, v); let mut inlineable = inlineable; - exprt = true; unsafe { let llty = llvm::LLVMTypeOf(v); diff --git a/src/test/auxiliary/reexport-should-still-link.rs b/src/test/auxiliary/reexport-should-still-link.rs new file mode 100644 index 00000000000..9d5464e6526 --- /dev/null +++ b/src/test/auxiliary/reexport-should-still-link.rs @@ -0,0 +1,15 @@ +// Copyright 2013 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. + +pub use foo::bar; + +mod foo { + pub fn bar() {} +} diff --git a/src/test/run-pass/reexport-should-still-link.rs b/src/test/run-pass/reexport-should-still-link.rs new file mode 100644 index 00000000000..ed5c3941c36 --- /dev/null +++ b/src/test/run-pass/reexport-should-still-link.rs @@ -0,0 +1,18 @@ +// Copyright 2013 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:reexport-should-still-link.rs +// xfail-fast windows doesn't like extern mod + +extern mod foo(name = "reexport-should-still-link"); + +fn main() { + foo::bar(); +}