Remove __extensions__ in names for a "pretty name"

As with the previous commit, this is targeted at removing the possibility of
collisions between statics. The main use case here is when there's a
type-parametric function with an inner static that's compiled as a library.
Before this commit, any impl would generate a path item of "__extensions__".
This changes this identifier to be a "pretty name", which is either the last
element of the path of the trait implemented or the last element of the type's
path that's being implemented.  That doesn't quite cut it though, so the (trait,
type) pair is hashed and again used to append information to the symbol.

Essentially, __extensions__ was removed for something nicer for debugging, and
then some more information was added to symbol name by including a hash of the
trait being implemented and type it's being implemented for. This should prevent
colliding names for inner statics in regular functions with similar names.
This commit is contained in:
Alex Crichton 2013-08-30 00:47:10 -07:00
parent 4600212a38
commit 36a4af49e0
10 changed files with 135 additions and 22 deletions

View File

@ -35,7 +35,7 @@ use std::run;
use std::str;
use std::vec;
use syntax::ast;
use syntax::ast_map::{path, path_mod, path_name};
use syntax::ast_map::{path, path_mod, path_name, path_pretty_name};
use syntax::attr;
use syntax::attr::{AttrMetaMethods};
use syntax::print::pprust;
@ -741,19 +741,40 @@ pub fn sanitize(s: &str) -> ~str {
}
pub fn mangle(sess: Session, ss: path) -> ~str {
// Follow C++ namespace-mangling style
// Follow C++ namespace-mangling style, see
// http://en.wikipedia.org/wiki/Name_mangling for more info.
let mut n = ~"_ZN"; // Begin name-sequence.
let mut n = ~"_ZN"; // _Z == Begin name-sequence, N == nested
// First, connect each component with <len, name> pairs.
for s in ss.iter() {
match *s {
path_name(s) | path_mod(s) => {
path_name(s) | path_mod(s) | path_pretty_name(s, _) => {
let sani = sanitize(sess.str_of(s));
n.push_str(fmt!("%u%s", sani.len(), sani));
}
}
}
n.push_char('E'); // End name-sequence.
// next, if any identifiers are "pretty" and need extra information tacked
// on, then use the hash to generate two unique characters. For now
// hopefully 2 characters is enough to avoid collisions.
static EXTRA_CHARS: &'static str =
"abcdefghijklmnopqrstuvwxyz\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\
0123456789";
for s in ss.iter() {
match *s {
path_pretty_name(_, extra) => {
let hi = (extra >> 32) as u32 as uint;
let lo = extra as u32 as uint;
n.push_char(EXTRA_CHARS[hi % EXTRA_CHARS.len()] as char);
n.push_char(EXTRA_CHARS[lo % EXTRA_CHARS.len()] as char);
}
_ => {}
}
}
n
}

View File

@ -188,6 +188,10 @@ pub static tag_impls_impl: uint = 0x84;
pub static tag_items_data_item_inherent_impl: uint = 0x85;
pub static tag_items_data_item_extension_impl: uint = 0x86;
pub static tag_path_elt_pretty_name: uint = 0x87;
pub static tag_path_elt_pretty_name_ident: uint = 0x88;
pub static tag_path_elt_pretty_name_extra: uint = 0x89;
pub struct LinkMeta {
name: @str,
vers: @str,

View File

@ -303,6 +303,15 @@ fn item_path(item_doc: ebml::Doc) -> ast_map::path {
} else if tag == tag_path_elt_name {
let str = elt_doc.as_str_slice();
result.push(ast_map::path_name(token::str_to_ident(str)));
} else if tag == tag_path_elt_pretty_name {
let name_doc = reader::get_doc(elt_doc,
tag_path_elt_pretty_name_ident);
let extra_doc = reader::get_doc(elt_doc,
tag_path_elt_pretty_name_extra);
let str = name_doc.as_str_slice();
let extra = reader::doc_as_u64(extra_doc);
result.push(ast_map::path_pretty_name(token::str_to_ident(str),
extra));
} else {
// ignore tag_path_len element
}

View File

@ -358,12 +358,21 @@ fn encode_path(ecx: &EncodeContext,
fn encode_path_elt(ecx: &EncodeContext,
ebml_w: &mut writer::Encoder,
elt: ast_map::path_elt) {
let (tag, name) = match elt {
ast_map::path_mod(name) => (tag_path_elt_mod, name),
ast_map::path_name(name) => (tag_path_elt_name, name)
};
ebml_w.wr_tagged_str(tag, ecx.tcx.sess.str_of(name));
match elt {
ast_map::path_mod(n) => {
ebml_w.wr_tagged_str(tag_path_elt_mod, ecx.tcx.sess.str_of(n));
}
ast_map::path_name(n) => {
ebml_w.wr_tagged_str(tag_path_elt_name, ecx.tcx.sess.str_of(n));
}
ast_map::path_pretty_name(n, extra) => {
ebml_w.start_tag(tag_path_elt_pretty_name);
ebml_w.wr_tagged_str(tag_path_elt_pretty_name_ident,
ecx.tcx.sess.str_of(n));
ebml_w.wr_tagged_u64(tag_path_elt_pretty_name_extra, extra);
ebml_w.end_tag();
}
}
}
ebml_w.start_tag(tag_path);

View File

@ -947,7 +947,8 @@ pub fn path_str(sess: session::Session, p: &[path_elt]) -> ~str {
let mut first = true;
for e in p.iter() {
match *e {
ast_map::path_name(s) | ast_map::path_mod(s) => {
ast_map::path_name(s) | ast_map::path_mod(s) |
ast_map::path_pretty_name(s, _) => {
if first {
first = false
} else {

View File

@ -34,7 +34,7 @@ use middle::trans::type_::Type;
use std::c_str::ToCStr;
use std::vec;
use syntax::ast_map::{path, path_mod, path_name};
use syntax::ast_map::{path, path_mod, path_name, path_pretty_name};
use syntax::ast_util;
use syntax::{ast, ast_map};
use syntax::visit;
@ -254,7 +254,7 @@ pub fn trans_static_method_callee(bcx: @mut Block,
} else {
let path = csearch::get_item_path(bcx.tcx(), method_id);
match path[path.len()-1] {
path_name(s) => { s }
path_pretty_name(s, _) | path_name(s) => { s }
path_mod(_) => { fail!("path doesn't have a name?") }
}
};

View File

@ -799,7 +799,8 @@ impl Repr for ast_map::path_elt {
fn repr(&self, tcx: ctxt) -> ~str {
match *self {
ast_map::path_mod(id) => id.repr(tcx),
ast_map::path_name(id) => id.repr(tcx)
ast_map::path_name(id) => id.repr(tcx),
ast_map::path_pretty_name(id, _) => id.repr(tcx),
}
}
}

View File

@ -22,6 +22,7 @@ use print::pprust;
use visit::{Visitor, fn_kind};
use visit;
use std::hash;
use std::hashmap::HashMap;
use std::vec;
@ -29,6 +30,12 @@ use std::vec;
pub enum path_elt {
path_mod(Ident),
path_name(Ident)
// A pretty name can come from an `impl` block. We attempt to select a
// reasonable name for debuggers to see, but to guarantee uniqueness with
// other paths the hash should also be taken into account during symbol
// generation.
path_pretty_name(Ident, u64),
}
pub type path = ~[path_elt];
@ -37,8 +44,9 @@ pub fn path_to_str_with_sep(p: &[path_elt], sep: &str, itr: @ident_interner)
-> ~str {
let strs = do p.map |e| {
match *e {
path_mod(s) => itr.get(s.name),
path_name(s) => itr.get(s.name)
path_mod(s) | path_name(s) | path_pretty_name(s, _) => {
itr.get(s.name)
}
}
};
strs.connect(sep)
@ -58,8 +66,9 @@ pub fn path_to_str(p: &[path_elt], itr: @ident_interner) -> ~str {
pub fn path_elt_to_str(pe: path_elt, itr: @ident_interner) -> ~str {
match pe {
path_mod(s) => itr.get(s.name).to_owned(),
path_name(s) => itr.get(s.name).to_owned()
path_mod(s) | path_name(s) | path_pretty_name(s, _) => {
itr.get(s.name).to_owned()
}
}
}
@ -195,12 +204,33 @@ impl Visitor<()> for Ctx {
let item_path = @self.path.clone();
self.map.insert(i.id, node_item(i, item_path));
match i.node {
item_impl(_, _, _, ref ms) => {
item_impl(_, ref maybe_trait, ref ty, ref ms) => {
let impl_did = ast_util::local_def(i.id);
for m in ms.iter() {
let extended = { self.extend(i.ident) };
self.map_method(impl_did, extended, *m, false)
}
// Right now the ident on impls is __extensions__ which isn't
// very pretty when debugging, so attempt to select a better
// name to use.
let name = match *maybe_trait {
Some(ref trait_ref) => {
trait_ref.path.segments.last().identifier
}
None => {
match ty.node {
ty_path(ref p, _, _) => {
p.segments.last().identifier
}
// oh well, just give up for now
_ => { i.ident }
}
}
};
let hash = hash::hash_keyed_2(maybe_trait, ty, 0, 0);
self.path.push(path_pretty_name(name, hash));
}
item_enum(ref enum_definition, _) => {
for v in (*enum_definition).variants.iter() {
@ -267,6 +297,7 @@ impl Visitor<()> for Ctx {
item_mod(_) | item_foreign_mod(_) => {
self.path.push(path_mod(i.ident));
}
item_impl(*) => {} // this was guessed above.
_ => self.path.push(path_name(i.ident))
}
visit::walk_item(self, i, ());

View File

@ -9,20 +9,53 @@
// except according to those terms.
pub struct A<T>;
pub struct B<T>;
pub mod test {
pub struct A<T>;
}
impl<T> A<T> {
pub fn foo(&self) -> int {
static a: int = 1;
return a
}
pub fn bar(&self) -> int {
static a: int = 2;
return a;
}
}
impl<T> B<T> {
pub fn foo(&self) -> int {
static a: int = 3;
return a
}
pub fn bar(&self) -> int {
static a: int = 4;
return a;
}
}
impl<T> test::A<T> {
pub fn foo(&self) -> int {
static a: int = 5;
return a
}
pub fn bar(&self) -> int {
static a: int = 3;
static a: int = 6;
return a;
}
}
pub fn foo() -> int {
let a = A::<()>;
return a.foo() + a.bar();
let b = B::<()>;
let c = test::A::<()>;
return a.foo() + a.bar() +
b.foo() + b.bar() +
c.foo() + c.bar();
}

View File

@ -15,5 +15,9 @@ extern mod inner_static;
pub fn main() {
let a = inner_static::A::<()>;
assert_eq!(a.bar(), 3);
let b = inner_static::B::<()>;
let c = inner_static::test::A::<()>;
assert_eq!(a.bar(), 2);
assert_eq!(b.bar(), 4);
assert_eq!(c.bar(), 6);
}