提交 2a50d127 编写于 作者: M Michael Woerister

incr.comp.: Remove support for loading metadata fingerprints.

上级 5974ec74
......@@ -60,7 +60,7 @@
//! user of the `DepNode` API of having to know how to compute the expected
//! fingerprint for a given set of node parameters.
use hir::def_id::{CrateNum, DefId, DefIndex};
use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX};
use hir::map::DefPathHash;
use hir::{HirId, ItemLocalId};
......@@ -420,7 +420,7 @@ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
[input] Hir(DefId),
// Represents metadata from an extern crate.
[input] MetaData(DefId),
[input] CrateMetadata(CrateNum),
// Represents some artifact that we save to disk. Note that these
// do not have a def-id as part of their identifier.
......@@ -678,6 +678,22 @@ fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String {
}
}
impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (CrateNum,) {
const CAN_RECONSTRUCT_QUERY_KEY: bool = true;
fn to_fingerprint(&self, tcx: TyCtxt) -> Fingerprint {
let def_id = DefId {
krate: self.0,
index: CRATE_DEF_INDEX,
};
tcx.def_path_hash(def_id).0
}
fn to_debug_str(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> String {
tcx.crate_name(self.0).as_str().to_string()
}
}
impl<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> DepNodeParams<'a, 'gcx, 'tcx> for (DefId, DefId) {
const CAN_RECONSTRUCT_QUERY_KEY: bool = false;
......
......@@ -267,6 +267,8 @@ pub trait CrateStore {
fn export_macros_untracked(&self, cnum: CrateNum);
fn dep_kind_untracked(&self, cnum: CrateNum) -> DepKind;
fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol;
fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> Symbol;
fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh;
fn struct_field_names_untracked(&self, def: DefId) -> Vec<ast::Name>;
fn item_children_untracked(&self, did: DefId, sess: &Session) -> Vec<def::Export>;
fn load_macro_untracked(&self, did: DefId, sess: &Session) -> LoadedMacro;
......@@ -336,6 +338,10 @@ fn associated_item_cloned_untracked(&self, def: DefId) -> ty::AssociatedItem
fn dep_kind_untracked(&self, cnum: CrateNum) -> DepKind { bug!("is_explicitly_linked") }
fn export_macros_untracked(&self, cnum: CrateNum) { bug!("export_macros") }
fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol { bug!("crate_name") }
fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> Symbol {
bug!("crate_disambiguator")
}
fn crate_hash_untracked(&self, cnum: CrateNum) -> Svh { bug!("crate_hash") }
// resolve
fn def_key(&self, def: DefId) -> DefKey { bug!("def_key") }
......
......@@ -1021,7 +1021,7 @@ fn parse_optimization_fuel(slot: &mut Option<(String, u64)>, v: Option<&str>) ->
"attempt to recover from parse errors (experimental)"),
incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
"enable incremental compilation (experimental)"),
incremental_cc: bool = (true, parse_bool, [UNTRACKED],
incremental_cc: bool = (false, parse_bool, [UNTRACKED],
"enable cross-crate incremental compilation (even more experimental)"),
incremental_info: bool = (false, parse_bool, [UNTRACKED],
"print high-level information about incremental reuse (or the lack thereof)"),
......
......@@ -11,6 +11,7 @@
//! type context book-keeping
use dep_graph::DepGraph;
use dep_graph::{DepNode, DepConstructor};
use errors::DiagnosticBuilder;
use session::Session;
use session::config::OutputFilenames;
......@@ -1237,6 +1238,25 @@ pub fn create_stable_hashing_context(self) -> StableHashingContext<'gcx> {
self.cstore)
}
// This method makes sure that we have a DepNode and a Fingerprint for
// every upstream crate. It needs to be called once right after the tcx is
// created.
// With full-fledged red/green, the method will probably become unnecessary
// as this will be done on-demand.
pub fn allocate_metadata_dep_nodes(self) {
// We cannot use the query versions of crates() and crate_hash(), since
// those would need the DepNodes that we are allocating here.
for cnum in self.cstore.crates_untracked() {
let dep_node = DepNode::new(self, DepConstructor::CrateMetadata(cnum));
let crate_hash = self.cstore.crate_hash_untracked(cnum);
self.dep_graph.with_task(dep_node,
self,
crate_hash,
|_, x| x // No transformation needed
);
}
}
// This method exercises the `in_scope_traits_map` query for all possible
// values so that we have their fingerprints available in the DepGraph.
// This is only required as long as we still use the old dependency tracking
......
......@@ -114,15 +114,12 @@
//! unsupported file system and emit a warning in that case. This is not yet
//! implemented.
use rustc::hir::def_id::CrateNum;
use rustc::hir::svh::Svh;
use rustc::session::Session;
use rustc::ty::TyCtxt;
use rustc::util::fs as fs_util;
use rustc_data_structures::{flock, base_n};
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
use std::ffi::OsString;
use std::fs as std_fs;
use std::io;
use std::mem;
......@@ -158,10 +155,6 @@ pub fn metadata_hash_export_path(sess: &Session) -> PathBuf {
in_incr_comp_dir_sess(sess, METADATA_HASHES_FILENAME)
}
pub fn metadata_hash_import_path(import_session_dir: &Path) -> PathBuf {
import_session_dir.join(METADATA_HASHES_FILENAME)
}
pub fn lock_file_path(session_dir: &Path) -> PathBuf {
let crate_dir = session_dir.parent().unwrap();
......@@ -621,70 +614,6 @@ fn string_to_timestamp(s: &str) -> Result<SystemTime, ()> {
Ok(UNIX_EPOCH + duration)
}
fn crate_path_tcx(tcx: TyCtxt, cnum: CrateNum) -> PathBuf {
crate_path(tcx.sess, &tcx.crate_name(cnum).as_str(), &tcx.crate_disambiguator(cnum).as_str())
}
/// Finds the session directory containing the correct metadata hashes file for
/// the given crate. In order to do that it has to compute the crate directory
/// of the given crate, and in there, look for the session directory with the
/// correct SVH in it.
/// Note that we have to match on the exact SVH here, not just the
/// crate's (name, disambiguator) pair. The metadata hashes are only valid for
/// the exact version of the binary we are reading from now (i.e. the hashes
/// are part of the dependency graph of a specific compilation session).
pub fn find_metadata_hashes_for(tcx: TyCtxt, cnum: CrateNum) -> Option<PathBuf> {
let crate_directory = crate_path_tcx(tcx, cnum);
if !crate_directory.exists() {
return None
}
let dir_entries = match crate_directory.read_dir() {
Ok(dir_entries) => dir_entries,
Err(e) => {
tcx.sess
.err(&format!("incremental compilation: Could not read crate directory `{}`: {}",
crate_directory.display(), e));
return None
}
};
let target_svh = tcx.crate_hash(cnum);
let target_svh = base_n::encode(target_svh.as_u64(), INT_ENCODE_BASE);
let sub_dir = find_metadata_hashes_iter(&target_svh, dir_entries.filter_map(|e| {
e.ok().map(|e| e.file_name().to_string_lossy().into_owned())
}));
sub_dir.map(|sub_dir_name| crate_directory.join(&sub_dir_name))
}
fn find_metadata_hashes_iter<'a, I>(target_svh: &str, iter: I) -> Option<OsString>
where I: Iterator<Item=String>
{
for sub_dir_name in iter {
if !is_session_directory(&sub_dir_name) || !is_finalized(&sub_dir_name) {
// This is not a usable session directory
continue
}
let is_match = if let Some(last_dash_pos) = sub_dir_name.rfind("-") {
let candidate_svh = &sub_dir_name[last_dash_pos + 1 .. ];
target_svh == candidate_svh
} else {
// some kind of invalid directory name
continue
};
if is_match {
return Some(OsString::from(sub_dir_name))
}
}
None
}
fn crate_path(sess: &Session,
crate_name: &str,
crate_disambiguator: &str)
......
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use rustc::dep_graph::{DepNode, DepKind};
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc::hir::svh::Svh;
use rustc::ich::Fingerprint;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::flock;
use rustc_serialize::Decodable;
use rustc_serialize::opaque::Decoder;
use super::data::*;
use super::fs::*;
use super::file_format;
use std::hash::Hash;
use std::fmt::Debug;
pub struct HashContext<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
metadata_hashes: FxHashMap<DefId, Fingerprint>,
crate_hashes: FxHashMap<CrateNum, Svh>,
}
impl<'a, 'tcx> HashContext<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
HashContext {
tcx,
metadata_hashes: FxHashMap(),
crate_hashes: FxHashMap(),
}
}
pub fn hash(&mut self, dep_node: &DepNode) -> Option<Fingerprint> {
match dep_node.kind {
// HIR nodes (which always come from our crate) are an input:
DepKind::Krate |
DepKind::InScopeTraits |
DepKind::Hir |
DepKind::HirBody => {
Some(self.tcx.dep_graph.fingerprint_of(dep_node).unwrap())
}
// MetaData from other crates is an *input* to us.
// MetaData nodes from *our* crates are an *output*; we
// don't hash them, but we do compute a hash for them and
// save it for others to use.
DepKind::MetaData => {
let def_id = dep_node.extract_def_id(self.tcx).unwrap();
assert!(!def_id.is_local());
Some(self.metadata_hash(def_id,
def_id.krate,
|this| &mut this.metadata_hashes))
}
_ => {
// Other kinds of nodes represent computed by-products
// that we don't hash directly; instead, they should
// have some transitive dependency on a Hir or
// MetaData node, so we'll just hash that
None
}
}
}
fn metadata_hash<K, C>(&mut self,
key: K,
cnum: CrateNum,
cache: C)
-> Fingerprint
where K: Hash + Eq + Debug,
C: Fn(&mut Self) -> &mut FxHashMap<K, Fingerprint>,
{
debug!("metadata_hash(key={:?})", key);
debug_assert!(cnum != LOCAL_CRATE);
loop {
// check whether we have a result cached for this def-id
if let Some(&hash) = cache(self).get(&key) {
return hash;
}
// check whether we did not find detailed metadata for this
// krate; in that case, we just use the krate's overall hash
if let Some(&svh) = self.crate_hashes.get(&cnum) {
// micro-"optimization": avoid a cache miss if we ask
// for metadata from this particular def-id again.
let fingerprint = svh_to_fingerprint(svh);
cache(self).insert(key, fingerprint);
return fingerprint;
}
// otherwise, load the data and repeat.
self.load_data(cnum);
assert!(self.crate_hashes.contains_key(&cnum));
}
}
fn load_data(&mut self, cnum: CrateNum) {
debug!("load_data(cnum={})", cnum);
let svh = self.tcx.crate_hash(cnum);
let old = self.crate_hashes.insert(cnum, svh);
debug!("load_data: svh={}", svh);
assert!(old.is_none(), "loaded data for crate {:?} twice", cnum);
if let Some(session_dir) = find_metadata_hashes_for(self.tcx, cnum) {
debug!("load_data: session_dir={:?}", session_dir);
// Lock the directory we'll be reading the hashes from.
let lock_file_path = lock_file_path(&session_dir);
let _lock = match flock::Lock::new(&lock_file_path,
false, // don't wait
false, // don't create the lock-file
false) { // shared lock
Ok(lock) => lock,
Err(err) => {
debug!("Could not acquire lock on `{}` while trying to \
load metadata hashes: {}",
lock_file_path.display(),
err);
// Could not acquire the lock. The directory is probably in
// in the process of being deleted. It's OK to just exit
// here. It's the same scenario as if the file had not
// existed in the first place.
return
}
};
let hashes_file_path = metadata_hash_import_path(&session_dir);
match file_format::read_file(self.tcx.sess, &hashes_file_path)
{
Ok(Some(data)) => {
match self.load_from_data(cnum, &data, svh) {
Ok(()) => { }
Err(err) => {
bug!("decoding error in dep-graph from `{}`: {}",
&hashes_file_path.display(), err);
}
}
}
Ok(None) => {
// If the file is not found, that's ok.
}
Err(err) => {
self.tcx.sess.err(
&format!("could not load dep information from `{}`: {}",
hashes_file_path.display(), err));
}
}
}
}
fn load_from_data(&mut self,
cnum: CrateNum,
data: &[u8],
expected_svh: Svh) -> Result<(), String> {
debug!("load_from_data(cnum={})", cnum);
// Load up the hashes for the def-ids from this crate.
let mut decoder = Decoder::new(data, 0);
let svh_in_hashes_file = Svh::decode(&mut decoder)?;
if svh_in_hashes_file != expected_svh {
// We should not be able to get here. If we do, then
// `fs::find_metadata_hashes_for()` has messed up.
bug!("mismatch between SVH in crate and SVH in incr. comp. hashes")
}
let serialized_hashes = SerializedMetadataHashes::decode(&mut decoder)?;
for serialized_hash in serialized_hashes.entry_hashes {
// the hashes are stored with just a def-index, which is
// always relative to the old crate; convert that to use
// our internal crate number
let def_id = DefId { krate: cnum, index: serialized_hash.def_index };
// record the hash for this dep-node
let old = self.metadata_hashes.insert(def_id, serialized_hash.hash);
debug!("load_from_data: def_id={:?} hash={}", def_id, serialized_hash.hash);
assert!(old.is_none(), "already have hash for {:?}", def_id);
}
Ok(())
}
}
fn svh_to_fingerprint(svh: Svh) -> Fingerprint {
Fingerprint::from_smaller_hash(svh.as_u64())
}
......@@ -10,7 +10,7 @@
//! Code to save/load the dep-graph from files.
use rustc::dep_graph::{DepNode, WorkProductId, DepKind};
use rustc::dep_graph::{DepNode, WorkProductId, DepKind, PreviousDepGraph};
use rustc::hir::svh::Svh;
use rustc::ich::Fingerprint;
use rustc::session::Session;
......@@ -24,7 +24,6 @@
use super::data::*;
use super::dirty_clean;
use super::hash::*;
use super::fs::*;
use super::file_format;
use super::work_product;
......@@ -40,6 +39,7 @@
/// actually it doesn't matter all that much.) See `README.md` for
/// more general overview.
pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
tcx.allocate_metadata_dep_nodes();
tcx.precompute_in_scope_traits_hashes();
if tcx.sess.incr_session_load_dep_graph() {
let _ignore = tcx.dep_graph.in_ignore();
......@@ -103,7 +103,7 @@ fn does_still_exist(tcx: TyCtxt, dep_node: &DepNode) -> bool {
DepKind::Hir |
DepKind::HirBody |
DepKind::InScopeTraits |
DepKind::MetaData => {
DepKind::CrateMetadata => {
dep_node.extract_def_id(tcx).is_some()
}
_ => {
......@@ -198,15 +198,12 @@ fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
nodes: &IndexVec<DepNodeIndex, DepNode>,
serialized_hashes: &[(DepNodeIndex, Fingerprint)])
-> DirtyNodes {
let mut hcx = HashContext::new(tcx);
let mut dirty_nodes = FxHashMap();
for &(dep_node_index, prev_hash) in serialized_hashes {
let dep_node = nodes[dep_node_index];
if does_still_exist(tcx, &dep_node) {
let current_hash = hcx.hash(&dep_node).unwrap_or_else(|| {
bug!("Cannot find current ICH for input that still exists?")
});
let current_hash = tcx.dep_graph.fingerprint_of(&dep_node);
if current_hash == prev_hash {
debug!("initial_dirty_nodes: {:?} is clean (hash={:?})",
......@@ -416,7 +413,7 @@ fn process_edge<'a, 'tcx, 'edges>(
// clean target because removing the input would have dirtied the input
// node and transitively dirtied the target.
debug_assert!(match nodes[source].kind {
DepKind::Hir | DepKind::HirBody | DepKind::MetaData => {
DepKind::Hir | DepKind::HirBody | DepKind::CrateMetadata => {
does_still_exist(tcx, &nodes[source])
}
_ => true,
......
......@@ -15,7 +15,6 @@
mod data;
mod dirty_clean;
mod fs;
mod hash;
mod load;
mod preds;
mod save;
......
......@@ -10,10 +10,10 @@
use rustc::dep_graph::{DepGraphQuery, DepNode, DepKind};
use rustc::ich::Fingerprint;
use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph::{Graph, NodeIndex};
use super::hash::*;
mod compress;
......@@ -40,15 +40,13 @@ pub struct Predecessors<'query> {
}
impl<'q> Predecessors<'q> {
pub fn new(query: &'q DepGraphQuery, hcx: &mut HashContext) -> Self {
let tcx = hcx.tcx;
pub fn new(tcx: TyCtxt, query: &'q DepGraphQuery) -> Self {
// Find the set of "start nodes". These are nodes that we will
// possibly query later.
let is_output = |node: &DepNode| -> bool {
match node.kind {
DepKind::WorkProduct => true,
DepKind::MetaData => {
DepKind::CrateMetadata => {
// We do *not* create dep-nodes for the current crate's
// metadata anymore, just for metadata that we import/read
// from other crates.
......@@ -74,7 +72,7 @@ pub fn new(query: &'q DepGraphQuery, hcx: &mut HashContext) -> Self {
let input = *graph.node_data(input_index);
debug!("computing hash for input node `{:?}`", input);
hashes.entry(input)
.or_insert_with(|| hcx.hash(input).unwrap());
.or_insert_with(|| tcx.dep_graph.fingerprint_of(&input));
}
if tcx.sess.opts.debugging_opts.query_dep_graph {
......@@ -89,7 +87,7 @@ pub fn new(query: &'q DepGraphQuery, hcx: &mut HashContext) -> Self {
for node in hir_nodes {
hashes.entry(node)
.or_insert_with(|| hcx.hash(node).unwrap());
.or_insert_with(|| tcx.dep_graph.fingerprint_of(&node));
}
}
......
......@@ -15,6 +15,7 @@
use rustc::middle::cstore::EncodedMetadataHashes;
use rustc::session::Session;
use rustc::ty::TyCtxt;
use rustc::util::common::time;
use rustc::util::nodemap::DefIdMap;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph;
......@@ -26,7 +27,6 @@
use std::path::PathBuf;
use super::data::*;
use super::hash::*;
use super::preds::*;
use super::fs::*;
use super::dirty_clean;
......@@ -45,13 +45,6 @@ pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
return;
}
let query = tcx.dep_graph.query();
if tcx.sess.opts.debugging_opts.incremental_info {
eprintln!("incremental: {} nodes in dep-graph", query.graph.len_nodes());
eprintln!("incremental: {} edges in dep-graph", query.graph.len_edges());
}
// We load the previous metadata hashes now before overwriting the file
// (if we need them for testing).
let prev_metadata_hashes = if tcx.sess.opts.debugging_opts.query_dep_graph {
......@@ -60,8 +53,6 @@ pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
DefIdMap()
};
let mut hcx = HashContext::new(tcx);
let preds = Predecessors::new(&query, &mut hcx);
let mut current_metadata_hashes = FxHashMap();
// IMPORTANT: We are saving the metadata hashes *before* the dep-graph,
......@@ -78,9 +69,25 @@ pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
e));
}
save_in(sess,
dep_graph_path(sess),
|e| encode_dep_graph(tcx, &preds, e));
time(sess.time_passes(), "persist dep-graph (old)", || {
let query = tcx.dep_graph.query();
if tcx.sess.opts.debugging_opts.incremental_info {
eprintln!("incremental: {} nodes in dep-graph", query.graph.len_nodes());
eprintln!("incremental: {} edges in dep-graph", query.graph.len_edges());
}
let preds = Predecessors::new(tcx, &query);
save_in(sess,
dep_graph_path(sess),
|e| encode_dep_graph(tcx, &preds, e));
});
time(sess.time_passes(), "persist dep-graph (new)", || {
save_in(sess,
dep_graph_path_new(sess),
|e| encode_dep_graph_new(tcx, e));
});
dirty_clean::check_dirty_clean_metadata(tcx,
&prev_metadata_hashes,
......
......@@ -55,9 +55,14 @@ pub fn provide<$lt>(providers: &mut Providers<$lt>) {
let ($def_id, $other) = def_id_arg.into_args();
assert!(!$def_id.is_local());
let def_path_hash = $tcx.def_path_hash($def_id);
let dep_node = def_path_hash.to_dep_node(::rustc::dep_graph::DepKind::MetaData);
let def_path_hash = $tcx.def_path_hash(DefId {
krate: $def_id.krate,
index: CRATE_DEF_INDEX
});
let dep_node = def_path_hash
.to_dep_node(::rustc::dep_graph::DepKind::CrateMetadata);
// The DepNodeIndex of the DepNode::CrateMetadata should be
// cached somewhere, so that we can use read_index().
$tcx.dep_graph.read(dep_node);
let $cdata = $tcx.crate_data_as_rc_any($def_id.krate);
......@@ -379,6 +384,16 @@ fn crate_name_untracked(&self, cnum: CrateNum) -> Symbol
self.get_crate_data(cnum).name
}
fn crate_disambiguator_untracked(&self, cnum: CrateNum) -> Symbol
{
self.get_crate_data(cnum).disambiguator()
}
fn crate_hash_untracked(&self, cnum: CrateNum) -> hir::svh::Svh
{
self.get_crate_data(cnum).hash()
}
/// Returns the `DefKey` for a given `DefId`. This indicates the
/// parent `DefId` as well as some idea of what kind of data the
/// `DefId` refers to.
......
......@@ -12,6 +12,8 @@
// revisions:rpass1 rpass2
// compile-flags:-Z query-dep-graph
// ignore-test -- ignored until red/green restores cross-crate tracking fidelity
#![feature(rustc_attrs)]
extern crate a;
......
......@@ -15,6 +15,8 @@
// compile-flags: -Z query-dep-graph
// aux-build:point.rs
// ignore-test -- ignored until red/green restores cross-crate tracking fidelity
#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]
#![allow(dead_code)]
......
......@@ -15,6 +15,8 @@
// compile-flags: -Z query-dep-graph
// aux-build:point.rs
// ignore-test -- ignored until red/green restores cross-crate tracking fidelity
#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]
#![allow(dead_code)]
......
......@@ -18,6 +18,8 @@
// no-prefer-dynamic
// compile-flags: -Z query-dep-graph
// ignore-test -- ignored until red/green restores cross-crate tracking fidelity
#![feature(rustc_attrs)]
extern crate a;
......
......@@ -12,6 +12,8 @@
// revisions:rpass1 rpass2
// compile-flags: -Z query-dep-graph
// ignore-test -- ignored until red/green restores cross-crate tracking fidelity
#![feature(rustc_attrs)]
extern crate a;
......
......@@ -12,6 +12,8 @@
// revisions:rpass1 rpass2 rpass3
// compile-flags: -Z query-dep-graph
// ignore-test -- ignored until red/green restores cross-crate tracking fidelity
#![feature(rustc_attrs)]
extern crate a;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册