about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-02-24 19:21:45 -0500
committerGitHub <noreply@github.com>2025-02-24 19:21:45 -0500
commit0bb00e2085b169bfc21e58b299f67f44c906d2fa (patch)
treef5126f1db3325a493fdca88f16e7630b2107618b
parent748af6b3e441fb624ee96d48d6e3935642427a91 (diff)
parentb46acc01916ba3e8b8f8ab9d89608861d1d4cb87 (diff)
downloadrust-0bb00e2085b169bfc21e58b299f67f44c906d2fa.tar.gz
rust-0bb00e2085b169bfc21e58b299f67f44c906d2fa.zip
Rollup merge of #137289 - compiler-errors:coerce-unsized-errors, r=oli-obk
Consolidate and improve error messaging for `CoerceUnsized` and `DispatchFromDyn`

Firstly, this PR consolidates and reworks the error diagnostics for `CoercePointee` and `DispatchFromDyn`. There was a ton of duplication for no reason -- this reworks both the errors and also the error codes, since they can be shared between both traits since they report the same thing.

Secondly, when encountering a struct with multiple fields that must be coerced, point out the field spans, rather than mentioning the fields by name. This makes the error message clearer, but also means that we don't mention the `__S` dummy parameter for `derive(CoercePointee)`.

Thirdly, emit a custom error message when we encounter a trait error that comes from the recursive field `CoerceUnsized`/`DispatchFromDyn` trait check. **Note:** This is the only one I'm not too satisfied with -- I think it could use some more refinement, but ideally it explains that the field must be an unsize-able pointer... Feedback welcome.

Finally, don't emit `DispatchFromDyn` validity errors if we detect `CoerceUnsized` validity errors from an impl of the same ADT.

This is best reviewed per commit.

r? `@oli-obk` perhaps?

cc `@dingxiangfei2009` -- sorry for making my own attempt at this PR, but I wanted to see if I could implement a fix for #136796 in a less complicated way, since communicating over github review comments can be a bit slow. I'll leave comments inline to explain my thinking about the diagnostics changes.
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0374.md53
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0375.md47
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0376.md38
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0377.md23
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl16
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs170
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs70
-rw-r--r--library/core/src/marker.rs1
-rw-r--r--tests/ui/coercion/issue-26905.stderr13
-rw-r--r--tests/ui/deriving/deriving-coerce-pointee-neg.rs23
-rw-r--r--tests/ui/deriving/deriving-coerce-pointee-neg.stderr54
-rw-r--r--tests/ui/error-codes/E0374.stderr2
-rw-r--r--tests/ui/error-codes/E0375.stderr13
-rw-r--r--tests/ui/error-codes/E0376.rs10
-rw-r--r--tests/ui/error-codes/E0376.stderr9
-rw-r--r--tests/ui/invalid_dispatch_from_dyn_impls.rs14
-rw-r--r--tests/ui/invalid_dispatch_from_dyn_impls.stderr28
-rw-r--r--tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.rs2
-rw-r--r--tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.stderr13
-rw-r--r--tests/ui/self/dispatch-from-dyn-zst-transmute.rs2
-rw-r--r--tests/ui/self/dispatch-from-dyn-zst-transmute.stderr13
-rw-r--r--tests/ui/traits/issue-78372.stderr4
22 files changed, 308 insertions, 310 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0374.md b/compiler/rustc_error_codes/src/error_codes/E0374.md
index 6d7dc88823c..63c243b54ff 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0374.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0374.md
@@ -1,5 +1,5 @@
-`CoerceUnsized` was implemented on a struct which does not contain a field with
-an unsized type.
+`CoerceUnsized` or `DispatchFromDyn` was implemented on a struct which does not
+contain a field that is being unsized.
 
 Example of erroneous code:
 
@@ -11,47 +11,20 @@ struct Foo<T: ?Sized> {
     a: i32,
 }
 
-// error: Struct `Foo` has no unsized fields that need `CoerceUnsized`.
+// error: Struct `Foo` has no unsized fields that need to be coerced.
 impl<T, U> CoerceUnsized<Foo<U>> for Foo<T>
     where T: CoerceUnsized<U> {}
 ```
 
-An [unsized type][1] is any type where the compiler does not know the length or
-alignment of at compile time. Any struct containing an unsized type is also
-unsized.
+`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
+like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
+is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
+trait.
 
-[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
+If the struct doesn't have any fields of unsized types then there is no
+meaningful way to implement `CoerceUnsized` or `DispatchFromDyn`, since
+there is no coercion taking place.
 
-`CoerceUnsized` is used to coerce one struct containing an unsized type
-into another struct containing a different unsized type. If the struct
-doesn't have any fields of unsized types then you don't need explicit
-coercion to get the types you want. To fix this you can either
-not try to implement `CoerceUnsized` or you can add a field that is
-unsized to the struct.
-
-Example:
-
-```
-#![feature(coerce_unsized)]
-use std::ops::CoerceUnsized;
-
-// We don't need to impl `CoerceUnsized` here.
-struct Foo {
-    a: i32,
-}
-
-// We add the unsized type field to the struct.
-struct Bar<T: ?Sized> {
-    a: i32,
-    b: T,
-}
-
-// The struct has an unsized field so we can implement
-// `CoerceUnsized` for it.
-impl<T, U> CoerceUnsized<Bar<U>> for Bar<T>
-    where T: CoerceUnsized<U> {}
-```
-
-Note that `CoerceUnsized` is mainly used by smart pointers like `Box`, `Rc`
-and `Arc` to be able to mark that they can coerce unsized types that they
-are pointing at.
+Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
+like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
+that they are pointing at.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0375.md b/compiler/rustc_error_codes/src/error_codes/E0375.md
index 71e53057165..7abb3b6afd0 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0375.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0375.md
@@ -1,5 +1,5 @@
-`CoerceUnsized` was implemented on a struct which contains more than one field
-with an unsized type.
+`CoerceUnsized` or `DispatchFromDyn` was implemented on a struct which contains
+more than one field that is being unsized.
 
 Erroneous code example:
 
@@ -17,39 +17,14 @@ struct Foo<T: ?Sized, U: ?Sized> {
 impl<T, U> CoerceUnsized<Foo<U, T>> for Foo<T, U> {}
 ```
 
-A struct with more than one field containing an unsized type cannot implement
-`CoerceUnsized`. This only occurs when you are trying to coerce one of the
-types in your struct to another type in the struct. In this case we try to
-impl `CoerceUnsized` from `T` to `U` which are both types that the struct
-takes. An [unsized type][1] is any type that the compiler doesn't know the
-length or alignment of at compile time. Any struct containing an unsized type
-is also unsized.
+`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
+like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
+is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
+trait.
 
-`CoerceUnsized` only allows for coercion from a structure with a single
-unsized type field to another struct with a single unsized type field.
-In fact Rust only allows for a struct to have one unsized type in a struct
-and that unsized type must be the last field in the struct. So having two
-unsized types in a single struct is not allowed by the compiler. To fix this
-use only one field containing an unsized type in the struct and then use
-multiple structs to manage each unsized type field you need.
+If the struct has multiple fields that must be unsized, then the compiler has no
+way to generate a valid implementation of `CoerceUnsized` or `DispatchFromDyn`.
 
-Example:
-
-```
-#![feature(coerce_unsized)]
-use std::ops::CoerceUnsized;
-
-struct Foo<T: ?Sized> {
-    a: i32,
-    b: T,
-}
-
-impl <T, U> CoerceUnsized<Foo<U>> for Foo<T>
-    where T: CoerceUnsized<U> {}
-
-fn coerce_foo<T: CoerceUnsized<U>, U>(t: T) -> Foo<U> {
-    Foo { a: 12i32, b: t } // we use coercion to get the `Foo<U>` type we need
-}
-```
-
-[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
+Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
+like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
+that they are pointing at.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0376.md b/compiler/rustc_error_codes/src/error_codes/E0376.md
index 50de15bd30f..5b564ec22fc 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0376.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0376.md
@@ -1,8 +1,11 @@
-`CoerceUnsized` was implemented on something that isn't a struct.
+#### Note: this error code is no longer emitted by the compiler.
+
+`CoerceUnsized` or `DispatchFromDyn` was implemented between two types that
+are not structs.
 
 Erroneous code example:
 
-```compile_fail,E0376
+```compile_fail,E0377
 #![feature(coerce_unsized)]
 use std::ops::CoerceUnsized;
 
@@ -14,33 +17,4 @@ struct Foo<T: ?Sized> {
 impl<T, U> CoerceUnsized<U> for Foo<T> {}
 ```
 
-`CoerceUnsized` can only be implemented for a struct. Unsized types are
-already able to be coerced without an implementation of `CoerceUnsized`
-whereas a struct containing an unsized type needs to know the unsized type
-field it's containing is able to be coerced. An [unsized type][1]
-is any type that the compiler doesn't know the length or alignment of at
-compile time. Any struct containing an unsized type is also unsized.
-
-[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
-
-The `CoerceUnsized` trait takes a struct type. Make sure the type you are
-providing to `CoerceUnsized` is a struct with only the last field containing an
-unsized type.
-
-Example:
-
-```
-#![feature(coerce_unsized)]
-use std::ops::CoerceUnsized;
-
-struct Foo<T> {
-    a: T,
-}
-
-// The `Foo<U>` is a struct so `CoerceUnsized` can be implemented
-impl<T, U> CoerceUnsized<Foo<U>> for Foo<T> where T: CoerceUnsized<U> {}
-```
-
-Note that in Rust, structs can only contain an unsized type if the field
-containing the unsized type is the last and only unsized type field in the
-struct.
+`CoerceUnsized` or `DispatchFromDyn` can only be implemented between structs.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0377.md b/compiler/rustc_error_codes/src/error_codes/E0377.md
index b1d36406332..cd2b26260a8 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0377.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0377.md
@@ -1,5 +1,5 @@
-The trait `CoerceUnsized` may only be implemented for a coercion between
-structures with the same definition.
+`CoerceUnsized` or `DispatchFromDyn` may only be implemented between structs
+of the same type.
 
 Example of erroneous code:
 
@@ -20,10 +20,15 @@ pub struct Bar<T: ?Sized> {
 impl<T, U> CoerceUnsized<Bar<U>> for Foo<T> where T: CoerceUnsized<U> {}
 ```
 
-When attempting to implement `CoerceUnsized`, the `impl` signature must look
-like: `impl CoerceUnsized<Type<U>> for Type<T> where T: CoerceUnsized<U>`;
-the *implementer* and *`CoerceUnsized` type parameter* must be the same
-type. In this example, `Bar` and `Foo` (even though structurally identical)
-are *not* the same type and are rejected. Learn more about the `CoerceUnsized`
-trait and DST coercion in
-[the `CoerceUnsized` docs](../std/ops/trait.CoerceUnsized.html).
+`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
+like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
+is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
+trait.
+
+The compiler cannot support coercions between structs of different types, so
+a valid implementation of `CoerceUnsized` or `DispatchFromDyn` should be
+implemented between the same struct with different generic parameters.
+
+Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
+like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
+that they are pointing at.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 47d5976be09..6badd290917 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -85,6 +85,10 @@ hir_analysis_cmse_output_stack_spill =
     .note1 = functions with the `{$abi}` ABI must pass their result via the available return registers
     .note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
 
+hir_analysis_coerce_multi = implementing `{$trait_name}` does not allow multiple fields to be coerced
+    .note = the trait `{$trait_name}` may only be implemented when a single field is being coerced
+    .label = these fields must be coerced for `{$trait_name}` to be valid
+
 hir_analysis_coerce_pointee_no_field = `CoercePointee` can only be derived on `struct`s with at least one field
 
 hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden
@@ -95,12 +99,12 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
 
 hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
 
+hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}`
+    .label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
+
 hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
 
-hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions
-    .note = `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced
-    .coercions_note = currently, {$number} fields need coercions: {$coercions}
-    .label = requires multiple coercions
+hir_analysis_coerce_zero = implementing `{$trait_name}` requires a field to be coerced
 
 hir_analysis_coercion_between_struct_same_note = expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}`
 
@@ -139,10 +143,6 @@ hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `
 hir_analysis_cross_crate_traits_defined = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type defined in the current crate
     .label = can't implement cross-crate trait for type in another crate
 
-hir_analysis_dispatch_from_dyn_multi = implementing the `DispatchFromDyn` trait requires multiple coercions
-    .note = the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
-    .coercions_note = currently, {$number} fields need coercions: {$coercions}
-
 hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`
 
 hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index b46b805f0a9..cee2f487639 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeVisitableExt, TypingMode, suggest_constraining_type_params,
 };
-use rustc_span::{DUMMY_SP, Span};
+use rustc_span::{DUMMY_SP, Span, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::traits::misc::{
     ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason,
@@ -195,8 +195,14 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E
     // Just compute this for the side-effects, in particular reporting
     // errors; other parts of the code may demand it for the info of
     // course.
-    let span = tcx.def_span(impl_did);
-    tcx.at(span).ensure_ok().coerce_unsized_info(impl_did)
+    tcx.ensure_ok().coerce_unsized_info(impl_did)
+}
+
+fn is_from_coerce_pointee_derive(tcx: TyCtxt<'_>, span: Span) -> bool {
+    span.ctxt()
+        .outer_expn_data()
+        .macro_def_id
+        .is_some_and(|def_id| tcx.is_diagnostic_item(sym::CoercePointee, def_id))
 }
 
 fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
@@ -206,17 +212,29 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
     debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
 
     let span = tcx.def_span(impl_did);
+    let trait_name = "DispatchFromDyn";
 
     let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
 
     let source = trait_ref.self_ty();
-    assert!(!source.has_escaping_bound_vars());
     let target = {
         assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
 
         trait_ref.args.type_at(1)
     };
 
+    // Check `CoercePointee` impl is WF -- if not, then there's no reason to report
+    // redundant errors for `DispatchFromDyn`. This is best effort, though.
+    let mut res = Ok(());
+    tcx.for_each_relevant_impl(
+        tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)),
+        source,
+        |impl_def_id| {
+            res = res.and(tcx.ensure_ok().coerce_unsized_info(impl_def_id));
+        },
+    );
+    res?;
+
     debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target);
 
     let param_env = tcx.param_env(impl_did);
@@ -242,26 +260,25 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
             if def_a != def_b {
                 let source_path = tcx.def_path_str(def_a.did());
                 let target_path = tcx.def_path_str(def_b.did());
-
-                return Err(tcx.dcx().emit_err(errors::DispatchFromDynCoercion {
+                return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
                     span,
-                    trait_name: "DispatchFromDyn",
+                    trait_name,
                     note: true,
                     source_path,
                     target_path,
                 }));
             }
 
-            let mut res = Ok(());
             if def_a.repr().c() || def_a.repr().packed() {
-                res = Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }));
+                return Err(tcx.dcx().emit_err(errors::DispatchFromDynRepr { span }));
             }
 
             let fields = &def_a.non_enum_variant().fields;
 
+            let mut res = Ok(());
             let coerced_fields = fields
-                .iter()
-                .filter(|field| {
+                .iter_enumerated()
+                .filter_map(|(i, field)| {
                     // Ignore PhantomData fields
                     let unnormalized_ty = tcx.type_of(field.did).instantiate_identity();
                     if tcx
@@ -272,7 +289,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
                         .unwrap_or(unnormalized_ty)
                         .is_phantom_data()
                     {
-                        return false;
+                        return None;
                     }
 
                     let ty_a = field.ty(tcx, args_a);
@@ -290,7 +307,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
                             && !ty_a.has_non_region_param()
                         {
                             // ignore 1-ZST fields
-                            return false;
+                            return None;
                         }
 
                         res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST {
@@ -299,64 +316,57 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
                             ty: ty_a,
                         }));
 
-                        return false;
+                        None
+                    } else {
+                        Some((i, ty_a, ty_b, tcx.def_span(field.did)))
                     }
-
-                    true
                 })
                 .collect::<Vec<_>>();
+            res?;
 
             if coerced_fields.is_empty() {
-                res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle {
+                return Err(tcx.dcx().emit_err(errors::CoerceNoField {
                     span,
-                    trait_name: "DispatchFromDyn",
+                    trait_name,
                     note: true,
                 }));
-            } else if coerced_fields.len() > 1 {
-                res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti {
-                    span,
-                    coercions_note: true,
-                    number: coerced_fields.len(),
-                    coercions: coerced_fields
-                        .iter()
-                        .map(|field| {
-                            format!(
-                                "`{}` (`{}` to `{}`)",
-                                field.name,
-                                field.ty(tcx, args_a),
-                                field.ty(tcx, args_b),
-                            )
-                        })
-                        .collect::<Vec<_>>()
-                        .join(", "),
-                }));
-            } else {
+            } else if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] {
                 let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
-                for field in coerced_fields {
-                    ocx.register_obligation(Obligation::new(
-                        tcx,
-                        cause.clone(),
-                        param_env,
-                        ty::TraitRef::new(
-                            tcx,
-                            dispatch_from_dyn_trait,
-                            [field.ty(tcx, args_a), field.ty(tcx, args_b)],
-                        ),
-                    ));
-                }
+                ocx.register_obligation(Obligation::new(
+                    tcx,
+                    cause.clone(),
+                    param_env,
+                    ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]),
+                ));
                 let errors = ocx.select_all_or_error();
                 if !errors.is_empty() {
-                    res = Err(infcx.err_ctxt().report_fulfillment_errors(errors));
+                    if is_from_coerce_pointee_derive(tcx, span) {
+                        return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {
+                            span,
+                            trait_name,
+                            ty: trait_ref.self_ty(),
+                            field_span,
+                            field_ty: ty_a,
+                        }));
+                    } else {
+                        return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
+                    }
                 }
 
                 // Finally, resolve all regions.
-                res = res.and(ocx.resolve_regions_and_report_errors(impl_did, param_env, []));
+                ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
+
+                Ok(())
+            } else {
+                return Err(tcx.dcx().emit_err(errors::CoerceMulti {
+                    span,
+                    trait_name,
+                    number: coerced_fields.len(),
+                    fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
+                }));
             }
-            res
         }
-        _ => Err(tcx
-            .dcx()
-            .emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" })),
+        _ => Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })),
     }
 }
 
@@ -366,13 +376,14 @@ pub(crate) fn coerce_unsized_info<'tcx>(
 ) -> Result<CoerceUnsizedInfo, ErrorGuaranteed> {
     debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
     let span = tcx.def_span(impl_did);
+    let trait_name = "CoerceUnsized";
 
     let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
-
     let unsize_trait = tcx.require_lang_item(LangItem::Unsize, Some(span));
 
     let source = tcx.type_of(impl_did).instantiate_identity();
     let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity();
+
     assert_eq!(trait_ref.def_id, coerce_unsized_trait);
     let target = trait_ref.args.type_at(1);
     debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
@@ -399,9 +410,9 @@ pub(crate) fn coerce_unsized_info<'tcx>(
                 )
                 .emit();
         }
-        (mt_a.ty, mt_b.ty, unsize_trait, None)
+        (mt_a.ty, mt_b.ty, unsize_trait, None, span)
     };
-    let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
+    let (source, target, trait_def_id, kind, field_span) = match (source.kind(), target.kind()) {
         (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
             infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
             let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
@@ -422,9 +433,9 @@ pub(crate) fn coerce_unsized_info<'tcx>(
             if def_a != def_b {
                 let source_path = tcx.def_path_str(def_a.did());
                 let target_path = tcx.def_path_str(def_b.did());
-                return Err(tcx.dcx().emit_err(errors::DispatchFromDynSame {
+                return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
                     span,
-                    trait_name: "CoerceUnsized",
+                    trait_name,
                     note: true,
                     source_path,
                     target_path,
@@ -504,14 +515,14 @@ pub(crate) fn coerce_unsized_info<'tcx>(
 
                     // Collect up all fields that were significantly changed
                     // i.e., those that contain T in coerce_unsized T -> U
-                    Some((i, a, b))
+                    Some((i, a, b, tcx.def_span(f.did)))
                 })
                 .collect::<Vec<_>>();
 
             if diff_fields.is_empty() {
-                return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField {
+                return Err(tcx.dcx().emit_err(errors::CoerceNoField {
                     span,
-                    trait_name: "CoerceUnsized",
+                    trait_name,
                     note: true,
                 }));
             } else if diff_fields.len() > 1 {
@@ -522,27 +533,21 @@ pub(crate) fn coerce_unsized_info<'tcx>(
                     tcx.def_span(impl_did)
                 };
 
-                return Err(tcx.dcx().emit_err(errors::CoerceUnsizedMulti {
+                return Err(tcx.dcx().emit_err(errors::CoerceMulti {
                     span,
-                    coercions_note: true,
+                    trait_name,
                     number: diff_fields.len(),
-                    coercions: diff_fields
-                        .iter()
-                        .map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b))
-                        .collect::<Vec<_>>()
-                        .join(", "),
+                    fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
                 }));
             }
 
-            let (i, a, b) = diff_fields[0];
+            let (i, a, b, field_span) = diff_fields[0];
             let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
-            (a, b, coerce_unsized_trait, Some(kind))
+            (a, b, coerce_unsized_trait, Some(kind), field_span)
         }
 
         _ => {
-            return Err(tcx
-                .dcx()
-                .emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" }));
+            return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name }));
         }
     };
 
@@ -557,12 +562,23 @@ pub(crate) fn coerce_unsized_info<'tcx>(
     );
     ocx.register_obligation(obligation);
     let errors = ocx.select_all_or_error();
+
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(errors);
+        if is_from_coerce_pointee_derive(tcx, span) {
+            return Err(tcx.dcx().emit_err(errors::CoerceFieldValidity {
+                span,
+                trait_name,
+                ty: trait_ref.self_ty(),
+                field_span,
+                field_ty: source,
+            }));
+        } else {
+            return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
+        }
     }
 
     // Finally, resolve all regions.
-    let _ = ocx.resolve_regions_and_report_errors(impl_did, param_env, []);
+    ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
 
     Ok(CoerceUnsizedInfo { custom_kind: kind })
 }
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 1a0b0edb257..99262f9871e 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1165,18 +1165,6 @@ pub(crate) struct InherentTyOutside {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_coerce_unsized_may, code = E0378)]
-pub(crate) struct DispatchFromDynCoercion<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub trait_name: &'a str,
-    #[note(hir_analysis_coercion_between_struct_same_note)]
-    pub note: bool,
-    pub source_path: String,
-    pub target_path: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(hir_analysis_dispatch_from_dyn_repr, code = E0378)]
 pub(crate) struct DispatchFromDynRepr {
     #[primary_span]
@@ -1293,41 +1281,40 @@ pub(crate) struct DispatchFromDynZST<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_coerce_unsized_may, code = E0378)]
-pub(crate) struct DispatchFromDynSingle<'a> {
+#[diag(hir_analysis_coerce_zero, code = E0374)]
+pub(crate) struct CoerceNoField {
     #[primary_span]
     pub span: Span,
-    pub trait_name: &'a str,
+    pub trait_name: &'static str,
     #[note(hir_analysis_coercion_between_struct_single_note)]
     pub note: bool,
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_dispatch_from_dyn_multi, code = E0378)]
-#[note]
-pub(crate) struct DispatchFromDynMulti {
+#[diag(hir_analysis_coerce_multi, code = E0375)]
+pub(crate) struct CoerceMulti {
+    pub trait_name: &'static str,
     #[primary_span]
     pub span: Span,
-    #[note(hir_analysis_coercions_note)]
-    pub coercions_note: bool,
     pub number: usize,
-    pub coercions: String,
+    #[note]
+    pub fields: MultiSpan,
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_coerce_unsized_may, code = E0376)]
-pub(crate) struct DispatchFromDynStruct<'a> {
+#[diag(hir_analysis_coerce_unsized_may, code = E0377)]
+pub(crate) struct CoerceUnsizedNonStruct {
     #[primary_span]
     pub span: Span,
-    pub trait_name: &'a str,
+    pub trait_name: &'static str,
 }
 
 #[derive(Diagnostic)]
 #[diag(hir_analysis_coerce_unsized_may, code = E0377)]
-pub(crate) struct DispatchFromDynSame<'a> {
+pub(crate) struct CoerceSameStruct {
     #[primary_span]
     pub span: Span,
-    pub trait_name: &'a str,
+    pub trait_name: &'static str,
     #[note(hir_analysis_coercion_between_struct_same_note)]
     pub note: bool,
     pub source_path: String,
@@ -1335,34 +1322,15 @@ pub(crate) struct DispatchFromDynSame<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_coerce_unsized_may, code = E0374)]
-pub(crate) struct CoerceUnsizedOneField<'a> {
+#[diag(hir_analysis_coerce_unsized_field_validity)]
+pub(crate) struct CoerceFieldValidity<'tcx> {
     #[primary_span]
     pub span: Span,
-    pub trait_name: &'a str,
-    #[note(hir_analysis_coercion_between_struct_single_note)]
-    pub note: bool,
-}
-
-#[derive(Diagnostic)]
-#[diag(hir_analysis_coerce_unsized_multi, code = E0375)]
-#[note]
-pub(crate) struct CoerceUnsizedMulti {
-    #[primary_span]
+    pub ty: Ty<'tcx>,
+    pub trait_name: &'static str,
     #[label]
-    pub span: Span,
-    #[note(hir_analysis_coercions_note)]
-    pub coercions_note: bool,
-    pub number: usize,
-    pub coercions: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(hir_analysis_coerce_unsized_may, code = E0378)]
-pub(crate) struct CoerceUnsizedMay<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub trait_name: &'a str,
+    pub field_span: Span,
+    pub field_ty: Ty<'tcx>,
 }
 
 #[derive(Diagnostic)]
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 842a48e1606..b0571bf7247 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -1302,6 +1302,7 @@ pub trait FnPtr: Copy + Clone {
 /// ```
 #[rustc_builtin_macro(CoercePointee, attributes(pointee))]
 #[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize, coerce_pointee_validated)]
+#[cfg_attr(not(test), rustc_diagnostic_item = "CoercePointee")]
 #[unstable(feature = "derive_coerce_pointee", issue = "123430")]
 pub macro CoercePointee($item:item) {
     /* compiler built-in */
diff --git a/tests/ui/coercion/issue-26905.stderr b/tests/ui/coercion/issue-26905.stderr
index 86f6a14cd10..17387ae992b 100644
--- a/tests/ui/coercion/issue-26905.stderr
+++ b/tests/ui/coercion/issue-26905.stderr
@@ -1,11 +1,16 @@
-error[E0375]: implementing the trait `CoerceUnsized` requires multiple coercions
+error[E0375]: implementing `CoerceUnsized` does not allow multiple fields to be coerced
   --> $DIR/issue-26905.rs:16:40
    |
 LL | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<MyRc<U>> for MyRc<T>{ }
-   |                                        ^^^^^^^^^^^^^^^^^^^^^^ requires multiple coercions
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced
-   = note: currently, 2 fields need coercions: `_ptr` (`*const T` to `*const U`), `_boo` (`NotPhantomData<T>` to `NotPhantomData<U>`)
+note: the trait `CoerceUnsized` may only be implemented when a single field is being coerced
+  --> $DIR/issue-26905.rs:12:5
+   |
+LL |     _ptr: *const T,
+   |     ^^^^^^^^^^^^^^
+LL |     _boo: NotPhantomData<T>,
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.rs b/tests/ui/deriving/deriving-coerce-pointee-neg.rs
index 6577500d8eb..2713366945e 100644
--- a/tests/ui/deriving/deriving-coerce-pointee-neg.rs
+++ b/tests/ui/deriving/deriving-coerce-pointee-neg.rs
@@ -142,4 +142,27 @@ struct TryToWipeRepr<'a, #[pointee] T: ?Sized> {
     ptr: &'a T,
 }
 
+#[repr(transparent)]
+#[derive(CoercePointee)]
+//~^ ERROR for `RcWithId<T>` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `Rc<(i32, Box<T>)>`
+struct RcWithId<T: ?Sized> {
+    inner: std::rc::Rc<(i32, Box<T>)>,
+}
+
+#[repr(transparent)]
+#[derive(CoercePointee)]
+//~^ ERROR implementing `CoerceUnsized` does not allow multiple fields to be coerced
+struct MoreThanOneField<T: ?Sized> {
+    //~^ ERROR transparent struct needs at most one field with non-trivial size or alignment, but has 2
+    inner1: Box<T>,
+    inner2: Box<T>,
+}
+
+struct NotCoercePointeeData<T: ?Sized>(T);
+
+#[repr(transparent)]
+#[derive(CoercePointee)]
+//~^ ERROR for `UsingNonCoercePointeeData<T>` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData<T>`
+struct UsingNonCoercePointeeData<T: ?Sized>(NotCoercePointeeData<T>);
+
 fn main() {}
diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr
index 999214bfa9f..d3d73132078 100644
--- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr
+++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr
@@ -118,7 +118,55 @@ error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(
 LL | struct TryToWipeRepr<'a, #[pointee] T: ?Sized> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 17 previous errors
+error: for `RcWithId<T>` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `Rc<(i32, Box<T>)>`
+  --> $DIR/deriving-coerce-pointee-neg.rs:146:10
+   |
+LL | #[derive(CoercePointee)]
+   |          ^^^^^^^^^^^^^
+...
+LL |     inner: std::rc::Rc<(i32, Box<T>)>,
+   |     --------------------------------- `Rc<(i32, Box<T>)>` must be a pointer, reference, or smart pointer that is allowed to be unsized
+   |
+   = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0375]: implementing `CoerceUnsized` does not allow multiple fields to be coerced
+  --> $DIR/deriving-coerce-pointee-neg.rs:153:10
+   |
+LL | #[derive(CoercePointee)]
+   |          ^^^^^^^^^^^^^
+   |
+note: the trait `CoerceUnsized` may only be implemented when a single field is being coerced
+  --> $DIR/deriving-coerce-pointee-neg.rs:157:5
+   |
+LL |     inner1: Box<T>,
+   |     ^^^^^^^^^^^^^^
+LL |     inner2: Box<T>,
+   |     ^^^^^^^^^^^^^^
+   = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: for `UsingNonCoercePointeeData<T>` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData<T>`
+  --> $DIR/deriving-coerce-pointee-neg.rs:164:10
+   |
+LL | #[derive(CoercePointee)]
+   |          ^^^^^^^^^^^^^
+LL |
+LL | struct UsingNonCoercePointeeData<T: ?Sized>(NotCoercePointeeData<T>);
+   |                                             ----------------------- `NotCoercePointeeData<T>` must be a pointer, reference, or smart pointer that is allowed to be unsized
+   |
+   = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
+  --> $DIR/deriving-coerce-pointee-neg.rs:155:1
+   |
+LL | struct MoreThanOneField<T: ?Sized> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
+LL |
+LL |     inner1: Box<T>,
+   |     -------------- this field has non-zero size or requires alignment
+LL |     inner2: Box<T>,
+   |     -------------- this field has non-zero size or requires alignment
+
+error: aborting due to 21 previous errors
 
-Some errors have detailed explanations: E0392, E0802.
-For more information about an error, try `rustc --explain E0392`.
+Some errors have detailed explanations: E0375, E0392, E0690, E0802.
+For more information about an error, try `rustc --explain E0375`.
diff --git a/tests/ui/error-codes/E0374.stderr b/tests/ui/error-codes/E0374.stderr
index 71eec4c16fd..95e6b95e0d5 100644
--- a/tests/ui/error-codes/E0374.stderr
+++ b/tests/ui/error-codes/E0374.stderr
@@ -6,7 +6,7 @@ LL | struct Foo<T: ?Sized> {
    |
    = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
 
-error[E0374]: the trait `CoerceUnsized` may only be implemented for a coercion between structures
+error[E0374]: implementing `CoerceUnsized` requires a field to be coerced
   --> $DIR/E0374.rs:8:1
    |
 LL | / impl<T, U> CoerceUnsized<Foo<U>> for Foo<T>
diff --git a/tests/ui/error-codes/E0375.stderr b/tests/ui/error-codes/E0375.stderr
index af720bd40e7..a797ba9d461 100644
--- a/tests/ui/error-codes/E0375.stderr
+++ b/tests/ui/error-codes/E0375.stderr
@@ -23,14 +23,19 @@ help: the `Box` type always has a statically known size and allocates its conten
 LL |     b: Box<T>,
    |        ++++ +
 
-error[E0375]: implementing the trait `CoerceUnsized` requires multiple coercions
+error[E0375]: implementing `CoerceUnsized` does not allow multiple fields to be coerced
   --> $DIR/E0375.rs:10:12
    |
 LL | impl<T, U> CoerceUnsized<Foo<U, T>> for Foo<T, U> {}
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^ requires multiple coercions
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced
-   = note: currently, 2 fields need coercions: `b` (`T` to `U`), `c` (`U` to `T`)
+note: the trait `CoerceUnsized` may only be implemented when a single field is being coerced
+  --> $DIR/E0375.rs:6:5
+   |
+LL |     b: T,
+   |     ^^^^
+LL |     c: U,
+   |     ^^^^
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/error-codes/E0376.rs b/tests/ui/error-codes/E0376.rs
deleted file mode 100644
index f092eb02c2b..00000000000
--- a/tests/ui/error-codes/E0376.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-#![feature(coerce_unsized)]
-use std::ops::CoerceUnsized;
-
-struct Foo<T: ?Sized> {
-    a: T,
-}
-
-impl<T, U> CoerceUnsized<U> for Foo<T> {} //~ ERROR E0376
-
-fn main() {}
diff --git a/tests/ui/error-codes/E0376.stderr b/tests/ui/error-codes/E0376.stderr
deleted file mode 100644
index 46668e05a42..00000000000
--- a/tests/ui/error-codes/E0376.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0376]: the trait `CoerceUnsized` may only be implemented for a coercion between structures
-  --> $DIR/E0376.rs:8:1
-   |
-LL | impl<T, U> CoerceUnsized<U> for Foo<T> {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0376`.
diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.rs b/tests/ui/invalid_dispatch_from_dyn_impls.rs
index b7bc766fbe0..972465d7197 100644
--- a/tests/ui/invalid_dispatch_from_dyn_impls.rs
+++ b/tests/ui/invalid_dispatch_from_dyn_impls.rs
@@ -8,9 +8,10 @@ use std::{
 struct WrapperWithExtraField<T>(T, i32);
 
 impl<T, U> DispatchFromDyn<WrapperWithExtraField<U>> for WrapperWithExtraField<T>
+//~^ ERROR [E0378]
 where
     T: DispatchFromDyn<U>,
-{} //~^^^ ERROR [E0378]
+{}
 
 
 struct MultiplePointers<T: ?Sized>{
@@ -19,9 +20,10 @@ struct MultiplePointers<T: ?Sized>{
 }
 
 impl<T: ?Sized, U: ?Sized> DispatchFromDyn<MultiplePointers<U>> for MultiplePointers<T>
+//~^ implementing `DispatchFromDyn` does not allow multiple fields to be coerced
 where
     T: Unsize<U>,
-{} //~^^^ ERROR [E0378]
+{}
 
 
 struct NothingToCoerce<T: ?Sized> {
@@ -29,23 +31,25 @@ struct NothingToCoerce<T: ?Sized> {
 }
 
 impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NothingToCoerce<T>> for NothingToCoerce<U> {}
-//~^ ERROR [E0378]
+//~^ ERROR implementing `DispatchFromDyn` requires a field to be coerced
 
 #[repr(C)]
 struct HasReprC<T: ?Sized>(Box<T>);
 
 impl<T: ?Sized, U: ?Sized> DispatchFromDyn<HasReprC<U>> for HasReprC<T>
+//~^ ERROR [E0378]
 where
     T: Unsize<U>,
-{} //~^^^ ERROR [E0378]
+{}
 
 #[repr(align(64))]
 struct OverAlignedZst;
 struct OverAligned<T: ?Sized>(Box<T>, OverAlignedZst);
 
 impl<T: ?Sized, U: ?Sized> DispatchFromDyn<OverAligned<U>> for OverAligned<T>
+//~^ ERROR [E0378]
     where
         T: Unsize<U>,
-{} //~^^^ ERROR [E0378]
+{}
 
 fn main() {}
diff --git a/tests/ui/invalid_dispatch_from_dyn_impls.stderr b/tests/ui/invalid_dispatch_from_dyn_impls.stderr
index 02718334c73..93ec6bbe089 100644
--- a/tests/ui/invalid_dispatch_from_dyn_impls.stderr
+++ b/tests/ui/invalid_dispatch_from_dyn_impls.stderr
@@ -2,25 +2,32 @@ error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs co
   --> $DIR/invalid_dispatch_from_dyn_impls.rs:10:1
    |
 LL | / impl<T, U> DispatchFromDyn<WrapperWithExtraField<U>> for WrapperWithExtraField<T>
+LL | |
 LL | | where
 LL | |     T: DispatchFromDyn<U>,
    | |__________________________^
    |
    = note: extra field `1` of type `i32` is not allowed
 
-error[E0378]: implementing the `DispatchFromDyn` trait requires multiple coercions
-  --> $DIR/invalid_dispatch_from_dyn_impls.rs:21:1
+error[E0375]: implementing `DispatchFromDyn` does not allow multiple fields to be coerced
+  --> $DIR/invalid_dispatch_from_dyn_impls.rs:22:1
    |
 LL | / impl<T: ?Sized, U: ?Sized> DispatchFromDyn<MultiplePointers<U>> for MultiplePointers<T>
+LL | |
 LL | | where
 LL | |     T: Unsize<U>,
    | |_________________^
    |
-   = note: the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
-   = note: currently, 2 fields need coercions: `ptr1` (`*const T` to `*const U`), `ptr2` (`*const T` to `*const U`)
+note: the trait `DispatchFromDyn` may only be implemented when a single field is being coerced
+  --> $DIR/invalid_dispatch_from_dyn_impls.rs:18:5
+   |
+LL |     ptr1: *const T,
+   |     ^^^^^^^^^^^^^^
+LL |     ptr2: *const T,
+   |     ^^^^^^^^^^^^^^
 
-error[E0378]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures
-  --> $DIR/invalid_dispatch_from_dyn_impls.rs:31:1
+error[E0374]: implementing `DispatchFromDyn` requires a field to be coerced
+  --> $DIR/invalid_dispatch_from_dyn_impls.rs:33:1
    |
 LL | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NothingToCoerce<T>> for NothingToCoerce<U> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -28,17 +35,19 @@ LL | impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NothingToCoerce<T>> for NothingT
    = note: expected a single field to be coerced, none found
 
 error[E0378]: structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`
-  --> $DIR/invalid_dispatch_from_dyn_impls.rs:37:1
+  --> $DIR/invalid_dispatch_from_dyn_impls.rs:39:1
    |
 LL | / impl<T: ?Sized, U: ?Sized> DispatchFromDyn<HasReprC<U>> for HasReprC<T>
+LL | |
 LL | | where
 LL | |     T: Unsize<U>,
    | |_________________^
 
 error[E0378]: the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else
-  --> $DIR/invalid_dispatch_from_dyn_impls.rs:46:1
+  --> $DIR/invalid_dispatch_from_dyn_impls.rs:49:1
    |
 LL | / impl<T: ?Sized, U: ?Sized> DispatchFromDyn<OverAligned<U>> for OverAligned<T>
+LL | |
 LL | |     where
 LL | |         T: Unsize<U>,
    | |_____________________^
@@ -47,4 +56,5 @@ LL | |         T: Unsize<U>,
 
 error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0378`.
+Some errors have detailed explanations: E0374, E0375, E0378.
+For more information about an error, try `rustc --explain E0374`.
diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.rs b/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.rs
index 71f198f7dc7..94b76fe9685 100644
--- a/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.rs
+++ b/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.rs
@@ -15,7 +15,7 @@ struct Dispatchable<T: ?Sized, Z> {
 }
 
 impl<T, U> DispatchFromDyn<Dispatchable<U, i32>> for Dispatchable<T, ()>
-//~^ ERROR implementing the `DispatchFromDyn` trait requires multiple coercions
+//~^ ERROR implementing `DispatchFromDyn` does not allow multiple fields to be coerced
 where
     T: Unsize<U> + ?Sized,
     U: ?Sized,
diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.stderr b/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.stderr
index 1f13c51f679..91760b9e2ea 100644
--- a/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.stderr
+++ b/tests/ui/self/dispatch-from-dyn-zst-transmute-zst-nonzst.stderr
@@ -1,4 +1,4 @@
-error[E0378]: implementing the `DispatchFromDyn` trait requires multiple coercions
+error[E0375]: implementing `DispatchFromDyn` does not allow multiple fields to be coerced
   --> $DIR/dispatch-from-dyn-zst-transmute-zst-nonzst.rs:17:1
    |
 LL | / impl<T, U> DispatchFromDyn<Dispatchable<U, i32>> for Dispatchable<T, ()>
@@ -8,9 +8,14 @@ LL | |     T: Unsize<U> + ?Sized,
 LL | |     U: ?Sized,
    | |______________^
    |
-   = note: the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
-   = note: currently, 2 fields need coercions: `_ptr` (`Box<T>` to `Box<U>`), `z` (`()` to `i32`)
+note: the trait `DispatchFromDyn` may only be implemented when a single field is being coerced
+  --> $DIR/dispatch-from-dyn-zst-transmute-zst-nonzst.rs:13:5
+   |
+LL |     _ptr: Box<T>,
+   |     ^^^^^^^^^^^^
+LL |     z: Z,
+   |     ^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0378`.
+For more information about this error, try `rustc --explain E0375`.
diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute.rs b/tests/ui/self/dispatch-from-dyn-zst-transmute.rs
index 57c255b4d7b..967958ab486 100644
--- a/tests/ui/self/dispatch-from-dyn-zst-transmute.rs
+++ b/tests/ui/self/dispatch-from-dyn-zst-transmute.rs
@@ -15,7 +15,7 @@ struct Foo<'a, U: ?Sized> {
 }
 
 impl<'a, T, U> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T>
-//~^ ERROR implementing the `DispatchFromDyn` trait requires multiple coercions
+//~^ ERROR implementing `DispatchFromDyn` does not allow multiple fields to be coerced
 where
     T: Unsize<U> + ?Sized,
     U: ?Sized {}
diff --git a/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr b/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr
index 5a8ae88b5f1..cc8be45e99d 100644
--- a/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr
+++ b/tests/ui/self/dispatch-from-dyn-zst-transmute.stderr
@@ -1,4 +1,4 @@
-error[E0378]: implementing the `DispatchFromDyn` trait requires multiple coercions
+error[E0375]: implementing `DispatchFromDyn` does not allow multiple fields to be coerced
   --> $DIR/dispatch-from-dyn-zst-transmute.rs:17:1
    |
 LL | / impl<'a, T, U> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T>
@@ -8,9 +8,14 @@ LL | |     T: Unsize<U> + ?Sized,
 LL | |     U: ?Sized {}
    | |_____________^
    |
-   = note: the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
-   = note: currently, 2 fields need coercions: `token` (`IsSendToken<T>` to `IsSendToken<U>`), `ptr` (`&'a T` to `&'a U`)
+note: the trait `DispatchFromDyn` may only be implemented when a single field is being coerced
+  --> $DIR/dispatch-from-dyn-zst-transmute.rs:13:5
+   |
+LL |     token: IsSendToken<U>,
+   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |     ptr: &'a U,
+   |     ^^^^^^^^^^
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0378`.
+For more information about this error, try `rustc --explain E0375`.
diff --git a/tests/ui/traits/issue-78372.stderr b/tests/ui/traits/issue-78372.stderr
index d4dfba4f039..fbc60ce5d83 100644
--- a/tests/ui/traits/issue-78372.stderr
+++ b/tests/ui/traits/issue-78372.stderr
@@ -65,7 +65,7 @@ LL |     fn foo(self: Smaht<Self, T>);
    = note: type of `self` must be `Self` or a type that dereferences to it
    = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
 
-error[E0378]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures
+error[E0377]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures
   --> $DIR/issue-78372.rs:3:1
    |
 LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
@@ -73,5 +73,5 @@ LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
 
 error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0307, E0378, E0412, E0658.
+Some errors have detailed explanations: E0307, E0377, E0412, E0658.
 For more information about an error, try `rustc --explain E0307`.