diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 46787f38320..40fd3dede3d 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -150,6 +150,7 @@ impl DepNode { check! { CollectItem, BorrowCheck, + Hir, TransCrateItem, TypeckItemType, TypeckItemBody, diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 77cc62060aa..a816d26edad 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -14,6 +14,7 @@ use dep_graph::{DepGraph, DepTrackingMap}; use session::Session; use middle; use middle::cstore::LOCAL_CRATE; +use hir::TraitMap; use hir::def::DefMap; use hir::def_id::{DefId, DefIndex}; use hir::map as ast_map; @@ -299,8 +300,16 @@ pub struct GlobalCtxt<'tcx> { pub types: CommonTypes<'tcx>, pub sess: &'tcx Session, + + /// Map from path id to the results from resolve; generated + /// initially by resolve and updated during typeck in some cases + /// (e.g., UFCS paths) pub def_map: RefCell, + /// Map indicating what traits are in scope for places where this + /// is relevant; generated by resolve. + pub trait_map: TraitMap, + pub named_region_map: resolve_lifetime::NamedRegionMap, pub region_maps: RegionMaps, @@ -666,6 +675,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn create_and_enter(s: &'tcx Session, arenas: &'tcx CtxtArenas<'tcx>, def_map: DefMap, + trait_map: TraitMap, named_region_map: resolve_lifetime::NamedRegionMap, map: ast_map::Map<'tcx>, freevars: FreevarMap, @@ -694,6 +704,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { variance_computed: Cell::new(false), sess: s, def_map: RefCell::new(def_map), + trait_map: trait_map, tables: RefCell::new(Tables::empty()), impl_trait_refs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), trait_defs: RefCell::new(DepTrackingMap::new(dep_graph.clone())), diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f1f5e194da4..7f50522b203 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -846,10 +846,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, let index = stability::Index::new(&hir_map); - let trait_map = resolutions.trait_map; TyCtxt::create_and_enter(sess, arenas, resolutions.def_map, + resolutions.trait_map, named_region_map, hir_map, resolutions.freevars, @@ -864,7 +864,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, || rustc_incremental::load_dep_graph(tcx)); // passes are timed inside typeck - try_with_f!(typeck::check_crate(tcx, trait_map), (tcx, None, analysis)); + try_with_f!(typeck::check_crate(tcx), (tcx, None, analysis)); time(time_passes, "const checking", diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 39763bfa0eb..2b0bd14d83f 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -131,6 +131,7 @@ fn test_env(source_string: &str, TyCtxt::create_and_enter(&sess, &arenas, resolutions.def_map, + resolutions.trait_map, named_region_map.unwrap(), ast_map, resolutions.freevars, diff --git a/src/librustc_incremental/calculate_svh.rs b/src/librustc_incremental/calculate_svh.rs deleted file mode 100644 index 4b2d42ca889..00000000000 --- a/src/librustc_incremental/calculate_svh.rs +++ /dev/null @@ -1,454 +0,0 @@ -// 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 syntax::attr::AttributeMethods; -use std::hash::{Hash, SipHasher, Hasher}; -use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; -use rustc::hir::map::{NodeItem, NodeForeignItem}; -use rustc::hir::svh::Svh; -use rustc::ty::TyCtxt; -use rustc::hir::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<'a, 'tcx> SvhCalculate for TyCtxt<'a, 'tcx, '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.local_crate_disambiguator(); - 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); - - // FIXME(#32753) -- at (*) we `to_le` for endianness, but is - // this enough, and does it matter anyway? - "crate_disambiguator".hash(&mut state); - crate_disambiguator.len().to_le().hash(&mut state); // (*) - crate_disambiguator.hash(&mut state); - - debug!("crate_disambiguator: {:?}", crate_disambiguator); - 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.meta().hash(&mut state); - } - - Svh::new(state.finish()) - } - - fn calculate_item_hash(self, def_id: DefId) -> u64 { - assert!(def_id.is_local()); - - debug!("calculate_item_hash(def_id={:?})", def_id); - - 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(); - match self.map.find(node_id) { - Some(NodeItem(item)) => visit.visit_item(item), - Some(NodeForeignItem(item)) => visit.visit_foreign_item(item), - r => bug!("calculate_item_hash: expected an item for node {} not {:?}", - node_id, r), - } - } - } - - let hash = state.finish(); - - debug!("calculate_item_hash: def_id={:?} hash={:?}", def_id, hash); - - hash - } -} - -// 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::parse::token; - use syntax_pos::Span; - use rustc::ty::TyCtxt; - use rustc::hir; - use rustc::hir::*; - use rustc::hir::map::DefPath; - use rustc::hir::intravisit as visit; - use rustc::hir::intravisit::{Visitor, FnKind}; - - use std::hash::{Hash, SipHasher}; - - pub struct StrictVersionHashVisitor<'a, 'tcx: 'a> { - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, - pub st: &'a mut SipHasher, - } - - impl<'a, 'tcx> StrictVersionHashVisitor<'a, 'tcx> { - pub fn new(st: &'a mut SipHasher, - tcx: TyCtxt<'a, 'tcx, 'tcx>) - -> Self { - StrictVersionHashVisitor { st: st, tcx: tcx } - } - - fn hash_def_path(&mut self, path: &DefPath) { - path.deterministic_hash_to(self.tcx, self.st); - } - } - - // 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, - 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.node.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.as_str())), - ExprAgain(id) => SawExprAgain(id.map(|id| id.node.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) { - let def_path = self.tcx.map.def_path_from_id(item.id).unwrap(); - debug!("visit_nested_item: def_path={:?} st={:?}", def_path, self.st); - self.hash_def_path(&def_path); - } - - fn visit_variant_data(&mut self, s: &'a VariantData, name: Name, - g: &'a Generics, _: NodeId, _: Span) { - debug!("visit_variant_data: st={:?}", self.st); - 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) { - debug!("visit_variant: st={:?}", self.st); - 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) { - debug!("visit_name: st={:?}", self.st); - SawIdent(name.as_str()).hash(self.st); - } - - fn visit_lifetime(&mut self, l: &'a Lifetime) { - debug!("visit_lifetime: st={:?}", self.st); - SawLifetime(l.name.as_str()).hash(self.st); - } - - fn visit_lifetime_def(&mut self, l: &'a LifetimeDef) { - debug!("visit_lifetime_def: st={:?}", self.st); - 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) { - debug!("visit_expr: st={:?}", self.st); - SawExpr(saw_expr(&ex.node)).hash(self.st); visit::walk_expr(self, ex) - } - - fn visit_stmt(&mut self, s: &'a Stmt) { - debug!("visit_stmt: st={:?}", self.st); - SawStmt(saw_stmt(&s.node)).hash(self.st); visit::walk_stmt(self, s) - } - - fn visit_foreign_item(&mut self, i: &'a ForeignItem) { - debug!("visit_foreign_item: st={:?}", self.st); - - // 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) { - debug!("visit_mod: st={:?}", self.st); - SawMod.hash(self.st); visit::walk_mod(self, m, n) - } - - fn visit_decl(&mut self, d: &'a Decl) { - debug!("visit_decl: st={:?}", self.st); - SawDecl.hash(self.st); visit::walk_decl(self, d) - } - - fn visit_ty(&mut self, t: &'a Ty) { - debug!("visit_ty: st={:?}", self.st); - SawTy.hash(self.st); visit::walk_ty(self, t) - } - - fn visit_generics(&mut self, g: &'a Generics) { - debug!("visit_generics: st={:?}", self.st); - 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, n: NodeId) { - debug!("visit_fn: st={:?}", self.st); - SawFn.hash(self.st); visit::walk_fn(self, fk, fd, b, s, n) - } - - fn visit_trait_item(&mut self, ti: &'a TraitItem) { - debug!("visit_trait_item: st={:?}", self.st); - SawTraitItem.hash(self.st); visit::walk_trait_item(self, ti) - } - - fn visit_impl_item(&mut self, ii: &'a ImplItem) { - debug!("visit_impl_item: st={:?}", self.st); - SawImplItem.hash(self.st); visit::walk_impl_item(self, ii) - } - - fn visit_struct_field(&mut self, s: &'a StructField) { - debug!("visit_struct_field: st={:?}", self.st); - SawStructField.hash(self.st); visit::walk_struct_field(self, s) - } - - fn visit_path(&mut self, path: &'a Path, _: ast::NodeId) { - debug!("visit_path: st={:?}", self.st); - SawPath.hash(self.st); visit::walk_path(self, path) - } - - fn visit_block(&mut self, b: &'a Block) { - debug!("visit_block: st={:?}", self.st); - SawBlock.hash(self.st); visit::walk_block(self, b) - } - - fn visit_pat(&mut self, p: &'a Pat) { - debug!("visit_pat: st={:?}", self.st); - SawPat.hash(self.st); visit::walk_pat(self, p) - } - - fn visit_local(&mut self, l: &'a Local) { - debug!("visit_local: st={:?}", self.st); - SawLocal.hash(self.st); visit::walk_local(self, l) - } - - fn visit_arm(&mut self, a: &'a Arm) { - debug!("visit_arm: st={:?}", self.st); - SawArm.hash(self.st); visit::walk_arm(self, a) - } - } -} diff --git a/src/librustc_incremental/calculate_svh/mod.rs b/src/librustc_incremental/calculate_svh/mod.rs new file mode 100644 index 00000000000..d7caf8c882f --- /dev/null +++ b/src/librustc_incremental/calculate_svh/mod.rs @@ -0,0 +1,113 @@ +// 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 syntax::attr::AttributeMethods; +use std::hash::{Hash, SipHasher, Hasher}; +use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; +use rustc::hir::map::{NodeItem, NodeForeignItem}; +use rustc::hir::svh::Svh; +use rustc::ty::TyCtxt; +use rustc::hir::intravisit::{self, Visitor}; + +use self::svh_visitor::StrictVersionHashVisitor; + +mod svh_visitor; + +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<'a, 'tcx> SvhCalculate for TyCtxt<'a, 'tcx, '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.local_crate_disambiguator(); + 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); + + // FIXME(#32753) -- at (*) we `to_le` for endianness, but is + // this enough, and does it matter anyway? + "crate_disambiguator".hash(&mut state); + crate_disambiguator.len().to_le().hash(&mut state); // (*) + crate_disambiguator.hash(&mut state); + + debug!("crate_disambiguator: {:?}", crate_disambiguator); + 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.meta().hash(&mut state); + } + + Svh::new(state.finish()) + } + + fn calculate_item_hash(self, def_id: DefId) -> u64 { + assert!(def_id.is_local()); + + debug!("calculate_item_hash(def_id={:?})", def_id); + + 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(); + match self.map.find(node_id) { + Some(NodeItem(item)) => visit.visit_item(item), + Some(NodeForeignItem(item)) => visit.visit_foreign_item(item), + r => bug!("calculate_item_hash: expected an item for node {} not {:?}", + node_id, r), + } + } + } + + let hash = state.finish(); + + debug!("calculate_item_hash: def_id={:?} hash={:?}", def_id, hash); + + hash + } +} diff --git a/src/librustc_incremental/calculate_svh/svh_visitor.rs b/src/librustc_incremental/calculate_svh/svh_visitor.rs new file mode 100644 index 00000000000..42e7abeeaca --- /dev/null +++ b/src/librustc_incremental/calculate_svh/svh_visitor.rs @@ -0,0 +1,439 @@ +// 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. + +// 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. + +pub use self::SawExprComponent::*; +pub use self::SawStmtComponent::*; +use self::SawAbiComponent::*; +use syntax::ast::{self, Name, NodeId}; +use syntax::parse::token; +use syntax_pos::Span; +use rustc::hir; +use rustc::hir::*; +use rustc::hir::def::{Def, PathResolution}; +use rustc::hir::def_id::DefId; +use rustc::hir::intravisit as visit; +use rustc::hir::intravisit::{Visitor, FnKind}; +use rustc::hir::map::DefPath; +use rustc::ty::TyCtxt; + +use std::hash::{Hash, SipHasher}; + +pub struct StrictVersionHashVisitor<'a, 'tcx: 'a> { + pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub st: &'a mut SipHasher, +} + +impl<'a, 'tcx> StrictVersionHashVisitor<'a, 'tcx> { + pub fn new(st: &'a mut SipHasher, + tcx: TyCtxt<'a, 'tcx, 'tcx>) + -> Self { + StrictVersionHashVisitor { st: st, tcx: tcx } + } + + fn hash_def_path(&mut self, path: &DefPath) { + path.deterministic_hash_to(self.tcx, self.st); + } +} + +// 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, + SawTy, + SawGenerics, + SawFn, + SawTraitItem, + SawImplItem, + SawStructField, + SawVariant, + 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.node.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.as_str())), + ExprAgain(id) => SawExprAgain(id.map(|id| id.node.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 { + SawStmtExpr, + SawStmtSemi, +} + +impl<'a, 'tcx> Visitor<'a> for StrictVersionHashVisitor<'a, 'tcx> { + fn visit_nested_item(&mut self, _: ItemId) { + // Each item is hashed independently; ignore nested items. + } + + fn visit_variant_data(&mut self, s: &'a VariantData, name: Name, + g: &'a Generics, _: NodeId, _: Span) { + debug!("visit_variant_data: st={:?}", self.st); + 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) { + debug!("visit_variant: st={:?}", self.st); + 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) { + debug!("visit_name: st={:?}", self.st); + SawIdent(name.as_str()).hash(self.st); + } + + fn visit_lifetime(&mut self, l: &'a Lifetime) { + debug!("visit_lifetime: st={:?}", self.st); + SawLifetime(l.name.as_str()).hash(self.st); + } + + fn visit_lifetime_def(&mut self, l: &'a LifetimeDef) { + debug!("visit_lifetime_def: st={:?}", self.st); + 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) { + debug!("visit_expr: st={:?}", self.st); + SawExpr(saw_expr(&ex.node)).hash(self.st); visit::walk_expr(self, ex) + } + + fn visit_stmt(&mut self, s: &'a Stmt) { + debug!("visit_stmt: st={:?}", self.st); + + // We don't want to modify the hash for decls, because + // they might be item decls (if they are local decls, + // we'll hash that fact in visit_local); but we do want to + // remember if this was a StmtExpr or StmtSemi (the later + // had an explicit semi-colon; this affects the typing + // rules). + match s.node { + StmtDecl(..) => (), + StmtExpr(..) => SawStmt(SawStmtExpr).hash(self.st), + StmtSemi(..) => SawStmt(SawStmtSemi).hash(self.st), + } + + visit::walk_stmt(self, s) + } + + fn visit_foreign_item(&mut self, i: &'a ForeignItem) { + debug!("visit_foreign_item: st={:?}", self.st); + + // 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) { + debug!("visit_mod: st={:?}", self.st); + SawMod.hash(self.st); visit::walk_mod(self, m, n) + } + + fn visit_ty(&mut self, t: &'a Ty) { + debug!("visit_ty: st={:?}", self.st); + SawTy.hash(self.st); visit::walk_ty(self, t) + } + + fn visit_generics(&mut self, g: &'a Generics) { + debug!("visit_generics: st={:?}", self.st); + 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, n: NodeId) { + debug!("visit_fn: st={:?}", self.st); + SawFn.hash(self.st); visit::walk_fn(self, fk, fd, b, s, n) + } + + fn visit_trait_item(&mut self, ti: &'a TraitItem) { + debug!("visit_trait_item: st={:?}", self.st); + SawTraitItem.hash(self.st); visit::walk_trait_item(self, ti) + } + + fn visit_impl_item(&mut self, ii: &'a ImplItem) { + debug!("visit_impl_item: st={:?}", self.st); + SawImplItem.hash(self.st); visit::walk_impl_item(self, ii) + } + + fn visit_struct_field(&mut self, s: &'a StructField) { + debug!("visit_struct_field: st={:?}", self.st); + SawStructField.hash(self.st); visit::walk_struct_field(self, s) + } + + fn visit_path(&mut self, path: &'a Path, _: ast::NodeId) { + debug!("visit_path: st={:?}", self.st); + SawPath.hash(self.st); visit::walk_path(self, path) + } + + fn visit_block(&mut self, b: &'a Block) { + debug!("visit_block: st={:?}", self.st); + SawBlock.hash(self.st); visit::walk_block(self, b) + } + + fn visit_pat(&mut self, p: &'a Pat) { + debug!("visit_pat: st={:?}", self.st); + SawPat.hash(self.st); visit::walk_pat(self, p) + } + + fn visit_local(&mut self, l: &'a Local) { + debug!("visit_local: st={:?}", self.st); + SawLocal.hash(self.st); visit::walk_local(self, l) + } + + fn visit_arm(&mut self, a: &'a Arm) { + debug!("visit_arm: st={:?}", self.st); + SawArm.hash(self.st); visit::walk_arm(self, a) + } + + fn visit_id(&mut self, id: NodeId) { + debug!("visit_id: id={} st={:?}", id, self.st); + self.hash_resolve(id); + } +} + +#[derive(Hash)] +pub enum DefHash { + SawDefId, + SawLabel, + SawPrimTy, + SawSelfTy, + SawErr, +} + +impl<'a, 'tcx> StrictVersionHashVisitor<'a, 'tcx> { + fn hash_resolve(&mut self, id: ast::NodeId) { + // Because whether or not a given id has an entry is dependent + // solely on expr variant etc, we don't need to hash whether + // or not an entry was present (we are already hashing what + // variant it is above when we visit the HIR). + + if let Some(def) = self.tcx.def_map.borrow().get(&id) { + self.hash_partial_def(def); + } + + if let Some(traits) = self.tcx.trait_map.get(&id) { + traits.len().hash(self.st); + for candidate in traits { + self.hash_def_id(candidate.def_id); + } + } + } + + fn hash_def_id(&mut self, def_id: DefId) { + let def_path = self.tcx.def_path(def_id); + self.hash_def_path(&def_path); + } + + fn hash_partial_def(&mut self, def: &PathResolution) { + self.hash_def(def.base_def); + def.depth.hash(self.st); + } + + fn hash_def(&mut self, def: Def) { + match def { + // Crucial point: for all of these variants, the variant + + // add'l data that is added is always the same if the + // def-id is the same, so it suffices to hash the def-id + Def::Fn(..) | + Def::Mod(..) | + Def::ForeignMod(..) | + Def::Static(..) | + Def::Variant(..) | + Def::Enum(..) | + Def::TyAlias(..) | + Def::AssociatedTy(..) | + Def::TyParam(..) | + Def::Struct(..) | + Def::Trait(..) | + Def::Method(..) | + Def::Const(..) | + Def::AssociatedConst(..) | + Def::Local(..) | + Def::Upvar(..) => { + DefHash::SawDefId.hash(self.st); + self.hash_def_id(def.def_id()); + } + + Def::Label(..) => { + DefHash::SawLabel.hash(self.st); + // we don't encode the `id` because it always refers to something + // within this item, so if it changed, there would have to be other + // changes too + } + Def::PrimTy(ref prim_ty) => { + DefHash::SawPrimTy.hash(self.st); + prim_ty.hash(self.st); + } + Def::SelfTy(..) => { + DefHash::SawSelfTy.hash(self.st); + // the meaning of Self is always the same within a + // given context, so we don't need to hash the other + // fields + } + Def::Err => { + DefHash::SawErr.hash(self.st); + } + } + } +} diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index dee4d667b8d..3c77cc07d3d 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -24,10 +24,13 @@ //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. +use super::directory::RetracedDefIdDirectory; +use super::load::DirtyNodes; use rustc::dep_graph::{DepGraphQuery, DepNode}; use rustc::hir; use rustc::hir::def_id::DefId; use rustc::hir::intravisit::Visitor; +use rustc_data_structures::fnv::FnvHashSet; use syntax::ast::{self, Attribute, MetaItem}; use syntax::attr::AttrMetaMethods; use syntax::parse::token::InternedString; @@ -38,19 +41,33 @@ const CLEAN: &'static str = "rustc_clean"; const LABEL: &'static str = "label"; const CFG: &'static str = "cfg"; -pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { +pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + dirty_inputs: &DirtyNodes, + retraced: &RetracedDefIdDirectory) { + // can't add `#[rustc_dirty]` etc without opting in to this feature + if !tcx.sess.features.borrow().rustc_attrs { + return; + } + let _ignore = tcx.dep_graph.in_ignore(); + let dirty_inputs: FnvHashSet> = + dirty_inputs.iter() + .filter_map(|d| retraced.map(d)) + .collect(); let query = tcx.dep_graph.query(); + debug!("query-nodes: {:?}", query.nodes()); let krate = tcx.map.krate(); krate.visit_all_items(&mut DirtyCleanVisitor { tcx: tcx, query: &query, + dirty_inputs: dirty_inputs, }); } pub struct DirtyCleanVisitor<'a, 'tcx:'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, query: &'a DepGraphQuery, + dirty_inputs: FnvHashSet>, } impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { @@ -81,10 +98,13 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { return true; } } + return false; } } - debug!("check_config: no match found"); - return false; + + self.tcx.sess.span_fatal( + attr.span, + &format!("no cfg attribute")); } fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode { @@ -105,29 +125,62 @@ impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> { self.tcx.sess.span_fatal(attr.span, "no `label` found"); } - fn dep_node_str(&self, dep_node: DepNode) -> DepNode { + 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)); + match dep_node { + DepNode::Hir(_) => { + // HIR nodes are inputs, so if we are asserting that the HIR node is + // dirty, we check the dirty input set. + if !self.dirty_inputs.contains(&dep_node) { + let dep_node_str = self.dep_node_str(&dep_node); + self.tcx.sess.span_err( + item.span, + &format!("`{:?}` not found in dirty set, but should be dirty", + dep_node_str)); + } + } + _ => { + // Other kinds of nodes would be targets, so check if + // the dep-graph contains the 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)); + match dep_node { + DepNode::Hir(_) => { + // For HIR nodes, check the inputs. + if self.dirty_inputs.contains(&dep_node) { + let dep_node_str = self.dep_node_str(&dep_node); + self.tcx.sess.span_err( + item.span, + &format!("`{:?}` found in dirty-node set, but should be clean", + dep_node_str)); + } + } + _ => { + // Otherwise, check if the dep-node exists. + 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)); + } + } } } } diff --git a/src/librustc_incremental/persist/load.rs b/src/librustc_incremental/persist/load.rs index f012d27eb4b..79b90b63dc6 100644 --- a/src/librustc_incremental/persist/load.rs +++ b/src/librustc_incremental/persist/load.rs @@ -28,7 +28,7 @@ use super::dirty_clean; use super::hash::*; use super::util::*; -type DirtyNodes = FnvHashSet>; +pub type DirtyNodes = FnvHashSet>; type CleanEdges = Vec<(DepNode, DepNode)>; @@ -45,7 +45,6 @@ pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { let _ignore = tcx.dep_graph.in_ignore(); load_dep_graph_if_exists(tcx); - dirty_clean::check_dirty_clean_annotations(tcx); } fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { @@ -62,7 +61,7 @@ fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { }; match decode_dep_graph(tcx, &dep_graph_data, &work_products_data) { - Ok(()) => return, + Ok(dirty_nodes) => dirty_nodes, Err(err) => { tcx.sess.warn( &format!("decoding error in dep-graph from `{}` and `{}`: {}", @@ -184,6 +183,8 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let work_products = try!(>::decode(&mut work_product_decoder)); reconcile_work_products(tcx, work_products, &dirty_target_nodes); + dirty_clean::check_dirty_clean_annotations(tcx, &dirty_raw_source_nodes, &retraced); + Ok(()) } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 9f1f07004f4..860e569ba7e 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1005,7 +1005,7 @@ pub struct Resolver<'a> { // // There will be an anonymous module created around `g` with the ID of the // entry block for `f`. - pub module_map: NodeMap>, + module_map: NodeMap>, // Whether or not to print error messages. Can be set to true // when getting additional info for error message suggestions, diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 6a1baf13b27..648d1f42fb5 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -580,7 +580,7 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { -> Result<(), MethodError<'tcx>> { let mut duplicates = HashSet::new(); - let opt_applicable_traits = self.ccx.trait_map.get(&expr_id); + let opt_applicable_traits = self.tcx.trait_map.get(&expr_id); if let Some(applicable_traits) = opt_applicable_traits { for trait_candidate in applicable_traits { let trait_did = trait_candidate.def_id; diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 65e00705121..eac26487ea3 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -139,9 +139,6 @@ pub struct TypeAndSubsts<'tcx> { pub struct CrateCtxt<'a, 'tcx: 'a> { ast_ty_to_ty_cache: RefCell>>, - /// A mapping from method call sites to traits that have that method. - pub trait_map: hir::TraitMap, - /// A vector of every trait accessible in the whole crate /// (i.e. including those from subcrates). This is used only for /// error reporting, and so is lazily initialised and generally @@ -321,13 +318,11 @@ fn check_for_entry_fn(ccx: &CrateCtxt) { } } -pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - trait_map: hir::TraitMap) +pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> CompileResult { let time_passes = tcx.sess.time_passes(); let ccx = CrateCtxt { ast_ty_to_ty_cache: RefCell::new(NodeMap()), - trait_map: trait_map, all_traits: RefCell::new(None), stack: RefCell::new(Vec::new()), tcx: tcx diff --git a/src/test/incremental/ich_method_call_trait_scope.rs b/src/test/incremental/ich_method_call_trait_scope.rs new file mode 100644 index 00000000000..f28ecf74dd4 --- /dev/null +++ b/src/test/incremental/ich_method_call_trait_scope.rs @@ -0,0 +1,60 @@ +// 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. + +// Check that the hash for a method call is sensitive to the traits in +// scope. + +// revisions: rpass1 rpass2 + +#![feature(rustc_attrs)] + +fn test() { } + +trait Trait1 { + fn method(&self) { } +} + +impl Trait1 for () { } + +trait Trait2 { + fn method(&self) { } +} + +impl Trait2 for () { } + +#[cfg(rpass1)] +mod mod3 { + use Trait1; + + fn bar() { + ().method(); + } + + fn baz() { + 22; // no method call, traits in scope don't matter + } +} + +#[cfg(rpass2)] +mod mod3 { + use Trait2; + + #[rustc_dirty(label="Hir", cfg="rpass2")] + fn bar() { + ().method(); + } + + #[rustc_clean(label="Hir", cfg="rpass2")] + fn baz() { + 22; // no method call, traits in scope don't matter + } +} + +fn main() { } diff --git a/src/test/incremental/ich_nested_items.rs b/src/test/incremental/ich_nested_items.rs new file mode 100644 index 00000000000..4466cfb1317 --- /dev/null +++ b/src/test/incremental/ich_nested_items.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. + +// Check that the hash of `foo` doesn't change just because we ordered +// the nested items (or even added new ones). + +// revisions: rpass1 rpass2 + +#![feature(rustc_attrs)] + +#[cfg(rpass1)] +fn foo() { + fn bar() { } + fn baz() { } +} + +#[cfg(rpass2)] +#[rustc_clean(label="Hir", cfg="rpass2")] +fn foo() { + #[rustc_clean(label="Hir", cfg="rpass2")] + fn baz() { } // order is different... + + #[rustc_clean(label="Hir", cfg="rpass2")] + fn bar() { } // but that doesn't matter. + + fn bap() { } // neither does adding a new item +} + +fn main() { } diff --git a/src/test/incremental/ich_resolve_results.rs b/src/test/incremental/ich_resolve_results.rs new file mode 100644 index 00000000000..680a91da09f --- /dev/null +++ b/src/test/incremental/ich_resolve_results.rs @@ -0,0 +1,74 @@ +// 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. + +// Check that the hash for `mod3::bar` changes when we change the +// `use` to something different. + +// revisions: rpass1 rpass2 rpass3 + +#![feature(rustc_attrs)] + +fn test() { } + +mod mod1 { + pub struct Foo(pub u32); +} + +mod mod2 { + pub struct Foo(pub i64); +} + +#[cfg(rpass1)] +mod mod3 { + use test; + use mod1::Foo; + + fn in_expr() { + Foo(0); + } + + fn in_type() { + test::(); + } +} + +#[cfg(rpass2)] +mod mod3 { + use mod1::Foo; // <-- Nothing changed, but reordered! + use test; + + #[rustc_clean(label="Hir", cfg="rpass2")] + fn in_expr() { + Foo(0); + } + + #[rustc_clean(label="Hir", cfg="rpass2")] + fn in_type() { + test::(); + } +} + +#[cfg(rpass3)] +mod mod3 { + use test; + use mod2::Foo; // <-- This changed! + + #[rustc_dirty(label="Hir", cfg="rpass3")] + fn in_expr() { + Foo(0); + } + + #[rustc_dirty(label="Hir", cfg="rpass3")] + fn in_type() { + test::(); + } +} + +fn main() { } diff --git a/src/test/incremental/string_constant.rs b/src/test/incremental/string_constant.rs index f4062169256..72072248ec0 100644 --- a/src/test/incremental/string_constant.rs +++ b/src/test/incremental/string_constant.rs @@ -41,8 +41,10 @@ mod x { mod y { use x; - #[rustc_clean(label="TypeckItemBody", cfg="rpass2")] - #[rustc_clean(label="TransCrateItem", cfg="rpass2")] + // FIXME(#35078) -- when body of `x` changes, we treat it as + // though signature changed. + #[rustc_dirty(label="TypeckItemBody", cfg="rpass2")] + #[rustc_dirty(label="TransCrateItem", cfg="rpass2")] pub fn y() { x::x(); }