Skip to content

Commit 4824a19

Browse files
committedMay 3, 2017
factor variances into a proper query
There are now two queries: crate and item. The crate one computes the variance of all items in the crate; it is sort of an implementation detail, and not meant to be used. The item one reads from the crate one, synthesizing correct deps in lieu of the red-green algorithm. At the same time, remove the `variance_computed` flag, which was a horrible hack used to force invariance early on (e.g. when type-checking constants). This is only needed because of trait applications, and traits are always invariant anyway. Therefore, we now change to take advantage of the query system: - When asked to compute variances for a trait, just return a vector saying 'all invariant'. - Remove the corresponding "inferreds" from traits, and tweak the constraint generation code to understand that traits are always inferred.
1 parent 55412a2 commit 4824a19

File tree

13 files changed

+295
-174
lines changed

13 files changed

+295
-174
lines changed
 

‎src/librustc/dep_graph/dep_node.rs

+6
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ pub enum DepNode<D: Clone + Debug> {
8181
TransCrateItem(D),
8282
TransInlinedItem(D),
8383
TransWriteMetadata,
84+
CrateVariances,
8485

8586
// Nodes representing bits of computed IR in the tcx. Each shared
8687
// table in the tcx (or elsewhere) maps to one of these
@@ -89,6 +90,8 @@ pub enum DepNode<D: Clone + Debug> {
8990
// predicates for an item wind up in `ItemSignature`).
9091
AssociatedItems(D),
9192
ItemSignature(D),
93+
ItemVarianceConstraints(D),
94+
ItemVariances(D),
9295
IsForeignItem(D),
9396
TypeParamPredicates((D, D)),
9497
SizedConstraint(D),
@@ -199,6 +202,7 @@ impl<D: Clone + Debug> DepNode<D> {
199202
MirKrate => Some(MirKrate),
200203
TypeckBodiesKrate => Some(TypeckBodiesKrate),
201204
Coherence => Some(Coherence),
205+
CrateVariances => Some(CrateVariances),
202206
Resolve => Some(Resolve),
203207
Variance => Some(Variance),
204208
PrivacyAccessLevels(k) => Some(PrivacyAccessLevels(k)),
@@ -230,6 +234,8 @@ impl<D: Clone + Debug> DepNode<D> {
230234
TransInlinedItem(ref d) => op(d).map(TransInlinedItem),
231235
AssociatedItems(ref d) => op(d).map(AssociatedItems),
232236
ItemSignature(ref d) => op(d).map(ItemSignature),
237+
ItemVariances(ref d) => op(d).map(ItemVariances),
238+
ItemVarianceConstraints(ref d) => op(d).map(ItemVarianceConstraints),
233239
IsForeignItem(ref d) => op(d).map(IsForeignItem),
234240
TypeParamPredicates((ref item, ref param)) => {
235241
Some(TypeParamPredicates((try_opt!(op(item)), try_opt!(op(param)))))

‎src/librustc/ty/context.rs

-4
Original file line numberDiff line numberDiff line change
@@ -468,9 +468,6 @@ pub struct GlobalCtxt<'tcx> {
468468

469469
pub lang_items: middle::lang_items::LanguageItems,
470470

471-
/// True if the variance has been computed yet; false otherwise.
472-
pub variance_computed: Cell<bool>,
473-
474471
/// Set of used unsafe nodes (functions or blocks). Unsafe nodes not
475472
/// present in this set can be warned about.
476473
pub used_unsafe: RefCell<NodeSet>,
@@ -753,7 +750,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
753750
dep_graph: dep_graph.clone(),
754751
types: common_types,
755752
named_region_map: named_region_map,
756-
variance_computed: Cell::new(false),
757753
trait_map: resolutions.trait_map,
758754
export_map: resolutions.export_map,
759755
fulfilled_predicates: RefCell::new(fulfilled_predicates),

‎src/librustc/ty/maps.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,12 @@ impl<'tcx> QueryDescription for queries::crate_inherent_impls_overlap_check<'tcx
265265
}
266266
}
267267

268+
impl<'tcx> QueryDescription for queries::crate_variances<'tcx> {
269+
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
270+
format!("computing the variances for items in this crate")
271+
}
272+
}
273+
268274
impl<'tcx> QueryDescription for queries::mir_shims<'tcx> {
269275
fn describe(tcx: TyCtxt, def: ty::InstanceDef<'tcx>) -> String {
270276
format!("generating MIR shim for `{}`",
@@ -673,9 +679,13 @@ define_maps! { <'tcx>
673679
/// True if this is a foreign item (i.e., linked via `extern { ... }`).
674680
[] is_foreign_item: IsForeignItem(DefId) -> bool,
675681

682+
/// Get a map with the variance of every item; use `item_variance`
683+
/// instead.
684+
[] crate_variances: crate_variances(CrateNum) -> Rc<ty::CrateVariancesMap>,
685+
676686
/// Maps from def-id of a type or region parameter to its
677687
/// (inferred) variance.
678-
[pub] variances_of: ItemSignature(DefId) -> Rc<Vec<ty::Variance>>,
688+
[pub] variances_of: ItemVariances(DefId) -> Rc<Vec<ty::Variance>>,
679689

680690
/// Maps from an impl/trait def-id to a list of the def-ids of its items
681691
[] associated_item_def_ids: AssociatedItemDefIds(DefId) -> Rc<Vec<DefId>>,
@@ -810,3 +820,7 @@ fn const_eval_dep_node((def_id, _): (DefId, &Substs)) -> DepNode<DefId> {
810820
fn mir_keys(_: CrateNum) -> DepNode<DefId> {
811821
DepNode::MirKeys
812822
}
823+
824+
fn crate_variances(_: CrateNum) -> DepNode<DefId> {
825+
DepNode::CrateVariances
826+
}

‎src/librustc/ty/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ use rustc_const_math::ConstInt;
5555
use rustc_data_structures::accumulate_vec::IntoIter as AccIntoIter;
5656
use rustc_data_structures::stable_hasher::{StableHasher, StableHasherResult,
5757
HashStable};
58+
use rustc_data_structures::transitive_relation::TransitiveRelation;
5859

5960
use hir;
6061
use hir::itemlikevisit::ItemLikeVisitor;
@@ -309,6 +310,27 @@ pub enum Variance {
309310
Bivariant, // T<A> <: T<B> -- e.g., unused type parameter
310311
}
311312

313+
/// The crate variances map is computed during typeck and contains the
314+
/// variance of every item in the local crate. You should not use it
315+
/// directly, because to do so will make your pass dependent on the
316+
/// HIR of every item in the local crate. Instead, use
317+
/// `tcx.item_variances()` to get the variance for a *particular*
318+
/// item.
319+
pub struct CrateVariancesMap {
320+
/// This relation tracks the dependencies between the variance of
321+
/// various items. In particular, if `a < b`, then the variance of
322+
/// `a` depends on the sources of `b`.
323+
pub dependencies: TransitiveRelation<DefId>,
324+
325+
/// For each item with generics, maps to a vector of the variance
326+
/// of its generics. If an item has no generics, it will have no
327+
/// entry.
328+
pub variances: FxHashMap<DefId, Rc<Vec<ty::Variance>>>,
329+
330+
/// An empty vector, useful for cloning.
331+
pub empty_variance: Rc<Vec<ty::Variance>>,
332+
}
333+
312334
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
313335
pub struct MethodCallee<'tcx> {
314336
/// Impl method ID, for inherent methods, or trait method ID, otherwise.

‎src/librustc/ty/relate.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,8 @@ fn relate_item_substs<'a, 'gcx, 'tcx, R>(relation: &mut R,
124124
a_subst,
125125
b_subst);
126126

127-
let variances;
128-
let opt_variances = if relation.tcx().variance_computed.get() {
129-
variances = relation.tcx().variances_of(item_def_id);
130-
Some(&*variances)
131-
} else {
132-
None
133-
};
134-
relate_substs(relation, opt_variances, a_subst, b_subst)
127+
let opt_variances = relation.tcx().variances_of(item_def_id);
128+
relate_substs(relation, Some(&opt_variances), a_subst, b_subst)
135129
}
136130

137131
pub fn relate_substs<'a, 'gcx, 'tcx, R>(relation: &mut R,

‎src/librustc_data_structures/transitive_relation.rs

+14
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,20 @@ impl<T: Clone + Debug + Eq + Hash + Clone> TransitiveRelation<T> {
134134
}
135135
}
136136

137+
/// Returns a vector of all things less than `a`.
138+
///
139+
/// Really this probably ought to be `impl Iterator<Item=&T>`, but
140+
/// I'm too lazy to make that work, and -- given the caching
141+
/// strategy -- it'd be a touch tricky anyhow.
142+
pub fn less_than(&self, a: &T) -> Vec<&T> {
143+
match self.index(a) {
144+
Some(a) => self.with_closure(|closure| {
145+
closure.iter(a.0).map(|i| &self.elements[i]).collect()
146+
}),
147+
None => vec![],
148+
}
149+
}
150+
137151
/// Picks what I am referring to as the "postdominating"
138152
/// upper-bound for `a` and `b`. This is usually the least upper
139153
/// bound, but in cases where there is no single least upper

‎src/librustc_typeck/lib.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ pub fn provide(providers: &mut Providers) {
293293
collect::provide(providers);
294294
coherence::provide(providers);
295295
check::provide(providers);
296+
variance::provide(providers);
296297
}
297298

298299
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>)
307308

308309
})?;
309310

310-
time(time_passes, "variance inference", ||
311-
variance::infer_variance(tcx));
312-
313311
tcx.sess.track_errors(|| {
314312
time(time_passes, "impl wf inference", ||
315313
impl_wf_check::impl_wf_check(tcx));

‎src/librustc_typeck/variance/README.md

+23-45
Original file line numberDiff line numberDiff line change
@@ -97,51 +97,29 @@ types involved before considering variance.
9797

9898
#### Dependency graph management
9999

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

146124
### Addendum: Variance on traits
147125

‎src/librustc_typeck/variance/constraints.rs

+102-65
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@
1515
1616
use hir::def_id::DefId;
1717
use middle::resolve_lifetime as rl;
18+
use rustc::dep_graph::{AssertDepGraphSafe, DepNode};
1819
use rustc::ty::subst::Substs;
1920
use rustc::ty::{self, Ty, TyCtxt};
2021
use rustc::hir::map as hir_map;
2122
use syntax::ast;
2223
use rustc::hir;
2324
use rustc::hir::itemlikevisit::ItemLikeVisitor;
2425

26+
use rustc_data_structures::transitive_relation::TransitiveRelation;
27+
2528
use super::terms::*;
2629
use super::terms::VarianceTerm::*;
2730
use super::xform::*;
2831

29-
use dep_graph::DepNode::ItemSignature as VarianceDepNode;
30-
3132
pub struct ConstraintContext<'a, 'tcx: 'a> {
3233
pub terms_cx: TermsContext<'a, 'tcx>,
3334

@@ -38,6 +39,11 @@ pub struct ConstraintContext<'a, 'tcx: 'a> {
3839
bivariant: VarianceTermPtr<'a>,
3940

4041
pub constraints: Vec<Constraint<'a>>,
42+
43+
/// This relation tracks the dependencies between the variance of
44+
/// various items. In particular, if `a < b`, then the variance of
45+
/// `a` depends on the sources of `b`.
46+
pub dependencies: TransitiveRelation<DefId>,
4147
}
4248

4349
/// Declares that the variable `decl_id` appears in a location with
@@ -57,7 +63,6 @@ pub struct Constraint<'a> {
5763
///
5864
/// then while we are visiting `Bar<T>`, the `CurrentItem` would have
5965
/// the def-id and generics of `Foo`.
60-
#[allow(dead_code)] // TODO -- `def_id` field not used yet
6166
pub struct CurrentItem<'a> {
6267
def_id: DefId,
6368
generics: &'a ty::Generics,
@@ -77,10 +82,10 @@ pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>)
7782
invariant: invariant,
7883
bivariant: bivariant,
7984
constraints: Vec::new(),
85+
dependencies: TransitiveRelation::new(),
8086
};
8187

82-
// See README.md for a discussion on dep-graph management.
83-
tcx.visit_all_item_likes_in_krate(VarianceDepNode, &mut constraint_cx);
88+
tcx.hir.krate().visit_all_item_likes(&mut constraint_cx);
8489

8590
constraint_cx
8691
}
@@ -90,13 +95,64 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
9095
let tcx = self.terms_cx.tcx;
9196
let def_id = tcx.hir.local_def_id(item.id);
9297

98+
// Encapsulate constructing the constraints into a task we can
99+
// reference later. This can go away once the red-green
100+
// algorithm is in place.
101+
//
102+
// See README.md for a detailed discussion
103+
// on dep-graph management.
104+
match item.node {
105+
hir::ItemEnum(..) |
106+
hir::ItemStruct(..) |
107+
hir::ItemUnion(..) => {
108+
tcx.dep_graph.with_task(DepNode::ItemVarianceConstraints(def_id),
109+
AssertDepGraphSafe(self),
110+
def_id,
111+
visit_item_task);
112+
}
113+
_ => {
114+
// Nothing to do here, skip the task.
115+
}
116+
}
117+
118+
fn visit_item_task<'a, 'tcx>(ccx: AssertDepGraphSafe<&mut ConstraintContext<'a, 'tcx>>,
119+
def_id: DefId)
120+
{
121+
ccx.0.build_constraints_for_item(def_id);
122+
}
123+
}
124+
125+
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
126+
}
127+
128+
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
129+
}
130+
}
131+
132+
/// Is `param_id` a lifetime according to `map`?
133+
fn is_lifetime(map: &hir_map::Map, param_id: ast::NodeId) -> bool {
134+
match map.find(param_id) {
135+
Some(hir_map::NodeLifetime(..)) => true,
136+
_ => false,
137+
}
138+
}
139+
140+
impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
141+
fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
142+
self.terms_cx.tcx
143+
}
144+
145+
fn build_constraints_for_item(&mut self, def_id: DefId) {
146+
let tcx = self.tcx();
147+
let id = self.tcx().hir.as_local_node_id(def_id).unwrap();
148+
let item = tcx.hir.expect_item(id);
93149
debug!("visit_item item={}", tcx.hir.node_to_string(item.id));
94150

95151
match item.node {
96152
hir::ItemEnum(..) |
97153
hir::ItemStruct(..) |
98154
hir::ItemUnion(..) => {
99-
let generics = tcx.generics_of(did);
155+
let generics = tcx.generics_of(def_id);
100156
let current_item = &CurrentItem { def_id, generics };
101157

102158
// Not entirely obvious: constraints on structs/enums do not
@@ -105,24 +161,14 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
105161
//
106162
// self.add_constraints_from_generics(generics);
107163

108-
for field in tcx.adt_def(did).all_fields() {
164+
for field in tcx.adt_def(def_id).all_fields() {
109165
self.add_constraints_from_ty(current_item,
110-
tcx.item_type(field.did),
166+
tcx.type_of(field.did),
111167
self.covariant);
112168
}
113169
}
114-
hir::ItemTrait(..) => {
115-
let generics = tcx.generics_of(did);
116-
let current_item = &CurrentItem { def_id, generics };
117-
let trait_ref = ty::TraitRef {
118-
def_id: def_id,
119-
substs: Substs::identity_for_item(tcx, def_id)
120-
};
121-
self.add_constraints_from_trait_ref(current_item,
122-
trait_ref,
123-
self.invariant);
124-
}
125170

171+
hir::ItemTrait(..) |
126172
hir::ItemExternCrate(_) |
127173
hir::ItemUse(..) |
128174
hir::ItemStatic(..) |
@@ -133,38 +179,25 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for ConstraintContext<'a, 'tcx> {
133179
hir::ItemGlobalAsm(..) |
134180
hir::ItemTy(..) |
135181
hir::ItemImpl(..) |
136-
hir::ItemDefaultImpl(..) => {}
182+
hir::ItemDefaultImpl(..) => {
183+
span_bug!(item.span, "`build_constraints_for_item` invoked for non-type-def");
184+
}
137185
}
138186
}
139187

140-
fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
141-
}
142-
143-
fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
144-
}
145-
}
146-
147-
/// Is `param_id` a lifetime according to `map`?
148-
fn is_lifetime(map: &hir_map::Map, param_id: ast::NodeId) -> bool {
149-
match map.find(param_id) {
150-
Some(hir_map::NodeLifetime(..)) => true,
151-
_ => false,
152-
}
153-
}
154-
155-
impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
156-
fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> {
157-
self.terms_cx.tcx
188+
/// Load the generics for another item, adding a corresponding
189+
/// relation into the dependencies to indicate that the variance
190+
/// for `current` relies on `def_id`.
191+
fn read_generics(&mut self, current: &CurrentItem, def_id: DefId) -> &'tcx ty::Generics {
192+
let generics = self.tcx().generics_of(def_id);
193+
if self.tcx().dep_graph.is_fully_enabled() {
194+
self.dependencies.add(current.def_id, def_id);
195+
}
196+
generics
158197
}
159198

160-
fn inferred_index(&self, param_id: ast::NodeId) -> InferredIndex {
161-
match self.terms_cx.inferred_map.get(&param_id) {
162-
Some(&index) => index,
163-
None => {
164-
bug!("no inferred index entry for {}",
165-
self.tcx().hir.node_to_string(param_id));
166-
}
167-
}
199+
fn opt_inferred_index(&self, param_id: ast::NodeId) -> Option<&InferredIndex> {
200+
self.terms_cx.inferred_map.get(&param_id)
168201
}
169202

170203
fn find_binding_for_lifetime(&self, param_id: ast::NodeId) -> ast::NodeId {
@@ -245,8 +278,27 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
245278
// Parameter on an item defined within current crate:
246279
// variance not yet inferred, so return a symbolic
247280
// variance.
248-
let InferredIndex(index) = self.inferred_index(param_node_id);
249-
self.terms_cx.inferred_infos[index].term
281+
if let Some(&InferredIndex(index)) = self.opt_inferred_index(param_node_id) {
282+
self.terms_cx.inferred_infos[index].term
283+
} else {
284+
// If there is no inferred entry for a type parameter,
285+
// it must be declared on a (locally defiend) trait -- they don't
286+
// get inferreds because they are always invariant.
287+
if cfg!(debug_assertions) {
288+
let item_node_id = self.tcx().hir.as_local_node_id(item_def_id).unwrap();
289+
let item = self.tcx().hir.expect_item(item_node_id);
290+
let success = match item.node {
291+
hir::ItemTrait(..) => true,
292+
_ => false,
293+
};
294+
if !success {
295+
bug!("parameter {:?} has no inferred, but declared on non-trait: {:?}",
296+
item_def_id,
297+
item);
298+
}
299+
}
300+
self.invariant
301+
}
250302
} else {
251303
// Parameter on an item defined within another crate:
252304
// variance already inferred, just look it up.
@@ -305,11 +357,6 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
305357

306358
let trait_generics = self.tcx().generics_of(trait_ref.def_id);
307359

308-
// This edge is actually implied by the call to
309-
// `trait_def`, but I'm trying to be future-proof. See
310-
// README.md for a discussion on dep-graph management.
311-
self.tcx().dep_graph.read(VarianceDepNode(trait_ref.def_id));
312-
313360
self.add_constraints_from_substs(current,
314361
trait_ref.def_id,
315362
&trait_generics.types,
@@ -362,12 +409,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
362409
}
363410

364411
ty::TyAdt(def, substs) => {
365-
let adt_generics = self.tcx().generics_of(def.did);
366-
367-
// This edge is actually implied by the call to
368-
// `trait_def`, but I'm trying to be future-proof. See
369-
// README.md for a discussion on dep-graph management.
370-
self.tcx().dep_graph.read(VarianceDepNode(def.did));
412+
let adt_generics = self.read_generics(current, def.did);
371413

372414
self.add_constraints_from_substs(current,
373415
def.did,
@@ -381,11 +423,6 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
381423
let trait_ref = &data.trait_ref;
382424
let trait_generics = self.tcx().generics_of(trait_ref.def_id);
383425

384-
// This edge is actually implied by the call to
385-
// `trait_def`, but I'm trying to be future-proof. See
386-
// README.md for a discussion on dep-graph management.
387-
self.tcx().dep_graph.read(VarianceDepNode(trait_ref.def_id));
388-
389426
self.add_constraints_from_substs(current,
390427
trait_ref.def_id,
391428
&trait_generics.types,
@@ -505,7 +542,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
505542
let def_id = current.generics.regions[i].def_id;
506543
let node_id = self.tcx().hir.as_local_node_id(def_id).unwrap();
507544
if self.is_to_be_inferred(node_id) {
508-
let index = self.inferred_index(node_id);
545+
let &index = self.opt_inferred_index(node_id).unwrap();
509546
self.add_constraint(index, variance);
510547
}
511548
}

‎src/librustc_typeck/variance/mod.rs

+59-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@
1212
//! parameters. See README.md for details.
1313
1414
use arena;
15-
use rustc::ty::TyCtxt;
15+
use rustc::dep_graph::DepNode;
16+
use rustc::hir;
17+
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
18+
use rustc::ty::{self, CrateVariancesMap, TyCtxt};
19+
use rustc::ty::maps::Providers;
20+
use std::rc::Rc;
1621

1722
/// Defines the `TermsContext` basically houses an arena where we can
1823
/// allocate terms.
@@ -27,10 +32,60 @@ mod solve;
2732
/// Code for transforming variances.
2833
mod xform;
2934

30-
pub fn infer_variance<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
35+
pub fn provide(providers: &mut Providers) {
36+
*providers = Providers {
37+
variances_of,
38+
crate_variances,
39+
..*providers
40+
};
41+
}
42+
43+
fn crate_variances<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, crate_num: CrateNum)
44+
-> Rc<CrateVariancesMap> {
45+
assert_eq!(crate_num, LOCAL_CRATE);
3146
let mut arena = arena::TypedArena::new();
3247
let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena);
3348
let constraints_cx = constraints::add_constraints_from_crate(terms_cx);
34-
solve::solve_constraints(constraints_cx);
35-
tcx.variance_computed.set(true);
49+
Rc::new(solve::solve_constraints(constraints_cx))
50+
}
51+
52+
fn variances_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_def_id: DefId)
53+
-> Rc<Vec<ty::Variance>> {
54+
let item_id = tcx.hir.as_local_node_id(item_def_id).expect("expected local def-id");
55+
let item = tcx.hir.expect_item(item_id);
56+
match item.node {
57+
hir::ItemTrait(..) => {
58+
// Traits are always invariant.
59+
let generics = tcx.generics_of(item_def_id);
60+
assert!(generics.parent.is_none());
61+
Rc::new(vec![ty::Variance::Invariant; generics.count()])
62+
}
63+
64+
hir::ItemEnum(..) |
65+
hir::ItemStruct(..) |
66+
hir::ItemUnion(..) => {
67+
// Everything else must be inferred.
68+
69+
// Lacking red/green, we read the variances for all items here
70+
// but ignore the dependencies, then re-synthesize the ones we need.
71+
let crate_map = tcx.dep_graph.with_ignore(|| tcx.crate_variances(LOCAL_CRATE));
72+
tcx.dep_graph.read(DepNode::ItemVarianceConstraints(item_def_id));
73+
for &dep_def_id in crate_map.dependencies.less_than(&item_def_id) {
74+
if dep_def_id.is_local() {
75+
tcx.dep_graph.read(DepNode::ItemVarianceConstraints(dep_def_id));
76+
} else {
77+
tcx.dep_graph.read(DepNode::ItemVariances(dep_def_id));
78+
}
79+
}
80+
81+
crate_map.variances.get(&item_def_id)
82+
.unwrap_or(&crate_map.empty_variance)
83+
.clone()
84+
}
85+
86+
_ => {
87+
// Variance not relevant.
88+
span_bug!(item.span, "asked to compute variance for wrong kind of item")
89+
}
90+
}
3691
}

‎src/librustc_typeck/variance/solve.rs

+13-11
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
//! optimal solution to the constraints. The final variance for each
1616
//! inferred is then written into the `variance_map` in the tcx.
1717
18+
use rustc::hir::def_id::DefId;
1819
use rustc::ty;
20+
use rustc_data_structures::fx::FxHashMap;
1921
use std::rc::Rc;
2022

2123
use super::constraints::*;
@@ -31,8 +33,8 @@ struct SolveContext<'a, 'tcx: 'a> {
3133
solutions: Vec<ty::Variance>,
3234
}
3335

34-
pub fn solve_constraints(constraints_cx: ConstraintContext) {
35-
let ConstraintContext { terms_cx, constraints, .. } = constraints_cx;
36+
pub fn solve_constraints(constraints_cx: ConstraintContext) -> ty::CrateVariancesMap {
37+
let ConstraintContext { terms_cx, dependencies, constraints, .. } = constraints_cx;
3638

3739
let solutions = terms_cx.inferred_infos
3840
.iter()
@@ -45,7 +47,10 @@ pub fn solve_constraints(constraints_cx: ConstraintContext) {
4547
solutions: solutions,
4648
};
4749
solutions_cx.solve();
48-
solutions_cx.write();
50+
let variances = solutions_cx.create_map();
51+
let empty_variance = Rc::new(Vec::new());
52+
53+
ty::CrateVariancesMap { dependencies, variances, empty_variance }
4954
}
5055

5156
impl<'a, 'tcx> SolveContext<'a, 'tcx> {
@@ -83,7 +88,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
8388
}
8489
}
8590

86-
fn write(&self) {
91+
fn create_map(&self) -> FxHashMap<DefId, Rc<Vec<ty::Variance>>> {
8792
// Collect all the variances for a particular item and stick
8893
// them into the variance map. We rely on the fact that we
8994
// generate all the inferreds for a particular item
@@ -95,11 +100,7 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
95100

96101
let tcx = self.terms_cx.tcx;
97102

98-
// Ignore the writes here because the relevant edges were
99-
// already accounted for in `constraints.rs`. See the section
100-
// on dependency graph management in README.md for more
101-
// information.
102-
let _ignore = tcx.dep_graph.in_ignore();
103+
let mut map = FxHashMap();
103104

104105
let solutions = &self.solutions;
105106
let inferred_infos = &self.terms_cx.inferred_infos;
@@ -137,9 +138,10 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
137138
item_variances);
138139
}
139140

140-
tcx.maps.variances_of.borrow_mut()
141-
.insert(item_def_id, Rc::new(item_variances));
141+
map.insert(item_def_id, Rc::new(item_variances));
142142
}
143+
144+
map
143145
}
144146

145147
fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance {

‎src/librustc_typeck/variance/terms.rs

+6-33
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ fn lang_items(tcx: TyCtxt) -> Vec<(ast::NodeId, Vec<ty::Variance>)> {
139139
impl<'a, 'tcx> TermsContext<'a, 'tcx> {
140140
fn add_inferreds_for_item(&mut self,
141141
item_id: ast::NodeId,
142-
has_self: bool,
143142
generics: &hir::Generics) {
144143
//! Add "inferreds" for the generic parameters declared on this
145144
//! item. This has a lot of annoying parameters because we are
@@ -149,38 +148,17 @@ impl<'a, 'tcx> TermsContext<'a, 'tcx> {
149148
//!
150149
151150
// NB: In the code below for writing the results back into the
152-
// tcx, we rely on the fact that all inferreds for a particular
153-
// item are assigned continuous indices.
151+
// `CrateVariancesMap`, we rely on the fact that all inferreds
152+
// for a particular item are assigned continuous indices.
154153

155-
let inferreds_on_entry = self.num_inferred();
156-
157-
if has_self {
158-
self.add_inferred(item_id, 0, item_id);
159-
}
160-
161-
for (i, p) in generics.lifetimes.iter().enumerate() {
154+
for (p, i) in generics.lifetimes.iter().zip(0..) {
162155
let id = p.lifetime.id;
163-
let i = has_self as usize + i;
164156
self.add_inferred(item_id, i, id);
165157
}
166158

167-
for (i, p) in generics.ty_params.iter().enumerate() {
168-
let i = has_self as usize + generics.lifetimes.len() + i;
159+
for (p, i) in generics.ty_params.iter().zip(generics.lifetimes.len()..) {
169160
self.add_inferred(item_id, i, p.id);
170161
}
171-
172-
// If this item has no type or lifetime parameters,
173-
// then there are no variances to infer, so just
174-
// insert an empty entry into the variance map.
175-
// Arguably we could just leave the map empty in this
176-
// case but it seems cleaner to be able to distinguish
177-
// "invalid item id" from "item id with no
178-
// parameters".
179-
if self.num_inferred() == inferreds_on_entry {
180-
let item_def_id = self.tcx.hir.local_def_id(item_id);
181-
self.tcx.maps.variances_of.borrow_mut()
182-
.insert(item_def_id, self.empty_variances.clone());
183-
}
184162
}
185163

186164
fn add_inferred(&mut self, item_id: ast::NodeId, index: usize, param_id: ast::NodeId) {
@@ -232,15 +210,10 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for TermsContext<'a, 'tcx> {
232210
hir::ItemEnum(_, ref generics) |
233211
hir::ItemStruct(_, ref generics) |
234212
hir::ItemUnion(_, ref generics) => {
235-
self.add_inferreds_for_item(item.id, false, generics);
236-
}
237-
hir::ItemTrait(_, ref generics, ..) => {
238-
// Note: all inputs for traits are ultimately
239-
// constrained to be invariant. See `visit_item` in
240-
// the impl for `ConstraintContext` in `constraints.rs`.
241-
self.add_inferreds_for_item(item.id, true, generics);
213+
self.add_inferreds_for_item(item.id, generics);
242214
}
243215

216+
hir::ItemTrait(..) |
244217
hir::ItemExternCrate(_) |
245218
hir::ItemUse(..) |
246219
hir::ItemDefaultImpl(..) |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that changing what a `type` points to does not go unnoticed
12+
// by the variance analysis.
13+
14+
// compile-flags: -Z query-dep-graph
15+
16+
#![feature(rustc_attrs)]
17+
#![allow(dead_code)]
18+
#![allow(unused_variables)]
19+
20+
fn main() { }
21+
22+
struct Foo<T> {
23+
f: T
24+
}
25+
26+
#[rustc_if_this_changed]
27+
type TypeAlias<T> = Foo<T>;
28+
29+
#[rustc_then_this_would_need(ItemVariances)] //~ ERROR OK
30+
struct Use<T> {
31+
x: TypeAlias<T>
32+
}

0 commit comments

Comments
 (0)
Please sign in to comment.