Skip to content

Commit 4824a19

Browse files
committed
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

Lines changed: 6 additions & 0 deletions
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

Lines changed: 0 additions & 4 deletions
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

Lines changed: 15 additions & 1 deletion
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

Lines changed: 22 additions & 0 deletions
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

Lines changed: 2 additions & 8 deletions
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

Lines changed: 14 additions & 0 deletions
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

Lines changed: 1 addition & 3 deletions
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

Lines changed: 23 additions & 45 deletions
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

0 commit comments

Comments
 (0)