about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-08-01 23:51:37 +0000
committerMichael Goulet <michael@errs.io>2023-08-03 18:21:11 +0000
commit7c942ccb0c8b404ff804b2678f011f42729e81f2 (patch)
treec29ec1d1e90c5827d3f3c82723f8e58ef4c2758e
parent238beae5e588fb3dc4550d970085b5bf29f40776 (diff)
downloadrust-7c942ccb0c8b404ff804b2678f011f42729e81f2.tar.gz
rust-7c942ccb0c8b404ff804b2678f011f42729e81f2.zip
Don't be incomplete
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs35
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs93
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs92
3 files changed, 128 insertions, 92 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 8f2f02a5e41..e3da87a2210 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -745,7 +745,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         match (source.kind(), target.kind()) {
             // Trait+Kx+'a -> Trait+Ky+'b (upcasts).
-            (&ty::Dynamic(ref data_a, _, ty::Dyn), &ty::Dynamic(ref data_b, _, ty::Dyn)) => {
+            (
+                &ty::Dynamic(ref a_data, a_region, ty::Dyn),
+                &ty::Dynamic(ref b_data, b_region, ty::Dyn),
+            ) => {
                 // Upcast coercions permit several things:
                 //
                 // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
@@ -757,19 +760,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 //
                 // We always perform upcasting coercions when we can because of reason
                 // #2 (region bounds).
-                let auto_traits_compatible = data_b
+                let auto_traits_compatible = b_data
                     .auto_traits()
                     // All of a's auto traits need to be in b's auto traits.
-                    .all(|b| data_a.auto_traits().any(|a| a == b));
+                    .all(|b| a_data.auto_traits().any(|a| a == b));
                 if auto_traits_compatible {
-                    let principal_def_id_a = data_a.principal_def_id();
-                    let principal_def_id_b = data_b.principal_def_id();
+                    let principal_def_id_a = a_data.principal_def_id();
+                    let principal_def_id_b = b_data.principal_def_id();
                     if principal_def_id_a == principal_def_id_b {
                         // no cyclic
                         candidates.vec.push(BuiltinUnsizeCandidate);
                     } else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
                         // not casual unsizing, now check whether this is trait upcasting coercion.
-                        let principal_a = data_a.principal().unwrap();
+                        let principal_a = a_data.principal().unwrap();
                         let target_trait_did = principal_def_id_b.unwrap();
                         let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
                         if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object(
@@ -785,9 +788,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         for (idx, upcast_trait_ref) in
                             util::supertraits(self.tcx(), source_trait_ref).enumerate()
                         {
-                            if upcast_trait_ref.def_id() == target_trait_did {
-                                candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
-                            }
+                            self.infcx.probe(|_| {
+                                if upcast_trait_ref.def_id() == target_trait_did
+                                    && let Ok(nested) = self.match_upcast_principal(
+                                        obligation,
+                                        upcast_trait_ref,
+                                        a_data,
+                                        b_data,
+                                        a_region,
+                                        b_region,
+                                    )
+                                {
+                                    if nested.is_none() {
+                                        candidates.ambiguous = true;
+                                    }
+                                    candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
+                                }
+                            })
                         }
                     }
                 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 3d6df08f6db..65d43b55b90 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -890,89 +890,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let unnormalized_upcast_principal =
             util::supertraits(tcx, source_principal).nth(idx).unwrap();
 
-        let mut nested = vec![];
-        let upcast_principal = normalize_with_depth_to(
-            self,
-            obligation.param_env,
-            obligation.cause.clone(),
-            obligation.recursion_depth + 1,
-            unnormalized_upcast_principal,
-            &mut nested,
-        );
-
-        for bound in b_data {
-            match bound.skip_binder() {
-                // Check that a's supertrait (upcast_principal) is compatible
-                // with the target (b_ty).
-                ty::ExistentialPredicate::Trait(target_principal) => {
-                    nested.extend(
-                        self.infcx
-                            .at(&obligation.cause, obligation.param_env)
-                            .sup(
-                                DefineOpaqueTypes::No,
-                                upcast_principal.map_bound(|trait_ref| {
-                                    ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
-                                }),
-                                bound.rebind(target_principal),
-                            )
-                            .map_err(|_| SelectionError::Unimplemented)?
-                            .into_obligations(),
-                    );
-                }
-                // Check that b_ty's projection is satisfied by exactly one of
-                // a_ty's projections. First, we look through the list to see if
-                // any match. If not, error. Then, if *more* than one matches, we
-                // return ambiguity. Otherwise, if exactly one matches, equate
-                // it with b_ty's projection.
-                ty::ExistentialPredicate::Projection(target_projection) => {
-                    let target_projection = bound.rebind(target_projection);
-                    let mut matching_projections =
-                        a_data.projection_bounds().filter(|source_projection| {
-                            // Eager normalization means that we can just use can_eq
-                            // here instead of equating and processing obligations.
-                            source_projection.item_def_id() == target_projection.item_def_id()
-                                && self.infcx.can_eq(
-                                    obligation.param_env,
-                                    *source_projection,
-                                    target_projection,
-                                )
-                        });
-                    let Some(source_projection) = matching_projections.next() else {
-                        return Err(SelectionError::Unimplemented);
-                    };
-                    if matching_projections.next().is_some() {
-                        // This is incomplete but I don't care. We should never
-                        // have more than one projection that ever applies with
-                        // eager norm and actually implementable traits, since
-                        // you can't have two supertraits like:
-                        // `trait A: B<i32, Assoc = First> + B<i32, Assoc = Second>`
-                        return Err(SelectionError::Unimplemented);
-                    }
-                    nested.extend(
-                        self.infcx
-                            .at(&obligation.cause, obligation.param_env)
-                            .sup(DefineOpaqueTypes::No, source_projection, target_projection)
-                            .map_err(|_| SelectionError::Unimplemented)?
-                            .into_obligations(),
-                    );
-                }
-                // Check that b_ty's auto trait is present in a_ty's bounds.
-                ty::ExistentialPredicate::AutoTrait(def_id) => {
-                    if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
-                        return Err(SelectionError::Unimplemented);
-                    }
-                }
-            }
-        }
-
-        // Also require that a_ty's lifetime outlives b_ty's lifetime.
-        nested.push(Obligation::with_depth(
-            tcx,
-            obligation.cause.clone(),
-            obligation.recursion_depth + 1,
-            obligation.param_env,
-            ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
-        ));
+        let nested = self
+            .match_upcast_principal(
+                obligation,
+                unnormalized_upcast_principal,
+                a_data,
+                b_data,
+                a_region,
+                b_region,
+            )?
+            .expect("did not expect ambiguity during confirmation");
 
         let vtable_segment_callback = {
             let mut vptr_offset = 0;
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 5da8d838db2..f1bd9f8b71a 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -2477,6 +2477,98 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         Ok(Normalized { value: impl_args, obligations: nested_obligations })
     }
 
+    fn match_upcast_principal(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        unnormalized_upcast_principal: ty::PolyTraitRef<'tcx>,
+        a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+        a_region: ty::Region<'tcx>,
+        b_region: ty::Region<'tcx>,
+    ) -> SelectionResult<'tcx, Vec<PredicateObligation<'tcx>>> {
+        let tcx = self.tcx();
+        let mut nested = vec![];
+
+        let upcast_principal = normalize_with_depth_to(
+            self,
+            obligation.param_env,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            unnormalized_upcast_principal,
+            &mut nested,
+        );
+
+        for bound in b_data {
+            match bound.skip_binder() {
+                // Check that a_ty's supertrait (upcast_principal) is compatible
+                // with the target (b_ty).
+                ty::ExistentialPredicate::Trait(target_principal) => {
+                    nested.extend(
+                        self.infcx
+                            .at(&obligation.cause, obligation.param_env)
+                            .sup(
+                                DefineOpaqueTypes::No,
+                                upcast_principal.map_bound(|trait_ref| {
+                                    ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
+                                }),
+                                bound.rebind(target_principal),
+                            )
+                            .map_err(|_| SelectionError::Unimplemented)?
+                            .into_obligations(),
+                    );
+                }
+                // Check that b_ty's projection is satisfied by exactly one of
+                // a_ty's projections. First, we look through the list to see if
+                // any match. If not, error. Then, if *more* than one matches, we
+                // return ambiguity. Otherwise, if exactly one matches, equate
+                // it with b_ty's projection.
+                ty::ExistentialPredicate::Projection(target_projection) => {
+                    let target_projection = bound.rebind(target_projection);
+                    let mut matching_projections =
+                        a_data.projection_bounds().filter(|source_projection| {
+                            // Eager normalization means that we can just use can_eq
+                            // here instead of equating and processing obligations.
+                            source_projection.item_def_id() == target_projection.item_def_id()
+                                && self.infcx.can_eq(
+                                    obligation.param_env,
+                                    *source_projection,
+                                    target_projection,
+                                )
+                        });
+                    let Some(source_projection) = matching_projections.next() else {
+                        return Err(SelectionError::Unimplemented);
+                    };
+                    if matching_projections.next().is_some() {
+                        return Ok(None);
+                    }
+                    nested.extend(
+                        self.infcx
+                            .at(&obligation.cause, obligation.param_env)
+                            .sup(DefineOpaqueTypes::No, source_projection, target_projection)
+                            .map_err(|_| SelectionError::Unimplemented)?
+                            .into_obligations(),
+                    );
+                }
+                // Check that b_ty's auto traits are present in a_ty's bounds.
+                ty::ExistentialPredicate::AutoTrait(def_id) => {
+                    if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
+                        return Err(SelectionError::Unimplemented);
+                    }
+                }
+            }
+        }
+
+        nested.push(Obligation::with_depth(
+            tcx,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            obligation.param_env,
+            ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
+        ));
+
+        Ok(Some(nested))
+    }
+
     /// Normalize `where_clause_trait_ref` and try to match it against
     /// `obligation`. If successful, return any predicates that
     /// result from the normalization.