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,12 @@ 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
+ cache : HashMap < ( ty:: DebruijnIndex , I :: Ty ) , I :: Ty > ,
49
54
}
50
55
51
56
impl < ' a , D : SolverDelegate < Interner = I > , I : Interner > Canonicalizer < ' a , D , I > {
@@ -60,12 +65,14 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
60
65
canonicalize_mode,
61
66
62
67
variables,
68
+ variable_lookup_table : Default :: default ( ) ,
63
69
primitive_var_infos : Vec :: new ( ) ,
64
70
binder_index : ty:: INNERMOST ,
71
+
72
+ cache : Default :: default ( ) ,
65
73
} ;
66
74
67
75
let value = value. fold_with ( & mut canonicalizer) ;
68
- // FIXME: Restore these assertions. Should we uplift type flags?
69
76
assert ! ( !value. has_infer( ) , "unexpected infer in {value:?}" ) ;
70
77
assert ! ( !value. has_placeholders( ) , "unexpected placeholders in {value:?}" ) ;
71
78
@@ -75,6 +82,37 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
75
82
Canonical { defining_opaque_types, max_universe, variables, value }
76
83
}
77
84
85
+ fn get_or_insert_bound_var (
86
+ & mut self ,
87
+ arg : impl Into < I :: GenericArg > ,
88
+ canonical_var_info : CanonicalVarInfo < I > ,
89
+ ) -> ty:: BoundVar {
90
+ // FIXME: 16 is made up and arbitrary. We should look at some
91
+ // perf data here.
92
+ let arg = arg. into ( ) ;
93
+ let idx = if self . variables . len ( ) > 16 {
94
+ if self . variable_lookup_table . is_empty ( ) {
95
+ self . variable_lookup_table . extend ( self . variables . iter ( ) . copied ( ) . zip ( 0 ..) ) ;
96
+ }
97
+
98
+ * self . variable_lookup_table . entry ( arg) . or_insert_with ( || {
99
+ let var = self . variables . len ( ) ;
100
+ self . variables . push ( arg) ;
101
+ self . primitive_var_infos . push ( canonical_var_info) ;
102
+ var
103
+ } )
104
+ } else {
105
+ self . variables . iter ( ) . position ( |& v| v == arg) . unwrap_or_else ( || {
106
+ let var = self . variables . len ( ) ;
107
+ self . variables . push ( arg) ;
108
+ self . primitive_var_infos . push ( canonical_var_info) ;
109
+ var
110
+ } )
111
+ } ;
112
+
113
+ ty:: BoundVar :: from ( idx)
114
+ }
115
+
78
116
fn finalize ( self ) -> ( ty:: UniverseIndex , I :: CanonicalVars ) {
79
117
let mut var_infos = self . primitive_var_infos ;
80
118
// See the rustc-dev-guide section about how we deal with universes
@@ -124,8 +162,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
124
162
// - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
125
163
// - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
126
164
//
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.
165
+ // This algorithm runs in `O(mn )` where `n` is the number of different universes and
166
+ // `m` the number of variables . This should be fine as both are expected to be small.
129
167
let mut curr_compressed_uv = ty:: UniverseIndex :: ROOT ;
130
168
let mut existential_in_new_uv = None ;
131
169
let mut next_orig_uv = Some ( ty:: UniverseIndex :: ROOT ) ;
@@ -185,14 +223,16 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
185
223
for var in var_infos. iter_mut ( ) {
186
224
// We simply put all regions from the input into the highest
187
225
// 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)
226
+ if !var. is_region ( ) {
227
+ if is_existential == var. is_existential ( ) {
228
+ update_uv ( var, orig_uv, is_existential)
229
+ }
190
230
}
191
231
}
192
232
}
193
233
}
194
234
195
- // We uniquify regions and always put them into their own universe
235
+ // We put all regions into a separate universe.
196
236
let mut first_region = true ;
197
237
for var in var_infos. iter_mut ( ) {
198
238
if var. is_region ( ) {
@@ -208,93 +248,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
208
248
let var_infos = self . delegate . cx ( ) . mk_canonical_var_infos ( & var_infos) ;
209
249
( curr_compressed_uv, var_infos)
210
250
}
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
251
294
- Region :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
295
- }
296
-
297
- fn fold_ty ( & mut self , t : I :: Ty ) -> I :: Ty {
252
+ fn cached_fold_ty ( & mut self , t : I :: Ty ) -> I :: Ty {
298
253
let kind = match t. kind ( ) {
299
254
ty:: Infer ( i) => match i {
300
255
ty:: TyVar ( vid) => {
@@ -368,20 +323,98 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
368
323
| ty:: Tuple ( _)
369
324
| ty:: Alias ( _, _)
370
325
| ty:: Bound ( _, _)
371
- | ty:: Error ( _) => return t. super_fold_with ( self ) ,
326
+ | ty:: Error ( _) => {
327
+ return t. super_fold_with ( self ) ;
328
+ }
372
329
} ;
373
330
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
- ) ;
331
+ let var = self . get_or_insert_bound_var ( t, CanonicalVarInfo { kind } ) ;
382
332
383
333
Ty :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
384
334
}
335
+ }
336
+
337
+ impl < D : SolverDelegate < Interner = I > , I : Interner > TypeFolder < I > for Canonicalizer < ' _ , D , I > {
338
+ fn cx ( & self ) -> I {
339
+ self . delegate . cx ( )
340
+ }
341
+
342
+ fn fold_binder < T > ( & mut self , t : ty:: Binder < I , T > ) -> ty:: Binder < I , T >
343
+ where
344
+ T : TypeFoldable < I > ,
345
+ {
346
+ self . binder_index . shift_in ( 1 ) ;
347
+ let t = t. super_fold_with ( self ) ;
348
+ self . binder_index . shift_out ( 1 ) ;
349
+ t
350
+ }
351
+
352
+ fn fold_region ( & mut self , r : I :: Region ) -> I :: Region {
353
+ let kind = match r. kind ( ) {
354
+ ty:: ReBound ( ..) => return r,
355
+
356
+ // We may encounter `ReStatic` in item signatures or the hidden type
357
+ // of an opaque. `ReErased` should only be encountered in the hidden
358
+ // type of an opaque for regions that are ignored for the purposes of
359
+ // captures.
360
+ //
361
+ // FIXME: We should investigate the perf implications of not uniquifying
362
+ // `ReErased`. We may be able to short-circuit registering region
363
+ // obligations if we encounter a `ReErased` on one side, for example.
364
+ ty:: ReStatic | ty:: ReErased | ty:: ReError ( _) => match self . canonicalize_mode {
365
+ CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
366
+ CanonicalizeMode :: Response { .. } => return r,
367
+ } ,
368
+
369
+ ty:: ReEarlyParam ( _) | ty:: ReLateParam ( _) => match self . canonicalize_mode {
370
+ CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
371
+ CanonicalizeMode :: Response { .. } => {
372
+ panic ! ( "unexpected region in response: {r:?}" )
373
+ }
374
+ } ,
375
+
376
+ ty:: RePlaceholder ( placeholder) => match self . canonicalize_mode {
377
+ // We canonicalize placeholder regions as existentials in query inputs.
378
+ CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
379
+ CanonicalizeMode :: Response { max_input_universe } => {
380
+ // If we have a placeholder region inside of a query, it must be from
381
+ // a new universe.
382
+ if max_input_universe. can_name ( placeholder. universe ( ) ) {
383
+ panic ! ( "new placeholder in universe {max_input_universe:?}: {r:?}" ) ;
384
+ }
385
+ CanonicalVarKind :: PlaceholderRegion ( placeholder)
386
+ }
387
+ } ,
388
+
389
+ ty:: ReVar ( vid) => {
390
+ assert_eq ! (
391
+ self . delegate. opportunistic_resolve_lt_var( vid) ,
392
+ r,
393
+ "region vid should have been resolved fully before canonicalization"
394
+ ) ;
395
+ match self . canonicalize_mode {
396
+ CanonicalizeMode :: Input => CanonicalVarKind :: Region ( ty:: UniverseIndex :: ROOT ) ,
397
+ CanonicalizeMode :: Response { .. } => {
398
+ CanonicalVarKind :: Region ( self . delegate . universe_of_lt ( vid) . unwrap ( ) )
399
+ }
400
+ }
401
+ }
402
+ } ;
403
+
404
+ let var = self . get_or_insert_bound_var ( r, CanonicalVarInfo { kind } ) ;
405
+
406
+ Region :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
407
+ }
408
+
409
+ fn fold_ty ( & mut self , t : I :: Ty ) -> I :: Ty {
410
+ if let Some ( & ty) = self . cache . get ( & ( self . binder_index , t) ) {
411
+ ty
412
+ } else {
413
+ let res = self . cached_fold_ty ( t) ;
414
+ assert ! ( self . cache. insert( ( self . binder_index, t) , res) . is_none( ) ) ;
415
+ res
416
+ }
417
+ }
385
418
386
419
fn fold_const ( & mut self , c : I :: Const ) -> I :: Const {
387
420
let kind = match c. kind ( ) {
@@ -419,14 +452,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
419
452
| ty:: ConstKind :: Expr ( _) => return c. super_fold_with ( self ) ,
420
453
} ;
421
454
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
- ) ;
455
+ let var = self . get_or_insert_bound_var ( c, CanonicalVarInfo { kind } ) ;
430
456
431
457
Const :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
432
458
}
0 commit comments