@@ -257,10 +257,46 @@ impl<'tcx> TyCtxt<'tcx> {
257257 false
258258 }
259259
260- /// Returns the deeply last field of nested structures, or the same type,
261- /// if not a structure at all. Corresponds to the only possible unsized
262- /// field, and its type can be used to determine unsizing strategy.
263- pub fn struct_tail ( self , mut ty : Ty < ' tcx > ) -> Ty < ' tcx > {
260+ /// Attempts to returns the deeply last field of nested structures, but
261+ /// does not apply any normalization in its search. Returns the same type
262+ /// if input `ty` is not a structure at all.
263+ pub fn struct_tail_without_normalization ( self , ty : Ty < ' tcx > ) -> Ty < ' tcx >
264+ {
265+ let tcx = self ;
266+ tcx. struct_tail_with_normalize ( ty, |ty| ty)
267+ }
268+
269+ /// Returns the deeply last field of nested structures, or the same type if
270+ /// not a structure at all. Corresponds to the only possible unsized field,
271+ /// and its type can be used to determine unsizing strategy.
272+ ///
273+ /// Should only be called if `ty` has no inference variables and does not
274+ /// need its lifetimes preserved (e.g. as part of codegen); otherwise
275+ /// normalization attempt may cause compiler bugs.
276+ pub fn struct_tail_erasing_lifetimes ( self ,
277+ ty : Ty < ' tcx > ,
278+ param_env : ty:: ParamEnv < ' tcx > )
279+ -> Ty < ' tcx >
280+ {
281+ let tcx = self ;
282+ tcx. struct_tail_with_normalize ( ty, |ty| tcx. normalize_erasing_regions ( param_env, ty) )
283+ }
284+
285+ /// Returns the deeply last field of nested structures, or the same type if
286+ /// not a structure at all. Corresponds to the only possible unsized field,
287+ /// and its type can be used to determine unsizing strategy.
288+ ///
289+ /// This is parameterized over the normalization strategy (i.e. how to
290+ /// handle `<T as Trait>::Assoc` and `impl Trait`); pass the identity
291+ /// function to indicate no normalization should take place.
292+ ///
293+ /// See also `struct_tail_erasing_lifetimes`, which is suitable for use
294+ /// during codegen.
295+ pub fn struct_tail_with_normalize ( self ,
296+ mut ty : Ty < ' tcx > ,
297+ normalize : impl Fn ( Ty < ' tcx > ) -> Ty < ' tcx > )
298+ -> Ty < ' tcx >
299+ {
264300 loop {
265301 match ty. sty {
266302 ty:: Adt ( def, substs) => {
@@ -281,6 +317,15 @@ impl<'tcx> TyCtxt<'tcx> {
281317 }
282318 }
283319
320+ ty:: Projection ( _) | ty:: Opaque ( ..) => {
321+ let normalized = normalize ( ty) ;
322+ if ty == normalized {
323+ return ty;
324+ } else {
325+ ty = normalized;
326+ }
327+ }
328+
284329 _ => {
285330 break ;
286331 }
@@ -294,10 +339,35 @@ impl<'tcx> TyCtxt<'tcx> {
294339 /// structure definitions.
295340 /// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, Trait)`,
296341 /// whereas struct_tail produces `T`, and `Trait`, respectively.
297- pub fn struct_lockstep_tails ( self ,
298- source : Ty < ' tcx > ,
299- target : Ty < ' tcx > )
300- -> ( Ty < ' tcx > , Ty < ' tcx > ) {
342+ ///
343+ /// Should only be called if the types have no inference variables and do
344+ /// not need their lifetimes preserved (e.g. as part of codegen); otherwise
345+ /// normalization attempt may cause compiler bugs.
346+ pub fn struct_lockstep_tails_erasing_lifetimes ( self ,
347+ source : Ty < ' tcx > ,
348+ target : Ty < ' tcx > ,
349+ param_env : ty:: ParamEnv < ' tcx > )
350+ -> ( Ty < ' tcx > , Ty < ' tcx > )
351+ {
352+ let tcx = self ;
353+ tcx. struct_lockstep_tails_with_normalize (
354+ source, target, |ty| tcx. normalize_erasing_regions ( param_env, ty) )
355+ }
356+
357+ /// Same as applying struct_tail on `source` and `target`, but only
358+ /// keeps going as long as the two types are instances of the same
359+ /// structure definitions.
360+ /// For `(Foo<Foo<T>>, Foo<dyn Trait>)`, the result will be `(Foo<T>, Trait)`,
361+ /// whereas struct_tail produces `T`, and `Trait`, respectively.
362+ ///
363+ /// See also `struct_lockstep_tails_erasing_lifetimes`, which is suitable for use
364+ /// during codegen.
365+ pub fn struct_lockstep_tails_with_normalize ( self ,
366+ source : Ty < ' tcx > ,
367+ target : Ty < ' tcx > ,
368+ normalize : impl Fn ( Ty < ' tcx > ) -> Ty < ' tcx > )
369+ -> ( Ty < ' tcx > , Ty < ' tcx > )
370+ {
301371 let ( mut a, mut b) = ( source, target) ;
302372 loop {
303373 match ( & a. sty , & b. sty ) {
@@ -319,6 +389,22 @@ impl<'tcx> TyCtxt<'tcx> {
319389 break ;
320390 }
321391 } ,
392+ ( ty:: Projection ( _) , _) | ( ty:: Opaque ( ..) , _) |
393+ ( _, ty:: Projection ( _) ) | ( _, ty:: Opaque ( ..) ) => {
394+ // If either side is a projection, attempt to
395+ // progress via normalization. (Should be safe to
396+ // apply to both sides as normalization is
397+ // idempotent.)
398+ let a_norm = normalize ( a) ;
399+ let b_norm = normalize ( b) ;
400+ if a == a_norm && b == b_norm {
401+ break ;
402+ } else {
403+ a = a_norm;
404+ b = b_norm;
405+ }
406+ }
407+
322408 _ => break ,
323409 }
324410 }
0 commit comments