Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #50865: ICE on impl-trait returning functions reaching private items #53545

Merged
merged 7 commits into from
Aug 24, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2237,6 +2237,7 @@ dependencies = [
name = "rustc_privacy"
version = "0.0.0"
dependencies = [
"log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you avoid introducing this new dependency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any debug!("...") calls would then also need to be removed. Is this version of log different from that used by the rest of the rustc_* crates?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debug!(...) logging used for a specific debugging session is usually useless for anything else in the future, so I mean yes, it is better removed.

"rustc 0.0.0",
"rustc_data_structures 0.0.0",
"rustc_typeck 0.0.0",
Expand Down
1 change: 1 addition & 0 deletions src/librustc/ich/impls_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,7 @@ impl_stable_hash_for!(enum traits::Reveal {
});

impl_stable_hash_for!(enum ::middle::privacy::AccessLevel {
ReachableFromImplTrait,
Reachable,
Exported,
Public
Expand Down
4 changes: 3 additions & 1 deletion src/librustc/middle/privacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/middle/reachable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,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))
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_privacy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ path = "lib.rs"
crate-type = ["dylib"]

[dependencies]
log = { version = "0.4", features = ["release_max_level_info", "std"] }
rustc = { path = "../librustc" }
rustc_typeck = { path = "../librustc_typeck" }
syntax = { path = "../libsyntax" }
Expand Down
31 changes: 25 additions & 6 deletions src/librustc_privacy/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#![recursion_limit="256"]

#[macro_use] extern crate log;
#[macro_use] extern crate rustc;
#[macro_use] extern crate syntax;
extern crate rustc_typeck;
Expand Down Expand Up @@ -83,6 +84,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>,
}
Expand Down Expand Up @@ -133,6 +135,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,
}
Expand All @@ -147,6 +150,7 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
}

fn visit_item(&mut self, item: &'tcx hir::Item) {
debug!("Walked item {:?}", item);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually this would be something like: debug!("visit_item({:?})", item);, so it's easier to see where it comes from.

let inherited_item_level = match item.node {
// Impls inherit level from their types and traits
hir::ItemKind::Impl(..) => {
Expand All @@ -157,12 +161,21 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
hir::ItemKind::ForeignMod(..) => {
self.prev_level
}
// 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));
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you move this piece of code from here to // Update levels of nested things below, it belongs there logically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

if item.vis.node.is_pub() { self.prev_level } else { None }
}
// Other `pub` items inherit levels from parents
hir::ItemKind::Const(..) | hir::ItemKind::Enum(..) | hir::ItemKind::ExternCrate(..) |
hir::ItemKind::GlobalAsm(..) | hir::ItemKind::Fn(..) | hir::ItemKind::Mod(..) |
hir::ItemKind::Static(..) | hir::ItemKind::Struct(..) |
hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) |
hir::ItemKind::Existential(..) |
hir::ItemKind::Ty(..) | hir::ItemKind::Union(..) | hir::ItemKind::Use(..) => {
if item.vis.node.is_pub() { self.prev_level } else { None }
}
Expand All @@ -171,6 +184,8 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
// Update level of the item itself
let item_level = self.update(item.id, inherited_item_level);

debug!("Its privacy is believed to be: {:?}", item_level);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps just item_level = {:?} is sufficient?


// Update levels of nested things
match item.node {
hir::ItemKind::Enum(ref def, _) => {
Expand Down Expand Up @@ -227,6 +242,9 @@ impl<'a, 'tcx> Visitor<'tcx> for EmbargoVisitor<'a, 'tcx> {
hir::ItemKind::ExternCrate(..) => {}
}

let orig_level = self.prev_level;
self.prev_level = item_level;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: let orig_level = mem::replace(&mut self.prev_level, item_level);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure why these lines were moved from there to here actually.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. This was moved while I was figuring out how best to propagate the level from the ReachEverythingInTheInterfaceVisitor. Will move back and modify.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this was moved so that EmbargoVisitor::reach would be able to see whether the parent node was ReachableFromImplTrait or at least Reachable, to propagate the correct level.


// Mark all items in interfaces of reachable items as reachable
match item.node {
// The interface is empty
Expand Down Expand Up @@ -324,9 +342,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;
Expand Down Expand Up @@ -462,7 +477,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);
}
}
}
Expand All @@ -483,7 +498,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);
}
}

Expand Down Expand Up @@ -1737,6 +1752,8 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
}
visitor.update(ast::CRATE_NODE_ID, Some(AccessLevel::Public));

debug!("access levels after embargo: {:?}", &visitor.access_levels);

{
let mut visitor = ObsoleteVisiblePrivateTypesVisitor {
tcx,
Expand Down Expand Up @@ -1766,6 +1783,8 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
krate.visit_all_item_likes(&mut DeepVisitor::new(&mut visitor));
}

debug!("final access levels: {:?}", &visitor.access_levels);

Lrc::new(visitor.access_levels)
}

Expand Down
24 changes: 24 additions & 0 deletions src/test/run-pass/issue-50865-private-impl-trait/auxiliary/lib.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 25 additions & 0 deletions src/test/run-pass/issue-50865-private-impl-trait/main.rs
Original file line number Diff line number Diff line change
@@ -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
}