Skip to content

Commit 7cd8f69

Browse files
committed
Auto merge of #29620 - petrochenkov:reachable2, r=alexcrichton
Handle them in `middle::reachable` instead (no optimizations so far, just drop all trait impl items into the reachable set, as before). Addresses the concerns from #29291 (comment) \+ In `middle::reachable` don't treat impls of `Drop` specially, they are subsumed by the general impl treatment. \+ Add some tests checking reachability of trait methods written in UFCS form \+ Minor refactoring in the second commit r? @alexcrichton
2 parents 2f59977 + 1d69397 commit 7cd8f69

File tree

8 files changed

+85
-55
lines changed

8 files changed

+85
-55
lines changed

src/librustc/middle/reachable.rs

+41-26
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ReachableContext<'a, 'tcx> {
125125
hir::ExprMethodCall(..) => {
126126
let method_call = ty::MethodCall::expr(expr.id);
127127
let def_id = self.tcx.tables.borrow().method_map[&method_call].def_id;
128+
129+
// Mark the trait item (and, possibly, its default impl) as reachable
130+
// Or mark inherent impl item as reachable
128131
if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) {
129132
if self.def_id_represents_local_inlined_item(def_id) {
130133
self.worklist.push(node_id)
@@ -322,57 +325,69 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
322325
}
323326
}
324327
}
328+
}
325329

326-
// Step 3: Mark all destructors as reachable.
327-
//
328-
// FIXME #10732: This is a conservative overapproximation, but fixing
329-
// this properly would result in the necessity of computing *type*
330-
// reachability, which might result in a compile time loss.
331-
fn mark_destructors_reachable(&mut self) {
332-
let drop_trait = match self.tcx.lang_items.drop_trait() {
333-
Some(id) => self.tcx.lookup_trait_def(id), None => { return }
334-
};
335-
drop_trait.for_each_impl(self.tcx, |drop_impl| {
336-
for destructor in &self.tcx.impl_items.borrow()[&drop_impl] {
337-
let destructor_did = destructor.def_id();
338-
if let Some(destructor_node_id) = self.tcx.map.as_local_node_id(destructor_did) {
339-
self.reachable_symbols.insert(destructor_node_id);
330+
// Some methods from non-exported (completely private) trait impls still have to be
331+
// reachable if they are called from inlinable code. Generally, it's not known until
332+
// monomorphization if a specific trait impl item can be reachable or not. So, we
333+
// conservatively mark all of them as reachable.
334+
// FIXME: One possible strategy for pruning the reachable set is to avoid marking impl
335+
// items of non-exported traits (or maybe all local traits?) unless their respective
336+
// trait items are used from inlinable code through method call syntax or UFCS, or their
337+
// trait is a lang item.
338+
struct CollectPrivateImplItemsVisitor<'a> {
339+
exported_items: &'a privacy::ExportedItems,
340+
worklist: &'a mut Vec<ast::NodeId>,
341+
}
342+
343+
impl<'a, 'v> Visitor<'v> for CollectPrivateImplItemsVisitor<'a> {
344+
fn visit_item(&mut self, item: &hir::Item) {
345+
// We need only trait impls here, not inherent impls, and only non-exported ones
346+
if let hir::ItemImpl(_, _, _, Some(_), _, ref impl_items) = item.node {
347+
if !self.exported_items.contains(&item.id) {
348+
for impl_item in impl_items {
349+
self.worklist.push(impl_item.id);
340350
}
341351
}
342-
})
352+
}
353+
354+
visit::walk_item(self, item);
343355
}
344356
}
345357

346358
pub fn find_reachable(tcx: &ty::ctxt,
347359
exported_items: &privacy::ExportedItems)
348360
-> NodeSet {
361+
349362
let mut reachable_context = ReachableContext::new(tcx);
350363

351364
// Step 1: Seed the worklist with all nodes which were found to be public as
352-
// a result of the privacy pass along with all local lang items. If
353-
// other crates link to us, they're going to expect to be able to
365+
// a result of the privacy pass along with all local lang items and impl items.
366+
// If other crates link to us, they're going to expect to be able to
354367
// use the lang items, so we need to be sure to mark them as
355368
// exported.
356369
for id in exported_items {
357370
reachable_context.worklist.push(*id);
358371
}
359372
for (_, item) in tcx.lang_items.items() {
360-
match *item {
361-
Some(did) => {
362-
if let Some(node_id) = tcx.map.as_local_node_id(did) {
363-
reachable_context.worklist.push(node_id);
364-
}
373+
if let Some(did) = *item {
374+
if let Some(node_id) = tcx.map.as_local_node_id(did) {
375+
reachable_context.worklist.push(node_id);
365376
}
366-
_ => {}
367377
}
368378
}
379+
{
380+
let mut collect_private_impl_items = CollectPrivateImplItemsVisitor {
381+
exported_items: exported_items,
382+
worklist: &mut reachable_context.worklist,
383+
};
384+
385+
visit::walk_crate(&mut collect_private_impl_items, tcx.map.krate());
386+
}
369387

370388
// Step 2: Mark all symbols that the symbols on the worklist touch.
371389
reachable_context.propagate();
372390

373-
// Step 3: Mark all destructors as reachable.
374-
reachable_context.mark_destructors_reachable();
375-
376391
// Return the set of reachable symbols.
377392
reachable_context.reachable_symbols
378393
}

src/librustc_privacy/lib.rs

+19-27
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,6 @@ struct EmbargoVisitor<'a, 'tcx: 'a> {
165165
// may jump across private boundaries through reexport statements or type aliases.
166166
exported_items: ExportedItems,
167167

168-
// This sets contains all the destination nodes which are publicly
169-
// re-exported. This is *not* a set of all reexported nodes, only a set of
170-
// all nodes which are reexported *and* reachable from external crates. This
171-
// means that the destination of the reexport is exported, and hence the
172-
// destination must also be exported.
173-
reexports: NodeSet,
174-
175168
// Items that are directly public without help of reexports or type aliases.
176169
// These two fields are closely related to one another in that they are only
177170
// used for generation of the `public_items` set, not for privacy checking at
@@ -185,7 +178,9 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
185178
fn is_public_exported_ty(&self, ty: &hir::Ty) -> (bool, bool) {
186179
if let hir::TyPath(..) = ty.node {
187180
match self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def() {
188-
def::DefPrimTy(..) | def::DefSelfTy(..) => (true, true),
181+
def::DefPrimTy(..) | def::DefSelfTy(..) | def::DefTyParam(..) => {
182+
(true, true)
183+
}
189184
def => {
190185
if let Some(node_id) = self.tcx.map.as_local_node_id(def.def_id()) {
191186
(self.public_items.contains(&node_id),
@@ -235,7 +230,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
235230
_ => {
236231
self.prev_public = self.prev_public && item.vis == hir::Public;
237232
self.prev_exported = (self.prev_exported && item.vis == hir::Public) ||
238-
self.reexports.contains(&item.id);
233+
self.exported_items.contains(&item.id);
239234

240235
self.maybe_insert_id(item.id);
241236
}
@@ -272,25 +267,26 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
272267
}
273268
}
274269

275-
// It's not known until monomorphization if a trait impl item should be reachable
276-
// from external crates or not. So, we conservatively mark all of them exported and
277-
// the reachability pass (middle::reachable) marks all exported items as reachable.
278-
// For example of private trait impl for private type that should be reachable see
279-
// src/test/auxiliary/issue-11225-3.rs
270+
// Trait impl and its items are public/exported if both the self type and the trait
271+
// of this impl are public/exported
280272
hir::ItemImpl(_, _, _, Some(ref trait_ref), ref ty, ref impl_items) => {
281-
let (public_ty, _exported_ty) = self.is_public_exported_ty(&ty);
282-
let (public_trait, _exported_trait) = self.is_public_exported_trait(trait_ref);
273+
let (public_ty, exported_ty) = self.is_public_exported_ty(&ty);
274+
let (public_trait, exported_trait) = self.is_public_exported_trait(trait_ref);
283275

284276
if public_ty && public_trait {
285277
self.public_items.insert(item.id);
286278
}
287-
self.exported_items.insert(item.id);
279+
if exported_ty && exported_trait {
280+
self.exported_items.insert(item.id);
281+
}
288282

289283
for impl_item in impl_items {
290284
if public_ty && public_trait {
291285
self.public_items.insert(impl_item.id);
292286
}
293-
self.exported_items.insert(impl_item.id);
287+
if exported_ty && exported_trait {
288+
self.exported_items.insert(impl_item.id);
289+
}
294290
}
295291
}
296292

@@ -332,8 +328,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
332328
match self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def() {
333329
def::DefPrimTy(..) | def::DefSelfTy(..) | def::DefTyParam(..) => {},
334330
def => {
335-
let did = def.def_id();
336-
if let Some(node_id) = self.tcx.map.as_local_node_id(did) {
331+
if let Some(node_id) = self.tcx.map.as_local_node_id(def.def_id()) {
337332
self.exported_items.insert(node_id);
338333
}
339334
}
@@ -345,7 +340,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
345340
for foreign_item in &foreign_mod.items {
346341
let public = self.prev_public && foreign_item.vis == hir::Public;
347342
let exported = (self.prev_exported && foreign_item.vis == hir::Public) ||
348-
self.reexports.contains(&foreign_item.id);
343+
self.exported_items.contains(&foreign_item.id);
349344

350345
if public {
351346
self.public_items.insert(foreign_item.id);
@@ -385,7 +380,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
385380
assert!(self.export_map.contains_key(&id), "wut {}", id);
386381
for export in self.export_map.get(&id).unwrap() {
387382
if let Some(node_id) = self.tcx.map.as_local_node_id(export.def_id) {
388-
self.reexports.insert(node_id);
383+
self.exported_items.insert(node_id);
389384
}
390385
}
391386
}
@@ -1530,17 +1525,14 @@ pub fn check_crate(tcx: &ty::ctxt,
15301525
tcx: tcx,
15311526
exported_items: NodeSet(),
15321527
public_items: NodeSet(),
1533-
reexports: NodeSet(),
15341528
export_map: export_map,
15351529
prev_exported: true,
15361530
prev_public: true,
15371531
};
15381532
loop {
1539-
let before = (visitor.exported_items.len(), visitor.public_items.len(),
1540-
visitor.reexports.len());
1533+
let before = (visitor.exported_items.len(), visitor.public_items.len());
15411534
visit::walk_crate(&mut visitor, krate);
1542-
let after = (visitor.exported_items.len(), visitor.public_items.len(),
1543-
visitor.reexports.len());
1535+
let after = (visitor.exported_items.len(), visitor.public_items.len());
15441536
if after == before {
15451537
break
15461538
}

src/test/auxiliary/issue-11225-1.rs

+5
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@
1111
mod inner {
1212
pub trait Trait {
1313
fn f(&self) { f(); }
14+
fn f_ufcs(&self) { f_ufcs(); }
1415
}
1516

1617
impl Trait for isize {}
1718

1819
fn f() {}
20+
fn f_ufcs() {}
1921
}
2022

2123
pub fn foo<T: inner::Trait>(t: T) {
2224
t.f();
2325
}
26+
pub fn foo_ufcs<T: inner::Trait>(t: T) {
27+
T::f_ufcs(&t);
28+
}

src/test/auxiliary/issue-11225-2.rs

+6
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,25 @@ mod inner {
1414
pub struct Foo;
1515
pub trait Trait {
1616
fn f(&self);
17+
fn f_ufcs(&self);
1718
}
1819

1920
impl Trait for Foo {
2021
fn f(&self) { }
22+
fn f_ufcs(&self) { }
2123
}
2224
}
2325

2426
pub trait Outer {
2527
fn foo<T: Trait>(&self, t: T) { t.f(); }
28+
fn foo_ufcs<T: Trait>(&self, t: T) { T::f(&t); }
2629
}
2730

2831
impl Outer for isize {}
2932

3033
pub fn foo<T: Outer>(t: T) {
3134
t.foo(inner::Foo);
3235
}
36+
pub fn foo_ufcs<T: Outer>(t: T) {
37+
T::foo_ufcs(&t, inner::Foo)
38+
}

src/test/auxiliary/issue-11225-3.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,29 @@
1010

1111
trait PrivateTrait {
1212
fn private_trait_method(&self);
13+
fn private_trait_method_ufcs(&self);
1314
}
1415

1516
struct PrivateStruct;
1617

1718
impl PrivateStruct {
1819
fn private_inherent_method(&self) { }
20+
fn private_inherent_method_ufcs(&self) { }
1921
}
2022

2123
impl PrivateTrait for PrivateStruct {
2224
fn private_trait_method(&self) { }
25+
fn private_trait_method_ufcs(&self) { }
2326
}
2427

2528
#[inline]
26-
pub fn public_generic_function() {
29+
pub fn public_inlinable_function() {
2730
PrivateStruct.private_trait_method();
2831
PrivateStruct.private_inherent_method();
2932
}
33+
34+
#[inline]
35+
pub fn public_inlinable_function_ufcs() {
36+
PrivateStruct::private_trait_method(&PrivateStruct);
37+
PrivateStruct::private_inherent_method(&PrivateStruct);
38+
}

src/test/run-pass/issue-11225-1.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ extern crate issue_11225_1 as foo;
1616

1717
pub fn main() {
1818
foo::foo(1);
19+
foo::foo_ufcs(1);
1920
}

src/test/run-pass/issue-11225-2.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ extern crate issue_11225_2 as foo;
1616

1717
pub fn main() {
1818
foo::foo(1);
19+
foo::foo_ufcs(1);
1920
}

src/test/run-pass/issue-11225-3.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@
1515
extern crate issue_11225_3;
1616

1717
pub fn main() {
18-
issue_11225_3::public_generic_function();
18+
issue_11225_3::public_inlinable_function();
19+
issue_11225_3::public_inlinable_function_ufcs();
1920
}

0 commit comments

Comments
 (0)