@@ -8,23 +8,24 @@ use rustc_span::Span;
8
8
use super :: explicit:: ExplicitPredicatesMap ;
9
9
use super :: utils:: * ;
10
10
11
+ pub ( super ) type GlobalInferredOutlives < ' tcx > =
12
+ FxHashMap < DefId , ty:: EarlyBinder < RequiredPredicates < ' tcx > > > ;
13
+
11
14
/// Infer predicates for the items in the crate.
12
15
///
13
16
/// `global_inferred_outlives`: this is initially the empty map that
14
17
/// was generated by walking the items in the crate. This will
15
18
/// now be filled with inferred predicates.
16
- pub ( super ) fn infer_predicates < ' tcx > (
17
- tcx : TyCtxt < ' tcx > ,
18
- ) -> FxHashMap < DefId , ty:: EarlyBinder < RequiredPredicates < ' tcx > > > {
19
- debug ! ( "infer_predicates" ) ;
20
-
19
+ pub ( super ) fn infer_predicates < ' tcx > ( tcx : TyCtxt < ' tcx > ) -> GlobalInferredOutlives < ' tcx > {
21
20
let mut explicit_map = ExplicitPredicatesMap :: new ( ) ;
22
21
23
22
let mut global_inferred_outlives = FxHashMap :: default ( ) ;
24
23
25
24
// If new predicates were added then we need to re-calculate
26
25
// all crates since there could be new implied predicates.
26
+ let mut round = 1 ;
27
27
' outer: loop {
28
+ debug ! ( "infer_predicates: round {round}" ) ;
28
29
let mut predicates_added = false ;
29
30
30
31
// Visit all the crates and infer predicates
@@ -50,6 +51,7 @@ pub(super) fn infer_predicates<'tcx>(
50
51
let field_span = tcx. def_span ( field_def. did ) ;
51
52
insert_required_predicates_to_be_wf (
52
53
tcx,
54
+ adt_def. did ( ) ,
53
55
field_ty,
54
56
field_span,
55
57
& global_inferred_outlives,
@@ -72,6 +74,22 @@ pub(super) fn infer_predicates<'tcx>(
72
74
global_inferred_outlives. get ( & item_did. to_def_id ( ) ) . map_or ( 0 , |p| p. 0 . len ( ) ) ;
73
75
if item_required_predicates. len ( ) > item_predicates_len {
74
76
predicates_added = true ;
77
+ if tracing:: enabled!( tracing:: Level :: DEBUG ) {
78
+ let def_id = item_did. to_def_id ( ) ;
79
+ use std:: collections:: BTreeSet ;
80
+ let global_preds: BTreeSet < _ > =
81
+ global_inferred_outlives. get ( & def_id) . map_or_else ( Default :: default, |e| {
82
+ e. 0 . iter ( ) . map ( |( pred, _) | pred) . collect ( )
83
+ } ) ;
84
+ let computed_preds: BTreeSet < _ > =
85
+ item_required_predicates. iter ( ) . map ( |( pred, _) | pred) . collect ( ) ;
86
+ let added = computed_preds. difference ( & global_preds) . collect :: < BTreeSet < _ > > ( ) ;
87
+ debug ! ( "global_inferred_outlives grew for {def_id:?}, added: {added:?}" ) ;
88
+ let removed = global_preds. difference ( & computed_preds) . collect :: < BTreeSet < _ > > ( ) ;
89
+ if !removed. is_empty ( ) {
90
+ debug ! ( "global_inferred_outlives lost predicates: {removed:?}" )
91
+ }
92
+ }
75
93
global_inferred_outlives
76
94
. insert ( item_did. to_def_id ( ) , ty:: EarlyBinder ( item_required_predicates) ) ;
77
95
}
@@ -80,16 +98,18 @@ pub(super) fn infer_predicates<'tcx>(
80
98
if !predicates_added {
81
99
break ' outer;
82
100
}
101
+ round += 1 ;
83
102
}
84
103
85
104
global_inferred_outlives
86
105
}
87
106
88
107
fn insert_required_predicates_to_be_wf < ' tcx > (
89
108
tcx : TyCtxt < ' tcx > ,
109
+ self_did : DefId ,
90
110
field_ty : Ty < ' tcx > ,
91
111
field_span : Span ,
92
- global_inferred_outlives : & FxHashMap < DefId , ty :: EarlyBinder < RequiredPredicates < ' tcx > > > ,
112
+ global_inferred_outlives : & GlobalInferredOutlives < ' tcx > ,
93
113
required_predicates : & mut RequiredPredicates < ' tcx > ,
94
114
explicit_map : & mut ExplicitPredicatesMap < ' tcx > ,
95
115
) {
@@ -109,14 +129,22 @@ fn insert_required_predicates_to_be_wf<'tcx>(
109
129
// We also want to calculate potential predicates for the T
110
130
ty:: Ref ( region, rty, _) => {
111
131
debug ! ( "Ref" ) ;
112
- insert_outlives_predicate ( tcx, rty. into ( ) , region, field_span, required_predicates) ;
132
+ insert_outlives_predicate (
133
+ tcx,
134
+ rty. into ( ) ,
135
+ region,
136
+ self_did,
137
+ field_span,
138
+ required_predicates,
139
+ None ,
140
+ ) ;
113
141
}
114
142
115
143
// For each Adt (struct/enum/union) type `Foo<'a, T>`, we
116
144
// can load the current set of inferred and explicit
117
145
// predicates from `global_inferred_outlives` and filter the
118
146
// ones that are TypeOutlives.
119
- ty:: Adt ( def , substs) => {
147
+ ty:: Adt ( adt , substs) => {
120
148
// First check the inferred predicates
121
149
//
122
150
// Example 1:
@@ -136,21 +164,83 @@ fn insert_required_predicates_to_be_wf<'tcx>(
136
164
// `['b => 'a, U => T]` and thus get the requirement that `T:
137
165
// 'a` holds for `Foo`.
138
166
debug ! ( "Adt" ) ;
139
- if let Some ( unsubstituted_predicates) = global_inferred_outlives. get ( & def . did ( ) ) {
140
- for ( unsubstituted_predicate, & span ) in & unsubstituted_predicates. 0 {
167
+ if let Some ( unsubstituted_predicates) = global_inferred_outlives. get ( & adt . did ( ) ) {
168
+ for ( unsubstituted_predicate, stack ) in & unsubstituted_predicates. 0 {
141
169
// `unsubstituted_predicate` is `U: 'b` in the
142
170
// example above. So apply the substitution to
143
171
// get `T: 'a` (or `predicate`):
144
172
let predicate = unsubstituted_predicates
145
173
. rebind ( * unsubstituted_predicate)
146
174
. subst ( tcx, substs) ;
147
- insert_outlives_predicate (
148
- tcx,
149
- predicate. 0 ,
150
- predicate. 1 ,
151
- span,
152
- required_predicates,
153
- ) ;
175
+
176
+ // We must detect cycles in the inference. If we don't, rustc can hang.
177
+ // Cycles can be formed by associated types on traits when they are used like so:
178
+ //
179
+ // ```
180
+ // trait Trait<'a> { type Assoc: 'a; }
181
+ // struct Node<'node, T: Trait<'node>>(Var<'node, T::Assoc>, Option<T::Assoc>);
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>>>);
185
+ // ```
186
+ //
187
+ // Visiting Node, we walk the fields and find a Var. Var has an explicit
188
+ // R : 'var.
189
+ // Node finds this on its Var field, substitutes through, and gets an inferred
190
+ // <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
195
+ // <RGen<R> as Trait<'var>>::Assoc: 'var
196
+ // But Node contains a Var. So Node gets
197
+ // <RGen<<T as Trait<'node>>::Assoc> as Trait<'node>>::Assoc 'node
198
+ // Var gets
199
+ // <RGen<<RGen<R> as Trait<'var>>::Assoc> as Trait<'var>>::Assoc: 'var
200
+ // Etc. This goes on forever.
201
+ //
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.
206
+ //
207
+ // Try: RUSTC_LOG=rustc_hir_analysis::outlives=debug \
208
+ // rustc +stage1 src/test/ui/typeck/issue-102966.rs 2>&1 \
209
+ // | rg '(grew|cycle)'
210
+ //
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.
224
+ if stack. iter ( ) . any ( |& ( did, _span) | did == self_did) {
225
+ debug ! (
226
+ "detected cycle in inferred_outlives_predicates,\
227
+ for unsubstituted predicate {unsubstituted_predicate:?}:\
228
+ {self_did:?} found in {stack:?}"
229
+ ) ;
230
+ } else {
231
+ insert_outlives_predicate (
232
+ tcx,
233
+ predicate. 0 ,
234
+ 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
+ self_did,
238
+ field_span,
239
+ required_predicates,
240
+ // a copy of this is made for the predicate and (self_did, field_span) is pushed.
241
+ Some ( stack) ,
242
+ ) ;
243
+ }
154
244
}
155
245
}
156
246
@@ -159,7 +249,8 @@ fn insert_required_predicates_to_be_wf<'tcx>(
159
249
// let _: () = substs.region_at(0);
160
250
check_explicit_predicates (
161
251
tcx,
162
- def. did ( ) ,
252
+ self_did,
253
+ adt. did ( ) ,
163
254
substs,
164
255
required_predicates,
165
256
explicit_map,
@@ -187,6 +278,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
187
278
ex_trait_ref. with_self_ty ( tcx, tcx. types . usize ) . skip_binder ( ) . substs ;
188
279
check_explicit_predicates (
189
280
tcx,
281
+ self_did,
190
282
ex_trait_ref. skip_binder ( ) . def_id ,
191
283
substs,
192
284
required_predicates,
@@ -202,6 +294,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
202
294
debug ! ( "Projection" ) ;
203
295
check_explicit_predicates (
204
296
tcx,
297
+ self_did,
205
298
tcx. parent ( obj. item_def_id ) ,
206
299
obj. substs ,
207
300
required_predicates,
@@ -232,23 +325,28 @@ fn insert_required_predicates_to_be_wf<'tcx>(
232
325
/// applying the substitution as above.
233
326
fn check_explicit_predicates < ' tcx > (
234
327
tcx : TyCtxt < ' tcx > ,
328
+ // i.e. Foo
329
+ self_did : DefId ,
330
+ // i.e. Bar
235
331
def_id : DefId ,
236
332
substs : & [ GenericArg < ' tcx > ] ,
237
333
required_predicates : & mut RequiredPredicates < ' tcx > ,
238
334
explicit_map : & mut ExplicitPredicatesMap < ' tcx > ,
239
335
ignored_self_ty : Option < Ty < ' tcx > > ,
240
336
) {
241
337
debug ! (
242
- "check_explicit_predicates(def_id={:?}, \
338
+ "check_explicit_predicates(\
339
+ self_did={:?},\
340
+ def_id={:?}, \
243
341
substs={:?}, \
244
342
explicit_map={:?}, \
245
343
required_predicates={:?}, \
246
344
ignored_self_ty={:?})",
247
- def_id, substs, explicit_map, required_predicates, ignored_self_ty,
345
+ self_did , def_id, substs, explicit_map, required_predicates, ignored_self_ty,
248
346
) ;
249
- let explicit_predicates = explicit_map. explicit_predicates_of ( tcx, def_id) ;
347
+ let explicit_predicates = explicit_map. explicit_predicates_of ( tcx, self_did , def_id) ;
250
348
251
- for ( outlives_predicate, & span ) in & explicit_predicates. 0 {
349
+ for ( outlives_predicate, stack ) in & explicit_predicates. 0 {
252
350
debug ! ( "outlives_predicate = {:?}" , & outlives_predicate) ;
253
351
254
352
// Careful: If we are inferring the effects of a `dyn Trait<..>`
@@ -293,8 +391,18 @@ fn check_explicit_predicates<'tcx>(
293
391
continue ;
294
392
}
295
393
394
+ let & ( _foo_did, span) = stack. last ( ) . unwrap ( ) ;
296
395
let predicate = explicit_predicates. rebind ( * outlives_predicate) . subst ( tcx, substs) ;
297
396
debug ! ( "predicate = {:?}" , & predicate) ;
298
- insert_outlives_predicate ( tcx, predicate. 0 , predicate. 1 , span, required_predicates) ;
397
+ insert_outlives_predicate (
398
+ tcx,
399
+ predicate. 0 ,
400
+ predicate. 1 ,
401
+ // i.e. Foo, not the field ADT definition.
402
+ self_did,
403
+ span,
404
+ required_predicates,
405
+ None ,
406
+ ) ;
299
407
}
300
408
}
0 commit comments