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

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

Merged
merged 7 commits into from
Sep 16, 2019
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);
varkor marked this conversation as resolved.
Show resolved Hide resolved
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() {}