auto merge of #8875 : alexcrichton/rust/fix-inner-static-library-bug, r=huonw

These commits fix bugs related to identically named statics in functions of implementations in various situations. The commit messages have most of the information about what bugs are being fixed and why.

As a bonus, while I was messing around with name mangling, I improved the backtraces we'll get in gdb by removing `__extensions__` for the trait/type being implemented and by adding the method name as well. Yay!
This commit is contained in:
bors 2013-09-04 23:55:46 -07:00
commit 3c3ae1d0e2
13 changed files with 262 additions and 53 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;
@ -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 <len, name> 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 {

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

@ -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);

View File

@ -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);

View File

@ -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 {

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

@ -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<trait_ref>,
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, ());

View File

@ -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 <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.
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 = 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();
}

View File

@ -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`

View File

@ -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() {

View File

@ -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 <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.
// 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);
}