auto merge of #10217 : alexcrichton/rust/less-reachable, r=pcwalton
Previously, all functions called by a reachable function were considered reachable, but this is only the case if the original function was possibly inlineable (if it's type generic or #[inline]-flagged).
This commit is contained in:
commit
3899f8da6b
@ -50,6 +50,7 @@ fn item_might_be_inlined(item: @ast::item) -> bool {
|
||||
}
|
||||
|
||||
match item.node {
|
||||
ast::item_impl(ref generics, _, _, _) |
|
||||
ast::item_fn(_, _, _, ref generics, _) => {
|
||||
generics_require_inlining(generics)
|
||||
}
|
||||
@ -64,6 +65,25 @@ fn ty_method_might_be_inlined(ty_method: &ast::TypeMethod) -> bool {
|
||||
generics_require_inlining(&ty_method.generics)
|
||||
}
|
||||
|
||||
fn method_might_be_inlined(tcx: ty::ctxt, method: &ast::method,
|
||||
impl_src: ast::DefId) -> bool {
|
||||
if attributes_specify_inlining(method.attrs) ||
|
||||
generics_require_inlining(&method.generics) {
|
||||
return true
|
||||
}
|
||||
if is_local(impl_src) {
|
||||
match tcx.items.find(&impl_src.node) {
|
||||
Some(&ast_map::node_item(item, _)) => item_might_be_inlined(item),
|
||||
Some(*) | None => {
|
||||
tcx.sess.span_bug(method.span, "impl did is not an item")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tcx.sess.span_bug(method.span, "found a foreign impl as a parent of a \
|
||||
local method")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the given trait method must be inlined because it may be
|
||||
// monomorphized or it was marked with `#[inline]`.
|
||||
fn trait_method_might_be_inlined(trait_method: &ast::trait_method) -> bool {
|
||||
@ -100,50 +120,54 @@ impl Visitor<()> for MarkSymbolVisitor {
|
||||
|
||||
fn visit_expr(&mut self, expr:@ast::Expr, _:()) {
|
||||
|
||||
match expr.node {
|
||||
ast::ExprPath(_) => {
|
||||
let def = match self.tcx.def_map.find(&expr.id) {
|
||||
Some(&def) => def,
|
||||
None => {
|
||||
self.tcx.sess.span_bug(expr.span,
|
||||
"def ID not in def map?!")
|
||||
}
|
||||
};
|
||||
match expr.node {
|
||||
ast::ExprPath(_) => {
|
||||
let def = match self.tcx.def_map.find(&expr.id) {
|
||||
Some(&def) => def,
|
||||
None => {
|
||||
self.tcx.sess.span_bug(expr.span,
|
||||
"def ID not in def map?!")
|
||||
}
|
||||
};
|
||||
|
||||
let def_id = def_id_of_def(def);
|
||||
let def_id = def_id_of_def(def);
|
||||
if ReachableContext::
|
||||
def_id_represents_local_inlined_item(self.tcx, def_id) {
|
||||
self.worklist.push(def_id.node)
|
||||
}
|
||||
self.reachable_symbols.insert(def_id.node);
|
||||
}
|
||||
ast::ExprMethodCall(*) => {
|
||||
match self.method_map.find(&expr.id) {
|
||||
Some(&typeck::method_map_entry {
|
||||
origin: typeck::method_static(def_id),
|
||||
_
|
||||
}) => {
|
||||
if ReachableContext::
|
||||
def_id_represents_local_inlined_item(self.tcx,
|
||||
def_id) {
|
||||
self.worklist.push(def_id.node)
|
||||
}
|
||||
def_id_represents_local_inlined_item(
|
||||
self.tcx,
|
||||
def_id) {
|
||||
self.worklist.push(def_id.node)
|
||||
}
|
||||
self.reachable_symbols.insert(def_id.node);
|
||||
}
|
||||
ast::ExprMethodCall(*) => {
|
||||
match self.method_map.find(&expr.id) {
|
||||
Some(&typeck::method_map_entry {
|
||||
origin: typeck::method_static(def_id),
|
||||
_
|
||||
}) => {
|
||||
if ReachableContext::
|
||||
def_id_represents_local_inlined_item(
|
||||
self.tcx,
|
||||
def_id) {
|
||||
self.worklist.push(def_id.node)
|
||||
}
|
||||
self.reachable_symbols.insert(def_id.node);
|
||||
}
|
||||
Some(_) => {}
|
||||
None => {
|
||||
self.tcx.sess.span_bug(expr.span,
|
||||
"method call expression \
|
||||
Some(_) => {}
|
||||
None => {
|
||||
self.tcx.sess.span_bug(expr.span,
|
||||
"method call expression \
|
||||
not in method map?!")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
visit::walk_expr(self, expr, ())
|
||||
visit::walk_expr(self, expr, ())
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, _item: @ast::item, _: ()) {
|
||||
// Do not recurse into items. These items will be added to the worklist
|
||||
// and recursed into manually if necessary.
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,8 +287,11 @@ fn propagate(&self) {
|
||||
Some(&ast_map::node_item(item, _)) => {
|
||||
match item.node {
|
||||
ast::item_fn(_, _, _, _, ref search_block) => {
|
||||
visit::walk_block(&mut visitor, search_block, ())
|
||||
if item_might_be_inlined(item) {
|
||||
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
|
||||
@ -331,8 +358,10 @@ fn propagate(&self) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(&ast_map::node_method(ref method, _, _)) => {
|
||||
visit::walk_block(&mut visitor, &method.body, ())
|
||||
Some(&ast_map::node_method(method, did, _)) => {
|
||||
if method_might_be_inlined(self.tcx, method, did) {
|
||||
visit::walk_block(&mut visitor, &method.body, ())
|
||||
}
|
||||
}
|
||||
// Nothing to recurse on for these
|
||||
Some(&ast_map::node_foreign_item(*)) |
|
||||
@ -378,17 +407,6 @@ pub fn find_reachable(tcx: ty::ctxt,
|
||||
exp_map2: resolve::ExportMap2,
|
||||
exported_items: &privacy::ExportedItems)
|
||||
-> @mut HashSet<ast::NodeId> {
|
||||
// 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`,
|
||||
// because it might be the target of a `pub use` somewhere. For now, I
|
||||
// think we are fine, because you can't `pub use` something that wasn't
|
||||
// exported due to the bug whereby `use` only looks through public
|
||||
// modules even if you're inside the module the `use` appears in. When
|
||||
// this bug is fixed, however, this code will need to be updated. Probably
|
||||
// the easiest way to fix this (although a conservative overapproximation)
|
||||
// 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, exp_map2);
|
||||
|
||||
// Step 1: Seed the worklist with all nodes which were found to be public as
|
||||
|
@ -2471,7 +2471,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
|
||||
match val {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
let mut exprt = false;
|
||||
let mut foreign = false;
|
||||
let item = ccx.tcx.items.get_copy(&id);
|
||||
let val = match item {
|
||||
ast_map::node_item(i, pth) => {
|
||||
@ -2584,7 +2584,6 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
|
||||
get_item_val()");
|
||||
}
|
||||
ast::provided(m) => {
|
||||
exprt = true;
|
||||
register_method(ccx, id, pth, m)
|
||||
}
|
||||
}
|
||||
@ -2596,7 +2595,7 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
|
||||
|
||||
ast_map::node_foreign_item(ni, abis, _, pth) => {
|
||||
let ty = ty::node_id_to_type(ccx.tcx, ni.id);
|
||||
exprt = true;
|
||||
foreign = true;
|
||||
|
||||
match ni.node {
|
||||
ast::foreign_item_fn(*) => {
|
||||
@ -2690,7 +2689,10 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
|
||||
}
|
||||
};
|
||||
|
||||
if !exprt && !ccx.reachable.contains(&id) {
|
||||
// foreign items (extern fns and extern statics) don't have internal
|
||||
// linkage b/c that doesn't quite make sense. Otherwise items can
|
||||
// have internal linkage if they're not reachable.
|
||||
if !foreign && !ccx.reachable.contains(&id) {
|
||||
lib::llvm::SetLinkage(val, lib::llvm::InternalLinkage);
|
||||
}
|
||||
|
||||
|
36
src/test/auxiliary/linkage-visibility.rs
Normal file
36
src/test/auxiliary/linkage-visibility.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// 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 <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.
|
||||
|
||||
use std::unstable::dynamic_lib::DynamicLibrary;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn foo() { bar(); }
|
||||
|
||||
pub fn foo2<T>() {
|
||||
fn bar2() {
|
||||
bar();
|
||||
}
|
||||
bar2();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn bar() { }
|
||||
|
||||
#[no_mangle]
|
||||
fn baz() { }
|
||||
|
||||
pub fn test() {
|
||||
let lib = DynamicLibrary::open(None).unwrap();
|
||||
unsafe {
|
||||
assert!(lib.symbol::<int>("foo").is_ok());
|
||||
assert!(lib.symbol::<int>("baz").is_err());
|
||||
assert!(lib.symbol::<int>("bar").is_err());
|
||||
}
|
||||
}
|
20
src/test/run-pass/linkage-visibility.rs
Normal file
20
src/test/run-pass/linkage-visibility.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// 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 <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:linkage-visibility.rs
|
||||
// xfail-fast windows doesn't like aux-build
|
||||
|
||||
extern mod foo(name = "linkage-visibility");
|
||||
|
||||
fn main() {
|
||||
foo::test();
|
||||
foo::foo2::<int>();
|
||||
foo::foo();
|
||||
}
|
Loading…
Reference in New Issue
Block a user