From: Niko Matsakis Date: Wed, 16 Mar 2016 09:57:03 +0000 (-0400) Subject: refactor item-paths in diagnostics, symbol names X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=2291abf313b1c619a34694e1756ddaf2a5cb34d9;p=rust.git refactor item-paths in diagnostics, symbol names This change has a few parts. We introduce a new `item_path` module for constructing item paths. The job of this module is basically to make nice, user-readable paths -- but these paths are not necessarily 100% unique. They meant to help a *human* find code, but not necessarily a compute. These paths are used to drive `item_path_str` but also symbol names. Because the paths are not unique, we also modify the symbol name hash to include the full `DefPath`, whereas before it included only those aspects of the def-path that were not included in the "informative" symbol name. Eventually, I'd like to make the item-path infrastructure a bit more declarative. Right now it's based purely on strings. In particular, for impls, we should supply the raw types to the `ItemPathBuffer`, so that symbol names can be encoded using the C++ encoding scheme for better integration with tooling. --- diff --git a/src/librustc/middle/ty/item_path.rs b/src/librustc/middle/ty/item_path.rs new file mode 100644 index 00000000000..147230f5bdc --- /dev/null +++ b/src/librustc/middle/ty/item_path.rs @@ -0,0 +1,317 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use front::map::DefPathData; +use middle::cstore::LOCAL_CRATE; +use middle::def_id::DefId; +use middle::ty::{self, Ty, TyCtxt}; +use syntax::ast; + +impl<'tcx> TyCtxt<'tcx> { + /// Returns a string identifying this def-id. This string is + /// suitable for user output. It is relative to the current crate + /// root. + pub fn item_path_str(&self, def_id: DefId) -> String { + let mut buffer = LocalPathBuffer::new(RootMode::Local); + self.push_item_path(&mut buffer, def_id); + buffer.into_string() + } + + /// Returns a string identifying this def-id. This string is + /// suitable for user output. It always begins with a crate identifier. + pub fn absolute_item_path_str(&self, def_id: DefId) -> String { + let mut buffer = LocalPathBuffer::new(RootMode::Absolute); + self.push_item_path(&mut buffer, def_id); + buffer.into_string() + } + + /// Returns the "path" to a particular crate. This can proceed in + /// various ways, depending on the `root_mode` of the `buffer`. + /// (See `RootMode` enum for more details.) + pub fn push_krate_path(&self, buffer: &mut T, cnum: ast::CrateNum) + where T: ItemPathBuffer + { + match *buffer.root_mode() { + RootMode::Local => { + // In local mode, when we encounter a crate other than + // LOCAL_CRATE, execution proceeds in one of two ways: + // + // 1. for a direct dependency, where user added an + // `extern crate` manually, we put the `extern + // crate` as the parent. So you wind up with + // something relative to the current crate. + // 2. for an indirect crate, where there is no extern + // crate, we just prepend the crate name. + // + // Returns `None` for the local crate. + if cnum != LOCAL_CRATE { + let opt_extern_crate = self.sess.cstore.extern_crate(cnum); + let opt_extern_crate = opt_extern_crate.and_then(|extern_crate| { + if extern_crate.direct { + Some(extern_crate.def_id) + } else { + None + } + }); + if let Some(extern_crate_def_id) = opt_extern_crate { + self.push_item_path(buffer, extern_crate_def_id); + } else { + buffer.push(&self.crate_name(cnum)); + } + } + } + RootMode::Absolute => { + // In absolute mode, just write the crate name + // unconditionally. + buffer.push(&self.crate_name(cnum)); + } + } + } + + pub fn push_item_path(&self, buffer: &mut T, def_id: DefId) + where T: ItemPathBuffer + { + let key = self.def_key(def_id); + match key.disambiguated_data.data { + DefPathData::CrateRoot => { + assert!(key.parent.is_none()); + self.push_krate_path(buffer, def_id.krate); + } + + DefPathData::InlinedRoot(ref root_path) => { + assert!(key.parent.is_none()); + self.push_item_path(buffer, root_path.def_id); + } + + DefPathData::Impl => { + self.push_impl_path(buffer, def_id); + } + + // Unclear if there is any value in distinguishing these. + // Probably eventually (and maybe we would even want + // finer-grained distinctions, e.g. between enum/struct). + data @ DefPathData::Misc | + data @ DefPathData::TypeNs(..) | + data @ DefPathData::ValueNs(..) | + data @ DefPathData::TypeParam(..) | + data @ DefPathData::LifetimeDef(..) | + data @ DefPathData::EnumVariant(..) | + data @ DefPathData::Field(..) | + data @ DefPathData::StructCtor | + data @ DefPathData::Initializer | + data @ DefPathData::MacroDef(..) | + data @ DefPathData::ClosureExpr | + data @ DefPathData::Binding(..) => { + let parent_def_id = self.parent_def_id(def_id).unwrap(); + self.push_item_path(buffer, parent_def_id); + buffer.push(&data.as_interned_str()); + } + } + } + + fn push_impl_path(&self, + buffer: &mut T, + impl_def_id: DefId) + where T: ItemPathBuffer + { + let parent_def_id = self.parent_def_id(impl_def_id).unwrap(); + + let use_types = if !impl_def_id.is_local() { + // always have full types available for extern crates + true + } else { + // for local crates, check whether type info is + // available; typeck might not have completed yet + self.impl_trait_refs.borrow().contains_key(&impl_def_id) + }; + + if !use_types { + return self.push_impl_path_fallback(buffer, impl_def_id); + } + + // Decide whether to print the parent path for the impl. + // Logically, since impls are global, it's never needed, but + // users may find it useful. Currently, we omit the parent if + // the impl is either in the same module as the self-type or + // as the trait. + let self_ty = self.lookup_item_type(impl_def_id).ty; + let in_self_mod = match self.characteristic_def_id_of_type(self_ty) { + None => false, + Some(ty_def_id) => self.parent_def_id(ty_def_id) == Some(parent_def_id), + }; + + let impl_trait_ref = self.impl_trait_ref(impl_def_id); + let in_trait_mod = match impl_trait_ref { + None => false, + Some(trait_ref) => self.parent_def_id(trait_ref.def_id) == Some(parent_def_id), + }; + + if !in_self_mod && !in_trait_mod { + // If the impl is not co-located with either self-type or + // trait-type, then fallback to a format that identifies + // the module more clearly. + self.push_item_path(buffer, parent_def_id); + if let Some(trait_ref) = impl_trait_ref { + buffer.push(&format!("", trait_ref, self_ty)); + } else { + buffer.push(&format!("", self_ty)); + } + return; + } + + // Otherwise, try to give a good form that would be valid language + // syntax. Preferably using associated item notation. + + if let Some(trait_ref) = impl_trait_ref { + // Trait impls. + buffer.push(&format!("<{} as {}>", + self_ty, + trait_ref)); + return; + } + + // Inherent impls. Try to print `Foo::bar` for an inherent + // impl on `Foo`, but fallback to `::bar` if self-type is + // anything other than a simple path. + match self_ty.sty { + ty::TyStruct(adt_def, substs) | + ty::TyEnum(adt_def, substs) => { + if substs.types.is_empty() { // ignore regions + self.push_item_path(buffer, adt_def.did); + } else { + buffer.push(&format!("<{}>", self_ty)); + } + } + + ty::TyBool | + ty::TyChar | + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyFloat(_) | + ty::TyStr => { + buffer.push(&format!("{}", self_ty)); + } + + _ => { + buffer.push(&format!("<{}>", self_ty)); + } + } + } + + fn push_impl_path_fallback(&self, + buffer: &mut T, + impl_def_id: DefId) + where T: ItemPathBuffer + { + // If no type info is available, fall back to + // pretty printing some span information. This should + // only occur very early in the compiler pipeline. + let parent_def_id = self.parent_def_id(impl_def_id).unwrap(); + self.push_item_path(buffer, parent_def_id); + let node_id = self.map.as_local_node_id(impl_def_id).unwrap(); + let item = self.map.expect_item(node_id); + let span_str = self.sess.codemap().span_to_string(item.span); + buffer.push(&format!("", span_str)); + } + + /// As a heuristic, when we see an impl, if we see that the + /// 'self-type' is a type defined in the same module as the impl, + /// we can omit including the path to the impl itself. This + /// function tries to find a "characteristic def-id" for a + /// type. It's just a heuristic so it makes some questionable + /// decisions and we may want to adjust it later. + fn characteristic_def_id_of_type(&self, ty: Ty<'tcx>) -> Option { + match ty.sty { + ty::TyStruct(adt_def, _) | + ty::TyEnum(adt_def, _) => + Some(adt_def.did), + + ty::TyTrait(ref data) => + Some(data.principal_def_id()), + + ty::TyBox(subty) => + self.characteristic_def_id_of_type(subty), + + ty::TyRawPtr(mt) | + ty::TyRef(_, mt) => + self.characteristic_def_id_of_type(mt.ty), + + ty::TyTuple(ref tys) => + tys.iter() + .filter_map(|ty| self.characteristic_def_id_of_type(ty)) + .next(), + + _ => + None + } + } + + /// Returns the def-id of `def_id`'s parent in the def tree. If + /// this returns `None`, then `def_id` represents a crate root or + /// inlined root. + fn parent_def_id(&self, def_id: DefId) -> Option { + let key = self.def_key(def_id); + key.parent.map(|index| DefId { krate: def_id.krate, index: index }) + } +} + +/// Unifying Trait for different kinds of item paths we might +/// construct. The basic interface is that components get pushed: the +/// instance can also customize how we handle the root of a crate. +pub trait ItemPathBuffer { + fn root_mode(&self) -> &RootMode; + fn push(&mut self, text: &str); +} + +#[derive(Debug)] +pub enum RootMode { + /// Try to make a path relative to the local crate. In + /// particular, local paths have no prefix, and if the path comes + /// from an extern crate, start with the path to the `extern + /// crate` declaration. + Local, + + /// Always prepend the crate name to the path, forming an absolute + /// path from within a given set of crates. + Absolute, +} + +#[derive(Debug)] +struct LocalPathBuffer { + root_mode: RootMode, + str: String, +} + +impl LocalPathBuffer { + fn new(root_mode: RootMode) -> LocalPathBuffer { + LocalPathBuffer { + root_mode: root_mode, + str: String::new() + } + } + + fn into_string(self) -> String { + self.str + } + +} + +impl ItemPathBuffer for LocalPathBuffer { + fn root_mode(&self) -> &RootMode { + &self.root_mode + } + + fn push(&mut self, text: &str) { + if !self.str.is_empty() { + self.str.push_str("::"); + } + self.str.push_str(text); + } +} diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 61e591e2fce..a4c3e82b633 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -86,6 +86,7 @@ pub mod error; pub mod fast_reject; pub mod fold; +pub mod item_path; pub mod _match; pub mod maps; pub mod outlives; @@ -2218,8 +2219,12 @@ pub fn trait_ref_to_def_id(&self, tr: &hir::TraitRef) -> DefId { self.def_map.borrow().get(&tr.ref_id).expect("no def-map entry for trait").def_id() } - pub fn item_path_str(&self, id: DefId) -> String { - self.with_path(id, |path| ast_map::path_to_string(path)) + pub fn def_key(&self, id: DefId) -> ast_map::DefKey { + if id.is_local() { + self.map.def_key(id) + } else { + self.sess.cstore.def_key(id) + } } /// Returns the `DefPath` of an item. Note that if `id` is not diff --git a/src/librustc_trans/back/symbol_names.rs b/src/librustc_trans/back/symbol_names.rs index a67b6841f02..81b00c3827f 100644 --- a/src/librustc_trans/back/symbol_names.rs +++ b/src/librustc_trans/back/symbol_names.rs @@ -103,10 +103,10 @@ use rustc::middle::cstore; use rustc::middle::def_id::DefId; use rustc::middle::ty::{self, TypeFoldable}; +use rustc::middle::ty::item_path::{ItemPathBuffer, RootMode}; use rustc::front::map::definitions::DefPath; use std::fmt::Write; -use syntax::ast; use syntax::parse::token::{self, InternedString}; use serialize::hex::ToHex; @@ -135,29 +135,23 @@ pub fn def_path_to_string<'tcx>(tcx: &ty::TyCtxt<'tcx>, def_path: &DefPath) -> S fn get_symbol_hash<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, def_path: &DefPath, - originating_crate: ast::CrateNum, parameters: &[ty::Ty<'tcx>]) -> String { + debug!("get_symbol_hash(def_path={:?}, parameters={:?})", + def_path, parameters); + let tcx = ccx.tcx(); let mut hash_state = ccx.symbol_hasher().borrow_mut(); hash_state.reset(); - if originating_crate == cstore::LOCAL_CRATE { - hash_state.input_str(&tcx.sess.crate_disambiguator.borrow()[..]); - } else { - hash_state.input_str(&tcx.sess.cstore.crate_disambiguator(originating_crate)); - } - - for component in def_path { - let disambiguator_bytes = [(component.disambiguator >> 0) as u8, - (component.disambiguator >> 8) as u8, - (component.disambiguator >> 16) as u8, - (component.disambiguator >> 24) as u8]; - hash_state.input(&disambiguator_bytes); - } + // the main symbol name is not necessarily unique; hash in the + // compiler's internal def-path, guaranteeing each symbol has a + // truly unique path + hash_state.input_str(&def_path_to_string(tcx, def_path)); + // also include any type parameters (for generic items) for t in parameters { assert!(!t.has_erasable_regions()); assert!(!t.needs_subst()); @@ -180,6 +174,9 @@ fn exported_name_with_opt_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, -> String { let &Instance { def: mut def_id, params: parameters } = instance; + debug!("exported_name_with_opt_suffix(def_id={:?}, parameters={:?}, suffix={:?})", + def_id, parameters, suffix); + if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) { if let Some(&src_def_id) = ccx.external_srcs().borrow().get(&node_id) { def_id = src_def_id; @@ -187,21 +184,34 @@ fn exported_name_with_opt_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } let def_path = ccx.tcx().def_path(def_id); - let hash = get_symbol_hash(ccx, &def_path, def_id.krate, parameters.as_slice()); + assert_eq!(def_path.krate, def_id.krate); + let hash = get_symbol_hash(ccx, &def_path, parameters.as_slice()); - let mut path = Vec::with_capacity(16); + let mut buffer = SymbolPathBuffer { + names: Vec::with_capacity(def_path.data.len()) + }; + ccx.tcx().push_item_path(&mut buffer, def_id); - if def_id.is_local() { - path.push(ccx.tcx().crate_name.clone()); + if let Some(suffix) = suffix { + buffer.push(suffix); } - path.extend(def_path.into_iter().map(|e| e.data.as_interned_str())); + mangle(buffer.names.into_iter(), Some(&hash[..])) +} - if let Some(suffix) = suffix { - path.push(token::intern_and_get_ident(suffix)); +struct SymbolPathBuffer { + names: Vec, +} + +impl ItemPathBuffer for SymbolPathBuffer { + fn root_mode(&self) -> &RootMode { + const ABSOLUTE: &'static RootMode = &RootMode::Absolute; + ABSOLUTE } - mangle(path.into_iter(), Some(&hash[..])) + fn push(&mut self, text: &str) { + self.names.push(token::intern(text).as_str()); + } } pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, @@ -225,7 +235,11 @@ pub fn internal_name_from_type_and_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx> -> String { let path = [token::intern(&t.to_string()).as_str(), gensym_name(suffix).as_str()]; - let hash = get_symbol_hash(ccx, &Vec::new(), cstore::LOCAL_CRATE, &[t]); + let def_path = DefPath { + data: vec![], + krate: cstore::LOCAL_CRATE, + }; + let hash = get_symbol_hash(ccx, &def_path, &[t]); mangle(path.iter().cloned(), Some(&hash[..])) }