Skip to content

Commit 8015f9c

Browse files
committed
auto merge of #9791 : alexcrichton/rust/reachable, r=catamorphism
This fixes a bug in which the visibility rules were approximated by reachability, but forgot to cover the case where a 'pub use' reexports a private item. This fixes the commit by instead using the results of the privacy pass of the compiler to create the initial working set of the reachability pass. This may have the side effect of increasing the size of metadata, but it's difficult to avoid for correctness purposes sadly. Closes #9790
2 parents 0ede2ea + caf7b67 commit 8015f9c

15 files changed

+111
-160
lines changed

src/librustc/driver/driver.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,10 @@ pub fn phase_3_run_analysis_passes(sess: Session,
263263
method_map, ty_cx));
264264

265265
let maps = (external_exports, last_private_map);
266-
time(time_passes, "privacy checking", maps, |(a, b)|
267-
middle::privacy::check_crate(ty_cx, &method_map, &exp_map2,
268-
a, b, crate));
266+
let exported_items =
267+
time(time_passes, "privacy checking", maps, |(a, b)|
268+
middle::privacy::check_crate(ty_cx, &method_map, &exp_map2,
269+
a, b, crate));
269270

270271
time(time_passes, "effect checking", (), |_|
271272
middle::effect::check_crate(ty_cx, method_map, crate));
@@ -300,7 +301,8 @@ pub fn phase_3_run_analysis_passes(sess: Session,
300301

301302
let reachable_map =
302303
time(time_passes, "reachability checking", (), |_|
303-
reachable::find_reachable(ty_cx, method_map, crate));
304+
reachable::find_reachable(ty_cx, method_map, exp_map2,
305+
&exported_items));
304306

305307
time(time_passes, "lint checking", (), |_|
306308
lint::check_crate(ty_cx, crate));

src/librustc/middle/privacy.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ use syntax::visit::Visitor;
3131

3232
type Context<'self> = (&'self method_map, &'self resolve::ExportMap2);
3333

34+
// A set of all nodes in the ast which can be considered "publicly exported" in
35+
// the sense that they are accessible from anywhere in any hierarchy.
36+
pub type ExportedItems = HashSet<ast::NodeId>;
37+
3438
// This visitor is used to determine the parent of all nodes in question when it
3539
// comes to privacy. This is used to determine later on if a usage is actually
3640
// valid or not.
@@ -137,7 +141,7 @@ impl<'self> Visitor<()> for ParentVisitor<'self> {
137141
// This visitor is used to determine which items of the ast are embargoed,
138142
// otherwise known as not exported.
139143
struct EmbargoVisitor<'self> {
140-
exported_items: &'self mut HashSet<ast::NodeId>,
144+
exported_items: &'self mut ExportedItems,
141145
exp_map2: &'self resolve::ExportMap2,
142146
path_all_public: bool,
143147
}
@@ -239,7 +243,7 @@ struct PrivacyVisitor<'self> {
239243
curitem: ast::NodeId,
240244

241245
// Results of previous analyses necessary for privacy checking.
242-
exported_items: &'self HashSet<ast::NodeId>,
246+
exported_items: &'self ExportedItems,
243247
method_map: &'self method_map,
244248
parents: &'self HashMap<ast::NodeId, ast::NodeId>,
245249
external_exports: resolve::ExternalExports,
@@ -785,7 +789,7 @@ pub fn check_crate(tcx: ty::ctxt,
785789
exp_map2: &resolve::ExportMap2,
786790
external_exports: resolve::ExternalExports,
787791
last_private_map: resolve::LastPrivateMap,
788-
crate: &ast::Crate) {
792+
crate: &ast::Crate) -> ExportedItems {
789793
let mut parents = HashMap::new();
790794
let mut exported_items = HashSet::new();
791795

@@ -802,7 +806,7 @@ pub fn check_crate(tcx: ty::ctxt,
802806
{
803807
// Initialize the exported items with resolve's id for the "root crate"
804808
// to resolve references to `super` leading to the root and such.
805-
exported_items.insert(0);
809+
exported_items.insert(ast::CRATE_NODE_ID);
806810
let mut visitor = EmbargoVisitor {
807811
exported_items: &mut exported_items,
808812
exp_map2: exp_map2,
@@ -824,4 +828,5 @@ pub fn check_crate(tcx: ty::ctxt,
824828
};
825829
visit::walk_crate(&mut visitor, crate, ());
826830
}
831+
return exported_items;
827832
}

src/librustc/middle/reachable.rs

+50-138
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
use middle::ty;
1919
use middle::typeck;
20+
use middle::privacy;
21+
use middle::resolve;
2022

2123
use std::hashmap::HashSet;
2224
use syntax::ast::*;
2325
use syntax::ast_map;
24-
use syntax::ast_util::def_id_of_def;
26+
use syntax::ast_util::{def_id_of_def, is_local};
2527
use syntax::attr;
2628
use syntax::parse::token;
2729
use syntax::visit::Visitor;
@@ -71,15 +73,6 @@ fn trait_method_might_be_inlined(trait_method: &trait_method) -> bool {
7173
}
7274
}
7375

74-
// The context we're in. If we're in a public context, then public symbols are
75-
// marked reachable. If we're in a private context, then only trait
76-
// implementations are marked reachable.
77-
#[deriving(Clone, Eq)]
78-
enum PrivacyContext {
79-
PublicContext,
80-
PrivateContext,
81-
}
82-
8376
// Information needed while computing reachability.
8477
struct ReachableContext {
8578
// The type context.
@@ -92,108 +85,8 @@ struct ReachableContext {
9285
// A worklist of item IDs. Each item ID in this worklist will be inlined
9386
// and will be scanned for further references.
9487
worklist: @mut ~[NodeId],
95-
}
96-
97-
struct ReachableVisitor {
98-
reachable_symbols: @mut HashSet<NodeId>,
99-
worklist: @mut ~[NodeId],
100-
}
101-
102-
impl Visitor<PrivacyContext> for ReachableVisitor {
103-
104-
fn visit_item(&mut self, item:@item, privacy_context:PrivacyContext) {
105-
106-
match item.node {
107-
item_fn(*) => {
108-
if privacy_context == PublicContext {
109-
self.reachable_symbols.insert(item.id);
110-
}
111-
if item_might_be_inlined(item) {
112-
self.worklist.push(item.id)
113-
}
114-
}
115-
item_struct(ref struct_def, _) => {
116-
match struct_def.ctor_id {
117-
Some(ctor_id) if
118-
privacy_context == PublicContext => {
119-
self.reachable_symbols.insert(ctor_id);
120-
}
121-
Some(_) | None => {}
122-
}
123-
}
124-
item_enum(ref enum_def, _) => {
125-
if privacy_context == PublicContext {
126-
for variant in enum_def.variants.iter() {
127-
self.reachable_symbols.insert(variant.node.id);
128-
}
129-
}
130-
}
131-
item_impl(ref generics, ref trait_ref, _, ref methods) => {
132-
// XXX(pcwalton): We conservatively assume any methods
133-
// on a trait implementation are reachable, when this
134-
// is not the case. We could be more precise by only
135-
// treating implementations of reachable or cross-
136-
// crate traits as reachable.
137-
138-
let should_be_considered_public = |method: @method| {
139-
(method.vis == public &&
140-
privacy_context == PublicContext) ||
141-
trait_ref.is_some()
142-
};
143-
144-
// Mark all public methods as reachable.
145-
for &method in methods.iter() {
146-
if should_be_considered_public(method) {
147-
self.reachable_symbols.insert(method.id);
148-
}
149-
}
150-
151-
if generics_require_inlining(generics) {
152-
// If the impl itself has generics, add all public
153-
// symbols to the worklist.
154-
for &method in methods.iter() {
155-
if should_be_considered_public(method) {
156-
self.worklist.push(method.id)
157-
}
158-
}
159-
} else {
160-
// Otherwise, add only public methods that have
161-
// generics to the worklist.
162-
for method in methods.iter() {
163-
let generics = &method.generics;
164-
let attrs = &method.attrs;
165-
if generics_require_inlining(generics) ||
166-
attributes_specify_inlining(*attrs) ||
167-
should_be_considered_public(*method) {
168-
self.worklist.push(method.id)
169-
}
170-
}
171-
}
172-
}
173-
item_trait(_, _, ref trait_methods) => {
174-
// Mark all provided methods as reachable.
175-
if privacy_context == PublicContext {
176-
for trait_method in trait_methods.iter() {
177-
match *trait_method {
178-
provided(method) => {
179-
self.reachable_symbols.insert(method.id);
180-
self.worklist.push(method.id)
181-
}
182-
required(_) => {}
183-
}
184-
}
185-
}
186-
}
187-
_ => {}
188-
}
189-
190-
if item.vis == public && privacy_context == PublicContext {
191-
visit::walk_item(self, item, PublicContext)
192-
} else {
193-
visit::walk_item(self, item, PrivateContext)
194-
}
195-
}
196-
88+
// Known reexports of modules
89+
exp_map2: resolve::ExportMap2,
19790
}
19891

19992
struct MarkSymbolVisitor {
@@ -256,31 +149,17 @@ impl Visitor<()> for MarkSymbolVisitor {
256149

257150
impl ReachableContext {
258151
// Creates a new reachability computation context.
259-
fn new(tcx: ty::ctxt, method_map: typeck::method_map)
260-
-> ReachableContext {
152+
fn new(tcx: ty::ctxt, method_map: typeck::method_map,
153+
exp_map2: resolve::ExportMap2) -> ReachableContext {
261154
ReachableContext {
262155
tcx: tcx,
263156
method_map: method_map,
264157
reachable_symbols: @mut HashSet::new(),
265158
worklist: @mut ~[],
159+
exp_map2: exp_map2,
266160
}
267161
}
268162

269-
// Step 1: Mark all public symbols, and add all public symbols that might
270-
// be inlined to a worklist.
271-
fn mark_public_symbols(&self, crate: &Crate) {
272-
let reachable_symbols = self.reachable_symbols;
273-
let worklist = self.worklist;
274-
275-
let mut visitor = ReachableVisitor {
276-
reachable_symbols: reachable_symbols,
277-
worklist: worklist,
278-
};
279-
280-
281-
visit::walk_crate(&mut visitor, crate, PublicContext);
282-
}
283-
284163
// Returns true if the given def ID represents a local item that is
285164
// eligible for inlining and false otherwise.
286165
fn def_id_represents_local_inlined_item(tcx: ty::ctxt, def_id: DefId)
@@ -352,6 +231,19 @@ impl ReachableContext {
352231
}
353232
}
354233

234+
fn propagate_mod(&self, id: NodeId) {
235+
match self.exp_map2.find(&id) {
236+
Some(l) => {
237+
for reexport in l.iter() {
238+
if reexport.reexport && is_local(reexport.def_id) {
239+
self.worklist.push(reexport.def_id.node);
240+
}
241+
}
242+
}
243+
None => {}
244+
}
245+
}
246+
355247
// Step 2: Mark all symbols that the symbols on the worklist touch.
356248
fn propagate(&self) {
357249
let mut visitor = self.init_visitor();
@@ -373,6 +265,18 @@ impl ReachableContext {
373265
item_fn(_, _, _, _, ref search_block) => {
374266
visit::walk_block(&mut visitor, search_block, ())
375267
}
268+
// Our recursion into modules involves looking up their
269+
// public reexports and the destinations of those
270+
// exports. Privacy will put them in the worklist, but
271+
// we won't find them in the ast_map, so this is where
272+
// we deal with publicly re-exported items instead.
273+
item_mod(*) => { self.propagate_mod(item.id); }
274+
// These are normal, nothing reachable about these
275+
// inherently and their children are already in the
276+
// worklist
277+
item_struct(*) | item_impl(*) | item_static(*) |
278+
item_enum(*) | item_ty(*) | item_trait(*) |
279+
item_foreign_mod(*) => {}
376280
_ => {
377281
self.tcx.sess.span_bug(item.span,
378282
"found non-function item \
@@ -382,10 +286,8 @@ impl ReachableContext {
382286
}
383287
Some(&ast_map::node_trait_method(trait_method, _, _)) => {
384288
match *trait_method {
385-
required(ref ty_method) => {
386-
self.tcx.sess.span_bug(ty_method.span,
387-
"found required method in \
388-
worklist?!")
289+
required(*) => {
290+
// Keep going, nothing to get exported
389291
}
390292
provided(ref method) => {
391293
visit::walk_block(&mut visitor, &method.body, ())
@@ -395,6 +297,10 @@ impl ReachableContext {
395297
Some(&ast_map::node_method(ref method, _, _)) => {
396298
visit::walk_block(&mut visitor, &method.body, ())
397299
}
300+
// Nothing to recurse on for these
301+
Some(&ast_map::node_foreign_item(*)) |
302+
Some(&ast_map::node_variant(*)) |
303+
Some(&ast_map::node_struct_ctor(*)) => {}
398304
Some(_) => {
399305
let ident_interner = token::get_ident_interner();
400306
let desc = ast_map::node_id_to_str(self.tcx.items,
@@ -404,6 +310,9 @@ impl ReachableContext {
404310
worklist: {}",
405311
desc))
406312
}
313+
None if search_item == CRATE_NODE_ID => {
314+
self.propagate_mod(search_item);
315+
}
407316
None => {
408317
self.tcx.sess.bug(format!("found unmapped ID in worklist: \
409318
{}",
@@ -429,7 +338,8 @@ impl ReachableContext {
429338

430339
pub fn find_reachable(tcx: ty::ctxt,
431340
method_map: typeck::method_map,
432-
crate: &Crate)
341+
exp_map2: resolve::ExportMap2,
342+
exported_items: &privacy::ExportedItems)
433343
-> @mut HashSet<NodeId> {
434344
// XXX(pcwalton): We only need to mark symbols that are exported. But this
435345
// is more complicated than just looking at whether the symbol is `pub`,
@@ -442,11 +352,13 @@ pub fn find_reachable(tcx: ty::ctxt,
442352
// is to have the name resolution pass mark all targets of a `pub use` as
443353
// "must be reachable".
444354

445-
let reachable_context = ReachableContext::new(tcx, method_map);
355+
let reachable_context = ReachableContext::new(tcx, method_map, exp_map2);
446356

447-
// Step 1: Mark all public symbols, and add all public symbols that might
448-
// be inlined to a worklist.
449-
reachable_context.mark_public_symbols(crate);
357+
// Step 1: Seed the worklist with all nodes which were found to be public as
358+
// a result of the privacy pass
359+
for &id in exported_items.iter() {
360+
reachable_context.worklist.push(id);
361+
}
450362

451363
// Step 2: Mark all symbols that the symbols on the worklist touch.
452364
reachable_context.propagate();

src/librustc/middle/trans/base.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2535,7 +2535,6 @@ pub fn get_item_val(ccx: @mut CrateContext, id: ast::NodeId) -> ValueRef {
25352535
let (v, inlineable) = consts::const_expr(ccx, expr);
25362536
ccx.const_values.insert(id, v);
25372537
let mut inlineable = inlineable;
2538-
exprt = true;
25392538

25402539
unsafe {
25412540
let llty = llvm::LLVMTypeOf(v);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub use foo::bar;
12+
13+
mod foo {
14+
pub fn bar() {}
15+
}

src/test/codegen/iterate-over-array.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
#[no_mangle]
12-
fn test(x: &[int]) -> int {
12+
pub fn test(x: &[int]) -> int {
1313
let mut y = 0;
1414
let mut i = 0;
1515
while (i < x.len()) {

src/test/codegen/scalar-function-call.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ fn foo(x: int) -> int {
1313
}
1414

1515
#[no_mangle]
16-
fn test() {
16+
pub fn test() {
1717
let _x = foo(10);
1818
}

0 commit comments

Comments
 (0)