TransCrateItem(D),
TransInlinedItem(D),
TransWriteMetadata,
+ CrateVariances,
// Nodes representing bits of computed IR in the tcx. Each shared
// table in the tcx (or elsewhere) maps to one of these
// predicates for an item wind up in `ItemSignature`).
AssociatedItems(D),
ItemSignature(D),
+ ItemVarianceConstraints(D),
+ ItemVariances(D),
IsForeignItem(D),
TypeParamPredicates((D, D)),
SizedConstraint(D),
MirKrate => Some(MirKrate),
TypeckBodiesKrate => Some(TypeckBodiesKrate),
Coherence => Some(Coherence),
+ CrateVariances => Some(CrateVariances),
Resolve => Some(Resolve),
Variance => Some(Variance),
PrivacyAccessLevels(k) => Some(PrivacyAccessLevels(k)),
TransInlinedItem(ref d) => op(d).map(TransInlinedItem),
AssociatedItems(ref d) => op(d).map(AssociatedItems),
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),
TypeParamPredicates((ref item, ref param)) => {
Some(TypeParamPredicates((try_opt!(op(item)), try_opt!(op(param)))))
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
/// present in this set can be warned about.
pub used_unsafe: RefCell<NodeSet>,
dep_graph: dep_graph.clone(),
types: common_types,
named_region_map: named_region_map,
- variance_computed: Cell::new(false),
trait_map: resolutions.trait_map,
export_map: resolutions.export_map,
fulfilled_predicates: RefCell::new(fulfilled_predicates),
}
}
+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> {
fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String {
format!("generating MIR shim for `{}`",
/// True if this is a foreign item (i.e., linked via `extern { ... }`).
[] 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
/// (inferred) variance.
- [pub] variances_of: ItemSignature(DefId) -> Rc<Vec<ty::Variance>>,
+ [pub] 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
[] associated_item_def_ids: AssociatedItemDefIds(DefId) -> Rc<Vec<DefId>>,
fn mir_keys(_: CrateNum) -> DepNode<DefId> {
DepNode::MirKeys
}
+
+fn crate_variances(_: CrateNum) -> DepNode<DefId> {
+ DepNode::CrateVariances
+}
use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter;
use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
HashStable};
+use rustc_data_structures::transitive_relation::TransitiveRelation;
use hir;
use hir::itemlikevisit::ItemLikeVisitor;
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.item_variances()` 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)]
pub struct MethodCallee<'tcx> {
/// Impl method ID, for inherent methods, or trait method ID, otherwise.
a_subst,
b_subst);
- let variances;
- let opt_variances = if relation.tcx().variance_computed.get() {
- variances = relation.tcx().variances_of(item_def_id);
- Some(&*variances)
- } else {
- None
- };
- relate_substs(relation, opt_variances, a_subst, b_subst)
+ let opt_variances = relation.tcx().variances_of(item_def_id);
+ relate_substs(relation, Some(&opt_variances), a_subst, b_subst)
}
pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R,
}
}
+ /// 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"
/// upper-bound for `a` and `b`. This is usually the least upper
/// bound, but in cases where there is no single least upper
collect::provide(providers);
coherence::provide(providers);
check::provide(providers);
+ variance::provide(providers);
}
pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
})?;
- time(time_passes, "variance inference", ||
- variance::infer_variance(tcx));
-
tcx.sess.track_errors(|| {
time(time_passes, "impl wf inference", ||
impl_wf_check::impl_wf_check(tcx));
#### Dependency graph management
-Because variance works in two phases, if we are not careful, we wind
-up with a muddled mess of a dep-graph. Basically, when gathering up
-the constraints, things are fairly well-structured, but then we do a
-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
-construct the constraint set: so, if computing the constraints for
-node `X` requires loading the inference variables from node `Y`, then
-we can add an edge `Y -> X`, since the variance we ultimately infer
-for `Y` will affect the variance we ultimately infer for `X`.
-
-At this point, we've basically mirrored the inference graph in the
-dependency graph. This means we can just completely ignore the
-fixed-point iteration, since it is just shuffling values along this
-graph. In other words, if we added the fine-grained switching of tasks
-I described earlier, all it would show is that we repeatedly read the
-values described by the constraints, but those edges were already
-added when building the constraints in the first place.
-
-Here is how this is implemented (at least as of the time of this
-writing). The associated `DepNode` for the variance map is (at least
-presently) `Signature(DefId)`. This means that, in `constraints.rs`,
-when we visit an item to load up its constraints, we set
-`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.
+Because variance is a whole-crate inference, its dependency graph
+can become quite muddled if we are not careful. To resolve this, we refactor
+into two queries:
+
+- `crate_variances` computes the variance for all items in the current crate.
+- `item_variances` accesses the variance for an individual reading; it
+ works by requesting `crate_variances` and extracting the relevant data.
+
+If you limit yourself to reading `item_variances`, your code will only
+depend then on the inference inferred for that particular item.
+
+Eventually, the goal is to rely on the red-green dependency management
+algorithm. At the moment, however, we rely instead on a hack, where
+`item_variances` ignores the dependencies of accessing
+`crate_variances` and instead computes the *correct* dependencies
+itself. To this end, when we build up the constraints in the system,
+we also built up a transitive `dependencies` relation as part of the
+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
+on the variance of some element of `Y`. This is to some extent a
+mirroring of the inference graph in the dependency graph. This means
+we can just completely ignore the fixed-point iteration, since it is
+just shuffling values along this graph.
### Addendum: Variance on traits
use hir::def_id::DefId;
use middle::resolve_lifetime as rl;
+use rustc::dep_graph::{AssertDepGraphSafe, DepNode};
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::hir::map as hir_map;
use rustc::hir;
use rustc::hir::itemlikevisit::ItemLikeVisitor;
+use rustc_data_structures::transitive_relation::TransitiveRelation;
+
use super::terms::*;
use super::terms::VarianceTerm::*;
use super::xform::*;
-use dep_graph::DepNode::ItemSignature as VarianceDepNode;
-
pub struct ConstraintContext<'a, 'tcx: 'a> {
pub terms_cx: TermsContext<'a, 'tcx>,
bivariant: VarianceTermPtr<'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
///
/// then while we are visiting `Bar<T>`, the `CurrentItem` would have
/// the def-id and generics of `Foo`.
-#[allow(dead_code)] // TODO -- `def_id` field not used yet
pub struct CurrentItem<'a> {
def_id: DefId,
generics: &'a ty::Generics,
invariant: invariant,
bivariant: bivariant,
constraints: Vec::new(),
+ dependencies: TransitiveRelation::new(),
};
- // See README.md for a discussion on dep-graph management.
- tcx.visit_all_item_likes_in_krate(VarianceDepNode, &mut constraint_cx);
+ tcx.hir.krate().visit_all_item_likes(&mut constraint_cx);
constraint_cx
}
let tcx = self.terms_cx.tcx;
let def_id = tcx.hir.local_def_id(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 {
+ hir::ItemEnum(..) |
+ hir::ItemStruct(..) |
+ hir::ItemUnion(..) => {
+ tcx.dep_graph.with_task(DepNode::ItemVarianceConstraints(def_id),
+ AssertDepGraphSafe(self),
+ def_id,
+ visit_item_task);
+ }
+ _ => {
+ // Nothing to do here, skip the task.
+ }
+ }
+
+ fn visit_item_task<'a, 'tcx>(ccx: AssertDepGraphSafe<&mut ConstraintContext<'a, 'tcx>>,
+ def_id: DefId)
+ {
+ ccx.0.build_constraints_for_item(def_id);
+ }
+ }
+
+ fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
+ }
+
+ fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
+ }
+}
+
+/// Is `param_id` a lifetime according to `map`?
+fn is_lifetime(map: &hir_map::Map, param_id: ast::NodeId) -> bool {
+ match map.find(param_id) {
+ Some(hir_map::NodeLifetime(..)) => true,
+ _ => false,
+ }
+}
+
+impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
+ self.terms_cx.tcx
+ }
+
+ fn build_constraints_for_item(&mut self, def_id: DefId) {
+ let tcx = self.tcx();
+ let id = self.tcx().hir.as_local_node_id(def_id).unwrap();
+ let item = tcx.hir.expect_item(id);
debug!("visit_item item={}", tcx.hir.node_to_string(item.id));
match item.node {
hir::ItemEnum(..) |
hir::ItemStruct(..) |
hir::ItemUnion(..) => {
- let generics = tcx.generics_of(did);
+ let generics = tcx.generics_of(def_id);
let current_item = &CurrentItem { def_id, generics };
// Not entirely obvious: constraints on structs/enums do not
//
// self.add_constraints_from_generics(generics);
- for field in tcx.adt_def(did).all_fields() {
+ for field in tcx.adt_def(def_id).all_fields() {
self.add_constraints_from_ty(current_item,
- tcx.item_type(field.did),
+ tcx.type_of(field.did),
self.covariant);
}
}
- hir::ItemTrait(..) => {
- let generics = tcx.generics_of(did);
- let current_item = &CurrentItem { def_id, generics };
- let trait_ref = ty::TraitRef {
- def_id: def_id,
- substs: Substs::identity_for_item(tcx, def_id)
- };
- self.add_constraints_from_trait_ref(current_item,
- trait_ref,
- self.invariant);
- }
+ hir::ItemTrait(..) |
hir::ItemExternCrate(_) |
hir::ItemUse(..) |
hir::ItemStatic(..) |
hir::ItemGlobalAsm(..) |
hir::ItemTy(..) |
hir::ItemImpl(..) |
- hir::ItemDefaultImpl(..) => {}
+ hir::ItemDefaultImpl(..) => {
+ span_bug!(item.span, "`build_constraints_for_item` invoked for non-type-def");
+ }
}
}
- fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
- }
-
- fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
- }
-}
-
-/// Is `param_id` a lifetime according to `map`?
-fn is_lifetime(map: &hir_map::Map, param_id: ast::NodeId) -> bool {
- match map.find(param_id) {
- Some(hir_map::NodeLifetime(..)) => true,
- _ => false,
- }
-}
-
-impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
- fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
- self.terms_cx.tcx
+ /// 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 inferred_index(&self, param_id: ast::NodeId) -> InferredIndex {
- match self.terms_cx.inferred_map.get(¶m_id) {
- Some(&index) => index,
- None => {
- bug!("no inferred index entry for {}",
- self.tcx().hir.node_to_string(param_id));
- }
- }
+ fn opt_inferred_index(&self, param_id: ast::NodeId) -> Option<&InferredIndex> {
+ self.terms_cx.inferred_map.get(¶m_id)
}
fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId {
// Parameter on an item defined within current crate:
// variance not yet inferred, so return a symbolic
// variance.
- let InferredIndex(index) = self.inferred_index(param_node_id);
- self.terms_cx.inferred_infos[index].term
+ if let Some(&InferredIndex(index)) = self.opt_inferred_index(param_node_id) {
+ 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 {
// Parameter on an item defined within another crate:
// variance already inferred, just look it up.
let trait_generics = self.tcx().generics_of(trait_ref.def_id);
- // This edge is actually implied by the call to
- // `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(current,
trait_ref.def_id,
&trait_generics.types,
}
ty::TyAdt(def, substs) => {
- let adt_generics = self.tcx().generics_of(def.did);
-
- // This edge is actually implied by the call to
- // `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));
+ let adt_generics = self.read_generics(current, def.did);
self.add_constraints_from_substs(current,
def.did,
let trait_ref = &data.trait_ref;
let trait_generics = self.tcx().generics_of(trait_ref.def_id);
- // This edge is actually implied by the call to
- // `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(current,
trait_ref.def_id,
&trait_generics.types,
let def_id = current.generics.regions[i].def_id;
let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
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);
}
}
//! parameters. See README.md for details.
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
/// allocate terms.
/// Code for transforming variances.
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 terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena);
let constraints_cx = constraints::add_constraints_from_crate(terms_cx);
- solve::solve_constraints(constraints_cx);
- tcx.variance_computed.set(true);
+ Rc::new(solve::solve_constraints(constraints_cx))
+}
+
+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")
+ }
+ }
}
//! optimal solution to the constraints. The final variance for each
//! inferred is then written into the `variance_map` in the tcx.
+use rustc::hir::def_id::DefId;
use rustc::ty;
+use rustc_data_structures::fx::FxHashMap;
use std::rc::Rc;
use super::constraints::*;
solutions: Vec<ty::Variance>,
}
-pub fn solve_constraints(constraints_cx: ConstraintContext) {
- let ConstraintContext { terms_cx, constraints, .. } = constraints_cx;
+pub fn solve_constraints(constraints_cx: ConstraintContext) -> ty::CrateVariancesMap {
+ let ConstraintContext { terms_cx, dependencies, constraints, .. } = constraints_cx;
let solutions = terms_cx.inferred_infos
.iter()
solutions: solutions,
};
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> {
}
}
- fn write(&self) {
+ fn create_map(&self) -> FxHashMap<DefId, Rc<Vec<ty::Variance>>> {
// Collect all the variances for a particular item and stick
// them into the variance map. We rely on the fact that we
// generate all the inferreds for a particular item
let tcx = self.terms_cx.tcx;
- // Ignore the writes here because the relevant edges were
- // 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 mut map = FxHashMap();
let solutions = &self.solutions;
let inferred_infos = &self.terms_cx.inferred_infos;
item_variances);
}
- tcx.maps.variances_of.borrow_mut()
- .insert(item_def_id, Rc::new(item_variances));
+ map.insert(item_def_id, Rc::new(item_variances));
}
+
+ map
}
fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance {
impl<'a, 'tcx> TermsContext<'a, 'tcx> {
fn add_inferreds_for_item(&mut self,
item_id: ast::NodeId,
- has_self: bool,
generics: &hir::Generics) {
//! Add "inferreds" for the generic parameters declared on this
//! item. This has a lot of annoying parameters because we are
//!
// NB: In the code below for writing the results back into the
- // tcx, we rely on the fact that all inferreds for a particular
- // item are assigned continuous indices.
+ // `CrateVariancesMap`, we rely on the fact that all inferreds
+ // for a particular item are assigned continuous indices.
- let inferreds_on_entry = self.num_inferred();
-
- if has_self {
- self.add_inferred(item_id, 0, item_id);
- }
-
- for (i, p) in generics.lifetimes.iter().enumerate() {
+ for (p, i) in generics.lifetimes.iter().zip(0..) {
let id = p.lifetime.id;
- let i = has_self as usize + i;
self.add_inferred(item_id, i, id);
}
- for (i, p) in generics.ty_params.iter().enumerate() {
- let i = has_self as usize + generics.lifetimes.len() + i;
+ for (p, i) in generics.ty_params.iter().zip(generics.lifetimes.len()..) {
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) {
hir::ItemEnum(_, ref generics) |
hir::ItemStruct(_, ref generics) |
hir::ItemUnion(_, ref generics) => {
- self.add_inferreds_for_item(item.id, false, 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);
+ self.add_inferreds_for_item(item.id, generics);
}
+ hir::ItemTrait(..) |
hir::ItemExternCrate(_) |
hir::ItemUse(..) |
hir::ItemDefaultImpl(..) |
--- /dev/null
+// 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>
+}