about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2016-07-21 12:50:15 -0400
committerNiko Matsakis <niko@alum.mit.edu>2016-07-28 12:05:45 -0400
commit08a72d20c9c97ab7b1d3b023d6cc0caddcd12f77 (patch)
treee1cc694b31d5946372440ea3f7ac8cb059103e4d
parent58d4b8edd319d0f0d76024504cdfc74f89a001b1 (diff)
downloadrust-08a72d20c9c97ab7b1d3b023d6cc0caddcd12f77.tar.gz
rust-08a72d20c9c97ab7b1d3b023d6cc0caddcd12f77.zip
Add a testing mechanism and a simple spike test
-rw-r--r--src/librustc_trans/assert_module_sources.rs149
-rw-r--r--src/librustc_trans/base.rs3
-rw-r--r--src/librustc_trans/lib.rs1
-rw-r--r--src/libsyntax/feature_gate.rs10
-rw-r--r--src/test/incremental/spike-neg1.rs67
-rw-r--r--src/test/incremental/spike-neg2.rs67
-rw-r--r--src/test/incremental/spike.rs63
7 files changed, 360 insertions, 0 deletions
diff --git a/src/librustc_trans/assert_module_sources.rs b/src/librustc_trans/assert_module_sources.rs
new file mode 100644
index 00000000000..e0532e7476f
--- /dev/null
+++ b/src/librustc_trans/assert_module_sources.rs
@@ -0,0 +1,149 @@
+// Copyright 2012-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.
+
+//! This pass is only used for UNIT TESTS related to incremental
+//! compilation. It tests whether a particular `.o` file will be re-used
+//! from a previous compilation or whether it must be regenerated.
+//!
+//! The user adds annotations to the crate of the following form:
+//!
+//! ```
+//! #![rustc_partition_reused(module="spike", cfg="rpass2")]
+//! #![rustc_partition_translated(module="spike-x", cfg="rpass2")]
+//! ```
+//!
+//! The first indicates (in the cfg `rpass2`) that `spike.o` will be
+//! reused, the second that `spike-x.o` will be recreated. If these
+//! annotations are inaccurate, errors are reported.
+//!
+//! The reason that we use `cfg=...` and not `#[cfg_attr]` is so that
+//! the HIR doesn't change as a result of the annotations, which might
+//! perturb the reuse results.
+
+use rustc::ty::TyCtxt;
+use syntax::ast;
+use syntax::attr::AttrMetaMethods;
+use syntax::parse::token::InternedString;
+
+use {ModuleSource, ModuleTranslation};
+
+const PARTITION_REUSED: &'static str = "rustc_partition_reused";
+const PARTITION_TRANSLATED: &'static str = "rustc_partition_translated";
+
+const MODULE: &'static str = "module";
+const CFG: &'static str = "cfg";
+
+#[derive(Debug, PartialEq)]
+enum Disposition { Reused, Translated }
+
+pub fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                       modules: &[ModuleTranslation]) {
+    let _ignore = tcx.dep_graph.in_ignore();
+
+    if tcx.sess.opts.incremental.is_none() {
+        return;
+    }
+
+    let ams = AssertModuleSource { tcx: tcx, modules: modules };
+    for attr in &tcx.map.krate().attrs {
+        ams.check_attr(attr);
+    }
+}
+
+struct AssertModuleSource<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    modules: &'a [ModuleTranslation],
+}
+
+impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> {
+    fn check_attr(&self, attr: &ast::Attribute) {
+        let disposition = if attr.check_name(PARTITION_REUSED) {
+            Disposition::Reused
+        } else if attr.check_name(PARTITION_TRANSLATED) {
+            Disposition::Translated
+        } else {
+            return;
+        };
+
+        if !self.check_config(attr) {
+            debug!("check_attr: config does not match, ignoring attr");
+            return;
+        }
+
+        let mname = self.field(attr, MODULE);
+        let mtrans = self.modules.iter().find(|mtrans| &mtrans.name[..] == &mname[..]);
+        let mtrans = match mtrans {
+            Some(m) => m,
+            None => {
+                debug!("module name `{}` not found amongst:", mname);
+                for mtrans in self.modules {
+                    debug!("module named `{}` with disposition {:?}",
+                           mtrans.name,
+                           self.disposition(mtrans));
+                }
+
+                self.tcx.sess.span_err(
+                    attr.span,
+                    &format!("no module named `{}`", mname));
+                return;
+            }
+        };
+
+        let mtrans_disposition = self.disposition(mtrans);
+        if disposition != mtrans_disposition {
+            self.tcx.sess.span_err(
+                attr.span,
+                &format!("expected module named `{}` to be {:?} but is {:?}",
+                         mname,
+                         disposition,
+                         mtrans_disposition));
+        }
+    }
+
+    fn disposition(&self, mtrans: &ModuleTranslation) -> Disposition {
+        match mtrans.source {
+            ModuleSource::Preexisting(_) => Disposition::Reused,
+            ModuleSource::Translated(_) => Disposition::Translated,
+        }
+    }
+
+    fn field(&self, attr: &ast::Attribute, name: &str) -> InternedString {
+        for item in attr.meta_item_list().unwrap_or(&[]) {
+            if item.check_name(name) {
+                if let Some(value) = item.value_str() {
+                    return value;
+                } else {
+                    self.tcx.sess.span_fatal(
+                        item.span,
+                        &format!("associated value expected for `{}`", name));
+                }
+            }
+        }
+
+        self.tcx.sess.span_fatal(
+            attr.span,
+            &format!("no field `{}`", name));
+    }
+
+    /// Scan for a `cfg="foo"` attribute and check whether we have a
+    /// cfg flag called `foo`.
+    fn check_config(&self, attr: &ast::Attribute) -> bool {
+        let config = &self.tcx.map.krate().config;
+        let value = self.field(attr, CFG);
+        debug!("check_config(config={:?}, value={:?})", config, value);
+        if config.iter().any(|c| c.check_name(&value[..])) {
+            debug!("check_config: matched");
+            return true;
+        }
+        debug!("check_config: no match found");
+        return false;
+    }
+
+}
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index a77ababaa63..69a88443135 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -30,6 +30,7 @@ use super::ModuleLlvm;
 use super::ModuleSource;
 use super::ModuleTranslation;
 
+use assert_module_sources;
 use back::link;
 use back::linker::LinkerInfo;
 use llvm::{BasicBlockRef, Linkage, ValueRef, Vector, get_param};
@@ -2558,6 +2559,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         })
         .collect();
 
+    assert_module_sources::assert_module_sources(tcx, &modules);
+
     // Skip crate items and just output metadata in -Z no-trans mode.
     if tcx.sess.opts.no_trans {
         let linker_info = LinkerInfo::new(&shared_ccx, &[]);
diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs
index 6ac64d32910..67475081cae 100644
--- a/src/librustc_trans/lib.rs
+++ b/src/librustc_trans/lib.rs
@@ -88,6 +88,7 @@ mod macros;
 mod abi;
 mod adt;
 mod asm;
+mod assert_module_sources;
 mod attributes;
 mod base;
 mod basic_block;
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 27485ee65fc..f80f25a2e77 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -481,6 +481,16 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
                                         is just used for rustc unit tests \
                                         and will never be stable",
                                        cfg_fn!(rustc_attrs))),
+    ("rustc_partition_reused", Whitelisted, Gated("rustc_attrs",
+                                                  "this attribute \
+                                                   is just used for rustc unit tests \
+                                                   and will never be stable",
+                                                  cfg_fn!(rustc_attrs))),
+    ("rustc_partition_translated", Whitelisted, Gated("rustc_attrs",
+                                                      "this attribute \
+                                                       is just used for rustc unit tests \
+                                                       and will never be stable",
+                                                      cfg_fn!(rustc_attrs))),
     ("rustc_symbol_name", Whitelisted, Gated("rustc_attrs",
                                              "internal rustc attributes will never be stable",
                                              cfg_fn!(rustc_attrs))),
diff --git a/src/test/incremental/spike-neg1.rs b/src/test/incremental/spike-neg1.rs
new file mode 100644
index 00000000000..e84906d12d0
--- /dev/null
+++ b/src/test/incremental/spike-neg1.rs
@@ -0,0 +1,67 @@
+// Copyright 2014 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.
+
+// A variant of the first "spike" test that serves to test the
+// `rustc_partition_reused` and `rustc_partition_translated` tests.
+// Here we change and say that the `x` module will be reused (when in
+// fact it will not), and then indicate that the test itself
+// should-fail (because an error will be reported, and hence the
+// revision rpass2 will not compile, despite being named rpass).
+
+// revisions:rpass1 rpass2
+// should-fail
+
+#![feature(rustc_attrs)]
+
+#![rustc_partition_reused(module="spike_neg1", cfg="rpass2")]
+#![rustc_partition_reused(module="spike_neg1-x", cfg="rpass2")] // this is wrong!
+#![rustc_partition_reused(module="spike_neg1-y", cfg="rpass2")]
+
+mod x {
+    pub struct X {
+        x: u32, y: u32,
+    }
+
+    #[cfg(rpass1)]
+    fn make() -> X {
+        X { x: 22, y: 0 }
+    }
+
+    #[cfg(rpass2)]
+    fn make() -> X {
+        X { x: 11, y: 11 }
+    }
+
+    #[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+    #[rustc_clean(label="ItemSignature", cfg="rpass2")]
+    pub fn new() -> X {
+        make()
+    }
+
+    #[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+    #[rustc_clean(label="ItemSignature", cfg="rpass2")]
+    pub fn sum(x: &X) -> u32 {
+        x.x + x.y
+    }
+}
+
+mod y {
+    use x;
+
+    #[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+    pub fn assert_sum() -> bool {
+        let x = x::new();
+        x::sum(&x) == 22
+    }
+}
+
+pub fn main() {
+    y::assert_sum();
+}
diff --git a/src/test/incremental/spike-neg2.rs b/src/test/incremental/spike-neg2.rs
new file mode 100644
index 00000000000..40f4b4f0c44
--- /dev/null
+++ b/src/test/incremental/spike-neg2.rs
@@ -0,0 +1,67 @@
+// Copyright 2014 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.
+
+// A variant of the first "spike" test that serves to test the
+// `rustc_partition_reused` and `rustc_partition_translated` tests.
+// Here we change and say that the `y` module will be translated (when
+// in fact it will not), and then indicate that the test itself
+// should-fail (because an error will be reported, and hence the
+// revision rpass2 will not compile, despite being named rpass).
+
+// revisions:rpass1 rpass2
+// should-fail
+
+#![feature(rustc_attrs)]
+
+#![rustc_partition_reused(module="spike_neg2", cfg="rpass2")]
+#![rustc_partition_translated(module="spike_neg2-x", cfg="rpass2")]
+#![rustc_partition_translated(module="spike_neg2-y", cfg="rpass2")] // this is wrong!
+
+mod x {
+    pub struct X {
+        x: u32, y: u32,
+    }
+
+    #[cfg(rpass1)]
+    fn make() -> X {
+        X { x: 22, y: 0 }
+    }
+
+    #[cfg(rpass2)]
+    fn make() -> X {
+        X { x: 11, y: 11 }
+    }
+
+    #[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+    #[rustc_clean(label="ItemSignature", cfg="rpass2")]
+    pub fn new() -> X {
+        make()
+    }
+
+    #[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+    #[rustc_clean(label="ItemSignature", cfg="rpass2")]
+    pub fn sum(x: &X) -> u32 {
+        x.x + x.y
+    }
+}
+
+mod y {
+    use x;
+
+    #[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+    pub fn assert_sum() -> bool {
+        let x = x::new();
+        x::sum(&x) == 22
+    }
+}
+
+pub fn main() {
+    y::assert_sum();
+}
diff --git a/src/test/incremental/spike.rs b/src/test/incremental/spike.rs
new file mode 100644
index 00000000000..68af20d4191
--- /dev/null
+++ b/src/test/incremental/spike.rs
@@ -0,0 +1,63 @@
+// Copyright 2014 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.
+
+// A first "spike" for incremental compilation: here, we change the
+// content of the `make` function, and we find that we can reuse the
+// `y` module entirely (but not the `x` module).
+
+// revisions:rpass1 rpass2
+
+#![feature(rustc_attrs)]
+
+#![rustc_partition_reused(module="spike", cfg="rpass2")]
+#![rustc_partition_translated(module="spike-x", cfg="rpass2")]
+#![rustc_partition_reused(module="spike-y", cfg="rpass2")]
+
+mod x {
+    pub struct X {
+        x: u32, y: u32,
+    }
+
+    #[cfg(rpass1)]
+    fn make() -> X {
+        X { x: 22, y: 0 }
+    }
+
+    #[cfg(rpass2)]
+    fn make() -> X {
+        X { x: 11, y: 11 }
+    }
+
+    #[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
+    #[rustc_clean(label="ItemSignature", cfg="rpass2")]
+    pub fn new() -> X {
+        make()
+    }
+
+    #[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+    #[rustc_clean(label="ItemSignature", cfg="rpass2")]
+    pub fn sum(x: &X) -> u32 {
+        x.x + x.y
+    }
+}
+
+mod y {
+    use x;
+
+    #[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
+    pub fn assert_sum() -> bool {
+        let x = x::new();
+        x::sum(&x) == 22
+    }
+}
+
+pub fn main() {
+    y::assert_sum();
+}