17
17
18
18
use middle:: ty;
19
19
use middle:: typeck;
20
- use middle:: privacy;
21
- use middle:: resolve;
22
20
23
21
use std:: hashmap:: HashSet ;
24
22
use syntax:: ast:: * ;
25
23
use syntax:: ast_map;
26
- use syntax:: ast_util:: { def_id_of_def, is_local } ;
24
+ use syntax:: ast_util:: def_id_of_def;
27
25
use syntax:: attr;
28
26
use syntax:: parse:: token;
29
27
use syntax:: visit:: Visitor ;
@@ -73,6 +71,15 @@ fn trait_method_might_be_inlined(trait_method: &trait_method) -> bool {
73
71
}
74
72
}
75
73
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
+
76
83
// Information needed while computing reachability.
77
84
struct ReachableContext {
78
85
// The type context.
@@ -85,8 +92,108 @@ struct ReachableContext {
85
92
// A worklist of item IDs. Each item ID in this worklist will be inlined
86
93
// and will be scanned for further references.
87
94
worklist : @mut ~[ NodeId ] ,
88
- // Known reexports of modules
89
- exp_map2 : resolve:: ExportMap2 ,
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
+
90
197
}
91
198
92
199
struct MarkSymbolVisitor {
@@ -149,17 +256,31 @@ impl Visitor<()> for MarkSymbolVisitor {
149
256
150
257
impl ReachableContext {
151
258
// Creates a new reachability computation context.
152
- fn new ( tcx : ty:: ctxt , method_map : typeck:: method_map ,
153
- exp_map2 : resolve :: ExportMap2 ) -> ReachableContext {
259
+ fn new ( tcx : ty:: ctxt , method_map : typeck:: method_map )
260
+ -> ReachableContext {
154
261
ReachableContext {
155
262
tcx : tcx,
156
263
method_map : method_map,
157
264
reachable_symbols : @mut HashSet :: new ( ) ,
158
265
worklist : @mut ~[ ] ,
159
- exp_map2 : exp_map2,
160
266
}
161
267
}
162
268
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
+
163
284
// Returns true if the given def ID represents a local item that is
164
285
// eligible for inlining and false otherwise.
165
286
fn def_id_represents_local_inlined_item ( tcx : ty:: ctxt , def_id : DefId )
@@ -231,19 +352,6 @@ impl ReachableContext {
231
352
}
232
353
}
233
354
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
-
247
355
// Step 2: Mark all symbols that the symbols on the worklist touch.
248
356
fn propagate ( & self ) {
249
357
let mut visitor = self . init_visitor ( ) ;
@@ -265,18 +373,6 @@ impl ReachableContext {
265
373
item_fn( _, _, _, _, ref search_block) => {
266
374
visit:: walk_block ( & mut visitor, search_block, ( ) )
267
375
}
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( * ) => { }
280
376
_ => {
281
377
self . tcx . sess . span_bug ( item. span ,
282
378
"found non-function item \
@@ -286,8 +382,10 @@ impl ReachableContext {
286
382
}
287
383
Some ( & ast_map:: node_trait_method( trait_method, _, _) ) => {
288
384
match * trait_method {
289
- required( * ) => {
290
- // Keep going, nothing to get exported
385
+ required( ref ty_method) => {
386
+ self . tcx . sess . span_bug ( ty_method. span ,
387
+ "found required method in \
388
+ worklist?!")
291
389
}
292
390
provided( ref method) => {
293
391
visit:: walk_block ( & mut visitor, & method. body , ( ) )
@@ -297,10 +395,6 @@ impl ReachableContext {
297
395
Some ( & ast_map:: node_method( ref method, _, _) ) => {
298
396
visit:: walk_block ( & mut visitor, & method. body , ( ) )
299
397
}
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( * ) ) => { }
304
398
Some ( _) => {
305
399
let ident_interner = token:: get_ident_interner ( ) ;
306
400
let desc = ast_map:: node_id_to_str ( self . tcx . items ,
@@ -310,9 +404,6 @@ impl ReachableContext {
310
404
worklist: {}",
311
405
desc) )
312
406
}
313
- None if search_item == CRATE_NODE_ID => {
314
- self . propagate_mod ( search_item) ;
315
- }
316
407
None => {
317
408
self . tcx . sess . bug ( format ! ( "found unmapped ID in worklist: \
318
409
{}",
@@ -338,8 +429,7 @@ impl ReachableContext {
338
429
339
430
pub fn find_reachable ( tcx : ty:: ctxt ,
340
431
method_map : typeck:: method_map ,
341
- exp_map2 : resolve:: ExportMap2 ,
342
- exported_items : & privacy:: ExportedItems )
432
+ crate : & Crate )
343
433
-> @mut HashSet < NodeId > {
344
434
// XXX(pcwalton): We only need to mark symbols that are exported. But this
345
435
// is more complicated than just looking at whether the symbol is `pub`,
@@ -352,13 +442,11 @@ pub fn find_reachable(tcx: ty::ctxt,
352
442
// is to have the name resolution pass mark all targets of a `pub use` as
353
443
// "must be reachable".
354
444
355
- let reachable_context = ReachableContext :: new ( tcx, method_map, exp_map2 ) ;
445
+ let reachable_context = ReachableContext :: new ( tcx, method_map) ;
356
446
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
- }
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 ) ;
362
450
363
451
// Step 2: Mark all symbols that the symbols on the worklist touch.
364
452
reachable_context. propagate ( ) ;
0 commit comments