about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs25
-rw-r--r--tests/ui/traits/trait-upcasting/upcast-through-struct-tail.current.stderr13
-rw-r--r--tests/ui/traits/trait-upcasting/upcast-through-struct-tail.next.stderr13
-rw-r--r--tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs14
4 files changed, 60 insertions, 5 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index 6045001510e..69af33e874b 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -100,10 +100,18 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
                 rematch_impl(self, goal, def_id, nested_obligations)
             }
 
+            // If an unsize goal is ambiguous, then we can manually rematch it to make
+            // selection progress for coercion during HIR typeck. If it is *not* ambiguous,
+            // but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals,
+            // and we need to rematch those to detect tuple unsizing and trait upcasting.
+            // FIXME: This will be wrong if we have param-env or where-clause bounds
+            // with the unsize goal -- we may need to mark those with different impl
+            // sources.
             (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src))
+            | (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc))
                 if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
             {
-                rematch_unsize(self, goal, nested_obligations, src)
+                rematch_unsize(self, goal, nested_obligations, src, certainty)
             }
 
             // Technically some builtin impls have nested obligations, but if
@@ -217,6 +225,7 @@ fn rematch_unsize<'tcx>(
     goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
     mut nested: Vec<PredicateObligation<'tcx>>,
     source: BuiltinImplSource,
+    certainty: Certainty,
 ) -> SelectionResult<'tcx, Selection<'tcx>> {
     let tcx = infcx.tcx;
     let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested);
@@ -227,6 +236,12 @@ fn rematch_unsize<'tcx>(
         &mut nested,
     );
     match (a_ty.kind(), b_ty.kind()) {
+        // Stall any ambiguous upcasting goals, since we can't rematch those
+        (ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty {
+            Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))),
+            _ => Ok(None),
+        },
+        // `T` -> `dyn Trait` upcasting
         (_, &ty::Dynamic(data, region, ty::Dyn)) => {
             // Check that the type implements all of the predicates of the def-id.
             // (i.e. the principal, all of the associated types match, and any auto traits)
@@ -354,10 +369,10 @@ fn rematch_unsize<'tcx>(
             );
             Ok(Some(ImplSource::Builtin(source, nested)))
         }
-        // FIXME: We *could* ICE here if either:
-        // 1. the certainty is `Certainty::Yes`,
-        // 2. we're in codegen (which should mean `Certainty::Yes`).
-        _ => Ok(None),
+        _ => {
+            assert_ne!(certainty, Certainty::Yes);
+            Ok(None)
+        }
     }
 }
 
diff --git a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.current.stderr b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.current.stderr
new file mode 100644
index 00000000000..9f0993d65a7
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.current.stderr
@@ -0,0 +1,13 @@
+error[E0658]: cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental
+  --> $DIR/upcast-through-struct-tail.rs:10:5
+   |
+LL |     x
+   |     ^
+   |
+   = note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
+   = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
+   = note: required when coercing `Box<Wrapper<(dyn A + 'a)>>` into `Box<Wrapper<(dyn B + 'a)>>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.next.stderr b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.next.stderr
new file mode 100644
index 00000000000..9f0993d65a7
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.next.stderr
@@ -0,0 +1,13 @@
+error[E0658]: cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental
+  --> $DIR/upcast-through-struct-tail.rs:10:5
+   |
+LL |     x
+   |     ^
+   |
+   = note: see issue #65991 <https://github.com/rust-lang/rust/issues/65991> for more information
+   = help: add `#![feature(trait_upcasting)]` to the crate attributes to enable
+   = note: required when coercing `Box<Wrapper<(dyn A + 'a)>>` into `Box<Wrapper<(dyn B + 'a)>>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs
new file mode 100644
index 00000000000..42495f45f8a
--- /dev/null
+++ b/tests/ui/traits/trait-upcasting/upcast-through-struct-tail.rs
@@ -0,0 +1,14 @@
+// revisions: current next
+//[next] compile-flags: -Ztrait-solver=next
+
+struct Wrapper<T: ?Sized>(T);
+
+trait A: B {}
+trait B {}
+
+fn test<'a>(x: Box<Wrapper<dyn A + 'a>>) -> Box<Wrapper<dyn B + 'a>> {
+    x
+    //~^ ERROR cannot cast `dyn A` to `dyn B`, trait upcasting coercion is experimental
+}
+
+fn main() {}