Auto merge of #33998 - nikomatsakis:incr-comp-dep-node-trait, r=mw
Incr. comp. dep-node for traits, tests Introduce new tests and also make dep-node for trait selection a bit more selective. Fixes #33850 r? @michaelwoerister
This commit is contained in:
commit
382ab92cee
19 changed files with 447 additions and 82 deletions
|
@ -10,7 +10,16 @@
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
|
macro_rules! try_opt {
|
||||||
|
($e:expr) => (
|
||||||
|
match $e {
|
||||||
|
Some(r) => r,
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
|
||||||
pub enum DepNode<D: Clone + Debug> {
|
pub enum DepNode<D: Clone + Debug> {
|
||||||
// The `D` type is "how definitions are identified".
|
// The `D` type is "how definitions are identified".
|
||||||
// During compilation, it is always `DefId`, but when serializing
|
// During compilation, it is always `DefId`, but when serializing
|
||||||
|
@ -116,7 +125,7 @@ pub enum DepNode<D: Clone + Debug> {
|
||||||
// which would yield an overly conservative dep-graph.
|
// which would yield an overly conservative dep-graph.
|
||||||
TraitItems(D),
|
TraitItems(D),
|
||||||
ReprHints(D),
|
ReprHints(D),
|
||||||
TraitSelect(D),
|
TraitSelect(D, Vec<D>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Clone + Debug> DepNode<D> {
|
impl<D: Clone + Debug> DepNode<D> {
|
||||||
|
@ -212,7 +221,11 @@ impl<D: Clone + Debug> DepNode<D> {
|
||||||
TraitImpls(ref d) => op(d).map(TraitImpls),
|
TraitImpls(ref d) => op(d).map(TraitImpls),
|
||||||
TraitItems(ref d) => op(d).map(TraitItems),
|
TraitItems(ref d) => op(d).map(TraitItems),
|
||||||
ReprHints(ref d) => op(d).map(ReprHints),
|
ReprHints(ref d) => op(d).map(ReprHints),
|
||||||
TraitSelect(ref d) => op(d).map(TraitSelect),
|
TraitSelect(ref d, ref type_ds) => {
|
||||||
|
let d = try_opt!(op(d));
|
||||||
|
let type_ds = try_opt!(type_ds.iter().map(|d| op(d)).collect());
|
||||||
|
Some(TraitSelect(d, type_ds))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,26 +47,26 @@ impl<D: Clone + Debug + Hash + Eq> DepGraphQuery<D> {
|
||||||
self.indices.contains_key(&node)
|
self.indices.contains_key(&node)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nodes(&self) -> Vec<DepNode<D>> {
|
pub fn nodes(&self) -> Vec<&DepNode<D>> {
|
||||||
self.graph.all_nodes()
|
self.graph.all_nodes()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|n| n.data.clone())
|
.map(|n| &n.data)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edges(&self) -> Vec<(DepNode<D>,DepNode<D>)> {
|
pub fn edges(&self) -> Vec<(&DepNode<D>,&DepNode<D>)> {
|
||||||
self.graph.all_edges()
|
self.graph.all_edges()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|edge| (edge.source(), edge.target()))
|
.map(|edge| (edge.source(), edge.target()))
|
||||||
.map(|(s, t)| (self.graph.node_data(s).clone(),
|
.map(|(s, t)| (self.graph.node_data(s),
|
||||||
self.graph.node_data(t).clone()))
|
self.graph.node_data(t)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reachable_nodes(&self, node: DepNode<D>, direction: Direction) -> Vec<DepNode<D>> {
|
fn reachable_nodes(&self, node: &DepNode<D>, direction: Direction) -> Vec<&DepNode<D>> {
|
||||||
if let Some(&index) = self.indices.get(&node) {
|
if let Some(&index) = self.indices.get(node) {
|
||||||
self.graph.depth_traverse(index, direction)
|
self.graph.depth_traverse(index, direction)
|
||||||
.map(|s| self.graph.node_data(s).clone())
|
.map(|s| self.graph.node_data(s))
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
|
@ -75,20 +75,20 @@ impl<D: Clone + Debug + Hash + Eq> DepGraphQuery<D> {
|
||||||
|
|
||||||
/// All nodes reachable from `node`. In other words, things that
|
/// All nodes reachable from `node`. In other words, things that
|
||||||
/// will have to be recomputed if `node` changes.
|
/// will have to be recomputed if `node` changes.
|
||||||
pub fn transitive_successors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
|
pub fn transitive_successors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
|
||||||
self.reachable_nodes(node, OUTGOING)
|
self.reachable_nodes(node, OUTGOING)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All nodes that can reach `node`.
|
/// All nodes that can reach `node`.
|
||||||
pub fn transitive_predecessors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
|
pub fn transitive_predecessors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
|
||||||
self.reachable_nodes(node, INCOMING)
|
self.reachable_nodes(node, INCOMING)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Just the outgoing edges from `node`.
|
/// Just the outgoing edges from `node`.
|
||||||
pub fn immediate_successors(&self, node: DepNode<D>) -> Vec<DepNode<D>> {
|
pub fn immediate_successors(&self, node: &DepNode<D>) -> Vec<&DepNode<D>> {
|
||||||
if let Some(&index) = self.indices.get(&node) {
|
if let Some(&index) = self.indices.get(&node) {
|
||||||
self.graph.successor_nodes(index)
|
self.graph.successor_nodes(index)
|
||||||
.map(|s| self.graph.node_data(s).clone())
|
.map(|s| self.graph.node_data(s))
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
|
|
|
@ -14,20 +14,20 @@ use super::thread::{DepGraphThreadData, DepMessage};
|
||||||
|
|
||||||
pub struct DepTask<'graph> {
|
pub struct DepTask<'graph> {
|
||||||
data: &'graph DepGraphThreadData,
|
data: &'graph DepGraphThreadData,
|
||||||
key: DepNode<DefId>,
|
key: Option<DepNode<DefId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'graph> DepTask<'graph> {
|
impl<'graph> DepTask<'graph> {
|
||||||
pub fn new(data: &'graph DepGraphThreadData, key: DepNode<DefId>)
|
pub fn new(data: &'graph DepGraphThreadData, key: DepNode<DefId>)
|
||||||
-> DepTask<'graph> {
|
-> DepTask<'graph> {
|
||||||
data.enqueue(DepMessage::PushTask(key));
|
data.enqueue(DepMessage::PushTask(key.clone()));
|
||||||
DepTask { data: data, key: key }
|
DepTask { data: data, key: Some(key) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'graph> Drop for DepTask<'graph> {
|
impl<'graph> Drop for DepTask<'graph> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.data.enqueue(DepMessage::PopTask(self.key));
|
self.data.enqueue(DepMessage::PopTask(self.key.take().unwrap()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub fn visit_all_items_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
fn visit_item(&mut self, i: &'tcx hir::Item) {
|
fn visit_item(&mut self, i: &'tcx hir::Item) {
|
||||||
let item_def_id = self.tcx.map.local_def_id(i.id);
|
let item_def_id = self.tcx.map.local_def_id(i.id);
|
||||||
let task_id = (self.dep_node_fn)(item_def_id);
|
let task_id = (self.dep_node_fn)(item_def_id);
|
||||||
let _task = self.tcx.dep_graph.in_task(task_id);
|
let _task = self.tcx.dep_graph.in_task(task_id.clone());
|
||||||
debug!("Started task {:?}", task_id);
|
debug!("Started task {:?}", task_id);
|
||||||
self.tcx.dep_graph.read(DepNode::Hir(item_def_id));
|
self.tcx.dep_graph.read(DepNode::Hir(item_def_id));
|
||||||
self.visitor.visit_item(i);
|
self.visitor.visit_item(i);
|
||||||
|
|
|
@ -946,7 +946,28 @@ impl<'tcx> TraitPredicate<'tcx> {
|
||||||
|
|
||||||
/// Creates the dep-node for selecting/evaluating this trait reference.
|
/// Creates the dep-node for selecting/evaluating this trait reference.
|
||||||
fn dep_node(&self) -> DepNode<DefId> {
|
fn dep_node(&self) -> DepNode<DefId> {
|
||||||
DepNode::TraitSelect(self.def_id())
|
// Ideally, the dep-node would just have all the input types
|
||||||
|
// in it. But they are limited to including def-ids. So as an
|
||||||
|
// approximation we include the def-ids for all nominal types
|
||||||
|
// found somewhere. This means that we will e.g. conflate the
|
||||||
|
// dep-nodes for `u32: SomeTrait` and `u64: SomeTrait`, but we
|
||||||
|
// would have distinct dep-nodes for `Vec<u32>: SomeTrait`,
|
||||||
|
// `Rc<u32>: SomeTrait`, and `(Vec<u32>, Rc<u32>): SomeTrait`.
|
||||||
|
// Note that it's always sound to conflate dep-nodes, it just
|
||||||
|
// leads to more recompilation.
|
||||||
|
let def_ids: Vec<_> =
|
||||||
|
self.input_types()
|
||||||
|
.iter()
|
||||||
|
.flat_map(|t| t.walk())
|
||||||
|
.filter_map(|t| match t.sty {
|
||||||
|
ty::TyStruct(adt_def, _) |
|
||||||
|
ty::TyEnum(adt_def, _) =>
|
||||||
|
Some(adt_def.did),
|
||||||
|
_ =>
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
DepNode::TraitSelect(self.def_id(), def_ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_types(&self) -> &[Ty<'tcx>] {
|
pub fn input_types(&self) -> &[Ty<'tcx>] {
|
||||||
|
@ -1768,9 +1789,8 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
|
||||||
stack: &mut Vec<AdtDefMaster<'tcx>>)
|
stack: &mut Vec<AdtDefMaster<'tcx>>)
|
||||||
{
|
{
|
||||||
|
|
||||||
let dep_node = DepNode::SizedConstraint(self.did);
|
let dep_node = || DepNode::SizedConstraint(self.did);
|
||||||
|
if self.sized_constraint.get(dep_node()).is_some() {
|
||||||
if self.sized_constraint.get(dep_node).is_some() {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1780,7 +1800,7 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
|
||||||
//
|
//
|
||||||
// Consider the type as Sized in the meanwhile to avoid
|
// Consider the type as Sized in the meanwhile to avoid
|
||||||
// further errors.
|
// further errors.
|
||||||
self.sized_constraint.fulfill(dep_node, tcx.types.err);
|
self.sized_constraint.fulfill(dep_node(), tcx.types.err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1803,14 +1823,14 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> {
|
||||||
_ => tcx.mk_tup(tys)
|
_ => tcx.mk_tup(tys)
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.sized_constraint.get(dep_node) {
|
match self.sized_constraint.get(dep_node()) {
|
||||||
Some(old_ty) => {
|
Some(old_ty) => {
|
||||||
debug!("calculate_sized_constraint: {:?} recurred", self);
|
debug!("calculate_sized_constraint: {:?} recurred", self);
|
||||||
assert_eq!(old_ty, tcx.types.err)
|
assert_eq!(old_ty, tcx.types.err)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
debug!("calculate_sized_constraint: {:?} => {:?}", self, ty);
|
debug!("calculate_sized_constraint: {:?} => {:?}", self, ty);
|
||||||
self.sized_constraint.fulfill(dep_node, ty)
|
self.sized_constraint.fulfill(dep_node(), ty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ fn check_paths<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for &(_, source_def_id, source_dep_node) in sources {
|
for &(_, source_def_id, ref source_dep_node) in sources {
|
||||||
let dependents = query.transitive_successors(source_dep_node);
|
let dependents = query.transitive_successors(source_dep_node);
|
||||||
for &(target_span, ref target_pass, _, ref target_dep_node) in targets {
|
for &(target_span, ref target_pass, _, ref target_dep_node) in targets {
|
||||||
if !dependents.contains(&target_dep_node) {
|
if !dependents.contains(&target_dep_node) {
|
||||||
|
@ -239,7 +239,7 @@ fn dump_graph(tcx: TyCtxt) {
|
||||||
{ // dump a .txt file with just the edges:
|
{ // dump a .txt file with just the edges:
|
||||||
let txt_path = format!("{}.txt", path);
|
let txt_path = format!("{}.txt", path);
|
||||||
let mut file = File::create(&txt_path).unwrap();
|
let mut file = File::create(&txt_path).unwrap();
|
||||||
for &(source, target) in &edges {
|
for &(ref source, ref target) in &edges {
|
||||||
write!(file, "{:?} -> {:?}\n", source, target).unwrap();
|
write!(file, "{:?} -> {:?}\n", source, target).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -252,34 +252,34 @@ fn dump_graph(tcx: TyCtxt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GraphvizDepGraph(FnvHashSet<DepNode<DefId>>,
|
pub struct GraphvizDepGraph<'q>(FnvHashSet<&'q DepNode<DefId>>,
|
||||||
Vec<(DepNode<DefId>, DepNode<DefId>)>);
|
Vec<(&'q DepNode<DefId>, &'q DepNode<DefId>)>);
|
||||||
|
|
||||||
impl<'a, 'tcx> dot::GraphWalk<'a> for GraphvizDepGraph {
|
impl<'a, 'tcx, 'q> dot::GraphWalk<'a> for GraphvizDepGraph<'q> {
|
||||||
type Node = DepNode<DefId>;
|
type Node = &'q DepNode<DefId>;
|
||||||
type Edge = (DepNode<DefId>, DepNode<DefId>);
|
type Edge = (&'q DepNode<DefId>, &'q DepNode<DefId>);
|
||||||
fn nodes(&self) -> dot::Nodes<DepNode<DefId>> {
|
fn nodes(&self) -> dot::Nodes<&'q DepNode<DefId>> {
|
||||||
let nodes: Vec<_> = self.0.iter().cloned().collect();
|
let nodes: Vec<_> = self.0.iter().cloned().collect();
|
||||||
nodes.into_cow()
|
nodes.into_cow()
|
||||||
}
|
}
|
||||||
fn edges(&self) -> dot::Edges<(DepNode<DefId>, DepNode<DefId>)> {
|
fn edges(&self) -> dot::Edges<(&'q DepNode<DefId>, &'q DepNode<DefId>)> {
|
||||||
self.1[..].into_cow()
|
self.1[..].into_cow()
|
||||||
}
|
}
|
||||||
fn source(&self, edge: &(DepNode<DefId>, DepNode<DefId>)) -> DepNode<DefId> {
|
fn source(&self, edge: &(&'q DepNode<DefId>, &'q DepNode<DefId>)) -> &'q DepNode<DefId> {
|
||||||
edge.0
|
edge.0
|
||||||
}
|
}
|
||||||
fn target(&self, edge: &(DepNode<DefId>, DepNode<DefId>)) -> DepNode<DefId> {
|
fn target(&self, edge: &(&'q DepNode<DefId>, &'q DepNode<DefId>)) -> &'q DepNode<DefId> {
|
||||||
edge.1
|
edge.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
|
impl<'a, 'tcx, 'q> dot::Labeller<'a> for GraphvizDepGraph<'q> {
|
||||||
type Node = DepNode<DefId>;
|
type Node = &'q DepNode<DefId>;
|
||||||
type Edge = (DepNode<DefId>, DepNode<DefId>);
|
type Edge = (&'q DepNode<DefId>, &'q DepNode<DefId>);
|
||||||
fn graph_id(&self) -> dot::Id {
|
fn graph_id(&self) -> dot::Id {
|
||||||
dot::Id::new("DependencyGraph").unwrap()
|
dot::Id::new("DependencyGraph").unwrap()
|
||||||
}
|
}
|
||||||
fn node_id(&self, n: &DepNode<DefId>) -> dot::Id {
|
fn node_id(&self, n: &&'q DepNode<DefId>) -> dot::Id {
|
||||||
let s: String =
|
let s: String =
|
||||||
format!("{:?}", n).chars()
|
format!("{:?}", n).chars()
|
||||||
.map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
|
.map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
|
||||||
|
@ -287,7 +287,7 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
|
||||||
debug!("n={:?} s={:?}", n, s);
|
debug!("n={:?} s={:?}", n, s);
|
||||||
dot::Id::new(s).unwrap()
|
dot::Id::new(s).unwrap()
|
||||||
}
|
}
|
||||||
fn node_label(&self, n: &DepNode<DefId>) -> dot::LabelText {
|
fn node_label(&self, n: &&'q DepNode<DefId>) -> dot::LabelText {
|
||||||
dot::LabelText::label(format!("{:?}", n))
|
dot::LabelText::label(format!("{:?}", n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,8 +295,8 @@ impl<'a, 'tcx> dot::Labeller<'a> for GraphvizDepGraph {
|
||||||
// Given an optional filter like `"x,y,z"`, returns either `None` (no
|
// Given an optional filter like `"x,y,z"`, returns either `None` (no
|
||||||
// filter) or the set of nodes whose labels contain all of those
|
// filter) or the set of nodes whose labels contain all of those
|
||||||
// substrings.
|
// substrings.
|
||||||
fn node_set(query: &DepGraphQuery<DefId>, filter: &DepNodeFilter)
|
fn node_set<'q>(query: &'q DepGraphQuery<DefId>, filter: &DepNodeFilter)
|
||||||
-> Option<FnvHashSet<DepNode<DefId>>>
|
-> Option<FnvHashSet<&'q DepNode<DefId>>>
|
||||||
{
|
{
|
||||||
debug!("node_set(filter={:?})", filter);
|
debug!("node_set(filter={:?})", filter);
|
||||||
|
|
||||||
|
@ -307,10 +307,10 @@ fn node_set(query: &DepGraphQuery<DefId>, filter: &DepNodeFilter)
|
||||||
Some(query.nodes().into_iter().filter(|n| filter.test(n)).collect())
|
Some(query.nodes().into_iter().filter(|n| filter.test(n)).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_nodes(query: &DepGraphQuery<DefId>,
|
fn filter_nodes<'q>(query: &'q DepGraphQuery<DefId>,
|
||||||
sources: &Option<FnvHashSet<DepNode<DefId>>>,
|
sources: &Option<FnvHashSet<&'q DepNode<DefId>>>,
|
||||||
targets: &Option<FnvHashSet<DepNode<DefId>>>)
|
targets: &Option<FnvHashSet<&'q DepNode<DefId>>>)
|
||||||
-> FnvHashSet<DepNode<DefId>>
|
-> FnvHashSet<&'q DepNode<DefId>>
|
||||||
{
|
{
|
||||||
if let &Some(ref sources) = sources {
|
if let &Some(ref sources) = sources {
|
||||||
if let &Some(ref targets) = targets {
|
if let &Some(ref targets) = targets {
|
||||||
|
@ -325,21 +325,21 @@ fn filter_nodes(query: &DepGraphQuery<DefId>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_nodes(query: &DepGraphQuery<DefId>,
|
fn walk_nodes<'q>(query: &'q DepGraphQuery<DefId>,
|
||||||
starts: &FnvHashSet<DepNode<DefId>>,
|
starts: &FnvHashSet<&'q DepNode<DefId>>,
|
||||||
direction: Direction)
|
direction: Direction)
|
||||||
-> FnvHashSet<DepNode<DefId>>
|
-> FnvHashSet<&'q DepNode<DefId>>
|
||||||
{
|
{
|
||||||
let mut set = FnvHashSet();
|
let mut set = FnvHashSet();
|
||||||
for start in starts {
|
for &start in starts {
|
||||||
debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
|
debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
|
||||||
if set.insert(*start) {
|
if set.insert(start) {
|
||||||
let mut stack = vec![query.indices[start]];
|
let mut stack = vec![query.indices[start]];
|
||||||
while let Some(index) = stack.pop() {
|
while let Some(index) = stack.pop() {
|
||||||
for (_, edge) in query.graph.adjacent_edges(index, direction) {
|
for (_, edge) in query.graph.adjacent_edges(index, direction) {
|
||||||
let neighbor_index = edge.source_or_target(direction);
|
let neighbor_index = edge.source_or_target(direction);
|
||||||
let neighbor = query.graph.node_data(neighbor_index);
|
let neighbor = query.graph.node_data(neighbor_index);
|
||||||
if set.insert(*neighbor) {
|
if set.insert(neighbor) {
|
||||||
stack.push(neighbor_index);
|
stack.push(neighbor_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,10 +349,10 @@ fn walk_nodes(query: &DepGraphQuery<DefId>,
|
||||||
set
|
set
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_between(query: &DepGraphQuery<DefId>,
|
fn walk_between<'q>(query: &'q DepGraphQuery<DefId>,
|
||||||
sources: &FnvHashSet<DepNode<DefId>>,
|
sources: &FnvHashSet<&'q DepNode<DefId>>,
|
||||||
targets: &FnvHashSet<DepNode<DefId>>)
|
targets: &FnvHashSet<&'q DepNode<DefId>>)
|
||||||
-> FnvHashSet<DepNode<DefId>>
|
-> FnvHashSet<&'q DepNode<DefId>>
|
||||||
{
|
{
|
||||||
// This is a bit tricky. We want to include a node only if it is:
|
// 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
|
// (a) reachable from a source and (b) will reach a target. And we
|
||||||
|
@ -365,16 +365,16 @@ fn walk_between(query: &DepGraphQuery<DefId>,
|
||||||
let mut node_states = vec![State::Undecided; query.graph.len_nodes()];
|
let mut node_states = vec![State::Undecided; query.graph.len_nodes()];
|
||||||
|
|
||||||
for &target in targets {
|
for &target in targets {
|
||||||
node_states[query.indices[&target].0] = State::Included;
|
node_states[query.indices[target].0] = State::Included;
|
||||||
}
|
}
|
||||||
|
|
||||||
for source in sources.iter().map(|n| query.indices[n]) {
|
for source in sources.iter().map(|&n| query.indices[n]) {
|
||||||
recurse(query, &mut node_states, source);
|
recurse(query, &mut node_states, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
return query.nodes()
|
return query.nodes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|n| {
|
.filter(|&n| {
|
||||||
let index = query.indices[n];
|
let index = query.indices[n];
|
||||||
node_states[index.0] == State::Included
|
node_states[index.0] == State::Included
|
||||||
})
|
})
|
||||||
|
@ -417,12 +417,12 @@ fn walk_between(query: &DepGraphQuery<DefId>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_edges(query: &DepGraphQuery<DefId>,
|
fn filter_edges<'q>(query: &'q DepGraphQuery<DefId>,
|
||||||
nodes: &FnvHashSet<DepNode<DefId>>)
|
nodes: &FnvHashSet<&'q DepNode<DefId>>)
|
||||||
-> Vec<(DepNode<DefId>, DepNode<DefId>)>
|
-> Vec<(&'q DepNode<DefId>, &'q DepNode<DefId>)>
|
||||||
{
|
{
|
||||||
query.edges()
|
query.edges()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|&(source, target)| nodes.contains(&source) && nodes.contains(&target))
|
.filter(|&(source, target)| nodes.contains(source) && nodes.contains(target))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl RetracedDefIdDirectory {
|
||||||
self.ids[index.index as usize]
|
self.ids[index.index as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map(&self, node: DepNode<DefPathIndex>) -> Option<DepNode<DefId>> {
|
pub fn map(&self, node: &DepNode<DefPathIndex>) -> Option<DepNode<DefId>> {
|
||||||
node.map_def(|&index| self.def_id(index))
|
node.map_def(|&index| self.def_id(index))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ impl<'a,'tcx> DefIdDirectoryBuilder<'a,'tcx> {
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map(&mut self, node: DepNode<DefId>) -> DepNode<DefPathIndex> {
|
pub fn map(&mut self, node: &DepNode<DefId>) -> DepNode<DefPathIndex> {
|
||||||
node.map_def(|&def_id| Some(self.add(def_id))).unwrap()
|
node.map_def(|&def_id| Some(self.add(def_id))).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,8 @@ impl<'a, 'tcx> HashContext<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash(&mut self, dep_node: DepNode<DefId>) -> Option<u64> {
|
pub fn hash(&mut self, dep_node: &DepNode<DefId>) -> Option<u64> {
|
||||||
match dep_node {
|
match *dep_node {
|
||||||
// HIR nodes (which always come from our crate) are an input:
|
// HIR nodes (which always come from our crate) are an input:
|
||||||
DepNode::Hir(def_id) => {
|
DepNode::Hir(def_id) => {
|
||||||
assert!(def_id.is_local());
|
assert!(def_id.is_local());
|
||||||
|
|
|
@ -114,15 +114,15 @@ pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
let clean_nodes =
|
let clean_nodes =
|
||||||
serialized_dep_graph.nodes
|
serialized_dep_graph.nodes
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|&node| retraced.map(node))
|
.filter_map(|node| retraced.map(node))
|
||||||
.filter(|node| !dirty_nodes.contains(node))
|
.filter(|node| !dirty_nodes.contains(node))
|
||||||
.map(|node| (node, node));
|
.map(|node| (node.clone(), node));
|
||||||
|
|
||||||
// Add nodes and edges that are not dirty into our main graph.
|
// Add nodes and edges that are not dirty into our main graph.
|
||||||
let dep_graph = tcx.dep_graph.clone();
|
let dep_graph = tcx.dep_graph.clone();
|
||||||
for (source, target) in clean_edges.into_iter().chain(clean_nodes) {
|
for (source, target) in clean_edges.into_iter().chain(clean_nodes) {
|
||||||
let _task = dep_graph.in_task(target);
|
let _task = dep_graph.in_task(target.clone());
|
||||||
dep_graph.read(source);
|
dep_graph.read(source.clone());
|
||||||
|
|
||||||
debug!("decode_dep_graph: clean edge: {:?} -> {:?}", source, target);
|
debug!("decode_dep_graph: clean edge: {:?} -> {:?}", source, target);
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
for hash in hashes {
|
for hash in hashes {
|
||||||
match hash.node.map_def(|&i| retraced.def_id(i)) {
|
match hash.node.map_def(|&i| retraced.def_id(i)) {
|
||||||
Some(dep_node) => {
|
Some(dep_node) => {
|
||||||
let current_hash = hcx.hash(dep_node).unwrap();
|
let current_hash = hcx.hash(&dep_node).unwrap();
|
||||||
debug!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}",
|
debug!("initial_dirty_nodes: hash of {:?} is {:?}, was {:?}",
|
||||||
dep_node, current_hash, hash.hash);
|
dep_node, current_hash, hash.hash);
|
||||||
if current_hash != hash.hash {
|
if current_hash != hash.hash {
|
||||||
|
@ -171,7 +171,7 @@ fn compute_clean_edges(serialized_edges: &[(SerializedEdge)],
|
||||||
// target) if neither node has been removed. If the source has
|
// target) if neither node has been removed. If the source has
|
||||||
// been removed, add target to the list of dirty nodes.
|
// been removed, add target to the list of dirty nodes.
|
||||||
let mut clean_edges = Vec::with_capacity(serialized_edges.len());
|
let mut clean_edges = Vec::with_capacity(serialized_edges.len());
|
||||||
for &(serialized_source, serialized_target) in serialized_edges {
|
for &(ref serialized_source, ref serialized_target) in serialized_edges {
|
||||||
if let Some(target) = retraced.map(serialized_target) {
|
if let Some(target) = retraced.map(serialized_target) {
|
||||||
if let Some(source) = retraced.map(serialized_source) {
|
if let Some(source) = retraced.map(serialized_source) {
|
||||||
clean_edges.push((source, target))
|
clean_edges.push((source, target))
|
||||||
|
|
|
@ -99,7 +99,7 @@ pub fn encode_dep_graph<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
|
||||||
query.nodes()
|
query.nodes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|dep_node| {
|
.filter_map(|dep_node| {
|
||||||
hcx.hash(dep_node)
|
hcx.hash(&dep_node)
|
||||||
.map(|hash| {
|
.map(|hash| {
|
||||||
let node = builder.map(dep_node);
|
let node = builder.map(dep_node);
|
||||||
SerializedHash { node: node, hash: hash }
|
SerializedHash { node: node, hash: hash }
|
||||||
|
@ -147,7 +147,7 @@ pub fn encode_metadata_hashes<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
|
||||||
let meta_data_def_ids =
|
let meta_data_def_ids =
|
||||||
query.nodes()
|
query.nodes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|dep_node| match dep_node {
|
.filter_map(|dep_node| match *dep_node {
|
||||||
DepNode::MetaData(def_id) if def_id.is_local() => Some(def_id),
|
DepNode::MetaData(def_id) if def_id.is_local() => Some(def_id),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
@ -165,8 +165,8 @@ pub fn encode_metadata_hashes<'a, 'tcx>(hcx: &mut HashContext<'a, 'tcx>,
|
||||||
let dep_node = DepNode::MetaData(def_id);
|
let dep_node = DepNode::MetaData(def_id);
|
||||||
let mut state = SipHasher::new();
|
let mut state = SipHasher::new();
|
||||||
debug!("save: computing metadata hash for {:?}", dep_node);
|
debug!("save: computing metadata hash for {:?}", dep_node);
|
||||||
for node in query.transitive_predecessors(dep_node) {
|
for node in query.transitive_predecessors(&dep_node) {
|
||||||
if let Some(hash) = hcx.hash(node) {
|
if let Some(hash) = hcx.hash(&node) {
|
||||||
debug!("save: predecessor {:?} has hash {}", node, hash);
|
debug!("save: predecessor {:?} has hash {}", node, hash);
|
||||||
state.write_u64(hash.to_le());
|
state.write_u64(hash.to_le());
|
||||||
} else {
|
} else {
|
||||||
|
|
48
src/test/incremental/struct_add_field.rs
Normal file
48
src/test/incremental/struct_add_field.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Test incremental compilation tracking where we change field names
|
||||||
|
// in between revisions (hashing should be stable).
|
||||||
|
|
||||||
|
// revisions:rpass1 rpass2
|
||||||
|
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
pub struct X {
|
||||||
|
pub x: u32,
|
||||||
|
|
||||||
|
#[cfg(rpass2)]
|
||||||
|
pub x2: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EmbedX {
|
||||||
|
x: X
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Y {
|
||||||
|
pub y: char
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_X(x: X) -> u32 {
|
||||||
|
x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_EmbedX(embed: EmbedX) -> u32 {
|
||||||
|
embed.x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_Y() {
|
||||||
|
let x: Y = Y { y: 'c' };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() { }
|
55
src/test/incremental/struct_change_field_name.rs
Normal file
55
src/test/incremental/struct_change_field_name.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Test incremental compilation tracking where we change field names
|
||||||
|
// in between revisions (hashing should be stable).
|
||||||
|
|
||||||
|
// revisions:rpass1 cfail2
|
||||||
|
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
#[cfg(rpass1)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(cfail2)]
|
||||||
|
pub struct X {
|
||||||
|
pub y: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EmbedX {
|
||||||
|
x: X
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Y {
|
||||||
|
pub y: char
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="cfail2")]
|
||||||
|
pub fn use_X() -> u32 {
|
||||||
|
let x: X = X { x: 22 };
|
||||||
|
//[cfail2]~^ ERROR structure `X` has no field named `x`
|
||||||
|
x.x as u32
|
||||||
|
//[cfail2]~^ ERROR attempted access of field `x`
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="cfail2")]
|
||||||
|
pub fn use_EmbedX(embed: EmbedX) -> u32 {
|
||||||
|
embed.x.x as u32
|
||||||
|
//[cfail2]~^ ERROR attempted access of field `x`
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="cfail2")]
|
||||||
|
pub fn use_Y() {
|
||||||
|
let x: Y = Y { y: 'c' };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() { }
|
53
src/test/incremental/struct_change_field_type.rs
Normal file
53
src/test/incremental/struct_change_field_type.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Test incremental compilation tracking where we change nothing
|
||||||
|
// in between revisions (hashing should be stable).
|
||||||
|
|
||||||
|
// revisions:rpass1 rpass2
|
||||||
|
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
#[cfg(rpass1)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(rpass2)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EmbedX {
|
||||||
|
x: X
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Y {
|
||||||
|
pub y: char
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_X() -> u32 {
|
||||||
|
let x: X = X { x: 22 };
|
||||||
|
x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_EmbedX(x: EmbedX) -> u32 {
|
||||||
|
let x: X = X { x: 22 };
|
||||||
|
x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_Y() {
|
||||||
|
let x: Y = Y { y: 'c' };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() { }
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#![crate_type="rlib"]
|
||||||
|
|
||||||
|
#[cfg(rpass1)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(rpass2)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EmbedX {
|
||||||
|
pub x: X
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Y {
|
||||||
|
pub y: char
|
||||||
|
}
|
|
@ -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 <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.
|
||||||
|
|
||||||
|
// aux-build:a.rs
|
||||||
|
// revisions:rpass1 rpass2
|
||||||
|
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
extern crate a;
|
||||||
|
|
||||||
|
use a::*;
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_X() -> u32 {
|
||||||
|
let x: X = X { x: 22 };
|
||||||
|
x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_EmbedX(embed: EmbedX) -> u32 {
|
||||||
|
embed.x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_Y() {
|
||||||
|
let x: Y = Y { y: 'c' };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() { }
|
53
src/test/incremental/struct_change_nothing.rs
Normal file
53
src/test/incremental/struct_change_nothing.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Test incremental compilation tracking where we change nothing
|
||||||
|
// in between revisions (hashing should be stable).
|
||||||
|
|
||||||
|
// revisions:rpass1 rpass2
|
||||||
|
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
#[cfg(rpass1)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(rpass2)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EmbedX {
|
||||||
|
x: X
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Y {
|
||||||
|
pub y: char
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_X() -> u32 {
|
||||||
|
let x: X = X { x: 22 };
|
||||||
|
x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_EmbedX(x: EmbedX) -> u32 {
|
||||||
|
let x: X = X { x: 22 };
|
||||||
|
x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_Y() {
|
||||||
|
let x: Y = Y { y: 'c' };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() { }
|
52
src/test/incremental/struct_remove_field.rs
Normal file
52
src/test/incremental/struct_remove_field.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Test incremental compilation tracking where we change field names
|
||||||
|
// in between revisions (hashing should be stable).
|
||||||
|
|
||||||
|
// revisions:rpass1 rpass2
|
||||||
|
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
#[cfg(rpass1)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: u32,
|
||||||
|
pub x2: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(rpass2)]
|
||||||
|
pub struct X {
|
||||||
|
pub x: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EmbedX {
|
||||||
|
x: X
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Y {
|
||||||
|
pub y: char
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_X(x: X) -> u32 {
|
||||||
|
x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_EmbedX(embed: EmbedX) -> u32 {
|
||||||
|
embed.x.x as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
pub fn use_Y() {
|
||||||
|
let x: Y = Y { y: 'c' };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() { }
|
|
@ -16,4 +16,8 @@ pub type X = u32;
|
||||||
#[cfg(rpass2)]
|
#[cfg(rpass2)]
|
||||||
pub type X = i32;
|
pub type X = i32;
|
||||||
|
|
||||||
|
// this version doesn't actually change anything:
|
||||||
|
#[cfg(rpass3)]
|
||||||
|
pub type X = i32;
|
||||||
|
|
||||||
pub type Y = char;
|
pub type Y = char;
|
||||||
|
|
|
@ -9,19 +9,21 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// aux-build:a.rs
|
// aux-build:a.rs
|
||||||
// revisions:rpass1 rpass2
|
// revisions:rpass1 rpass2 rpass3
|
||||||
|
|
||||||
#![feature(rustc_attrs)]
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
extern crate a;
|
extern crate a;
|
||||||
|
|
||||||
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
#[rustc_dirty(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass3")]
|
||||||
pub fn use_X() -> u32 {
|
pub fn use_X() -> u32 {
|
||||||
let x: a::X = 22;
|
let x: a::X = 22;
|
||||||
x as u32
|
x as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass2")]
|
||||||
|
#[rustc_clean(label="TypeckItemBody", cfg="rpass3")]
|
||||||
pub fn use_Y() {
|
pub fn use_Y() {
|
||||||
let x: a::Y = 'c';
|
let x: a::Y = 'c';
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue