auto merge of #21113 : alexcrichton/rust/plug-a-hole, r=brson

With the addition of separate search paths to the compiler, it was intended that
applications such as Cargo could require a `--extern` flag per `extern crate`
directive in the source. The system can currently be subverted, however, due to
the `existing_match()` logic in the crate loader.

When loading crates we first attempt to match an `extern crate` directive
against all previously loaded crates to avoid reading metadata twice. This "hit
the cache if possible" step was erroneously leaking crates across the search
path boundaries, however. For example:

    extern crate b;
    extern crate a;

If `b` depends on `a`, then it will load crate `a` when the `extern crate b`
directive is being processed. When the compiler reaches `extern crate a` it will
use the previously loaded version no matter what. If the compiler was not
invoked with `-L crate=path/to/a`, it will still succeed.

This behavior is allowing `extern crate` declarations in Cargo without a
corresponding declaration in the manifest of a dependency, which is considered
a bug.

This commit fixes this problem by keeping track of the origin search path for a
crate. Crates loaded from the dependency search path are not candidates for
crates which are loaded from the crate search path.
This commit is contained in:
bors 2015-01-16 19:17:30 +00:00
commit 653e6880c9
15 changed files with 145 additions and 78 deletions

View File

@ -58,8 +58,8 @@ fn dump_crates(cstore: &CStore) {
debug!(" hash: {}", data.hash()); debug!(" hash: {}", data.hash());
opt_source.map(|cs| { opt_source.map(|cs| {
let CrateSource { dylib, rlib, cnum: _ } = cs; let CrateSource { dylib, rlib, cnum: _ } = cs;
dylib.map(|dl| debug!(" dylib: {}", dl.display())); dylib.map(|dl| debug!(" dylib: {}", dl.0.display()));
rlib.map(|rl| debug!(" rlib: {}", rl.display())); rlib.map(|rl| debug!(" rlib: {}", rl.0.display()));
}); });
}) })
} }
@ -305,8 +305,8 @@ impl<'a> CrateReader<'a> {
} }
} }
fn existing_match(&self, name: &str, fn existing_match(&self, name: &str, hash: Option<&Svh>, kind: PathKind)
hash: Option<&Svh>) -> Option<ast::CrateNum> { -> Option<ast::CrateNum> {
let mut ret = None; let mut ret = None;
self.sess.cstore.iter_crate_data(|cnum, data| { self.sess.cstore.iter_crate_data(|cnum, data| {
if data.name != name { return } if data.name != name { return }
@ -317,27 +317,37 @@ impl<'a> CrateReader<'a> {
None => {} None => {}
} }
// When the hash is None we're dealing with a top-level dependency in // When the hash is None we're dealing with a top-level dependency
// which case we may have a specification on the command line for this // in which case we may have a specification on the command line for
// library. Even though an upstream library may have loaded something of // this library. Even though an upstream library may have loaded
// the same name, we have to make sure it was loaded from the exact same // something of the same name, we have to make sure it was loaded
// location as well. // from the exact same location as well.
// //
// We're also sure to compare *paths*, not actual byte slices. The // We're also sure to compare *paths*, not actual byte slices. The
// `source` stores paths which are normalized which may be different // `source` stores paths which are normalized which may be different
// from the strings on the command line. // from the strings on the command line.
let source = self.sess.cstore.get_used_crate_source(cnum).unwrap(); let source = self.sess.cstore.get_used_crate_source(cnum).unwrap();
match self.sess.opts.externs.get(name) { if let Some(locs) = self.sess.opts.externs.get(name) {
Some(locs) => { let found = locs.iter().any(|l| {
let found = locs.iter().any(|l| { let l = fs::realpath(&Path::new(&l[])).ok();
let l = fs::realpath(&Path::new(&l[])).ok(); source.dylib.as_ref().map(|p| &p.0) == l.as_ref() ||
l == source.dylib || l == source.rlib source.rlib.as_ref().map(|p| &p.0) == l.as_ref()
}); });
if found { if found {
ret = Some(cnum); ret = Some(cnum);
}
} }
None => ret = Some(cnum), }
// Alright, so we've gotten this far which means that `data` has the
// right name, we don't have a hash, and we don't have a --extern
// pointing for ourselves. We're still not quite yet done because we
// have to make sure that this crate was found in the crate lookup
// path (this is a top-level dependency) as we don't want to
// implicitly load anything inside the dependency lookup path.
let prev_kind = source.dylib.as_ref().or(source.rlib.as_ref())
.unwrap().1;
if ret.is_none() && (prev_kind == kind || prev_kind == PathKind::All) {
ret = Some(cnum);
} }
}); });
return ret; return ret;
@ -359,8 +369,8 @@ impl<'a> CrateReader<'a> {
let crate_paths = if root.is_none() { let crate_paths = if root.is_none() {
Some(CratePaths { Some(CratePaths {
ident: ident.to_string(), ident: ident.to_string(),
dylib: lib.dylib.clone(), dylib: lib.dylib.clone().map(|p| p.0),
rlib: lib.rlib.clone(), rlib: lib.rlib.clone().map(|p| p.0),
}) })
} else { } else {
None None
@ -400,7 +410,7 @@ impl<'a> CrateReader<'a> {
kind: PathKind) kind: PathKind)
-> (ast::CrateNum, Rc<cstore::crate_metadata>, -> (ast::CrateNum, Rc<cstore::crate_metadata>,
cstore::CrateSource) { cstore::CrateSource) {
match self.existing_match(name, hash) { match self.existing_match(name, hash, kind) {
None => { None => {
let mut load_ctxt = loader::Context { let mut load_ctxt = loader::Context {
sess: self.sess, sess: self.sess,
@ -483,8 +493,8 @@ impl<'a> CrateReader<'a> {
let library = match load_ctxt.maybe_load_library_crate() { let library = match load_ctxt.maybe_load_library_crate() {
Some(l) => l, Some(l) => l,
None if is_cross => { None if is_cross => {
// Try loading from target crates. This will abort later if we try to // Try loading from target crates. This will abort later if we
// load a plugin registrar function, // try to load a plugin registrar function,
target_only = true; target_only = true;
should_link = info.should_link; should_link = info.should_link;
@ -497,7 +507,9 @@ impl<'a> CrateReader<'a> {
}; };
let dylib = library.dylib.clone(); let dylib = library.dylib.clone();
let register = should_link && self.existing_match(info.name.as_slice(), None).is_none(); let register = should_link && self.existing_match(info.name.as_slice(),
None,
PathKind::Crate).is_none();
let metadata = if register { let metadata = if register {
// Register crate now to avoid double-reading metadata // Register crate now to avoid double-reading metadata
let (_, cmd, _) = self.register_crate(&None, &info.ident[], let (_, cmd, _) = self.register_crate(&None, &info.ident[],
@ -511,7 +523,7 @@ impl<'a> CrateReader<'a> {
PluginMetadata { PluginMetadata {
sess: self.sess, sess: self.sess,
metadata: metadata, metadata: metadata,
dylib: dylib, dylib: dylib.map(|p| p.0),
info: info, info: info,
vi_span: span, vi_span: span,
target_only: target_only, target_only: target_only,

View File

@ -20,6 +20,7 @@ pub use self::NativeLibraryKind::*;
use back::svh::Svh; use back::svh::Svh;
use metadata::decoder; use metadata::decoder;
use metadata::loader; use metadata::loader;
use session::search_paths::PathKind;
use util::nodemap::{FnvHashMap, NodeMap}; use util::nodemap::{FnvHashMap, NodeMap};
use std::cell::RefCell; use std::cell::RefCell;
@ -65,8 +66,8 @@ pub enum NativeLibraryKind {
// must be non-None. // must be non-None.
#[derive(PartialEq, Clone)] #[derive(PartialEq, Clone)]
pub struct CrateSource { pub struct CrateSource {
pub dylib: Option<Path>, pub dylib: Option<(Path, PathKind)>,
pub rlib: Option<Path>, pub rlib: Option<(Path, PathKind)>,
pub cnum: ast::CrateNum, pub cnum: ast::CrateNum,
} }
@ -178,10 +179,10 @@ impl CStore {
let mut libs = self.used_crate_sources.borrow() let mut libs = self.used_crate_sources.borrow()
.iter() .iter()
.map(|src| (src.cnum, match prefer { .map(|src| (src.cnum, match prefer {
RequireDynamic => src.dylib.clone(), RequireDynamic => src.dylib.clone().map(|p| p.0),
RequireStatic => src.rlib.clone(), RequireStatic => src.rlib.clone().map(|p| p.0),
})) }))
.collect::<Vec<(ast::CrateNum, Option<Path>)>>(); .collect::<Vec<_>>();
libs.sort_by(|&(a, _), &(b, _)| { libs.sort_by(|&(a, _), &(b, _)| {
ordering.position_elem(&a).cmp(&ordering.position_elem(&b)) ordering.position_elem(&a).cmp(&ordering.position_elem(&b))
}); });

View File

@ -39,13 +39,13 @@ pub struct FileSearch<'a> {
impl<'a> FileSearch<'a> { impl<'a> FileSearch<'a> {
pub fn for_each_lib_search_path<F>(&self, mut f: F) where pub fn for_each_lib_search_path<F>(&self, mut f: F) where
F: FnMut(&Path) -> FileMatch, F: FnMut(&Path, PathKind) -> FileMatch,
{ {
let mut visited_dirs = HashSet::new(); let mut visited_dirs = HashSet::new();
let mut found = false; let mut found = false;
for path in self.search_paths.iter(self.kind) { for (path, kind) in self.search_paths.iter(self.kind) {
match f(path) { match f(path, kind) {
FileMatches => found = true, FileMatches => found = true,
FileDoesntMatch => () FileDoesntMatch => ()
} }
@ -56,7 +56,7 @@ impl<'a> FileSearch<'a> {
let tlib_path = make_target_lib_path(self.sysroot, let tlib_path = make_target_lib_path(self.sysroot,
self.triple); self.triple);
if !visited_dirs.contains(tlib_path.as_vec()) { if !visited_dirs.contains(tlib_path.as_vec()) {
match f(&tlib_path) { match f(&tlib_path, PathKind::All) {
FileMatches => found = true, FileMatches => found = true,
FileDoesntMatch => () FileDoesntMatch => ()
} }
@ -76,7 +76,7 @@ impl<'a> FileSearch<'a> {
visited_dirs.insert(tlib_path.as_vec().to_vec()); visited_dirs.insert(tlib_path.as_vec().to_vec());
// Don't keep searching the RUST_PATH if one match turns up -- // Don't keep searching the RUST_PATH if one match turns up --
// if we did, we'd get a "multiple matching crates" error // if we did, we'd get a "multiple matching crates" error
match f(&tlib_path) { match f(&tlib_path, PathKind::All) {
FileMatches => { FileMatches => {
break; break;
} }
@ -91,8 +91,10 @@ impl<'a> FileSearch<'a> {
make_target_lib_path(self.sysroot, self.triple) make_target_lib_path(self.sysroot, self.triple)
} }
pub fn search<F>(&self, mut pick: F) where F: FnMut(&Path) -> FileMatch { pub fn search<F>(&self, mut pick: F)
self.for_each_lib_search_path(|lib_search_path| { where F: FnMut(&Path, PathKind) -> FileMatch
{
self.for_each_lib_search_path(|lib_search_path, kind| {
debug!("searching {}", lib_search_path.display()); debug!("searching {}", lib_search_path.display());
match fs::readdir(lib_search_path) { match fs::readdir(lib_search_path) {
Ok(files) => { Ok(files) => {
@ -108,7 +110,7 @@ impl<'a> FileSearch<'a> {
let files2 = files.iter().filter(|p| !is_rlib(p)); let files2 = files.iter().filter(|p| !is_rlib(p));
for path in files1.chain(files2) { for path in files1.chain(files2) {
debug!("testing {}", path.display()); debug!("testing {}", path.display());
let maybe_picked = pick(path); let maybe_picked = pick(path, kind);
match maybe_picked { match maybe_picked {
FileMatches => { FileMatches => {
debug!("picked {}", path.display()); debug!("picked {}", path.display());
@ -142,7 +144,7 @@ impl<'a> FileSearch<'a> {
// Returns a list of directories where target-specific dylibs might be located. // Returns a list of directories where target-specific dylibs might be located.
pub fn get_dylib_search_paths(&self) -> Vec<Path> { pub fn get_dylib_search_paths(&self) -> Vec<Path> {
let mut paths = Vec::new(); let mut paths = Vec::new();
self.for_each_lib_search_path(|lib_search_path| { self.for_each_lib_search_path(|lib_search_path, _| {
paths.push(lib_search_path.clone()); paths.push(lib_search_path.clone());
FileDoesntMatch FileDoesntMatch
}); });

View File

@ -215,6 +215,7 @@
use back::archive::{METADATA_FILENAME}; use back::archive::{METADATA_FILENAME};
use back::svh::Svh; use back::svh::Svh;
use session::Session; use session::Session;
use session::search_paths::PathKind;
use llvm; use llvm;
use llvm::{False, ObjectFile, mk_section_iter}; use llvm::{False, ObjectFile, mk_section_iter};
use llvm::archive_ro::ArchiveRO; use llvm::archive_ro::ArchiveRO;
@ -229,7 +230,7 @@ use rustc_back::target::Target;
use std::ffi::CString; use std::ffi::CString;
use std::cmp; use std::cmp;
use std::collections::{HashMap, HashSet}; use std::collections::HashMap;
use std::io::fs::PathExtensions; use std::io::fs::PathExtensions;
use std::io; use std::io;
use std::ptr; use std::ptr;
@ -260,8 +261,8 @@ pub struct Context<'a> {
} }
pub struct Library { pub struct Library {
pub dylib: Option<Path>, pub dylib: Option<(Path, PathKind)>,
pub rlib: Option<Path>, pub rlib: Option<(Path, PathKind)>,
pub metadata: MetadataBlob, pub metadata: MetadataBlob,
} }
@ -384,7 +385,7 @@ impl<'a> Context<'a> {
// of the crate id (path/name/id). // of the crate id (path/name/id).
// //
// The goal of this step is to look at as little metadata as possible. // The goal of this step is to look at as little metadata as possible.
self.filesearch.search(|path| { self.filesearch.search(|path, kind| {
let file = match path.filename_str() { let file = match path.filename_str() {
None => return FileDoesntMatch, None => return FileDoesntMatch,
Some(file) => file, Some(file) => file,
@ -404,12 +405,12 @@ impl<'a> Context<'a> {
let hash_str = hash.to_string(); let hash_str = hash.to_string();
let slot = candidates.entry(hash_str).get().unwrap_or_else( let slot = candidates.entry(hash_str).get().unwrap_or_else(
|vacant_entry| vacant_entry.insert((HashSet::new(), HashSet::new()))); |vacant_entry| vacant_entry.insert((HashMap::new(), HashMap::new())));
let (ref mut rlibs, ref mut dylibs) = *slot; let (ref mut rlibs, ref mut dylibs) = *slot;
if rlib { if rlib {
rlibs.insert(fs::realpath(path).unwrap()); rlibs.insert(fs::realpath(path).unwrap(), kind);
} else { } else {
dylibs.insert(fs::realpath(path).unwrap()); dylibs.insert(fs::realpath(path).unwrap(), kind);
} }
FileMatches FileMatches
@ -453,16 +454,16 @@ impl<'a> Context<'a> {
self.sess.note("candidates:"); self.sess.note("candidates:");
for lib in libraries.iter() { for lib in libraries.iter() {
match lib.dylib { match lib.dylib {
Some(ref p) => { Some((ref p, _)) => {
self.sess.note(&format!("path: {}", self.sess.note(&format!("path: {}",
p.display())[]); p.display())[]);
} }
None => {} None => {}
} }
match lib.rlib { match lib.rlib {
Some(ref p) => { Some((ref p, _)) => {
self.sess.note(&format!("path: {}", self.sess.note(&format!("path: {}",
p.display())[]); p.display())[]);
} }
None => {} None => {}
} }
@ -483,9 +484,9 @@ impl<'a> Context<'a> {
// read the metadata from it if `*slot` is `None`. If the metadata couldn't // read the metadata from it if `*slot` is `None`. If the metadata couldn't
// be read, it is assumed that the file isn't a valid rust library (no // be read, it is assumed that the file isn't a valid rust library (no
// errors are emitted). // errors are emitted).
fn extract_one(&mut self, m: HashSet<Path>, flavor: &str, fn extract_one(&mut self, m: HashMap<Path, PathKind>, flavor: &str,
slot: &mut Option<MetadataBlob>) -> Option<Path> { slot: &mut Option<MetadataBlob>) -> Option<(Path, PathKind)> {
let mut ret = None::<Path>; let mut ret = None::<(Path, PathKind)>;
let mut error = 0u; let mut error = 0u;
if slot.is_some() { if slot.is_some() {
@ -500,7 +501,7 @@ impl<'a> Context<'a> {
} }
} }
for lib in m.into_iter() { for (lib, kind) in m.into_iter() {
info!("{} reading metadata from: {}", flavor, lib.display()); info!("{} reading metadata from: {}", flavor, lib.display());
let metadata = match get_metadata_section(self.target.options.is_like_osx, let metadata = match get_metadata_section(self.target.options.is_like_osx,
&lib) { &lib) {
@ -525,7 +526,7 @@ impl<'a> Context<'a> {
self.crate_name)[]); self.crate_name)[]);
self.sess.span_note(self.span, self.sess.span_note(self.span,
&format!(r"candidate #1: {}", &format!(r"candidate #1: {}",
ret.as_ref().unwrap() ret.as_ref().unwrap().0
.display())[]); .display())[]);
error = 1; error = 1;
ret = None; ret = None;
@ -538,7 +539,7 @@ impl<'a> Context<'a> {
continue continue
} }
*slot = Some(metadata); *slot = Some(metadata);
ret = Some(lib); ret = Some((lib, kind));
} }
return if error > 0 {None} else {ret} return if error > 0 {None} else {ret}
} }
@ -606,8 +607,8 @@ impl<'a> Context<'a> {
// rlibs/dylibs. // rlibs/dylibs.
let sess = self.sess; let sess = self.sess;
let dylibname = self.dylibname(); let dylibname = self.dylibname();
let mut rlibs = HashSet::new(); let mut rlibs = HashMap::new();
let mut dylibs = HashSet::new(); let mut dylibs = HashMap::new();
{ {
let mut locs = locs.iter().map(|l| Path::new(&l[])).filter(|loc| { let mut locs = locs.iter().map(|l| Path::new(&l[])).filter(|loc| {
if !loc.exists() { if !loc.exists() {
@ -637,13 +638,15 @@ impl<'a> Context<'a> {
false false
}); });
// Now that we have an iterator of good candidates, make sure there's at // Now that we have an iterator of good candidates, make sure
// most one rlib and at most one dylib. // there's at most one rlib and at most one dylib.
for loc in locs { for loc in locs {
if loc.filename_str().unwrap().ends_with(".rlib") { if loc.filename_str().unwrap().ends_with(".rlib") {
rlibs.insert(fs::realpath(&loc).unwrap()); rlibs.insert(fs::realpath(&loc).unwrap(),
PathKind::ExternFlag);
} else { } else {
dylibs.insert(fs::realpath(&loc).unwrap()); dylibs.insert(fs::realpath(&loc).unwrap(),
PathKind::ExternFlag);
} }
} }
}; };

View File

@ -25,6 +25,7 @@ pub enum PathKind {
Native, Native,
Crate, Crate,
Dependency, Dependency,
ExternFlag,
All, All,
} }
@ -54,14 +55,16 @@ impl SearchPaths {
} }
impl<'a> Iterator for Iter<'a> { impl<'a> Iterator for Iter<'a> {
type Item = &'a Path; type Item = (&'a Path, PathKind);
fn next(&mut self) -> Option<&'a Path> { fn next(&mut self) -> Option<(&'a Path, PathKind)> {
loop { loop {
match self.iter.next() { match self.iter.next() {
Some(&(kind, ref p)) if self.kind == PathKind::All || Some(&(kind, ref p)) if self.kind == PathKind::All ||
kind == PathKind::All || kind == PathKind::All ||
kind == self.kind => return Some(p), kind == self.kind => {
return Some((p, kind))
}
Some(..) => {} Some(..) => {}
None => return None, None => return None,
} }

View File

@ -507,7 +507,7 @@ fn link_binary_output(sess: &Session,
fn archive_search_paths(sess: &Session) -> Vec<Path> { fn archive_search_paths(sess: &Session) -> Vec<Path> {
let mut search = Vec::new(); let mut search = Vec::new();
sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path| { sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| {
search.push(path.clone()); search.push(path.clone());
FileDoesntMatch FileDoesntMatch
}); });
@ -1043,7 +1043,7 @@ fn link_args(cmd: &mut Command,
// in the current crate. Upstream crates with native library dependencies // in the current crate. Upstream crates with native library dependencies
// may have their native library pulled in above. // may have their native library pulled in above.
fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { fn add_local_native_libraries(cmd: &mut Command, sess: &Session) {
sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path| { sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, _| {
cmd.arg("-L").arg(path); cmd.arg("-L").arg(path);
FileDoesntMatch FileDoesntMatch
}); });
@ -1146,10 +1146,10 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session,
let src = sess.cstore.get_used_crate_source(cnum).unwrap(); let src = sess.cstore.get_used_crate_source(cnum).unwrap();
match kind { match kind {
cstore::RequireDynamic => { cstore::RequireDynamic => {
add_dynamic_crate(cmd, sess, src.dylib.unwrap()) add_dynamic_crate(cmd, sess, src.dylib.unwrap().0)
} }
cstore::RequireStatic => { cstore::RequireStatic => {
add_static_crate(cmd, sess, tmpdir, src.rlib.unwrap()) add_static_crate(cmd, sess, tmpdir, src.rlib.unwrap().0)
} }
} }

View File

@ -0,0 +1,8 @@
-include ../tools.mk
all:
mkdir -p $(TMPDIR)/a $(TMPDIR)/b
$(RUSTC) a.rs && mv $(TMPDIR)/liba.rlib $(TMPDIR)/a
$(RUSTC) b.rs -L $(TMPDIR)/a && mv $(TMPDIR)/libb.rlib $(TMPDIR)/b
$(RUSTC) c.rs -L crate=$(TMPDIR)/b -L dependency=$(TMPDIR)/a \
&& exit 1 || exit 0

View File

@ -0,0 +1,11 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "lib"]

View File

@ -0,0 +1,12 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "lib"]
extern crate a;

View File

@ -0,0 +1,13 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "lib"]
extern crate b;
extern crate a;

View File

@ -17,8 +17,10 @@ all:
$(RUSTC) -C metadata=2 -C extra-filename=-2 a.rs $(RUSTC) -C metadata=2 -C extra-filename=-2 a.rs
$(RUSTC) b.rs --extern a=$(TMPDIR)/liba-1.rlib $(RUSTC) b.rs --extern a=$(TMPDIR)/liba-1.rlib
$(RUSTC) c.rs --extern a=$(TMPDIR)/liba-2.rlib $(RUSTC) c.rs --extern a=$(TMPDIR)/liba-2.rlib
@echo before
$(RUSTC) --cfg before d.rs --extern a=$(TMPDIR)/liba-1.rlib $(RUSTC) --cfg before d.rs --extern a=$(TMPDIR)/liba-1.rlib
$(call RUN,d) $(call RUN,d)
@echo after
$(RUSTC) --cfg after d.rs --extern a=$(TMPDIR)/liba-1.rlib $(RUSTC) --cfg after d.rs --extern a=$(TMPDIR)/liba-1.rlib
$(call RUN,d) $(call RUN,d)

View File

@ -11,6 +11,6 @@
#![crate_name = "a"] #![crate_name = "a"]
#![crate_type = "rlib"] #![crate_type = "rlib"]
static FOO: uint = 3; static FOO: usize = 3;
pub fn token() -> &'static uint { &FOO } pub fn token() -> &'static usize { &FOO }

View File

@ -13,7 +13,7 @@
extern crate a; extern crate a;
static FOO: uint = 3; static FOO: usize = 3;
pub fn token() -> &'static uint { &FOO } pub fn token() -> &'static usize { &FOO }
pub fn a_token() -> &'static uint { a::token() } pub fn a_token() -> &'static usize { a::token() }

View File

@ -13,7 +13,7 @@
extern crate a; extern crate a;
static FOO: uint = 3; static FOO: usize = 3;
pub fn token() -> &'static uint { &FOO } pub fn token() -> &'static usize { &FOO }
pub fn a_token() -> &'static uint { a::token() } pub fn a_token() -> &'static usize { a::token() }

View File

@ -13,7 +13,7 @@ extern crate b;
extern crate c; extern crate c;
#[cfg(after)] extern crate a; #[cfg(after)] extern crate a;
fn t(a: &'static uint) -> uint { a as *const _ as uint } fn t(a: &'static usize) -> usize { a as *const _ as usize }
fn main() { fn main() {
assert!(t(a::token()) == t(b::a_token())); assert!(t(a::token()) == t(b::a_token()));