Rollup merge of #53545 - FelixMcFelix:fix-50865-beta, r=petrochenkov
Fix #50865: ICE on impl-trait returning functions reaching private items Adds a test case as suggested in #50865, and implements @petrochenkov's suggestion. Fixes #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.
This commit is contained in:
commit
a1ec2f76bb
6 changed files with 72 additions and 7 deletions
|
@ -1091,6 +1091,7 @@ impl_stable_hash_for!(enum traits::Reveal {
|
|||
});
|
||||
|
||||
impl_stable_hash_for!(enum ::middle::privacy::AccessLevel {
|
||||
ReachableFromImplTrait,
|
||||
Reachable,
|
||||
Exported,
|
||||
Public
|
||||
|
|
|
@ -21,6 +21,8 @@ use syntax::ast::NodeId;
|
|||
// Accessibility levels, sorted in ascending order
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum AccessLevel {
|
||||
// Superset of Reachable used to mark impl Trait items.
|
||||
ReachableFromImplTrait,
|
||||
// Exported items + items participating in various kinds of public interfaces,
|
||||
// but not directly nameable. For example, if function `fn f() -> T {...}` is
|
||||
// public, then type `T` is reachable. Its values can be obtained by other crates
|
||||
|
@ -40,7 +42,7 @@ pub struct AccessLevels<Id = NodeId> {
|
|||
|
||||
impl<Id: Hash + Eq> AccessLevels<Id> {
|
||||
pub fn is_reachable(&self, id: Id) -> bool {
|
||||
self.map.contains_key(&id)
|
||||
self.map.get(&id) >= Some(&AccessLevel::Reachable)
|
||||
}
|
||||
pub fn is_exported(&self, id: Id) -> bool {
|
||||
self.map.get(&id) >= Some(&AccessLevel::Exported)
|
||||
|
|
|
@ -434,6 +434,8 @@ fn reachable_set<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum) ->
|
|||
// Step 2: Mark all symbols that the symbols on the worklist touch.
|
||||
reachable_context.propagate();
|
||||
|
||||
debug!("Inline reachability shows: {:?}", reachable_context.reachable_symbols);
|
||||
|
||||
// Return the set of reachable symbols.
|
||||
ReachableSet(Lrc::new(reachable_context.reachable_symbols))
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ struct EmbargoVisitor<'a, 'tcx: 'a> {
|
|||
}
|
||||
|
||||
struct ReachEverythingInTheInterfaceVisitor<'b, 'a: 'b, 'tcx: 'a> {
|
||||
access_level: Option<AccessLevel>,
|
||||
item_def_id: DefId,
|
||||
ev: &'b mut EmbargoVisitor<'a, 'tcx>,
|
||||
}
|
||||
|
@ -132,6 +133,7 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
|
|||
fn reach<'b>(&'b mut self, item_id: ast::NodeId)
|
||||
-> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
|
||||
ReachEverythingInTheInterfaceVisitor {
|
||||
access_level: self.prev_level.map(|l| l.min(AccessLevel::Reachable)),
|
||||
item_def_id: self.tcx.hir.local_def_id(item_id),
|
||||
ev: self,
|
||||
}
|
||||
|
@ -214,7 +216,15 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Existential(..) |
|
||||
// Impl trait return types mark their parent function.
|
||||
// It (and its children) are revisited if the change applies.
|
||||
hir::ItemKind::Existential(ref ty_data) => {
|
||||
if let Some(impl_trait_fn) = ty_data.impl_trait_fn {
|
||||
if let Some(node_id) = self.tcx.hir.as_local_node_id(impl_trait_fn) {
|
||||
self.update(node_id, Some(AccessLevel::ReachableFromImplTrait));
|
||||
}
|
||||
}
|
||||
}
|
||||
hir::ItemKind::Use(..) |
|
||||
hir::ItemKind::Static(..) |
|
||||
hir::ItemKind::Const(..) |
|
||||
|
@ -226,6 +236,10 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
|
|||
hir::ItemKind::ExternCrate(..) => {}
|
||||
}
|
||||
|
||||
// Store this node's access level here to propagate the correct
|
||||
// reachability level through interfaces and children.
|
||||
let orig_level = replace(&mut self.prev_level, item_level);
|
||||
|
||||
// Mark all items in interfaces of reachable items as reachable
|
||||
match item.node {
|
||||
// The interface is empty
|
||||
|
@ -324,9 +338,6 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
let orig_level = self.prev_level;
|
||||
self.prev_level = item_level;
|
||||
|
||||
intravisit::walk_item(self, item);
|
||||
|
||||
self.prev_level = orig_level;
|
||||
|
@ -462,7 +473,7 @@ impl<'b, 'a, 'tcx> ReachEverythingInTheInterfaceVisitor<'b, 'a, 'tcx> {
|
|||
fn check_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) {
|
||||
if let Some(node_id) = self.ev.tcx.hir.as_local_node_id(trait_ref.def_id) {
|
||||
let item = self.ev.tcx.hir.expect_item(node_id);
|
||||
self.ev.update(item.id, Some(AccessLevel::Reachable));
|
||||
self.ev.update(item.id, self.access_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -483,7 +494,7 @@ impl<'b, 'a, 'tcx> TypeVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'b
|
|||
|
||||
if let Some(def_id) = ty_def_id {
|
||||
if let Some(node_id) = self.ev.tcx.hir.as_local_node_id(def_id) {
|
||||
self.ev.update(node_id, Some(AccessLevel::Reachable));
|
||||
self.ev.update(node_id, self.access_level);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
pub fn bar<P>( // Error won't happen if "bar" is not generic
|
||||
_baz: P,
|
||||
) {
|
||||
hide_foo()();
|
||||
}
|
||||
|
||||
fn hide_foo() -> impl Fn() { // Error won't happen if "iterate" hasn't impl Trait or has generics
|
||||
foo
|
||||
}
|
||||
|
||||
fn foo() { // Error won't happen if "foo" isn't used in "iterate" or has generics
|
||||
}
|
25
src/test/run-pass/issue-50865-private-impl-trait/main.rs
Normal file
25
src/test/run-pass/issue-50865-private-impl-trait/main.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2018 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:lib.rs
|
||||
|
||||
// Regression test for #50865.
|
||||
// When using generics or specifying the type directly, this example
|
||||
// codegens `foo` internally. However, when using a private `impl Trait`
|
||||
// function which references another private item, `foo` (in this case)
|
||||
// wouldn't be codegenned until main.rs used `bar`, as with impl Trait
|
||||
// it is not cast to `fn()` automatically to satisfy e.g.
|
||||
// `fn foo() -> fn() { ... }`.
|
||||
|
||||
extern crate lib;
|
||||
|
||||
fn main() {
|
||||
lib::bar(()); // Error won't happen if bar is called from same crate
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue