From fe47ca0d0b04f1cd46195b19cbb42602319aaf98 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 28 Mar 2016 17:43:36 -0400 Subject: [PATCH 01/20] restructure rustc options relating to incr. comp. You can now pass `-Z incremental=dir` as well as saying `-Z query-dep-graph` if you want to enable queries for some other purpose. Accessor functions take the place of computed boolean flags. --- src/librustc/session/config.rs | 31 ++++++++++++------- src/librustc_driver/driver.rs | 2 +- .../dep-graph-assoc-type-trans.rs | 2 +- .../compile-fail/dep-graph-caller-callee.rs | 2 +- .../dep-graph-struct-signature.rs | 2 +- ...graph-trait-impl-two-traits-same-method.rs | 2 +- .../dep-graph-trait-impl-two-traits.rs | 2 +- src/test/compile-fail/dep-graph-trait-impl.rs | 2 +- src/test/compile-fail/dep-graph-unrelated.rs | 2 +- src/test/run-make/execution-engine/test.rs | 2 +- 10 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 4d4ba50bd4a79..681a6ba8eb48d 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -139,11 +139,9 @@ pub struct Options { pub continue_parse_after_error: bool, pub mir_opt_level: usize, - /// if true, build up the dep-graph - pub build_dep_graph: bool, - - /// if true, -Z dump-dep-graph was passed to dump out the dep-graph - pub dump_dep_graph: bool, + /// if Some, enable incremental compilation, using the given + /// directory to store intermediate results + pub incremental: Option, pub no_analysis: bool, pub debugging_opts: DebuggingOptions, @@ -260,8 +258,7 @@ pub fn basic_options() -> Options { treat_err_as_bug: false, continue_parse_after_error: false, mir_opt_level: 1, - build_dep_graph: false, - dump_dep_graph: false, + incremental: None, no_analysis: false, debugging_opts: basic_debugging_options(), prints: Vec::new(), @@ -276,6 +273,15 @@ pub fn basic_options() -> Options { } } +impl Options { + /// True if there is a reason to build the dep graph. + pub fn build_dep_graph(&self) -> bool { + self.incremental.is_some() || + self.debugging_opts.dump_dep_graph || + self.debugging_opts.query_dep_graph + } +} + // The type of entry function, so // users can have their own entry // functions that don't start a @@ -635,10 +641,12 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "treat all errors that occur as bugs"), continue_parse_after_error: bool = (false, parse_bool, "attempt to recover from parse errors (experimental)"), - incr_comp: bool = (false, parse_bool, + incremental: Option = (None, parse_opt_string, "enable incremental compilation (experimental)"), dump_dep_graph: bool = (false, parse_bool, "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), + query_dep_graph: bool = (false, parse_bool, + "enable queries of the dependency graph for regression testing"), no_analysis: bool = (false, parse_bool, "parse and expand the source, but run no analysis"), extra_plugins: Vec = (Vec::new(), parse_list, @@ -1051,8 +1059,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let treat_err_as_bug = debugging_opts.treat_err_as_bug; let continue_parse_after_error = debugging_opts.continue_parse_after_error; let mir_opt_level = debugging_opts.mir_opt_level.unwrap_or(1); - let incremental_compilation = debugging_opts.incr_comp; - let dump_dep_graph = debugging_opts.dump_dep_graph; let no_analysis = debugging_opts.no_analysis; let mut output_types = HashMap::new(); @@ -1212,6 +1218,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let crate_name = matches.opt_str("crate-name"); + let incremental = debugging_opts.incremental.as_ref().map(|m| PathBuf::from(m)); + Options { crate_types: crate_types, gc: gc, @@ -1231,8 +1239,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { treat_err_as_bug: treat_err_as_bug, continue_parse_after_error: continue_parse_after_error, mir_opt_level: mir_opt_level, - build_dep_graph: incremental_compilation || dump_dep_graph, - dump_dep_graph: dump_dep_graph, + incremental: incremental, no_analysis: no_analysis, debugging_opts: debugging_opts, prints: prints, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f661b2a38b655..a4d34aaf6529e 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -121,7 +121,7 @@ pub fn compile_input(sess: &Session, let expanded_crate = assign_node_ids(sess, expanded_crate); // Lower ast -> hir. let lcx = LoweringContext::new(sess, Some(&expanded_crate)); - let dep_graph = DepGraph::new(sess.opts.build_dep_graph); + let dep_graph = DepGraph::new(sess.opts.build_dep_graph()); let mut hir_forest = time(sess.time_passes(), "lowering ast -> hir", || hir_map::Forest::new(lower_crate(&lcx, &expanded_crate), diff --git a/src/test/compile-fail/dep-graph-assoc-type-trans.rs b/src/test/compile-fail/dep-graph-assoc-type-trans.rs index d1ecff5984ac5..b56be9e5683a8 100644 --- a/src/test/compile-fail/dep-graph-assoc-type-trans.rs +++ b/src/test/compile-fail/dep-graph-assoc-type-trans.rs @@ -11,7 +11,7 @@ // Test that when a trait impl changes, fns whose body uses that trait // must also be recompiled. -// compile-flags: -Z incr-comp +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![allow(warnings)] diff --git a/src/test/compile-fail/dep-graph-caller-callee.rs b/src/test/compile-fail/dep-graph-caller-callee.rs index acd6091cbdd48..0d6954ab9df62 100644 --- a/src/test/compile-fail/dep-graph-caller-callee.rs +++ b/src/test/compile-fail/dep-graph-caller-callee.rs @@ -11,7 +11,7 @@ // Test that immediate callers have to change when callee changes, but // not callers' callers. -// compile-flags: -Z incr-comp +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/compile-fail/dep-graph-struct-signature.rs b/src/test/compile-fail/dep-graph-struct-signature.rs index c16998cd33c64..235fc5051d780 100644 --- a/src/test/compile-fail/dep-graph-struct-signature.rs +++ b/src/test/compile-fail/dep-graph-struct-signature.rs @@ -11,7 +11,7 @@ // Test cases where a changing struct appears in the signature of fns // and methods. -// compile-flags: -Z incr-comp +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs b/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs index 57e83586d8d37..1afecd80ff5ad 100644 --- a/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs +++ b/src/test/compile-fail/dep-graph-trait-impl-two-traits-same-method.rs @@ -11,7 +11,7 @@ // Test that adding an impl to a trait `Foo` DOES affect functions // that only use `Bar` if they have methods in common. -// compile-flags: -Z incr-comp +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs b/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs index ba54a05620945..2ec7573cb81dc 100644 --- a/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs +++ b/src/test/compile-fail/dep-graph-trait-impl-two-traits.rs @@ -11,7 +11,7 @@ // Test that adding an impl to a trait `Foo` does not affect functions // that only use `Bar`, so long as they do not have methods in common. -// compile-flags: -Z incr-comp +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![allow(warnings)] diff --git a/src/test/compile-fail/dep-graph-trait-impl.rs b/src/test/compile-fail/dep-graph-trait-impl.rs index b38fdad9809ed..d87d7a6be1c28 100644 --- a/src/test/compile-fail/dep-graph-trait-impl.rs +++ b/src/test/compile-fail/dep-graph-trait-impl.rs @@ -11,7 +11,7 @@ // Test that when a trait impl changes, fns whose body uses that trait // must also be recompiled. -// compile-flags: -Z incr-comp +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![allow(warnings)] diff --git a/src/test/compile-fail/dep-graph-unrelated.rs b/src/test/compile-fail/dep-graph-unrelated.rs index 8feec12a2f76c..079f2b52fd462 100644 --- a/src/test/compile-fail/dep-graph-unrelated.rs +++ b/src/test/compile-fail/dep-graph-unrelated.rs @@ -10,7 +10,7 @@ // Test that two unrelated functions have no trans dependency. -// compile-flags: -Z incr-comp +// compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] #![allow(dead_code)] diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs index 91b4f5f30ad0d..12cc475f121f6 100644 --- a/src/test/run-make/execution-engine/test.rs +++ b/src/test/run-make/execution-engine/test.rs @@ -239,7 +239,7 @@ fn compile_program(input: &str, sysroot: PathBuf) let krate = driver::assign_node_ids(&sess, krate); let lcx = LoweringContext::new(&sess, Some(&krate)); - let dep_graph = DepGraph::new(sess.opts.build_dep_graph); + let dep_graph = DepGraph::new(sess.opts.build_dep_graph()); let mut hir_forest = ast_map::Forest::new(lower_crate(&lcx, &krate), dep_graph); let arenas = ty::CtxtArenas::new(); let ast_map = driver::make_map(&sess, &mut hir_forest); From b1e68b9e2d75e66bc866a194b744ddf8502ca129 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 28 Mar 2016 17:36:56 -0400 Subject: [PATCH 02/20] make an incremental crate for now, this houses `svh` and the code to check `assert_dep_graph` is sane --- mk/crates.mk | 5 +- src/librustc/hir/svh.rs | 374 +--------------- src/librustc/lib.rs | 1 + src/librustc/middle/cstore.rs | 1 + src/librustc_driver/Cargo.toml | 1 + src/librustc_driver/driver.rs | 12 +- src/librustc_driver/lib.rs | 1 + src/librustc_incremental/Cargo.toml | 17 + .../assert_dep_graph.rs | 130 +++--- src/librustc_incremental/calculate_svh.rs | 420 ++++++++++++++++++ src/librustc_incremental/lib.rs | 41 ++ src/librustc_metadata/decoder.rs | 6 +- src/librustc_trans/Cargo.toml | 1 + src/librustc_trans/back/link.rs | 9 +- src/librustc_trans/base.rs | 5 +- src/librustc_trans/lib.rs | 2 +- 16 files changed, 584 insertions(+), 442 deletions(-) create mode 100644 src/librustc_incremental/Cargo.toml rename src/{librustc_trans => librustc_incremental}/assert_dep_graph.rs (78%) create mode 100644 src/librustc_incremental/calculate_svh.rs create mode 100644 src/librustc_incremental/lib.rs diff --git a/mk/crates.mk b/mk/crates.mk index 4c06afcae0c42..84452945f2706 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -58,7 +58,7 @@ RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_ rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \ rustc_data_structures rustc_platform_intrinsics \ rustc_plugin rustc_metadata rustc_passes rustc_save_analysis \ - rustc_const_eval rustc_const_math + rustc_const_eval rustc_const_math rustc_incremental HOST_CRATES := syntax syntax_ext $(RUSTC_CRATES) rustdoc fmt_macros \ flate arena graphviz rbml log serialize TOOLS := compiletest rustdoc rustc rustbook error_index_generator @@ -117,7 +117,8 @@ DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir DEPS_rustc_privacy := rustc log syntax DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \ log syntax serialize rustc_llvm rustc_platform_intrinsics \ - rustc_const_math rustc_const_eval + rustc_const_math rustc_const_eval rustc_incremental +DEPS_rustc_incremental := rbml rustc serialize rustc_data_structures DEPS_rustc_save_analysis := rustc log syntax DEPS_rustc_typeck := rustc syntax rustc_platform_intrinsics rustc_const_math \ rustc_const_eval diff --git a/src/librustc/hir/svh.rs b/src/librustc/hir/svh.rs index 7ae20f68ad0c2..08c3d70034a0d 100644 --- a/src/librustc/hir/svh.rs +++ b/src/librustc/hir/svh.rs @@ -47,9 +47,6 @@ //! Original issue: https://github.com/rust-lang/rust/issues/10207 use std::fmt; -use std::hash::{Hash, SipHasher, Hasher}; -use hir; -use hir::intravisit as visit; #[derive(Clone, PartialEq, Debug)] pub struct Svh { @@ -57,53 +54,16 @@ pub struct Svh { } impl Svh { - pub fn new(hash: &str) -> Svh { + /// Create a new `Svh` given the hash. If you actually want to + /// compute the SVH from some HIR, you want the `calculate_svh` + /// function found in `librustc_trans`. + pub fn new(hash: String) -> Svh { assert!(hash.len() == 16); - Svh { hash: hash.to_string() } + Svh { hash: hash } } - pub fn as_str<'a>(&'a self) -> &'a str { - &self.hash - } - - pub fn calculate(crate_disambiguator: &str, krate: &hir::Crate) -> Svh { - // FIXME (#14132): This is better than it used to be, but it still not - // ideal. We now attempt to hash only the relevant portions of the - // Crate AST as well as the top-level crate attributes. (However, - // the hashing of the crate attributes should be double-checked - // to ensure it is not incorporating implementation artifacts into - // the hash that are not otherwise visible.) - - // FIXME: this should use SHA1, not SipHash. SipHash is not built to - // avoid collisions. - let mut state = SipHasher::new(); - - "crate_disambiguator".hash(&mut state); - crate_disambiguator.len().hash(&mut state); - crate_disambiguator.hash(&mut state); - - { - let mut visit = svh_visitor::make(&mut state, krate); - visit::walk_crate(&mut visit, krate); - } - - // FIXME (#14132): This hash is still sensitive to e.g. the - // spans of the crate Attributes and their underlying - // MetaItems; we should make ContentHashable impl for those - // types and then use hash_content. But, since all crate - // attributes should appear near beginning of the file, it is - // not such a big deal to be sensitive to their spans for now. - // - // We hash only the MetaItems instead of the entire Attribute - // to avoid hashing the AttrId - for attr in &krate.attrs { - attr.node.value.hash(&mut state); - } - - let hash = state.finish(); - return Svh { - hash: (0..64).step_by(4).map(|i| hex(hash >> i)).collect() - }; + pub fn from_hash(hash: u64) -> Svh { + return Svh::new((0..64).step_by(4).map(|i| hex(hash >> i)).collect()); fn hex(b: u64) -> char { let b = (b & 0xf) as u8; @@ -114,6 +74,10 @@ impl Svh { b as char } } + + pub fn as_str<'a>(&'a self) -> &'a str { + &self.hash + } } impl fmt::Display for Svh { @@ -121,319 +85,3 @@ impl fmt::Display for Svh { f.pad(self.as_str()) } } - -// FIXME (#14132): Even this SVH computation still has implementation -// artifacts: namely, the order of item declaration will affect the -// hash computation, but for many kinds of items the order of -// declaration should be irrelevant to the ABI. - -mod svh_visitor { - pub use self::SawExprComponent::*; - pub use self::SawStmtComponent::*; - use self::SawAbiComponent::*; - use syntax::ast::{self, Name, NodeId}; - use syntax::codemap::Span; - use syntax::parse::token; - use hir::intravisit as visit; - use hir::intravisit::{Visitor, FnKind}; - use hir::*; - use hir; - - use std::hash::{Hash, SipHasher}; - - pub struct StrictVersionHashVisitor<'a> { - pub krate: &'a Crate, - pub st: &'a mut SipHasher, - } - - pub fn make<'a>(st: &'a mut SipHasher, krate: &'a Crate) -> StrictVersionHashVisitor<'a> { - StrictVersionHashVisitor { st: st, krate: krate } - } - - // To off-load the bulk of the hash-computation on #[derive(Hash)], - // we define a set of enums corresponding to the content that our - // crate visitor will encounter as it traverses the ast. - // - // The important invariant is that all of the Saw*Component enums - // do not carry any Spans, Names, or Idents. - // - // Not carrying any Names/Idents is the important fix for problem - // noted on PR #13948: using the ident.name as the basis for a - // hash leads to unstable SVH, because ident.name is just an index - // into intern table (i.e. essentially a random address), not - // computed from the name content. - // - // With the below enums, the SVH computation is not sensitive to - // artifacts of how rustc was invoked nor of how the source code - // was laid out. (Or at least it is *less* sensitive.) - - // This enum represents the different potential bits of code the - // visitor could encounter that could affect the ABI for the crate, - // and assigns each a distinct tag to feed into the hash computation. - #[derive(Hash)] - enum SawAbiComponent<'a> { - - // FIXME (#14132): should we include (some function of) - // ident.ctxt as well? - SawIdent(token::InternedString), - SawStructDef(token::InternedString), - - SawLifetime(token::InternedString), - SawLifetimeDef(token::InternedString), - - SawMod, - SawForeignItem, - SawItem, - SawDecl, - SawTy, - SawGenerics, - SawFn, - SawTraitItem, - SawImplItem, - SawStructField, - SawVariant, - SawExplicitSelf, - SawPath, - SawBlock, - SawPat, - SawLocal, - SawArm, - SawExpr(SawExprComponent<'a>), - SawStmt(SawStmtComponent), - } - - /// SawExprComponent carries all of the information that we want - /// to include in the hash that *won't* be covered by the - /// subsequent recursive traversal of the expression's - /// substructure by the visitor. - /// - /// We know every Expr_ variant is covered by a variant because - /// `fn saw_expr` maps each to some case below. Ensuring that - /// each variant carries an appropriate payload has to be verified - /// by hand. - /// - /// (However, getting that *exactly* right is not so important - /// because the SVH is just a developer convenience; there is no - /// guarantee of collision-freedom, hash collisions are just - /// (hopefully) unlikely.) - #[derive(Hash)] - pub enum SawExprComponent<'a> { - - SawExprLoop(Option), - SawExprField(token::InternedString), - SawExprTupField(usize), - SawExprBreak(Option), - SawExprAgain(Option), - - SawExprBox, - SawExprVec, - SawExprCall, - SawExprMethodCall, - SawExprTup, - SawExprBinary(hir::BinOp_), - SawExprUnary(hir::UnOp), - SawExprLit(ast::LitKind), - SawExprCast, - SawExprType, - SawExprIf, - SawExprWhile, - SawExprMatch, - SawExprClosure, - SawExprBlock, - SawExprAssign, - SawExprAssignOp(hir::BinOp_), - SawExprIndex, - SawExprPath(Option), - SawExprAddrOf(hir::Mutability), - SawExprRet, - SawExprInlineAsm(&'a hir::InlineAsm), - SawExprStruct, - SawExprRepeat, - } - - fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> { - match *node { - ExprBox(..) => SawExprBox, - ExprVec(..) => SawExprVec, - ExprCall(..) => SawExprCall, - ExprMethodCall(..) => SawExprMethodCall, - ExprTup(..) => SawExprTup, - ExprBinary(op, _, _) => SawExprBinary(op.node), - ExprUnary(op, _) => SawExprUnary(op), - ExprLit(ref lit) => SawExprLit(lit.node.clone()), - ExprCast(..) => SawExprCast, - ExprType(..) => SawExprType, - ExprIf(..) => SawExprIf, - ExprWhile(..) => SawExprWhile, - ExprLoop(_, id) => SawExprLoop(id.map(|id| id.name.as_str())), - ExprMatch(..) => SawExprMatch, - ExprClosure(..) => SawExprClosure, - ExprBlock(..) => SawExprBlock, - ExprAssign(..) => SawExprAssign, - ExprAssignOp(op, _, _) => SawExprAssignOp(op.node), - ExprField(_, name) => SawExprField(name.node.as_str()), - ExprTupField(_, id) => SawExprTupField(id.node), - ExprIndex(..) => SawExprIndex, - ExprPath(ref qself, _) => SawExprPath(qself.as_ref().map(|q| q.position)), - ExprAddrOf(m, _) => SawExprAddrOf(m), - ExprBreak(id) => SawExprBreak(id.map(|id| id.node.name.as_str())), - ExprAgain(id) => SawExprAgain(id.map(|id| id.node.name.as_str())), - ExprRet(..) => SawExprRet, - ExprInlineAsm(ref a,_,_) => SawExprInlineAsm(a), - ExprStruct(..) => SawExprStruct, - ExprRepeat(..) => SawExprRepeat, - } - } - - /// SawStmtComponent is analogous to SawExprComponent, but for statements. - #[derive(Hash)] - pub enum SawStmtComponent { - SawStmtDecl, - SawStmtExpr, - SawStmtSemi, - } - - fn saw_stmt(node: &Stmt_) -> SawStmtComponent { - match *node { - StmtDecl(..) => SawStmtDecl, - StmtExpr(..) => SawStmtExpr, - StmtSemi(..) => SawStmtSemi, - } - } - - impl<'a> Visitor<'a> for StrictVersionHashVisitor<'a> { - fn visit_nested_item(&mut self, item: ItemId) { - self.visit_item(self.krate.item(item.id)) - } - - fn visit_variant_data(&mut self, s: &'a VariantData, name: Name, - g: &'a Generics, _: NodeId, _: Span) { - SawStructDef(name.as_str()).hash(self.st); - visit::walk_generics(self, g); - visit::walk_struct_def(self, s) - } - - fn visit_variant(&mut self, v: &'a Variant, g: &'a Generics, item_id: NodeId) { - SawVariant.hash(self.st); - // walk_variant does not call walk_generics, so do it here. - visit::walk_generics(self, g); - visit::walk_variant(self, v, g, item_id) - } - - // All of the remaining methods just record (in the hash - // SipHasher) that the visitor saw that particular variant - // (with its payload), and continue walking as the default - // visitor would. - // - // Some of the implementations have some notes as to how one - // might try to make their SVH computation less discerning - // (e.g. by incorporating reachability analysis). But - // currently all of their implementations are uniform and - // uninteresting. - // - // (If you edit a method such that it deviates from the - // pattern, please move that method up above this comment.) - - fn visit_name(&mut self, _: Span, name: Name) { - SawIdent(name.as_str()).hash(self.st); - } - - fn visit_lifetime(&mut self, l: &'a Lifetime) { - SawLifetime(l.name.as_str()).hash(self.st); - } - - fn visit_lifetime_def(&mut self, l: &'a LifetimeDef) { - SawLifetimeDef(l.lifetime.name.as_str()).hash(self.st); - } - - // We do recursively walk the bodies of functions/methods - // (rather than omitting their bodies from the hash) since - // monomorphization and cross-crate inlining generally implies - // that a change to a crate body will require downstream - // crates to be recompiled. - fn visit_expr(&mut self, ex: &'a Expr) { - SawExpr(saw_expr(&ex.node)).hash(self.st); visit::walk_expr(self, ex) - } - - fn visit_stmt(&mut self, s: &'a Stmt) { - SawStmt(saw_stmt(&s.node)).hash(self.st); visit::walk_stmt(self, s) - } - - fn visit_foreign_item(&mut self, i: &'a ForeignItem) { - // FIXME (#14132) ideally we would incorporate privacy (or - // perhaps reachability) somewhere here, so foreign items - // that do not leak into downstream crates would not be - // part of the ABI. - SawForeignItem.hash(self.st); visit::walk_foreign_item(self, i) - } - - fn visit_item(&mut self, i: &'a Item) { - // FIXME (#14132) ideally would incorporate reachability - // analysis somewhere here, so items that never leak into - // downstream crates (e.g. via monomorphisation or - // inlining) would not be part of the ABI. - SawItem.hash(self.st); visit::walk_item(self, i) - } - - fn visit_mod(&mut self, m: &'a Mod, _s: Span, _n: NodeId) { - SawMod.hash(self.st); visit::walk_mod(self, m) - } - - fn visit_decl(&mut self, d: &'a Decl) { - SawDecl.hash(self.st); visit::walk_decl(self, d) - } - - fn visit_ty(&mut self, t: &'a Ty) { - SawTy.hash(self.st); visit::walk_ty(self, t) - } - - fn visit_generics(&mut self, g: &'a Generics) { - SawGenerics.hash(self.st); visit::walk_generics(self, g) - } - - fn visit_fn(&mut self, fk: FnKind<'a>, fd: &'a FnDecl, - b: &'a Block, s: Span, _: NodeId) { - SawFn.hash(self.st); visit::walk_fn(self, fk, fd, b, s) - } - - fn visit_trait_item(&mut self, ti: &'a TraitItem) { - SawTraitItem.hash(self.st); visit::walk_trait_item(self, ti) - } - - fn visit_impl_item(&mut self, ii: &'a ImplItem) { - SawImplItem.hash(self.st); visit::walk_impl_item(self, ii) - } - - fn visit_struct_field(&mut self, s: &'a StructField) { - SawStructField.hash(self.st); visit::walk_struct_field(self, s) - } - - fn visit_explicit_self(&mut self, es: &'a ExplicitSelf) { - SawExplicitSelf.hash(self.st); visit::walk_explicit_self(self, es) - } - - fn visit_path(&mut self, path: &'a Path, _: ast::NodeId) { - SawPath.hash(self.st); visit::walk_path(self, path) - } - - fn visit_path_list_item(&mut self, prefix: &'a Path, item: &'a PathListItem) { - SawPath.hash(self.st); visit::walk_path_list_item(self, prefix, item) - } - - fn visit_block(&mut self, b: &'a Block) { - SawBlock.hash(self.st); visit::walk_block(self, b) - } - - fn visit_pat(&mut self, p: &'a Pat) { - SawPat.hash(self.st); visit::walk_pat(self, p) - } - - fn visit_local(&mut self, l: &'a Local) { - SawLocal.hash(self.st); visit::walk_local(self, l) - } - - fn visit_arm(&mut self, a: &'a Arm) { - SawArm.hash(self.st); visit::walk_arm(self, a) - } - } -} diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index deb2062777220..342007d46c535 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -100,6 +100,7 @@ pub mod middle { pub mod recursion_limit; pub mod resolve_lifetime; pub mod stability; + pub mod svh; pub mod weak_lang_items; } diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 1f6328187a59b..0409451043dfb 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -28,6 +28,7 @@ use hir::def::{self, Def}; use middle::lang_items; use ty::{self, Ty, TyCtxt, VariantKind}; use hir::def_id::{DefId, DefIndex}; +use hir::svh::Svh; use mir::repr::Mir; use mir::mir_map::MirMap; use session::Session; diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index bac5900f3edaf..4533946d26ea4 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -23,6 +23,7 @@ rustc_mir = { path = "../librustc_mir" } rustc_plugin = { path = "../librustc_plugin" } rustc_passes = { path = "../librustc_passes" } rustc_privacy = { path = "../librustc_privacy" } +rustc_incremental = { path = "../librustc_incremental" } rustc_resolve = { path = "../librustc_resolve" } rustc_save_analysis = { path = "../librustc_save_analysis" } rustc_trans = { path = "../librustc_trans" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index a4d34aaf6529e..f35d5f259621f 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -24,6 +24,7 @@ use rustc::util::common::time; use rustc::util::nodemap::NodeSet; use rustc_back::sha2::{Sha256, Digest}; use rustc_borrowck as borrowck; +use rustc_incremental; use rustc_resolve as resolve; use rustc_metadata::macro_import; use rustc_metadata::creader::LocalCrateReader; @@ -952,9 +953,16 @@ pub fn phase_4_translate_to_llvm<'tcx>(tcx: &TyCtxt<'tcx>, passes.run_passes(tcx, &mut mir_map); }); + let translation = + time(time_passes, + "translation", + move || trans::trans_crate(tcx, &mir_map, analysis)); + time(time_passes, - "translation", - move || trans::trans_crate(tcx, &mir_map, analysis)) + "assert dep graph", + move || rustc_incremental::assert_dep_graph(tcx)); + + translation } /// Run LLVM itself, producing a bitcode file, assembly file or object file diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index b4b53d30e3c04..85807dec0ffe1 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -45,6 +45,7 @@ extern crate rustc_passes; extern crate rustc_lint; extern crate rustc_plugin; extern crate rustc_privacy; +extern crate rustc_incremental; extern crate rustc_metadata; extern crate rustc_mir; extern crate rustc_resolve; diff --git a/src/librustc_incremental/Cargo.toml b/src/librustc_incremental/Cargo.toml new file mode 100644 index 0000000000000..1a8b5a483e01f --- /dev/null +++ b/src/librustc_incremental/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_serialize" +version = "0.0.0" + +[lib] +name = "rustc_serialize" +path = "lib.rs" +crate-type = ["dylib"] + +[dependencies] +graphviz = { path = "../libgraphviz" } +rbml = { path = "../librbml" } +rustc = { path = "../librustc" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_front = { path = "../librustc_front" } +serialize = { path = "../libserialize" } diff --git a/src/librustc_trans/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs similarity index 78% rename from src/librustc_trans/assert_dep_graph.rs rename to src/librustc_incremental/assert_dep_graph.rs index 932f66f17cc7b..88d8ed8d58118 100644 --- a/src/librustc_trans/assert_dep_graph.rs +++ b/src/librustc_incremental/assert_dep_graph.rs @@ -13,12 +13,17 @@ //! will dump graphs in graphviz form to disk, and it searches for //! `#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]` //! annotations. These annotations can be used to test whether paths -//! exist in the graph. We report errors on each -//! `rustc_if_this_changed` annotation. If a path exists in all -//! cases, then we would report "all path(s) exist". Otherwise, we -//! report: "no path to `foo`" for each case where no path exists. -//! `compile-fail` tests can then be used to check when paths exist or -//! do not. +//! exist in the graph. These checks run after trans, so they view the +//! the final state of the dependency graph. Note that there are +//! similar assertions found in `persist::dirty_clean` which check the +//! **initial** state of the dependency graph, just after it has been +//! loaded from disk. +//! +//! In this code, we report errors on each `rustc_if_this_changed` +//! annotation. If a path exists in all cases, then we would report +//! "all path(s) exist". Otherwise, we report: "no path to `foo`" for +//! each case where no path exists. `compile-fail` tests can then be +//! used to check when paths exist or do not. //! //! The full form of the `rustc_if_this_changed` annotation is //! `#[rustc_if_this_changed(id)]`. The `"id"` is optional and @@ -61,7 +66,7 @@ const ID: &'static str = "id"; pub fn assert_dep_graph(tcx: &TyCtxt) { let _ignore = tcx.dep_graph.in_ignore(); - if tcx.sess.opts.dump_dep_graph { + if tcx.sess.opts.debugging_opts.dump_dep_graph { dump_graph(tcx); } @@ -74,14 +79,23 @@ pub fn assert_dep_graph(tcx: &TyCtxt) { (visitor.if_this_changed, visitor.then_this_would_need) }; + if !if_this_changed.is_empty() || !then_this_would_need.is_empty() { + assert!(tcx.sess.opts.debugging_opts.query_dep_graph, + "cannot use the `#[{}]` or `#[{}]` annotations \ + without supplying `-Z query-dep-graph`", + IF_THIS_CHANGED, THEN_THIS_WOULD_NEED); + } + // Check paths. check_paths(tcx, &if_this_changed, &then_this_would_need); } -type SourceHashMap = FnvHashMap>; -type TargetHashMap = FnvHashMap>; +type SourceHashMap = + FnvHashMap)>>; +type TargetHashMap = + FnvHashMap)>>; struct IfThisChanged<'a, 'tcx:'a> { tcx: &'a TyCtxt<'tcx>, @@ -124,34 +138,21 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> { } } } - let dep_node_str = dep_node_interned.as_ref().map(|s| &**s); - macro_rules! match_depnode_name { - ($input:expr, $def_id:expr, match { $($variant:ident,)* } else $y:expr) => { - match $input { - $(Some(stringify!($variant)) => DepNode::$variant($def_id),)* - _ => $y + let dep_node = match dep_node_interned { + Some(ref n) => { + match DepNode::from_label_string(&n[..], def_id) { + Ok(n) => n, + Err(()) => { + self.tcx.sess.span_fatal( + attr.span, + &format!("unrecognized DepNode variant {:?}", n)); + } } } - } - let dep_node = match_depnode_name! { - dep_node_str, def_id, match { - CollectItem, - BorrowCheck, - TransCrateItem, - TypeckItemType, - TypeckItemBody, - ImplOrTraitItems, - ItemSignature, - FieldTy, - TraitItemDefIds, - InherentImpls, - ImplItems, - TraitImpls, - ReprHints, - } else { + None => { self.tcx.sess.span_fatal( attr.span, - &format!("unrecognized DepNode variant {:?}", dep_node_str)); + &format!("missing DepNode variant")); } }; let id = id.unwrap_or(InternedString::new(ID)); @@ -194,7 +195,7 @@ fn check_paths(tcx: &TyCtxt, }; for &(_, source_def_id, source_dep_node) in sources { - let dependents = query.dependents(source_dep_node); + let dependents = query.transitive_dependents(source_dep_node); for &(target_span, ref target_pass, _, ref target_dep_node) in targets { if !dependents.contains(&target_dep_node) { tcx.sess.span_err( @@ -251,33 +252,34 @@ fn dump_graph(tcx: &TyCtxt) { } } -pub struct GraphvizDepGraph(FnvHashSet, Vec<(DepNode, DepNode)>); +pub struct GraphvizDepGraph(FnvHashSet>, + Vec<(DepNode, DepNode)>); impl<'a, 'tcx> dot::GraphWalk<'a> for GraphvizDepGraph { - type Node = DepNode; - type Edge = (DepNode, DepNode); - fn nodes(&self) -> dot::Nodes { + type Node = DepNode; + type Edge = (DepNode, DepNode); + fn nodes(&self) -> dot::Nodes> { let nodes: Vec<_> = self.0.iter().cloned().collect(); nodes.into_cow() } - fn edges(&self) -> dot::Edges<(DepNode, DepNode)> { + fn edges(&self) -> dot::Edges<(DepNode, DepNode)> { self.1[..].into_cow() } - fn source(&self, edge: &(DepNode, DepNode)) -> DepNode { + fn source(&self, edge: &(DepNode, DepNode)) -> DepNode { edge.0 } - fn target(&self, edge: &(DepNode, DepNode)) -> DepNode { + fn target(&self, edge: &(DepNode, DepNode)) -> DepNode { edge.1 } } impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph { - type Node = DepNode; - type Edge = (DepNode, DepNode); + type Node = DepNode; + type Edge = (DepNode, DepNode); fn graph_id(&self) -> dot::Id { dot::Id::new("DependencyGraph").unwrap() } - fn node_id(&self, n: &DepNode) -> dot::Id { + fn node_id(&self, n: &DepNode) -> dot::Id { let s: String = format!("{:?}", n).chars() .map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' }) @@ -285,7 +287,7 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph { debug!("n={:?} s={:?}", n, s); dot::Id::new(s).unwrap() } - fn node_label(&self, n: &DepNode) -> dot::LabelText { + fn node_label(&self, n: &DepNode) -> dot::LabelText { dot::LabelText::label(format!("{:?}", n)) } } @@ -293,7 +295,9 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph { // Given an optional filter like `"x,y,z"`, returns either `None` (no // filter) or the set of nodes whose labels contain all of those // substrings. -fn node_set(query: &DepGraphQuery, filter: &str) -> Option> { +fn node_set(query: &DepGraphQuery, filter: &str) + -> Option>> +{ debug!("node_set(filter={:?})", filter); if filter.trim().is_empty() { @@ -313,10 +317,10 @@ fn node_set(query: &DepGraphQuery, filter: &str) -> Option> .collect()) } -fn filter_nodes(query: &DepGraphQuery, - sources: &Option>, - targets: &Option>) - -> FnvHashSet +fn filter_nodes(query: &DepGraphQuery, + sources: &Option>>, + targets: &Option>>) + -> FnvHashSet> { if let &Some(ref sources) = sources { if let &Some(ref targets) = targets { @@ -331,10 +335,10 @@ fn filter_nodes(query: &DepGraphQuery, } } -fn walk_nodes(query: &DepGraphQuery, - starts: &FnvHashSet, +fn walk_nodes(query: &DepGraphQuery, + starts: &FnvHashSet>, direction: Direction) - -> FnvHashSet + -> FnvHashSet> { let mut set = FnvHashSet(); for start in starts { @@ -355,10 +359,10 @@ fn walk_nodes(query: &DepGraphQuery, set } -fn walk_between(query: &DepGraphQuery, - sources: &FnvHashSet, - targets: &FnvHashSet) - -> FnvHashSet +fn walk_between(query: &DepGraphQuery, + sources: &FnvHashSet>, + targets: &FnvHashSet>) + -> FnvHashSet> { // This is a bit tricky. We want to include a node only if it is: // (a) reachable from a source and (b) will reach a target. And we @@ -386,7 +390,7 @@ fn walk_between(query: &DepGraphQuery, }) .collect(); - fn recurse(query: &DepGraphQuery, + fn recurse(query: &DepGraphQuery, node_states: &mut [State], node: NodeIndex) -> bool @@ -423,9 +427,9 @@ fn walk_between(query: &DepGraphQuery, } } -fn filter_edges(query: &DepGraphQuery, - nodes: &FnvHashSet) - -> Vec<(DepNode, DepNode)> +fn filter_edges(query: &DepGraphQuery, + nodes: &FnvHashSet>) + -> Vec<(DepNode, DepNode)> { query.edges() .into_iter() diff --git a/src/librustc_incremental/calculate_svh.rs b/src/librustc_incremental/calculate_svh.rs new file mode 100644 index 0000000000000..158db34ee8ac9 --- /dev/null +++ b/src/librustc_incremental/calculate_svh.rs @@ -0,0 +1,420 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Calculation of a Strict Version Hash for crates. For a length +//! comment explaining the general idea, see `librustc/middle/svh.rs`. + +use std::hash::{Hash, SipHasher, Hasher}; +use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; +use rustc::hir::svh::Svh; +use rustc::ty; +use rustc_front::intravisit::{self, Visitor}; + +use self::svh_visitor::StrictVersionHashVisitor; + +pub trait SvhCalculate { + /// Calculate the SVH for an entire krate. + fn calculate_krate_hash(&self) -> Svh; + + /// Calculate the SVH for a particular item. + fn calculate_item_hash(&self, def_id: DefId) -> u64; +} + +impl<'tcx> SvhCalculate for ty::TyCtxt<'tcx> { + fn calculate_krate_hash(&self) -> Svh { + // FIXME (#14132): This is better than it used to be, but it still not + // ideal. We now attempt to hash only the relevant portions of the + // Crate AST as well as the top-level crate attributes. (However, + // the hashing of the crate attributes should be double-checked + // to ensure it is not incorporating implementation artifacts into + // the hash that are not otherwise visible.) + + let crate_disambiguator = self.sess.crate_disambiguator.get(); + let krate = self.map.krate(); + + // FIXME: this should use SHA1, not SipHash. SipHash is not built to + // avoid collisions. + let mut state = SipHasher::new(); + debug!("state: {:?}", state); + + "crate_disambiguator".hash(&mut state); + crate_disambiguator.as_str().len().hash(&mut state); + crate_disambiguator.as_str().hash(&mut state); + + debug!("crate_disambiguator: {:?}", crate_disambiguator.as_str()); + debug!("state: {:?}", state); + + { + let mut visit = StrictVersionHashVisitor::new(&mut state, self); + krate.visit_all_items(&mut visit); + } + + // FIXME (#14132): This hash is still sensitive to e.g. the + // spans of the crate Attributes and their underlying + // MetaItems; we should make ContentHashable impl for those + // types and then use hash_content. But, since all crate + // attributes should appear near beginning of the file, it is + // not such a big deal to be sensitive to their spans for now. + // + // We hash only the MetaItems instead of the entire Attribute + // to avoid hashing the AttrId + for attr in &krate.attrs { + debug!("krate attr {:?}", attr); + attr.node.value.hash(&mut state); + } + + Svh::from_hash(state.finish()) + } + + fn calculate_item_hash(&self, def_id: DefId) -> u64 { + assert!(def_id.is_local()); + + let mut state = SipHasher::new(); + + { + let mut visit = StrictVersionHashVisitor::new(&mut state, self); + if def_id.index == CRATE_DEF_INDEX { + // the crate root itself is not registered in the map + // as an item, so we have to fetch it this way + let krate = self.map.krate(); + intravisit::walk_crate(&mut visit, krate); + } else { + let node_id = self.map.as_local_node_id(def_id).unwrap(); + visit.visit_item(self.map.expect_item(node_id)); + } + } + + state.finish() + } +} + +// FIXME (#14132): Even this SVH computation still has implementation +// artifacts: namely, the order of item declaration will affect the +// hash computation, but for many kinds of items the order of +// declaration should be irrelevant to the ABI. + +mod svh_visitor { + pub use self::SawExprComponent::*; + pub use self::SawStmtComponent::*; + use self::SawAbiComponent::*; + use syntax::ast::{self, Name, NodeId}; + use syntax::codemap::Span; + use syntax::parse::token; + use rustc::ty; + use rustc_front::intravisit as visit; + use rustc_front::intravisit::{Visitor, FnKind}; + use rustc_front::hir::*; + use rustc_front::hir; + + use std::hash::{Hash, SipHasher}; + + pub struct StrictVersionHashVisitor<'a, 'tcx: 'a> { + pub tcx: &'a ty::TyCtxt<'tcx>, + pub st: &'a mut SipHasher, + } + + impl<'a, 'tcx> StrictVersionHashVisitor<'a, 'tcx> { + pub fn new(st: &'a mut SipHasher, + tcx: &'a ty::TyCtxt<'tcx>) + -> Self { + StrictVersionHashVisitor { st: st, tcx: tcx } + } + } + + // To off-load the bulk of the hash-computation on #[derive(Hash)], + // we define a set of enums corresponding to the content that our + // crate visitor will encounter as it traverses the ast. + // + // The important invariant is that all of the Saw*Component enums + // do not carry any Spans, Names, or Idents. + // + // Not carrying any Names/Idents is the important fix for problem + // noted on PR #13948: using the ident.name as the basis for a + // hash leads to unstable SVH, because ident.name is just an index + // into intern table (i.e. essentially a random address), not + // computed from the name content. + // + // With the below enums, the SVH computation is not sensitive to + // artifacts of how rustc was invoked nor of how the source code + // was laid out. (Or at least it is *less* sensitive.) + + // This enum represents the different potential bits of code the + // visitor could encounter that could affect the ABI for the crate, + // and assigns each a distinct tag to feed into the hash computation. + #[derive(Hash)] + enum SawAbiComponent<'a> { + + // FIXME (#14132): should we include (some function of) + // ident.ctxt as well? + SawIdent(token::InternedString), + SawStructDef(token::InternedString), + + SawLifetime(token::InternedString), + SawLifetimeDef(token::InternedString), + + SawMod, + SawForeignItem, + SawItem, + SawDecl, + SawTy, + SawGenerics, + SawFn, + SawTraitItem, + SawImplItem, + SawStructField, + SawVariant, + SawExplicitSelf, + SawPath, + SawBlock, + SawPat, + SawLocal, + SawArm, + SawExpr(SawExprComponent<'a>), + SawStmt(SawStmtComponent), + } + + /// SawExprComponent carries all of the information that we want + /// to include in the hash that *won't* be covered by the + /// subsequent recursive traversal of the expression's + /// substructure by the visitor. + /// + /// We know every Expr_ variant is covered by a variant because + /// `fn saw_expr` maps each to some case below. Ensuring that + /// each variant carries an appropriate payload has to be verified + /// by hand. + /// + /// (However, getting that *exactly* right is not so important + /// because the SVH is just a developer convenience; there is no + /// guarantee of collision-freedom, hash collisions are just + /// (hopefully) unlikely.) + #[derive(Hash)] + pub enum SawExprComponent<'a> { + + SawExprLoop(Option), + SawExprField(token::InternedString), + SawExprTupField(usize), + SawExprBreak(Option), + SawExprAgain(Option), + + SawExprBox, + SawExprVec, + SawExprCall, + SawExprMethodCall, + SawExprTup, + SawExprBinary(hir::BinOp_), + SawExprUnary(hir::UnOp), + SawExprLit(ast::LitKind), + SawExprCast, + SawExprType, + SawExprIf, + SawExprWhile, + SawExprMatch, + SawExprClosure, + SawExprBlock, + SawExprAssign, + SawExprAssignOp(hir::BinOp_), + SawExprIndex, + SawExprPath(Option), + SawExprAddrOf(hir::Mutability), + SawExprRet, + SawExprInlineAsm(&'a hir::InlineAsm), + SawExprStruct, + SawExprRepeat, + } + + fn saw_expr<'a>(node: &'a Expr_) -> SawExprComponent<'a> { + match *node { + ExprBox(..) => SawExprBox, + ExprVec(..) => SawExprVec, + ExprCall(..) => SawExprCall, + ExprMethodCall(..) => SawExprMethodCall, + ExprTup(..) => SawExprTup, + ExprBinary(op, _, _) => SawExprBinary(op.node), + ExprUnary(op, _) => SawExprUnary(op), + ExprLit(ref lit) => SawExprLit(lit.node.clone()), + ExprCast(..) => SawExprCast, + ExprType(..) => SawExprType, + ExprIf(..) => SawExprIf, + ExprWhile(..) => SawExprWhile, + ExprLoop(_, id) => SawExprLoop(id.map(|id| id.name.as_str())), + ExprMatch(..) => SawExprMatch, + ExprClosure(..) => SawExprClosure, + ExprBlock(..) => SawExprBlock, + ExprAssign(..) => SawExprAssign, + ExprAssignOp(op, _, _) => SawExprAssignOp(op.node), + ExprField(_, name) => SawExprField(name.node.as_str()), + ExprTupField(_, id) => SawExprTupField(id.node), + ExprIndex(..) => SawExprIndex, + ExprPath(ref qself, _) => SawExprPath(qself.as_ref().map(|q| q.position)), + ExprAddrOf(m, _) => SawExprAddrOf(m), + ExprBreak(id) => SawExprBreak(id.map(|id| id.node.name.as_str())), + ExprAgain(id) => SawExprAgain(id.map(|id| id.node.name.as_str())), + ExprRet(..) => SawExprRet, + ExprInlineAsm(ref a,_,_) => SawExprInlineAsm(a), + ExprStruct(..) => SawExprStruct, + ExprRepeat(..) => SawExprRepeat, + } + } + + /// SawStmtComponent is analogous to SawExprComponent, but for statements. + #[derive(Hash)] + pub enum SawStmtComponent { + SawStmtDecl, + SawStmtExpr, + SawStmtSemi, + } + + fn saw_stmt(node: &Stmt_) -> SawStmtComponent { + match *node { + StmtDecl(..) => SawStmtDecl, + StmtExpr(..) => SawStmtExpr, + StmtSemi(..) => SawStmtSemi, + } + } + + impl<'a, 'tcx> Visitor<'a> for StrictVersionHashVisitor<'a, 'tcx> { + fn visit_nested_item(&mut self, item: ItemId) { + debug!("visit_nested_item: {:?} st={:?}", item, self.st); + let def_path = self.tcx.map.def_path_from_id(item.id); + def_path.hash(self.st); + } + + fn visit_variant_data(&mut self, s: &'a VariantData, name: Name, + g: &'a Generics, _: NodeId, _: Span) { + SawStructDef(name.as_str()).hash(self.st); + visit::walk_generics(self, g); + visit::walk_struct_def(self, s) + } + + fn visit_variant(&mut self, v: &'a Variant, g: &'a Generics, item_id: NodeId) { + SawVariant.hash(self.st); + // walk_variant does not call walk_generics, so do it here. + visit::walk_generics(self, g); + visit::walk_variant(self, v, g, item_id) + } + + // All of the remaining methods just record (in the hash + // SipHasher) that the visitor saw that particular variant + // (with its payload), and continue walking as the default + // visitor would. + // + // Some of the implementations have some notes as to how one + // might try to make their SVH computation less discerning + // (e.g. by incorporating reachability analysis). But + // currently all of their implementations are uniform and + // uninteresting. + // + // (If you edit a method such that it deviates from the + // pattern, please move that method up above this comment.) + + fn visit_name(&mut self, _: Span, name: Name) { + SawIdent(name.as_str()).hash(self.st); + } + + fn visit_lifetime(&mut self, l: &'a Lifetime) { + SawLifetime(l.name.as_str()).hash(self.st); + } + + fn visit_lifetime_def(&mut self, l: &'a LifetimeDef) { + SawLifetimeDef(l.lifetime.name.as_str()).hash(self.st); + } + + // We do recursively walk the bodies of functions/methods + // (rather than omitting their bodies from the hash) since + // monomorphization and cross-crate inlining generally implies + // that a change to a crate body will require downstream + // crates to be recompiled. + fn visit_expr(&mut self, ex: &'a Expr) { + SawExpr(saw_expr(&ex.node)).hash(self.st); visit::walk_expr(self, ex) + } + + fn visit_stmt(&mut self, s: &'a Stmt) { + SawStmt(saw_stmt(&s.node)).hash(self.st); visit::walk_stmt(self, s) + } + + fn visit_foreign_item(&mut self, i: &'a ForeignItem) { + // FIXME (#14132) ideally we would incorporate privacy (or + // perhaps reachability) somewhere here, so foreign items + // that do not leak into downstream crates would not be + // part of the ABI. + SawForeignItem.hash(self.st); visit::walk_foreign_item(self, i) + } + + fn visit_item(&mut self, i: &'a Item) { + debug!("visit_item: {:?} st={:?}", i, self.st); + // FIXME (#14132) ideally would incorporate reachability + // analysis somewhere here, so items that never leak into + // downstream crates (e.g. via monomorphisation or + // inlining) would not be part of the ABI. + SawItem.hash(self.st); visit::walk_item(self, i) + } + + fn visit_mod(&mut self, m: &'a Mod, _s: Span, _n: NodeId) { + SawMod.hash(self.st); visit::walk_mod(self, m) + } + + fn visit_decl(&mut self, d: &'a Decl) { + SawDecl.hash(self.st); visit::walk_decl(self, d) + } + + fn visit_ty(&mut self, t: &'a Ty) { + SawTy.hash(self.st); visit::walk_ty(self, t) + } + + fn visit_generics(&mut self, g: &'a Generics) { + SawGenerics.hash(self.st); visit::walk_generics(self, g) + } + + fn visit_fn(&mut self, fk: FnKind<'a>, fd: &'a FnDecl, + b: &'a Block, s: Span, _: NodeId) { + SawFn.hash(self.st); visit::walk_fn(self, fk, fd, b, s) + } + + fn visit_trait_item(&mut self, ti: &'a TraitItem) { + SawTraitItem.hash(self.st); visit::walk_trait_item(self, ti) + } + + fn visit_impl_item(&mut self, ii: &'a ImplItem) { + SawImplItem.hash(self.st); visit::walk_impl_item(self, ii) + } + + fn visit_struct_field(&mut self, s: &'a StructField) { + SawStructField.hash(self.st); visit::walk_struct_field(self, s) + } + + fn visit_explicit_self(&mut self, es: &'a ExplicitSelf) { + SawExplicitSelf.hash(self.st); visit::walk_explicit_self(self, es) + } + + fn visit_path(&mut self, path: &'a Path, _: ast::NodeId) { + SawPath.hash(self.st); visit::walk_path(self, path) + } + + fn visit_path_list_item(&mut self, prefix: &'a Path, item: &'a PathListItem) { + SawPath.hash(self.st); visit::walk_path_list_item(self, prefix, item) + } + + fn visit_block(&mut self, b: &'a Block) { + SawBlock.hash(self.st); visit::walk_block(self, b) + } + + fn visit_pat(&mut self, p: &'a Pat) { + SawPat.hash(self.st); visit::walk_pat(self, p) + } + + fn visit_local(&mut self, l: &'a Local) { + SawLocal.hash(self.st); visit::walk_local(self, l) + } + + fn visit_arm(&mut self, a: &'a Arm) { + SawArm.hash(self.st); visit::walk_arm(self, a) + } + } +} diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs new file mode 100644 index 0000000000000..3af8fe5cc5f90 --- /dev/null +++ b/src/librustc_incremental/lib.rs @@ -0,0 +1,41 @@ +// Copyright 2012-2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Support for serializing the dep-graph and reloading it. + +#![crate_name = "rustc_incremental"] +#![unstable(feature = "rustc_private", issue = "27812")] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] +#![cfg_attr(not(stage0), deny(warnings))] + +#![feature(rustc_private)] +#![feature(staged_api)] + +extern crate graphviz; +extern crate rbml; +extern crate rustc; +extern crate rustc_data_structures; +extern crate serialize as rustc_serialize; + +#[macro_use] extern crate log; +#[macro_use] extern crate syntax; + +mod assert_dep_graph; +mod calculate_svh; +mod persist; + +pub use assert_dep_graph::assert_dep_graph; +pub use calculate_svh::SvhCalculate; +pub use persist::load_dep_graph; +pub use persist::save_dep_graph; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 6b53edbbff1d8..bb95104ffeaf6 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -1242,7 +1242,7 @@ pub fn get_crate_deps(data: &[u8]) -> Vec { reader::tagged_docs(depsdoc, tag_crate_dep).enumerate().map(|(crate_num, depdoc)| { let name = docstr(depdoc, tag_crate_dep_crate_name); - let hash = Svh::new(&docstr(depdoc, tag_crate_dep_hash)); + let hash = Svh::new(docstr(depdoc, tag_crate_dep_hash)); let doc = reader::get_doc(depdoc, tag_crate_dep_explicitly_linked); let explicitly_linked = reader::doc_as_u8(doc) != 0; CrateDep { @@ -1266,14 +1266,14 @@ fn list_crate_deps(data: &[u8], out: &mut io::Write) -> io::Result<()> { pub fn maybe_get_crate_hash(data: &[u8]) -> Option { let cratedoc = rbml::Doc::new(data); reader::maybe_get_doc(cratedoc, tag_crate_hash).map(|doc| { - Svh::new(doc.as_str_slice()) + Svh::new(doc.as_str_slice().to_string()) }) } pub fn get_crate_hash(data: &[u8]) -> Svh { let cratedoc = rbml::Doc::new(data); let hashdoc = reader::get_doc(cratedoc, tag_crate_hash); - Svh::new(hashdoc.as_str_slice()) + Svh::new(hashdoc.as_str_slice().to_string()) } pub fn maybe_get_crate_name(data: &[u8]) -> Option<&str> { diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml index ea4cef03b707a..ccb430fbb782f 100644 --- a/src/librustc_trans/Cargo.toml +++ b/src/librustc_trans/Cargo.toml @@ -18,6 +18,7 @@ rustc_back = { path = "../librustc_back" } rustc_const_eval = { path = "../librustc_const_eval" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } +rustc_incremental = { path = "../librustc_incremental" } rustc_llvm = { path = "../librustc_llvm" } rustc_mir = { path = "../librustc_mir" } rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 130499603e7a7..1d15e67651aa5 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -13,7 +13,6 @@ use super::linker::{Linker, GnuLinker, MsvcLinker}; use super::rpath::RPathConfig; use super::rpath; use super::msvc; -use super::svh::Svh; use session::config; use session::config::NoDebugInfo; use session::config::{OutputFilenames, Input, OutputType}; @@ -26,8 +25,10 @@ use middle::dependency_format::Linkage; use CrateTranslation; use util::common::time; use util::fs::fix_windows_verbatim_for_gcc; +use rustc::ty::TyCtxt; use rustc_back::tempdir::TempDir; +use rustc_incremental::SvhCalculate; use std::ascii; use std::char; use std::env; @@ -122,15 +123,15 @@ pub fn find_crate_name(sess: Option<&Session>, } "rust_out".to_string() + } -pub fn build_link_meta(sess: &Session, - krate: &hir::Crate, +pub fn build_link_meta(tcx: &TyCtxt, name: &str) -> LinkMeta { let r = LinkMeta { crate_name: name.to_owned(), - crate_hash: Svh::calculate(&sess.crate_disambiguator.get().as_str(), krate), + crate_hash: tcx.calculate_krate_hash(), }; info!("{:?}", r); return r; diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index c8ed4e629e4c1..17230eff6e639 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -54,7 +54,6 @@ use session::Session; use _match; use abi::{self, Abi, FnType}; use adt; -use assert_dep_graph; use attributes; use build::*; use builder::{Builder, noname}; @@ -2730,7 +2729,7 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>, } } - let link_meta = link::build_link_meta(&tcx.sess, krate, name); + let link_meta = link::build_link_meta(&tcx, name); let codegen_units = tcx.sess.opts.cg.codegen_units; let shared_ccx = SharedCrateContext::new(&link_meta.crate_name, @@ -2856,8 +2855,6 @@ pub fn trans_crate<'tcx>(tcx: &TyCtxt<'tcx>, }; let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); - assert_dep_graph::assert_dep_graph(tcx); - CrateTranslation { modules: modules, metadata_module: metadata_module, diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 19a172e7f102b..cb421b6be472b 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -46,6 +46,7 @@ extern crate libc; #[macro_use] extern crate rustc; extern crate rustc_back; extern crate rustc_data_structures; +extern crate rustc_incremental; pub extern crate rustc_llvm as llvm; extern crate rustc_mir; extern crate rustc_platform_intrinsics as intrinsics; @@ -85,7 +86,6 @@ mod macros; mod abi; mod adt; mod asm; -mod assert_dep_graph; mod attributes; mod base; mod basic_block; From a9b6205aff943d44820df53d63d14a06b319b9dd Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 28 Mar 2016 17:37:34 -0400 Subject: [PATCH 03/20] break dep-graph into modules, parameterize DepNode it is useful later to customize how change the type we use for reference items away from DefId --- src/librustc/dep_graph/dep_node.rs | 207 +++++++++++++++++++++ src/librustc/dep_graph/dep_tracking_map.rs | 3 +- src/librustc/dep_graph/edges.rs | 28 +-- src/librustc/dep_graph/graph.rs | 71 +++++++ src/librustc/dep_graph/mod.rs | 206 +------------------- src/librustc/dep_graph/query.rs | 40 +++- src/librustc/dep_graph/raii.rs | 6 +- src/librustc/dep_graph/thread.rs | 15 +- src/librustc/dep_graph/visit.rs | 56 ++++++ src/librustc/hir/map/mod.rs | 2 +- src/librustc/ty/ivar.rs | 7 +- src/librustc/ty/maps.rs | 2 +- src/librustc/ty/mod.rs | 6 +- src/librustc_mir/transform/type_check.rs | 3 +- src/librustc_trans/context.rs | 2 +- 15 files changed, 411 insertions(+), 243 deletions(-) create mode 100644 src/librustc/dep_graph/dep_node.rs create mode 100644 src/librustc/dep_graph/graph.rs create mode 100644 src/librustc/dep_graph/visit.rs diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs new file mode 100644 index 0000000000000..446313f7037f3 --- /dev/null +++ b/src/librustc/dep_graph/dep_node.rs @@ -0,0 +1,207 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::fmt::Debug; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +pub enum DepNode { + // The `D` type is "how definitions are identified". + // During compilation, it is always `DefId`, but when serializing + // it is mapped to `DefPath`. + + // Represents the `Krate` as a whole (the `hir::Krate` value) (as + // distinct from the krate module). This is basically a hash of + // the entire krate, so if you read from `Krate` (e.g., by calling + // `tcx.map.krate()`), we will have to assume that any change + // means that you need to be recompiled. This is because the + // `Krate` value gives you access to all other items. To avoid + // this fate, do not call `tcx.map.krate()`; instead, prefer + // wrappers like `tcx.visit_all_items_in_krate()`. If there is no + // suitable wrapper, you can use `tcx.dep_graph.ignore()` to gain + // access to the krate, but you must remember to add suitable + // edges yourself for the individual items that you read. + Krate, + + // Represents the HIR node with the given node-id + Hir(D), + + // Represents different phases in the compiler. + CrateReader, + CollectLanguageItems, + CheckStaticRecursion, + ResolveLifetimes, + RegionResolveCrate, + CheckLoops, + PluginRegistrar, + StabilityIndex, + CollectItem(D), + Coherence, + EffectCheck, + Liveness, + Resolve, + EntryPoint, + CheckEntryFn, + CoherenceCheckImpl(D), + CoherenceOverlapCheck(D), + CoherenceOverlapCheckSpecial(D), + CoherenceOverlapInherentCheck(D), + CoherenceOrphanCheck(D), + Variance, + WfCheck(D), + TypeckItemType(D), + TypeckItemBody(D), + Dropck, + DropckImpl(D), + CheckConst(D), + Privacy, + IntrinsicCheck(D), + MatchCheck(D), + MirMapConstruction(D), + MirTypeck(D), + BorrowCheck(D), + RvalueCheck(D), + Reachability, + DeadCheck, + StabilityCheck, + LateLintCheck, + IntrinsicUseCheck, + TransCrate, + TransCrateItem(D), + TransInlinedItem(D), + TransWriteMetadata, + + // Nodes representing bits of computed IR in the tcx. Each shared + // table in the tcx (or elsewhere) maps to one of these + // nodes. Often we map multiple tables to the same node if there + // is no point in distinguishing them (e.g., both the type and + // predicates for an item wind up in `ItemSignature`). Other + // times, such as `ImplItems` vs `TraitItemDefIds`, tables which + // might be mergable are kept distinct because the sets of def-ids + // to which they apply are disjoint, and hence we might as well + // have distinct labels for easier debugging. + ImplOrTraitItems(D), + ItemSignature(D), + FieldTy(D), + TraitItemDefIds(D), + InherentImpls(D), + ImplItems(D), + + // The set of impls for a given trait. Ultimately, it would be + // nice to get more fine-grained here (e.g., to include a + // simplified type), but we can't do that until we restructure the + // HIR to distinguish the *header* of an impl from its body. This + // is because changes to the header may change the self-type of + // the impl and hence would require us to be more conservative + // than changes in the impl body. + TraitImpls(D), + + // Nodes representing caches. To properly handle a true cache, we + // don't use a DepTrackingMap, but rather we push a task node. + // Otherwise the write into the map would be incorrectly + // attributed to the first task that happened to fill the cache, + // which would yield an overly conservative dep-graph. + TraitItems(D), + ReprHints(D), + TraitSelect(D), +} + +impl DepNode { + /// Used in testing + pub fn from_label_string(label: &str, data: D) -> Result, ()> { + macro_rules! check { + ($($name:ident,)*) => { + match label { + $(stringify!($name) => Ok(DepNode::$name(data)),)* + _ => Err(()) + } + } + } + + check! { + CollectItem, + BorrowCheck, + TransCrateItem, + TypeckItemType, + TypeckItemBody, + ImplOrTraitItems, + ItemSignature, + FieldTy, + TraitItemDefIds, + InherentImpls, + ImplItems, + TraitImpls, + ReprHints, + } + } + + pub fn map_def(&self, mut op: OP) -> Option> + where OP: FnMut(&D) -> Option, E: Clone + Debug + { + use self::DepNode::*; + + match *self { + Krate => Some(Krate), + CrateReader => Some(CrateReader), + CollectLanguageItems => Some(CollectLanguageItems), + CheckStaticRecursion => Some(CheckStaticRecursion), + ResolveLifetimes => Some(ResolveLifetimes), + RegionResolveCrate => Some(RegionResolveCrate), + CheckLoops => Some(CheckLoops), + PluginRegistrar => Some(PluginRegistrar), + StabilityIndex => Some(StabilityIndex), + Coherence => Some(Coherence), + EffectCheck => Some(EffectCheck), + Liveness => Some(Liveness), + Resolve => Some(Resolve), + EntryPoint => Some(EntryPoint), + CheckEntryFn => Some(CheckEntryFn), + Variance => Some(Variance), + Dropck => Some(Dropck), + Privacy => Some(Privacy), + Reachability => Some(Reachability), + DeadCheck => Some(DeadCheck), + StabilityCheck => Some(StabilityCheck), + LateLintCheck => Some(LateLintCheck), + IntrinsicUseCheck => Some(IntrinsicUseCheck), + TransCrate => Some(TransCrate), + TransWriteMetadata => Some(TransWriteMetadata), + Hir(ref d) => op(d).map(Hir), + CollectItem(ref d) => op(d).map(CollectItem), + CoherenceCheckImpl(ref d) => op(d).map(CoherenceCheckImpl), + CoherenceOverlapCheck(ref d) => op(d).map(CoherenceOverlapCheck), + CoherenceOverlapCheckSpecial(ref d) => op(d).map(CoherenceOverlapCheckSpecial), + CoherenceOverlapInherentCheck(ref d) => op(d).map(CoherenceOverlapInherentCheck), + CoherenceOrphanCheck(ref d) => op(d).map(CoherenceOrphanCheck), + WfCheck(ref d) => op(d).map(WfCheck), + TypeckItemType(ref d) => op(d).map(TypeckItemType), + TypeckItemBody(ref d) => op(d).map(TypeckItemBody), + DropckImpl(ref d) => op(d).map(DropckImpl), + CheckConst(ref d) => op(d).map(CheckConst), + IntrinsicCheck(ref d) => op(d).map(IntrinsicCheck), + MatchCheck(ref d) => op(d).map(MatchCheck), + MirMapConstruction(ref d) => op(d).map(MirMapConstruction), + MirTypeck(ref d) => op(d).map(MirTypeck), + BorrowCheck(ref d) => op(d).map(BorrowCheck), + RvalueCheck(ref d) => op(d).map(RvalueCheck), + TransCrateItem(ref d) => op(d).map(TransCrateItem), + TransInlinedItem(ref d) => op(d).map(TransInlinedItem), + ImplOrTraitItems(ref d) => op(d).map(ImplOrTraitItems), + ItemSignature(ref d) => op(d).map(ItemSignature), + FieldTy(ref d) => op(d).map(FieldTy), + TraitItemDefIds(ref d) => op(d).map(TraitItemDefIds), + InherentImpls(ref d) => op(d).map(InherentImpls), + ImplItems(ref d) => op(d).map(ImplItems), + TraitImpls(ref d) => op(d).map(TraitImpls), + TraitItems(ref d) => op(d).map(TraitItems), + ReprHints(ref d) => op(d).map(ReprHints), + TraitSelect(ref d) => op(d).map(TraitSelect), + } + } +} diff --git a/src/librustc/dep_graph/dep_tracking_map.rs b/src/librustc/dep_graph/dep_tracking_map.rs index c49e64f0f543b..922d32a306796 100644 --- a/src/librustc/dep_graph/dep_tracking_map.rs +++ b/src/librustc/dep_graph/dep_tracking_map.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use hir::def_id::DefId; use rustc_data_structures::fnv::FnvHashMap; use std::cell::RefCell; use std::ops::Index; @@ -29,7 +30,7 @@ pub struct DepTrackingMap { pub trait DepTrackingMapConfig { type Key: Eq + Hash + Clone; type Value: Clone; - fn to_dep_node(key: &Self::Key) -> DepNode; + fn to_dep_node(key: &Self::Key) -> DepNode; } impl DepTrackingMap { diff --git a/src/librustc/dep_graph/edges.rs b/src/librustc/dep_graph/edges.rs index d3ced8aa518bb..10f3d21f2af6d 100644 --- a/src/librustc/dep_graph/edges.rs +++ b/src/librustc/dep_graph/edges.rs @@ -9,11 +9,13 @@ // except according to those terms. use rustc_data_structures::fnv::{FnvHashMap, FnvHashSet}; +use std::fmt::Debug; +use std::hash::Hash; use super::{DepGraphQuery, DepNode}; -pub struct DepGraphEdges { - nodes: Vec, - indices: FnvHashMap, +pub struct DepGraphEdges { + nodes: Vec>, + indices: FnvHashMap, IdIndex>, edges: FnvHashSet<(IdIndex, IdIndex)>, open_nodes: Vec, } @@ -40,8 +42,8 @@ enum OpenNode { Ignore, } -impl DepGraphEdges { - pub fn new() -> DepGraphEdges { +impl DepGraphEdges { + pub fn new() -> DepGraphEdges { DepGraphEdges { nodes: vec![], indices: FnvHashMap(), @@ -50,12 +52,12 @@ impl DepGraphEdges { } } - fn id(&self, index: IdIndex) -> DepNode { - self.nodes[index.index()] + fn id(&self, index: IdIndex) -> DepNode { + self.nodes[index.index()].clone() } /// Creates a node for `id` in the graph. - fn make_node(&mut self, id: DepNode) -> IdIndex { + fn make_node(&mut self, id: DepNode) -> IdIndex { if let Some(&i) = self.indices.get(&id) { return i; } @@ -80,7 +82,7 @@ impl DepGraphEdges { assert_eq!(popped_node, OpenNode::Ignore); } - pub fn push_task(&mut self, key: DepNode) { + pub fn push_task(&mut self, key: DepNode) { let top_node = self.current_node(); let new_node = self.make_node(key); @@ -93,7 +95,7 @@ impl DepGraphEdges { } } - pub fn pop_task(&mut self, key: DepNode) { + pub fn pop_task(&mut self, key: DepNode) { let popped_node = self.open_nodes.pop().unwrap(); assert_eq!(OpenNode::Node(self.indices[&key]), popped_node); } @@ -101,7 +103,7 @@ impl DepGraphEdges { /// Indicates that the current task `C` reads `v` by adding an /// edge from `v` to `C`. If there is no current task, panics. If /// you want to suppress this edge, use `ignore`. - pub fn read(&mut self, v: DepNode) { + pub fn read(&mut self, v: DepNode) { let source = self.make_node(v); self.add_edge_from_current_node(|current| (source, current)) } @@ -109,7 +111,7 @@ impl DepGraphEdges { /// Indicates that the current task `C` writes `v` by adding an /// edge from `C` to `v`. If there is no current task, panics. If /// you want to suppress this edge, use `ignore`. - pub fn write(&mut self, v: DepNode) { + pub fn write(&mut self, v: DepNode) { let target = self.make_node(v); self.add_edge_from_current_node(|current| (current, target)) } @@ -153,7 +155,7 @@ impl DepGraphEdges { } } - pub fn query(&self) -> DepGraphQuery { + pub fn query(&self) -> DepGraphQuery { let edges: Vec<_> = self.edges.iter() .map(|&(i, j)| (self.id(i), self.id(j))) .collect(); diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs new file mode 100644 index 0000000000000..741ad65c29fda --- /dev/null +++ b/src/librustc/dep_graph/graph.rs @@ -0,0 +1,71 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use hir::def_id::DefId; +use std::rc::Rc; + +use super::dep_node::DepNode; +use super::query::DepGraphQuery; +use super::raii; +use super::thread::{DepGraphThreadData, DepMessage}; + +#[derive(Clone)] +pub struct DepGraph { + data: Rc +} + +impl DepGraph { + pub fn new(enabled: bool) -> DepGraph { + DepGraph { + data: Rc::new(DepGraphThreadData::new(enabled)) + } + } + + /// True if we are actually building a dep-graph. If this returns false, + /// then the other methods on this `DepGraph` will have no net effect. + #[inline] + pub fn enabled(&self) -> bool { + self.data.enabled() + } + + pub fn query(&self) -> DepGraphQuery { + self.data.query() + } + + pub fn in_ignore<'graph>(&'graph self) -> raii::IgnoreTask<'graph> { + raii::IgnoreTask::new(&self.data) + } + + pub fn in_task<'graph>(&'graph self, key: DepNode) -> raii::DepTask<'graph> { + raii::DepTask::new(&self.data, key) + } + + pub fn with_ignore(&self, op: OP) -> R + where OP: FnOnce() -> R + { + let _task = self.in_ignore(); + op() + } + + pub fn with_task(&self, key: DepNode, op: OP) -> R + where OP: FnOnce() -> R + { + let _task = self.in_task(key); + op() + } + + pub fn read(&self, v: DepNode) { + self.data.enqueue(DepMessage::Read(v)); + } + + pub fn write(&self, v: DepNode) { + self.data.enqueue(DepMessage::Write(v)); + } +} diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs index 55ec56a4bbe66..49481dcb79672 100644 --- a/src/librustc/dep_graph/mod.rs +++ b/src/librustc/dep_graph/mod.rs @@ -8,211 +8,17 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use self::thread::{DepGraphThreadData, DepMessage}; -use hir::def_id::DefId; -use syntax::ast::NodeId; -use ty::TyCtxt; -use hir; -use hir::intravisit::Visitor; -use std::rc::Rc; - +mod dep_node; mod dep_tracking_map; mod edges; +mod graph; mod query; mod raii; mod thread; - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum DepNode { - // Represents the `Krate` as a whole (the `hir::Krate` value) (as - // distinct from the krate module). This is basically a hash of - // the entire krate, so if you read from `Krate` (e.g., by calling - // `tcx.map.krate()`), we will have to assume that any change - // means that you need to be recompiled. This is because the - // `Krate` value gives you access to all other items. To avoid - // this fate, do not call `tcx.map.krate()`; instead, prefer - // wrappers like `tcx.visit_all_items_in_krate()`. If there is no - // suitable wrapper, you can use `tcx.dep_graph.ignore()` to gain - // access to the krate, but you must remember to add suitable - // edges yourself for the individual items that you read. - Krate, - - // Represents the HIR node with the given node-id - Hir(DefId), - - // Represents different phases in the compiler. - CrateReader, - CollectLanguageItems, - CheckStaticRecursion, - ResolveLifetimes, - RegionResolveCrate, - CheckLoops, - PluginRegistrar, - StabilityIndex, - CollectItem(DefId), - Coherence, - EffectCheck, - Liveness, - Resolve, - EntryPoint, - CheckEntryFn, - CoherenceCheckImpl(DefId), - CoherenceOverlapCheck(DefId), - CoherenceOverlapCheckSpecial(DefId), - CoherenceOverlapInherentCheck(DefId), - CoherenceOrphanCheck(DefId), - Variance, - WfCheck(DefId), - TypeckItemType(DefId), - TypeckItemBody(DefId), - Dropck, - DropckImpl(DefId), - CheckConst(DefId), - Privacy, - IntrinsicCheck(DefId), - MatchCheck(DefId), - MirMapConstruction(DefId), - MirTypeck(NodeId), - BorrowCheck(DefId), - RvalueCheck(DefId), - Reachability, - DeadCheck, - StabilityCheck, - LateLintCheck, - IntrinsicUseCheck, - TransCrate, - TransCrateItem(DefId), - TransInlinedItem(DefId), - TransWriteMetadata, - - // Nodes representing bits of computed IR in the tcx. Each shared - // table in the tcx (or elsewhere) maps to one of these - // nodes. Often we map multiple tables to the same node if there - // is no point in distinguishing them (e.g., both the type and - // predicates for an item wind up in `ItemSignature`). Other - // times, such as `ImplItems` vs `TraitItemDefIds`, tables which - // might be mergable are kept distinct because the sets of def-ids - // to which they apply are disjoint, and hence we might as well - // have distinct labels for easier debugging. - ImplOrTraitItems(DefId), - ItemSignature(DefId), - FieldTy(DefId), - TraitItemDefIds(DefId), - InherentImpls(DefId), - ImplItems(DefId), - - // The set of impls for a given trait. Ultimately, it would be - // nice to get more fine-grained here (e.g., to include a - // simplified type), but we can't do that until we restructure the - // HIR to distinguish the *header* of an impl from its body. This - // is because changes to the header may change the self-type of - // the impl and hence would require us to be more conservative - // than changes in the impl body. - TraitImpls(DefId), - - // Nodes representing caches. To properly handle a true cache, we - // don't use a DepTrackingMap, but rather we push a task node. - // Otherwise the write into the map would be incorrectly - // attributed to the first task that happened to fill the cache, - // which would yield an overly conservative dep-graph. - TraitItems(DefId), - ReprHints(DefId), - TraitSelect(DefId), -} - -#[derive(Clone)] -pub struct DepGraph { - data: Rc -} - -impl DepGraph { - pub fn new(enabled: bool) -> DepGraph { - DepGraph { - data: Rc::new(DepGraphThreadData::new(enabled)) - } - } - - /// True if we are actually building a dep-graph. If this returns false, - /// then the other methods on this `DepGraph` will have no net effect. - #[inline] - pub fn enabled(&self) -> bool { - self.data.enabled() - } - - pub fn query(&self) -> DepGraphQuery { - self.data.query() - } - - pub fn in_ignore<'graph>(&'graph self) -> raii::IgnoreTask<'graph> { - raii::IgnoreTask::new(&self.data) - } - - pub fn in_task<'graph>(&'graph self, key: DepNode) -> raii::DepTask<'graph> { - raii::DepTask::new(&self.data, key) - } - - pub fn with_ignore(&self, op: OP) -> R - where OP: FnOnce() -> R - { - let _task = self.in_ignore(); - op() - } - - pub fn with_task(&self, key: DepNode, op: OP) -> R - where OP: FnOnce() -> R - { - let _task = self.in_task(key); - op() - } - - pub fn read(&self, v: DepNode) { - self.data.enqueue(DepMessage::Read(v)); - } - - pub fn write(&self, v: DepNode) { - self.data.enqueue(DepMessage::Write(v)); - } -} +mod visit; pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig}; - +pub use self::dep_node::DepNode; +pub use self::graph::DepGraph; pub use self::query::DepGraphQuery; - -/// Visit all the items in the krate in some order. When visiting a -/// particular item, first create a dep-node by calling `dep_node_fn` -/// and push that onto the dep-graph stack of tasks, and also create a -/// read edge from the corresponding AST node. This is used in -/// compiler passes to automatically record the item that they are -/// working on. -pub fn visit_all_items_in_krate<'tcx,V,F>(tcx: &TyCtxt<'tcx>, - mut dep_node_fn: F, - visitor: &mut V) - where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> -{ - struct TrackingVisitor<'visit, 'tcx: 'visit, F: 'visit, V: 'visit> { - tcx: &'visit TyCtxt<'tcx>, - dep_node_fn: &'visit mut F, - visitor: &'visit mut V - } - - impl<'visit, 'tcx, F, V> Visitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V> - where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> - { - fn visit_item(&mut self, i: &'tcx hir::Item) { - let item_def_id = self.tcx.map.local_def_id(i.id); - let task_id = (self.dep_node_fn)(item_def_id); - let _task = self.tcx.dep_graph.in_task(task_id); - debug!("Started task {:?}", task_id); - self.tcx.dep_graph.read(DepNode::Hir(item_def_id)); - self.visitor.visit_item(i) - } - } - - let krate = tcx.dep_graph.with_ignore(|| tcx.map.krate()); - let mut tracking_visitor = TrackingVisitor { - tcx: tcx, - dep_node_fn: &mut dep_node_fn, - visitor: visitor - }; - krate.visit_all_items(&mut tracking_visitor) -} +pub use self::visit::visit_all_items_in_krate; diff --git a/src/librustc/dep_graph/query.rs b/src/librustc/dep_graph/query.rs index 74a054acb4fa0..acc6660da6e89 100644 --- a/src/librustc/dep_graph/query.rs +++ b/src/librustc/dep_graph/query.rs @@ -10,16 +10,20 @@ use rustc_data_structures::fnv::FnvHashMap; use rustc_data_structures::graph::{Graph, NodeIndex}; +use std::fmt::Debug; +use std::hash::Hash; use super::DepNode; -pub struct DepGraphQuery { - pub graph: Graph, - pub indices: FnvHashMap, +pub struct DepGraphQuery { + pub graph: Graph, ()>, + pub indices: FnvHashMap, NodeIndex>, } -impl DepGraphQuery { - pub fn new(nodes: &[DepNode], edges: &[(DepNode, DepNode)]) -> DepGraphQuery { +impl DepGraphQuery { + pub fn new(nodes: &[DepNode], + edges: &[(DepNode, DepNode)]) + -> DepGraphQuery { let mut graph = Graph::new(); let mut indices = FnvHashMap(); for node in nodes { @@ -39,27 +43,43 @@ impl DepGraphQuery { } } - pub fn nodes(&self) -> Vec { + pub fn contains_node(&self, node: &DepNode) -> bool { + self.indices.contains_key(&node) + } + + pub fn nodes(&self) -> Vec> { self.graph.all_nodes() .iter() .map(|n| n.data.clone()) .collect() } - pub fn edges(&self) -> Vec<(DepNode,DepNode)> { + pub fn edges(&self) -> Vec<(DepNode,DepNode)> { self.graph.all_edges() .iter() .map(|edge| (edge.source(), edge.target())) - .map(|(s, t)| (self.graph.node_data(s).clone(), self.graph.node_data(t).clone())) + .map(|(s, t)| (self.graph.node_data(s).clone(), + self.graph.node_data(t).clone())) .collect() } /// All nodes reachable from `node`. In other words, things that /// will have to be recomputed if `node` changes. - pub fn dependents(&self, node: DepNode) -> Vec { + pub fn transitive_dependents(&self, node: DepNode) -> Vec> { if let Some(&index) = self.indices.get(&node) { self.graph.depth_traverse(index) - .map(|dependent_node| self.graph.node_data(dependent_node).clone()) + .map(|s| self.graph.node_data(s).clone()) + .collect() + } else { + vec![] + } + } + + /// Just the outgoing edges from `node`. + pub fn immediate_dependents(&self, node: DepNode) -> Vec> { + if let Some(&index) = self.indices.get(&node) { + self.graph.successor_nodes(index) + .map(|s| self.graph.node_data(s).clone()) .collect() } else { vec![] diff --git a/src/librustc/dep_graph/raii.rs b/src/librustc/dep_graph/raii.rs index dd7ff92f9c360..13151d169fc3f 100644 --- a/src/librustc/dep_graph/raii.rs +++ b/src/librustc/dep_graph/raii.rs @@ -8,16 +8,18 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use hir::def_id::DefId; use super::DepNode; use super::thread::{DepGraphThreadData, DepMessage}; pub struct DepTask<'graph> { data: &'graph DepGraphThreadData, - key: DepNode, + key: DepNode, } impl<'graph> DepTask<'graph> { - pub fn new(data: &'graph DepGraphThreadData, key: DepNode) -> DepTask<'graph> { + pub fn new(data: &'graph DepGraphThreadData, key: DepNode) + -> DepTask<'graph> { data.enqueue(DepMessage::PushTask(key)); DepTask { data: data, key: key } } diff --git a/src/librustc/dep_graph/thread.rs b/src/librustc/dep_graph/thread.rs index 1b1d3469bc53f..5b0e4a909c8d3 100644 --- a/src/librustc/dep_graph/thread.rs +++ b/src/librustc/dep_graph/thread.rs @@ -18,6 +18,7 @@ //! to accumulate more messages. This way we only ever have two vectors //! allocated (and both have a fairly large capacity). +use hir::def_id::DefId; use rustc_data_structures::veccell::VecCell; use std::cell::Cell; use std::sync::mpsc::{self, Sender, Receiver}; @@ -28,10 +29,10 @@ use super::DepNode; use super::edges::DepGraphEdges; pub enum DepMessage { - Read(DepNode), - Write(DepNode), - PushTask(DepNode), - PopTask(DepNode), + Read(DepNode), + Write(DepNode), + PushTask(DepNode), + PopTask(DepNode), PushIgnore, PopIgnore, Query, @@ -57,7 +58,7 @@ pub struct DepGraphThreadData { swap_out: Sender>, // where to receive query results - query_in: Receiver, + query_in: Receiver>, } const INITIAL_CAPACITY: usize = 2048; @@ -105,7 +106,7 @@ impl DepGraphThreadData { self.swap_out.send(old_messages).unwrap(); } - pub fn query(&self) -> DepGraphQuery { + pub fn query(&self) -> DepGraphQuery { assert!(self.enabled, "cannot query if dep graph construction not enabled"); self.enqueue(DepMessage::Query); self.swap(); @@ -155,7 +156,7 @@ impl DepGraphThreadData { /// Definition of the depgraph thread. pub fn main(swap_in: Receiver>, swap_out: Sender>, - query_out: Sender) { + query_out: Sender>) { let mut edges = DepGraphEdges::new(); // the compiler thread always expects a fresh buffer to be diff --git a/src/librustc/dep_graph/visit.rs b/src/librustc/dep_graph/visit.rs new file mode 100644 index 0000000000000..8ce177efe92a5 --- /dev/null +++ b/src/librustc/dep_graph/visit.rs @@ -0,0 +1,56 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use hir::def_id::DefId; +use ty::TyCtxt; +use rustc_front::hir; +use rustc_front::intravisit::Visitor; + +use super::dep_node::DepNode; + + +/// Visit all the items in the krate in some order. When visiting a +/// particular item, first create a dep-node by calling `dep_node_fn` +/// and push that onto the dep-graph stack of tasks, and also create a +/// read edge from the corresponding AST node. This is used in +/// compiler passes to automatically record the item that they are +/// working on. +pub fn visit_all_items_in_krate<'tcx,V,F>(tcx: &TyCtxt<'tcx>, + mut dep_node_fn: F, + visitor: &mut V) + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> +{ + struct TrackingVisitor<'visit, 'tcx: 'visit, F: 'visit, V: 'visit> { + tcx: &'visit TyCtxt<'tcx>, + dep_node_fn: &'visit mut F, + visitor: &'visit mut V + } + + impl<'visit, 'tcx, F, V> Visitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V> + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> + { + fn visit_item(&mut self, i: &'tcx hir::Item) { + let item_def_id = self.tcx.map.local_def_id(i.id); + let task_id = (self.dep_node_fn)(item_def_id); + let _task = self.tcx.dep_graph.in_task(task_id); + debug!("Started task {:?}", task_id); + self.tcx.dep_graph.read(DepNode::Hir(item_def_id)); + self.visitor.visit_item(i) + } + } + + let krate = tcx.dep_graph.with_ignore(|| tcx.map.krate()); + let mut tracking_visitor = TrackingVisitor { + tcx: tcx, + dep_node_fn: &mut dep_node_fn, + visitor: visitor + }; + krate.visit_all_items(&mut tracking_visitor) +} diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index e1b7afda58bbe..044fe7767f4cc 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -208,7 +208,7 @@ impl<'ast> Map<'ast> { self.dep_graph.read(self.dep_node(id)); } - fn dep_node(&self, id0: NodeId) -> DepNode { + fn dep_node(&self, id0: NodeId) -> DepNode { let map = self.map.borrow(); let mut id = id0; loop { diff --git a/src/librustc/ty/ivar.rs b/src/librustc/ty/ivar.rs index b0f443fc19b42..88327ab19a5cb 100644 --- a/src/librustc/ty/ivar.rs +++ b/src/librustc/ty/ivar.rs @@ -9,6 +9,7 @@ // except according to those terms. use dep_graph::DepNode; +use hir::def_id::DefId; use ty::{Ty, TyS}; use ty::tls; @@ -46,7 +47,7 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> { } #[inline] - pub fn get(&self, dep_node: DepNode) -> Option> { + pub fn get(&self, dep_node: DepNode) -> Option> { tls::with(|tcx| tcx.dep_graph.read(dep_node)); self.untracked_get() } @@ -61,11 +62,11 @@ impl<'tcx, 'lt> TyIVar<'tcx, 'lt> { } #[inline] - pub fn unwrap(&self, dep_node: DepNode) -> Ty<'tcx> { + pub fn unwrap(&self, dep_node: DepNode) -> Ty<'tcx> { self.get(dep_node).unwrap() } - pub fn fulfill(&self, dep_node: DepNode, value: Ty<'lt>) { + pub fn fulfill(&self, dep_node: DepNode, value: Ty<'lt>) { tls::with(|tcx| tcx.dep_graph.write(dep_node)); // Invariant (A) is fulfilled, because by (B), every alias diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index 65a96e79ff43c..57b1dd66bea9d 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -24,7 +24,7 @@ macro_rules! dep_map_ty { impl<'tcx> DepTrackingMapConfig for $ty_name<'tcx> { type Key = $key; type Value = $value; - fn to_dep_node(key: &$key) -> DepNode { DepNode::$node_name(*key) } + fn to_dep_node(key: &$key) -> DepNode { DepNode::$node_name(*key) } } } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 444fea0918f8d..46615fca90429 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -887,7 +887,7 @@ impl<'tcx> TraitPredicate<'tcx> { } /// Creates the dep-node for selecting/evaluating this trait reference. - fn dep_node(&self) -> DepNode { + fn dep_node(&self) -> DepNode { DepNode::TraitSelect(self.def_id()) } @@ -906,7 +906,7 @@ impl<'tcx> PolyTraitPredicate<'tcx> { self.0.def_id() } - pub fn dep_node(&self) -> DepNode { + pub fn dep_node(&self) -> DepNode { // ok to skip binder since depnode does not care about regions self.0.dep_node() } @@ -2666,7 +2666,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn visit_all_items_in_krate(&self, dep_node_fn: F, visitor: &mut V) - where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> + where F: FnMut(DefId) -> DepNode, V: Visitor<'tcx> { dep_graph::visit_all_items_in_krate(self, dep_node_fn, visitor); } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index ce8ede7f4b959..11ac1fa8f82a1 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -584,7 +584,8 @@ impl<'tcx> MirPass<'tcx> for TypeckMir { // broken MIR, so try not to report duplicate errors. return; } - let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(id)); + let def_id = tcx.map.local_def_id(id); + let _task = tcx.dep_graph.in_task(DepNode::MirTypeck(def_id)); let param_env = ty::ParameterEnvironment::for_item(tcx, id); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index 9bbc72eba36ef..c1802a5f0a9c2 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -179,7 +179,7 @@ pub struct TraitSelectionCache<'tcx> { impl<'tcx> DepTrackingMapConfig for TraitSelectionCache<'tcx> { type Key = ty::PolyTraitRef<'tcx>; type Value = traits::Vtable<'tcx, ()>; - fn to_dep_node(key: &ty::PolyTraitRef<'tcx>) -> DepNode { + fn to_dep_node(key: &ty::PolyTraitRef<'tcx>) -> DepNode { ty::tls::with(|tcx| { let lifted_key = tcx.lift(key).unwrap(); lifted_key.to_poly_trait_predicate().dep_node() From d8263c4758e8821d87d4fc300fd30e78457c769f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 28 Mar 2016 17:39:57 -0400 Subject: [PATCH 04/20] add function to retrace a DefPath to a DefId used after loading state from previous compilation --- src/librustc/hir/map/definitions.rs | 44 +++++++++++++++++++++++++++++ src/librustc/hir/map/mod.rs | 5 ++++ 2 files changed, 49 insertions(+) diff --git a/src/librustc/hir/map/definitions.rs b/src/librustc/hir/map/definitions.rs index d045313cb1e97..2e26fe5057e2f 100644 --- a/src/librustc/hir/map/definitions.rs +++ b/src/librustc/hir/map/definitions.rs @@ -203,17 +203,56 @@ impl Definitions { } } + pub fn retrace_path(&self, path: &DefPath) -> Option { + debug!("retrace_path(path={:?})", path); + + // we assume that we only want to retrace paths relative to + // the crate root + assert!(path.is_local()); + + let root_key = DefKey { + parent: None, + disambiguated_data: DisambiguatedDefPathData { + data: DefPathData::CrateRoot, + disambiguator: 0, + }, + }; + let root_id = self.key_map[&root_key]; + + debug!("retrace_path: root_id={:?}", root_id); + + let mut id = root_id; + for data in &path.data { + let key = DefKey { parent: Some(id), disambiguated_data: data.clone() }; + debug!("key = {:?}", key); + id = match self.key_map.get(&key) { + Some(&id) => id, + None => return None + }; + } + + Some(id) + } + pub fn create_def_with_parent(&mut self, parent: Option, node_id: ast::NodeId, data: DefPathData) -> DefIndex { + debug!("create_def_with_parent(parent={:?}, node_id={:?}, data={:?})", + parent, node_id, data); + assert!(!self.node_map.contains_key(&node_id), "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}", node_id, data, self.data[self.node_map[&node_id].as_usize()]); + assert!(parent.is_some() ^ match data { + DefPathData::CrateRoot | DefPathData::InlinedRoot(_) => true, + _ => false, + }); + // Find a unique DefKey. This basically means incrementing the disambiguator // until we get no match. let mut key = DefKey { @@ -228,12 +267,17 @@ impl Definitions { key.disambiguated_data.disambiguator += 1; } + debug!("create_def_with_parent: after disambiguation, key = {:?}", key); + // Create the definition. let index = DefIndex::new(self.data.len()); self.data.push(DefData { key: key.clone(), node_id: node_id }); + debug!("create_def_with_parent: node_map[{:?}] = {:?}", node_id, index); self.node_map.insert(node_id, index); + debug!("create_def_with_parent: key_map[{:?}] = {:?}", key, index); self.key_map.insert(key, index); + index } } diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 044fe7767f4cc..644591d22c815 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -282,6 +282,11 @@ impl<'ast> Map<'ast> { self.definitions.borrow().def_path(def_id.index) } + pub fn retrace_path(&self, path: &DefPath) -> Option { + self.definitions.borrow().retrace_path(path) + .map(DefId::local) + } + pub fn local_def_id(&self, node: NodeId) -> DefId { self.opt_local_def_id(node).unwrap_or_else(|| { bug!("local_def_id: no entry for `{}`, which has a map of `{:?}`", From 3fb40c1d95dd3a14ad14d71e3ec847a0a102bd49 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 28 Mar 2016 17:42:39 -0400 Subject: [PATCH 05/20] add code to persist graph and for unit-testing --- src/librustc_driver/driver.rs | 8 + src/librustc_incremental/persist/data.rs | 35 +++ src/librustc_incremental/persist/directory.rs | 118 ++++++++++ .../persist/dirty_clean.rs | 151 ++++++++++++ src/librustc_incremental/persist/load.rs | 221 ++++++++++++++++++ src/librustc_incremental/persist/mod.rs | 23 ++ src/librustc_incremental/persist/save.rs | 142 +++++++++++ src/librustc_incremental/persist/serialize.rs | 0 src/librustc_incremental/persist/util.rs | 36 +++ src/libsyntax/feature_gate.rs | 8 + 10 files changed, 742 insertions(+) create mode 100644 src/librustc_incremental/persist/data.rs create mode 100644 src/librustc_incremental/persist/directory.rs create mode 100644 src/librustc_incremental/persist/dirty_clean.rs create mode 100644 src/librustc_incremental/persist/load.rs create mode 100644 src/librustc_incremental/persist/mod.rs create mode 100644 src/librustc_incremental/persist/save.rs create mode 100644 src/librustc_incremental/persist/serialize.rs create mode 100644 src/librustc_incremental/persist/util.rs diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f35d5f259621f..67c52bb6c36d7 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -829,6 +829,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, index, name, |tcx| { + time(time_passes, + "load_dep_graph", + || rustc_incremental::load_dep_graph(tcx)); + // passes are timed inside typeck try_with_f!(typeck::check_crate(tcx, trait_map), (tcx, None, analysis)); @@ -962,6 +966,10 @@ pub fn phase_4_translate_to_llvm<'tcx>(tcx: &TyCtxt<'tcx>, "assert dep graph", move || rustc_incremental::assert_dep_graph(tcx)); + time(time_passes, + "serialize dep graph", + move || rustc_incremental::save_dep_graph(tcx)); + translation } diff --git a/src/librustc_incremental/persist/data.rs b/src/librustc_incremental/persist/data.rs new file mode 100644 index 0000000000000..8be8bd60b2367 --- /dev/null +++ b/src/librustc_incremental/persist/data.rs @@ -0,0 +1,35 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The data that we will serialize and deserialize. + +use rustc::dep_graph::DepNode; +use rustc_serialize::{Decoder as RustcDecoder, + Encodable as RustcEncodable, Encoder as RustcEncoder}; + +use super::directory::DefPathIndex; + +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct SerializedDepGraph { + pub nodes: Vec>, + pub edges: Vec, + pub hashes: Vec, +} + +pub type SerializedEdge = (DepNode, DepNode); + +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct SerializedHash { + pub index: DefPathIndex, + + /// the hash itself, computed by `calculate_item_hash` + pub hash: u64, +} + diff --git a/src/librustc_incremental/persist/directory.rs b/src/librustc_incremental/persist/directory.rs new file mode 100644 index 0000000000000..f4d155962d1f2 --- /dev/null +++ b/src/librustc_incremental/persist/directory.rs @@ -0,0 +1,118 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Code to convert a DefId into a DefPath (when serializing) and then +//! back again (when deserializing). Note that the new DefId +//! necessarily will not be the same as the old (and of course the +//! item might even be removed in the meantime). + +use rustc::dep_graph::DepNode; +use rustc::front::map::DefPath; +use rustc::middle::def_id::DefId; +use rustc::ty; +use rustc::util::nodemap::DefIdMap; +use rustc_serialize::{Decoder as RustcDecoder, + Encodable as RustcEncodable, Encoder as RustcEncoder}; +use std::fmt::{self, Debug}; + +/// Index into the DefIdDirectory +#[derive(Copy, Clone, Debug, PartialOrd, Ord, Hash, PartialEq, Eq, + RustcEncodable, RustcDecodable)] +pub struct DefPathIndex { + index: u32 +} + +#[derive(RustcEncodable, RustcDecodable)] +pub struct DefIdDirectory { + // N.B. don't use Removable here because these def-ids are loaded + // directly without remapping, so loading them should not fail. + paths: Vec +} + +impl DefIdDirectory { + pub fn new() -> DefIdDirectory { + DefIdDirectory { paths: vec![] } + } + + pub fn retrace(&self, tcx: &ty::TyCtxt) -> RetracedDefIdDirectory { + let ids = self.paths.iter() + .map(|path| tcx.map.retrace_path(path)) + .collect(); + RetracedDefIdDirectory { ids: ids } + } +} + +#[derive(Debug, RustcEncodable, RustcDecodable)] +pub struct RetracedDefIdDirectory { + ids: Vec> +} + +impl RetracedDefIdDirectory { + pub fn def_id(&self, index: DefPathIndex) -> Option { + self.ids[index.index as usize] + } + + pub fn map(&self, node: DepNode) -> Option> { + node.map_def(|&index| self.def_id(index)) + } +} + +pub struct DefIdDirectoryBuilder<'a,'tcx:'a> { + tcx: &'a ty::TyCtxt<'tcx>, + hash: DefIdMap>, + directory: DefIdDirectory, +} + +impl<'a,'tcx> DefIdDirectoryBuilder<'a,'tcx> { + pub fn new(tcx: &'a ty::TyCtxt<'tcx>) -> DefIdDirectoryBuilder<'a, 'tcx> { + DefIdDirectoryBuilder { + tcx: tcx, + hash: DefIdMap(), + directory: DefIdDirectory::new() + } + } + + pub fn add(&mut self, def_id: DefId) -> Option { + if !def_id.is_local() { + // FIXME(#32015) clarify story about cross-crate dep tracking + return None; + } + + let tcx = self.tcx; + let paths = &mut self.directory.paths; + self.hash.entry(def_id) + .or_insert_with(|| { + let def_path = tcx.def_path(def_id); + if !def_path.is_local() { + return None; + } + let index = paths.len() as u32; + paths.push(def_path); + Some(DefPathIndex { index: index }) + }) + .clone() + } + + pub fn map(&mut self, node: DepNode) -> Option> { + node.map_def(|&def_id| self.add(def_id)) + } + + pub fn into_directory(self) -> DefIdDirectory { + self.directory + } +} + +impl Debug for DefIdDirectory { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt.debug_list() + .entries(self.paths.iter().enumerate()) + .finish() + } +} diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs new file mode 100644 index 0000000000000..0aeddd5b7c879 --- /dev/null +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -0,0 +1,151 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Debugging code to test the state of the dependency graph just +//! after it is loaded from disk. For each node marked with +//! `#[rustc_clean]` or `#[rustc_dirty]`, we will check that a +//! suitable node for that item either appears or does not appear in +//! the dep-graph, as appropriate: +//! +//! - `#[rustc_dirty(label="TypeckItemBody", cfg="rev2")]` if we are +//! in `#[cfg(rev2)]`, then there MUST NOT be a node +//! `DepNode::TypeckItemBody(X)` where `X` is the def-id of the +//! current node. +//! - `#[rustc_clean(label="TypeckItemBody", cfg="rev2")]` same as above, +//! except that the node MUST exist. +//! +//! Errors are reported if we are in the suitable configuration but +//! the required condition is not met. + +use rustc::dep_graph::{DepGraphQuery, DepNode}; +use rustc::middle::def_id::DefId; +use rustc_front::hir; +use rustc_front::intravisit::Visitor; +use syntax::ast::{self, Attribute, MetaItem}; +use syntax::attr::AttrMetaMethods; +use syntax::parse::token::InternedString; +use rustc::ty; + +const DIRTY: &'static str = "rustc_dirty"; +const CLEAN: &'static str = "rustc_clean"; +const LABEL: &'static str = "label"; +const CFG: &'static str = "cfg"; + +pub fn check_dirty_clean_annotations(tcx: &ty::TyCtxt) { + let _ignore = tcx.dep_graph.in_ignore(); + let query = tcx.dep_graph.query(); + let krate = tcx.map.krate(); + krate.visit_all_items(&mut DirtyCleanVisitor { + tcx: tcx, + query: &query, + }); +} + +pub struct DirtyCleanVisitor<'a, 'tcx:'a> { + tcx: &'a ty::TyCtxt<'tcx>, + query: &'a DepGraphQuery, +} + +impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { + fn expect_associated_value(&self, item: &MetaItem) -> InternedString { + if let Some(value) = item.value_str() { + value + } else { + self.tcx.sess.span_fatal( + item.span, + &format!("associated value expected for `{}`", item.name())); + } + } + + /// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan + /// for a `cfg="foo"` attribute and check whether we have a cfg + /// flag called `foo`. + fn check_config(&self, attr: &ast::Attribute) -> bool { + debug!("check_config(attr={:?})", attr); + let config = &self.tcx.map.krate().config; + debug!("check_config: config={:?}", config); + for item in attr.meta_item_list().unwrap_or(&[]) { + if item.check_name(CFG) { + let value = self.expect_associated_value(item); + debug!("check_config: searching for cfg {:?}", value); + for cfg in &config[..] { + if cfg.check_name(&value[..]) { + debug!("check_config: matched {:?}", cfg); + return true; + } + } + } + } + debug!("check_config: no match found"); + return false; + } + + fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode { + for item in attr.meta_item_list().unwrap_or(&[]) { + if item.check_name(LABEL) { + let value = self.expect_associated_value(item); + match DepNode::from_label_string(&value[..], def_id) { + Ok(def_id) => return def_id, + Err(()) => { + self.tcx.sess.span_fatal( + item.span, + &format!("dep-node label `{}` not recognized", value)); + } + } + } + } + + self.tcx.sess.span_fatal(attr.span, "no `label` found"); + } + + fn dep_node_str(&self, dep_node: DepNode) -> DepNode { + dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap() + } + + fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode) { + debug!("assert_dirty({:?})", dep_node); + + if self.query.contains_node(&dep_node) { + let dep_node_str = self.dep_node_str(dep_node); + self.tcx.sess.span_err( + item.span, + &format!("`{:?}` found in dep graph, but should be dirty", dep_node_str)); + } + } + + fn assert_clean(&self, item: &hir::Item, dep_node: DepNode) { + debug!("assert_clean({:?})", dep_node); + + if !self.query.contains_node(&dep_node) { + let dep_node_str = self.dep_node_str(dep_node); + self.tcx.sess.span_err( + item.span, + &format!("`{:?}` not found in dep graph, but should be clean", dep_node_str)); + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item) { + let def_id = self.tcx.map.local_def_id(item.id); + for attr in self.tcx.get_attrs(def_id).iter() { + if attr.check_name(DIRTY) { + if self.check_config(attr) { + self.assert_dirty(item, self.dep_node(attr, def_id)); + } + } else if attr.check_name(CLEAN) { + if self.check_config(attr) { + self.assert_clean(item, self.dep_node(attr, def_id)); + } + } + } + } +} + diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs new file mode 100644 index 0000000000000..588113e2eaaad --- /dev/null +++ b/src/librustc_incremental/persist/load.rs @@ -0,0 +1,221 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Code to save/load the dep-graph from files. + + +use calculate_svh::SvhCalculate; +use rbml::{self, Doc}; +use rbml::reader::{self, DecodeResult, Decoder}; +use rustc::dep_graph::DepNode; +use rustc::middle::def_id::DefId; +use rustc::ty; +use rustc_data_structures::fnv::FnvHashSet; +use rustc_serialize::Decodable as RustcDecodable; +use std::io::Read; +use std::fs::File; +use std::path::Path; + +use super::data::*; +use super::directory::*; +use super::dirty_clean; +use super::util::*; + +type DirtyNodes = FnvHashSet>; + +type CleanEdges = Vec<(DepNode, DepNode)>; + +pub fn load_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>) { + let _ignore = tcx.dep_graph.in_ignore(); + + if let Some(dep_graph) = dep_graph_path(tcx) { + load_dep_graph_if_exists(tcx, &dep_graph); + dirty_clean::check_dirty_clean_annotations(tcx); + } +} + +pub fn load_dep_graph_if_exists<'tcx>(tcx: &ty::TyCtxt<'tcx>, path: &Path) { + if !path.exists() { + return; + } + + let mut data = vec![]; + match + File::open(path) + .and_then(|mut file| file.read_to_end(&mut data)) + { + Ok(_) => { } + Err(err) => { + tcx.sess.err( + &format!("could not load dep-graph from `{}`: {}", + path.display(), err)); + return; + } + } + + match decode_dep_graph(tcx, Doc::new(&data)) { + Ok(dirty) => dirty, + Err(err) => { + tcx.sess.bug( + &format!("decoding error in dep-graph from `{}`: {}", + path.display(), err)); + } + } +} + +pub fn decode_dep_graph<'tcx, 'doc>(tcx: &ty::TyCtxt<'tcx>, doc: rbml::Doc<'doc>) + -> DecodeResult<()> +{ + // First load the directory, which maps the def-ids found + // elsewhere into `DefPath`. We can then refresh the `DefPath` to + // obtain updated def-ids. + let directory = { + let directory_doc = reader::get_doc(doc, DIRECTORY_TAG); + let mut decoder = Decoder::new(directory_doc); + try!(DefIdDirectory::decode(&mut decoder)) + }; + + debug!("decode_dep_graph: directory = {:#?}", directory); + + // Retrace those paths to find their current location (if any). + let retraced = directory.retrace(tcx); + + debug!("decode_dep_graph: retraced = {:#?}", retraced); + + // Deserialize the dep-graph (which will include DefPathIndex entries) + let serialized_dep_graph = { + let dep_graph_doc = reader::get_doc(doc, DEP_GRAPH_TAG); + let mut decoder = Decoder::new(dep_graph_doc); + try!(SerializedDepGraph::decode(&mut decoder)) + }; + + debug!("decode_dep_graph: serialized_dep_graph = {:#?}", serialized_dep_graph); + + // Compute the set of Hir nodes whose data has changed. + let mut dirty_nodes = + initial_dirty_nodes(tcx, &serialized_dep_graph.hashes, &retraced); + + debug!("decode_dep_graph: initial dirty_nodes = {:#?}", dirty_nodes); + + // Find all DepNodes reachable from that core set. This loop + // iterates repeatedly over the list of edges whose source is not + // known to be dirty (`clean_edges`). If it finds an edge whose + // source is dirty, it removes it from that list and adds the + // target to `dirty_nodes`. It stops when it reaches a fixed + // point. + let clean_edges = compute_clean_edges(&serialized_dep_graph.edges, + &retraced, + &mut dirty_nodes); + + // Add synthetic `foo->foo` edges for each clean node `foo` that + // we had before. This is sort of a hack to create clean nodes in + // the graph, since the existence of a node is a signal that the + // work it represents need not be repeated. + let clean_nodes = + serialized_dep_graph.nodes + .iter() + .filter_map(|&node| retraced.map(node)) + .filter(|node| !dirty_nodes.contains(node)) + .map(|node| (node, node)); + + // Add nodes and edges that are not dirty into our main graph. + let dep_graph = tcx.dep_graph.clone(); + for (source, target) in clean_edges.into_iter().chain(clean_nodes) { + let _task = dep_graph.in_task(target); + dep_graph.read(source); + + debug!("decode_dep_graph: clean edge: {:?} -> {:?}", source, target); + } + + Ok(()) +} + +fn initial_dirty_nodes<'tcx>(tcx: &ty::TyCtxt<'tcx>, + hashed_items: &[SerializedHash], + retraced: &RetracedDefIdDirectory) + -> DirtyNodes { + let mut items_removed = false; + let mut dirty_nodes = FnvHashSet(); + for hashed_item in hashed_items { + match retraced.def_id(hashed_item.index) { + Some(def_id) => { + let current_hash = tcx.calculate_item_hash(def_id); + debug!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}", + def_id, current_hash, hashed_item.hash); + if current_hash != hashed_item.hash { + dirty_nodes.insert(DepNode::Hir(def_id)); + } + } + None => { + items_removed = true; + } + } + } + + // If any of the items in the krate have changed, then we consider + // the meta-node `Krate` to be dirty, since that means something + // which (potentially) read the contents of every single item. + if items_removed || !dirty_nodes.is_empty() { + dirty_nodes.insert(DepNode::Krate); + } + + dirty_nodes +} + +fn compute_clean_edges(serialized_edges: &[(SerializedEdge)], + retraced: &RetracedDefIdDirectory, + dirty_nodes: &mut DirtyNodes) + -> CleanEdges { + // Build up an initial list of edges. Include an edge (source, + // target) if neither node has been removed. If the source has + // been removed, add target to the list of dirty nodes. + let mut clean_edges = Vec::with_capacity(serialized_edges.len()); + for &(serialized_source, serialized_target) in serialized_edges { + if let Some(target) = retraced.map(serialized_target) { + if let Some(source) = retraced.map(serialized_source) { + clean_edges.push((source, target)) + } else { + // source removed, target must be dirty + dirty_nodes.insert(target); + } + } else { + // target removed, ignore the edge + } + } + + debug!("compute_clean_edges: dirty_nodes={:#?}", dirty_nodes); + + // Propagate dirty marks by iterating repeatedly over + // `clean_edges`. If we find an edge `(source, target)` where + // `source` is dirty, add `target` to the list of dirty nodes and + // remove it. Keep doing this until we find no more dirty nodes. + let mut previous_size = 0; + while dirty_nodes.len() > previous_size { + debug!("compute_clean_edges: previous_size={}", previous_size); + previous_size = dirty_nodes.len(); + let mut i = 0; + while i < clean_edges.len() { + if dirty_nodes.contains(&clean_edges[i].0) { + let (source, target) = clean_edges.swap_remove(i); + debug!("compute_clean_edges: dirty source {:?} -> {:?}", + source, target); + dirty_nodes.insert(target); + } else if dirty_nodes.contains(&clean_edges[i].1) { + let (source, target) = clean_edges.swap_remove(i); + debug!("compute_clean_edges: dirty target {:?} -> {:?}", + source, target); + } else { + i += 1; + } + } + } + + clean_edges +} diff --git a/src/librustc_incremental/persist/mod.rs b/src/librustc_incremental/persist/mod.rs new file mode 100644 index 0000000000000..8d04fd30a1920 --- /dev/null +++ b/src/librustc_incremental/persist/mod.rs @@ -0,0 +1,23 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! When in incremental mode, this pass dumps out the dependency graph +//! into the given directory. At the same time, it also hashes the +//! various HIR nodes. + +mod data; +mod directory; +mod dirty_clean; +mod load; +mod save; +mod util; + +pub use self::load::load_dep_graph; +pub use self::save::save_dep_graph; diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs new file mode 100644 index 0000000000000..23889c7bbfc1b --- /dev/null +++ b/src/librustc_incremental/persist/save.rs @@ -0,0 +1,142 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use calculate_svh::SvhCalculate; +use rbml::writer::{EncodeResult, Encoder}; +use rustc::dep_graph::DepNode; +use rustc::ty; +use rustc_serialize::{Encodable as RustcEncodable}; +use std::io::{Cursor, Write}; +use std::fs::{self, File}; + +use super::data::*; +use super::directory::*; +use super::util::*; + +pub fn save_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>) { + let _ignore = tcx.dep_graph.in_ignore(); + + if let Some(dep_graph) = dep_graph_path(tcx) { + // delete the old dep-graph, if any + if dep_graph.exists() { + match fs::remove_file(&dep_graph) { + Ok(()) => { } + Err(err) => { + tcx.sess.err( + &format!("unable to delete old dep-graph at `{}`: {}", + dep_graph.display(), err)); + return; + } + } + } + + // generate the data in a memory buffer + let mut wr = Cursor::new(Vec::new()); + match encode_dep_graph(tcx, &mut Encoder::new(&mut wr)) { + Ok(()) => { } + Err(err) => { + tcx.sess.err( + &format!("could not encode dep-graph to `{}`: {}", + dep_graph.display(), err)); + return; + } + } + + // write the data out + let data = wr.into_inner(); + match + File::create(&dep_graph) + .and_then(|mut file| file.write_all(&data)) + { + Ok(_) => { } + Err(err) => { + tcx.sess.err( + &format!("failed to write dep-graph to `{}`: {}", + dep_graph.display(), err)); + return; + } + } + } +} + +pub fn encode_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>, + encoder: &mut Encoder) + -> EncodeResult +{ + // Here we take advantage of how RBML allows us to skip around + // and encode the depgraph as a two-part structure: + // + // ``` + // [SerializedDepGraph] // tag 0 + // [DefIdDirectory] // tag 1 + // ``` + // + // Then later we can load the directory by skipping to find tag 1. + + let query = tcx.dep_graph.query(); + + let mut builder = DefIdDirectoryBuilder::new(tcx); + + // Create hashes for things we can persist. + let hashes = + query.nodes() + .into_iter() + .filter_map(|dep_node| match dep_node { + DepNode::Hir(def_id) => { + assert!(def_id.is_local()); + builder.add(def_id) + .map(|index| { + let hash = tcx.calculate_item_hash(def_id); + SerializedHash { index: index, hash: hash } + }) + } + _ => None + }) + .collect(); + + // Create the serialized dep-graph, dropping nodes that are + // from other crates or from inlined items. + // + // FIXME(#32015) fix handling of other crates + let graph = SerializedDepGraph { + nodes: query.nodes().into_iter() + .flat_map(|node| builder.map(node)) + .collect(), + edges: query.edges().into_iter() + .flat_map(|(source_node, target_node)| { + builder.map(source_node) + .and_then(|source| { + builder.map(target_node) + .map(|target| (source, target)) + }) + }) + .collect(), + hashes: hashes, + }; + + debug!("graph = {:#?}", graph); + + // Encode the graph data into RBML. + try!(encoder.start_tag(DEP_GRAPH_TAG)); + try!(graph.encode(encoder)); + try!(encoder.end_tag()); + + // Now encode the directory. + let directory = builder.into_directory(); + + debug!("directory = {:#?}", directory); + + try!(encoder.start_tag(DIRECTORY_TAG)); + try!(directory.encode(encoder)); + try!(encoder.end_tag()); + + Ok(()) +} + diff --git a/src/librustc_incremental/persist/serialize.rs b/src/librustc_incremental/persist/serialize.rs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/librustc_incremental/persist/util.rs b/src/librustc_incremental/persist/util.rs new file mode 100644 index 0000000000000..11cc55950ca7a --- /dev/null +++ b/src/librustc_incremental/persist/util.rs @@ -0,0 +1,36 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::ty; +use std::fs; +use std::path::PathBuf; + +pub const DEP_GRAPH_TAG: usize = 0x100; + +pub const DIRECTORY_TAG: usize = DEP_GRAPH_TAG + 1; + +pub fn dep_graph_path<'tcx>(tcx: &ty::TyCtxt<'tcx>) -> Option { + // For now, just save/load dep-graph from + // directory/dep_graph.rbml + tcx.sess.opts.incremental.as_ref().and_then(|incr_dir| { + match fs::create_dir_all(&incr_dir){ + Ok(()) => {} + Err(err) => { + tcx.sess.err( + &format!("could not create the directory `{}`: {}", + incr_dir.display(), err)); + return None; + } + } + + Some(incr_dir.join("dep_graph.rbml")) + }) +} + diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 5ade636a3271d..f08d3c685bb55 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -356,6 +356,14 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat "the `#[rustc_if_this_changed]` attribute \ is just used for rustc unit tests \ and will never be stable")), + ("rustc_dirty", Whitelisted, Gated("rustc_attrs", + "the `#[rustc_dirty]` attribute \ + is just used for rustc unit tests \ + and will never be stable")), + ("rustc_clean", Whitelisted, Gated("rustc_attrs", + "the `#[rustc_clean]` attribute \ + is just used for rustc unit tests \ + and will never be stable")), ("rustc_symbol_name", Whitelisted, Gated("rustc_attrs", "internal rustc attributes will never be stable")), ("rustc_item_path", Whitelisted, Gated("rustc_attrs", From 068142a2e6d8a72e52091ccebd3368ffaf267643 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 28 Mar 2016 17:49:02 -0400 Subject: [PATCH 06/20] add incremental test runner and some tests --- mk/tests.mk | 12 ++++- src/compiletest/common.rs | 5 +- src/compiletest/compiletest.rs | 3 +- src/compiletest/runtest.rs | 65 +++++++++++++++++++++++++ src/test/incremental/dirty_clean.rs | 54 ++++++++++++++++++++ src/test/incremental/hello_world.rs | 46 +++++++++++++++++ src/test/incremental/string_constant.rs | 58 ++++++++++++++++++++++ 7 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 src/test/incremental/dirty_clean.rs create mode 100644 src/test/incremental/hello_world.rs create mode 100644 src/test/incremental/string_constant.rs diff --git a/mk/tests.mk b/mk/tests.mk index 1a02b5c228104..0b8f0f80deac2 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -305,6 +305,7 @@ check-stage$(1)-T-$(2)-H-$(3)-exec: \ check-stage$(1)-T-$(2)-H-$(3)-doc-crates-exec \ check-stage$(1)-T-$(2)-H-$(3)-debuginfo-gdb-exec \ check-stage$(1)-T-$(2)-H-$(3)-debuginfo-lldb-exec \ + check-stage$(1)-T-$(2)-H-$(3)-incremental-exec \ check-stage$(1)-T-$(2)-H-$(3)-doc-exec \ check-stage$(1)-T-$(2)-H-$(3)-pretty-exec @@ -481,6 +482,7 @@ DEBUGINFO_LLDB_RS := $(call rwildcard,$(S)src/test/debuginfo/,*.rs) CODEGEN_RS := $(call rwildcard,$(S)src/test/codegen/,*.rs) CODEGEN_CC := $(call rwildcard,$(S)src/test/codegen/,*.cc) CODEGEN_UNITS_RS := $(call rwildcard,$(S)src/test/codegen-units/,*.rs) +INCREMENTAL_RS := $(call rwildcard,$(S)src/test/incremental/,*.rs) RUSTDOCCK_RS := $(call rwildcard,$(S)src/test/rustdoc/,*.rs) RPASS_TESTS := $(RPASS_RS) @@ -496,6 +498,7 @@ DEBUGINFO_GDB_TESTS := $(DEBUGINFO_GDB_RS) DEBUGINFO_LLDB_TESTS := $(DEBUGINFO_LLDB_RS) CODEGEN_TESTS := $(CODEGEN_RS) $(CODEGEN_CC) CODEGEN_UNITS_TESTS := $(CODEGEN_UNITS_RS) +INCREMENTAL_TESTS := $(INCREMENTAL_RS) RUSTDOCCK_TESTS := $(RUSTDOCCK_RS) CTEST_SRC_BASE_rpass = run-pass @@ -558,6 +561,11 @@ CTEST_BUILD_BASE_codegen-units = codegen-units CTEST_MODE_codegen-units = codegen-units CTEST_RUNTOOL_codegen-units = $(CTEST_RUNTOOL) +CTEST_SRC_BASE_incremental = incremental +CTEST_BUILD_BASE_incremental = incremental +CTEST_MODE_incremental = incremental +CTEST_RUNTOOL_incremental = $(CTEST_RUNTOOL) + CTEST_SRC_BASE_rustdocck = rustdoc CTEST_BUILD_BASE_rustdocck = rustdoc CTEST_MODE_rustdocck = rustdoc @@ -681,6 +689,7 @@ CTEST_DEPS_debuginfo-lldb_$(1)-T-$(2)-H-$(3) = $$(DEBUGINFO_LLDB_TESTS) \ $(S)src/etc/lldb_rust_formatters.py CTEST_DEPS_codegen_$(1)-T-$(2)-H-$(3) = $$(CODEGEN_TESTS) CTEST_DEPS_codegen-units_$(1)-T-$(2)-H-$(3) = $$(CODEGEN_UNITS_TESTS) +CTEST_DEPS_incremental_$(1)-T-$(2)-H-$(3) = $$(INCREMENTAL_TESTS) CTEST_DEPS_rustdocck_$(1)-T-$(2)-H-$(3) = $$(RUSTDOCCK_TESTS) \ $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \ $(S)src/etc/htmldocck.py @@ -747,7 +756,7 @@ endif endef CTEST_NAMES = rpass rpass-valgrind rpass-full rfail-full cfail-full rfail cfail pfail \ - debuginfo-gdb debuginfo-lldb codegen codegen-units rustdocck + debuginfo-gdb debuginfo-lldb codegen codegen-units rustdocck incremental $(foreach host,$(CFG_HOST), \ $(eval $(foreach target,$(CFG_TARGET), \ @@ -945,6 +954,7 @@ TEST_GROUPS = \ debuginfo-lldb \ codegen \ codegen-units \ + incremental \ doc \ $(foreach docname,$(DOC_NAMES),doc-$(docname)) \ pretty \ diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index bfcc1759b955d..a6960ff17854e 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -25,7 +25,8 @@ pub enum Mode { DebugInfoLldb, Codegen, Rustdoc, - CodegenUnits + CodegenUnits, + Incremental, } impl FromStr for Mode { @@ -43,6 +44,7 @@ impl FromStr for Mode { "codegen" => Ok(Codegen), "rustdoc" => Ok(Rustdoc), "codegen-units" => Ok(CodegenUnits), + "incremental" => Ok(Incremental), _ => Err(()), } } @@ -62,6 +64,7 @@ impl fmt::Display for Mode { Codegen => "codegen", Rustdoc => "rustdoc", CodegenUnits => "codegen-units", + Incremental => "incremental", }, f) } } diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 787d77bc56c38..1c06b2a23d54d 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -71,7 +71,8 @@ pub fn parse_config(args: Vec ) -> Config { reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"), reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"), reqopt("", "mode", "which sort of compile tests to run", - "(compile-fail|parse-fail|run-fail|run-pass|run-pass-valgrind|pretty|debug-info)"), + "(compile-fail|parse-fail|run-fail|run-pass|\ + run-pass-valgrind|pretty|debug-info|incremental)"), optflag("", "ignored", "run tests marked as ignored"), optopt("", "runtool", "supervisor program to run tests under \ (eg. emulator, valgrind)", "PROGRAM"), diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 24fd413ef09c2..802b967ed7d90 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -11,6 +11,7 @@ use common::Config; use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind}; use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits}; +use common::{Incremental}; use errors::{self, ErrorKind}; use header::TestProps; use header; @@ -59,6 +60,7 @@ pub fn run(config: Config, testpaths: &TestPaths) { Codegen => run_codegen_test(&config, &props, &testpaths), Rustdoc => run_rustdoc_test(&config, &props, &testpaths), CodegenUnits => run_codegen_units_test(&config, &props, &testpaths), + Incremental => run_incremental_test(&config, &props, &testpaths), } } @@ -1966,3 +1968,66 @@ fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPa panic!(); } } + +fn run_incremental_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { + // Basic plan for a test incremental/foo/bar.rs: + // - load list of revisions pass1, fail2, pass3 + // - each should begin with `pass` or `fail` + // - if `pass`, expect compile to succeed + // - if `fail`, expect errors from file + // - create a directory build/foo/bar.incremental + // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C pass1 + // - because name of revision starts with "pass", expect success + // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C fail2 + // - because name of revision starts with "fail", expect an error + // - load expected errors as usual, but filter for those that end in `[fail2]` + // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C pass3 + // - because name of revision starts with "pass", expect success + // - execute build/foo/bar.exe and save output + // + // FIXME -- use non-incremental mode as an oracle? That doesn't apply + // to #[rustc_dirty] and clean tests I guess + + assert!(!props.revisions.is_empty(), "incremental tests require a list of revisions"); + + let output_base_name = output_base_name(config, testpaths); + + // Create the incremental workproduct directory. + let incremental_dir = output_base_name.with_extension("incremental"); + if incremental_dir.exists() { + fs::remove_dir_all(&incremental_dir).unwrap(); + } + fs::create_dir_all(&incremental_dir).unwrap(); + + if config.verbose { + print!("incremental_dir={}", incremental_dir.display()); + } + + for revision in &props.revisions { + let mut revision_props = props.clone(); + header::load_props_into(&mut revision_props, &testpaths.file, Some(&revision)); + + revision_props.compile_flags.extend(vec![ + format!("-Z"), + format!("incremental={}", incremental_dir.display()), + format!("--cfg"), + format!("{}", revision), + ]); + + if config.verbose { + print!("revision={:?} revision_props={:#?}", revision, revision_props); + } + + if revision.starts_with("rpass") { + run_rpass_test_revision(config, &revision_props, testpaths, Some(&revision)); + } else if revision.starts_with("rfail") { + run_rfail_test_revision(config, &revision_props, testpaths, Some(&revision)); + } else if revision.starts_with("cfail") { + run_cfail_test_revision(config, &revision_props, testpaths, Some(&revision)); + } else { + fatal( + Some(revision), + "revision name must begin with rpass, rfail, or cfail"); + } + } +} diff --git a/src/test/incremental/dirty_clean.rs b/src/test/incremental/dirty_clean.rs new file mode 100644 index 0000000000000..f0e596baa2d27 --- /dev/null +++ b/src/test/incremental/dirty_clean.rs @@ -0,0 +1,54 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: rpass1 cfail2 + +#![allow(warnings)] +#![feature(rustc_attrs)] + +// Sanity check for the dirty-clean system. Give the opposite +// annotations that we expect to see, so that we check that errors are +// reported. + +fn main() { } + +mod x { + #[cfg(rpass1)] + pub fn x() -> usize { + 22 + } + + #[cfg(cfail2)] + pub fn x() -> u32 { + 22 + } +} + +mod y { + use x; + + #[rustc_clean(label="TypeckItemBody", cfg="cfail2")] + #[rustc_clean(label="TransCrateItem", cfg="cfail2")] + pub fn y() { + //[cfail2]~^ ERROR `TypeckItemBody("y::y")` not found in dep graph, but should be clean + //[cfail2]~| ERROR `TransCrateItem("y::y")` not found in dep graph, but should be clean + x::x(); + } +} + +mod z { + #[rustc_dirty(label="TypeckItemBody", cfg="cfail2")] + #[rustc_dirty(label="TransCrateItem", cfg="cfail2")] + pub fn z() { + //[cfail2]~^ ERROR `TypeckItemBody("z::z")` found in dep graph, but should be dirty + // FIXME(#32014) -- TransCrateItem ought to be clean, but it is in fact + // dirty, hence we don't see an error here. + } +} diff --git a/src/test/incremental/hello_world.rs b/src/test/incremental/hello_world.rs new file mode 100644 index 0000000000000..f98ae188bad9f --- /dev/null +++ b/src/test/incremental/hello_world.rs @@ -0,0 +1,46 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: rpass1 rpass2 + +#![allow(warnings)] +#![feature(rustc_attrs)] + +fn main() { } + +mod x { + #[cfg(rpass1)] + pub fn x() -> i32 { + 1 + } + + #[cfg(rpass2)] + pub fn x() -> i32 { + 2 + } +} + +mod y { + use x; + + #[rustc_dirty(label="TypeckItemBody", cfg="rpass2")] + pub fn y() { + x::x(); + } +} + +mod z { + use y; + + #[rustc_clean(label="TypeckItemBody", cfg="rpass2")] + pub fn z() { + y::y(); + } +} diff --git a/src/test/incremental/string_constant.rs b/src/test/incremental/string_constant.rs new file mode 100644 index 0000000000000..1acca8b2952a6 --- /dev/null +++ b/src/test/incremental/string_constant.rs @@ -0,0 +1,58 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// revisions: rpass1 rpass2 + +#![allow(warnings)] +#![feature(rustc_attrs)] + +// Here the only thing which changes is the string constant in `x`. +// Therefore, the compiler deduces (correctly) that typeck is not +// needed even for callers of `x`. +// +// It is not entirely clear why `TransCrateItem` invalidates `y` and +// `z`, actually, I think it's because of the structure of +// trans. -nmatsakis + +fn main() { } + +mod x { + #[cfg(rpass1)] + pub fn x() { + println!("1"); + } + + #[cfg(rpass2)] + #[rustc_dirty(label="TypeckItemBody", cfg="rpass2")] + #[rustc_dirty(label="TransCrateItem", cfg="rpass2")] + pub fn x() { + println!("2"); + } +} + +mod y { + use x; + + #[rustc_clean(label="TypeckItemBody", cfg="rpass2")] + #[rustc_dirty(label="TransCrateItem", cfg="rpass2")] + pub fn y() { + x::x(); + } +} + +mod z { + use y; + + #[rustc_clean(label="TypeckItemBody", cfg="rpass2")] + #[rustc_dirty(label="TransCrateItem", cfg="rpass2")] + pub fn z() { + y::y(); + } +} From a5236e9da03fe58c4db8f011880c74a6911ed5e7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Apr 2016 17:39:49 -0400 Subject: [PATCH 07/20] add some comments --- src/librustc_incremental/persist/README.md | 13 +++++++++++++ src/librustc_incremental/persist/load.rs | 7 ++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/librustc_incremental/persist/README.md diff --git a/src/librustc_incremental/persist/README.md b/src/librustc_incremental/persist/README.md new file mode 100644 index 0000000000000..95e0940001639 --- /dev/null +++ b/src/librustc_incremental/persist/README.md @@ -0,0 +1,13 @@ +This is the code to load/save the dependency graph. Loading is assumed +to run early in compilation, and saving at the very end. When loading, +the basic idea is that we will load up the dependency graph from the +previous compilation and compare the hashes of our HIR nodes to the +hashes of the HIR nodes that existed at the time. For each node whose +hash has changed, or which no longer exists in the new HIR, we can +remove that node from the old graph along with any nodes that depend +on it. Then we add what's left to the new graph (if any such nodes or +edges already exist, then there would be no effect, but since we do +this first thing, they do not). + + + diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 588113e2eaaad..6925127099447 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -10,7 +10,6 @@ //! Code to save/load the dep-graph from files. - use calculate_svh::SvhCalculate; use rbml::{self, Doc}; use rbml::reader::{self, DecodeResult, Decoder}; @@ -32,6 +31,12 @@ type DirtyNodes = FnvHashSet>; type CleanEdges = Vec<(DepNode, DepNode)>; +/// If we are in incremental mode, and a previous dep-graph exists, +/// then load up those nodes/edges that are still valid into the +/// dep-graph for this session. (This is assumed to be running very +/// early in compilation, before we've really done any work, but +/// actually it doesn't matter all that much.) See `README.md` for +/// more general overview. pub fn load_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>) { let _ignore = tcx.dep_graph.in_ignore(); From 098571797ca093f98f1e92634568aee38e8964cc Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Apr 2016 17:48:15 -0400 Subject: [PATCH 08/20] Address nits. --- mk/tests.mk | 2 +- src/compiletest/runtest.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mk/tests.mk b/mk/tests.mk index 0b8f0f80deac2..20736165b73b0 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -954,7 +954,7 @@ TEST_GROUPS = \ debuginfo-lldb \ codegen \ codegen-units \ - incremental \ + incremental \ doc \ $(foreach docname,$(DOC_NAMES),doc-$(docname)) \ pretty \ diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 802b967ed7d90..994f45da14626 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -1972,9 +1972,10 @@ fn run_codegen_units_test(config: &Config, props: &TestProps, testpaths: &TestPa fn run_incremental_test(config: &Config, props: &TestProps, testpaths: &TestPaths) { // Basic plan for a test incremental/foo/bar.rs: // - load list of revisions pass1, fail2, pass3 - // - each should begin with `pass` or `fail` - // - if `pass`, expect compile to succeed - // - if `fail`, expect errors from file + // - each should begin with `rpass`, `rfail`, or `cfail` + // - if `rpass`, expect compile and execution to succeed + // - if `cfail`, expect compilation to fail + // - if `rfail`, expect execution to fail // - create a directory build/foo/bar.incremental // - compile foo/bar.rs with -Z incremental=.../foo/bar.incremental and -C pass1 // - because name of revision starts with "pass", expect success From a2e0cbc6fc64db7672d8b0d5140dea964c1abf23 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Apr 2016 18:04:32 -0400 Subject: [PATCH 09/20] rebase: get `bug!` macro --- src/librustc_incremental/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index 3af8fe5cc5f90..005146d91eae4 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -24,7 +24,7 @@ extern crate graphviz; extern crate rbml; -extern crate rustc; +#[macro_use] extern crate rustc; extern crate rustc_data_structures; extern crate serialize as rustc_serialize; From 50a40e1c37761acc404d4b95e0448d7a01b6e38b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Apr 2016 18:04:39 -0400 Subject: [PATCH 10/20] add FIXME #32753 markers: SVH vs ICH --- src/librustc_incremental/calculate_svh.rs | 4 +++- src/librustc_incremental/persist/load.rs | 1 + src/librustc_incremental/persist/save.rs | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustc_incremental/calculate_svh.rs b/src/librustc_incremental/calculate_svh.rs index 158db34ee8ac9..2378e3f45fba7 100644 --- a/src/librustc_incremental/calculate_svh.rs +++ b/src/librustc_incremental/calculate_svh.rs @@ -44,8 +44,10 @@ impl<'tcx> SvhCalculate for ty::TyCtxt<'tcx> { let mut state = SipHasher::new(); debug!("state: {:?}", state); + // FIXME(#32753) -- at (*) we `to_le` for endianness, but is + // this enough, and does it matter anyway? "crate_disambiguator".hash(&mut state); - crate_disambiguator.as_str().len().hash(&mut state); + crate_disambiguator.as_str().len().to_le().hash(&mut state); // (*) crate_disambiguator.as_str().hash(&mut state); debug!("crate_disambiguator: {:?}", crate_disambiguator.as_str()); diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 6925127099447..142367e08eec2 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -151,6 +151,7 @@ fn initial_dirty_nodes<'tcx>(tcx: &ty::TyCtxt<'tcx>, for hashed_item in hashed_items { match retraced.def_id(hashed_item.index) { Some(def_id) => { + // FIXME(#32753) -- should we use a distinct hash here let current_hash = tcx.calculate_item_hash(def_id); debug!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}", def_id, current_hash, hashed_item.hash); diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 23889c7bbfc1b..692d51aa2567a 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -93,6 +93,7 @@ pub fn encode_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>, assert!(def_id.is_local()); builder.add(def_id) .map(|index| { + // FIXME(#32753) -- should we use a distinct hash here let hash = tcx.calculate_item_hash(def_id); SerializedHash { index: index, hash: hash } }) From 82f43611551c2f44710dd949f6418eda8df93ca9 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Apr 2016 18:08:06 -0400 Subject: [PATCH 11/20] FIXME for lockfile --- src/librustc_incremental/persist/load.rs | 1 + src/librustc_incremental/persist/save.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 142367e08eec2..7cf53b37b1c18 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -41,6 +41,7 @@ pub fn load_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>) { let _ignore = tcx.dep_graph.in_ignore(); if let Some(dep_graph) = dep_graph_path(tcx) { + // FIXME(#32754) lock file? load_dep_graph_if_exists(tcx, &dep_graph); dirty_clean::check_dirty_clean_annotations(tcx); } diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 692d51aa2567a..1db129ca70007 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -24,6 +24,8 @@ pub fn save_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>) { let _ignore = tcx.dep_graph.in_ignore(); if let Some(dep_graph) = dep_graph_path(tcx) { + // FIXME(#32754) lock file? + // delete the old dep-graph, if any if dep_graph.exists() { match fs::remove_file(&dep_graph) { From e9ec282df8fb658cca1a0a3acb2cb6cf62fd1f84 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Apr 2016 18:21:12 -0400 Subject: [PATCH 12/20] Fix another call to bug into the macro --- src/librustc_incremental/persist/load.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 7cf53b37b1c18..30923f6826efd 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -69,9 +69,7 @@ pub fn load_dep_graph_if_exists<'tcx>(tcx: &ty::TyCtxt<'tcx>, path: &Path) { match decode_dep_graph(tcx, Doc::new(&data)) { Ok(dirty) => dirty, Err(err) => { - tcx.sess.bug( - &format!("decoding error in dep-graph from `{}`: {}", - path.display(), err)); + bug!("decoding error in dep-graph from `{}`: {}", path.display(), err); } } } From f2051212c2ac670bf20f8467bac7898e72d4d34d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 5 Apr 2016 18:37:51 -0400 Subject: [PATCH 13/20] remove use of rbml and just use opaque encoder --- src/librustc_incremental/persist/load.rs | 34 ++++++++---------------- src/librustc_incremental/persist/save.rs | 19 ++++--------- src/librustc_incremental/persist/util.rs | 4 --- 3 files changed, 16 insertions(+), 41 deletions(-) diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index 30923f6826efd..a86d1c3c13bcd 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -11,8 +11,8 @@ //! Code to save/load the dep-graph from files. use calculate_svh::SvhCalculate; -use rbml::{self, Doc}; -use rbml::reader::{self, DecodeResult, Decoder}; +use rbml::Error; +use rbml::opaque::Decoder; use rustc::dep_graph::DepNode; use rustc::middle::def_id::DefId; use rustc::ty; @@ -66,7 +66,7 @@ pub fn load_dep_graph_if_exists<'tcx>(tcx: &ty::TyCtxt<'tcx>, path: &Path) { } } - match decode_dep_graph(tcx, Doc::new(&data)) { + match decode_dep_graph(tcx, &data) { Ok(dirty) => dirty, Err(err) => { bug!("decoding error in dep-graph from `{}`: {}", path.display(), err); @@ -74,34 +74,22 @@ pub fn load_dep_graph_if_exists<'tcx>(tcx: &ty::TyCtxt<'tcx>, path: &Path) { } } -pub fn decode_dep_graph<'tcx, 'doc>(tcx: &ty::TyCtxt<'tcx>, doc: rbml::Doc<'doc>) - -> DecodeResult<()> +pub fn decode_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>, data: &[u8]) + -> Result<(), Error> { - // First load the directory, which maps the def-ids found - // elsewhere into `DefPath`. We can then refresh the `DefPath` to - // obtain updated def-ids. - let directory = { - let directory_doc = reader::get_doc(doc, DIRECTORY_TAG); - let mut decoder = Decoder::new(directory_doc); - try!(DefIdDirectory::decode(&mut decoder)) - }; + // Deserialize the directory and dep-graph. + let mut decoder = Decoder::new(data, 0); + let directory = try!(DefIdDirectory::decode(&mut decoder)); + let serialized_dep_graph = try!(SerializedDepGraph::decode(&mut decoder)); debug!("decode_dep_graph: directory = {:#?}", directory); + debug!("decode_dep_graph: serialized_dep_graph = {:#?}", serialized_dep_graph); - // Retrace those paths to find their current location (if any). + // Retrace the paths in the directory to find their current location (if any). let retraced = directory.retrace(tcx); debug!("decode_dep_graph: retraced = {:#?}", retraced); - // Deserialize the dep-graph (which will include DefPathIndex entries) - let serialized_dep_graph = { - let dep_graph_doc = reader::get_doc(doc, DEP_GRAPH_TAG); - let mut decoder = Decoder::new(dep_graph_doc); - try!(SerializedDepGraph::decode(&mut decoder)) - }; - - debug!("decode_dep_graph: serialized_dep_graph = {:#?}", serialized_dep_graph); - // Compute the set of Hir nodes whose data has changed. let mut dirty_nodes = initial_dirty_nodes(tcx, &serialized_dep_graph.hashes, &retraced); diff --git a/src/librustc_incremental/persist/save.rs b/src/librustc_incremental/persist/save.rs index 1db129ca70007..d88f9e42b08ce 100644 --- a/src/librustc_incremental/persist/save.rs +++ b/src/librustc_incremental/persist/save.rs @@ -9,11 +9,11 @@ // except according to those terms. use calculate_svh::SvhCalculate; -use rbml::writer::{EncodeResult, Encoder}; +use rbml::opaque::Encoder; use rustc::dep_graph::DepNode; use rustc::ty; use rustc_serialize::{Encodable as RustcEncodable}; -use std::io::{Cursor, Write}; +use std::io::{self, Cursor, Write}; use std::fs::{self, File}; use super::data::*; @@ -70,7 +70,7 @@ pub fn save_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>) { pub fn encode_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>, encoder: &mut Encoder) - -> EncodeResult + -> io::Result<()> { // Here we take advantage of how RBML allows us to skip around // and encode the depgraph as a two-part structure: @@ -126,19 +126,10 @@ pub fn encode_dep_graph<'tcx>(tcx: &ty::TyCtxt<'tcx>, debug!("graph = {:#?}", graph); - // Encode the graph data into RBML. - try!(encoder.start_tag(DEP_GRAPH_TAG)); - try!(graph.encode(encoder)); - try!(encoder.end_tag()); - - // Now encode the directory. + // Encode the directory and then the graph data. let directory = builder.into_directory(); - - debug!("directory = {:#?}", directory); - - try!(encoder.start_tag(DIRECTORY_TAG)); try!(directory.encode(encoder)); - try!(encoder.end_tag()); + try!(graph.encode(encoder)); Ok(()) } diff --git a/src/librustc_incremental/persist/util.rs b/src/librustc_incremental/persist/util.rs index 11cc55950ca7a..9b4e5997efe42 100644 --- a/src/librustc_incremental/persist/util.rs +++ b/src/librustc_incremental/persist/util.rs @@ -12,10 +12,6 @@ use rustc::ty; use std::fs; use std::path::PathBuf; -pub const DEP_GRAPH_TAG: usize = 0x100; - -pub const DIRECTORY_TAG: usize = DEP_GRAPH_TAG + 1; - pub fn dep_graph_path<'tcx>(tcx: &ty::TyCtxt<'tcx>) -> Option { // For now, just save/load dep-graph from // directory/dep_graph.rbml From 4914b5fc6dd9ce2040ccde19553fc3670f467a01 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Apr 2016 09:42:33 -0400 Subject: [PATCH 14/20] patch name in incremental's Cargo.toml --- mk/crates.mk | 3 ++- src/librustc_incremental/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mk/crates.mk b/mk/crates.mk index 84452945f2706..dafda75f5fe7d 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -105,7 +105,8 @@ DEPS_rustc_data_structures := std log serialize DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \ rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \ rustc_trans rustc_privacy rustc_lint rustc_plugin \ - rustc_metadata syntax_ext rustc_passes rustc_save_analysis rustc_const_eval + rustc_metadata syntax_ext rustc_passes rustc_save_analysis rustc_const_eval \ + rustc_incremental DEPS_rustc_lint := rustc log syntax rustc_const_eval DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags DEPS_rustc_metadata := rustc syntax rbml rustc_const_math diff --git a/src/librustc_incremental/Cargo.toml b/src/librustc_incremental/Cargo.toml index 1a8b5a483e01f..d3b822566b0bd 100644 --- a/src/librustc_incremental/Cargo.toml +++ b/src/librustc_incremental/Cargo.toml @@ -1,10 +1,10 @@ [package] authors = ["The Rust Project Developers"] -name = "rustc_serialize" +name = "rustc_incremental" version = "0.0.0" [lib] -name = "rustc_serialize" +name = "rustc_incremental" path = "lib.rs" crate-type = ["dylib"] From ec47b59c24c0f907c0e8d5e39d1f5842a8c0945d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Apr 2016 13:38:18 -0400 Subject: [PATCH 15/20] remove svh module --- src/librustc/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 342007d46c535..deb2062777220 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -100,7 +100,6 @@ pub mod middle { pub mod recursion_limit; pub mod resolve_lifetime; pub mod stability; - pub mod svh; pub mod weak_lang_items; } From 22b4bb051a25ac68a43f092b6fd1ade804c82680 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Apr 2016 14:04:50 -0400 Subject: [PATCH 16/20] rebase over the hir rename --- src/librustc/dep_graph/visit.rs | 4 ++-- src/librustc/middle/cstore.rs | 1 - src/librustc_incremental/calculate_svh.rs | 10 +++++----- src/librustc_incremental/persist/directory.rs | 4 ++-- src/librustc_incremental/persist/dirty_clean.rs | 6 +++--- src/librustc_incremental/persist/load.rs | 2 +- src/librustc_trans/back/link.rs | 2 -- 7 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/librustc/dep_graph/visit.rs b/src/librustc/dep_graph/visit.rs index 8ce177efe92a5..cdc16216798ab 100644 --- a/src/librustc/dep_graph/visit.rs +++ b/src/librustc/dep_graph/visit.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use hir; use hir::def_id::DefId; +use hir::intravisit::Visitor; use ty::TyCtxt; -use rustc_front::hir; -use rustc_front::intravisit::Visitor; use super::dep_node::DepNode; diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 0409451043dfb..1f6328187a59b 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -28,7 +28,6 @@ use hir::def::{self, Def}; use middle::lang_items; use ty::{self, Ty, TyCtxt, VariantKind}; use hir::def_id::{DefId, DefIndex}; -use hir::svh::Svh; use mir::repr::Mir; use mir::mir_map::MirMap; use session::Session; diff --git a/src/librustc_incremental/calculate_svh.rs b/src/librustc_incremental/calculate_svh.rs index 2378e3f45fba7..ab1c6f5ace176 100644 --- a/src/librustc_incremental/calculate_svh.rs +++ b/src/librustc_incremental/calculate_svh.rs @@ -15,7 +15,7 @@ use std::hash::{Hash, SipHasher, Hasher}; use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; use rustc::hir::svh::Svh; use rustc::ty; -use rustc_front::intravisit::{self, Visitor}; +use rustc::hir::intravisit::{self, Visitor}; use self::svh_visitor::StrictVersionHashVisitor; @@ -110,10 +110,10 @@ mod svh_visitor { use syntax::codemap::Span; use syntax::parse::token; use rustc::ty; - use rustc_front::intravisit as visit; - use rustc_front::intravisit::{Visitor, FnKind}; - use rustc_front::hir::*; - use rustc_front::hir; + use rustc::hir; + use rustc::hir::*; + use rustc::hir::intravisit as visit; + use rustc::hir::intravisit::{Visitor, FnKind}; use std::hash::{Hash, SipHasher}; diff --git a/src/librustc_incremental/persist/directory.rs b/src/librustc_incremental/persist/directory.rs index f4d155962d1f2..0d0054cadc692 100644 --- a/src/librustc_incremental/persist/directory.rs +++ b/src/librustc_incremental/persist/directory.rs @@ -14,8 +14,8 @@ //! item might even be removed in the meantime). use rustc::dep_graph::DepNode; -use rustc::front::map::DefPath; -use rustc::middle::def_id::DefId; +use rustc::hir::map::DefPath; +use rustc::hir::def_id::DefId; use rustc::ty; use rustc::util::nodemap::DefIdMap; use rustc_serialize::{Decoder as RustcDecoder, diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index 0aeddd5b7c879..35fa69520b67f 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -25,9 +25,9 @@ //! the required condition is not met. use rustc::dep_graph::{DepGraphQuery, DepNode}; -use rustc::middle::def_id::DefId; -use rustc_front::hir; -use rustc_front::intravisit::Visitor; +use rustc::hir; +use rustc::hir::def_id::DefId; +use rustc::hir::intravisit::Visitor; use syntax::ast::{self, Attribute, MetaItem}; use syntax::attr::AttrMetaMethods; use syntax::parse::token::InternedString; diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index a86d1c3c13bcd..196c4511b0f1d 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -14,7 +14,7 @@ use calculate_svh::SvhCalculate; use rbml::Error; use rbml::opaque::Decoder; use rustc::dep_graph::DepNode; -use rustc::middle::def_id::DefId; +use rustc::hir::def_id::DefId; use rustc::ty; use rustc_data_structures::fnv::FnvHashSet; use rustc_serialize::Decodable as RustcDecodable; diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 1d15e67651aa5..4e77b2bc06940 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -44,8 +44,6 @@ use syntax::ast; use syntax::codemap::Span; use syntax::attr::AttrMetaMethods; -use rustc::hir; - // RLIB LLVM-BYTECODE OBJECT LAYOUT // Version 1 // Bytes Data From 54d78a46c506343be3e355447ecd45886b3c444a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Apr 2016 16:38:29 -0400 Subject: [PATCH 17/20] Fix test cases to reflect that #32014...went away Not sure what was the bug. Fixes #32014. --- src/test/incremental/dirty_clean.rs | 3 +-- src/test/incremental/string_constant.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/incremental/dirty_clean.rs b/src/test/incremental/dirty_clean.rs index f0e596baa2d27..9a3097831c563 100644 --- a/src/test/incremental/dirty_clean.rs +++ b/src/test/incremental/dirty_clean.rs @@ -48,7 +48,6 @@ mod z { #[rustc_dirty(label="TransCrateItem", cfg="cfail2")] pub fn z() { //[cfail2]~^ ERROR `TypeckItemBody("z::z")` found in dep graph, but should be dirty - // FIXME(#32014) -- TransCrateItem ought to be clean, but it is in fact - // dirty, hence we don't see an error here. + //[cfail2]~| ERROR `TransCrateItem("z::z")` found in dep graph, but should be dirty } } diff --git a/src/test/incremental/string_constant.rs b/src/test/incremental/string_constant.rs index 1acca8b2952a6..0aa728b94dda9 100644 --- a/src/test/incremental/string_constant.rs +++ b/src/test/incremental/string_constant.rs @@ -41,7 +41,7 @@ mod y { use x; #[rustc_clean(label="TypeckItemBody", cfg="rpass2")] - #[rustc_dirty(label="TransCrateItem", cfg="rpass2")] + #[rustc_clean(label="TransCrateItem", cfg="rpass2")] pub fn y() { x::x(); } @@ -51,7 +51,7 @@ mod z { use y; #[rustc_clean(label="TypeckItemBody", cfg="rpass2")] - #[rustc_dirty(label="TransCrateItem", cfg="rpass2")] + #[rustc_clean(label="TransCrateItem", cfg="rpass2")] pub fn z() { y::y(); } From 29ad9a2d578eb53e1f1ed8a54913bc45719762ce Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 6 Apr 2016 20:20:38 -0400 Subject: [PATCH 18/20] remove incorrect dependency on librustc_front --- src/librustc_incremental/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_incremental/Cargo.toml b/src/librustc_incremental/Cargo.toml index d3b822566b0bd..6f807cc6f9ac2 100644 --- a/src/librustc_incremental/Cargo.toml +++ b/src/librustc_incremental/Cargo.toml @@ -13,5 +13,4 @@ graphviz = { path = "../libgraphviz" } rbml = { path = "../librbml" } rustc = { path = "../librustc" } rustc_data_structures = { path = "../librustc_data_structures" } -rustc_front = { path = "../librustc_front" } serialize = { path = "../libserialize" } From 9eaae920117594f5bddab1f46f406e5402e8cc8a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 7 Apr 2016 13:09:00 -0400 Subject: [PATCH 19/20] argh, overlooked two extern crates --- src/librustc_incremental/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_incremental/Cargo.toml b/src/librustc_incremental/Cargo.toml index 6f807cc6f9ac2..927e4126472f6 100644 --- a/src/librustc_incremental/Cargo.toml +++ b/src/librustc_incremental/Cargo.toml @@ -14,3 +14,5 @@ rbml = { path = "../librbml" } rustc = { path = "../librustc" } rustc_data_structures = { path = "../librustc_data_structures" } serialize = { path = "../libserialize" } +log = { path = "../liblog" } +syntax = { path = "../libsyntax" } From 942d4c7e5486d28fbf852697995f8dcf804158fb Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Fri, 8 Apr 2016 08:13:29 +0300 Subject: [PATCH 20/20] Blacklist fn item types from being used with variadic functions. --- src/librustc_typeck/check/mod.rs | 21 +++++++++++++++------ src/test/compile-fail/issue-32201.rs | 22 ++++++++++++++++++++++ src/test/compile-fail/variadic-ffi-3.rs | 12 ++++++------ 3 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 src/test/compile-fail/issue-32201.rs diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 45877d7099bbf..67b91f7838c66 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -2578,24 +2578,33 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, ty::TyFloat(ast::FloatTy::F32) => { fcx.type_error_message(arg.span, |t| { - format!("can't pass an {} to variadic \ - function, cast to c_double", t) + format!("can't pass an `{}` to variadic \ + function, cast to `c_double`", t) }, arg_ty, None); } ty::TyInt(ast::IntTy::I8) | ty::TyInt(ast::IntTy::I16) | ty::TyBool => { fcx.type_error_message(arg.span, |t| { - format!("can't pass {} to variadic \ - function, cast to c_int", + format!("can't pass `{}` to variadic \ + function, cast to `c_int`", t) }, arg_ty, None); } ty::TyUint(ast::UintTy::U8) | ty::TyUint(ast::UintTy::U16) => { fcx.type_error_message(arg.span, |t| { - format!("can't pass {} to variadic \ - function, cast to c_uint", + format!("can't pass `{}` to variadic \ + function, cast to `c_uint`", t) }, arg_ty, None); } + ty::TyFnDef(_, _, f) => { + let ptr_ty = fcx.tcx().mk_ty(ty::TyFnPtr(f)); + let ptr_ty = fcx.infcx().resolve_type_vars_if_possible(&ptr_ty); + fcx.type_error_message(arg.span, + |t| { + format!("can't pass `{}` to variadic \ + function, cast to `{}`", t, ptr_ty) + }, arg_ty, None); + } _ => {} } } diff --git a/src/test/compile-fail/issue-32201.rs b/src/test/compile-fail/issue-32201.rs new file mode 100644 index 0000000000000..bcc53df68a323 --- /dev/null +++ b/src/test/compile-fail/issue-32201.rs @@ -0,0 +1,22 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern { + fn foo(a: i32, ...); +} + +fn bar(_: *const u8) {} + +fn main() { + unsafe { + foo(0, bar); + //~^ ERROR can't pass `fn(*const u8) {bar}` to variadic function, cast to `fn(*const u8)` + } +} diff --git a/src/test/compile-fail/variadic-ffi-3.rs b/src/test/compile-fail/variadic-ffi-3.rs index 1d5ebdbae3e2e..6e60562da6749 100644 --- a/src/test/compile-fail/variadic-ffi-3.rs +++ b/src/test/compile-fail/variadic-ffi-3.rs @@ -33,11 +33,11 @@ fn main() { //~| expected variadic fn //~| found non-variadic function - foo(1, 2, 3f32); //~ ERROR: can't pass an f32 to variadic function, cast to c_double - foo(1, 2, true); //~ ERROR: can't pass bool to variadic function, cast to c_int - foo(1, 2, 1i8); //~ ERROR: can't pass i8 to variadic function, cast to c_int - foo(1, 2, 1u8); //~ ERROR: can't pass u8 to variadic function, cast to c_uint - foo(1, 2, 1i16); //~ ERROR: can't pass i16 to variadic function, cast to c_int - foo(1, 2, 1u16); //~ ERROR: can't pass u16 to variadic function, cast to c_uint + foo(1, 2, 3f32); //~ ERROR: can't pass an `f32` to variadic function, cast to `c_double` + foo(1, 2, true); //~ ERROR: can't pass `bool` to variadic function, cast to `c_int` + foo(1, 2, 1i8); //~ ERROR: can't pass `i8` to variadic function, cast to `c_int` + foo(1, 2, 1u8); //~ ERROR: can't pass `u8` to variadic function, cast to `c_uint` + foo(1, 2, 1i16); //~ ERROR: can't pass `i16` to variadic function, cast to `c_int` + foo(1, 2, 1u16); //~ ERROR: can't pass `u16` to variadic function, cast to `c_uint` } }