@@ -25,11 +25,26 @@ pub enum Representability {
25
25
pub fn ty_is_representable < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , sp : Span ) -> Representability {
26
26
debug ! ( "is_type_representable: {:?}" , ty) ;
27
27
// To avoid a stack overflow when checking an enum variant or struct that
28
- // contains a different, structurally recursive type, maintain a stack
29
- // of seen types and check recursion for each of them (issues #3008, #3779).
28
+ // contains a different, structurally recursive type, maintain a stack of
29
+ // seen types and check recursion for each of them (issues #3008, #3779,
30
+ // #74224, #84611). `shadow_seen` contains the full stack and `seen` only
31
+ // the one for the current type (e.g. if we have structs A and B, B contains
32
+ // a field of type A, and we're currently looking at B, then `seen` will be
33
+ // cleared when recursing to check A, but `shadow_seen` won't, so that we
34
+ // can catch cases of mutual recursion where A also contains B).
30
35
let mut seen: Vec < Ty < ' _ > > = Vec :: new ( ) ;
36
+ let mut shadow_seen: Vec < & ' tcx ty:: AdtDef > = Vec :: new ( ) ;
31
37
let mut representable_cache = FxHashMap :: default ( ) ;
32
- let r = is_type_structurally_recursive ( tcx, sp, & mut seen, & mut representable_cache, ty) ;
38
+ let mut force_result = false ;
39
+ let r = is_type_structurally_recursive (
40
+ tcx,
41
+ sp,
42
+ & mut seen,
43
+ & mut shadow_seen,
44
+ & mut representable_cache,
45
+ ty,
46
+ & mut force_result,
47
+ ) ;
33
48
debug ! ( "is_type_representable: {:?} is {:?}" , ty, r) ;
34
49
r
35
50
}
@@ -48,21 +63,38 @@ fn are_inner_types_recursive<'tcx>(
48
63
tcx : TyCtxt < ' tcx > ,
49
64
sp : Span ,
50
65
seen : & mut Vec < Ty < ' tcx > > ,
66
+ shadow_seen : & mut Vec < & ' tcx ty:: AdtDef > ,
51
67
representable_cache : & mut FxHashMap < Ty < ' tcx > , Representability > ,
52
68
ty : Ty < ' tcx > ,
69
+ force_result : & mut bool ,
53
70
) -> Representability {
71
+ debug ! ( "are_inner_types_recursive({:?}, {:?}, {:?})" , ty, seen, shadow_seen) ;
54
72
match ty. kind ( ) {
55
73
ty:: Tuple ( ..) => {
56
74
// Find non representable
57
- fold_repr (
58
- ty. tuple_fields ( ) . map ( |ty| {
59
- is_type_structurally_recursive ( tcx, sp, seen, representable_cache, ty)
60
- } ) ,
61
- )
75
+ fold_repr ( ty. tuple_fields ( ) . map ( |ty| {
76
+ is_type_structurally_recursive (
77
+ tcx,
78
+ sp,
79
+ seen,
80
+ shadow_seen,
81
+ representable_cache,
82
+ ty,
83
+ force_result,
84
+ )
85
+ } ) )
62
86
}
63
87
// Fixed-length vectors.
64
88
// FIXME(#11924) Behavior undecided for zero-length vectors.
65
- ty:: Array ( ty, _) => is_type_structurally_recursive ( tcx, sp, seen, representable_cache, ty) ,
89
+ ty:: Array ( ty, _) => is_type_structurally_recursive (
90
+ tcx,
91
+ sp,
92
+ seen,
93
+ shadow_seen,
94
+ representable_cache,
95
+ ty,
96
+ force_result,
97
+ ) ,
66
98
ty:: Adt ( def, substs) => {
67
99
// Find non representable fields with their spans
68
100
fold_repr ( def. all_fields ( ) . map ( |field| {
@@ -76,12 +108,128 @@ fn are_inner_types_recursive<'tcx>(
76
108
Some ( hir:: Node :: Field ( field) ) => field. ty . span ,
77
109
_ => sp,
78
110
} ;
79
- match is_type_structurally_recursive ( tcx, span, seen, representable_cache, ty) {
80
- Representability :: SelfRecursive ( _) => {
81
- Representability :: SelfRecursive ( vec ! [ span] )
111
+
112
+ let mut result = None ;
113
+
114
+ // First, we check whether the field type per se is representable.
115
+ // This catches cases as in #74224 and #84611. There is a special
116
+ // case related to mutual recursion, though; consider this example:
117
+ //
118
+ // struct A<T> {
119
+ // z: T,
120
+ // x: B<T>,
121
+ // }
122
+ //
123
+ // struct B<T> {
124
+ // y: A<T>
125
+ // }
126
+ //
127
+ // Here, without the following special case, both A and B are
128
+ // ContainsRecursive, which is a problem because we only report
129
+ // errors for SelfRecursive. We fix this by detecting this special
130
+ // case (shadow_seen.first() is the type we are originally
131
+ // interested in, and if we ever encounter the same AdtDef again,
132
+ // we know that it must be SelfRecursive) and "forcibly" returning
133
+ // SelfRecursive (by setting force_result, which tells the calling
134
+ // invocations of are_inner_types_representable to forward the
135
+ // result without adjusting).
136
+ if shadow_seen. len ( ) > seen. len ( ) && shadow_seen. first ( ) == Some ( def) {
137
+ * force_result = true ;
138
+ result = Some ( Representability :: SelfRecursive ( vec ! [ span] ) ) ;
139
+ }
140
+
141
+ if result == None {
142
+ result = Some ( Representability :: Representable ) ;
143
+
144
+ // Now, we check whether the field types per se are representable, e.g.
145
+ // for struct Foo { x: Option<Foo> }, we first check whether Option<_>
146
+ // by itself is representable (which it is), and the nesting of Foo
147
+ // will be detected later. This is necessary for #74224 and #84611.
148
+
149
+ // If we have encountered an ADT definition that we have not seen
150
+ // before (no need to check them twice), recurse to see whether that
151
+ // definition is SelfRecursive. If so, we must be ContainsRecursive.
152
+ if shadow_seen. len ( ) > 1
153
+ && !shadow_seen
154
+ . iter ( )
155
+ . take ( shadow_seen. len ( ) - 1 )
156
+ . any ( |seen_def| seen_def == def)
157
+ {
158
+ let adt_def_id = def. did ;
159
+ let raw_adt_ty = tcx. type_of ( adt_def_id) ;
160
+ debug ! ( "are_inner_types_recursive: checking nested type: {:?}" , raw_adt_ty) ;
161
+
162
+ // Check independently whether the ADT is SelfRecursive. If so,
163
+ // we must be ContainsRecursive (except for the special case
164
+ // mentioned above).
165
+ let mut nested_seen: Vec < Ty < ' _ > > = vec ! [ ] ;
166
+ result = Some (
167
+ match is_type_structurally_recursive (
168
+ tcx,
169
+ span,
170
+ & mut nested_seen,
171
+ shadow_seen,
172
+ representable_cache,
173
+ raw_adt_ty,
174
+ force_result,
175
+ ) {
176
+ Representability :: SelfRecursive ( _) => {
177
+ if * force_result {
178
+ Representability :: SelfRecursive ( vec ! [ span] )
179
+ } else {
180
+ Representability :: ContainsRecursive
181
+ }
182
+ }
183
+ x => x,
184
+ } ,
185
+ ) ;
186
+ }
187
+
188
+ // We only enter the following block if the type looks representable
189
+ // so far. This is necessary for cases such as this one (#74224):
190
+ //
191
+ // struct A<T> {
192
+ // x: T,
193
+ // y: A<A<T>>,
194
+ // }
195
+ //
196
+ // struct B {
197
+ // z: A<usize>
198
+ // }
199
+ //
200
+ // When checking B, we recurse into A and check field y of type
201
+ // A<A<usize>>. We haven't seen this exact type before, so we recurse
202
+ // into A<A<usize>>, which contains, A<A<A<usize>>>, and so forth,
203
+ // ad infinitum. We can prevent this from happening by first checking
204
+ // A separately (the code above) and only checking for nested Bs if
205
+ // A actually looks representable (which it wouldn't in this example).
206
+ if result == Some ( Representability :: Representable ) {
207
+ // Now, even if the type is representable (e.g. Option<_>),
208
+ // it might still contribute to a recursive type, e.g.:
209
+ // struct Foo { x: Option<Option<Foo>> }
210
+ // These cases are handled by passing the full `seen`
211
+ // stack to is_type_structurally_recursive (instead of the
212
+ // empty `nested_seen` above):
213
+ result = Some (
214
+ match is_type_structurally_recursive (
215
+ tcx,
216
+ span,
217
+ seen,
218
+ shadow_seen,
219
+ representable_cache,
220
+ ty,
221
+ force_result,
222
+ ) {
223
+ Representability :: SelfRecursive ( _) => {
224
+ Representability :: SelfRecursive ( vec ! [ span] )
225
+ }
226
+ x => x,
227
+ } ,
228
+ ) ;
82
229
}
83
- x => x,
84
230
}
231
+
232
+ result. unwrap ( )
85
233
} ) )
86
234
}
87
235
ty:: Closure ( ..) => {
@@ -106,8 +254,10 @@ fn is_type_structurally_recursive<'tcx>(
106
254
tcx : TyCtxt < ' tcx > ,
107
255
sp : Span ,
108
256
seen : & mut Vec < Ty < ' tcx > > ,
257
+ shadow_seen : & mut Vec < & ' tcx ty:: AdtDef > ,
109
258
representable_cache : & mut FxHashMap < Ty < ' tcx > , Representability > ,
110
259
ty : Ty < ' tcx > ,
260
+ force_result : & mut bool ,
111
261
) -> Representability {
112
262
debug ! ( "is_type_structurally_recursive: {:?} {:?}" , ty, sp) ;
113
263
if let Some ( representability) = representable_cache. get ( ty) {
@@ -118,8 +268,15 @@ fn is_type_structurally_recursive<'tcx>(
118
268
return representability. clone ( ) ;
119
269
}
120
270
121
- let representability =
122
- is_type_structurally_recursive_inner ( tcx, sp, seen, representable_cache, ty) ;
271
+ let representability = is_type_structurally_recursive_inner (
272
+ tcx,
273
+ sp,
274
+ seen,
275
+ shadow_seen,
276
+ representable_cache,
277
+ ty,
278
+ force_result,
279
+ ) ;
123
280
124
281
representable_cache. insert ( ty, representability. clone ( ) ) ;
125
282
representability
@@ -129,12 +286,16 @@ fn is_type_structurally_recursive_inner<'tcx>(
129
286
tcx : TyCtxt < ' tcx > ,
130
287
sp : Span ,
131
288
seen : & mut Vec < Ty < ' tcx > > ,
289
+ shadow_seen : & mut Vec < & ' tcx ty:: AdtDef > ,
132
290
representable_cache : & mut FxHashMap < Ty < ' tcx > , Representability > ,
133
291
ty : Ty < ' tcx > ,
292
+ force_result : & mut bool ,
134
293
) -> Representability {
135
294
match ty. kind ( ) {
136
295
ty:: Adt ( def, _) => {
137
296
{
297
+ debug ! ( "is_type_structurally_recursive_inner: adt: {:?}, seen: {:?}" , ty, seen) ;
298
+
138
299
// Iterate through stack of previously seen types.
139
300
let mut iter = seen. iter ( ) ;
140
301
@@ -158,8 +319,10 @@ fn is_type_structurally_recursive_inner<'tcx>(
158
319
// will recurse infinitely for some inputs.
159
320
//
160
321
// It is important that we DO take generic parameters into account
161
- // here, so that code like this is considered SelfRecursive, not
162
- // ContainsRecursive:
322
+ // here, because nesting e.g. Options is allowed (as long as the
323
+ // definition of Option doesn't itself include an Option field, which
324
+ // would be a case of SelfRecursive above). The following, too, counts
325
+ // as SelfRecursive:
163
326
//
164
327
// struct Foo { Option<Option<Foo>> }
165
328
@@ -174,13 +337,31 @@ fn is_type_structurally_recursive_inner<'tcx>(
174
337
// For structs and enums, track all previously seen types by pushing them
175
338
// onto the 'seen' stack.
176
339
seen. push ( ty) ;
177
- let out = are_inner_types_recursive ( tcx, sp, seen, representable_cache, ty) ;
340
+ shadow_seen. push ( def) ;
341
+ let out = are_inner_types_recursive (
342
+ tcx,
343
+ sp,
344
+ seen,
345
+ shadow_seen,
346
+ representable_cache,
347
+ ty,
348
+ force_result,
349
+ ) ;
350
+ shadow_seen. pop ( ) ;
178
351
seen. pop ( ) ;
179
352
out
180
353
}
181
354
_ => {
182
355
// No need to push in other cases.
183
- are_inner_types_recursive ( tcx, sp, seen, representable_cache, ty)
356
+ are_inner_types_recursive (
357
+ tcx,
358
+ sp,
359
+ seen,
360
+ shadow_seen,
361
+ representable_cache,
362
+ ty,
363
+ force_result,
364
+ )
184
365
}
185
366
}
186
367
}
0 commit comments