Skip to content

Commit

Permalink
Rollup merge of rust-lang#64250 - Xanewok:save-analysis-assoc-nested,…
Browse files Browse the repository at this point in the history
… r=varkor

save-analysis: Nest typeck tables when processing functions/methods

Fixes an issue where we did not nest tables correctly when resolving
associated types in formal argument/return type positions.

This was the minimized reproduction case that I tested the fix on:
```rust
pub trait Trait {
    type Assoc;
}

pub struct A;

pub fn func() {
    fn _inner1<U: Trait>(_: U::Assoc) {}
    fn _inner2<U: Trait>() -> U::Assoc { unimplemented!() }

    impl A {
        fn _inner1<U: Trait>(self, _: U::Assoc) {}
        fn _inner2<U: Trait>(self) -> U::Assoc { unimplemented!() }
    }
}
```
using `debug_assertions`-enabled rustc and by additionally passing `-Zsave-analysis`.

Unfortunately the original assertion fired is a *debug* one and from what I can tell we don't run the tests with these on, so I'm not adding a test here. If I missed it and there is a way to run tests with these on, I'd love to add a test case for this.

Closes rust-lang#63663
Closes rust-lang#50328
Closes rust-lang#43982
  • Loading branch information
Centril committed Sep 15, 2019
2 parents 0a2e07e + a946b8d commit 2e11e81
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 80 deletions.
38 changes: 18 additions & 20 deletions src/librustc/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,26 +205,24 @@ pub struct LocalTableInContext<'a, V> {
fn validate_hir_id_for_typeck_tables(local_id_root: Option<DefId>,
hir_id: hir::HirId,
mut_access: bool) {
if cfg!(debug_assertions) {
if let Some(local_id_root) = local_id_root {
if hir_id.owner != local_id_root.index {
ty::tls::with(|tcx| {
bug!("node {} with HirId::owner {:?} cannot be placed in \
TypeckTables with local_id_root {:?}",
tcx.hir().node_to_string(hir_id),
DefId::local(hir_id.owner),
local_id_root)
});
}
} else {
// We use "Null Object" TypeckTables in some of the analysis passes.
// These are just expected to be empty and their `local_id_root` is
// `None`. Therefore we cannot verify whether a given `HirId` would
// be a valid key for the given table. Instead we make sure that
// nobody tries to write to such a Null Object table.
if mut_access {
bug!("access to invalid TypeckTables")
}
if let Some(local_id_root) = local_id_root {
if hir_id.owner != local_id_root.index {
ty::tls::with(|tcx| {
bug!("node {} with HirId::owner {:?} cannot be placed in \
TypeckTables with local_id_root {:?}",
tcx.hir().node_to_string(hir_id),
DefId::local(hir_id.owner),
local_id_root)
});
}
} else {
// We use "Null Object" TypeckTables in some of the analysis passes.
// These are just expected to be empty and their `local_id_root` is
// `None`. Therefore we cannot verify whether a given `HirId` would
// be a valid key for the given table. Instead we make sure that
// nobody tries to write to such a Null Object table.
if mut_access {
bug!("access to invalid TypeckTables")
}
}
}
Expand Down
119 changes: 63 additions & 56 deletions src/librustc_save_analysis/dump_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
self.save_ctxt.span_from_span(span)
}

fn lookup_def_id(&self, ref_id: NodeId) -> Option<DefId> {
self.save_ctxt.lookup_def_id(ref_id)
}

pub fn dump_crate_info(&mut self, name: &str, krate: &ast::Crate) {
let source_file = self.tcx.sess.local_crate_source_file.as_ref();
let crate_root = source_file.map(|source_file| {
Expand Down Expand Up @@ -223,13 +227,6 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
}
}

fn lookup_def_id(&self, ref_id: NodeId) -> Option<DefId> {
match self.save_ctxt.get_path_res(ref_id) {
Res::PrimTy(..) | Res::SelfTy(..) | Res::Err => None,
def => Some(def.def_id()),
}
}

fn process_formals(&mut self, formals: &'l [ast::Param], qualname: &str) {
for arg in formals {
self.visit_pat(&arg.pat);
Expand Down Expand Up @@ -283,36 +280,32 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
) {
debug!("process_method: {}:{}", id, ident);

if let Some(mut method_data) = self.save_ctxt.get_method_data(id, ident, span) {
let sig_str = crate::make_signature(&sig.decl, &generics);
if body.is_some() {
self.nest_tables(
id,
|v| v.process_formals(&sig.decl.inputs, &method_data.qualname),
);
}
let hir_id = self.tcx.hir().node_to_hir_id(id);
self.nest_tables(id, |v| {
if let Some(mut method_data) = v.save_ctxt.get_method_data(id, ident, span) {
v.process_formals(&sig.decl.inputs, &method_data.qualname);
v.process_generic_params(&generics, &method_data.qualname, id);

self.process_generic_params(&generics, &method_data.qualname, id);
method_data.value = crate::make_signature(&sig.decl, &generics);
method_data.sig = sig::method_signature(id, ident, generics, sig, &v.save_ctxt);

method_data.value = sig_str;
method_data.sig = sig::method_signature(id, ident, generics, sig, &self.save_ctxt);
let hir_id = self.tcx.hir().node_to_hir_id(id);
self.dumper.dump_def(&access_from_vis!(self.save_ctxt, vis, hir_id), method_data);
}
v.dumper.dump_def(&access_from_vis!(v.save_ctxt, vis, hir_id), method_data);
}

// walk arg and return types
for arg in &sig.decl.inputs {
self.visit_ty(&arg.ty);
}
// walk arg and return types
for arg in &sig.decl.inputs {
v.visit_ty(&arg.ty);
}

if let ast::FunctionRetTy::Ty(ref ret_ty) = sig.decl.output {
self.visit_ty(ret_ty);
}
if let ast::FunctionRetTy::Ty(ref ret_ty) = sig.decl.output {
v.visit_ty(ret_ty);
}

// walk the fn body
if let Some(body) = body {
self.nest_tables(id, |v| v.visit_block(body));
}
// walk the fn body
if let Some(body) = body {
v.visit_block(body);
}
});
}

fn process_struct_field_def(&mut self, field: &ast::StructField, parent_id: NodeId) {
Expand Down Expand Up @@ -377,26 +370,31 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
ty_params: &'l ast::Generics,
body: &'l ast::Block,
) {
if let Some(fn_data) = self.save_ctxt.get_item_data(item) {
down_cast_data!(fn_data, DefData, item.span);
self.nest_tables(
item.id,
|v| v.process_formals(&decl.inputs, &fn_data.qualname),
);
self.process_generic_params(ty_params, &fn_data.qualname, item.id);
let hir_id = self.tcx.hir().node_to_hir_id(item.id);
self.dumper.dump_def(&access_from!(self.save_ctxt, item, hir_id), fn_data);
}
let hir_id = self.tcx.hir().node_to_hir_id(item.id);
self.nest_tables(item.id, |v| {
if let Some(fn_data) = v.save_ctxt.get_item_data(item) {
down_cast_data!(fn_data, DefData, item.span);
v.process_formals(&decl.inputs, &fn_data.qualname);
v.process_generic_params(ty_params, &fn_data.qualname, item.id);

for arg in &decl.inputs {
self.visit_ty(&arg.ty);
}
v.dumper.dump_def(&access_from!(v.save_ctxt, item, hir_id), fn_data);
}

if let ast::FunctionRetTy::Ty(ref ret_ty) = decl.output {
self.visit_ty(&ret_ty);
}
for arg in &decl.inputs {
v.visit_ty(&arg.ty)
}

if let ast::FunctionRetTy::Ty(ref ret_ty) = decl.output {
if let ast::TyKind::ImplTrait(..) = ret_ty.node {
// FIXME: Opaque type desugaring prevents us from easily
// processing trait bounds. See `visit_ty` for more details.
} else {
v.visit_ty(&ret_ty);
}
}

self.nest_tables(item.id, |v| v.visit_block(&body));
v.visit_block(&body);
});
}

fn process_static_or_const_item(
Expand Down Expand Up @@ -1113,11 +1111,7 @@ impl<'l, 'tcx> DumpVisitor<'l, 'tcx> {
// FIXME: uses of the assoc type should ideally point to this
// 'def' and the name here should be a ref to the def in the
// trait.
for bound in bounds.iter() {
if let ast::GenericBound::Trait(trait_ref, _) = bound {
self.process_path(trait_ref.trait_ref.ref_id, &trait_ref.trait_ref.path)
}
}
self.process_bounds(&bounds);
}
ast::ImplItemKind::Macro(_) => {}
}
Expand Down Expand Up @@ -1364,10 +1358,10 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> {
self.visit_ty(&ty);
self.process_generic_params(ty_params, &qualname, item.id);
}
OpaqueTy(ref _bounds, ref ty_params) => {
OpaqueTy(ref bounds, ref ty_params) => {
let qualname = format!("::{}",
self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id)));
// FIXME do something with _bounds

let value = String::new();
if !self.span.filter_generated(item.ident.span) {
let span = self.span_from_span(item.ident.span);
Expand All @@ -1393,6 +1387,7 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> {
);
}

self.process_bounds(bounds);
self.process_generic_params(ty_params, &qualname, item.id);
}
Mac(_) => (),
Expand Down Expand Up @@ -1449,6 +1444,18 @@ impl<'l, 'tcx> Visitor<'l> for DumpVisitor<'l, 'tcx> {
self.visit_ty(element);
self.nest_tables(length.id, |v| v.visit_expr(&length.value));
}
ast::TyKind::ImplTrait(id, ref bounds) => {
// FIXME: As of writing, the opaque type lowering introduces
// another DefPath scope/segment (used to declare the resulting
// opaque type item).
// However, the synthetic scope does *not* have associated
// typeck tables, which means we can't nest it and we fire an
// assertion when resolving the qualified type paths in trait
// bounds...
// This will panic if called on return type `impl Trait`, which
// we guard against in `process_fn`.
self.nest_tables(id, |v| v.process_bounds(bounds));
}
_ => visit::walk_ty(self, t),
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/librustc_save_analysis/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
let impl_id = self.next_impl_id();
let span = self.span_from_span(sub_span);

let type_data = self.lookup_ref_id(typ.id);
let type_data = self.lookup_def_id(typ.id);
type_data.map(|type_data| {
Data::RelationData(Relation {
kind: RelationKind::Impl {
Expand All @@ -322,7 +322,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
from: id_from_def_id(type_data),
to: trait_ref
.as_ref()
.and_then(|t| self.lookup_ref_id(t.ref_id))
.and_then(|t| self.lookup_def_id(t.ref_id))
.map(id_from_def_id)
.unwrap_or_else(|| null_id()),
},
Expand Down Expand Up @@ -495,7 +495,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
}

pub fn get_trait_ref_data(&self, trait_ref: &ast::TraitRef) -> Option<Ref> {
self.lookup_ref_id(trait_ref.ref_id).and_then(|def_id| {
self.lookup_def_id(trait_ref.ref_id).and_then(|def_id| {
let span = trait_ref.path.span;
if generated_code(span) {
return None;
Expand Down Expand Up @@ -870,7 +870,7 @@ impl<'l, 'tcx> SaveContext<'l, 'tcx> {
})
}

fn lookup_ref_id(&self, ref_id: NodeId) -> Option<DefId> {
fn lookup_def_id(&self, ref_id: NodeId) -> Option<DefId> {
match self.get_path_res(ref_id) {
Res::PrimTy(_) | Res::SelfTy(..) | Res::Err => None,
def => Some(def.def_id()),
Expand Down
28 changes: 28 additions & 0 deletions src/test/ui/save-analysis/issue-63663.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// check-pass
// compile-flags: -Zsave-analysis

pub trait Trait {
type Assoc;
}

pub struct A;

trait Generic<T> {}
impl<T> Generic<T> for () {}

// Don't ICE when resolving type paths in return type `impl Trait`
fn assoc_in_opaque_type_bounds<U: Trait>() -> impl Generic<U::Assoc> {}

// Check that this doesn't ICE when processing associated const in formal
// argument and return type of functions defined inside function/method scope.
pub fn func() {
fn _inner1<U: Trait>(_: U::Assoc) {}
fn _inner2<U: Trait>() -> U::Assoc { unimplemented!() }

impl A {
fn _inner1<U: Trait>(self, _: U::Assoc) {}
fn _inner2<U: Trait>(self) -> U::Assoc { unimplemented!() }
}
}

fn main() {}

0 comments on commit 2e11e81

Please sign in to comment.