11use std:: ops:: ControlFlow ;
22
3+ use rustc_hir as hir;
34use rustc_hir:: def_id:: DefId ;
45use rustc_infer:: infer:: { DefineOpaqueTypes , InferCtxt , InferOk } ;
56use rustc_infer:: traits:: util:: supertraits;
@@ -11,7 +12,7 @@ use rustc_middle::traits::{
1112 ImplSource , ImplSourceObjectData , ImplSourceTraitUpcastingData , ImplSourceUserDefinedData ,
1213 ObligationCause , SelectionError ,
1314} ;
14- use rustc_middle:: ty:: { self , TyCtxt } ;
15+ use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
1516use rustc_span:: DUMMY_SP ;
1617
1718use crate :: solve:: assembly:: { BuiltinImplSource , Candidate , CandidateSource } ;
@@ -113,6 +114,12 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
113114 ) ,
114115 ) => rematch_object ( self , goal, nested_obligations) ,
115116
117+ ( Certainty :: Maybe ( _) , CandidateSource :: BuiltinImpl ( BuiltinImplSource :: Misc ) )
118+ if self . tcx . lang_items ( ) . unsize_trait ( ) == Some ( goal. predicate . def_id ( ) ) =>
119+ {
120+ rematch_unsize ( self , goal, nested_obligations)
121+ }
122+
116123 // Technically some builtin impls have nested obligations, but if
117124 // `Certainty::Yes`, then they should've all been verified and don't
118125 // need re-checking.
@@ -232,6 +239,9 @@ fn rematch_object<'tcx>(
232239 {
233240 assert_eq ! ( source_kind, ty:: Dyn , "cannot upcast dyn*" ) ;
234241 if let ty:: Dynamic ( data, _, ty:: Dyn ) = goal. predicate . trait_ref . substs . type_at ( 1 ) . kind ( ) {
242+ // FIXME: We also need to ensure that the source lifetime outlives the
243+ // target lifetime. This doesn't matter for codegen, though, and only
244+ // *really* matters if the goal's certainty is ambiguous.
235245 ( true , data. principal ( ) . unwrap ( ) . with_self_ty ( infcx. tcx , self_ty) )
236246 } else {
237247 bug ! ( )
@@ -305,3 +315,136 @@ fn rematch_object<'tcx>(
305315 ImplSource :: Object ( ImplSourceObjectData { vtable_base, nested } )
306316 } ) )
307317}
318+
319+ /// The `Unsize` trait is particularly important to coercion, so we try rematch it.
320+ /// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait
321+ /// goal assembly in the solver, both for soundness and in order to avoid ICEs.
322+ fn rematch_unsize < ' tcx > (
323+ infcx : & InferCtxt < ' tcx > ,
324+ goal : Goal < ' tcx , ty:: TraitPredicate < ' tcx > > ,
325+ mut nested : Vec < PredicateObligation < ' tcx > > ,
326+ ) -> SelectionResult < ' tcx , Selection < ' tcx > > {
327+ let tcx = infcx. tcx ;
328+ let a_ty = goal. predicate . self_ty ( ) ;
329+ let b_ty = goal. predicate . trait_ref . substs . type_at ( 1 ) ;
330+
331+ match ( a_ty. kind ( ) , b_ty. kind ( ) ) {
332+ ( _, & ty:: Dynamic ( data, region, ty:: Dyn ) ) => {
333+ // Check that the type implements all of the predicates of the def-id.
334+ // (i.e. the principal, all of the associated types match, and any auto traits)
335+ nested. extend ( data. iter ( ) . map ( |pred| {
336+ Obligation :: new (
337+ infcx. tcx ,
338+ ObligationCause :: dummy ( ) ,
339+ goal. param_env ,
340+ pred. with_self_ty ( tcx, a_ty) ,
341+ )
342+ } ) ) ;
343+ // The type must be Sized to be unsized.
344+ let sized_def_id = tcx. require_lang_item ( hir:: LangItem :: Sized , None ) ;
345+ nested. push ( Obligation :: new (
346+ infcx. tcx ,
347+ ObligationCause :: dummy ( ) ,
348+ goal. param_env ,
349+ ty:: TraitRef :: new ( tcx, sized_def_id, [ a_ty] ) ,
350+ ) ) ;
351+ // The type must outlive the lifetime of the `dyn` we're unsizing into.
352+ nested. push ( Obligation :: new (
353+ infcx. tcx ,
354+ ObligationCause :: dummy ( ) ,
355+ goal. param_env ,
356+ ty:: Binder :: dummy ( ty:: OutlivesPredicate ( a_ty, region) ) ,
357+ ) ) ;
358+ }
359+ // `[T; n]` -> `[T]` unsizing
360+ ( & ty:: Array ( a_elem_ty, ..) , & ty:: Slice ( b_elem_ty) ) => {
361+ nested. extend (
362+ infcx
363+ . at ( & ObligationCause :: dummy ( ) , goal. param_env )
364+ . eq ( DefineOpaqueTypes :: No , a_elem_ty, b_elem_ty)
365+ . expect ( "expected rematch to succeed" )
366+ . into_obligations ( ) ,
367+ ) ;
368+ }
369+ // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
370+ ( & ty:: Adt ( a_def, a_substs) , & ty:: Adt ( b_def, b_substs) )
371+ if a_def. is_struct ( ) && a_def. did ( ) == b_def. did ( ) =>
372+ {
373+ let unsizing_params = tcx. unsizing_params_for_adt ( a_def. did ( ) ) ;
374+ // We must be unsizing some type parameters. This also implies
375+ // that the struct has a tail field.
376+ if unsizing_params. is_empty ( ) {
377+ bug ! ( "expected rematch to succeed" )
378+ }
379+
380+ let tail_field = a_def
381+ . non_enum_variant ( )
382+ . fields
383+ . raw
384+ . last ( )
385+ . expect ( "expected unsized ADT to have a tail field" ) ;
386+ let tail_field_ty = tcx. type_of ( tail_field. did ) ;
387+
388+ let a_tail_ty = tail_field_ty. subst ( tcx, a_substs) ;
389+ let b_tail_ty = tail_field_ty. subst ( tcx, b_substs) ;
390+
391+ // Substitute just the unsizing params from B into A. The type after
392+ // this substitution must be equal to B. This is so we don't unsize
393+ // unrelated type parameters.
394+ let new_a_substs =
395+ tcx. mk_substs_from_iter ( a_substs. iter ( ) . enumerate ( ) . map ( |( i, a) | {
396+ if unsizing_params. contains ( i as u32 ) { b_substs[ i] } else { a }
397+ } ) ) ;
398+ let unsized_a_ty = Ty :: new_adt ( tcx, a_def, new_a_substs) ;
399+
400+ nested. extend (
401+ infcx
402+ . at ( & ObligationCause :: dummy ( ) , goal. param_env )
403+ . eq ( DefineOpaqueTypes :: No , unsized_a_ty, b_ty)
404+ . expect ( "expected rematch to succeed" )
405+ . into_obligations ( ) ,
406+ ) ;
407+
408+ // Finally, we require that `TailA: Unsize<TailB>` for the tail field
409+ // types.
410+ nested. push ( Obligation :: new (
411+ tcx,
412+ ObligationCause :: dummy ( ) ,
413+ goal. param_env ,
414+ ty:: TraitRef :: new ( tcx, goal. predicate . def_id ( ) , [ a_tail_ty, b_tail_ty] ) ,
415+ ) ) ;
416+ }
417+ // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
418+ ( & ty:: Tuple ( a_tys) , & ty:: Tuple ( b_tys) )
419+ if a_tys. len ( ) == b_tys. len ( ) && !a_tys. is_empty ( ) =>
420+ {
421+ let ( a_last_ty, a_rest_tys) = a_tys. split_last ( ) . unwrap ( ) ;
422+ let b_last_ty = b_tys. last ( ) . unwrap ( ) ;
423+
424+ // Substitute just the tail field of B., and require that they're equal.
425+ let unsized_a_ty =
426+ Ty :: new_tup_from_iter ( tcx, a_rest_tys. iter ( ) . chain ( [ b_last_ty] ) . copied ( ) ) ;
427+ nested. extend (
428+ infcx
429+ . at ( & ObligationCause :: dummy ( ) , goal. param_env )
430+ . eq ( DefineOpaqueTypes :: No , unsized_a_ty, b_ty)
431+ . expect ( "expected rematch to succeed" )
432+ . into_obligations ( ) ,
433+ ) ;
434+
435+ // Similar to ADTs, require that the rest of the fields are equal.
436+ nested. push ( Obligation :: new (
437+ tcx,
438+ ObligationCause :: dummy ( ) ,
439+ goal. param_env ,
440+ ty:: TraitRef :: new ( tcx, goal. predicate . def_id ( ) , [ * a_last_ty, * b_last_ty] ) ,
441+ ) ) ;
442+ }
443+ // FIXME: We *could* ICE here if either:
444+ // 1. the certainty is `Certainty::Yes`,
445+ // 2. we're in codegen (which should mean `Certainty::Yes`).
446+ _ => return Ok ( None ) ,
447+ }
448+
449+ Ok ( Some ( ImplSource :: Builtin ( nested) ) )
450+ }
0 commit comments