1
1
use std:: cmp:: Ordering ;
2
2
3
+ use rustc_type_ir:: data_structures:: HashMap ;
3
4
use rustc_type_ir:: fold:: { TypeFoldable , TypeFolder , TypeSuperFoldable } ;
4
5
use rustc_type_ir:: inherent:: * ;
5
6
use rustc_type_ir:: visit:: TypeVisitableExt ;
@@ -44,8 +45,15 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
44
45
canonicalize_mode : CanonicalizeMode ,
45
46
46
47
variables : & ' a mut Vec < I :: GenericArg > ,
48
+ variable_lookup_table : HashMap < I :: GenericArg , usize > ,
49
+
47
50
primitive_var_infos : Vec < CanonicalVarInfo < I > > ,
48
51
binder_index : ty:: DebruijnIndex ,
52
+
53
+ /// We only use the debruijn index during lookup as all other fields
54
+ /// should not be impacted by whether a type is folded once or multiple
55
+ /// times.
56
+ cache : HashMap < ( ty:: DebruijnIndex , I :: Ty ) , I :: Ty > ,
49
57
}
50
58
51
59
impl < ' a , D : SolverDelegate < Interner = I > , I : Interner > Canonicalizer < ' a , D , I > {
@@ -60,12 +68,14 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
60
68
canonicalize_mode,
61
69
62
70
variables,
71
+ variable_lookup_table : Default :: default ( ) ,
63
72
primitive_var_infos : Vec :: new ( ) ,
64
73
binder_index : ty:: INNERMOST ,
74
+
75
+ cache : Default :: default ( ) ,
65
76
} ;
66
77
67
78
let value = value. fold_with ( & mut canonicalizer) ;
68
- // FIXME: Restore these assertions. Should we uplift type flags?
69
79
assert ! ( !value. has_infer( ) , "unexpected infer in {value:?}" ) ;
70
80
assert ! ( !value. has_placeholders( ) , "unexpected placeholders in {value:?}" ) ;
71
81
@@ -75,6 +85,37 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
75
85
Canonical { defining_opaque_types, max_universe, variables, value }
76
86
}
77
87
88
+ fn get_or_insert_bound_var (
89
+ & mut self ,
90
+ arg : impl Into < I :: GenericArg > ,
91
+ canonical_var_info : CanonicalVarInfo < I > ,
92
+ ) -> ty:: BoundVar {
93
+ // FIXME: 16 is made up and arbitrary. We should look at some
94
+ // perf data here.
95
+ let arg = arg. into ( ) ;
96
+ let idx = if self . variables . len ( ) > 16 {
97
+ if self . variable_lookup_table . is_empty ( ) {
98
+ self . variable_lookup_table . extend ( self . variables . iter ( ) . copied ( ) . zip ( 0 ..) ) ;
99
+ }
100
+
101
+ * self . variable_lookup_table . entry ( arg) . or_insert_with ( || {
102
+ let var = self . variables . len ( ) ;
103
+ self . variables . push ( arg) ;
104
+ self . primitive_var_infos . push ( canonical_var_info) ;
105
+ var
106
+ } )
107
+ } else {
108
+ self . variables . iter ( ) . position ( |& v| v == arg) . unwrap_or_else ( || {
109
+ let var = self . variables . len ( ) ;
110
+ self . variables . push ( arg) ;
111
+ self . primitive_var_infos . push ( canonical_var_info) ;
112
+ var
113
+ } )
114
+ } ;
115
+
116
+ ty:: BoundVar :: from ( idx)
117
+ }
118
+
78
119
fn finalize ( self ) -> ( ty:: UniverseIndex , I :: CanonicalVars ) {
79
120
let mut var_infos = self . primitive_var_infos ;
80
121
// See the rustc-dev-guide section about how we deal with universes
@@ -124,8 +165,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
124
165
// - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
125
166
// - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
126
167
//
127
- // This algorithm runs in `O(n² )` where `n` is the number of different universe
128
- // indices in the input . This should be fine as `n` is expected to be small.
168
+ // This algorithm runs in `O(mn )` where `n` is the number of different universes and
169
+ // `m` the number of variables . This should be fine as both are expected to be small.
129
170
let mut curr_compressed_uv = ty:: UniverseIndex :: ROOT ;
130
171
let mut existential_in_new_uv = None ;
131
172
let mut next_orig_uv = Some ( ty:: UniverseIndex :: ROOT ) ;
@@ -185,14 +226,16 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
185
226
for var in var_infos. iter_mut ( ) {
186
227
// We simply put all regions from the input into the highest
187
228
// compressed universe, so we only deal with them at the end.
188
- if !var. is_region ( ) && is_existential == var. is_existential ( ) {
189
- update_uv ( var, orig_uv, is_existential)
229
+ if !var. is_region ( ) {
230
+ if is_existential == var. is_existential ( ) {
231
+ update_uv ( var, orig_uv, is_existential)
232
+ }
190
233
}
191
234
}
192
235
}
193
236
}
194
237
195
- // We uniquify regions and always put them into their own universe
238
+ // We put all regions into a separate universe.
196
239
let mut first_region = true ;
197
240
for var in var_infos. iter_mut ( ) {
198
241
if var. is_region ( ) {
@@ -208,93 +251,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
208
251
let var_infos = self . delegate . cx ( ) . mk_canonical_var_infos ( & var_infos) ;
209
252
( curr_compressed_uv, var_infos)
210
253
}
211
- }
212
-
213
- impl < D : SolverDelegate < Interner = I > , I : Interner > TypeFolder < I > for Canonicalizer < ' _ , D , I > {
214
- fn cx ( & self ) -> I {
215
- self . delegate . cx ( )
216
- }
217
-
218
- fn fold_binder < T > ( & mut self , t : ty:: Binder < I , T > ) -> ty:: Binder < I , T >
219
- where
220
- T : TypeFoldable < I > ,
221
- {
222
- self . binder_index . shift_in ( 1 ) ;
223
- let t = t. super_fold_with ( self ) ;
224
- self . binder_index . shift_out ( 1 ) ;
225
- t
226
- }
227
-
228
- fn fold_region ( & mut self , r : I :: Region ) -> I :: Region {
229
- let kind = match r. kind ( ) {
230
- ty:: ReBound ( ..) => return r,
231
-
232
- // We may encounter `ReStatic` in item signatures or the hidden type
233
- // of an opaque. `ReErased` should only be encountered in the hidden
234
- // type of an opaque for regions that are ignored for the purposes of
235
- // captures.
236
- //
237
- // FIXME: We should investigate the perf implications of not uniquifying
238
- // `ReErased`. We may be able to short-circuit registering region
239
- // obligations if we encounter a `ReErased` on one side, for example.
240
- ty:: ReStatic | ty:: ReErased | ty:: ReError ( _) => match self . canonicalize_mode {
241
- CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
242
- CanonicalizeMode :: Response { .. } => return r,
243
- } ,
244
-
245
- ty:: ReEarlyParam ( _) | ty:: ReLateParam ( _) => match self . canonicalize_mode {
246
- CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
247
- CanonicalizeMode :: Response { .. } => {
248
- panic ! ( "unexpected region in response: {r:?}" )
249
- }
250
- } ,
251
-
252
- ty:: RePlaceholder ( placeholder) => match self . canonicalize_mode {
253
- // We canonicalize placeholder regions as existentials in query inputs.
254
- CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
255
- CanonicalizeMode :: Response { max_input_universe } => {
256
- // If we have a placeholder region inside of a query, it must be from
257
- // a new universe.
258
- if max_input_universe. can_name ( placeholder. universe ( ) ) {
259
- panic ! ( "new placeholder in universe {max_input_universe:?}: {r:?}" ) ;
260
- }
261
- CanonicalVarKind :: PlaceholderRegion ( placeholder)
262
- }
263
- } ,
264
-
265
- ty:: ReVar ( vid) => {
266
- assert_eq ! (
267
- self . delegate. opportunistic_resolve_lt_var( vid) ,
268
- r,
269
- "region vid should have been resolved fully before canonicalization"
270
- ) ;
271
- match self . canonicalize_mode {
272
- CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
273
- CanonicalizeMode :: Response { .. } => {
274
- CanonicalVarKind :: Region ( self . delegate . universe_of_lt ( vid) . unwrap ( ) )
275
- }
276
- }
277
- }
278
- } ;
279
-
280
- let existing_bound_var = match self . canonicalize_mode {
281
- CanonicalizeMode :: Input => None ,
282
- CanonicalizeMode :: Response { .. } => {
283
- self . variables . iter ( ) . position ( |& v| v == r. into ( ) ) . map ( ty:: BoundVar :: from)
284
- }
285
- } ;
286
-
287
- let var = existing_bound_var. unwrap_or_else ( || {
288
- let var = ty:: BoundVar :: from ( self . variables . len ( ) ) ;
289
- self . variables . push ( r. into ( ) ) ;
290
- self . primitive_var_infos . push ( CanonicalVarInfo { kind } ) ;
291
- var
292
- } ) ;
293
254
294
- Region :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
295
- }
296
-
297
- fn fold_ty ( & mut self , t : I :: Ty ) -> I :: Ty {
255
+ fn cached_fold_ty ( & mut self , t : I :: Ty ) -> I :: Ty {
298
256
let kind = match t. kind ( ) {
299
257
ty:: Infer ( i) => match i {
300
258
ty:: TyVar ( vid) => {
@@ -368,20 +326,98 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
368
326
| ty:: Tuple ( _)
369
327
| ty:: Alias ( _, _)
370
328
| ty:: Bound ( _, _)
371
- | ty:: Error ( _) => return t. super_fold_with ( self ) ,
329
+ | ty:: Error ( _) => {
330
+ return t. super_fold_with ( self ) ;
331
+ }
372
332
} ;
373
333
374
- let var = ty:: BoundVar :: from (
375
- self . variables . iter ( ) . position ( |& v| v == t. into ( ) ) . unwrap_or_else ( || {
376
- let var = self . variables . len ( ) ;
377
- self . variables . push ( t. into ( ) ) ;
378
- self . primitive_var_infos . push ( CanonicalVarInfo { kind } ) ;
379
- var
380
- } ) ,
381
- ) ;
334
+ let var = self . get_or_insert_bound_var ( t, CanonicalVarInfo { kind } ) ;
382
335
383
336
Ty :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
384
337
}
338
+ }
339
+
340
+ impl < D : SolverDelegate < Interner = I > , I : Interner > TypeFolder < I > for Canonicalizer < ' _ , D , I > {
341
+ fn cx ( & self ) -> I {
342
+ self . delegate . cx ( )
343
+ }
344
+
345
+ fn fold_binder < T > ( & mut self , t : ty:: Binder < I , T > ) -> ty:: Binder < I , T >
346
+ where
347
+ T : TypeFoldable < I > ,
348
+ {
349
+ self . binder_index . shift_in ( 1 ) ;
350
+ let t = t. super_fold_with ( self ) ;
351
+ self . binder_index . shift_out ( 1 ) ;
352
+ t
353
+ }
354
+
355
+ fn fold_region ( & mut self , r : I :: Region ) -> I :: Region {
356
+ let kind = match r. kind ( ) {
357
+ ty:: ReBound ( ..) => return r,
358
+
359
+ // We may encounter `ReStatic` in item signatures or the hidden type
360
+ // of an opaque. `ReErased` should only be encountered in the hidden
361
+ // type of an opaque for regions that are ignored for the purposes of
362
+ // captures.
363
+ //
364
+ // FIXME: We should investigate the perf implications of not uniquifying
365
+ // `ReErased`. We may be able to short-circuit registering region
366
+ // obligations if we encounter a `ReErased` on one side, for example.
367
+ ty:: ReStatic | ty:: ReErased | ty:: ReError ( _) => match self . canonicalize_mode {
368
+ CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
369
+ CanonicalizeMode :: Response { .. } => return r,
370
+ } ,
371
+
372
+ ty:: ReEarlyParam ( _) | ty:: ReLateParam ( _) => match self . canonicalize_mode {
373
+ CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
374
+ CanonicalizeMode :: Response { .. } => {
375
+ panic ! ( "unexpected region in response: {r:?}" )
376
+ }
377
+ } ,
378
+
379
+ ty:: RePlaceholder ( placeholder) => match self . canonicalize_mode {
380
+ // We canonicalize placeholder regions as existentials in query inputs.
381
+ CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
382
+ CanonicalizeMode :: Response { max_input_universe } => {
383
+ // If we have a placeholder region inside of a query, it must be from
384
+ // a new universe.
385
+ if max_input_universe. can_name ( placeholder. universe ( ) ) {
386
+ panic ! ( "new placeholder in universe {max_input_universe:?}: {r:?}" ) ;
387
+ }
388
+ CanonicalVarKind :: PlaceholderRegion ( placeholder)
389
+ }
390
+ } ,
391
+
392
+ ty:: ReVar ( vid) => {
393
+ assert_eq ! (
394
+ self . delegate. opportunistic_resolve_lt_var( vid) ,
395
+ r,
396
+ "region vid should have been resolved fully before canonicalization"
397
+ ) ;
398
+ match self . canonicalize_mode {
399
+ CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
400
+ CanonicalizeMode :: Response { .. } => {
401
+ CanonicalVarKind :: Region ( self . delegate . universe_of_lt ( vid) . unwrap ( ) )
402
+ }
403
+ }
404
+ }
405
+ } ;
406
+
407
+ let var = self . get_or_insert_bound_var ( r, CanonicalVarInfo { kind } ) ;
408
+
409
+ Region :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
410
+ }
411
+
412
+ fn fold_ty ( & mut self , t : I :: Ty ) -> I :: Ty {
413
+ if let Some ( & ty) = self . cache . get ( & ( self . binder_index , t) ) {
414
+ ty
415
+ } else {
416
+ let res = self . cached_fold_ty ( t) ;
417
+ assert ! ( self . cache. insert( ( self . binder_index, t) , res) . is_none( ) ) ;
418
+ res
419
+ }
420
+ }
385
421
386
422
fn fold_const ( & mut self , c : I :: Const ) -> I :: Const {
387
423
let kind = match c. kind ( ) {
@@ -419,14 +455,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
419
455
| ty:: ConstKind :: Expr ( _) => return c. super_fold_with ( self ) ,
420
456
} ;
421
457
422
- let var = ty:: BoundVar :: from (
423
- self . variables . iter ( ) . position ( |& v| v == c. into ( ) ) . unwrap_or_else ( || {
424
- let var = self . variables . len ( ) ;
425
- self . variables . push ( c. into ( ) ) ;
426
- self . primitive_var_infos . push ( CanonicalVarInfo { kind } ) ;
427
- var
428
- } ) ,
429
- ) ;
458
+ let var = self . get_or_insert_bound_var ( c, CanonicalVarInfo { kind } ) ;
430
459
431
460
Const :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
432
461
}
0 commit comments