Skip to content

Commit a1ec2f7

Browse files
committed
Rollup merge of rust-lang#53545 - FelixMcFelix:fix-50865-beta, r=petrochenkov
Fix rust-lang#50865: ICE on impl-trait returning functions reaching private items Adds a test case as suggested in rust-lang#50865, and implements @petrochenkov's suggestion. Fixes rust-lang#50865. Impl-trait-returning functions are marked under a new (low) access level, which they propagate rather than `AccessLevels::Reachable`. `AccessLevels::is_reachable` returns false for such items (leaving stability analysis unaffected), these items may still be visible to the lints phase however.
2 parents 62f29c4 + 85a05d1 commit a1ec2f7

File tree

6 files changed

+72
-7
lines changed

6 files changed

+72
-7
lines changed

src/librustc/ich/impls_ty.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,7 @@ impl_stable_hash_for!(enum traits::Reveal {
10911091
});
10921092

10931093
impl_stable_hash_for!(enum ::middle::privacy::AccessLevel {
1094+
ReachableFromImplTrait,
10941095
Reachable,
10951096
Exported,
10961097
Public

src/librustc/middle/privacy.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use syntax::ast::NodeId;
2121
// Accessibility levels, sorted in ascending order
2222
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
2323
pub enum AccessLevel {
24+
// Superset of Reachable used to mark impl Trait items.
25+
ReachableFromImplTrait,
2426
// Exported items + items participating in various kinds of public interfaces,
2527
// but not directly nameable. For example, if function `fn f() -> T {...}` is
2628
// public, then type `T` is reachable. Its values can be obtained by other crates
@@ -40,7 +42,7 @@ pub struct AccessLevels<Id = NodeId> {
4042

4143
impl<Id: Hash + Eq> AccessLevels<Id> {
4244
pub fn is_reachable(&self, id: Id) -> bool {
43-
self.map.contains_key(&id)
45+
self.map.get(&id) >= Some(&AccessLevel::Reachable)
4446
}
4547
pub fn is_exported(&self, id: Id) -> bool {
4648
self.map.get(&id) >= Some(&AccessLevel::Exported)

src/librustc/middle/reachable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,8 @@ fn reachable_set<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) ->
434434
// Step 2: Mark all symbols that the symbols on the worklist touch.
435435
reachable_context.propagate();
436436

437+
debug!("Inline reachability shows: {:?}", reachable_context.reachable_symbols);
438+
437439
// Return the set of reachable symbols.
438440
ReachableSet(Lrc::new(reachable_context.reachable_symbols))
439441
}

src/librustc_privacy/lib.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ struct EmbargoVisitor<'a, 'tcx: 'a> {
8282
}
8383

8484
struct ReachEverythingInTheInterfaceVisitor<'b, 'a: 'b, 'tcx: 'a> {
85+
access_level: Option<AccessLevel>,
8586
item_def_id: DefId,
8687
ev: &'b mut EmbargoVisitor<'a, 'tcx>,
8788
}
@@ -132,6 +133,7 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
132133
fn reach<'b>(&'b mut self, item_id: ast::NodeId)
133134
-> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
134135
ReachEverythingInTheInterfaceVisitor {
136+
access_level: self.prev_level.map(|l| l.min(AccessLevel::Reachable)),
135137
item_def_id: self.tcx.hir.local_def_id(item_id),
136138
ev: self,
137139
}
@@ -214,7 +216,15 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
214216
}
215217
}
216218
}
217-
hir::ItemKind::Existential(..) |
219+
// Impl trait return types mark their parent function.
220+
// It (and its children) are revisited if the change applies.
221+
hir::ItemKind::Existential(ref ty_data) => {
222+
if let Some(impl_trait_fn) = ty_data.impl_trait_fn {
223+
if let Some(node_id) = self.tcx.hir.as_local_node_id(impl_trait_fn) {
224+
self.update(node_id, Some(AccessLevel::ReachableFromImplTrait));
225+
}
226+
}
227+
}
218228
hir::ItemKind::Use(..) |
219229
hir::ItemKind::Static(..) |
220230
hir::ItemKind::Const(..) |
@@ -226,6 +236,10 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
226236
hir::ItemKind::ExternCrate(..) => {}
227237
}
228238

239+
// Store this node's access level here to propagate the correct
240+
// reachability level through interfaces and children.
241+
let orig_level = replace(&mut self.prev_level, item_level);
242+
229243
// Mark all items in interfaces of reachable items as reachable
230244
match item.node {
231245
// The interface is empty
@@ -324,9 +338,6 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
324338
}
325339
}
326340

327-
let orig_level = self.prev_level;
328-
self.prev_level = item_level;
329-
330341
intravisit::walk_item(self, item);
331342

332343
self.prev_level = orig_level;
@@ -462,7 +473,7 @@ impl<'b, 'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
462473
fn check_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) {
463474
if let Some(node_id) = self.ev.tcx.hir.as_local_node_id(trait_ref.def_id) {
464475
let item = self.ev.tcx.hir.expect_item(node_id);
465-
self.ev.update(item.id, Some(AccessLevel::Reachable));
476+
self.ev.update(item.id, self.access_level);
466477
}
467478
}
468479
}
@@ -483,7 +494,7 @@ impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b
483494

484495
if let Some(def_id) = ty_def_id {
485496
if let Some(node_id) = self.ev.tcx.hir.as_local_node_id(def_id) {
486-
self.ev.update(node_id, Some(AccessLevel::Reachable));
497+
self.ev.update(node_id, self.access_level);
487498
}
488499
}
489500

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2018 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+
#![crate_type = "lib"]
12+
13+
pub fn bar<P>( // Error won't happen if "bar" is not generic
14+
_baz: P,
15+
) {
16+
hide_foo()();
17+
}
18+
19+
fn hide_foo() -> impl Fn() { // Error won't happen if "iterate" hasn't impl Trait or has generics
20+
foo
21+
}
22+
23+
fn foo() { // Error won't happen if "foo" isn't used in "iterate" or has generics
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2018 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+
// aux-build:lib.rs
12+
13+
// Regression test for #50865.
14+
// When using generics or specifying the type directly, this example
15+
// codegens `foo` internally. However, when using a private `impl Trait`
16+
// function which references another private item, `foo` (in this case)
17+
// wouldn't be codegenned until main.rs used `bar`, as with impl Trait
18+
// it is not cast to `fn()` automatically to satisfy e.g.
19+
// `fn foo() -> fn() { ... }`.
20+
21+
extern crate lib;
22+
23+
fn main() {
24+
lib::bar(()); // Error won't happen if bar is called from same crate
25+
}

0 commit comments

Comments
 (0)