@@ -444,7 +444,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
444444 Err ( NoSolution ) => vec ! [ ] ,
445445 } ;
446446
447- ecx. probe ( |_| CandidateKind :: DynUpcastingAssembly ) . enter ( |ecx| {
447+ ecx. probe ( |_| CandidateKind :: UnsizeAssembly ) . enter ( |ecx| {
448448 let a_ty = goal. predicate . self_ty ( ) ;
449449 // We need to normalize the b_ty since it's matched structurally
450450 // in the other functions below.
@@ -526,7 +526,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
526526 b_region : ty:: Region < ' tcx > ,
527527 ) -> Vec < ( CanonicalResponse < ' tcx > , BuiltinImplSource ) > {
528528 let tcx = self . tcx ( ) ;
529- let Goal { predicate : ( a_ty, b_ty ) , .. } = goal;
529+ let Goal { predicate : ( a_ty, _b_ty ) , .. } = goal;
530530
531531 // All of a's auto traits need to be in b's auto traits.
532532 let auto_traits_compatible =
@@ -535,51 +535,30 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
535535 return vec ! [ ] ;
536536 }
537537
538- // Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with
539- // the supertrait principal and subtyping the types.
540- let unsize_dyn_to_principal =
541- |ecx : & mut Self , principal : Option < ty:: PolyExistentialTraitRef < ' tcx > > | {
542- ecx. probe_candidate ( "upcast dyn to principle" ) . enter (
543- |ecx| -> Result < _ , NoSolution > {
544- // Require that all of the trait predicates from A match B, except for
545- // the auto traits. We do this by constructing a new A type with B's
546- // auto traits, and equating these types.
547- let new_a_data = principal
548- . into_iter ( )
549- . map ( |trait_ref| trait_ref. map_bound ( ty:: ExistentialPredicate :: Trait ) )
550- . chain ( a_data. iter ( ) . filter ( |a| {
551- matches ! ( a. skip_binder( ) , ty:: ExistentialPredicate :: Projection ( _) )
552- } ) )
553- . chain (
554- b_data
555- . auto_traits ( )
556- . map ( ty:: ExistentialPredicate :: AutoTrait )
557- . map ( ty:: Binder :: dummy) ,
558- ) ;
559- let new_a_data = tcx. mk_poly_existential_predicates_from_iter ( new_a_data) ;
560- let new_a_ty = Ty :: new_dynamic ( tcx, new_a_data, b_region, ty:: Dyn ) ;
561-
562- // We also require that A's lifetime outlives B's lifetime.
563- ecx. eq ( goal. param_env , new_a_ty, b_ty) ?;
564- ecx. add_goal ( goal. with ( tcx, ty:: OutlivesPredicate ( a_region, b_region) ) ) ;
565- ecx. evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
566- } ,
567- )
568- } ;
569-
570538 let mut responses = vec ! [ ] ;
571539 // If the principal def ids match (or are both none), then we're not doing
572540 // trait upcasting. We're just removing auto traits (or shortening the lifetime).
573541 if a_data. principal_def_id ( ) == b_data. principal_def_id ( ) {
574- if let Ok ( resp) = unsize_dyn_to_principal ( self , a_data. principal ( ) ) {
542+ if let Ok ( resp) = self . consider_builtin_upcast_to_principal (
543+ goal,
544+ a_data,
545+ a_region,
546+ b_data,
547+ b_region,
548+ a_data. principal ( ) ,
549+ ) {
575550 responses. push ( ( resp, BuiltinImplSource :: Misc ) ) ;
576551 }
577552 } else if let Some ( a_principal) = a_data. principal ( ) {
578553 self . walk_vtable (
579554 a_principal. with_self_ty ( tcx, a_ty) ,
580555 |ecx, new_a_principal, _, vtable_vptr_slot| {
581- if let Ok ( resp) = unsize_dyn_to_principal (
582- ecx,
556+ if let Ok ( resp) = ecx. consider_builtin_upcast_to_principal (
557+ goal,
558+ a_data,
559+ a_region,
560+ b_data,
561+ b_region,
583562 Some ( new_a_principal. map_bound ( |trait_ref| {
584563 ty:: ExistentialTraitRef :: erase_self_ty ( tcx, trait_ref)
585564 } ) ) ,
@@ -631,6 +610,83 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
631610 self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
632611 }
633612
613+ fn consider_builtin_upcast_to_principal (
614+ & mut self ,
615+ goal : Goal < ' tcx , ( Ty < ' tcx > , Ty < ' tcx > ) > ,
616+ a_data : & ' tcx ty:: List < ty:: PolyExistentialPredicate < ' tcx > > ,
617+ a_region : ty:: Region < ' tcx > ,
618+ b_data : & ' tcx ty:: List < ty:: PolyExistentialPredicate < ' tcx > > ,
619+ b_region : ty:: Region < ' tcx > ,
620+ upcast_principal : Option < ty:: PolyExistentialTraitRef < ' tcx > > ,
621+ ) -> QueryResult < ' tcx > {
622+ let param_env = goal. param_env ;
623+
624+ // More than one projection in a_ty's bounds may match the projection
625+ // in b_ty's bound. Use this to first determine *which* apply without
626+ // having any inference side-effects. We process obligations because
627+ // unification may initially succeed due to deferred projection equality.
628+ let projection_may_match =
629+ |ecx : & mut Self ,
630+ source_projection : ty:: PolyExistentialProjection < ' tcx > ,
631+ target_projection : ty:: PolyExistentialProjection < ' tcx > | {
632+ source_projection. item_def_id ( ) == target_projection. item_def_id ( )
633+ && ecx
634+ . probe ( |_| CandidateKind :: UpcastProbe )
635+ . enter ( |ecx| -> Result < ( ) , NoSolution > {
636+ ecx. eq ( param_env, source_projection, target_projection) ?;
637+ let _ = ecx. try_evaluate_added_goals ( ) ?;
638+ Ok ( ( ) )
639+ } )
640+ . is_ok ( )
641+ } ;
642+
643+ for bound in b_data {
644+ match bound. skip_binder ( ) {
645+ // Check that a's supertrait (upcast_principal) is compatible
646+ // with the target (b_ty).
647+ ty:: ExistentialPredicate :: Trait ( target_principal) => {
648+ self . eq ( param_env, upcast_principal. unwrap ( ) , bound. rebind ( target_principal) ) ?;
649+ }
650+ // Check that b_ty's projection is satisfied by exactly one of
651+ // a_ty's projections. First, we look through the list to see if
652+ // any match. If not, error. Then, if *more* than one matches, we
653+ // return ambiguity. Otherwise, if exactly one matches, equate
654+ // it with b_ty's projection.
655+ ty:: ExistentialPredicate :: Projection ( target_projection) => {
656+ let target_projection = bound. rebind ( target_projection) ;
657+ let mut matching_projections =
658+ a_data. projection_bounds ( ) . filter ( |source_projection| {
659+ projection_may_match ( self , * source_projection, target_projection)
660+ } ) ;
661+ let Some ( source_projection) = matching_projections. next ( ) else {
662+ return Err ( NoSolution ) ;
663+ } ;
664+ if matching_projections. next ( ) . is_some ( ) {
665+ return self . evaluate_added_goals_and_make_canonical_response (
666+ Certainty :: AMBIGUOUS ,
667+ ) ;
668+ }
669+ self . eq ( param_env, source_projection, target_projection) ?;
670+ }
671+ // Check that b_ty's auto traits are present in a_ty's bounds.
672+ ty:: ExistentialPredicate :: AutoTrait ( def_id) => {
673+ if !a_data. auto_traits ( ) . any ( |source_def_id| source_def_id == def_id) {
674+ return Err ( NoSolution ) ;
675+ }
676+ }
677+ }
678+ }
679+
680+ // Also require that a_ty's lifetime outlives b_ty's lifetime.
681+ self . add_goal ( Goal :: new (
682+ self . tcx ( ) ,
683+ param_env,
684+ ty:: Binder :: dummy ( ty:: OutlivesPredicate ( a_region, b_region) ) ,
685+ ) ) ;
686+
687+ self . evaluate_added_goals_and_make_canonical_response ( Certainty :: Yes )
688+ }
689+
634690 /// We have the following builtin impls for arrays:
635691 /// ```ignore (builtin impl example)
636692 /// impl<T: ?Sized, const N: usize> Unsize<[T]> for [T; N] {}
0 commit comments