11use std:: cmp:: Ordering ;
22
3+ use rustc_data_structures:: fx:: FxHashMap ;
34use rustc_type_ir:: fold:: { TypeFoldable , TypeFolder , TypeSuperFoldable } ;
45use rustc_type_ir:: inherent:: * ;
56use rustc_type_ir:: visit:: TypeVisitableExt ;
@@ -42,6 +43,8 @@ pub struct Canonicalizer<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
4243 canonicalize_mode : CanonicalizeMode ,
4344
4445 variables : & ' a mut Vec < I :: GenericArg > ,
46+ variable_lookup_table : FxHashMap < I :: GenericArg , usize > ,
47+
4548 primitive_var_infos : Vec < CanonicalVarInfo < I > > ,
4649 binder_index : ty:: DebruijnIndex ,
4750}
@@ -58,6 +61,7 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infc
5861 canonicalize_mode,
5962
6063 variables,
64+ variable_lookup_table : Default :: default ( ) ,
6165 primitive_var_infos : Vec :: new ( ) ,
6266 binder_index : ty:: INNERMOST ,
6367 } ;
@@ -73,6 +77,37 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infc
7377 Canonical { defining_opaque_types, max_universe, variables, value }
7478 }
7579
80+ fn get_or_insert_bound_var (
81+ & mut self ,
82+ arg : impl Into < I :: GenericArg > ,
83+ canonical_var_info : CanonicalVarInfo < I > ,
84+ ) -> ty:: BoundVar {
85+ // FIXME: 16 is made up and arbitrary. We should look at some
86+ // perf data here.
87+ let arg = arg. into ( ) ;
88+ let idx = if self . variables . len ( ) > 16 {
89+ if self . variable_lookup_table . is_empty ( ) {
90+ self . variable_lookup_table . extend ( self . variables . iter ( ) . copied ( ) . zip ( 0 ..) ) ;
91+ }
92+
93+ * self . variable_lookup_table . entry ( arg) . or_insert_with ( || {
94+ let var = self . variables . len ( ) ;
95+ self . variables . push ( arg) ;
96+ self . primitive_var_infos . push ( canonical_var_info) ;
97+ var
98+ } )
99+ } else {
100+ self . variables . iter ( ) . position ( |& v| v == arg) . unwrap_or_else ( || {
101+ let var = self . variables . len ( ) ;
102+ self . variables . push ( arg) ;
103+ self . primitive_var_infos . push ( canonical_var_info) ;
104+ var
105+ } )
106+ } ;
107+
108+ ty:: BoundVar :: from ( idx)
109+ }
110+
76111 fn finalize ( self ) -> ( ty:: UniverseIndex , I :: CanonicalVars ) {
77112 let mut var_infos = self . primitive_var_infos ;
78113 // See the rustc-dev-guide section about how we deal with universes
@@ -122,8 +157,8 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infc
122157 // - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
123158 // - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
124159 //
125- // This algorithm runs in `O(n² )` where `n` is the number of different universe
126- // indices in the input . This should be fine as `n` is expected to be small.
160+ // This algorithm runs in `O(mn )` where `n` is the number of different universes and
161+ // `m` the number of variables . This should be fine as both are expected to be small.
127162 let mut curr_compressed_uv = ty:: UniverseIndex :: ROOT ;
128163 let mut existential_in_new_uv = None ;
129164 let mut next_orig_uv = Some ( ty:: UniverseIndex :: ROOT ) ;
@@ -279,20 +314,20 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
279314 }
280315 } ;
281316
282- let existing_bound_var = match self . canonicalize_mode {
283- CanonicalizeMode :: Input => None ,
317+ let var = match self . canonicalize_mode {
318+ CanonicalizeMode :: Input => {
319+ // It's fine to not add `r` to the lookup table, as we will
320+ // never lookup regions when canonicalizing inputs.
321+ let var = ty:: BoundVar :: from ( self . variables . len ( ) ) ;
322+ self . variables . push ( r. into ( ) ) ;
323+ self . primitive_var_infos . push ( CanonicalVarInfo { kind } ) ;
324+ var
325+ }
284326 CanonicalizeMode :: Response { .. } => {
285- self . variables . iter ( ) . position ( | & v| v == r . into ( ) ) . map ( ty :: BoundVar :: from )
327+ self . get_or_insert_bound_var ( r , CanonicalVarInfo { kind } )
286328 }
287329 } ;
288330
289- let var = existing_bound_var. unwrap_or_else ( || {
290- let var = ty:: BoundVar :: from ( self . variables . len ( ) ) ;
291- self . variables . push ( r. into ( ) ) ;
292- self . primitive_var_infos . push ( CanonicalVarInfo { kind } ) ;
293- var
294- } ) ;
295-
296331 Region :: new_anon_bound ( self . interner ( ) , self . binder_index , var)
297332 }
298333
@@ -373,14 +408,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
373408 | ty:: Error ( _) => return t. super_fold_with ( self ) ,
374409 } ;
375410
376- let var = ty:: BoundVar :: from (
377- self . variables . iter ( ) . position ( |& v| v == t. into ( ) ) . unwrap_or_else ( || {
378- let var = self . variables . len ( ) ;
379- self . variables . push ( t. into ( ) ) ;
380- self . primitive_var_infos . push ( CanonicalVarInfo { kind } ) ;
381- var
382- } ) ,
383- ) ;
411+ let var = self . get_or_insert_bound_var ( t, CanonicalVarInfo { kind } ) ;
384412
385413 Ty :: new_anon_bound ( self . interner ( ) , self . binder_index , var)
386414 }
@@ -431,14 +459,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
431459 | ty:: ConstKind :: Expr ( _) => return c. super_fold_with ( self ) ,
432460 } ;
433461
434- let var = ty:: BoundVar :: from (
435- self . variables . iter ( ) . position ( |& v| v == c. into ( ) ) . unwrap_or_else ( || {
436- let var = self . variables . len ( ) ;
437- self . variables . push ( c. into ( ) ) ;
438- self . primitive_var_infos . push ( CanonicalVarInfo { kind } ) ;
439- var
440- } ) ,
441- ) ;
462+ let var = self . get_or_insert_bound_var ( c, CanonicalVarInfo { kind } ) ;
442463
443464 Const :: new_anon_bound ( self . interner ( ) , self . binder_index , var, ty)
444465 }
0 commit comments