rustc: Fix a leak in dependency= paths

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.

As a result of this fix, this is a likely a breaking change for a number of
Cargo packages. If the compiler starts informing that a crate can no longer be
found, it likely means that the dependency was forgotten in your Cargo.toml.

[breaking-change]
This commit is contained in:
Alex Crichton 2015-01-06 08:46:07 -08:00
parent 6ba9acd8ab
commit cbeb77ec7a
15 changed files with 145 additions and 78 deletions

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) b.rs --extern a=$(TMPDIR)/liba-1.rlib
$(RUSTC) c.rs --extern a=$(TMPDIR)/liba-2.rlib
@echo before
$(RUSTC) --cfg before d.rs --extern a=$(TMPDIR)/liba-1.rlib
$(call RUN,d)
@echo after
$(RUSTC) --cfg after d.rs --extern a=$(TMPDIR)/liba-1.rlib
$(call RUN,d)

View file

@ -11,6 +11,6 @@
#![crate_name = "a"]
#![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;
static FOO: uint = 3;
static FOO: usize = 3;
pub fn token() -> &'static uint { &FOO }
pub fn a_token() -> &'static uint { a::token() }
pub fn token() -> &'static usize { &FOO }
pub fn a_token() -> &'static usize { a::token() }

View file

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

View file

@ -13,7 +13,7 @@ extern crate b;
extern crate c;
#[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() {
assert!(t(a::token()) == t(b::a_token()));