diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index e2ef1f2282d..91e2331ec8c 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -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; @@ -667,8 +667,7 @@ pub fn truncated_hash_result(symbol_hasher: &mut hash::State) -> ~str { pub fn symbol_hash(tcx: ty::ctxt, symbol_hasher: &mut hash::State, t: ty::t, - link_meta: LinkMeta) - -> @str { + link_meta: LinkMeta) -> @str { // NB: do *not* use abbrevs here as we want the symbol names // to be independent of one another in the crate. @@ -723,7 +722,7 @@ pub fn sanitize(s: &str) -> ~str { 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' - | '_' => result.push_char(c), + | '_' | '.' => result.push_char(c), _ => { let mut tstr = ~""; @@ -744,19 +743,65 @@ pub fn sanitize(s: &str) -> ~str { return result; } -pub fn mangle(sess: Session, ss: path) -> ~str { - // Follow C++ namespace-mangling style +pub fn mangle(sess: Session, ss: path, + hash: Option<&str>, vers: Option<&str>) -> ~str { + // Follow C++ namespace-mangling style, see + // http://en.wikipedia.org/wiki/Name_mangling for more info. + // + // It turns out that on OSX you can actually have arbitrary symbols in + // function names (at least when given to LLVM), but this is not possible + // when using unix's linker. Perhaps one day when we just a linker from LLVM + // we won't need to do this name mangling. The problem with name mangling is + // that it seriously limits the available characters. For example we can't + // have things like @T or ~[T] in symbol names when one would theoretically + // want them for things like impls of traits on that type. + // + // To be able to work on all platforms and get *some* reasonable output, we + // use C++ name-mangling. - let mut n = ~"_ZN"; // Begin name-sequence. + let mut n = ~"_ZN"; // _Z == Begin name-sequence, N == nested + let push = |s: &str| { + let sani = sanitize(s); + n.push_str(fmt!("%u%s", sani.len(), sani)); + }; + + // First, connect each component with pairs. for s in ss.iter() { match *s { - path_name(s) | path_mod(s) => { - let sani = sanitize(sess.str_of(s)); - n.push_str(fmt!("%u%s", sani.len(), sani)); + path_name(s) | path_mod(s) | path_pretty_name(s, _) => { + push(sess.str_of(s)) } } } + + // 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"; + let mut hash = match hash { Some(s) => s.to_owned(), None => ~"" }; + 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; + hash.push_char(EXTRA_CHARS[hi % EXTRA_CHARS.len()] as char); + hash.push_char(EXTRA_CHARS[lo % EXTRA_CHARS.len()] as char); + } + _ => {} + } + } + if hash.len() > 0 { + push(hash); + } + match vers { + Some(s) => push(s), + None => {} + } + n.push_char('E'); // End name-sequence. n } @@ -765,10 +810,15 @@ pub fn exported_name(sess: Session, path: path, hash: &str, vers: &str) -> ~str { - mangle(sess, - vec::append_one( - vec::append_one(path, path_name(sess.ident_of(hash))), - path_name(sess.ident_of(vers)))) + // The version will get mangled to have a leading '_', but it makes more + // sense to lead with a 'v' b/c this is a version... + let vers = if vers.len() > 0 && !char::is_XID_start(vers.char_at(0)) { + "v" + vers + } else { + vers.to_owned() + }; + + mangle(sess, path, Some(hash), Some(vers.as_slice())) } pub fn mangle_exported_name(ccx: &mut CrateContext, @@ -786,31 +836,33 @@ pub fn mangle_internal_name_by_type_only(ccx: &mut CrateContext, let s = ppaux::ty_to_short_str(ccx.tcx, t); let hash = get_symbol_hash(ccx, t); return mangle(ccx.sess, - ~[path_name(ccx.sess.ident_of(name)), - path_name(ccx.sess.ident_of(s)), - path_name(ccx.sess.ident_of(hash))]); + ~[path_name(ccx.sess.ident_of(name)), + path_name(ccx.sess.ident_of(s))], + Some(hash.as_slice()), + None); } pub fn mangle_internal_name_by_type_and_seq(ccx: &mut CrateContext, - t: ty::t, - name: &str) -> ~str { + t: ty::t, + name: &str) -> ~str { let s = ppaux::ty_to_str(ccx.tcx, t); let hash = get_symbol_hash(ccx, t); return mangle(ccx.sess, - ~[path_name(ccx.sess.ident_of(s)), - path_name(ccx.sess.ident_of(hash)), - path_name(gensym_name(name))]); + ~[path_name(ccx.sess.ident_of(s)), + path_name(gensym_name(name))], + Some(hash.as_slice()), + None); } pub fn mangle_internal_name_by_path_and_seq(ccx: &mut CrateContext, mut path: path, flav: &str) -> ~str { path.push(path_name(gensym_name(flav))); - mangle(ccx.sess, path) + mangle(ccx.sess, path, None, None) } pub fn mangle_internal_name_by_path(ccx: &mut CrateContext, path: path) -> ~str { - mangle(ccx.sess, path) + mangle(ccx.sess, path, None, None) } pub fn mangle_internal_name_by_seq(_ccx: &mut CrateContext, flav: &str) -> ~str { diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 8af53586594..c3bc3e0fe25 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -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, diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index c15327f4e21..7d173e333fc 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -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 } diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 786d1bf3242..fb52963f4fc 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -359,12 +359,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); diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 9b5ba00a34a..5f5db9b4487 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -77,7 +77,7 @@ use std::local_data; use extra::time; use extra::sort; use syntax::ast::Ident; -use syntax::ast_map::{path, path_elt_to_str, path_name}; +use syntax::ast_map::{path, path_elt_to_str, path_name, path_pretty_name}; use syntax::ast_util::{local_def}; use syntax::attr; use syntax::attr::AttrMetaMethods; @@ -2627,8 +2627,7 @@ pub fn register_method(ccx: @mut CrateContext, let mty = ty::node_id_to_type(ccx.tcx, id); let mut path = (*path).clone(); - path.push(path_name(gensym_name("meth"))); - path.push(path_name(m.ident)); + path.push(path_pretty_name(m.ident, token::gensym("meth") as u64)); let sym = exported_name(ccx, path, mty, m.attrs); diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index d64f221cb9e..6e4928857f8 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -948,7 +948,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 { diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index 76cb943c6ed..76b23d9d536 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -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?") } } }; diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 4ce29f39131..8c1394618e3 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -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), } } } diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs index be67998ac5d..dd991b065e9 100644 --- a/src/libsyntax/ast_map.rs +++ b/src/libsyntax/ast_map.rs @@ -16,6 +16,7 @@ use ast_util; use codemap::Span; use codemap; use diagnostic::span_handler; +use parse::token::get_ident_interner; use parse::token::ident_interner; use parse::token::special_idents; use print::pprust; @@ -28,7 +29,13 @@ use std::vec; #[deriving(Clone, Eq)] pub enum path_elt { path_mod(Ident), - path_name(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() + } } } @@ -109,8 +118,8 @@ pub struct Ctx { } impl Ctx { - fn extend(&self, elt: Ident) -> @path { - @vec::append(self.path.clone(), [path_name(elt)]) + fn extend(&self, elt: path_elt) -> @path { + @vec::append(self.path.clone(), [elt]) } fn map_method(&mut self, @@ -131,7 +140,7 @@ impl Ctx { struct_def: @ast::struct_def, parent_node: ast_node, ident: ast::Ident) { - let p = self.extend(ident); + let p = self.extend(path_name(ident)); // If this is a tuple-like struct, register the constructor. match struct_def.ctor_id { @@ -173,7 +182,15 @@ impl Ctx { for a in decl.inputs.iter() { self.map.insert(a.id, node_arg(a.pat)); } + match *fk { + visit::fk_method(name, _, _) => { self.path.push(path_name(name)) } + _ => {} + } visit::walk_fn(self, fk, decl, body, sp, id, ()); + match *fk { + visit::fk_method(*) => { self.path.pop(); } + _ => {} + } } fn map_stmt(&mut self, stmt: @Stmt) { @@ -199,6 +216,28 @@ impl Ctx { visit::walk_pat(self, pat, ()); } + + fn impl_pretty_name(&self, trait_ref: &Option, + ty: &Ty, default: Ident) -> path_elt { + let itr = get_ident_interner(); + let ty_ident = match ty.node { + ty_path(ref path, _, _) => path.segments.last().identifier, + _ => default + }; + let hash = (trait_ref, ty).hash(); + match *trait_ref { + None => path_pretty_name(ty_ident, hash), + Some(ref trait_ref) => { + // XXX: this dollar sign is actually a relic of being one of the + // very few valid symbol names on unix. These kinds of + // details shouldn't be exposed way up here in the ast. + let s = fmt!("%s$%s", + itr.get(trait_ref.path.segments.last().identifier.name), + itr.get(ty_ident.name)); + path_pretty_name(Ident::new(itr.gensym(s)), hash) + } + } + } } impl Visitor<()> for Ctx { @@ -207,20 +246,28 @@ 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) => { + // 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 elt = self.impl_pretty_name(maybe_trait, ty, i.ident); + let impl_did = ast_util::local_def(i.id); for m in ms.iter() { - let extended = { self.extend(i.ident) }; + let extended = { self.extend(elt) }; self.map_method(impl_did, extended, *m, false) } + + self.path.push(elt); } item_enum(ref enum_definition, _) => { for v in (*enum_definition).variants.iter() { + let elt = path_name(i.ident); // FIXME #2543: bad clone self.map.insert(v.node.id, node_variant((*v).clone(), i, - self.extend(i.ident))); + self.extend(elt))); } } item_foreign_mod(ref nm) => { @@ -239,7 +286,9 @@ impl Visitor<()> for Ctx { // FIXME (#2543) if nm.sort == ast::named { - self.extend(i.ident) + let e = path_name( + i.ident); + self.extend(e) } else { // Anonymous extern // mods go in the @@ -258,7 +307,7 @@ impl Visitor<()> for Ctx { self.map.insert(p.ref_id, node_item(i, item_path)); } for tm in methods.iter() { - let ext = { self.extend(i.ident) }; + let ext = { self.extend(path_name(i.ident)) }; let d_id = ast_util::local_def(i.id); match *tm { required(ref m) => { @@ -279,6 +328,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, ()); diff --git a/src/test/auxiliary/inner_static.rs b/src/test/auxiliary/inner_static.rs new file mode 100644 index 00000000000..841da21c5e8 --- /dev/null +++ b/src/test/auxiliary/inner_static.rs @@ -0,0 +1,61 @@ +// Copyright 2013 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. + +pub struct A; +pub struct B; + +pub mod test { + pub struct A; +} + +impl A { + pub fn foo(&self) -> int { + static a: int = 1; + return a + } + + pub fn bar(&self) -> int { + static a: int = 2; + return a; + } +} + +impl B { + pub fn foo(&self) -> int { + static a: int = 3; + return a + } + + pub fn bar(&self) -> int { + static a: int = 4; + return a; + } +} + +impl test::A { + pub fn foo(&self) -> int { + static a: int = 5; + return a + } + + pub fn bar(&self) -> int { + static a: int = 6; + return a; + } +} + +pub fn foo() -> int { + let a = A::<()>; + let b = B::<()>; + let c = test::A::<()>; + return a.foo() + a.bar() + + b.foo() + b.bar() + + c.foo() + c.bar(); +} diff --git a/src/test/compile-fail/ambig_impl_2_exe.rs b/src/test/compile-fail/ambig_impl_2_exe.rs index 1cf08b7f503..0f5bac795e7 100644 --- a/src/test/compile-fail/ambig_impl_2_exe.rs +++ b/src/test/compile-fail/ambig_impl_2_exe.rs @@ -15,6 +15,6 @@ use ambig_impl_2_lib::me; trait me { fn me(&self) -> uint; } -impl me for uint { fn me(&self) -> uint { *self } } //~ NOTE is `__extensions__::me` +impl me for uint { fn me(&self) -> uint { *self } } //~ NOTE is `me$uint::me` fn main() { 1u.me(); } //~ ERROR multiple applicable methods in scope //~^ NOTE is `ambig_impl_2_lib::__extensions__::me` diff --git a/src/test/compile-fail/ambig_impl_unify.rs b/src/test/compile-fail/ambig_impl_unify.rs index ce8c2a29544..df88ff1f0d0 100644 --- a/src/test/compile-fail/ambig_impl_unify.rs +++ b/src/test/compile-fail/ambig_impl_unify.rs @@ -13,11 +13,11 @@ trait foo { } impl foo for ~[uint] { - fn foo(&self) -> int {1} //~ NOTE candidate #1 is `__extensions__::foo` + fn foo(&self) -> int {1} //~ NOTE candidate #1 is `foo$__extensions__::foo` } impl foo for ~[int] { - fn foo(&self) -> int {2} //~ NOTE candidate #2 is `__extensions__::foo` + fn foo(&self) -> int {2} //~ NOTE candidate #2 is `foo$__extensions__::foo` } fn main() { diff --git a/src/test/run-pass/inner-static.rs b/src/test/run-pass/inner-static.rs new file mode 100644 index 00000000000..3bd806aeef3 --- /dev/null +++ b/src/test/run-pass/inner-static.rs @@ -0,0 +1,23 @@ +// Copyright 2013 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. + +// aux-build:inner_static.rs +// xfail-fast + +extern mod inner_static; + +pub fn main() { + let a = inner_static::A::<()>; + 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); +}