11use std:: cmp:: Ordering ;
22
3+ use rustc_type_ir:: data_structures:: HashMap ;
34use rustc_type_ir:: fold:: { TypeFoldable , TypeFolder , TypeSuperFoldable } ;
45use rustc_type_ir:: inherent:: * ;
56use rustc_type_ir:: visit:: TypeVisitableExt ;
@@ -44,8 +45,15 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
4445 canonicalize_mode : CanonicalizeMode ,
4546
4647 variables : & ' a mut Vec < I :: GenericArg > ,
48+ variable_lookup_table : HashMap < I :: GenericArg , usize > ,
49+
4750 primitive_var_infos : Vec < CanonicalVarInfo < I > > ,
4851 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 > ,
4957}
5058
5159impl < ' 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> {
6068 canonicalize_mode,
6169
6270 variables,
71+ variable_lookup_table : Default :: default ( ) ,
6372 primitive_var_infos : Vec :: new ( ) ,
6473 binder_index : ty:: INNERMOST ,
74+
75+ cache : Default :: default ( ) ,
6576 } ;
6677
6778 let value = value. fold_with ( & mut canonicalizer) ;
68- // FIXME: Restore these assertions. Should we uplift type flags?
6979 assert ! ( !value. has_infer( ) , "unexpected infer in {value:?}" ) ;
7080 assert ! ( !value. has_placeholders( ) , "unexpected placeholders in {value:?}" ) ;
7181
@@ -75,6 +85,37 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
7585 Canonical { defining_opaque_types, max_universe, variables, value }
7686 }
7787
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+
78119 fn finalize ( self ) -> ( ty:: UniverseIndex , I :: CanonicalVars ) {
79120 let mut var_infos = self . primitive_var_infos ;
80121 // 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> {
124165 // - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
125166 // - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
126167 //
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.
129170 let mut curr_compressed_uv = ty:: UniverseIndex :: ROOT ;
130171 let mut existential_in_new_uv = None ;
131172 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> {
185226 for var in var_infos. iter_mut ( ) {
186227 // We simply put all regions from the input into the highest
187228 // 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+ }
190233 }
191234 }
192235 }
193236 }
194237
195- // We uniquify regions and always put them into their own universe
238+ // We put all regions into a separate universe.
196239 let mut first_region = true ;
197240 for var in var_infos. iter_mut ( ) {
198241 if var. is_region ( ) {
@@ -208,93 +251,8 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
208251 let var_infos = self . delegate . cx ( ) . mk_canonical_var_infos ( & var_infos) ;
209252 ( curr_compressed_uv, var_infos)
210253 }
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- } ) ;
293254
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 {
298256 let kind = match t. kind ( ) {
299257 ty:: Infer ( i) => match i {
300258 ty:: TyVar ( vid) => {
@@ -368,20 +326,98 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
368326 | ty:: Tuple ( _)
369327 | ty:: Alias ( _, _)
370328 | ty:: Bound ( _, _)
371- | ty:: Error ( _) => return t. super_fold_with ( self ) ,
329+ | ty:: Error ( _) => {
330+ return t. super_fold_with ( self ) ;
331+ }
372332 } ;
373333
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 } ) ;
382335
383336 Ty :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
384337 }
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+ }
385421
386422 fn fold_const ( & mut self , c : I :: Const ) -> I :: Const {
387423 let kind = match c. kind ( ) {
@@ -419,14 +455,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
419455 | ty:: ConstKind :: Expr ( _) => return c. super_fold_with ( self ) ,
420456 } ;
421457
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 } ) ;
430459
431460 Const :: new_anon_bound ( self . cx ( ) , self . binder_index , var)
432461 }
0 commit comments