diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index a32dd135af76..857e4f66489a 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -2055,6 +2055,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { return ControlFlow::Continue(()); }; + // Make sure this predicate is referring to either an `Unsize` or `CoerceUnsized` trait, + // Otherwise there's nothing to do. if !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize) && !self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::CoerceUnsized) { @@ -2062,8 +2064,19 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { } match goal.result() { + // If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing. Ok(Certainty::Yes) => ControlFlow::Continue(()), - Err(NoSolution) => ControlFlow::Break(()), + Err(NoSolution) => { + // Even if we find no solution, continue recursing if we find a single candidate + // for which we're shallowly certain it holds to get the right error source. + if let [only_candidate] = &goal.candidates()[..] + && only_candidate.shallow_certainty() == Certainty::Yes + { + only_candidate.visit_nested_no_probe(self) + } else { + ControlFlow::Break(()) + } + } Ok(Certainty::Maybe { .. }) => { // FIXME: structurally normalize? if self.fcx.tcx.is_lang_item(pred.def_id(), LangItem::Unsize) @@ -2071,6 +2084,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for CoerceVisitor<'_, 'tcx> { && let ty::Infer(ty::TyVar(vid)) = *pred.self_ty().skip_binder().kind() && self.fcx.type_var_is_sized(vid) { + // We get here when trying to unsize a type variable to a `dyn Trait`, + // knowing that that variable is sized. Unsizing definitely has to happen in that case. + // If the variable weren't sized, we may not need an unsizing coercion. + // In general, we don't want to add coercions too eagerly since it makes error messages much worse. ControlFlow::Continue(()) } else if let Some(cand) = goal.unique_applicable_candidate() && cand.shallow_certainty() == Certainty::Yes diff --git a/tests/ui/dst/dst-object-from-unsized-type.current.stderr b/tests/ui/dst/dst-object-from-unsized-type.current.stderr index d5f99eca974e..be9966743d4e 100644 --- a/tests/ui/dst/dst-object-from-unsized-type.current.stderr +++ b/tests/ui/dst/dst-object-from-unsized-type.current.stderr @@ -14,7 +14,7 @@ LL + fn test1(t: &T) { | error[E0277]: the size for values of type `T` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:16:23 + --> $DIR/dst-object-from-unsized-type.rs:17:23 | LL | fn test2(t: &T) { | - this type parameter needs to be `Sized` @@ -29,7 +29,7 @@ LL + fn test2(t: &T) { | error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:21:28 + --> $DIR/dst-object-from-unsized-type.rs:23:28 | LL | let _: &[&dyn Foo] = &["hi"]; | ^^^^ doesn't have a size known at compile-time @@ -38,7 +38,7 @@ LL | let _: &[&dyn Foo] = &["hi"]; = note: required for the cast from `&'static str` to `&dyn Foo` error[E0277]: the size for values of type `[u8]` cannot be known at compilation time - --> $DIR/dst-object-from-unsized-type.rs:26:23 + --> $DIR/dst-object-from-unsized-type.rs:29:23 | LL | let _: &dyn Foo = x as &dyn Foo; | ^ doesn't have a size known at compile-time diff --git a/tests/ui/dst/dst-object-from-unsized-type.next.stderr b/tests/ui/dst/dst-object-from-unsized-type.next.stderr index 6b4adda5154e..032ba0cb14ae 100644 --- a/tests/ui/dst/dst-object-from-unsized-type.next.stderr +++ b/tests/ui/dst/dst-object-from-unsized-type.next.stderr @@ -1,41 +1,57 @@ -error[E0308]: mismatched types +error[E0277]: the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T` --> $DIR/dst-object-from-unsized-type.rs:11:23 | LL | fn test1(t: &T) { - | - found this type parameter + | - this type parameter needs to be `Sized` LL | let u: &dyn Foo = t; - | -------- ^ expected `&dyn Foo`, found `&T` - | | - | expected due to this + | ^ within `T`, the trait `Sized` is not implemented for `T` + | + = note: required because it appears within the type `T` + = note: required for `&T` to implement `CoerceUnsized<&dyn Foo>` + = note: required for the cast from `&T` to `&dyn Foo` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn test1(t: &T) { +LL + fn test1(t: &T) { | - = note: expected reference `&dyn Foo` - found reference `&T` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters -error[E0605]: non-primitive cast: `&T` as `&dyn Foo` - --> $DIR/dst-object-from-unsized-type.rs:16:23 +error[E0277]: the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T` + --> $DIR/dst-object-from-unsized-type.rs:17:23 | +LL | fn test2(t: &T) { + | - this type parameter needs to be `Sized` LL | let v: &dyn Foo = t as &dyn Foo; - | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | ^ within `T`, the trait `Sized` is not implemented for `T` + | + = note: required because it appears within the type `T` + = note: required for `&T` to implement `CoerceUnsized<&dyn Foo>` + = note: required for the cast from `&T` to `&dyn Foo` +help: consider removing the `?Sized` bound to make the type parameter `Sized` + | +LL - fn test2(t: &T) { +LL + fn test2(t: &T) { + | -error[E0308]: mismatched types - --> $DIR/dst-object-from-unsized-type.rs:21:28 +error[E0277]: the trait bound `&str: CoerceUnsized<&dyn Foo>` is not satisfied in `str` + --> $DIR/dst-object-from-unsized-type.rs:23:28 | LL | let _: &[&dyn Foo] = &["hi"]; - | ^^^^ expected `&dyn Foo`, found `&str` + | ^^^^ within `str`, the trait `Sized` is not implemented for `str` | - = note: expected reference `&dyn Foo` - found reference `&'static str` - = help: `str` implements `Foo` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well + = note: `str` is considered to contain a `[u8]` slice for auto trait purposes + = note: required for `&str` to implement `CoerceUnsized<&dyn Foo>` + = note: required for the cast from `&'static str` to `&dyn Foo` -error[E0605]: non-primitive cast: `&[u8]` as `&dyn Foo` - --> $DIR/dst-object-from-unsized-type.rs:26:23 +error[E0277]: the trait bound `&[u8]: CoerceUnsized<&dyn Foo>` is not satisfied in `[u8]` + --> $DIR/dst-object-from-unsized-type.rs:29:23 | LL | let _: &dyn Foo = x as &dyn Foo; - | ^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + | ^ within `[u8]`, the trait `Sized` is not implemented for `[u8]` + | + = note: required because it appears within the type `[u8]` + = note: required for `&[u8]` to implement `CoerceUnsized<&dyn Foo>` + = note: required for the cast from `&[u8]` to `&dyn Foo` error: aborting due to 4 previous errors -Some errors have detailed explanations: E0308, E0605. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/dst/dst-object-from-unsized-type.rs b/tests/ui/dst/dst-object-from-unsized-type.rs index 5ba6c571a39a..1e6113b3fc6f 100644 --- a/tests/ui/dst/dst-object-from-unsized-type.rs +++ b/tests/ui/dst/dst-object-from-unsized-type.rs @@ -9,22 +9,26 @@ impl Foo for [u8] {} fn test1(t: &T) { let u: &dyn Foo = t; - //~^ ERROR the size for values of type + //[current]~^ ERROR the size for values of type + //[next]~^^ ERROR the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T` } fn test2(t: &T) { let v: &dyn Foo = t as &dyn Foo; - //~^ ERROR the size for values of type + //[current]~^ ERROR the size for values of type + //[next]~^^ ERROR the trait bound `&T: CoerceUnsized<&dyn Foo>` is not satisfied in `T` } fn test3() { let _: &[&dyn Foo] = &["hi"]; - //~^ ERROR the size for values of type + //[current]~^ ERROR the size for values of type + //[next]~^^ ERROR the trait bound `&str: CoerceUnsized<&dyn Foo>` is not satisfied in `str` } fn test4(x: &[u8]) { let _: &dyn Foo = x as &dyn Foo; - //~^ ERROR the size for values of type + //[current]~^ ERROR the size for values of type + //[next]~^^ ERROR the trait bound `&[u8]: CoerceUnsized<&dyn Foo>` is not satisfied in `[u8]` } fn main() { } diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr index b82f1eef42b5..392680aa5064 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr @@ -1,14 +1,13 @@ -error[E0308]: mismatched types +error[E0277]: the trait bound `&dyn for<'a> Subtrait<'a, 'a>: CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` is not satisfied --> $DIR/higher-ranked-upcasting-ub.rs:22:5 | -LL | fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { - | ----------------------------------- expected `&dyn for<'a, 'b> Supertrait<'a, 'b>` because of return type LL | x - | ^ expected trait `Supertrait`, found trait `Subtrait` + | ^ the trait `Unsize Supertrait<'a, 'b>>` is not implemented for `dyn for<'a> Subtrait<'a, 'a>` | - = note: expected reference `&dyn for<'a, 'b> Supertrait<'a, 'b>` - found reference `&dyn for<'a> Subtrait<'a, 'a>` + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information + = note: required for `&dyn for<'a> Subtrait<'a, 'a>` to implement `CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` + = note: required for the cast from `&dyn for<'a> Subtrait<'a, 'a>` to `&dyn for<'a, 'b> Supertrait<'a, 'b>` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs index af2594b95f3d..98ca30ca391f 100644 --- a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs @@ -19,8 +19,10 @@ impl<'a> Supertrait<'a, 'a> for () { } impl<'a> Subtrait<'a, 'a> for () {} fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { - x //~ ERROR mismatched types + x //[current]~^ ERROR mismatched types + //[current]~| ERROR mismatched types + //[next]~^^^ ERROR the trait bound `&dyn for<'a> Subtrait<'a, 'a>: CoerceUnsized<&dyn for<'a, 'b> Supertrait<'a, 'b>>` is not satisfied } fn transmute<'a, 'b>(x: &'a str) -> &'b str {