1
Fork 0

Rollup merge of #41734 - nikomatsakis:incr-comp-refactor-variance, r=pnkfelix

Refactor variance and remove last `[pub]` map

This PR refactors variance to work in a more red-green friendly way. Because red-green doesn't exist yet, it has to be a bit hacky. The basic idea is this:

- We compute a big map with the variance for all items in the crate; when you request variances for a particular item, we read it from the crate
- We now hard-code that traits are invariant (which they are, for deep reasons, not gonna' change)
- When building constraints, we compute the transitive closure of all things within the crate that depend on what using `TransitiveRelation`
    - this lets us gin up the correct dependencies when requesting variance of a single item

Ah damn, just remembered, one TODO:

- [x] Update the variance README -- ah, I guess the README updates I did are sufficient

r? @michaelwoerister
This commit is contained in:
Corey Farwell 2017-05-05 17:35:29 -04:00 committed by GitHub
commit 26e067b058
25 changed files with 469 additions and 366 deletions

View file

@ -81,6 +81,7 @@ pub enum DepNode<D: Clone + Debug> {
TransCrateItem(D), TransCrateItem(D),
TransInlinedItem(D), TransInlinedItem(D),
TransWriteMetadata, TransWriteMetadata,
CrateVariances,
// Nodes representing bits of computed IR in the tcx. Each shared // Nodes representing bits of computed IR in the tcx. Each shared
// table in the tcx (or elsewhere) maps to one of these // table in the tcx (or elsewhere) maps to one of these
@ -89,6 +90,8 @@ pub enum DepNode<D: Clone + Debug> {
// predicates for an item wind up in `ItemSignature`). // predicates for an item wind up in `ItemSignature`).
AssociatedItems(D), AssociatedItems(D),
ItemSignature(D), ItemSignature(D),
ItemVarianceConstraints(D),
ItemVariances(D),
IsForeignItem(D), IsForeignItem(D),
TypeParamPredicates((D, D)), TypeParamPredicates((D, D)),
SizedConstraint(D), SizedConstraint(D),
@ -180,6 +183,7 @@ impl<D: Clone + Debug> DepNode<D> {
TransCrateItem, TransCrateItem,
AssociatedItems, AssociatedItems,
ItemSignature, ItemSignature,
ItemVariances,
IsForeignItem, IsForeignItem,
AssociatedItemDefIds, AssociatedItemDefIds,
InherentImpls, InherentImpls,
@ -201,6 +205,7 @@ impl<D: Clone + Debug> DepNode<D> {
MirKrate => Some(MirKrate), MirKrate => Some(MirKrate),
TypeckBodiesKrate => Some(TypeckBodiesKrate), TypeckBodiesKrate => Some(TypeckBodiesKrate),
Coherence => Some(Coherence), Coherence => Some(Coherence),
CrateVariances => Some(CrateVariances),
Resolve => Some(Resolve), Resolve => Some(Resolve),
Variance => Some(Variance), Variance => Some(Variance),
PrivacyAccessLevels(k) => Some(PrivacyAccessLevels(k)), PrivacyAccessLevels(k) => Some(PrivacyAccessLevels(k)),
@ -232,6 +237,8 @@ impl<D: Clone + Debug> DepNode<D> {
TransInlinedItem(ref d) => op(d).map(TransInlinedItem), TransInlinedItem(ref d) => op(d).map(TransInlinedItem),
AssociatedItems(ref d) => op(d).map(AssociatedItems), AssociatedItems(ref d) => op(d).map(AssociatedItems),
ItemSignature(ref d) => op(d).map(ItemSignature), ItemSignature(ref d) => op(d).map(ItemSignature),
ItemVariances(ref d) => op(d).map(ItemVariances),
ItemVarianceConstraints(ref d) => op(d).map(ItemVarianceConstraints),
IsForeignItem(ref d) => op(d).map(IsForeignItem), IsForeignItem(ref d) => op(d).map(IsForeignItem),
TypeParamPredicates((ref item, ref param)) => { TypeParamPredicates((ref item, ref param)) => {
Some(TypeParamPredicates((try_opt!(op(item)), try_opt!(op(param))))) Some(TypeParamPredicates((try_opt!(op(item)), try_opt!(op(param)))))

View file

@ -18,7 +18,6 @@ mod raii;
mod safe; mod safe;
mod shadow; mod shadow;
mod thread; mod thread;
mod visit;
pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig}; pub use self::dep_tracking_map::{DepTrackingMap, DepTrackingMapConfig};
pub use self::dep_node::DepNode; pub use self::dep_node::DepNode;
@ -28,5 +27,4 @@ pub use self::graph::WorkProduct;
pub use self::query::DepGraphQuery; pub use self::query::DepGraphQuery;
pub use self::safe::AssertDepGraphSafe; pub use self::safe::AssertDepGraphSafe;
pub use self::safe::DepGraphSafe; pub use self::safe::DepGraphSafe;
pub use self::visit::visit_all_item_likes_in_krate;
pub use self::raii::DepTask; pub use self::raii::DepTask;

View file

@ -1,77 +0,0 @@
// 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 hir;
use hir::def_id::DefId;
use hir::itemlikevisit::ItemLikeVisitor;
use ty::TyCtxt;
use super::dep_node::DepNode;
/// Visit all the items in the krate in some order. When visiting a
/// particular item, first create a dep-node by calling `dep_node_fn`
/// and push that onto the dep-graph stack of tasks, and also create a
/// read edge from the corresponding AST node. This is used in
/// compiler passes to automatically record the item that they are
/// working on.
pub fn visit_all_item_likes_in_krate<'a, 'tcx, V, F>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mut dep_node_fn: F,
visitor: &mut V)
where F: FnMut(DefId) -> DepNode<DefId>, V: ItemLikeVisitor<'tcx>
{
struct TrackingVisitor<'visit, 'tcx: 'visit, F: 'visit, V: 'visit> {
tcx: TyCtxt<'visit, 'tcx, 'tcx>,
dep_node_fn: &'visit mut F,
visitor: &'visit mut V,
}
impl<'visit, 'tcx, F, V> ItemLikeVisitor<'tcx> for TrackingVisitor<'visit, 'tcx, F, V>
where F: FnMut(DefId) -> DepNode<DefId>, V: ItemLikeVisitor<'tcx>
{
fn visit_item(&mut self, i: &'tcx hir::Item) {
let item_def_id = self.tcx.hir.local_def_id(i.id);
let task_id = (self.dep_node_fn)(item_def_id);
let _task = self.tcx.dep_graph.in_task(task_id.clone());
debug!("Started task {:?}", task_id);
self.tcx.dep_graph.read(DepNode::Hir(item_def_id));
self.visitor.visit_item(i);
debug!("Ended task {:?}", task_id);
}
fn visit_trait_item(&mut self, i: &'tcx hir::TraitItem) {
let trait_item_def_id = self.tcx.hir.local_def_id(i.id);
let task_id = (self.dep_node_fn)(trait_item_def_id);
let _task = self.tcx.dep_graph.in_task(task_id.clone());
debug!("Started task {:?}", task_id);
self.tcx.dep_graph.read(DepNode::Hir(trait_item_def_id));
self.visitor.visit_trait_item(i);
debug!("Ended task {:?}", task_id);
}
fn visit_impl_item(&mut self, i: &'tcx hir::ImplItem) {
let impl_item_def_id = self.tcx.hir.local_def_id(i.id);
let task_id = (self.dep_node_fn)(impl_item_def_id);
let _task = self.tcx.dep_graph.in_task(task_id.clone());
debug!("Started task {:?}", task_id);
self.tcx.dep_graph.read(DepNode::Hir(impl_item_def_id));
self.visitor.visit_impl_item(i);
debug!("Ended task {:?}", task_id);
}
}
let krate = tcx.dep_graph.with_ignore(|| tcx.hir.krate());
let mut tracking_visitor = TrackingVisitor {
tcx: tcx,
dep_node_fn: &mut dep_node_fn,
visitor: visitor,
};
krate.visit_all_item_likes(&mut tracking_visitor)
}

View file

@ -88,7 +88,7 @@ pub enum NestedVisitorMap<'this, 'tcx: 'this> {
/// that are inside of an item-like. /// that are inside of an item-like.
/// ///
/// **This is the most common choice.** A very commmon pattern is /// **This is the most common choice.** A very commmon pattern is
/// to use `tcx.visit_all_item_likes_in_krate()` as an outer loop, /// to use `visit_all_item_likes()` as an outer loop,
/// and to have the visitor that visits the contents of each item /// and to have the visitor that visits the contents of each item
/// using this setting. /// using this setting.
OnlyBodies(&'this Map<'tcx>), OnlyBodies(&'this Map<'tcx>),

View file

@ -19,9 +19,8 @@ use super::intravisit::Visitor;
/// ///
/// 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR. /// 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR.
/// - Example: find all items with a `#[foo]` attribute on them. /// - Example: find all items with a `#[foo]` attribute on them.
/// - How: Implement `ItemLikeVisitor` and call `tcx.visit_all_item_likes_in_krate()`. /// - How: Implement `ItemLikeVisitor` and call `tcx.hir.krate().visit_all_item_likes()`.
/// - Pro: Efficient; just walks the lists of item-like things, not the nodes themselves. /// - Pro: Efficient; just walks the lists of item-like things, not the nodes themselves.
/// - Pro: Integrates well into dependency tracking.
/// - Con: Don't get information about nesting /// - Con: Don't get information about nesting
/// - Con: Don't have methods for specific bits of HIR, like "on /// - Con: Don't have methods for specific bits of HIR, like "on
/// every expr, do this". /// every expr, do this".
@ -30,7 +29,7 @@ use super::intravisit::Visitor;
/// within one another. /// within one another.
/// - Example: Examine each expression to look for its type and do some check or other. /// - Example: Examine each expression to look for its type and do some check or other.
/// - How: Implement `intravisit::Visitor` and use /// - How: Implement `intravisit::Visitor` and use
/// `tcx.visit_all_item_likes_in_krate(visitor.as_deep_visitor())`. Within /// `tcx.hir.krate().visit_all_item_likes(visitor.as_deep_visitor())`. Within
/// your `intravisit::Visitor` impl, implement methods like /// your `intravisit::Visitor` impl, implement methods like
/// `visit_expr()`; don't forget to invoke /// `visit_expr()`; don't forget to invoke
/// `intravisit::walk_visit_expr()` to keep walking the subparts. /// `intravisit::walk_visit_expr()` to keep walking the subparts.

View file

@ -470,9 +470,6 @@ pub struct GlobalCtxt<'tcx> {
pub lang_items: middle::lang_items::LanguageItems, pub lang_items: middle::lang_items::LanguageItems,
/// True if the variance has been computed yet; false otherwise.
pub variance_computed: Cell<bool>,
/// Set of used unsafe nodes (functions or blocks). Unsafe nodes not /// Set of used unsafe nodes (functions or blocks). Unsafe nodes not
/// present in this set can be warned about. /// present in this set can be warned about.
pub used_unsafe: RefCell<NodeSet>, pub used_unsafe: RefCell<NodeSet>,
@ -744,7 +741,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
dep_graph: dep_graph.clone(), dep_graph: dep_graph.clone(),
types: common_types, types: common_types,
named_region_map: named_region_map, named_region_map: named_region_map,
variance_computed: Cell::new(false),
trait_map: resolutions.trait_map, trait_map: resolutions.trait_map,
export_map: resolutions.export_map, export_map: resolutions.export_map,
fulfilled_predicates: RefCell::new(fulfilled_predicates), fulfilled_predicates: RefCell::new(fulfilled_predicates),

View file

@ -266,6 +266,12 @@ impl<'tcx> QueryDescription for queries::crate_inherent_impls_overlap_check<'tcx
} }
} }
impl<'tcx> QueryDescription for queries::crate_variances<'tcx> {
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
format!("computing the variances for items in this crate")
}
}
impl<'tcx> QueryDescription for queries::mir_shims<'tcx> { impl<'tcx> QueryDescription for queries::mir_shims<'tcx> {
fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String { fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String {
format!("generating MIR shim for `{}`", format!("generating MIR shim for `{}`",
@ -549,18 +555,6 @@ macro_rules! define_map_struct {
} }
}; };
// Detect things with the `pub` modifier
(tcx: $tcx:tt,
input: (([pub $($other_modifiers:tt)*] $attrs:tt $name:tt) $($input:tt)*),
output: $output:tt) => {
define_map_struct! {
tcx: $tcx,
ready: ([pub] $attrs $name),
input: ($($input)*),
output: $output
}
};
// No modifiers left? This is a private item. // No modifiers left? This is a private item.
(tcx: $tcx:tt, (tcx: $tcx:tt,
input: (([] $attrs:tt $name:tt) $($input:tt)*), input: (([] $attrs:tt $name:tt) $($input:tt)*),
@ -687,9 +681,13 @@ define_maps! { <'tcx>
/// True if this is a foreign item (i.e., linked via `extern { ... }`). /// True if this is a foreign item (i.e., linked via `extern { ... }`).
[] is_foreign_item: IsForeignItem(DefId) -> bool, [] is_foreign_item: IsForeignItem(DefId) -> bool,
/// Get a map with the variance of every item; use `item_variance`
/// instead.
[] crate_variances: crate_variances(CrateNum) -> Rc<ty::CrateVariancesMap>,
/// Maps from def-id of a type or region parameter to its /// Maps from def-id of a type or region parameter to its
/// (inferred) variance. /// (inferred) variance.
[pub] variances_of: ItemSignature(DefId) -> Rc<Vec<ty::Variance>>, [] variances_of: ItemVariances(DefId) -> Rc<Vec<ty::Variance>>,
/// Maps from an impl/trait def-id to a list of the def-ids of its items /// Maps from an impl/trait def-id to a list of the def-ids of its items
[] associated_item_def_ids: AssociatedItemDefIds(DefId) -> Rc<Vec<DefId>>, [] associated_item_def_ids: AssociatedItemDefIds(DefId) -> Rc<Vec<DefId>>,
@ -825,3 +823,7 @@ fn const_eval_dep_node((def_id, _): (DefId, &Substs)) -> DepNode<DefId> {
fn mir_keys(_: CrateNum) -> DepNode<DefId> { fn mir_keys(_: CrateNum) -> DepNode<DefId> {
DepNode::MirKeys DepNode::MirKeys
} }
fn crate_variances(_: CrateNum) -> DepNode<DefId> {
DepNode::CrateVariances
}

View file

@ -15,7 +15,7 @@ pub use self::IntVarValue::*;
pub use self::LvaluePreference::*; pub use self::LvaluePreference::*;
pub use self::fold::TypeFoldable; pub use self::fold::TypeFoldable;
use dep_graph::{self, DepNode}; use dep_graph::DepNode;
use hir::{map as hir_map, FreevarMap, TraitMap}; use hir::{map as hir_map, FreevarMap, TraitMap};
use hir::def::{Def, CtorKind, ExportMap}; use hir::def::{Def, CtorKind, ExportMap};
use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE}; use hir::def_id::{CrateNum, DefId, DefIndex, CRATE_DEF_INDEX, LOCAL_CRATE};
@ -55,9 +55,9 @@ use rustc_const_math::ConstInt;
use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter; use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter;
use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult, use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
HashStable}; HashStable};
use rustc_data_structures::transitive_relation::TransitiveRelation;
use hir; use hir;
use hir::itemlikevisit::ItemLikeVisitor;
pub use self::sty::{Binder, DebruijnIndex}; pub use self::sty::{Binder, DebruijnIndex};
pub use self::sty::{FnSig, PolyFnSig}; pub use self::sty::{FnSig, PolyFnSig};
@ -309,6 +309,27 @@ pub enum Variance {
Bivariant, // T<A> <: T<B> -- e.g., unused type parameter Bivariant, // T<A> <: T<B> -- e.g., unused type parameter
} }
/// The crate variances map is computed during typeck and contains the
/// variance of every item in the local crate. You should not use it
/// directly, because to do so will make your pass dependent on the
/// HIR of every item in the local crate. Instead, use
/// `tcx.variances_of()` to get the variance for a *particular*
/// item.
pub struct CrateVariancesMap {
/// This relation tracks the dependencies between the variance of
/// various items. In particular, if `a < b`, then the variance of
/// `a` depends on the sources of `b`.
pub dependencies: TransitiveRelation<DefId>,
/// For each item with generics, maps to a vector of the variance
/// of its generics. If an item has no generics, it will have no
/// entry.
pub variances: FxHashMap<DefId, Rc<Vec<ty::Variance>>>,
/// An empty vector, useful for cloning.
pub empty_variance: Rc<Vec<ty::Variance>>,
}
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)] #[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
pub struct MethodCallee<'tcx> { pub struct MethodCallee<'tcx> {
/// Impl method ID, for inherent methods, or trait method ID, otherwise. /// Impl method ID, for inherent methods, or trait method ID, otherwise.
@ -2543,14 +2564,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
self.mk_region(ty::ReScope(self.node_extent(id))) self.mk_region(ty::ReScope(self.node_extent(id)))
} }
pub fn visit_all_item_likes_in_krate<V,F>(self,
dep_node_fn: F,
visitor: &mut V)
where F: FnMut(DefId) -> DepNode<DefId>, V: ItemLikeVisitor<'gcx>
{
dep_graph::visit_all_item_likes_in_krate(self.global_tcx(), dep_node_fn, visitor);
}
/// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err` /// Looks up the span of `impl_did` if the impl is local; otherwise returns `Err`
/// with the name of the crate containing the impl. /// with the name of the crate containing the impl.
pub fn span_of_impl(self, impl_did: DefId) -> Result<Span, Symbol> { pub fn span_of_impl(self, impl_did: DefId) -> Result<Span, Symbol> {

View file

@ -124,14 +124,8 @@ fn relate_item_substs<'a, 'gcx, 'tcx, R>(relation: &mut R,
a_subst, a_subst,
b_subst); b_subst);
let variances; let opt_variances = relation.tcx().variances_of(item_def_id);
let opt_variances = if relation.tcx().variance_computed.get() { relate_substs(relation, Some(&opt_variances), a_subst, b_subst)
variances = relation.tcx().variances_of(item_def_id);
Some(&*variances)
} else {
None
};
relate_substs(relation, opt_variances, a_subst, b_subst)
} }
pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R, pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R,

View file

@ -9,21 +9,23 @@
// except according to those terms. // except according to those terms.
use bitvec::BitMatrix; use bitvec::BitMatrix;
use stable_hasher::{HashStable, StableHasher, StableHasherResult}; use fx::FxHashMap;
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
use stable_hasher::{HashStable, StableHasher, StableHasherResult};
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt::Debug; use std::fmt::Debug;
use std::hash::Hash;
use std::mem; use std::mem;
#[derive(Clone)] #[derive(Clone)]
pub struct TransitiveRelation<T: Debug + PartialEq> { pub struct TransitiveRelation<T: Clone + Debug + Eq + Hash + Clone> {
// List of elements. This is used to map from a T to a usize. We // List of elements. This is used to map from a T to a usize.
// expect domain to be small so just use a linear list versus a
// hashmap or something.
elements: Vec<T>, elements: Vec<T>,
// Maps each element to an index.
map: FxHashMap<T, Index>,
// List of base edges in the graph. Require to compute transitive // List of base edges in the graph. Require to compute transitive
// closure. // closure.
edges: Vec<Edge>, edges: Vec<Edge>,
@ -40,19 +42,20 @@ pub struct TransitiveRelation<T: Debug + PartialEq> {
closure: RefCell<Option<BitMatrix>>, closure: RefCell<Option<BitMatrix>>,
} }
#[derive(Clone, PartialEq, PartialOrd, RustcEncodable, RustcDecodable)] #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
struct Index(usize); struct Index(usize);
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
struct Edge { struct Edge {
source: Index, source: Index,
target: Index, target: Index,
} }
impl<T: Debug + PartialEq> TransitiveRelation<T> { impl<T: Clone + Debug + Eq + Hash + Clone> TransitiveRelation<T> {
pub fn new() -> TransitiveRelation<T> { pub fn new() -> TransitiveRelation<T> {
TransitiveRelation { TransitiveRelation {
elements: vec![], elements: vec![],
map: FxHashMap(),
edges: vec![], edges: vec![],
closure: RefCell::new(None), closure: RefCell::new(None),
} }
@ -63,21 +66,27 @@ impl<T: Debug + PartialEq> TransitiveRelation<T> {
} }
fn index(&self, a: &T) -> Option<Index> { fn index(&self, a: &T) -> Option<Index> {
self.elements.iter().position(|e| *e == *a).map(Index) self.map.get(a).cloned()
} }
fn add_index(&mut self, a: T) -> Index { fn add_index(&mut self, a: T) -> Index {
match self.index(&a) { let &mut TransitiveRelation {
Some(i) => i, ref mut elements,
None => { ref closure,
self.elements.push(a); ref mut map,
..
} = self;
// if we changed the dimensions, clear the cache map.entry(a.clone())
*self.closure.borrow_mut() = None; .or_insert_with(|| {
elements.push(a);
Index(self.elements.len() - 1) // if we changed the dimensions, clear the cache
} *closure.borrow_mut() = None;
}
Index(elements.len() - 1)
})
.clone()
} }
/// Applies the (partial) function to each edge and returns a new /// Applies the (partial) function to each edge and returns a new
@ -85,7 +94,7 @@ impl<T: Debug + PartialEq> TransitiveRelation<T> {
/// `None`. /// `None`.
pub fn maybe_map<F, U>(&self, mut f: F) -> Option<TransitiveRelation<U>> pub fn maybe_map<F, U>(&self, mut f: F) -> Option<TransitiveRelation<U>>
where F: FnMut(&T) -> Option<U>, where F: FnMut(&T) -> Option<U>,
U: Debug + PartialEq, U: Clone + Debug + Eq + Hash + Clone,
{ {
let mut result = TransitiveRelation::new(); let mut result = TransitiveRelation::new();
for edge in &self.edges { for edge in &self.edges {
@ -125,6 +134,20 @@ impl<T: Debug + PartialEq> TransitiveRelation<T> {
} }
} }
/// Returns a vector of all things less than `a`.
///
/// Really this probably ought to be `impl Iterator<Item=&T>`, but
/// I'm too lazy to make that work, and -- given the caching
/// strategy -- it'd be a touch tricky anyhow.
pub fn less_than(&self, a: &T) -> Vec<&T> {
match self.index(a) {
Some(a) => self.with_closure(|closure| {
closure.iter(a.0).map(|i| &self.elements[i]).collect()
}),
None => vec![],
}
}
/// Picks what I am referring to as the "postdominating" /// Picks what I am referring to as the "postdominating"
/// upper-bound for `a` and `b`. This is usually the least upper /// upper-bound for `a` and `b`. This is usually the least upper
/// bound, but in cases where there is no single least upper /// bound, but in cases where there is no single least upper
@ -335,7 +358,7 @@ fn pare_down(candidates: &mut Vec<usize>, closure: &BitMatrix) {
} }
impl<T> Encodable for TransitiveRelation<T> impl<T> Encodable for TransitiveRelation<T>
where T: Encodable + Debug + PartialEq where T: Clone + Encodable + Debug + Eq + Hash + Clone
{ {
fn encode<E: Encoder>(&self, s: &mut E) -> Result<(), E::Error> { fn encode<E: Encoder>(&self, s: &mut E) -> Result<(), E::Error> {
s.emit_struct("TransitiveRelation", 2, |s| { s.emit_struct("TransitiveRelation", 2, |s| {
@ -347,19 +370,23 @@ impl<T> Encodable for TransitiveRelation<T>
} }
impl<T> Decodable for TransitiveRelation<T> impl<T> Decodable for TransitiveRelation<T>
where T: Decodable + Debug + PartialEq where T: Clone + Decodable + Debug + Eq + Hash + Clone
{ {
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> { fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
d.read_struct("TransitiveRelation", 2, |d| { d.read_struct("TransitiveRelation", 2, |d| {
let elements = d.read_struct_field("elements", 0, |d| Decodable::decode(d))?; let elements: Vec<T> = d.read_struct_field("elements", 0, |d| Decodable::decode(d))?;
let edges = d.read_struct_field("edges", 1, |d| Decodable::decode(d))?; let edges = d.read_struct_field("edges", 1, |d| Decodable::decode(d))?;
Ok(TransitiveRelation { elements, edges, closure: RefCell::new(None) }) let map = elements.iter()
.enumerate()
.map(|(index, elem)| (elem.clone(), Index(index)))
.collect();
Ok(TransitiveRelation { elements, edges, map, closure: RefCell::new(None) })
}) })
} }
} }
impl<CTX, T> HashStable<CTX> for TransitiveRelation<T> impl<CTX, T> HashStable<CTX> for TransitiveRelation<T>
where T: HashStable<CTX> + PartialEq + Debug where T: HashStable<CTX> + Eq + Debug + Clone + Hash
{ {
fn hash_stable<W: StableHasherResult>(&self, fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut CTX, hcx: &mut CTX,
@ -369,6 +396,8 @@ impl<CTX, T> HashStable<CTX> for TransitiveRelation<T>
let TransitiveRelation { let TransitiveRelation {
ref elements, ref elements,
ref edges, ref edges,
// "map" is just a copy of elements vec
map: _,
// "closure" is just a copy of the data above // "closure" is just a copy of the data above
closure: _ closure: _
} = *self; } = *self;

View file

@ -51,7 +51,7 @@ use rustc::ty::TyCtxt;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::{Direction, INCOMING, OUTGOING, NodeIndex}; use rustc_data_structures::graph::{Direction, INCOMING, OUTGOING, NodeIndex};
use rustc::hir; use rustc::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc::ich::{ATTR_IF_THIS_CHANGED, ATTR_THEN_THIS_WOULD_NEED}; use rustc::ich::{ATTR_IF_THIS_CHANGED, ATTR_THEN_THIS_WOULD_NEED};
use graphviz::IntoCow; use graphviz::IntoCow;
use std::env; use std::env;
@ -80,7 +80,7 @@ pub fn assert_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
if_this_changed: vec![], if_this_changed: vec![],
then_this_would_need: vec![] }; then_this_would_need: vec![] };
visitor.process_attrs(ast::CRATE_NODE_ID, &tcx.hir.krate().attrs); visitor.process_attrs(ast::CRATE_NODE_ID, &tcx.hir.krate().attrs);
tcx.hir.krate().visit_all_item_likes(&mut visitor); tcx.hir.krate().visit_all_item_likes(&mut visitor.as_deep_visitor());
(visitor.if_this_changed, visitor.then_this_would_need) (visitor.if_this_changed, visitor.then_this_would_need)
}; };
@ -166,17 +166,29 @@ impl<'a, 'tcx> IfThisChanged<'a, 'tcx> {
} }
} }
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for IfThisChanged<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for IfThisChanged<'a, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::OnlyBodies(&self.tcx.hir)
}
fn visit_item(&mut self, item: &'tcx hir::Item) { fn visit_item(&mut self, item: &'tcx hir::Item) {
self.process_attrs(item.id, &item.attrs); self.process_attrs(item.id, &item.attrs);
intravisit::walk_item(self, item);
} }
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
self.process_attrs(trait_item.id, &trait_item.attrs); self.process_attrs(trait_item.id, &trait_item.attrs);
intravisit::walk_trait_item(self, trait_item);
} }
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
self.process_attrs(impl_item.id, &impl_item.attrs); self.process_attrs(impl_item.id, &impl_item.attrs);
intravisit::walk_impl_item(self, impl_item);
}
fn visit_struct_field(&mut self, s: &'tcx hir::StructField) {
self.process_attrs(s.id, &s.attrs);
intravisit::walk_struct_field(self, s);
} }
} }

View file

@ -240,8 +240,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
} }
impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> { impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
fn encode_item_variances(&mut self, def_id: DefId) -> LazySeq<ty::Variance> { fn encode_variances_of(&mut self, def_id: DefId) -> LazySeq<ty::Variance> {
debug!("EntryBuilder::encode_item_variances({:?})", def_id); debug!("EntryBuilder::encode_variances_of({:?})", def_id);
let tcx = self.tcx; let tcx = self.tcx;
self.lazy_seq_from_slice(&tcx.variances_of(def_id)) self.lazy_seq_from_slice(&tcx.variances_of(def_id))
} }
@ -824,7 +824,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
hir::ItemEnum(..) | hir::ItemEnum(..) |
hir::ItemStruct(..) | hir::ItemStruct(..) |
hir::ItemUnion(..) | hir::ItemUnion(..) |
hir::ItemTrait(..) => self.encode_item_variances(def_id), hir::ItemTrait(..) => self.encode_variances_of(def_id),
_ => LazySeq::empty(), _ => LazySeq::empty(),
}, },
generics: match item.node { generics: match item.node {

View file

@ -293,6 +293,7 @@ pub fn provide(providers: &mut Providers) {
collect::provide(providers); collect::provide(providers);
coherence::provide(providers); coherence::provide(providers);
check::provide(providers); check::provide(providers);
variance::provide(providers);
} }
pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
@ -307,9 +308,6 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
})?; })?;
time(time_passes, "variance inference", ||
variance::infer_variance(tcx));
tcx.sess.track_errors(|| { tcx.sess.track_errors(|| {
time(time_passes, "impl wf inference", || time(time_passes, "impl wf inference", ||
impl_wf_check::impl_wf_check(tcx)); impl_wf_check::impl_wf_check(tcx));
@ -320,6 +318,11 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
coherence::check_coherence(tcx)); coherence::check_coherence(tcx));
})?; })?;
tcx.sess.track_errors(|| {
time(time_passes, "variance testing", ||
variance::test::test_variance(tcx));
})?;
time(time_passes, "wf checking", || check::check_wf_new(tcx))?; time(time_passes, "wf checking", || check::check_wf_new(tcx))?;
time(time_passes, "item-types checking", || check::check_item_types(tcx))?; time(time_passes, "item-types checking", || check::check_item_types(tcx))?;

View file

@ -97,51 +97,29 @@ types involved before considering variance.
#### Dependency graph management #### Dependency graph management
Because variance works in two phases, if we are not careful, we wind Because variance is a whole-crate inference, its dependency graph
up with a muddled mess of a dep-graph. Basically, when gathering up can become quite muddled if we are not careful. To resolve this, we refactor
the constraints, things are fairly well-structured, but then we do a into two queries:
fixed-point iteration and write the results back where they
belong. You can't give this fixed-point iteration a single task
because it reads from (and writes to) the variance of all types in the
crate. In principle, we *could* switch the "current task" in a very
fine-grained way while propagating constraints in the fixed-point
iteration and everything would be automatically tracked, but that
would add some overhead and isn't really necessary anyway.
Instead what we do is to add edges into the dependency graph as we - `crate_variances` computes the variance for all items in the current crate.
construct the constraint set: so, if computing the constraints for - `variances_of` accesses the variance for an individual reading; it
node `X` requires loading the inference variables from node `Y`, then works by requesting `crate_variances` and extracting the relevant data.
we can add an edge `Y -> X`, since the variance we ultimately infer
for `Y` will affect the variance we ultimately infer for `X`. If you limit yourself to reading `variances_of`, your code will only
depend then on the inference inferred for that particular item.
At this point, we've basically mirrored the inference graph in the Eventually, the goal is to rely on the red-green dependency management
dependency graph. This means we can just completely ignore the algorithm. At the moment, however, we rely instead on a hack, where
fixed-point iteration, since it is just shuffling values along this `variances_of` ignores the dependencies of accessing
graph. In other words, if we added the fine-grained switching of tasks `crate_variances` and instead computes the *correct* dependencies
I described earlier, all it would show is that we repeatedly read the itself. To this end, when we build up the constraints in the system,
values described by the constraints, but those edges were already we also built up a transitive `dependencies` relation as part of the
added when building the constraints in the first place. crate map. A `(X, Y)` pair is added to the map each time we have a
constraint that the variance of some inferred for the item `X` depends
Here is how this is implemented (at least as of the time of this on the variance of some element of `Y`. This is to some extent a
writing). The associated `DepNode` for the variance map is (at least mirroring of the inference graph in the dependency graph. This means
presently) `Signature(DefId)`. This means that, in `constraints.rs`, we can just completely ignore the fixed-point iteration, since it is
when we visit an item to load up its constraints, we set just shuffling values along this graph.
`Signature(DefId)` as the current task (the "memoization" pattern
described in the `dep-graph` README). Then whenever we find an
embedded type or trait, we add a synthetic read of `Signature(DefId)`,
which covers the variances we will compute for all of its
parameters. This read is synthetic (i.e., we call
`variance_map.read()`) because, in fact, the final variance is not yet
computed -- the read *will* occur (repeatedly) during the fixed-point
iteration phase.
In fact, we don't really *need* this synthetic read. That's because we
do wind up looking up the `TypeScheme` or `TraitDef` for all
references types/traits, and those reads add an edge from
`Signature(DefId)` (that is, they share the same dep node as
variance). However, I've kept the synthetic reads in place anyway,
just for future-proofing (in case we change the dep-nodes in the
future), and because it makes the intention a bit clearer I think.
### Addendum: Variance on traits ### Addendum: Variance on traits

View file

@ -15,6 +15,7 @@
use hir::def_id::DefId; use hir::def_id::DefId;
use middle::resolve_lifetime as rl; use middle::resolve_lifetime as rl;
use rustc::dep_graph::{AssertDepGraphSafe, DepNode};
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
use rustc::hir::map as hir_map; use rustc::hir::map as hir_map;
@ -22,12 +23,12 @@ use syntax::ast;
use rustc::hir; use rustc::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc_data_structures::transitive_relation::TransitiveRelation;
use super::terms::*; use super::terms::*;
use super::terms::VarianceTerm::*; use super::terms::VarianceTerm::*;
use super::xform::*; use super::xform::*;
use dep_graph::DepNode::ItemSignature as VarianceDepNode;
pub struct ConstraintContext<'a, 'tcx: 'a> { pub struct ConstraintContext<'a, 'tcx: 'a> {
pub terms_cx: TermsContext<'a, 'tcx>, pub terms_cx: TermsContext<'a, 'tcx>,
@ -38,6 +39,11 @@ pub struct ConstraintContext<'a, 'tcx: 'a> {
bivariant: VarianceTermPtr<'a>, bivariant: VarianceTermPtr<'a>,
pub constraints: Vec<Constraint<'a>>, pub constraints: Vec<Constraint<'a>>,
/// This relation tracks the dependencies between the variance of
/// various items. In particular, if `a < b`, then the variance of
/// `a` depends on the sources of `b`.
pub dependencies: TransitiveRelation<DefId>,
} }
/// Declares that the variable `decl_id` appears in a location with /// Declares that the variable `decl_id` appears in a location with
@ -48,6 +54,20 @@ pub struct Constraint<'a> {
pub variance: &'a VarianceTerm<'a>, pub variance: &'a VarianceTerm<'a>,
} }
/// To build constriants, we visit one item (type, trait) at a time
/// and look at its contents. So e.g. if we have
///
/// struct Foo<T> {
/// b: Bar<T>
/// }
///
/// then while we are visiting `Bar<T>`, the `CurrentItem` would have
/// the def-id and generics of `Foo`.
pub struct CurrentItem<'a> {
def_id: DefId,
generics: &'a ty::Generics,
}
pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>) pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>)
-> ConstraintContext<'a, 'tcx> { -> ConstraintContext<'a, 'tcx> {
let tcx = terms_cx.tcx; let tcx = terms_cx.tcx;
@ -62,10 +82,10 @@ pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>)
invariant: invariant, invariant: invariant,
bivariant: bivariant, bivariant: bivariant,
constraints: Vec::new(), constraints: Vec::new(),
dependencies: TransitiveRelation::new(),
}; };
// See README.md for a discussion on dep-graph management. tcx.hir.krate().visit_all_item_likes(&mut constraint_cx);
tcx.visit_all_item_likes_in_krate(VarianceDepNode, &mut constraint_cx);
constraint_cx constraint_cx
} }
@ -73,50 +93,32 @@ pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>)
impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> { impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
fn visit_item(&mut self, item: &hir::Item) { fn visit_item(&mut self, item: &hir::Item) {
let tcx = self.terms_cx.tcx; let tcx = self.terms_cx.tcx;
let did = tcx.hir.local_def_id(item.id); let def_id = tcx.hir.local_def_id(item.id);
debug!("visit_item item={}", tcx.hir.node_to_string(item.id));
// Encapsulate constructing the constraints into a task we can
// reference later. This can go away once the red-green
// algorithm is in place.
//
// See README.md for a detailed discussion
// on dep-graph management.
match item.node { match item.node {
hir::ItemEnum(..) | hir::ItemEnum(..) |
hir::ItemStruct(..) | hir::ItemStruct(..) |
hir::ItemUnion(..) => { hir::ItemUnion(..) => {
let generics = tcx.generics_of(did); tcx.dep_graph.with_task(DepNode::ItemVarianceConstraints(def_id),
AssertDepGraphSafe(self),
// Not entirely obvious: constraints on structs/enums do not def_id,
// affect the variance of their type parameters. See discussion visit_item_task);
// in comment at top of module.
//
// self.add_constraints_from_generics(generics);
for field in tcx.adt_def(did).all_fields() {
self.add_constraints_from_ty(generics,
tcx.type_of(field.did),
self.covariant);
}
} }
hir::ItemTrait(..) => { _ => {
let generics = tcx.generics_of(did); // Nothing to do here, skip the task.
let trait_ref = ty::TraitRef {
def_id: did,
substs: Substs::identity_for_item(tcx, did)
};
self.add_constraints_from_trait_ref(generics,
trait_ref,
self.invariant);
} }
}
hir::ItemExternCrate(_) | fn visit_item_task<'a, 'tcx>(ccx: AssertDepGraphSafe<&mut ConstraintContext<'a, 'tcx>>,
hir::ItemUse(..) | def_id: DefId)
hir::ItemStatic(..) | {
hir::ItemConst(..) | ccx.0.build_constraints_for_item(def_id);
hir::ItemFn(..) |
hir::ItemMod(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) |
hir::ItemImpl(..) |
hir::ItemDefaultImpl(..) => {}
} }
} }
@ -140,16 +142,64 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
self.terms_cx.tcx self.terms_cx.tcx
} }
fn inferred_index(&self, param_id: ast::NodeId) -> InferredIndex { fn build_constraints_for_item(&mut self, def_id: DefId) {
match self.terms_cx.inferred_map.get(&param_id) { let tcx = self.tcx();
Some(&index) => index, let id = self.tcx().hir.as_local_node_id(def_id).unwrap();
None => { let item = tcx.hir.expect_item(id);
bug!("no inferred index entry for {}", debug!("visit_item item={}", tcx.hir.node_to_string(item.id));
self.tcx().hir.node_to_string(param_id));
match item.node {
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) => {
let generics = tcx.generics_of(def_id);
let current_item = &CurrentItem { def_id, generics };
// Not entirely obvious: constraints on structs/enums do not
// affect the variance of their type parameters. See discussion
// in comment at top of module.
//
// self.add_constraints_from_generics(generics);
for field in tcx.adt_def(def_id).all_fields() {
self.add_constraints_from_ty(current_item,
tcx.type_of(field.did),
self.covariant);
}
}
hir::ItemTrait(..) |
hir::ItemExternCrate(_) |
hir::ItemUse(..) |
hir::ItemStatic(..) |
hir::ItemConst(..) |
hir::ItemFn(..) |
hir::ItemMod(..) |
hir::ItemForeignMod(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) |
hir::ItemImpl(..) |
hir::ItemDefaultImpl(..) => {
span_bug!(item.span, "`build_constraints_for_item` invoked for non-type-def");
} }
} }
} }
/// Load the generics for another item, adding a corresponding
/// relation into the dependencies to indicate that the variance
/// for `current` relies on `def_id`.
fn read_generics(&mut self, current: &CurrentItem, def_id: DefId) -> &'tcx ty::Generics {
let generics = self.tcx().generics_of(def_id);
if self.tcx().dep_graph.is_fully_enabled() {
self.dependencies.add(current.def_id, def_id);
}
generics
}
fn opt_inferred_index(&self, param_id: ast::NodeId) -> Option<&InferredIndex> {
self.terms_cx.inferred_map.get(&param_id)
}
fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId { fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId {
let tcx = self.terms_cx.tcx; let tcx = self.terms_cx.tcx;
assert!(is_lifetime(&tcx.hir, param_id)); assert!(is_lifetime(&tcx.hir, param_id));
@ -228,8 +278,27 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
// Parameter on an item defined within current crate: // Parameter on an item defined within current crate:
// variance not yet inferred, so return a symbolic // variance not yet inferred, so return a symbolic
// variance. // variance.
let InferredIndex(index) = self.inferred_index(param_node_id); if let Some(&InferredIndex(index)) = self.opt_inferred_index(param_node_id) {
self.terms_cx.inferred_infos[index].term self.terms_cx.inferred_infos[index].term
} else {
// If there is no inferred entry for a type parameter,
// it must be declared on a (locally defiend) trait -- they don't
// get inferreds because they are always invariant.
if cfg!(debug_assertions) {
let item_node_id = self.tcx().hir.as_local_node_id(item_def_id).unwrap();
let item = self.tcx().hir.expect_item(item_node_id);
let success = match item.node {
hir::ItemTrait(..) => true,
_ => false,
};
if !success {
bug!("parameter {:?} has no inferred, but declared on non-trait: {:?}",
item_def_id,
item);
}
}
self.invariant
}
} else { } else {
// Parameter on an item defined within another crate: // Parameter on an item defined within another crate:
// variance already inferred, just look it up. // variance already inferred, just look it up.
@ -279,7 +348,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
} }
fn add_constraints_from_trait_ref(&mut self, fn add_constraints_from_trait_ref(&mut self,
generics: &ty::Generics, current: &CurrentItem,
trait_ref: ty::TraitRef<'tcx>, trait_ref: ty::TraitRef<'tcx>,
variance: VarianceTermPtr<'a>) { variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_trait_ref: trait_ref={:?} variance={:?}", debug!("add_constraints_from_trait_ref: trait_ref={:?} variance={:?}",
@ -288,12 +357,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
let trait_generics = self.tcx().generics_of(trait_ref.def_id); let trait_generics = self.tcx().generics_of(trait_ref.def_id);
// This edge is actually implied by the call to self.add_constraints_from_substs(current,
// `trait_def`, but I'm trying to be future-proof. See
// README.md for a discussion on dep-graph management.
self.tcx().dep_graph.read(VarianceDepNode(trait_ref.def_id));
self.add_constraints_from_substs(generics,
trait_ref.def_id, trait_ref.def_id,
&trait_generics.types, &trait_generics.types,
&trait_generics.regions, &trait_generics.regions,
@ -305,7 +369,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
/// in a context with the generics defined in `generics` and /// in a context with the generics defined in `generics` and
/// ambient variance `variance` /// ambient variance `variance`
fn add_constraints_from_ty(&mut self, fn add_constraints_from_ty(&mut self,
generics: &ty::Generics, current: &CurrentItem,
ty: Ty<'tcx>, ty: Ty<'tcx>,
variance: VarianceTermPtr<'a>) { variance: VarianceTermPtr<'a>) {
debug!("add_constraints_from_ty(ty={:?}, variance={:?})", debug!("add_constraints_from_ty(ty={:?}, variance={:?})",
@ -325,34 +389,29 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
ty::TyRef(region, ref mt) => { ty::TyRef(region, ref mt) => {
let contra = self.contravariant(variance); let contra = self.contravariant(variance);
self.add_constraints_from_region(generics, region, contra); self.add_constraints_from_region(current, region, contra);
self.add_constraints_from_mt(generics, mt, variance); self.add_constraints_from_mt(current, mt, variance);
} }
ty::TyArray(typ, _) | ty::TyArray(typ, _) |
ty::TySlice(typ) => { ty::TySlice(typ) => {
self.add_constraints_from_ty(generics, typ, variance); self.add_constraints_from_ty(current, typ, variance);
} }
ty::TyRawPtr(ref mt) => { ty::TyRawPtr(ref mt) => {
self.add_constraints_from_mt(generics, mt, variance); self.add_constraints_from_mt(current, mt, variance);
} }
ty::TyTuple(subtys, _) => { ty::TyTuple(subtys, _) => {
for &subty in subtys { for &subty in subtys {
self.add_constraints_from_ty(generics, subty, variance); self.add_constraints_from_ty(current, subty, variance);
} }
} }
ty::TyAdt(def, substs) => { ty::TyAdt(def, substs) => {
let adt_generics = self.tcx().generics_of(def.did); let adt_generics = self.read_generics(current, def.did);
// This edge is actually implied by the call to self.add_constraints_from_substs(current,
// `trait_def`, but I'm trying to be future-proof. See
// README.md for a discussion on dep-graph management.
self.tcx().dep_graph.read(VarianceDepNode(def.did));
self.add_constraints_from_substs(generics,
def.did, def.did,
&adt_generics.types, &adt_generics.types,
&adt_generics.regions, &adt_generics.regions,
@ -364,12 +423,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
let trait_ref = &data.trait_ref; let trait_ref = &data.trait_ref;
let trait_generics = self.tcx().generics_of(trait_ref.def_id); let trait_generics = self.tcx().generics_of(trait_ref.def_id);
// This edge is actually implied by the call to self.add_constraints_from_substs(current,
// `trait_def`, but I'm trying to be future-proof. See
// README.md for a discussion on dep-graph management.
self.tcx().dep_graph.read(VarianceDepNode(trait_ref.def_id));
self.add_constraints_from_substs(generics,
trait_ref.def_id, trait_ref.def_id,
&trait_generics.types, &trait_generics.types,
&trait_generics.regions, &trait_generics.regions,
@ -380,25 +434,25 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
ty::TyDynamic(ref data, r) => { ty::TyDynamic(ref data, r) => {
// The type `Foo<T+'a>` is contravariant w/r/t `'a`: // The type `Foo<T+'a>` is contravariant w/r/t `'a`:
let contra = self.contravariant(variance); let contra = self.contravariant(variance);
self.add_constraints_from_region(generics, r, contra); self.add_constraints_from_region(current, r, contra);
if let Some(p) = data.principal() { if let Some(p) = data.principal() {
let poly_trait_ref = p.with_self_ty(self.tcx(), self.tcx().types.err); let poly_trait_ref = p.with_self_ty(self.tcx(), self.tcx().types.err);
self.add_constraints_from_trait_ref(generics, poly_trait_ref.0, variance); self.add_constraints_from_trait_ref(current, poly_trait_ref.0, variance);
} }
for projection in data.projection_bounds() { for projection in data.projection_bounds() {
self.add_constraints_from_ty(generics, projection.0.ty, self.invariant); self.add_constraints_from_ty(current, projection.0.ty, self.invariant);
} }
} }
ty::TyParam(ref data) => { ty::TyParam(ref data) => {
assert_eq!(generics.parent, None); assert_eq!(current.generics.parent, None);
let mut i = data.idx as usize; let mut i = data.idx as usize;
if !generics.has_self || i > 0 { if !current.generics.has_self || i > 0 {
i -= generics.regions.len(); i -= current.generics.regions.len();
} }
let def_id = generics.types[i].def_id; let def_id = current.generics.types[i].def_id;
let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap(); let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
match self.terms_cx.inferred_map.get(&node_id) { match self.terms_cx.inferred_map.get(&node_id) {
Some(&index) => { Some(&index) => {
@ -414,7 +468,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
ty::TyFnDef(.., sig) | ty::TyFnDef(.., sig) |
ty::TyFnPtr(sig) => { ty::TyFnPtr(sig) => {
self.add_constraints_from_sig(generics, sig, variance); self.add_constraints_from_sig(current, sig, variance);
} }
ty::TyError => { ty::TyError => {
@ -433,7 +487,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
/// Adds constraints appropriate for a nominal type (enum, struct, /// Adds constraints appropriate for a nominal type (enum, struct,
/// object, etc) appearing in a context with ambient variance `variance` /// object, etc) appearing in a context with ambient variance `variance`
fn add_constraints_from_substs(&mut self, fn add_constraints_from_substs(&mut self,
generics: &ty::Generics, current: &CurrentItem,
def_id: DefId, def_id: DefId,
type_param_defs: &[ty::TypeParameterDef], type_param_defs: &[ty::TypeParameterDef],
region_param_defs: &[ty::RegionParameterDef], region_param_defs: &[ty::RegionParameterDef],
@ -451,44 +505,44 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
debug!("add_constraints_from_substs: variance_decl={:?} variance_i={:?}", debug!("add_constraints_from_substs: variance_decl={:?} variance_i={:?}",
variance_decl, variance_decl,
variance_i); variance_i);
self.add_constraints_from_ty(generics, substs_ty, variance_i); self.add_constraints_from_ty(current, substs_ty, variance_i);
} }
for p in region_param_defs { for p in region_param_defs {
let variance_decl = self.declared_variance(p.def_id, def_id, p.index as usize); let variance_decl = self.declared_variance(p.def_id, def_id, p.index as usize);
let variance_i = self.xform(variance, variance_decl); let variance_i = self.xform(variance, variance_decl);
let substs_r = substs.region_for_def(p); let substs_r = substs.region_for_def(p);
self.add_constraints_from_region(generics, substs_r, variance_i); self.add_constraints_from_region(current, substs_r, variance_i);
} }
} }
/// Adds constraints appropriate for a function with signature /// Adds constraints appropriate for a function with signature
/// `sig` appearing in a context with ambient variance `variance` /// `sig` appearing in a context with ambient variance `variance`
fn add_constraints_from_sig(&mut self, fn add_constraints_from_sig(&mut self,
generics: &ty::Generics, current: &CurrentItem,
sig: ty::PolyFnSig<'tcx>, sig: ty::PolyFnSig<'tcx>,
variance: VarianceTermPtr<'a>) { variance: VarianceTermPtr<'a>) {
let contra = self.contravariant(variance); let contra = self.contravariant(variance);
for &input in sig.0.inputs() { for &input in sig.0.inputs() {
self.add_constraints_from_ty(generics, input, contra); self.add_constraints_from_ty(current, input, contra);
} }
self.add_constraints_from_ty(generics, sig.0.output(), variance); self.add_constraints_from_ty(current, sig.0.output(), variance);
} }
/// Adds constraints appropriate for a region appearing in a /// Adds constraints appropriate for a region appearing in a
/// context with ambient variance `variance` /// context with ambient variance `variance`
fn add_constraints_from_region(&mut self, fn add_constraints_from_region(&mut self,
generics: &ty::Generics, current: &CurrentItem,
region: ty::Region<'tcx>, region: ty::Region<'tcx>,
variance: VarianceTermPtr<'a>) { variance: VarianceTermPtr<'a>) {
match *region { match *region {
ty::ReEarlyBound(ref data) => { ty::ReEarlyBound(ref data) => {
assert_eq!(generics.parent, None); assert_eq!(current.generics.parent, None);
let i = data.index as usize - generics.has_self as usize; let i = data.index as usize - current.generics.has_self as usize;
let def_id = generics.regions[i].def_id; let def_id = current.generics.regions[i].def_id;
let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap(); let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
if self.is_to_be_inferred(node_id) { if self.is_to_be_inferred(node_id) {
let index = self.inferred_index(node_id); let &index = self.opt_inferred_index(node_id).unwrap();
self.add_constraint(index, variance); self.add_constraint(index, variance);
} }
} }
@ -518,17 +572,17 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
/// Adds constraints appropriate for a mutability-type pair /// Adds constraints appropriate for a mutability-type pair
/// appearing in a context with ambient variance `variance` /// appearing in a context with ambient variance `variance`
fn add_constraints_from_mt(&mut self, fn add_constraints_from_mt(&mut self,
generics: &ty::Generics, current: &CurrentItem,
mt: &ty::TypeAndMut<'tcx>, mt: &ty::TypeAndMut<'tcx>,
variance: VarianceTermPtr<'a>) { variance: VarianceTermPtr<'a>) {
match mt.mutbl { match mt.mutbl {
hir::MutMutable => { hir::MutMutable => {
let invar = self.invariant(variance); let invar = self.invariant(variance);
self.add_constraints_from_ty(generics, mt.ty, invar); self.add_constraints_from_ty(current, mt.ty, invar);
} }
hir::MutImmutable => { hir::MutImmutable => {
self.add_constraints_from_ty(generics, mt.ty, variance); self.add_constraints_from_ty(current, mt.ty, variance);
} }
} }
} }

View file

@ -12,7 +12,12 @@
//! parameters. See README.md for details. //! parameters. See README.md for details.
use arena; use arena;
use rustc::ty::TyCtxt; use rustc::dep_graph::DepNode;
use rustc::hir;
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc::ty::{self, CrateVariancesMap, TyCtxt};
use rustc::ty::maps::Providers;
use std::rc::Rc;
/// Defines the `TermsContext` basically houses an arena where we can /// Defines the `TermsContext` basically houses an arena where we can
/// allocate terms. /// allocate terms.
@ -24,13 +29,67 @@ mod constraints;
/// Code to solve constraints and write out the results. /// Code to solve constraints and write out the results.
mod solve; mod solve;
/// Code to write unit tests of variance.
pub mod test;
/// Code for transforming variances. /// Code for transforming variances.
mod xform; mod xform;
pub fn infer_variance<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { pub fn provide(providers: &mut Providers) {
*providers = Providers {
variances_of,
crate_variances,
..*providers
};
}
fn crate_variances<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum)
-> Rc<CrateVariancesMap> {
assert_eq!(crate_num, LOCAL_CRATE);
let mut arena = arena::TypedArena::new(); let mut arena = arena::TypedArena::new();
let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena); let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena);
let constraints_cx = constraints::add_constraints_from_crate(terms_cx); let constraints_cx = constraints::add_constraints_from_crate(terms_cx);
solve::solve_constraints(constraints_cx); Rc::new(solve::solve_constraints(constraints_cx))
tcx.variance_computed.set(true);
} }
fn variances_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_def_id: DefId)
-> Rc<Vec<ty::Variance>> {
let item_id = tcx.hir.as_local_node_id(item_def_id).expect("expected local def-id");
let item = tcx.hir.expect_item(item_id);
match item.node {
hir::ItemTrait(..) => {
// Traits are always invariant.
let generics = tcx.generics_of(item_def_id);
assert!(generics.parent.is_none());
Rc::new(vec![ty::Variance::Invariant; generics.count()])
}
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) => {
// Everything else must be inferred.
// Lacking red/green, we read the variances for all items here
// but ignore the dependencies, then re-synthesize the ones we need.
let crate_map = tcx.dep_graph.with_ignore(|| tcx.crate_variances(LOCAL_CRATE));
tcx.dep_graph.read(DepNode::ItemVarianceConstraints(item_def_id));
for &dep_def_id in crate_map.dependencies.less_than(&item_def_id) {
if dep_def_id.is_local() {
tcx.dep_graph.read(DepNode::ItemVarianceConstraints(dep_def_id));
} else {
tcx.dep_graph.read(DepNode::ItemVariances(dep_def_id));
}
}
crate_map.variances.get(&item_def_id)
.unwrap_or(&crate_map.empty_variance)
.clone()
}
_ => {
// Variance not relevant.
span_bug!(item.span, "asked to compute variance for wrong kind of item")
}
}
}

View file

@ -15,7 +15,9 @@
//! optimal solution to the constraints. The final variance for each //! optimal solution to the constraints. The final variance for each
//! inferred is then written into the `variance_map` in the tcx. //! inferred is then written into the `variance_map` in the tcx.
use rustc::hir::def_id::DefId;
use rustc::ty; use rustc::ty;
use rustc_data_structures::fx::FxHashMap;
use std::rc::Rc; use std::rc::Rc;
use super::constraints::*; use super::constraints::*;
@ -31,8 +33,8 @@ struct SolveContext<'a, 'tcx: 'a> {
solutions: Vec<ty::Variance>, solutions: Vec<ty::Variance>,
} }
pub fn solve_constraints(constraints_cx: ConstraintContext) { pub fn solve_constraints(constraints_cx: ConstraintContext) -> ty::CrateVariancesMap {
let ConstraintContext { terms_cx, constraints, .. } = constraints_cx; let ConstraintContext { terms_cx, dependencies, constraints, .. } = constraints_cx;
let solutions = terms_cx.inferred_infos let solutions = terms_cx.inferred_infos
.iter() .iter()
@ -45,7 +47,10 @@ pub fn solve_constraints(constraints_cx: ConstraintContext) {
solutions: solutions, solutions: solutions,
}; };
solutions_cx.solve(); solutions_cx.solve();
solutions_cx.write(); let variances = solutions_cx.create_map();
let empty_variance = Rc::new(Vec::new());
ty::CrateVariancesMap { dependencies, variances, empty_variance }
} }
impl<'a, 'tcx> SolveContext<'a, 'tcx> { impl<'a, 'tcx> SolveContext<'a, 'tcx> {
@ -83,7 +88,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
} }
} }
fn write(&self) { fn create_map(&self) -> FxHashMap<DefId, Rc<Vec<ty::Variance>>> {
// Collect all the variances for a particular item and stick // Collect all the variances for a particular item and stick
// them into the variance map. We rely on the fact that we // them into the variance map. We rely on the fact that we
// generate all the inferreds for a particular item // generate all the inferreds for a particular item
@ -95,11 +100,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
let tcx = self.terms_cx.tcx; let tcx = self.terms_cx.tcx;
// Ignore the writes here because the relevant edges were let mut map = FxHashMap();
// already accounted for in `constraints.rs`. See the section
// on dependency graph management in README.md for more
// information.
let _ignore = tcx.dep_graph.in_ignore();
let solutions = &self.solutions; let solutions = &self.solutions;
let inferred_infos = &self.terms_cx.inferred_infos; let inferred_infos = &self.terms_cx.inferred_infos;
@ -127,19 +128,10 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
let item_def_id = tcx.hir.local_def_id(item_id); let item_def_id = tcx.hir.local_def_id(item_id);
// For unit testing: check for a special "rustc_variance" map.insert(item_def_id, Rc::new(item_variances));
// attribute and report an error with various results if found.
if tcx.has_attr(item_def_id, "rustc_variance") {
span_err!(tcx.sess,
tcx.hir.span(item_id),
E0208,
"{:?}",
item_variances);
}
tcx.maps.variances_of.borrow_mut()
.insert(item_def_id, Rc::new(item_variances));
} }
map
} }
fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance { fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance {

View file

@ -32,8 +32,6 @@ use self::VarianceTerm::*;
pub type VarianceTermPtr<'a> = &'a VarianceTerm<'a>; pub type VarianceTermPtr<'a> = &'a VarianceTerm<'a>;
use dep_graph::DepNode::ItemSignature as VarianceDepNode;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct InferredIndex(pub usize); pub struct InferredIndex(pub usize);
@ -109,7 +107,7 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>
}; };
// See README.md for a discussion on dep-graph management. // See README.md for a discussion on dep-graph management.
tcx.visit_all_item_likes_in_krate(|def_id| VarianceDepNode(def_id), &mut terms_cx); tcx.hir.krate().visit_all_item_likes(&mut terms_cx);
terms_cx terms_cx
} }
@ -139,7 +137,6 @@ fn lang_items(tcx: TyCtxt) -> Vec<(ast::NodeId, Vec<ty::Variance>)> {
impl<'a, 'tcx> TermsContext<'a, 'tcx> { impl<'a, 'tcx> TermsContext<'a, 'tcx> {
fn add_inferreds_for_item(&mut self, fn add_inferreds_for_item(&mut self,
item_id: ast::NodeId, item_id: ast::NodeId,
has_self: bool,
generics: &hir::Generics) { generics: &hir::Generics) {
//! Add "inferreds" for the generic parameters declared on this //! Add "inferreds" for the generic parameters declared on this
//! item. This has a lot of annoying parameters because we are //! item. This has a lot of annoying parameters because we are
@ -149,38 +146,17 @@ impl<'a, 'tcx> TermsContext<'a, 'tcx> {
//! //!
// NB: In the code below for writing the results back into the // NB: In the code below for writing the results back into the
// tcx, we rely on the fact that all inferreds for a particular // `CrateVariancesMap`, we rely on the fact that all inferreds
// item are assigned continuous indices. // for a particular item are assigned continuous indices.
let inferreds_on_entry = self.num_inferred(); for (p, i) in generics.lifetimes.iter().zip(0..) {
if has_self {
self.add_inferred(item_id, 0, item_id);
}
for (i, p) in generics.lifetimes.iter().enumerate() {
let id = p.lifetime.id; let id = p.lifetime.id;
let i = has_self as usize + i;
self.add_inferred(item_id, i, id); self.add_inferred(item_id, i, id);
} }
for (i, p) in generics.ty_params.iter().enumerate() { for (p, i) in generics.ty_params.iter().zip(generics.lifetimes.len()..) {
let i = has_self as usize + generics.lifetimes.len() + i;
self.add_inferred(item_id, i, p.id); self.add_inferred(item_id, i, p.id);
} }
// If this item has no type or lifetime parameters,
// then there are no variances to infer, so just
// insert an empty entry into the variance map.
// Arguably we could just leave the map empty in this
// case but it seems cleaner to be able to distinguish
// "invalid item id" from "item id with no
// parameters".
if self.num_inferred() == inferreds_on_entry {
let item_def_id = self.tcx.hir.local_def_id(item_id);
self.tcx.maps.variances_of.borrow_mut()
.insert(item_def_id, self.empty_variances.clone());
}
} }
fn add_inferred(&mut self, item_id: ast::NodeId, index: usize, param_id: ast::NodeId) { fn add_inferred(&mut self, item_id: ast::NodeId, index: usize, param_id: ast::NodeId) {
@ -232,15 +208,10 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for TermsContext<'a, 'tcx> {
hir::ItemEnum(_, ref generics) | hir::ItemEnum(_, ref generics) |
hir::ItemStruct(_, ref generics) | hir::ItemStruct(_, ref generics) |
hir::ItemUnion(_, ref generics) => { hir::ItemUnion(_, ref generics) => {
self.add_inferreds_for_item(item.id, false, generics); self.add_inferreds_for_item(item.id, generics);
}
hir::ItemTrait(_, ref generics, ..) => {
// Note: all inputs for traits are ultimately
// constrained to be invariant. See `visit_item` in
// the impl for `ConstraintContext` in `constraints.rs`.
self.add_inferreds_for_item(item.id, true, generics);
} }
hir::ItemTrait(..) |
hir::ItemExternCrate(_) | hir::ItemExternCrate(_) |
hir::ItemUse(..) | hir::ItemUse(..) |
hir::ItemDefaultImpl(..) | hir::ItemDefaultImpl(..) |

View file

@ -0,0 +1,41 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use rustc::ty::TyCtxt;
pub fn test_variance<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
tcx.hir.krate().visit_all_item_likes(&mut VarianceTest { tcx });
}
struct VarianceTest<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>
}
impl<'a, 'tcx> ItemLikeVisitor<'tcx> for VarianceTest<'a, 'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item) {
let item_def_id = self.tcx.hir.local_def_id(item.id);
// For unit testing: check for a special "rustc_variance"
// attribute and report an error with various results if found.
if self.tcx.has_attr(item_def_id, "rustc_variance") {
let variances_of = self.tcx.variances_of(item_def_id);
span_err!(self.tcx.sess,
item.span,
E0208,
"{:?}",
variances_of);
}
}
fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) { }
fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) { }
}

View file

@ -58,13 +58,15 @@ mod signatures {
fn method(&self, x: u32) { } fn method(&self, x: u32) { }
} }
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
struct WillChanges { struct WillChanges {
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
x: WillChange, x: WillChange,
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
y: WillChange y: WillChange
} }
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK // The fields change, not the type itself.
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path
fn indirect(x: WillChanges) { } fn indirect(x: WillChanges) { }
} }

View file

@ -23,15 +23,21 @@ fn main() { }
#[rustc_if_this_changed] #[rustc_if_this_changed]
type TypeAlias = u32; type TypeAlias = u32;
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK // The type alias directly affects the type of the field,
// not the enclosing struct:
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path
struct Struct { struct Struct {
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
x: TypeAlias, x: TypeAlias,
y: u32 y: u32
} }
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK #[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path
enum Enum { enum Enum {
Variant1(TypeAlias), Variant1 {
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
t: TypeAlias
},
Variant2(i32) Variant2(i32)
} }

View file

@ -0,0 +1,32 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <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 that changing what a `type` points to does not go unnoticed
// by the variance analysis.
// compile-flags: -Z query-dep-graph
#![feature(rustc_attrs)]
#![allow(dead_code)]
#![allow(unused_variables)]
fn main() { }
struct Foo<T> {
f: T
}
#[rustc_if_this_changed]
type TypeAlias<T> = Foo<T>;
#[rustc_then_this_would_need(ItemVariances)] //~ ERROR OK
struct Use<T> {
x: TypeAlias<T>
}

View file

@ -60,7 +60,6 @@ struct Test6<'a, 'b:'a> { //~ ERROR [-, o]
#[rustc_variance] #[rustc_variance]
struct Test7<'a> { //~ ERROR [*] struct Test7<'a> { //~ ERROR [*]
//~^ ERROR parameter `'a` is never used
x: isize x: isize
} }

View file

@ -16,7 +16,6 @@
#[rustc_variance] #[rustc_variance]
enum Base<'a, 'b, 'c:'b, 'd> { //~ ERROR [+, -, o, *] enum Base<'a, 'b, 'c:'b, 'd> { //~ ERROR [+, -, o, *]
//~^ ERROR parameter `'d` is never used
Test8A(extern "Rust" fn(&'a isize)), Test8A(extern "Rust" fn(&'a isize)),
Test8B(&'b [isize]), Test8B(&'b [isize]),
Test8C(&'b mut &'c str), Test8C(&'b mut &'c str),
@ -24,19 +23,16 @@ enum Base<'a, 'b, 'c:'b, 'd> { //~ ERROR [+, -, o, *]
#[rustc_variance] #[rustc_variance]
struct Derived1<'w, 'x:'y, 'y, 'z> { //~ ERROR [*, o, -, +] struct Derived1<'w, 'x:'y, 'y, 'z> { //~ ERROR [*, o, -, +]
//~^ ERROR parameter `'w` is never used
f: Base<'z, 'y, 'x, 'w> f: Base<'z, 'y, 'x, 'w>
} }
#[rustc_variance] // Combine - and + to yield o #[rustc_variance] // Combine - and + to yield o
struct Derived2<'a, 'b:'a, 'c> { //~ ERROR [o, o, *] struct Derived2<'a, 'b:'a, 'c> { //~ ERROR [o, o, *]
//~^ ERROR parameter `'c` is never used
f: Base<'a, 'a, 'b, 'c> f: Base<'a, 'a, 'b, 'c>
} }
#[rustc_variance] // Combine + and o to yield o (just pay attention to 'a here) #[rustc_variance] // Combine + and o to yield o (just pay attention to 'a here)
struct Derived3<'a:'b, 'b, 'c> { //~ ERROR [o, -, *] struct Derived3<'a:'b, 'b, 'c> { //~ ERROR [o, -, *]
//~^ ERROR parameter `'c` is never used
f: Base<'a, 'b, 'a, 'c> f: Base<'a, 'b, 'a, 'c>
} }

View file

@ -30,8 +30,7 @@ struct TestStruct<U,T:Setter<U>> { //~ ERROR [+, +]
} }
#[rustc_variance] #[rustc_variance]
enum TestEnum<U,T:Setter<U>> {//~ ERROR [*, +] enum TestEnum<U,T:Setter<U>> { //~ ERROR [*, +]
//~^ ERROR parameter `U` is never used
Foo(T) Foo(T)
} }
@ -51,13 +50,11 @@ trait TestTrait3<U> { //~ ERROR [o, o]
#[rustc_variance] #[rustc_variance]
struct TestContraStruct<U,T:Setter<U>> { //~ ERROR [*, +] struct TestContraStruct<U,T:Setter<U>> { //~ ERROR [*, +]
//~^ ERROR parameter `U` is never used
t: T t: T
} }
#[rustc_variance] #[rustc_variance]
struct TestBox<U,T:Getter<U>+Setter<U>> { //~ ERROR [*, +] struct TestBox<U,T:Getter<U>+Setter<U>> { //~ ERROR [*, +]
//~^ ERROR parameter `U` is never used
t: T t: T
} }