Skip to content

Commit 01ebc37

Browse files
committed
Change how dep-graph edges are handled in variance to
be more fine-grained, fixing the `dep-graph-struct-signature` test.
1 parent daa7408 commit 01ebc37

File tree

6 files changed

+94
-22
lines changed

6 files changed

+94
-22
lines changed

src/librustc_typeck/variance/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,54 @@ failure, but rather because the target type `Foo<Y>` is itself just
9393
not well-formed. Basically we get to assume well-formedness of all
9494
types involved before considering variance.
9595

96+
#### Dependency graph management
97+
98+
Because variance works in two phases, if we are not careful, we wind
99+
up with a muddled mess of a dep-graph. Basically, when gathering up
100+
the constraints, things are fairly well-structured, but then we do a
101+
fixed-point iteration and write the results back where they
102+
belong. You can't give this fixed-point iteration a single task
103+
because it reads from (and writes to) the variance of all types in the
104+
crate. In principle, we *could* switch the "current task" in a very
105+
fine-grained way while propagating constraints in the fixed-point
106+
iteration and everything would be automatically tracked, but that
107+
would add some overhead and isn't really necessary anyway.
108+
109+
Instead what we do is to add edges into the dependency graph as we
110+
construct the constraint set: so, if computing the constraints for
111+
node `X` requires loading the inference variables from node `Y`, then
112+
we can add an edge `Y -> X`, since the variance we ultimately infer
113+
for `Y` will affect the variance we ultimately infer for `X`.
114+
115+
At this point, we've basically mirrored the inference graph in the
116+
dependency graph. This means we can just completely ignore the
117+
fixed-point iteration, since it is just shuffling values along this
118+
graph. In other words, if we added the fine-grained switching of tasks
119+
I described earlier, all it would show is that we repeatedly read the
120+
values described by the constraints, but those edges were already
121+
added when building the constraints in the first place.
122+
123+
Here is how this is implemented (at least as of the time of this
124+
writing). The associated `DepNode` for the variance map is (at least
125+
presently) `Signature(DefId)`. This means that, in `constraints.rs`,
126+
when we visit an item to load up its constraints, we set
127+
`Signature(DefId)` as the current task (the "memoization" pattern
128+
described in the `dep-graph` README). Then whenever we find an
129+
embedded type or trait, we add a synthetic read of `Signature(DefId)`,
130+
which covers the variances we will compute for all of its
131+
parameters. This read is synthetic (i.e., we call
132+
`variance_map.read()`) because, in fact, the final variance is not yet
133+
computed -- the read *will* occur (repeatedly) during the fixed-point
134+
iteration phase.
135+
136+
In fact, we don't really *need* this synthetic read. That's because we
137+
do wind up looking up the `TypeScheme` or `TraitDef` for all
138+
references types/traits, and those reads add an edge from
139+
`Signature(DefId)` (that is, they share the same dep node as
140+
variance). However, I've kept the synthetic reads in place anyway,
141+
just for future-proofing (in case we change the dep-nodes in the
142+
future), and because it makes the intention a bit clearer I think.
143+
96144
### Addendum: Variance on traits
97145

98146
As mentioned above, we used to permit variance on traits. This was
@@ -250,3 +298,5 @@ validly derived as `&'a ()` for any `'a`:
250298
This change otoh means that `<'static () as Identity>::Out` is
251299
always `&'static ()` (which might then be upcast to `'a ()`,
252300
separately). This was helpful in solving #21750.
301+
302+

src/librustc_typeck/variance/constraints.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
//! The second pass over the AST determines the set of constraints.
1414
//! We walk the set of items and, for each member, generate new constraints.
1515
16+
use dep_graph::DepTrackingMapConfig;
1617
use middle::def_id::DefId;
1718
use middle::resolve_lifetime as rl;
1819
use middle::subst;
1920
use middle::subst::ParamSpace;
2021
use middle::ty::{self, Ty};
22+
use middle::ty::maps::ItemVariances;
2123
use rustc::front::map as hir_map;
2224
use syntax::ast;
2325
use rustc_front::hir;
@@ -48,10 +50,10 @@ pub struct Constraint<'a> {
4850
pub variance: &'a VarianceTerm<'a>,
4951
}
5052

51-
pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>,
52-
krate: &hir::Crate)
53+
pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>)
5354
-> ConstraintContext<'a, 'tcx>
5455
{
56+
let tcx = terms_cx.tcx;
5557
let covariant = terms_cx.arena.alloc(ConstantTerm(ty::Covariant));
5658
let contravariant = terms_cx.arena.alloc(ConstantTerm(ty::Contravariant));
5759
let invariant = terms_cx.arena.alloc(ConstantTerm(ty::Invariant));
@@ -64,7 +66,11 @@ pub fn add_constraints_from_crate<'a, 'tcx>(terms_cx: TermsContext<'a, 'tcx>,
6466
bivariant: bivariant,
6567
constraints: Vec::new(),
6668
};
67-
krate.visit_all_items(&mut constraint_cx);
69+
70+
// See README.md for a discussion on dep-graph management.
71+
tcx.visit_all_items_in_krate(|def_id| ItemVariances::to_dep_node(&def_id),
72+
&mut constraint_cx);
73+
6874
constraint_cx
6975
}
7076

@@ -289,6 +295,11 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
289295

290296
let trait_def = self.tcx().lookup_trait_def(trait_ref.def_id);
291297

298+
// This edge is actually implied by the call to
299+
// `lookup_trait_def`, but I'm trying to be future-proof. See
300+
// README.md for a discussion on dep-graph management.
301+
self.tcx().dep_graph.read(ItemVariances::to_dep_node(&trait_ref.def_id));
302+
292303
self.add_constraints_from_substs(
293304
generics,
294305
trait_ref.def_id,
@@ -345,6 +356,11 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
345356
ty::TyStruct(def, substs) => {
346357
let item_type = self.tcx().lookup_item_type(def.did);
347358

359+
// This edge is actually implied by the call to
360+
// `lookup_trait_def`, but I'm trying to be future-proof. See
361+
// README.md for a discussion on dep-graph management.
362+
self.tcx().dep_graph.read(ItemVariances::to_dep_node(&def.did));
363+
348364
// All type parameters on enums and structs should be
349365
// in the TypeSpace.
350366
assert!(item_type.generics.types.is_empty_in(subst::SelfSpace));
@@ -364,6 +380,12 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
364380
ty::TyProjection(ref data) => {
365381
let trait_ref = &data.trait_ref;
366382
let trait_def = self.tcx().lookup_trait_def(trait_ref.def_id);
383+
384+
// This edge is actually implied by the call to
385+
// `lookup_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(ItemVariances::to_dep_node(&trait_ref.def_id));
388+
367389
self.add_constraints_from_substs(
368390
generics,
369391
trait_ref.def_id,
@@ -424,7 +446,6 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
424446
}
425447
}
426448

427-
428449
/// Adds constraints appropriate for a nominal type (enum, struct,
429450
/// object, etc) appearing in a context with ambient variance `variance`
430451
fn add_constraints_from_substs(&mut self,

src/librustc_typeck/variance/mod.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
//! parameters. See README.md for details.
1313
1414
use arena;
15-
use dep_graph::DepNode;
1615
use middle::ty;
1716

1817
/// Defines the `TermsContext` basically houses an arena where we can
@@ -29,11 +28,9 @@ mod solve;
2928
mod xform;
3029

3130
pub fn infer_variance(tcx: &ty::ctxt) {
32-
let _task = tcx.dep_graph.in_task(DepNode::Variance);
33-
let krate = tcx.map.krate();
3431
let mut arena = arena::TypedArena::new();
35-
let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena, krate);
36-
let constraints_cx = constraints::add_constraints_from_crate(terms_cx, krate);
32+
let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena);
33+
let constraints_cx = constraints::add_constraints_from_crate(terms_cx);
3734
solve::solve_constraints(constraints_cx);
3835
tcx.variance_computed.set(true);
3936
}

src/librustc_typeck/variance/solve.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ impl<'a, 'tcx> SolveContext<'a, 'tcx> {
9696
// item id).
9797

9898
let tcx = self.terms_cx.tcx;
99+
100+
// Ignore the writes here because the relevant edges were
101+
// already accounted for in `constraints.rs`. See the section
102+
// on dependency graph management in README.md for more
103+
// information.
104+
let _ignore = tcx.dep_graph.in_ignore();
105+
99106
let solutions = &self.solutions;
100107
let inferred_infos = &self.terms_cx.inferred_infos;
101108
let mut index = 0;

src/librustc_typeck/variance/terms.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
// a variable.
2121

2222
use arena::TypedArena;
23+
use dep_graph::DepTrackingMapConfig;
2324
use middle::subst::{ParamSpace, FnSpace, TypeSpace, SelfSpace, VecPerParamSpace};
2425
use middle::ty;
26+
use middle::ty::maps::ItemVariances;
2527
use std::fmt;
2628
use std::rc::Rc;
2729
use syntax::ast;
@@ -97,8 +99,7 @@ pub struct InferredInfo<'a> {
9799

98100
pub fn determine_parameters_to_be_inferred<'a, 'tcx>(
99101
tcx: &'a ty::ctxt<'tcx>,
100-
arena: &'a mut TypedArena<VarianceTerm<'a>>,
101-
krate: &hir::Crate)
102+
arena: &'a mut TypedArena<VarianceTerm<'a>>)
102103
-> TermsContext<'a, 'tcx>
103104
{
104105
let mut terms_cx = TermsContext {
@@ -117,7 +118,9 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>(
117118
})
118119
};
119120

120-
krate.visit_all_items(&mut terms_cx);
121+
// See README.md for a discussion on dep-graph management.
122+
tcx.visit_all_items_in_krate(|def_id| ItemVariances::to_dep_node(&def_id),
123+
&mut terms_cx);
121124

122125
terms_cx
123126
}

src/test/compile-fail/dep-graph-struct-signature.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,23 +74,17 @@ mod signatures {
7474
fn indirect(x: WillChanges) { }
7575
}
7676

77-
// these are invalid dependencies, though sometimes we create edges
78-
// anyway.
7977
mod invalid_signatures {
8078
use WontChange;
8179

82-
// FIXME due to the variance pass having overly conservative edges,
83-
// we incorrectly think changes are needed here
84-
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
85-
#[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
80+
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path
81+
#[rustc_then_this_would_need(CollectItem)] //~ ERROR no path
8682
trait A {
8783
fn do_something_else_twice(x: WontChange);
8884
}
8985

90-
// FIXME due to the variance pass having overly conservative edges,
91-
// we incorrectly think changes are needed here
92-
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR OK
93-
#[rustc_then_this_would_need(CollectItem)] //~ ERROR OK
86+
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path
87+
#[rustc_then_this_would_need(CollectItem)] //~ ERROR no path
9488
fn b(x: WontChange) { }
9589

9690
#[rustc_then_this_would_need(ItemSignature)] //~ ERROR no path from `WillChange`

0 commit comments

Comments
 (0)