--- /dev/null
+// 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 <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.
+
+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<T>(&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<T>(&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<T>(&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!("<impl {} for {}>", trait_ref, self_ty));
+ } else {
+ buffer.push(&format!("<impl {}>", 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 `<Foo>::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<T>(&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!("<impl at {}>", 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<DefId> {
+ 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<DefId> {
+ 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);
+ }
+}
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;
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());
-> 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;
}
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<InternedString>,
+}
+
+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>,
-> 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[..]))
}