Skip to content

Commit 08a72d2

Browse files
committed
Add a testing mechanism and a simple spike test
1 parent 58d4b8e commit 08a72d2

File tree

7 files changed

+360
-0
lines changed

7 files changed

+360
-0
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! This pass is only used for UNIT TESTS related to incremental
12+
//! compilation. It tests whether a particular `.o` file will be re-used
13+
//! from a previous compilation or whether it must be regenerated.
14+
//!
15+
//! The user adds annotations to the crate of the following form:
16+
//!
17+
//! ```
18+
//! #![rustc_partition_reused(module="spike", cfg="rpass2")]
19+
//! #![rustc_partition_translated(module="spike-x", cfg="rpass2")]
20+
//! ```
21+
//!
22+
//! The first indicates (in the cfg `rpass2`) that `spike.o` will be
23+
//! reused, the second that `spike-x.o` will be recreated. If these
24+
//! annotations are inaccurate, errors are reported.
25+
//!
26+
//! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that
27+
//! the HIR doesn't change as a result of the annotations, which might
28+
//! perturb the reuse results.
29+
30+
use rustc::ty::TyCtxt;
31+
use syntax::ast;
32+
use syntax::attr::AttrMetaMethods;
33+
use syntax::parse::token::InternedString;
34+
35+
use {ModuleSource, ModuleTranslation};
36+
37+
const PARTITION_REUSED: &'static str = "rustc_partition_reused";
38+
const PARTITION_TRANSLATED: &'static str = "rustc_partition_translated";
39+
40+
const MODULE: &'static str = "module";
41+
const CFG: &'static str = "cfg";
42+
43+
#[derive(Debug, PartialEq)]
44+
enum Disposition { Reused, Translated }
45+
46+
pub fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
47+
modules: &[ModuleTranslation]) {
48+
let _ignore = tcx.dep_graph.in_ignore();
49+
50+
if tcx.sess.opts.incremental.is_none() {
51+
return;
52+
}
53+
54+
let ams = AssertModuleSource { tcx: tcx, modules: modules };
55+
for attr in &tcx.map.krate().attrs {
56+
ams.check_attr(attr);
57+
}
58+
}
59+
60+
struct AssertModuleSource<'a, 'tcx: 'a> {
61+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
62+
modules: &'a [ModuleTranslation],
63+
}
64+
65+
impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> {
66+
fn check_attr(&self, attr: &ast::Attribute) {
67+
let disposition = if attr.check_name(PARTITION_REUSED) {
68+
Disposition::Reused
69+
} else if attr.check_name(PARTITION_TRANSLATED) {
70+
Disposition::Translated
71+
} else {
72+
return;
73+
};
74+
75+
if !self.check_config(attr) {
76+
debug!("check_attr: config does not match, ignoring attr");
77+
return;
78+
}
79+
80+
let mname = self.field(attr, MODULE);
81+
let mtrans = self.modules.iter().find(|mtrans| &mtrans.name[..] == &mname[..]);
82+
let mtrans = match mtrans {
83+
Some(m) => m,
84+
None => {
85+
debug!("module name `{}` not found amongst:", mname);
86+
for mtrans in self.modules {
87+
debug!("module named `{}` with disposition {:?}",
88+
mtrans.name,
89+
self.disposition(mtrans));
90+
}
91+
92+
self.tcx.sess.span_err(
93+
attr.span,
94+
&format!("no module named `{}`", mname));
95+
return;
96+
}
97+
};
98+
99+
let mtrans_disposition = self.disposition(mtrans);
100+
if disposition != mtrans_disposition {
101+
self.tcx.sess.span_err(
102+
attr.span,
103+
&format!("expected module named `{}` to be {:?} but is {:?}",
104+
mname,
105+
disposition,
106+
mtrans_disposition));
107+
}
108+
}
109+
110+
fn disposition(&self, mtrans: &ModuleTranslation) -> Disposition {
111+
match mtrans.source {
112+
ModuleSource::Preexisting(_) => Disposition::Reused,
113+
ModuleSource::Translated(_) => Disposition::Translated,
114+
}
115+
}
116+
117+
fn field(&self, attr: &ast::Attribute, name: &str) -> InternedString {
118+
for item in attr.meta_item_list().unwrap_or(&[]) {
119+
if item.check_name(name) {
120+
if let Some(value) = item.value_str() {
121+
return value;
122+
} else {
123+
self.tcx.sess.span_fatal(
124+
item.span,
125+
&format!("associated value expected for `{}`", name));
126+
}
127+
}
128+
}
129+
130+
self.tcx.sess.span_fatal(
131+
attr.span,
132+
&format!("no field `{}`", name));
133+
}
134+
135+
/// Scan for a `cfg="foo"` attribute and check whether we have a
136+
/// cfg flag called `foo`.
137+
fn check_config(&self, attr: &ast::Attribute) -> bool {
138+
let config = &self.tcx.map.krate().config;
139+
let value = self.field(attr, CFG);
140+
debug!("check_config(config={:?}, value={:?})", config, value);
141+
if config.iter().any(|c| c.check_name(&value[..])) {
142+
debug!("check_config: matched");
143+
return true;
144+
}
145+
debug!("check_config: no match found");
146+
return false;
147+
}
148+
149+
}

src/librustc_trans/base.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use super::ModuleLlvm;
3030
use super::ModuleSource;
3131
use super::ModuleTranslation;
3232

33+
use assert_module_sources;
3334
use back::link;
3435
use back::linker::LinkerInfo;
3536
use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
@@ -2558,6 +2559,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
25582559
})
25592560
.collect();
25602561

2562+
assert_module_sources::assert_module_sources(tcx, &modules);
2563+
25612564
// Skip crate items and just output metadata in -Z no-trans mode.
25622565
if tcx.sess.opts.no_trans {
25632566
let linker_info = LinkerInfo::new(&shared_ccx, &[]);

src/librustc_trans/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ mod macros;
8888
mod abi;
8989
mod adt;
9090
mod asm;
91+
mod assert_module_sources;
9192
mod attributes;
9293
mod base;
9394
mod basic_block;

src/libsyntax/feature_gate.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,16 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
481481
is just used for rustc unit tests \
482482
and will never be stable",
483483
cfg_fn!(rustc_attrs))),
484+
("rustc_partition_reused", Whitelisted, Gated("rustc_attrs",
485+
"this attribute \
486+
is just used for rustc unit tests \
487+
and will never be stable",
488+
cfg_fn!(rustc_attrs))),
489+
("rustc_partition_translated", Whitelisted, Gated("rustc_attrs",
490+
"this attribute \
491+
is just used for rustc unit tests \
492+
and will never be stable",
493+
cfg_fn!(rustc_attrs))),
484494
("rustc_symbol_name", Whitelisted, Gated("rustc_attrs",
485495
"internal rustc attributes will never be stable",
486496
cfg_fn!(rustc_attrs))),

src/test/incremental/spike-neg1.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// A variant of the first "spike" test that serves to test the
12+
// `rustc_partition_reused` and `rustc_partition_translated` tests.
13+
// Here we change and say that the `x` module will be reused (when in
14+
// fact it will not), and then indicate that the test itself
15+
// should-fail (because an error will be reported, and hence the
16+
// revision rpass2 will not compile, despite being named rpass).
17+
18+
// revisions:rpass1 rpass2
19+
// should-fail
20+
21+
#![feature(rustc_attrs)]
22+
23+
#![rustc_partition_reused(module="spike_neg1", cfg="rpass2")]
24+
#![rustc_partition_reused(module="spike_neg1-x", cfg="rpass2")] // this is wrong!
25+
#![rustc_partition_reused(module="spike_neg1-y", cfg="rpass2")]
26+
27+
mod x {
28+
pub struct X {
29+
x: u32, y: u32,
30+
}
31+
32+
#[cfg(rpass1)]
33+
fn make() -> X {
34+
X { x: 22, y: 0 }
35+
}
36+
37+
#[cfg(rpass2)]
38+
fn make() -> X {
39+
X { x: 11, y: 11 }
40+
}
41+
42+
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
43+
#[rustc_clean(label="ItemSignature", cfg="rpass2")]
44+
pub fn new() -> X {
45+
make()
46+
}
47+
48+
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
49+
#[rustc_clean(label="ItemSignature", cfg="rpass2")]
50+
pub fn sum(x: &X) -> u32 {
51+
x.x + x.y
52+
}
53+
}
54+
55+
mod y {
56+
use x;
57+
58+
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
59+
pub fn assert_sum() -> bool {
60+
let x = x::new();
61+
x::sum(&x) == 22
62+
}
63+
}
64+
65+
pub fn main() {
66+
y::assert_sum();
67+
}

src/test/incremental/spike-neg2.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// A variant of the first "spike" test that serves to test the
12+
// `rustc_partition_reused` and `rustc_partition_translated` tests.
13+
// Here we change and say that the `y` module will be translated (when
14+
// in fact it will not), and then indicate that the test itself
15+
// should-fail (because an error will be reported, and hence the
16+
// revision rpass2 will not compile, despite being named rpass).
17+
18+
// revisions:rpass1 rpass2
19+
// should-fail
20+
21+
#![feature(rustc_attrs)]
22+
23+
#![rustc_partition_reused(module="spike_neg2", cfg="rpass2")]
24+
#![rustc_partition_translated(module="spike_neg2-x", cfg="rpass2")]
25+
#![rustc_partition_translated(module="spike_neg2-y", cfg="rpass2")] // this is wrong!
26+
27+
mod x {
28+
pub struct X {
29+
x: u32, y: u32,
30+
}
31+
32+
#[cfg(rpass1)]
33+
fn make() -> X {
34+
X { x: 22, y: 0 }
35+
}
36+
37+
#[cfg(rpass2)]
38+
fn make() -> X {
39+
X { x: 11, y: 11 }
40+
}
41+
42+
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
43+
#[rustc_clean(label="ItemSignature", cfg="rpass2")]
44+
pub fn new() -> X {
45+
make()
46+
}
47+
48+
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
49+
#[rustc_clean(label="ItemSignature", cfg="rpass2")]
50+
pub fn sum(x: &X) -> u32 {
51+
x.x + x.y
52+
}
53+
}
54+
55+
mod y {
56+
use x;
57+
58+
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
59+
pub fn assert_sum() -> bool {
60+
let x = x::new();
61+
x::sum(&x) == 22
62+
}
63+
}
64+
65+
pub fn main() {
66+
y::assert_sum();
67+
}

src/test/incremental/spike.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// A first "spike" for incremental compilation: here, we change the
12+
// content of the `make` function, and we find that we can reuse the
13+
// `y` module entirely (but not the `x` module).
14+
15+
// revisions:rpass1 rpass2
16+
17+
#![feature(rustc_attrs)]
18+
19+
#![rustc_partition_reused(module="spike", cfg="rpass2")]
20+
#![rustc_partition_translated(module="spike-x", cfg="rpass2")]
21+
#![rustc_partition_reused(module="spike-y", cfg="rpass2")]
22+
23+
mod x {
24+
pub struct X {
25+
x: u32, y: u32,
26+
}
27+
28+
#[cfg(rpass1)]
29+
fn make() -> X {
30+
X { x: 22, y: 0 }
31+
}
32+
33+
#[cfg(rpass2)]
34+
fn make() -> X {
35+
X { x: 11, y: 11 }
36+
}
37+
38+
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
39+
#[rustc_clean(label="ItemSignature", cfg="rpass2")]
40+
pub fn new() -> X {
41+
make()
42+
}
43+
44+
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
45+
#[rustc_clean(label="ItemSignature", cfg="rpass2")]
46+
pub fn sum(x: &X) -> u32 {
47+
x.x + x.y
48+
}
49+
}
50+
51+
mod y {
52+
use x;
53+
54+
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
55+
pub fn assert_sum() -> bool {
56+
let x = x::new();
57+
x::sum(&x) == 22
58+
}
59+
}
60+
61+
pub fn main() {
62+
y::assert_sum();
63+
}

0 commit comments

Comments
 (0)