From 3b013cd800ce675a445220105911bbefd2427e47 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 18 Sep 2012 11:26:07 -0700 Subject: rustc: Change all non-keyword uses of "link" --- src/libcore/dlist.rs | 48 +- src/libcore/iter-trait/dlist.rs | 8 +- src/libcore/libc.rs | 3 +- src/rustc/back/link.rs | 801 --------------------------------- src/rustc/back/linkage.rs | 801 +++++++++++++++++++++++++++++++++ src/rustc/driver/driver.rs | 45 +- src/rustc/driver/session.rs | 6 +- src/rustc/middle/trans/base.rs | 6 +- src/rustc/middle/trans/closure.rs | 2 +- src/rustc/middle/trans/common.rs | 2 +- src/rustc/middle/trans/controlflow.rs | 2 +- src/rustc/middle/trans/foreign.rs | 6 +- src/rustc/middle/trans/meth.rs | 2 +- src/rustc/middle/trans/monomorphize.rs | 2 +- src/rustc/rustc.rc | 2 +- src/rustdoc/astsrv.rs | 1 - src/rustdoc/doc.rs | 6 +- src/rustdoc/markdown_index_pass.rs | 16 +- src/rustdoc/markdown_pass.rs | 2 +- src/test/run-pass/mlist-cycle.rs | 14 +- 20 files changed, 889 insertions(+), 886 deletions(-) delete mode 100644 src/rustc/back/link.rs create mode 100644 src/rustc/back/linkage.rs (limited to 'src') diff --git a/src/libcore/dlist.rs b/src/libcore/dlist.rs index 58c7edd33cd..09f822e0e38 100644 --- a/src/libcore/dlist.rs +++ b/src/libcore/dlist.rs @@ -141,7 +141,7 @@ priv impl DList { // Link two nodes together. If either of them are 'none', also sets // the head and/or tail pointers appropriately. #[inline(always)] - fn link(+before: DListLink, +after: DListLink) { + fn link_nodes(+before: DListLink, +after: DListLink) { match before { Some(neighbour) => neighbour.next = after, None => self.hd = after @@ -155,7 +155,7 @@ priv impl DList { fn unlink(nobe: DListNode) { self.assert_mine(nobe); assert self.size > 0; - self.link(nobe.prev, nobe.next); + self.link_nodes(nobe.prev, nobe.next); nobe.prev = None; // Release extraneous references. nobe.next = None; nobe.linked = false; @@ -163,27 +163,27 @@ priv impl DList { } fn add_head(+nobe: DListLink) { - self.link(nobe, self.hd); // Might set tail too. + self.link_nodes(nobe, self.hd); // Might set tail too. self.hd = nobe; self.size += 1; } fn add_tail(+nobe: DListLink) { - self.link(self.tl, nobe); // Might set head too. + self.link_nodes(self.tl, nobe); // Might set head too. self.tl = nobe; self.size += 1; } fn insert_left(nobe: DListLink, neighbour: DListNode) { self.assert_mine(neighbour); assert self.size > 0; - self.link(neighbour.prev, nobe); - self.link(nobe, Some(neighbour)); + self.link_nodes(neighbour.prev, nobe); + self.link_nodes(nobe, Some(neighbour)); self.size += 1; } fn insert_right(neighbour: DListNode, nobe: DListLink) { self.assert_mine(neighbour); assert self.size > 0; - self.link(nobe, neighbour.next); - self.link(Some(neighbour), nobe); + self.link_nodes(nobe, neighbour.next); + self.link_nodes(Some(neighbour), nobe); self.size += 1; } } @@ -315,7 +315,7 @@ impl DList { fail ~"Cannot append a dlist to itself!" } if them.len() > 0 { - self.link(self.tl, them.hd); + self.link_nodes(self.tl, them.hd); self.tl = them.tl; self.size += them.size; them.size = 0; @@ -332,7 +332,7 @@ impl DList { fail ~"Cannot prepend a dlist to itself!" } if them.len() > 0 { - self.link(them.tl, self.hd); + self.link_nodes(them.tl, self.hd); self.hd = them.hd; self.size += them.size; them.size = 0; @@ -366,11 +366,11 @@ impl DList { /// Iterate over nodes. pure fn each_node(f: fn(DListNode) -> bool) { - let mut link = self.peek_n(); - while link.is_some() { - let nobe = link.get(); + let mut link_node = self.peek_n(); + while link_node.is_some() { + let nobe = link_node.get(); if !f(nobe) { break; } - link = nobe.next_link(); + link_node = nobe.next_link(); } } @@ -381,10 +381,10 @@ impl DList { } // iterate forwards let mut count = 0; - let mut link = self.peek_n(); - let mut rabbit = link; - while option::is_some(link) { - let nobe = option::get(link); + let mut link_node = self.peek_n(); + let mut rabbit = link_node; + while option::is_some(link_node) { + let nobe = option::get(link_node); assert nobe.linked; // check cycle if option::is_some(rabbit) { rabbit = option::get(rabbit).next; } @@ -393,15 +393,15 @@ impl DList { assert !box::ptr_eq(*option::get(rabbit), *nobe); } // advance - link = nobe.next_link(); + link_node = nobe.next_link(); count += 1; } assert count == self.len(); // iterate backwards - some of this is probably redundant. - link = self.peek_tail_n(); - rabbit = link; - while option::is_some(link) { - let nobe = option::get(link); + link_node = self.peek_tail_n(); + rabbit = link_node; + while option::is_some(link_node) { + let nobe = option::get(link_node); assert nobe.linked; // check cycle if option::is_some(rabbit) { rabbit = option::get(rabbit).prev; } @@ -410,7 +410,7 @@ impl DList { assert !box::ptr_eq(*option::get(rabbit), *nobe); } // advance - link = nobe.prev_link(); + link_node = nobe.prev_link(); count -= 1; } assert count == 0; diff --git a/src/libcore/iter-trait/dlist.rs b/src/libcore/iter-trait/dlist.rs index ae6265409ca..966cd138d4c 100644 --- a/src/libcore/iter-trait/dlist.rs +++ b/src/libcore/iter-trait/dlist.rs @@ -9,9 +9,9 @@ type IMPL_T = dlist::DList; * node is forbidden. */ pure fn EACH(self: IMPL_T, f: fn(A) -> bool) { - let mut link = self.peek_n(); - while option::is_some(link) { - let nobe = option::get(link); + let mut link_node = self.peek_n(); + while option::is_some(link_node) { + let nobe = option::get(link_node); assert nobe.linked; if !f(nobe.data) { break; } // Check (weakly) that the user didn't do a remove. @@ -25,7 +25,7 @@ pure fn EACH(self: IMPL_T, f: fn(A) -> bool) { || box::ptr_eq(*self.tl.expect(~"tailless dlist?"), *nobe)))) { fail ~"Removing a dlist node during iteration is forbidden!" } - link = nobe.next_link(); + link_node = nobe.next_link(); } } diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs index 491fe02ec31..f06a2c75889 100644 --- a/src/libcore/libc.rs +++ b/src/libcore/libc.rs @@ -1033,7 +1033,8 @@ mod funcs { fn getppid() -> pid_t; fn getuid() -> uid_t; fn isatty(fd: c_int) -> c_int; - fn link(src: *c_char, dst: *c_char) -> c_int; + #[link_name="link"] + fn lnk(src: *c_char, dst: *c_char) -> c_int; fn lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t; fn pathconf(path: *c_char, name: c_int) -> c_long; fn pause() -> c_int; diff --git a/src/rustc/back/link.rs b/src/rustc/back/link.rs deleted file mode 100644 index 0079ec9f363..00000000000 --- a/src/rustc/back/link.rs +++ /dev/null @@ -1,801 +0,0 @@ -use libc::{c_int, c_uint, c_char}; -use driver::session; -use session::session; -use lib::llvm::llvm; -use syntax::attr; -use middle::ty; -use metadata::{encoder, cstore}; -use middle::trans::common::crate_ctxt; -use metadata::common::link_meta; -use std::map::HashMap; -use std::sha1::sha1; -use syntax::ast; -use syntax::print::pprust; -use lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data, True, False, - PassManagerRef, FileType}; -use metadata::filesearch; -use syntax::ast_map::{path, path_mod, path_name}; -use io::{Writer, WriterUtil}; - -enum output_type { - output_type_none, - output_type_bitcode, - output_type_assembly, - output_type_llvm_assembly, - output_type_object, - output_type_exe, -} - -impl output_type : cmp::Eq { - pure fn eq(&&other: output_type) -> bool { - (self as uint) == (other as uint) - } - pure fn ne(&&other: output_type) -> bool { !self.eq(other) } -} - -fn llvm_err(sess: session, msg: ~str) -> ! unsafe { - let cstr = llvm::LLVMRustGetLastError(); - if cstr == ptr::null() { - sess.fatal(msg); - } else { sess.fatal(msg + ~": " + str::raw::from_c_str(cstr)); } -} - -fn WriteOutputFile(sess:session, - PM: lib::llvm::PassManagerRef, M: ModuleRef, - Triple: *c_char, - // FIXME: When #2334 is fixed, change - // c_uint to FileType - Output: *c_char, FileType: c_uint, - OptLevel: c_int, - EnableSegmentedStacks: bool) { - let result = llvm::LLVMRustWriteOutputFile( - PM, M, Triple, Output, FileType, OptLevel, EnableSegmentedStacks); - if (!result) { - llvm_err(sess, ~"Could not write output"); - } -} - -#[cfg(stage0)] -mod jit { - fn exec(_sess: session, - _pm: PassManagerRef, - _m: ModuleRef, - _opt: c_int, - _stacks: bool) { - fail - } -} - -#[cfg(stage1)] -#[cfg(stage2)] -#[cfg(stage3)] -mod jit { - #[nolink] - #[abi = "rust-intrinsic"] - extern mod rusti { - fn morestack_addr() -> *(); - } - - struct Closure { - code: *(), - env: *(), - } - - fn exec(sess: session, - pm: PassManagerRef, - m: ModuleRef, - opt: c_int, - stacks: bool) unsafe { - let ptr = llvm::LLVMRustJIT(rusti::morestack_addr(), - pm, m, opt, stacks); - - if ptr::is_null(ptr) { - llvm_err(sess, ~"Could not JIT"); - } else { - let closure = Closure { - code: ptr, - env: ptr::null() - }; - let func: fn(~[~str]) = unsafe::transmute(move closure); - - func(~[sess.opts.binary]); - } - } -} - -mod write { - fn is_object_or_assembly_or_exe(ot: output_type) -> bool { - if ot == output_type_assembly || ot == output_type_object || - ot == output_type_exe { - return true; - } - return false; - } - - fn run_passes(sess: session, llmod: ModuleRef, output: &Path) { - let opts = sess.opts; - if sess.time_llvm_passes() { llvm::LLVMRustEnableTimePasses(); } - let mut pm = mk_pass_manager(); - let td = mk_target_data( - sess.targ_cfg.target_strs.data_layout); - llvm::LLVMAddTargetData(td.lltd, pm.llpm); - // FIXME (#2812): run the linter here also, once there are llvm-c - // bindings for it. - - // Generate a pre-optimization intermediate file if -save-temps was - // specified. - - - if opts.save_temps { - match opts.output_type { - output_type_bitcode => { - if opts.optimize != session::No { - let filename = output.with_filetype("no-opt.bc"); - str::as_c_str(filename.to_str(), |buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf) - }); - } - } - _ => { - let filename = output.with_filetype("bc"); - str::as_c_str(filename.to_str(), |buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf) - }); - } - } - } - if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } - // FIXME (#2396): This is mostly a copy of the bits of opt's -O2 that - // are available in the C api. - // Also: We might want to add optimization levels like -O1, -O2, - // -Os, etc - // Also: Should we expose and use the pass lists used by the opt - // tool? - - if opts.optimize != session::No { - let fpm = mk_pass_manager(); - llvm::LLVMAddTargetData(td.lltd, fpm.llpm); - - let FPMB = llvm::LLVMPassManagerBuilderCreate(); - llvm::LLVMPassManagerBuilderSetOptLevel(FPMB, 2u as c_uint); - llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(FPMB, - fpm.llpm); - llvm::LLVMPassManagerBuilderDispose(FPMB); - - llvm::LLVMRunPassManager(fpm.llpm, llmod); - let mut threshold = 225; - if opts.optimize == session::Aggressive { threshold = 275; } - - let MPMB = llvm::LLVMPassManagerBuilderCreate(); - llvm::LLVMPassManagerBuilderSetOptLevel(MPMB, - opts.optimize as c_uint); - llvm::LLVMPassManagerBuilderSetSizeLevel(MPMB, False); - llvm::LLVMPassManagerBuilderSetDisableUnitAtATime(MPMB, False); - llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(MPMB, False); - llvm::LLVMPassManagerBuilderSetDisableSimplifyLibCalls(MPMB, - False); - - if threshold != 0u { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold - (MPMB, threshold as c_uint); - } - llvm::LLVMPassManagerBuilderPopulateModulePassManager(MPMB, - pm.llpm); - - llvm::LLVMPassManagerBuilderDispose(MPMB); - } - if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } - if is_object_or_assembly_or_exe(opts.output_type) || opts.jit { - let LLVMOptNone = 0 as c_int; // -O0 - let LLVMOptLess = 1 as c_int; // -O1 - let LLVMOptDefault = 2 as c_int; // -O2, -Os - let LLVMOptAggressive = 3 as c_int; // -O3 - - let mut CodeGenOptLevel = match opts.optimize { - session::No => LLVMOptNone, - session::Less => LLVMOptLess, - session::Default => LLVMOptDefault, - session::Aggressive => LLVMOptAggressive - }; - - if opts.jit { - // If we are using JIT, go ahead and create and - // execute the engine now. - // JIT execution takes ownership of the module, - // so don't dispose and return. - - // We need to tell LLVM where to resolve all linked - // symbols from. The equivalent of -lstd, -lcore, etc. - // By default the JIT will resolve symbols from the std and - // core linked into rustc. We don't want that, - // incase the user wants to use an older std library. - /*let cstore = sess.cstore; - for cstore::get_used_crate_files(cstore).each |cratepath| { - debug!{"linking: %s", cratepath}; - - let _: () = str::as_c_str( - cratepath, - |buf_t| { - if !llvm::LLVMRustLoadLibrary(buf_t) { - llvm_err(sess, ~"Could not link"); - } - debug!{"linked: %s", cratepath}; - }); - }*/ - - jit::exec(sess, pm.llpm, llmod, CodeGenOptLevel, true); - - if sess.time_llvm_passes() { - llvm::LLVMRustPrintPassTimings(); - } - return; - } - - let mut FileType; - if opts.output_type == output_type_object || - opts.output_type == output_type_exe { - FileType = lib::llvm::ObjectFile; - } else { FileType = lib::llvm::AssemblyFile; } - // Write optimized bitcode if --save-temps was on. - - if opts.save_temps { - // Always output the bitcode file with --save-temps - - let filename = output.with_filetype("opt.bc"); - llvm::LLVMRunPassManager(pm.llpm, llmod); - str::as_c_str(filename.to_str(), |buf| { - llvm::LLVMWriteBitcodeToFile(llmod, buf) - }); - pm = mk_pass_manager(); - // Save the assembly file if -S is used - - if opts.output_type == output_type_assembly { - let _: () = str::as_c_str( - sess.targ_cfg.target_strs.target_triple, - |buf_t| { - str::as_c_str(output.to_str(), |buf_o| { - WriteOutputFile( - sess, - pm.llpm, - llmod, - buf_t, - buf_o, - lib::llvm::AssemblyFile as c_uint, - CodeGenOptLevel, - true) - }) - }); - } - - - // Save the object file for -c or --save-temps alone - // This .o is needed when an exe is built - if opts.output_type == output_type_object || - opts.output_type == output_type_exe { - let _: () = str::as_c_str( - sess.targ_cfg.target_strs.target_triple, - |buf_t| { - str::as_c_str(output.to_str(), |buf_o| { - WriteOutputFile( - sess, - pm.llpm, - llmod, - buf_t, - buf_o, - lib::llvm::ObjectFile as c_uint, - CodeGenOptLevel, - true) - }) - }); - } - } else { - // If we aren't saving temps then just output the file - // type corresponding to the '-c' or '-S' flag used - - let _: () = str::as_c_str( - sess.targ_cfg.target_strs.target_triple, - |buf_t| { - str::as_c_str(output.to_str(), |buf_o| { - WriteOutputFile( - sess, - pm.llpm, - llmod, - buf_t, - buf_o, - FileType as c_uint, - CodeGenOptLevel, - true) - }) - }); - } - // Clean up and return - - llvm::LLVMDisposeModule(llmod); - if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } - return; - } - - if opts.output_type == output_type_llvm_assembly { - // Given options "-S --emit-llvm": output LLVM assembly - str::as_c_str(output.to_str(), |buf_o| { - llvm::LLVMRustAddPrintModulePass(pm.llpm, llmod, buf_o)}); - } else { - // If only a bitcode file is asked for by using the '--emit-llvm' - // flag, then output it here - llvm::LLVMRunPassManager(pm.llpm, llmod); - str::as_c_str(output.to_str(), - |buf| llvm::LLVMWriteBitcodeToFile(llmod, buf) ); - } - - llvm::LLVMDisposeModule(llmod); - if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } - } -} - - -/* - * Name mangling and its relationship to metadata. This is complex. Read - * carefully. - * - * The semantic model of Rust linkage is, broadly, that "there's no global - * namespace" between crates. Our aim is to preserve the illusion of this - * model despite the fact that it's not *quite* possible to implement on - * modern linkers. We initially didn't use system linkers at all, but have - * been convinced of their utility. - * - * There are a few issues to handle: - * - * - Linkers operate on a flat namespace, so we have to flatten names. - * We do this using the C++ namespace-mangling technique. Foo::bar - * symbols and such. - * - * - Symbols with the same name but different types need to get different - * linkage-names. We do this by hashing a string-encoding of the type into - * a fixed-size (currently 16-byte hex) cryptographic hash function (CHF: - * we use SHA1) to "prevent collisions". This is not airtight but 16 hex - * digits on uniform probability means you're going to need 2**32 same-name - * symbols in the same process before you're even hitting birthday-paradox - * collision probability. - * - * - Symbols in different crates but with same names "within" the crate need - * to get different linkage-names. - * - * So here is what we do: - * - * - Separate the meta tags into two sets: exported and local. Only work with - * the exported ones when considering linkage. - * - * - Consider two exported tags as special (and mandatory): name and vers. - * Every crate gets them; if it doesn't name them explicitly we infer them - * as basename(crate) and "0.1", respectively. Call these CNAME, CVERS. - * - * - Define CMETA as all the non-name, non-vers exported meta tags in the - * crate (in sorted order). - * - * - Define CMH as hash(CMETA + hashes of dependent crates). - * - * - Compile our crate to lib CNAME-CMH-CVERS.so - * - * - Define STH(sym) as hash(CNAME, CMH, type_str(sym)) - * - * - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the - * name, non-name metadata, and type sense, and versioned in the way - * system linkers understand. - * - */ - -fn build_link_meta(sess: session, c: ast::crate, output: &Path, - symbol_hasher: &hash::State) -> link_meta { - - type provided_metas = - {name: Option<~str>, - vers: Option<~str>, - cmh_items: ~[@ast::meta_item]}; - - fn provided_link_metas(sess: session, c: ast::crate) -> - provided_metas { - let mut name: Option<~str> = None; - let mut vers: Option<~str> = None; - let mut cmh_items: ~[@ast::meta_item] = ~[]; - let linkage_metas = attr::find_linkage_metas(c.node.attrs); - attr::require_unique_names(sess.diagnostic(), linkage_metas); - for linkage_metas.each |meta| { - if attr::get_meta_item_name(meta) == ~"name" { - match attr::get_meta_item_value_str(meta) { - Some(v) => { name = Some(v); } - None => vec::push(cmh_items, meta) - } - } else if attr::get_meta_item_name(meta) == ~"vers" { - match attr::get_meta_item_value_str(meta) { - Some(v) => { vers = Some(v); } - None => vec::push(cmh_items, meta) - } - } else { vec::push(cmh_items, meta); } - } - return {name: name, vers: vers, cmh_items: cmh_items}; - } - - // This calculates CMH as defined above - fn crate_meta_extras_hash(symbol_hasher: &hash::State, - _crate: ast::crate, - metas: provided_metas, - dep_hashes: ~[~str]) -> ~str { - fn len_and_str(s: ~str) -> ~str { - return fmt!("%u_%s", str::len(s), s); - } - - fn len_and_str_lit(l: ast::lit) -> ~str { - return len_and_str(pprust::lit_to_str(@l)); - } - - let cmh_items = attr::sort_meta_items(metas.cmh_items); - - symbol_hasher.reset(); - for cmh_items.each |m_| { - let m = m_; - match m.node { - ast::meta_name_value(key, value) => { - symbol_hasher.write_str(len_and_str(key)); - symbol_hasher.write_str(len_and_str_lit(value)); - } - ast::meta_word(name) => { - symbol_hasher.write_str(len_and_str(name)); - } - ast::meta_list(_, _) => { - // FIXME (#607): Implement this - fail ~"unimplemented meta_item variant"; - } - } - } - - for dep_hashes.each |dh| { - symbol_hasher.write_str(len_and_str(dh)); - } - - return truncated_hash_result(symbol_hasher); - } - - fn warn_missing(sess: session, name: ~str, default: ~str) { - if !sess.building_library { return; } - sess.warn(fmt!("missing crate link meta `%s`, using `%s` as default", - name, default)); - } - - fn crate_meta_name(sess: session, _crate: ast::crate, - output: &Path, metas: provided_metas) -> ~str { - return match metas.name { - Some(v) => v, - None => { - let name = match output.filestem() { - None => sess.fatal(fmt!("output file name `%s` doesn't\ - appear to have a stem", - output.to_str())), - Some(s) => s - }; - warn_missing(sess, ~"name", name); - name - } - }; - } - - fn crate_meta_vers(sess: session, _crate: ast::crate, - metas: provided_metas) -> ~str { - return match metas.vers { - Some(v) => v, - None => { - let vers = ~"0.0"; - warn_missing(sess, ~"vers", vers); - vers - } - }; - } - - let provided_metas = provided_link_metas(sess, c); - let name = crate_meta_name(sess, c, output, provided_metas); - let vers = crate_meta_vers(sess, c, provided_metas); - let dep_hashes = cstore::get_dep_hashes(sess.cstore); - let extras_hash = - crate_meta_extras_hash(symbol_hasher, c, provided_metas, dep_hashes); - - return {name: name, vers: vers, extras_hash: extras_hash}; -} - -fn truncated_hash_result(symbol_hasher: &hash::State) -> ~str unsafe { - symbol_hasher.result_str() -} - - -// This calculates STH for a symbol, as defined above -fn symbol_hash(tcx: ty::ctxt, symbol_hasher: &hash::State, t: ty::t, - link_meta: link_meta) -> ~str { - // NB: do *not* use abbrevs here as we want the symbol names - // to be independent of one another in the crate. - - symbol_hasher.reset(); - symbol_hasher.write_str(link_meta.name); - symbol_hasher.write_str(~"-"); - symbol_hasher.write_str(link_meta.extras_hash); - symbol_hasher.write_str(~"-"); - symbol_hasher.write_str(encoder::encoded_ty(tcx, t)); - let hash = truncated_hash_result(symbol_hasher); - // Prefix with _ so that it never blends into adjacent digits - - return ~"_" + hash; -} - -fn get_symbol_hash(ccx: @crate_ctxt, t: ty::t) -> ~str { - match ccx.type_hashcodes.find(t) { - Some(h) => return h, - None => { - let hash = symbol_hash(ccx.tcx, ccx.symbol_hasher, t, ccx.link_meta); - ccx.type_hashcodes.insert(t, hash); - return hash; - } - } -} - - -// Name sanitation. LLVM will happily accept identifiers with weird names, but -// gas doesn't! -fn sanitize(s: ~str) -> ~str { - let mut result = ~""; - do str::chars_iter(s) |c| { - match c { - '@' => result += ~"_sbox_", - '~' => result += ~"_ubox_", - '*' => result += ~"_ptr_", - '&' => result += ~"_ref_", - ',' => result += ~"_", - - '{' | '(' => result += ~"_of_", - 'a' .. 'z' - | 'A' .. 'Z' - | '0' .. '9' - | '_' => str::push_char(result,c), - _ => { - if c > 'z' && char::is_XID_continue(c) { - str::push_char(result,c); - } - } - } - } - - // Underscore-qualify anything that didn't start as an ident. - if result.len() > 0u && - result[0] != '_' as u8 && - ! char::is_XID_start(result[0] as char) { - return ~"_" + result; - } - - return result; -} - -fn mangle(sess: session, ss: path) -> ~str { - // Follow C++ namespace-mangling style - - let mut n = ~"_ZN"; // Begin name-sequence. - - for ss.each |s| { - match s { path_name(s) | path_mod(s) => { - let sani = sanitize(sess.str_of(s)); - n += fmt!("%u%s", str::len(sani), sani); - } } - } - n += ~"E"; // End name-sequence. - n -} - -fn exported_name(sess: session, path: path, hash: ~str, vers: ~str) -> ~str { - return mangle(sess, - vec::append_one( - vec::append_one(path, path_name(sess.ident_of(hash))), - path_name(sess.ident_of(vers)))); -} - -fn mangle_exported_name(ccx: @crate_ctxt, path: path, t: ty::t) -> ~str { - let hash = get_symbol_hash(ccx, t); - return exported_name(ccx.sess, path, hash, ccx.link_meta.vers); -} - -fn mangle_internal_name_by_type_only(ccx: @crate_ctxt, - t: ty::t, name: ~str) -> - ~str { - let s = util::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))]); -} - -fn mangle_internal_name_by_path_and_seq(ccx: @crate_ctxt, path: path, - flav: ~str) -> ~str { - return mangle(ccx.sess, - vec::append_one(path, path_name(ccx.names(flav)))); -} - -fn mangle_internal_name_by_path(ccx: @crate_ctxt, path: path) -> ~str { - return mangle(ccx.sess, path); -} - -fn mangle_internal_name_by_seq(ccx: @crate_ctxt, flav: ~str) -> ~str { - return fmt!("%s_%u", flav, ccx.names(flav)); -} - -// If the user wants an exe generated we need to invoke -// cc to link the object file with some libs -fn link_binary(sess: session, - obj_filename: &Path, - out_filename: &Path, - lm: link_meta) { - // Converts a library file-stem into a cc -l argument - fn unlib(config: @session::config, stem: ~str) -> ~str { - if stem.starts_with("lib") && - config.os != session::os_win32 { - stem.slice(3, stem.len()) - } else { - stem - } - } - - let output = if sess.building_library { - let long_libname = - os::dll_filename(fmt!("%s-%s-%s", - lm.name, lm.extras_hash, lm.vers)); - debug!("link_meta.name: %s", lm.name); - debug!("long_libname: %s", long_libname); - debug!("out_filename: %s", out_filename.to_str()); - debug!("dirname(out_filename): %s", out_filename.dir_path().to_str()); - - out_filename.dir_path().push(long_libname) - } else { - *out_filename - }; - - log(debug, ~"output: " + output.to_str()); - - // The default library location, we need this to find the runtime. - // The location of crates will be determined as needed. - let stage: ~str = ~"-L" + sess.filesearch.get_target_lib_path().to_str(); - - // In the future, FreeBSD will use clang as default compiler. - // It would be flexible to use cc (system's default C compiler) - // instead of hard-coded gcc. - // For win32, there is no cc command, - // so we add a condition to make it use gcc. - let cc_prog: ~str = - if sess.targ_cfg.os == session::os_win32 { ~"gcc" } else { ~"cc" }; - // The invocations of cc share some flags across platforms - - let mut cc_args = - vec::append(~[stage], sess.targ_cfg.target_strs.cc_args); - vec::push(cc_args, ~"-o"); - vec::push(cc_args, output.to_str()); - vec::push(cc_args, obj_filename.to_str()); - - let mut lib_cmd; - let os = sess.targ_cfg.os; - if os == session::os_macos { - lib_cmd = ~"-dynamiclib"; - } else { - lib_cmd = ~"-shared"; - } - - // # Crate linking - - let cstore = sess.cstore; - for cstore::get_used_crate_files(cstore).each |cratepath| { - if cratepath.filetype() == Some(~"rlib") { - vec::push(cc_args, cratepath.to_str()); - loop; - } - let dir = cratepath.dirname(); - if dir != ~"" { vec::push(cc_args, ~"-L" + dir); } - let libarg = unlib(sess.targ_cfg, option::get(cratepath.filestem())); - vec::push(cc_args, ~"-l" + libarg); - } - - let ula = cstore::get_used_link_args(cstore); - for ula.each |arg| { vec::push(cc_args, arg); } - - // # Extern library linking - - // User-supplied library search paths (-L on the cammand line) These are - // the same paths used to find Rust crates, so some of them may have been - // added already by the previous crate linking code. This only allows them - // to be found at compile time so it is still entirely up to outside - // forces to make sure that library can be found at runtime. - - let addl_paths = sess.opts.addl_lib_search_paths; - for addl_paths.each |path| { vec::push(cc_args, ~"-L" + path.to_str()); } - - // The names of the extern libraries - let used_libs = cstore::get_used_libraries(cstore); - for used_libs.each |l| { vec::push(cc_args, ~"-l" + l); } - - if sess.building_library { - vec::push(cc_args, lib_cmd); - - // On mac we need to tell the linker to let this library - // be rpathed - if sess.targ_cfg.os == session::os_macos { - vec::push(cc_args, ~"-Wl,-install_name,@rpath/" - + option::get(output.filename())); - } - } - - if !sess.debugging_opt(session::no_rt) { - // Always want the runtime linked in - vec::push(cc_args, ~"-lrustrt"); - } - - // On linux librt and libdl are an indirect dependencies via rustrt, - // and binutils 2.22+ won't add them automatically - if sess.targ_cfg.os == session::os_linux { - vec::push_all(cc_args, ~[~"-lrt", ~"-ldl"]); - - // LLVM implements the `frem` instruction as a call to `fmod`, - // which lives in libm. Similar to above, on some linuxes we - // have to be explicit about linking to it. See #2510 - vec::push(cc_args, ~"-lm"); - } - - if sess.targ_cfg.os == session::os_freebsd { - vec::push_all(cc_args, ~[~"-pthread", ~"-lrt", - ~"-L/usr/local/lib", ~"-lexecinfo", - ~"-L/usr/local/lib/gcc46", - ~"-L/usr/local/lib/gcc44", ~"-lstdc++", - ~"-Wl,-z,origin", - ~"-Wl,-rpath,/usr/local/lib/gcc46", - ~"-Wl,-rpath,/usr/local/lib/gcc44"]); - } - - // OS X 10.6 introduced 'compact unwind info', which is produced by the - // linker from the dwarf unwind info. Unfortunately, it does not seem to - // understand how to unwind our __morestack frame, so we have to turn it - // off. This has impacted some other projects like GHC. - if sess.targ_cfg.os == session::os_macos { - vec::push(cc_args, ~"-Wl,-no_compact_unwind"); - } - - // Stack growth requires statically linking a __morestack function - vec::push(cc_args, ~"-lmorestack"); - - // FIXME (#2397): At some point we want to rpath our guesses as to where - // extern libraries might live, based on the addl_lib_search_paths - vec::push_all(cc_args, rpath::get_rpath_flags(sess, &output)); - - debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" ")); - // We run 'cc' here - let prog = run::program_output(cc_prog, cc_args); - if 0 != prog.status { - sess.err(fmt!("linking with `%s` failed with code %d", - cc_prog, prog.status)); - sess.note(fmt!("%s arguments: %s", - cc_prog, str::connect(cc_args, ~" "))); - sess.note(prog.err + prog.out); - sess.abort_if_errors(); - } - - // Clean up on Darwin - if sess.targ_cfg.os == session::os_macos { - run::run_program(~"dsymutil", ~[output.to_str()]); - } - - // Remove the temporary object file if we aren't saving temps - if !sess.opts.save_temps { - if ! os::remove_file(obj_filename) { - sess.warn(fmt!("failed to delete object file `%s`", - obj_filename.to_str())); - } - } -} -// -// Local Variables: -// mode: rust -// fill-column: 78; -// indent-tabs-mode: nil -// c-basic-offset: 4 -// buffer-file-coding-system: utf-8-unix -// End: -// diff --git a/src/rustc/back/linkage.rs b/src/rustc/back/linkage.rs new file mode 100644 index 00000000000..0079ec9f363 --- /dev/null +++ b/src/rustc/back/linkage.rs @@ -0,0 +1,801 @@ +use libc::{c_int, c_uint, c_char}; +use driver::session; +use session::session; +use lib::llvm::llvm; +use syntax::attr; +use middle::ty; +use metadata::{encoder, cstore}; +use middle::trans::common::crate_ctxt; +use metadata::common::link_meta; +use std::map::HashMap; +use std::sha1::sha1; +use syntax::ast; +use syntax::print::pprust; +use lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data, True, False, + PassManagerRef, FileType}; +use metadata::filesearch; +use syntax::ast_map::{path, path_mod, path_name}; +use io::{Writer, WriterUtil}; + +enum output_type { + output_type_none, + output_type_bitcode, + output_type_assembly, + output_type_llvm_assembly, + output_type_object, + output_type_exe, +} + +impl output_type : cmp::Eq { + pure fn eq(&&other: output_type) -> bool { + (self as uint) == (other as uint) + } + pure fn ne(&&other: output_type) -> bool { !self.eq(other) } +} + +fn llvm_err(sess: session, msg: ~str) -> ! unsafe { + let cstr = llvm::LLVMRustGetLastError(); + if cstr == ptr::null() { + sess.fatal(msg); + } else { sess.fatal(msg + ~": " + str::raw::from_c_str(cstr)); } +} + +fn WriteOutputFile(sess:session, + PM: lib::llvm::PassManagerRef, M: ModuleRef, + Triple: *c_char, + // FIXME: When #2334 is fixed, change + // c_uint to FileType + Output: *c_char, FileType: c_uint, + OptLevel: c_int, + EnableSegmentedStacks: bool) { + let result = llvm::LLVMRustWriteOutputFile( + PM, M, Triple, Output, FileType, OptLevel, EnableSegmentedStacks); + if (!result) { + llvm_err(sess, ~"Could not write output"); + } +} + +#[cfg(stage0)] +mod jit { + fn exec(_sess: session, + _pm: PassManagerRef, + _m: ModuleRef, + _opt: c_int, + _stacks: bool) { + fail + } +} + +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +mod jit { + #[nolink] + #[abi = "rust-intrinsic"] + extern mod rusti { + fn morestack_addr() -> *(); + } + + struct Closure { + code: *(), + env: *(), + } + + fn exec(sess: session, + pm: PassManagerRef, + m: ModuleRef, + opt: c_int, + stacks: bool) unsafe { + let ptr = llvm::LLVMRustJIT(rusti::morestack_addr(), + pm, m, opt, stacks); + + if ptr::is_null(ptr) { + llvm_err(sess, ~"Could not JIT"); + } else { + let closure = Closure { + code: ptr, + env: ptr::null() + }; + let func: fn(~[~str]) = unsafe::transmute(move closure); + + func(~[sess.opts.binary]); + } + } +} + +mod write { + fn is_object_or_assembly_or_exe(ot: output_type) -> bool { + if ot == output_type_assembly || ot == output_type_object || + ot == output_type_exe { + return true; + } + return false; + } + + fn run_passes(sess: session, llmod: ModuleRef, output: &Path) { + let opts = sess.opts; + if sess.time_llvm_passes() { llvm::LLVMRustEnableTimePasses(); } + let mut pm = mk_pass_manager(); + let td = mk_target_data( + sess.targ_cfg.target_strs.data_layout); + llvm::LLVMAddTargetData(td.lltd, pm.llpm); + // FIXME (#2812): run the linter here also, once there are llvm-c + // bindings for it. + + // Generate a pre-optimization intermediate file if -save-temps was + // specified. + + + if opts.save_temps { + match opts.output_type { + output_type_bitcode => { + if opts.optimize != session::No { + let filename = output.with_filetype("no-opt.bc"); + str::as_c_str(filename.to_str(), |buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + } + } + _ => { + let filename = output.with_filetype("bc"); + str::as_c_str(filename.to_str(), |buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + } + } + } + if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } + // FIXME (#2396): This is mostly a copy of the bits of opt's -O2 that + // are available in the C api. + // Also: We might want to add optimization levels like -O1, -O2, + // -Os, etc + // Also: Should we expose and use the pass lists used by the opt + // tool? + + if opts.optimize != session::No { + let fpm = mk_pass_manager(); + llvm::LLVMAddTargetData(td.lltd, fpm.llpm); + + let FPMB = llvm::LLVMPassManagerBuilderCreate(); + llvm::LLVMPassManagerBuilderSetOptLevel(FPMB, 2u as c_uint); + llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(FPMB, + fpm.llpm); + llvm::LLVMPassManagerBuilderDispose(FPMB); + + llvm::LLVMRunPassManager(fpm.llpm, llmod); + let mut threshold = 225; + if opts.optimize == session::Aggressive { threshold = 275; } + + let MPMB = llvm::LLVMPassManagerBuilderCreate(); + llvm::LLVMPassManagerBuilderSetOptLevel(MPMB, + opts.optimize as c_uint); + llvm::LLVMPassManagerBuilderSetSizeLevel(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableUnitAtATime(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(MPMB, False); + llvm::LLVMPassManagerBuilderSetDisableSimplifyLibCalls(MPMB, + False); + + if threshold != 0u { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold + (MPMB, threshold as c_uint); + } + llvm::LLVMPassManagerBuilderPopulateModulePassManager(MPMB, + pm.llpm); + + llvm::LLVMPassManagerBuilderDispose(MPMB); + } + if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } + if is_object_or_assembly_or_exe(opts.output_type) || opts.jit { + let LLVMOptNone = 0 as c_int; // -O0 + let LLVMOptLess = 1 as c_int; // -O1 + let LLVMOptDefault = 2 as c_int; // -O2, -Os + let LLVMOptAggressive = 3 as c_int; // -O3 + + let mut CodeGenOptLevel = match opts.optimize { + session::No => LLVMOptNone, + session::Less => LLVMOptLess, + session::Default => LLVMOptDefault, + session::Aggressive => LLVMOptAggressive + }; + + if opts.jit { + // If we are using JIT, go ahead and create and + // execute the engine now. + // JIT execution takes ownership of the module, + // so don't dispose and return. + + // We need to tell LLVM where to resolve all linked + // symbols from. The equivalent of -lstd, -lcore, etc. + // By default the JIT will resolve symbols from the std and + // core linked into rustc. We don't want that, + // incase the user wants to use an older std library. + /*let cstore = sess.cstore; + for cstore::get_used_crate_files(cstore).each |cratepath| { + debug!{"linking: %s", cratepath}; + + let _: () = str::as_c_str( + cratepath, + |buf_t| { + if !llvm::LLVMRustLoadLibrary(buf_t) { + llvm_err(sess, ~"Could not link"); + } + debug!{"linked: %s", cratepath}; + }); + }*/ + + jit::exec(sess, pm.llpm, llmod, CodeGenOptLevel, true); + + if sess.time_llvm_passes() { + llvm::LLVMRustPrintPassTimings(); + } + return; + } + + let mut FileType; + if opts.output_type == output_type_object || + opts.output_type == output_type_exe { + FileType = lib::llvm::ObjectFile; + } else { FileType = lib::llvm::AssemblyFile; } + // Write optimized bitcode if --save-temps was on. + + if opts.save_temps { + // Always output the bitcode file with --save-temps + + let filename = output.with_filetype("opt.bc"); + llvm::LLVMRunPassManager(pm.llpm, llmod); + str::as_c_str(filename.to_str(), |buf| { + llvm::LLVMWriteBitcodeToFile(llmod, buf) + }); + pm = mk_pass_manager(); + // Save the assembly file if -S is used + + if opts.output_type == output_type_assembly { + let _: () = str::as_c_str( + sess.targ_cfg.target_strs.target_triple, + |buf_t| { + str::as_c_str(output.to_str(), |buf_o| { + WriteOutputFile( + sess, + pm.llpm, + llmod, + buf_t, + buf_o, + lib::llvm::AssemblyFile as c_uint, + CodeGenOptLevel, + true) + }) + }); + } + + + // Save the object file for -c or --save-temps alone + // This .o is needed when an exe is built + if opts.output_type == output_type_object || + opts.output_type == output_type_exe { + let _: () = str::as_c_str( + sess.targ_cfg.target_strs.target_triple, + |buf_t| { + str::as_c_str(output.to_str(), |buf_o| { + WriteOutputFile( + sess, + pm.llpm, + llmod, + buf_t, + buf_o, + lib::llvm::ObjectFile as c_uint, + CodeGenOptLevel, + true) + }) + }); + } + } else { + // If we aren't saving temps then just output the file + // type corresponding to the '-c' or '-S' flag used + + let _: () = str::as_c_str( + sess.targ_cfg.target_strs.target_triple, + |buf_t| { + str::as_c_str(output.to_str(), |buf_o| { + WriteOutputFile( + sess, + pm.llpm, + llmod, + buf_t, + buf_o, + FileType as c_uint, + CodeGenOptLevel, + true) + }) + }); + } + // Clean up and return + + llvm::LLVMDisposeModule(llmod); + if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + return; + } + + if opts.output_type == output_type_llvm_assembly { + // Given options "-S --emit-llvm": output LLVM assembly + str::as_c_str(output.to_str(), |buf_o| { + llvm::LLVMRustAddPrintModulePass(pm.llpm, llmod, buf_o)}); + } else { + // If only a bitcode file is asked for by using the '--emit-llvm' + // flag, then output it here + llvm::LLVMRunPassManager(pm.llpm, llmod); + str::as_c_str(output.to_str(), + |buf| llvm::LLVMWriteBitcodeToFile(llmod, buf) ); + } + + llvm::LLVMDisposeModule(llmod); + if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } + } +} + + +/* + * Name mangling and its relationship to metadata. This is complex. Read + * carefully. + * + * The semantic model of Rust linkage is, broadly, that "there's no global + * namespace" between crates. Our aim is to preserve the illusion of this + * model despite the fact that it's not *quite* possible to implement on + * modern linkers. We initially didn't use system linkers at all, but have + * been convinced of their utility. + * + * There are a few issues to handle: + * + * - Linkers operate on a flat namespace, so we have to flatten names. + * We do this using the C++ namespace-mangling technique. Foo::bar + * symbols and such. + * + * - Symbols with the same name but different types need to get different + * linkage-names. We do this by hashing a string-encoding of the type into + * a fixed-size (currently 16-byte hex) cryptographic hash function (CHF: + * we use SHA1) to "prevent collisions". This is not airtight but 16 hex + * digits on uniform probability means you're going to need 2**32 same-name + * symbols in the same process before you're even hitting birthday-paradox + * collision probability. + * + * - Symbols in different crates but with same names "within" the crate need + * to get different linkage-names. + * + * So here is what we do: + * + * - Separate the meta tags into two sets: exported and local. Only work with + * the exported ones when considering linkage. + * + * - Consider two exported tags as special (and mandatory): name and vers. + * Every crate gets them; if it doesn't name them explicitly we infer them + * as basename(crate) and "0.1", respectively. Call these CNAME, CVERS. + * + * - Define CMETA as all the non-name, non-vers exported meta tags in the + * crate (in sorted order). + * + * - Define CMH as hash(CMETA + hashes of dependent crates). + * + * - Compile our crate to lib CNAME-CMH-CVERS.so + * + * - Define STH(sym) as hash(CNAME, CMH, type_str(sym)) + * + * - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the + * name, non-name metadata, and type sense, and versioned in the way + * system linkers understand. + * + */ + +fn build_link_meta(sess: session, c: ast::crate, output: &Path, + symbol_hasher: &hash::State) -> link_meta { + + type provided_metas = + {name: Option<~str>, + vers: Option<~str>, + cmh_items: ~[@ast::meta_item]}; + + fn provided_link_metas(sess: session, c: ast::crate) -> + provided_metas { + let mut name: Option<~str> = None; + let mut vers: Option<~str> = None; + let mut cmh_items: ~[@ast::meta_item] = ~[]; + let linkage_metas = attr::find_linkage_metas(c.node.attrs); + attr::require_unique_names(sess.diagnostic(), linkage_metas); + for linkage_metas.each |meta| { + if attr::get_meta_item_name(meta) == ~"name" { + match attr::get_meta_item_value_str(meta) { + Some(v) => { name = Some(v); } + None => vec::push(cmh_items, meta) + } + } else if attr::get_meta_item_name(meta) == ~"vers" { + match attr::get_meta_item_value_str(meta) { + Some(v) => { vers = Some(v); } + None => vec::push(cmh_items, meta) + } + } else { vec::push(cmh_items, meta); } + } + return {name: name, vers: vers, cmh_items: cmh_items}; + } + + // This calculates CMH as defined above + fn crate_meta_extras_hash(symbol_hasher: &hash::State, + _crate: ast::crate, + metas: provided_metas, + dep_hashes: ~[~str]) -> ~str { + fn len_and_str(s: ~str) -> ~str { + return fmt!("%u_%s", str::len(s), s); + } + + fn len_and_str_lit(l: ast::lit) -> ~str { + return len_and_str(pprust::lit_to_str(@l)); + } + + let cmh_items = attr::sort_meta_items(metas.cmh_items); + + symbol_hasher.reset(); + for cmh_items.each |m_| { + let m = m_; + match m.node { + ast::meta_name_value(key, value) => { + symbol_hasher.write_str(len_and_str(key)); + symbol_hasher.write_str(len_and_str_lit(value)); + } + ast::meta_word(name) => { + symbol_hasher.write_str(len_and_str(name)); + } + ast::meta_list(_, _) => { + // FIXME (#607): Implement this + fail ~"unimplemented meta_item variant"; + } + } + } + + for dep_hashes.each |dh| { + symbol_hasher.write_str(len_and_str(dh)); + } + + return truncated_hash_result(symbol_hasher); + } + + fn warn_missing(sess: session, name: ~str, default: ~str) { + if !sess.building_library { return; } + sess.warn(fmt!("missing crate link meta `%s`, using `%s` as default", + name, default)); + } + + fn crate_meta_name(sess: session, _crate: ast::crate, + output: &Path, metas: provided_metas) -> ~str { + return match metas.name { + Some(v) => v, + None => { + let name = match output.filestem() { + None => sess.fatal(fmt!("output file name `%s` doesn't\ + appear to have a stem", + output.to_str())), + Some(s) => s + }; + warn_missing(sess, ~"name", name); + name + } + }; + } + + fn crate_meta_vers(sess: session, _crate: ast::crate, + metas: provided_metas) -> ~str { + return match metas.vers { + Some(v) => v, + None => { + let vers = ~"0.0"; + warn_missing(sess, ~"vers", vers); + vers + } + }; + } + + let provided_metas = provided_link_metas(sess, c); + let name = crate_meta_name(sess, c, output, provided_metas); + let vers = crate_meta_vers(sess, c, provided_metas); + let dep_hashes = cstore::get_dep_hashes(sess.cstore); + let extras_hash = + crate_meta_extras_hash(symbol_hasher, c, provided_metas, dep_hashes); + + return {name: name, vers: vers, extras_hash: extras_hash}; +} + +fn truncated_hash_result(symbol_hasher: &hash::State) -> ~str unsafe { + symbol_hasher.result_str() +} + + +// This calculates STH for a symbol, as defined above +fn symbol_hash(tcx: ty::ctxt, symbol_hasher: &hash::State, t: ty::t, + link_meta: link_meta) -> ~str { + // NB: do *not* use abbrevs here as we want the symbol names + // to be independent of one another in the crate. + + symbol_hasher.reset(); + symbol_hasher.write_str(link_meta.name); + symbol_hasher.write_str(~"-"); + symbol_hasher.write_str(link_meta.extras_hash); + symbol_hasher.write_str(~"-"); + symbol_hasher.write_str(encoder::encoded_ty(tcx, t)); + let hash = truncated_hash_result(symbol_hasher); + // Prefix with _ so that it never blends into adjacent digits + + return ~"_" + hash; +} + +fn get_symbol_hash(ccx: @crate_ctxt, t: ty::t) -> ~str { + match ccx.type_hashcodes.find(t) { + Some(h) => return h, + None => { + let hash = symbol_hash(ccx.tcx, ccx.symbol_hasher, t, ccx.link_meta); + ccx.type_hashcodes.insert(t, hash); + return hash; + } + } +} + + +// Name sanitation. LLVM will happily accept identifiers with weird names, but +// gas doesn't! +fn sanitize(s: ~str) -> ~str { + let mut result = ~""; + do str::chars_iter(s) |c| { + match c { + '@' => result += ~"_sbox_", + '~' => result += ~"_ubox_", + '*' => result += ~"_ptr_", + '&' => result += ~"_ref_", + ',' => result += ~"_", + + '{' | '(' => result += ~"_of_", + 'a' .. 'z' + | 'A' .. 'Z' + | '0' .. '9' + | '_' => str::push_char(result,c), + _ => { + if c > 'z' && char::is_XID_continue(c) { + str::push_char(result,c); + } + } + } + } + + // Underscore-qualify anything that didn't start as an ident. + if result.len() > 0u && + result[0] != '_' as u8 && + ! char::is_XID_start(result[0] as char) { + return ~"_" + result; + } + + return result; +} + +fn mangle(sess: session, ss: path) -> ~str { + // Follow C++ namespace-mangling style + + let mut n = ~"_ZN"; // Begin name-sequence. + + for ss.each |s| { + match s { path_name(s) | path_mod(s) => { + let sani = sanitize(sess.str_of(s)); + n += fmt!("%u%s", str::len(sani), sani); + } } + } + n += ~"E"; // End name-sequence. + n +} + +fn exported_name(sess: session, path: path, hash: ~str, vers: ~str) -> ~str { + return mangle(sess, + vec::append_one( + vec::append_one(path, path_name(sess.ident_of(hash))), + path_name(sess.ident_of(vers)))); +} + +fn mangle_exported_name(ccx: @crate_ctxt, path: path, t: ty::t) -> ~str { + let hash = get_symbol_hash(ccx, t); + return exported_name(ccx.sess, path, hash, ccx.link_meta.vers); +} + +fn mangle_internal_name_by_type_only(ccx: @crate_ctxt, + t: ty::t, name: ~str) -> + ~str { + let s = util::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))]); +} + +fn mangle_internal_name_by_path_and_seq(ccx: @crate_ctxt, path: path, + flav: ~str) -> ~str { + return mangle(ccx.sess, + vec::append_one(path, path_name(ccx.names(flav)))); +} + +fn mangle_internal_name_by_path(ccx: @crate_ctxt, path: path) -> ~str { + return mangle(ccx.sess, path); +} + +fn mangle_internal_name_by_seq(ccx: @crate_ctxt, flav: ~str) -> ~str { + return fmt!("%s_%u", flav, ccx.names(flav)); +} + +// If the user wants an exe generated we need to invoke +// cc to link the object file with some libs +fn link_binary(sess: session, + obj_filename: &Path, + out_filename: &Path, + lm: link_meta) { + // Converts a library file-stem into a cc -l argument + fn unlib(config: @session::config, stem: ~str) -> ~str { + if stem.starts_with("lib") && + config.os != session::os_win32 { + stem.slice(3, stem.len()) + } else { + stem + } + } + + let output = if sess.building_library { + let long_libname = + os::dll_filename(fmt!("%s-%s-%s", + lm.name, lm.extras_hash, lm.vers)); + debug!("link_meta.name: %s", lm.name); + debug!("long_libname: %s", long_libname); + debug!("out_filename: %s", out_filename.to_str()); + debug!("dirname(out_filename): %s", out_filename.dir_path().to_str()); + + out_filename.dir_path().push(long_libname) + } else { + *out_filename + }; + + log(debug, ~"output: " + output.to_str()); + + // The default library location, we need this to find the runtime. + // The location of crates will be determined as needed. + let stage: ~str = ~"-L" + sess.filesearch.get_target_lib_path().to_str(); + + // In the future, FreeBSD will use clang as default compiler. + // It would be flexible to use cc (system's default C compiler) + // instead of hard-coded gcc. + // For win32, there is no cc command, + // so we add a condition to make it use gcc. + let cc_prog: ~str = + if sess.targ_cfg.os == session::os_win32 { ~"gcc" } else { ~"cc" }; + // The invocations of cc share some flags across platforms + + let mut cc_args = + vec::append(~[stage], sess.targ_cfg.target_strs.cc_args); + vec::push(cc_args, ~"-o"); + vec::push(cc_args, output.to_str()); + vec::push(cc_args, obj_filename.to_str()); + + let mut lib_cmd; + let os = sess.targ_cfg.os; + if os == session::os_macos { + lib_cmd = ~"-dynamiclib"; + } else { + lib_cmd = ~"-shared"; + } + + // # Crate linking + + let cstore = sess.cstore; + for cstore::get_used_crate_files(cstore).each |cratepath| { + if cratepath.filetype() == Some(~"rlib") { + vec::push(cc_args, cratepath.to_str()); + loop; + } + let dir = cratepath.dirname(); + if dir != ~"" { vec::push(cc_args, ~"-L" + dir); } + let libarg = unlib(sess.targ_cfg, option::get(cratepath.filestem())); + vec::push(cc_args, ~"-l" + libarg); + } + + let ula = cstore::get_used_link_args(cstore); + for ula.each |arg| { vec::push(cc_args, arg); } + + // # Extern library linking + + // User-supplied library search paths (-L on the cammand line) These are + // the same paths used to find Rust crates, so some of them may have been + // added already by the previous crate linking code. This only allows them + // to be found at compile time so it is still entirely up to outside + // forces to make sure that library can be found at runtime. + + let addl_paths = sess.opts.addl_lib_search_paths; + for addl_paths.each |path| { vec::push(cc_args, ~"-L" + path.to_str()); } + + // The names of the extern libraries + let used_libs = cstore::get_used_libraries(cstore); + for used_libs.each |l| { vec::push(cc_args, ~"-l" + l); } + + if sess.building_library { + vec::push(cc_args, lib_cmd); + + // On mac we need to tell the linker to let this library + // be rpathed + if sess.targ_cfg.os == session::os_macos { + vec::push(cc_args, ~"-Wl,-install_name,@rpath/" + + option::get(output.filename())); + } + } + + if !sess.debugging_opt(session::no_rt) { + // Always want the runtime linked in + vec::push(cc_args, ~"-lrustrt"); + } + + // On linux librt and libdl are an indirect dependencies via rustrt, + // and binutils 2.22+ won't add them automatically + if sess.targ_cfg.os == session::os_linux { + vec::push_all(cc_args, ~[~"-lrt", ~"-ldl"]); + + // LLVM implements the `frem` instruction as a call to `fmod`, + // which lives in libm. Similar to above, on some linuxes we + // have to be explicit about linking to it. See #2510 + vec::push(cc_args, ~"-lm"); + } + + if sess.targ_cfg.os == session::os_freebsd { + vec::push_all(cc_args, ~[~"-pthread", ~"-lrt", + ~"-L/usr/local/lib", ~"-lexecinfo", + ~"-L/usr/local/lib/gcc46", + ~"-L/usr/local/lib/gcc44", ~"-lstdc++", + ~"-Wl,-z,origin", + ~"-Wl,-rpath,/usr/local/lib/gcc46", + ~"-Wl,-rpath,/usr/local/lib/gcc44"]); + } + + // OS X 10.6 introduced 'compact unwind info', which is produced by the + // linker from the dwarf unwind info. Unfortunately, it does not seem to + // understand how to unwind our __morestack frame, so we have to turn it + // off. This has impacted some other projects like GHC. + if sess.targ_cfg.os == session::os_macos { + vec::push(cc_args, ~"-Wl,-no_compact_unwind"); + } + + // Stack growth requires statically linking a __morestack function + vec::push(cc_args, ~"-lmorestack"); + + // FIXME (#2397): At some point we want to rpath our guesses as to where + // extern libraries might live, based on the addl_lib_search_paths + vec::push_all(cc_args, rpath::get_rpath_flags(sess, &output)); + + debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" ")); + // We run 'cc' here + let prog = run::program_output(cc_prog, cc_args); + if 0 != prog.status { + sess.err(fmt!("linking with `%s` failed with code %d", + cc_prog, prog.status)); + sess.note(fmt!("%s arguments: %s", + cc_prog, str::connect(cc_args, ~" "))); + sess.note(prog.err + prog.out); + sess.abort_if_errors(); + } + + // Clean up on Darwin + if sess.targ_cfg.os == session::os_macos { + run::run_program(~"dsymutil", ~[output.to_str()]); + } + + // Remove the temporary object file if we aren't saving temps + if !sess.opts.save_temps { + if ! os::remove_file(obj_filename) { + sess.warn(fmt!("failed to delete object file `%s`", + obj_filename.to_str())); + } + } +} +// +// Local Variables: +// mode: rust +// fill-column: 78; +// indent-tabs-mode: nil +// c-basic-offset: 4 +// buffer-file-coding-system: utf-8-unix +// End: +// diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs index 44d62e8d926..b476fa5217b 100644 --- a/src/rustc/driver/driver.rs +++ b/src/rustc/driver/driver.rs @@ -7,7 +7,7 @@ use syntax::attr; use middle::{trans, freevars, kind, ty, typeck, lint}; use syntax::print::{pp, pprust}; use util::ppaux; -use back::link; +use back::linkage; use result::{Ok, Err}; use std::getopts; use io::WriterUtil; @@ -258,20 +258,20 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg, exp_map, exp_map2, maps)); time(time_passes, ~"LLVM passes", || - link::write::run_passes(sess, llmod, - &outputs.obj_filename)); + linkage::write::run_passes(sess, llmod, + &outputs.obj_filename)); let stop_after_codegen = - sess.opts.output_type != link::output_type_exe || - (sess.opts.static && sess.building_library) || + sess.opts.output_type != linkage::output_type_exe || + (sess.opts.static && sess.building_library) || sess.opts.jit; if stop_after_codegen { return {crate: crate, tcx: Some(ty_cx)}; } time(time_passes, ~"linking", || - link::link_binary(sess, - &outputs.obj_filename, - &outputs.out_filename, link_meta)); + linkage::link_binary(sess, + &outputs.obj_filename, + &outputs.out_filename, link_meta)); return {crate: crate, tcx: Some(ty_cx)}; } @@ -492,17 +492,19 @@ fn build_session_options(binary: ~str, let jit = opt_present(matches, ~"jit"); let output_type = if parse_only || no_trans { - link::output_type_none + linkage::output_type_none } else if opt_present(matches, ~"S") && opt_present(matches, ~"emit-llvm") { - link::output_type_llvm_assembly + linkage::output_type_llvm_assembly } else if opt_present(matches, ~"S") { - link::output_type_assembly + linkage::output_type_assembly } else if opt_present(matches, ~"c") { - link::output_type_object + linkage::output_type_object } else if opt_present(matches, ~"emit-llvm") { - link::output_type_bitcode - } else { link::output_type_exe }; + linkage::output_type_bitcode + } else { + linkage::output_type_exe + }; let extra_debuginfo = opt_present(matches, ~"xg"); let debuginfo = opt_present(matches, ~"g") || extra_debuginfo; let sysroot_opt = getopts::opt_maybe_str(matches, ~"sysroot"); @@ -511,7 +513,8 @@ fn build_session_options(binary: ~str, let save_temps = getopts::opt_present(matches, ~"save-temps"); match output_type { // unless we're emitting huamn-readable assembly, omit comments. - link::output_type_llvm_assembly | link::output_type_assembly => (), + linkage::output_type_llvm_assembly | + linkage::output_type_assembly => (), _ => debugging_opts |= session::no_asm_comments } let opt_level = { @@ -657,18 +660,18 @@ fn build_output_filenames(input: input, let out_path; let sopts = sess.opts; let stop_after_codegen = - sopts.output_type != link::output_type_exe || + sopts.output_type != linkage::output_type_exe || sopts.static && sess.building_library; let obj_suffix = match sopts.output_type { - link::output_type_none => ~"none", - link::output_type_bitcode => ~"bc", - link::output_type_assembly => ~"s", - link::output_type_llvm_assembly => ~"ll", + linkage::output_type_none => ~"none", + linkage::output_type_bitcode => ~"bc", + linkage::output_type_assembly => ~"s", + linkage::output_type_llvm_assembly => ~"ll", // Object and exe output both use the '.o' extension here - link::output_type_object | link::output_type_exe => ~"o" + linkage::output_type_object | linkage::output_type_exe => ~"o" }; match *ofile { diff --git a/src/rustc/driver/session.rs b/src/rustc/driver/session.rs index 70e717aa6d9..c4dde985bdb 100644 --- a/src/rustc/driver/session.rs +++ b/src/rustc/driver/session.rs @@ -6,7 +6,7 @@ use syntax::ast::{int_ty, uint_ty, float_ty}; use syntax::parse::parse_sess; use metadata::filesearch; use back::target_strs; -use back::link; +use back::linkage; use middle::lint; @@ -113,7 +113,7 @@ type options = lint_opts: ~[(lint::lint, lint::level)], save_temps: bool, jit: bool, - output_type: back::link::output_type, + output_type: back::linkage::output_type, addl_lib_search_paths: ~[Path], maybe_sysroot: Option, target_triple: ~str, @@ -256,7 +256,7 @@ fn basic_options() -> @options { lint_opts: ~[], save_temps: false, jit: false, - output_type: link::output_type_exe, + output_type: linkage::output_type_exe, addl_lib_search_paths: ~[], maybe_sysroot: None, target_triple: driver::host_triple(), diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index ef791cd5020..f72b4214abe 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -20,7 +20,7 @@ use std::map::{int_hash, str_hash}; use driver::session; use session::session; use syntax::attr; -use back::{link, abi, upcall}; +use back::{linkage, abi, upcall}; use syntax::{ast, ast_util, codemap, ast_map}; use ast_util::{local_def, path_to_ident}; use syntax::visit; @@ -32,7 +32,7 @@ use util::common::is_main_name; use lib::llvm::{llvm, mk_target_data, mk_type_names}; use lib::llvm::{ModuleRef, ValueRef, TypeRef, BasicBlockRef}; use lib::llvm::{True, False}; -use link::{mangle_internal_name_by_type_only, +use linkage::{mangle_internal_name_by_type_only, mangle_internal_name_by_seq, mangle_internal_name_by_path, mangle_internal_name_by_path_and_seq, @@ -2571,7 +2571,7 @@ fn trans_crate(sess: session::session, let symbol_hasher = @hash::default_state(); let link_meta = - link::build_link_meta(sess, *crate, output, symbol_hasher); + linkage::build_link_meta(sess, *crate, output, symbol_hasher); let reachable = reachable::find_reachable(crate.node.module, emap, tcx, maps.method_map); diff --git a/src/rustc/middle/trans/closure.rs b/src/rustc/middle/trans/closure.rs index 1ba8e22607a..abc9cfecfd6 100644 --- a/src/rustc/middle/trans/closure.rs +++ b/src/rustc/middle/trans/closure.rs @@ -10,7 +10,7 @@ use type_of::*; use back::abi; use syntax::codemap::span; use syntax::print::pprust::expr_to_str; -use back::link::{ +use back::linkage::{ mangle_internal_name_by_path, mangle_internal_name_by_path_and_seq}; use util::ppaux::ty_to_str; diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index b0ac8d920bc..f3fc4bfaa26 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -10,7 +10,7 @@ use syntax::{ast, ast_map}; use driver::session; use session::session; use middle::ty; -use back::{link, abi, upcall}; +use back::{linkage, abi, upcall}; use syntax::codemap::span; use lib::llvm::{llvm, target_data, type_names, associate_type, name_has_type}; diff --git a/src/rustc/middle/trans/controlflow.rs b/src/rustc/middle/trans/controlflow.rs index 1affa18a683..7eda33fb2fb 100644 --- a/src/rustc/middle/trans/controlflow.rs +++ b/src/rustc/middle/trans/controlflow.rs @@ -165,7 +165,7 @@ fn trans_log(log_ex: @ast::expr, let global = if ccx.module_data.contains_key(modname) { ccx.module_data.get(modname) } else { - let s = link::mangle_internal_name_by_path_and_seq( + let s = linkage::mangle_internal_name_by_path_and_seq( ccx, modpath, ~"loglevel"); let global = str::as_c_str(s, |buf| { llvm::LLVMAddGlobal(ccx.llmod, T_i32(), buf) diff --git a/src/rustc/middle/trans/foreign.rs b/src/rustc/middle/trans/foreign.rs index b97fa54588c..d267599095a 100644 --- a/src/rustc/middle/trans/foreign.rs +++ b/src/rustc/middle/trans/foreign.rs @@ -10,7 +10,7 @@ use lib::llvm::{ llvm, TypeRef, ValueRef, Integer, Pointer, Float, Double, StructRetAttribute, ByValAttribute, SequentiallyConsistent, Acquire, Release, Xchg }; use syntax::{ast, ast_util}; -use back::{link, abi}; +use back::{linkage, abi}; use common::*; use build::*; use base::*; @@ -1007,7 +1007,7 @@ fn trans_foreign_fn(ccx: @crate_ctxt, path: ast_map::path, decl: ast::fn_decl, id: ast::node_id) -> ValueRef { let _icx = ccx.insn_ctxt("foreign::foreign::build_rust_fn"); let t = ty::node_id_to_type(ccx.tcx, id); - let ps = link::mangle_internal_name_by_path( + let ps = linkage::mangle_internal_name_by_path( ccx, vec::append_one(path, ast_map::path_name( syntax::parse::token::special_idents::clownshoe_abi ))); @@ -1046,7 +1046,7 @@ fn trans_foreign_fn(ccx: @crate_ctxt, path: ast_map::path, decl: ast::fn_decl, // is wired directly into the return slot in the shim struct } - let shim_name = link::mangle_internal_name_by_path( + let shim_name = linkage::mangle_internal_name_by_path( ccx, vec::append_one(path, ast_map::path_name( syntax::parse::token::special_idents::clownshoe_stack_shim ))); diff --git a/src/rustc/middle/trans/meth.rs b/src/rustc/middle/trans/meth.rs index a87ae02f8d2..ddb79f8cd4a 100644 --- a/src/rustc/middle/trans/meth.rs +++ b/src/rustc/middle/trans/meth.rs @@ -8,7 +8,7 @@ use syntax::{ast, ast_map}; use ast_map::{path, path_mod, path_name, node_id_to_str}; use syntax::ast_util::local_def; use metadata::csearch; -use back::{link, abi}; +use back::abi; use lib::llvm::llvm; use lib::llvm::{ValueRef, TypeRef}; use lib::llvm::llvm::LLVMGetParam; diff --git a/src/rustc/middle/trans/monomorphize.rs b/src/rustc/middle/trans/monomorphize.rs index 243b0b96fa3..239988c06e5 100644 --- a/src/rustc/middle/trans/monomorphize.rs +++ b/src/rustc/middle/trans/monomorphize.rs @@ -9,7 +9,7 @@ use base::{trans_item, get_item_val, no_self, self_arg, trans_fn, get_insn_ctxt}; use syntax::parse::token::special_idents; use type_of::type_of_fn_from_ty; -use back::link::mangle_exported_name; +use back::linkage::mangle_exported_name; use middle::ty::{FnTyBase, FnMeta, FnSig}; fn monomorphic_fn(ccx: @crate_ctxt, diff --git a/src/rustc/rustc.rc b/src/rustc/rustc.rc index 501192d3ced..46b2ff1fa50 100644 --- a/src/rustc/rustc.rc +++ b/src/rustc/rustc.rc @@ -121,7 +121,7 @@ mod front { } mod back { - mod link; + mod linkage; mod abi; mod upcall; mod x86; diff --git a/src/rustdoc/astsrv.rs b/src/rustdoc/astsrv.rs index cda89e3b083..1f6ba5ed907 100644 --- a/src/rustdoc/astsrv.rs +++ b/src/rustdoc/astsrv.rs @@ -17,7 +17,6 @@ use syntax::diagnostic::handler; use syntax::ast; use syntax::codemap; use syntax::ast_map; -use rustc::back::link; use rustc::metadata::filesearch; use rustc::front; diff --git a/src/rustdoc/doc.rs b/src/rustdoc/doc.rs index 5161fdff270..154a7f1c6d0 100644 --- a/src/rustdoc/doc.rs +++ b/src/rustdoc/doc.rs @@ -334,13 +334,13 @@ impl index : cmp::Eq { * * kind - The type of thing being indexed, e.g. 'Module' * * name - The name of the thing * * brief - The brief description - * * link - A format-specific string representing the link target + * * lnk - A format-specific string representing the link target */ type index_entry = { kind: ~str, name: ~str, brief: Option<~str>, - link: ~str + lnk: ~str }; impl index_entry : cmp::Eq { @@ -348,7 +348,7 @@ impl index_entry : cmp::Eq { self.kind == other.kind && self.name == other.name && self.brief == other.brief && - self.link == other.link + self.lnk == other.lnk } pure fn ne(&&other: index_entry) -> bool { !self.eq(other) } } diff --git a/src/rustdoc/markdown_index_pass.rs b/src/rustdoc/markdown_index_pass.rs index 172147b1f24..067951ef6f9 100644 --- a/src/rustdoc/markdown_index_pass.rs +++ b/src/rustdoc/markdown_index_pass.rs @@ -78,7 +78,7 @@ fn item_to_entry( doc: doc::itemtag, config: config::config ) -> doc::index_entry { - let link = match doc { + let lnk = match doc { doc::modtag(_) | doc::nmodtag(_) if config.output_style == config::doc_per_mod => { markdown_writer::make_filename(config, doc::itempage(doc)).to_str() @@ -92,7 +92,7 @@ fn item_to_entry( kind: markdown_pass::header_kind(doc), name: markdown_pass::header_name(doc), brief: doc.brief(), - link: link + lnk: lnk } } @@ -156,13 +156,13 @@ fn should_index_mod_contents() { kind: ~"Module", name: ~"a", brief: None, - link: ~"#module-a" + lnk: ~"#module-a" }; assert option::get(doc.cratemod().index).entries[1] == { kind: ~"Function", name: ~"b", brief: None, - link: ~"#function-b" + lnk: ~"#function-b" }; } @@ -176,13 +176,13 @@ fn should_index_mod_contents_multi_page() { kind: ~"Module", name: ~"a", brief: None, - link: ~"a.html" + lnk: ~"a.html" }; assert option::get(doc.cratemod().index).entries[1] == { kind: ~"Function", name: ~"b", brief: None, - link: ~"#function-b" + lnk: ~"#function-b" }; } @@ -196,7 +196,7 @@ fn should_index_foreign_mod_pages() { kind: ~"Foreign module", name: ~"a", brief: None, - link: ~"a.html" + lnk: ~"a.html" }; } @@ -220,7 +220,7 @@ fn should_index_foreign_mod_contents() { kind: ~"Function", name: ~"b", brief: None, - link: ~"#function-b" + lnk: ~"#function-b" }; } diff --git a/src/rustdoc/markdown_pass.rs b/src/rustdoc/markdown_pass.rs index 03726ca0188..726bbc56237 100644 --- a/src/rustdoc/markdown_pass.rs +++ b/src/rustdoc/markdown_pass.rs @@ -398,7 +398,7 @@ fn write_index(ctxt: ctxt, index: doc::index) { for index.entries.each |entry| { let header = header_text_(entry.kind, entry.name); - let id = entry.link; + let id = entry.lnk; if option::is_some(entry.brief) { ctxt.w.write_line(fmt!("* [%s](%s) - %s", header, id, option::get(entry.brief))); diff --git a/src/test/run-pass/mlist-cycle.rs b/src/test/run-pass/mlist-cycle.rs index 0fcb95fb73b..e07c066f9c9 100644 --- a/src/test/run-pass/mlist-cycle.rs +++ b/src/test/run-pass/mlist-cycle.rs @@ -2,14 +2,14 @@ // -*- rust -*- extern mod std; -type cell = {mut c: @list}; +type Cell = {mut c: @List}; -enum list { link(@cell), nil, } +enum List { Link(@Cell), Nil, } fn main() { - let first: @cell = @{mut c: @nil()}; - let second: @cell = @{mut c: @link(first)}; - first._0 = @link(second); + let first: @Cell = @{mut c: @Nil()}; + let second: @Cell = @{mut c: @Link(first)}; + first._0 = @Link(second); sys.rustrt.gc(); - let third: @cell = @{mut c: @nil()}; -} \ No newline at end of file + let third: @Cell = @{mut c: @Nil()}; +} -- cgit 1.4.1-3-g733a5