@@ -166,74 +166,77 @@ fn insert_required_predicates_to_be_wf<'tcx>(
166
166
debug ! ( "Adt" ) ;
167
167
if let Some ( unsubstituted_predicates) = global_inferred_outlives. get ( & adt. did ( ) ) {
168
168
for ( unsubstituted_predicate, stack) in & unsubstituted_predicates. 0 {
169
- // `unsubstituted_predicate` is `U: 'b` in the
170
- // example above. So apply the substitution to
171
- // get `T: 'a` (or `predicate`):
172
- let predicate = unsubstituted_predicates
173
- . rebind ( * unsubstituted_predicate)
174
- . subst ( tcx, substs) ;
175
-
176
169
// We must detect cycles in the inference. If we don't, rustc can hang.
177
170
// Cycles can be formed by associated types on traits when they are used like so:
178
171
//
179
172
// ```
180
- // trait Trait<'a> { type Assoc: 'a; }
181
- // struct Node<'node, T: Trait<'node>>(Var<'node, T::Assoc>, Option<T::Assoc>);
173
+ // trait Trait<'a> {
174
+ // type Assoc: 'a;
175
+ // }
176
+ // struct Node<'node, T: Trait<'node>> {
177
+ // var: Var<'node, T::Assoc>,
178
+ // }
179
+ // struct Var<'var, R: 'var> {
180
+ // node: Box<Node<'var, RGen<R>>>,
181
+ // }
182
182
// struct RGen<R>(std::marker::PhantomData<R>);
183
- // impl<'a, R: 'a> Trait<'a> for RGen<R> { type Assoc = R; }
184
- // struct Var<'var, R: 'var>(Box<Node<'var, RGen<R>>>);
183
+ // impl<'a, R: 'a> Trait<'a> for RGen<R> {
184
+ // type Assoc = R; // works with () too
185
+ // }
185
186
// ```
186
187
//
187
188
// Visiting Node, we walk the fields and find a Var. Var has an explicit
188
189
// R : 'var.
189
- // Node finds this on its Var field, substitutes through, and gets an inferred
190
+ // Node finds this in check_explicit_predicates.
191
+ // Node substitutes its type parameters for Var through, and gets an inferred
190
192
// <T as Trait<'node>>::Assoc: 'node.
191
- // Visiting Var, we walk the fields and find a Node. So Var then picks up
192
- // Node's new inferred predicate (in global_inferred_outlives) and substitutes
193
- // the types it passed to Node ('var for 'node, RGen<R> for T).
194
- // So Var gets
193
+ // Visiting Var, we walk the fields and find Node, for which there us an
194
+ // unsubstituted predicate in the global map. So Var gets
195
195
// <RGen<R> as Trait<'var>>::Assoc: 'var
196
196
// But Node contains a Var. So Node gets
197
197
// <RGen<<T as Trait<'node>>::Assoc> as Trait<'node>>::Assoc 'node
198
198
// Var gets
199
199
// <RGen<<RGen<R> as Trait<'var>>::Assoc> as Trait<'var>>::Assoc: 'var
200
- // Etc. This goes on forever.
201
200
//
202
- // We cut off the cycle formation by tracking in a stack the defs that
203
- // have picked up a substituted predicate each time we produce an edge,
204
- // and don't insert a predicate that is simply a substituted version of
205
- // one we've already seen and added.
201
+ // Etc. This goes on forever. The fact that RGen<R>::Assoc *is* R is
202
+ // irrelevant. It is simply that both types find a way to add a bigger
203
+ // predicate each time.
204
+ //
205
+ // The outlives requirements propagate through the crate's types like a
206
+ // graph walk, so to detect this cycle we can just track visited nodes in
207
+ // our branch by pushing them on a stack when we move through the graph.
208
+ // An edge move is when a type picks up a predicate from one of its fields
209
+ // that it hasn't seen before. We store the stacks alongside the predicates,
210
+ // so they carry their provenance with them through the current and
211
+ // subsequent rounds of crate-wide inference.
206
212
//
207
213
// Try: RUSTC_LOG=rustc_hir_analysis::outlives=debug \
208
- // rustc +stage1 src/test/ui/typeck /issue-102966.rs 2>&1 \
214
+ // rustc +stage1 src/test/ui/rfc-2093-infer-outlives /issue-102966.rs 2>&1 \
209
215
// | rg '(grew|cycle)'
210
216
//
211
- // We do not currently treat a type with an explicit bound as the first
212
- // in the visit stack. So Var here does not appear first in the stack,
213
- // Node does, and each of Node and Var will get a version of
214
- // `<RGen<R> as Trait<'node>>::Assoc: 'node` before the cycle is cut at
215
- // Node. This avoids having a second equivalent bound on Node, and also
216
- // having RGen involved in Node's predicates (which would be silly).
217
- //
218
- // It is not clear whether cyclically-substituted versions of bounds we
219
- // already have are always redundant/unnecessary to add to Self.
220
- // This solution avoids digging into `impl Trait for RGen` to find that it
221
- // unifies with an existing bound but it is really a guess that this
222
- // cyclic substitution cannot add valuable information. There may be
223
- // situations when an error is appropriate.
217
+ // We do not treat a type with an explicit bound as the first in the visit
218
+ // stack. So in the example, Node appears first in the stack, and each of
219
+ // Node and Var will get a new bound constraining an ::Assoc projection
220
+ // before the cycle is cut at Node. If Var were already in the visit stack,
221
+ // it would not receive the projection bound, which is something it needs.
222
+
224
223
if stack. iter ( ) . any ( |& ( did, _span) | did == self_did) {
225
224
debug ! (
226
225
"detected cycle in inferred_outlives_predicates,\
227
226
for unsubstituted predicate {unsubstituted_predicate:?}:\
228
227
{self_did:?} found in {stack:?}"
229
228
) ;
230
229
} else {
230
+ // `unsubstituted_predicate` is `U: 'b` in Example 1 above.
231
+ // So apply the substitution to get `T: 'a` (or `predicate`):
232
+ let predicate = unsubstituted_predicates
233
+ . rebind ( * unsubstituted_predicate)
234
+ . subst ( tcx, substs) ;
235
+
231
236
insert_outlives_predicate (
232
237
tcx,
233
238
predicate. 0 ,
234
239
predicate. 1 ,
235
- // Treat the top-level definition we are currently walking the fields of as the
236
- // type visited in the DefStack. Not the field type.
237
240
self_did,
238
241
field_span,
239
242
required_predicates,
0 commit comments