diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
index 82417a86dd9ee..5aed9f76f144f 100644
--- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs
@@ -3,11 +3,11 @@ use ast::ptr::P;
 use rustc_ast::mut_visit::MutVisitor;
 use rustc_ast::visit::BoundKind;
 use rustc_ast::{
-    self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
+    self as ast, GenericArg, GenericBound, GenericParamKind, Generics, ItemKind, MetaItem,
     TraitBoundModifiers, VariantData, WherePredicate,
 };
-use rustc_attr_parsing as attr;
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
+use rustc_errors::E0802;
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_macros::Diagnostic;
 use rustc_span::{Ident, Span, Symbol, sym};
@@ -32,15 +32,6 @@ pub(crate) fn expand_deriving_coerce_pointee(
     let (name_ident, generics) = if let Annotatable::Item(aitem) = item
         && let ItemKind::Struct(struct_data, g) = &aitem.kind
     {
-        let is_transparent = aitem.attrs.iter().any(|attr| {
-            attr::find_repr_attrs(cx.sess, attr)
-                .into_iter()
-                .any(|r| matches!(r, attr::ReprTransparent))
-        });
-        if !is_transparent {
-            cx.dcx().emit_err(RequireTransparent { span });
-            return;
-        }
         if !matches!(
             struct_data,
             VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _)
@@ -88,8 +79,7 @@ pub(crate) fn expand_deriving_coerce_pointee(
     } else {
         let mut pointees = type_params
             .iter()
-            .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)))
-            .fuse();
+            .filter_map(|&(idx, span, is_pointee)| is_pointee.then_some((idx, span)));
         match (pointees.next(), pointees.next()) {
             (Some((idx, _span)), None) => idx,
             (None, _) => {
@@ -110,6 +100,52 @@ pub(crate) fn expand_deriving_coerce_pointee(
     // Declare helper function that adds implementation blocks.
     // FIXME(dingxiangfei2009): Investigate the set of attributes on target struct to be propagated to impls
     let attrs = thin_vec![cx.attr_word(sym::automatically_derived, span),];
+    // # Validity assertion which will be checked later in `rustc_hir_analysis::coherence::builtins`.
+    {
+        let trait_path =
+            cx.path_all(span, true, path!(span, core::marker::CoercePointeeValidated), vec![]);
+        let trait_ref = cx.trait_ref(trait_path);
+        push(Annotatable::Item(
+            cx.item(
+                span,
+                Ident::empty(),
+                attrs.clone(),
+                ast::ItemKind::Impl(Box::new(ast::Impl {
+                    safety: ast::Safety::Default,
+                    polarity: ast::ImplPolarity::Positive,
+                    defaultness: ast::Defaultness::Final,
+                    constness: ast::Const::No,
+                    generics: Generics {
+                        params: generics
+                            .params
+                            .iter()
+                            .map(|p| match &p.kind {
+                                GenericParamKind::Lifetime => {
+                                    cx.lifetime_param(p.span(), p.ident, p.bounds.clone())
+                                }
+                                GenericParamKind::Type { default: _ } => {
+                                    cx.typaram(p.span(), p.ident, p.bounds.clone(), None)
+                                }
+                                GenericParamKind::Const { ty, kw_span: _, default: _ } => cx
+                                    .const_param(
+                                        p.span(),
+                                        p.ident,
+                                        p.bounds.clone(),
+                                        ty.clone(),
+                                        None,
+                                    ),
+                            })
+                            .collect(),
+                        where_clause: generics.where_clause.clone(),
+                        span: generics.span,
+                    },
+                    of_trait: Some(trait_ref),
+                    self_ty: self_type.clone(),
+                    items: ThinVec::new(),
+                })),
+            ),
+        ));
+    }
     let mut add_impl_block = |generics, trait_symbol, trait_args| {
         let mut parts = path!(span, core::ops);
         parts.push(Ident::new(trait_symbol, span));
@@ -430,35 +466,35 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for AlwaysErrorOnGenericParam<'a, 'b>
 }
 
 #[derive(Diagnostic)]
-#[diag(builtin_macros_coerce_pointee_requires_transparent)]
+#[diag(builtin_macros_coerce_pointee_requires_transparent, code = E0802)]
 struct RequireTransparent {
     #[primary_span]
     span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(builtin_macros_coerce_pointee_requires_one_field)]
+#[diag(builtin_macros_coerce_pointee_requires_one_field, code = E0802)]
 struct RequireOneField {
     #[primary_span]
     span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(builtin_macros_coerce_pointee_requires_one_generic)]
+#[diag(builtin_macros_coerce_pointee_requires_one_generic, code = E0802)]
 struct RequireOneGeneric {
     #[primary_span]
     span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(builtin_macros_coerce_pointee_requires_one_pointee)]
+#[diag(builtin_macros_coerce_pointee_requires_one_pointee, code = E0802)]
 struct RequireOnePointee {
     #[primary_span]
     span: Span,
 }
 
 #[derive(Diagnostic)]
-#[diag(builtin_macros_coerce_pointee_too_many_pointees)]
+#[diag(builtin_macros_coerce_pointee_too_many_pointees, code = E0802)]
 struct TooManyPointees {
     #[primary_span]
     one: Span,
@@ -467,7 +503,7 @@ struct TooManyPointees {
 }
 
 #[derive(Diagnostic)]
-#[diag(builtin_macros_coerce_pointee_requires_maybe_sized)]
+#[diag(builtin_macros_coerce_pointee_requires_maybe_sized, code = E0802)]
 struct RequiresMaybeSized {
     #[primary_span]
     span: Span,
diff --git a/compiler/rustc_error_codes/src/error_codes/E0802.md b/compiler/rustc_error_codes/src/error_codes/E0802.md
new file mode 100644
index 0000000000000..59061ff04359d
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0802.md
@@ -0,0 +1,94 @@
+The target of `derive(CoercePointee)` macro has inadmissible specification for
+a meaningful use.
+
+Erroneous code examples:
+
+The target data is not a `struct`.
+
+```compile_fail,E0802
+#![feature(coerce_pointee)]
+use std::marker::CoercePointee;
+#[derive(CoercePointee)]
+enum NotStruct<'a, T: ?Sized> {
+    Variant(&'a T),
+}
+```
+
+The target data has a layout that is not transparent, or `repr(transparent)`
+in other words.
+
+```compile_fail,E0802
+#![feature(coerce_pointee)]
+use std::marker::CoercePointee;
+#[derive(CoercePointee)]
+struct NotTransparent<'a, #[pointee] T: ?Sized> {
+    ptr: &'a T,
+}
+```
+
+The target data has no data field.
+
+```compile_fail,E0802
+#![feature(coerce_pointee)]
+use std::marker::CoercePointee;
+#[derive(CoercePointee)]
+#[repr(transparent)]
+struct NoField<'a, #[pointee] T: ?Sized> {}
+```
+
+The target data is not generic over any data, or has no generic type parameter.
+
+```compile_fail,E0802
+#![feature(coerce_pointee)]
+use std::marker::CoercePointee;
+#[derive(CoercePointee)]
+#[repr(transparent)]
+struct NoGeneric<'a>(&'a u8);
+```
+
+The target data has multiple generic type parameters, but none is designated as
+a pointee for coercion.
+
+```compile_fail,E0802
+#![feature(coerce_pointee)]
+use std::marker::CoercePointee;
+#[derive(CoercePointee)]
+#[repr(transparent)]
+struct AmbiguousPointee<'a, T1: ?Sized, T2: ?Sized> {
+    a: (&'a T1, &'a T2),
+}
+```
+
+The target data has multiple generic type parameters that are designated as
+pointees for coercion.
+
+```compile_fail,E0802
+#![feature(coerce_pointee)]
+use std::marker::CoercePointee;
+#[derive(CoercePointee)]
+#[repr(transparent)]
+struct TooManyPointees<
+    'a,
+    #[pointee] A: ?Sized,
+    #[pointee] B: ?Sized>
+((&'a A, &'a B));
+```
+
+The type parameter that is designated as a pointee is not marked `?Sized`.
+
+```compile_fail,E0802
+#![feature(coerce_pointee)]
+use std::marker::CoercePointee;
+#[derive(CoercePointee)]
+#[repr(transparent)]
+struct NoMaybeSized<'a, #[pointee] T> {
+    ptr: &'a T,
+}
+```
+
+In summary, the `CoercePointee` macro demands the type to be a `struct` that is
+generic over at least one type or over more types, one of which is marked with
+`#[pointee]`, and has at least one data field and adopts a `repr(transparent)`
+layout.
+The only generic type or the type marked with `#[pointee]` has to be also
+marked as `?Sized`.
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index 0a30bdb48a09c..e970b16f61064 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -545,6 +545,7 @@ E0798: 0798,
 E0799: 0799,
 E0800: 0800,
 E0801: 0801,
+E0802: 0802,
         );
     )
 }
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 8bf09cf96b3f7..df4a0031e3107 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -1,7 +1,8 @@
 use rustc_ast::ptr::P;
 use rustc_ast::util::literal;
 use rustc_ast::{
-    self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp, attr, token,
+    self as ast, AnonConst, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, UnOp,
+    attr, token,
 };
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
@@ -138,6 +139,42 @@ impl<'a> ExtCtxt<'a> {
         }
     }
 
+    pub fn lifetime_param(
+        &self,
+        span: Span,
+        ident: Ident,
+        bounds: ast::GenericBounds,
+    ) -> ast::GenericParam {
+        ast::GenericParam {
+            id: ast::DUMMY_NODE_ID,
+            ident: ident.with_span_pos(span),
+            attrs: AttrVec::new(),
+            bounds,
+            is_placeholder: false,
+            kind: ast::GenericParamKind::Lifetime,
+            colon_span: None,
+        }
+    }
+
+    pub fn const_param(
+        &self,
+        span: Span,
+        ident: Ident,
+        bounds: ast::GenericBounds,
+        ty: P<ast::Ty>,
+        default: Option<AnonConst>,
+    ) -> ast::GenericParam {
+        ast::GenericParam {
+            id: ast::DUMMY_NODE_ID,
+            ident: ident.with_span_pos(span),
+            attrs: AttrVec::new(),
+            bounds,
+            is_placeholder: false,
+            kind: ast::GenericParamKind::Const { ty, kw_span: DUMMY_SP, default },
+            colon_span: None,
+        }
+    }
+
     pub fn trait_ref(&self, path: ast::Path) -> ast::TraitRef {
         ast::TraitRef { path, ref_id: ast::DUMMY_NODE_ID }
     }
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index b6689c95c4bb2..1852987b1677f 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -370,6 +370,8 @@ language_item_table! {
 
     PointerLike,             sym::pointer_like,        pointer_like,               Target::Trait,          GenericRequirement::Exact(0);
 
+    CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait,     GenericRequirement::Exact(0);
+
     ConstParamTy,            sym::const_param_ty,      const_param_ty_trait,       Target::Trait,          GenericRequirement::Exact(0);
     UnsizedConstParamTy,     sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
 
@@ -429,9 +431,13 @@ language_item_table! {
     ContractCheckRequires,     sym::contract_check_requires,      contract_check_requires_fn,      Target::Fn, GenericRequirement::None;
 }
 
+/// The requirement imposed on the generics of a lang item
 pub enum GenericRequirement {
+    /// No restriction on the generics
     None,
+    /// A minimum number of generics that is demanded on a lang item
     Minimum(usize),
+    /// The number of generics must match precisely as stipulated
     Exact(usize),
 }
 
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index c28c1afcfe66c..258267c5ca929 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -85,6 +85,16 @@ hir_analysis_cmse_output_stack_spill =
     .note1 = functions with the `"{$abi_name}"` 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_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
+
+hir_analysis_coerce_pointee_not_concrete_ty = `derive(CoercePointee)` is only applicable to `struct`
+
+hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applicable to `struct`, instead of `{$kind}`
+
+hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
+
 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
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 3511dbc625295..c5aee60117b95 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -48,6 +48,10 @@ pub(super) fn check_trait<'tcx>(
     checker
         .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn)?;
     checker.check(lang_items.pointer_like(), visit_implementation_of_pointer_like)?;
+    checker.check(
+        lang_items.coerce_pointee_validated_trait(),
+        visit_implementation_of_coerce_pointee_validity,
+    )?;
     Ok(())
 }
 
@@ -782,3 +786,32 @@ fn visit_implementation_of_pointer_like(checker: &Checker<'_>) -> Result<(), Err
         .with_note(why_disqualified)
         .emit())
 }
+
+fn visit_implementation_of_coerce_pointee_validity(
+    checker: &Checker<'_>,
+) -> Result<(), ErrorGuaranteed> {
+    let tcx = checker.tcx;
+    let self_ty = tcx.impl_trait_ref(checker.impl_def_id).unwrap().instantiate_identity().self_ty();
+    let span = tcx.def_span(checker.impl_def_id);
+    if !tcx.is_builtin_derived(checker.impl_def_id.into()) {
+        return Err(tcx.dcx().emit_err(errors::CoercePointeeNoUserValidityAssertion { span }));
+    }
+    let ty::Adt(def, _args) = self_ty.kind() else {
+        return Err(tcx.dcx().emit_err(errors::CoercePointeeNotConcreteType { span }));
+    };
+    let did = def.did();
+    // Now get a more precise span of the `struct`.
+    let span = tcx.def_span(did);
+    if !def.is_struct() {
+        return Err(tcx
+            .dcx()
+            .emit_err(errors::CoercePointeeNotStruct { span, kind: def.descr().into() }));
+    }
+    if !def.repr().transparent() {
+        return Err(tcx.dcx().emit_err(errors::CoercePointeeNotTransparent { span }));
+    }
+    if def.all_fields().next().is_none() {
+        return Err(tcx.dcx().emit_err(errors::CoercePointeeNoField { span }));
+    }
+    Ok(())
+}
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 9769be302264d..12750543f4bf7 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1180,6 +1180,42 @@ pub(crate) struct DispatchFromDynRepr {
     pub span: Span,
 }
 
+#[derive(Diagnostic)]
+#[diag(hir_analysis_coerce_pointee_not_struct, code = E0802)]
+pub(crate) struct CoercePointeeNotStruct {
+    #[primary_span]
+    pub span: Span,
+    pub kind: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_coerce_pointee_not_concrete_ty, code = E0802)]
+pub(crate) struct CoercePointeeNotConcreteType {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_coerce_pointee_no_user_validity_assertion, code = E0802)]
+pub(crate) struct CoercePointeeNoUserValidityAssertion {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_coerce_pointee_not_transparent, code = E0802)]
+pub(crate) struct CoercePointeeNotTransparent {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_coerce_pointee_no_field, code = E0802)]
+pub(crate) struct CoercePointeeNoField {
+    #[primary_span]
+    pub span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(hir_analysis_inherent_ty_outside_relevant, code = E0390)]
 #[help]
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index c819d43323583..1601577669714 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -193,6 +193,7 @@ symbols! {
         Cleanup,
         Clone,
         CoercePointee,
+        CoercePointeeValidated,
         CoerceUnsized,
         Command,
         ConstParamTy,
@@ -619,6 +620,7 @@ symbols! {
         cmp_partialord_lt,
         cmpxchg16b_target_feature,
         cmse_nonsecure_entry,
+        coerce_pointee_validated,
         coerce_unsized,
         cold,
         cold_path,
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index 1a8ef20dd7b9d..029c8b356d074 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -1284,8 +1284,22 @@ pub trait FnPtr: Copy + Clone {
 /// }
 /// ```
 #[rustc_builtin_macro(CoercePointee, attributes(pointee))]
-#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize)]
+#[allow_internal_unstable(dispatch_from_dyn, coerce_unsized, unsize, coerce_pointee_validated)]
 #[unstable(feature = "derive_coerce_pointee", issue = "123430")]
 pub macro CoercePointee($item:item) {
     /* compiler built-in */
 }
+
+/// A trait that is implemented for ADTs with `derive(CoercePointee)` so that
+/// the compiler can enforce the derive impls are valid post-expansion, since
+/// the derive has stricter requirements than if the impls were written by hand.
+///
+/// This trait is not intended to be implemented by users or used other than
+/// validation, so it should never be stabilized.
+#[cfg(not(bootstrap))]
+#[lang = "coerce_pointee_validated"]
+#[unstable(feature = "coerce_pointee_validated", issue = "none")]
+#[doc(hidden)]
+pub trait CoercePointeeValidated {
+    /* compiler built-in */
+}
diff --git a/tests/ui/deriving/auxiliary/malicious-macro.rs b/tests/ui/deriving/auxiliary/malicious-macro.rs
new file mode 100644
index 0000000000000..6665b40a14f2b
--- /dev/null
+++ b/tests/ui/deriving/auxiliary/malicious-macro.rs
@@ -0,0 +1,31 @@
+#![feature(let_chains)]
+
+extern crate proc_macro;
+
+use proc_macro::{Delimiter, TokenStream, TokenTree};
+
+#[proc_macro_attribute]
+pub fn norepr(_: TokenStream, input: TokenStream) -> TokenStream {
+    let mut tokens = vec![];
+    let mut tts = input.into_iter().fuse().peekable();
+    loop {
+        let Some(token) = tts.next() else { break };
+        if let TokenTree::Punct(punct) = &token
+            && punct.as_char() == '#'
+        {
+            if let Some(TokenTree::Group(group)) = tts.peek()
+                && let Delimiter::Bracket = group.delimiter()
+                && let Some(TokenTree::Ident(ident)) = group.stream().into_iter().next()
+                && ident.to_string() == "repr"
+            {
+                let _ = tts.next();
+                // skip '#' and '[repr(..)]
+            } else {
+                tokens.push(token);
+            }
+        } else {
+            tokens.push(token);
+        }
+    }
+    tokens.into_iter().collect()
+}
diff --git a/tests/ui/deriving/built-in-proc-macro-scope.stdout b/tests/ui/deriving/built-in-proc-macro-scope.stdout
index db97c7145ea09..fa4e50968f4de 100644
--- a/tests/ui/deriving/built-in-proc-macro-scope.stdout
+++ b/tests/ui/deriving/built-in-proc-macro-scope.stdout
@@ -20,6 +20,8 @@ pub struct Ptr<'a, #[pointee] T: ?Sized> {
     data: &'a mut T,
 }
 #[automatically_derived]
+impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for Ptr<'a, T> { }
+#[automatically_derived]
 impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
     ::core::ops::DispatchFromDyn<Ptr<'a, __S>> for Ptr<'a, T> {
 }
diff --git a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout
index d6eaca5cba188..a774efbbe354b 100644
--- a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout
+++ b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout
@@ -16,6 +16,10 @@ struct MyPointer<'a, #[pointee] T: ?Sized> {
     ptr: &'a T,
 }
 #[automatically_derived]
+impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for
+    MyPointer<'a, T> {
+}
+#[automatically_derived]
 impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
     ::core::ops::DispatchFromDyn<MyPointer<'a, __S>> for MyPointer<'a, T> {
 }
@@ -31,6 +35,11 @@ pub struct MyPointer2<'a, Y, Z: MyTrait<T>, #[pointee] T: ?Sized + MyTrait<T>,
     x: core::marker::PhantomData<X>,
 }
 #[automatically_derived]
+impl<'a, Y, Z: MyTrait<T>, T: ?Sized + MyTrait<T>, X: MyTrait<T>>
+    ::core::marker::CoercePointeeValidated for MyPointer2<'a, Y, Z, T, X>
+    where Y: MyTrait<T> {
+}
+#[automatically_derived]
 impl<'a, Y, Z: MyTrait<T> + MyTrait<__S>, T: ?Sized + MyTrait<T> +
     ::core::marker::Unsize<__S>, __S: ?Sized + MyTrait<__S>, X: MyTrait<T> +
     MyTrait<__S>> ::core::ops::DispatchFromDyn<MyPointer2<'a, Y, Z, __S, X>>
@@ -48,6 +57,10 @@ struct MyPointerWithoutPointee<'a, T: ?Sized> {
     ptr: &'a T,
 }
 #[automatically_derived]
+impl<'a, T: ?Sized> ::core::marker::CoercePointeeValidated for
+    MyPointerWithoutPointee<'a, T> {
+}
+#[automatically_derived]
 impl<'a, T: ?Sized + ::core::marker::Unsize<__S>, __S: ?Sized>
     ::core::ops::DispatchFromDyn<MyPointerWithoutPointee<'a, __S>> for
     MyPointerWithoutPointee<'a, T> {
diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.rs b/tests/ui/deriving/deriving-coerce-pointee-neg.rs
index da25c854c546a..6577500d8eb0f 100644
--- a/tests/ui/deriving/deriving-coerce-pointee-neg.rs
+++ b/tests/ui/deriving/deriving-coerce-pointee-neg.rs
@@ -1,6 +1,9 @@
+//@ proc-macro: malicious-macro.rs
 #![feature(derive_coerce_pointee, arbitrary_self_types)]
 
 extern crate core;
+extern crate malicious_macro;
+
 use std::marker::CoercePointee;
 
 #[derive(CoercePointee)]
@@ -41,8 +44,8 @@ struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &
 //~^ ERROR: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits
 
 #[derive(CoercePointee)]
-//~^ ERROR: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`
 struct NotTransparent<'a, #[pointee] T: ?Sized> {
+    //~^ ERROR: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
     ptr: &'a T,
 }
 
@@ -131,4 +134,12 @@ struct GlobalCoreSized<'a, #[pointee] T: ?::core::marker::Sized> {
     ptr: &'a T,
 }
 
+#[derive(CoercePointee)]
+#[malicious_macro::norepr]
+#[repr(transparent)]
+struct TryToWipeRepr<'a, #[pointee] T: ?Sized> {
+    //~^ ERROR: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout [E0802]
+    ptr: &'a T,
+}
+
 fn main() {}
diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr
index c1e8be49d37d3..999214bfa9fe3 100644
--- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr
+++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr
@@ -1,89 +1,81 @@
-error: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`
-  --> $DIR/deriving-coerce-pointee-neg.rs:6:10
+error[E0802]: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`
+  --> $DIR/deriving-coerce-pointee-neg.rs:9:10
    |
 LL | #[derive(CoercePointee)]
    |          ^^^^^^^^^^^^^
    |
    = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: `CoercePointee` can only be derived on `struct`s with at least one field
-  --> $DIR/deriving-coerce-pointee-neg.rs:12:10
+error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field
+  --> $DIR/deriving-coerce-pointee-neg.rs:15:10
    |
 LL | #[derive(CoercePointee)]
    |          ^^^^^^^^^^^^^
    |
    = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: `CoercePointee` can only be derived on `struct`s with at least one field
-  --> $DIR/deriving-coerce-pointee-neg.rs:19:10
+error[E0802]: `CoercePointee` can only be derived on `struct`s with at least one field
+  --> $DIR/deriving-coerce-pointee-neg.rs:22:10
    |
 LL | #[derive(CoercePointee)]
    |          ^^^^^^^^^^^^^
    |
    = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: `CoercePointee` can only be derived on `struct`s that are generic over at least one type
-  --> $DIR/deriving-coerce-pointee-neg.rs:26:10
+error[E0802]: `CoercePointee` can only be derived on `struct`s that are generic over at least one type
+  --> $DIR/deriving-coerce-pointee-neg.rs:29:10
    |
 LL | #[derive(CoercePointee)]
    |          ^^^^^^^^^^^^^
    |
    = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits
-  --> $DIR/deriving-coerce-pointee-neg.rs:31:10
+error[E0802]: exactly one generic type parameter must be marked as `#[pointee]` to derive `CoercePointee` traits
+  --> $DIR/deriving-coerce-pointee-neg.rs:34:10
    |
 LL | #[derive(CoercePointee)]
    |          ^^^^^^^^^^^^^
    |
    = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits
-  --> $DIR/deriving-coerce-pointee-neg.rs:40:39
+error[E0802]: only one type parameter can be marked as `#[pointee]` when deriving `CoercePointee` traits
+  --> $DIR/deriving-coerce-pointee-neg.rs:43:39
    |
 LL | struct TooManyPointees<'a, #[pointee] A: ?Sized, #[pointee] B: ?Sized>((&'a A, &'a B));
    |                                       ^                     - here another type parameter is marked as `#[pointee]`
 
-error: `CoercePointee` can only be derived on `struct`s with `#[repr(transparent)]`
-  --> $DIR/deriving-coerce-pointee-neg.rs:43:10
-   |
-LL | #[derive(CoercePointee)]
-   |          ^^^^^^^^^^^^^
-   |
-   = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: `derive(CoercePointee)` requires `T` to be marked `?Sized`
-  --> $DIR/deriving-coerce-pointee-neg.rs:51:36
+error[E0802]: `derive(CoercePointee)` requires `T` to be marked `?Sized`
+  --> $DIR/deriving-coerce-pointee-neg.rs:54:36
    |
 LL | struct NoMaybeSized<'a, #[pointee] T> {
    |                                    ^
 
 error: the `#[pointee]` attribute may only be used on generic parameters
-  --> $DIR/deriving-coerce-pointee-neg.rs:59:5
+  --> $DIR/deriving-coerce-pointee-neg.rs:62:5
    |
 LL |     #[pointee]
    |     ^^^^^^^^^^
 
 error: the `#[pointee]` attribute may only be used on generic parameters
-  --> $DIR/deriving-coerce-pointee-neg.rs:69:33
+  --> $DIR/deriving-coerce-pointee-neg.rs:72:33
    |
 LL |                     struct UhOh<#[pointee] T>(T);
    |                                 ^^^^^^^^^^
 
 error: the `#[pointee]` attribute may only be used on generic parameters
-  --> $DIR/deriving-coerce-pointee-neg.rs:83:21
+  --> $DIR/deriving-coerce-pointee-neg.rs:86:21
    |
 LL |         struct UhOh<#[pointee] T>(T);
    |                     ^^^^^^^^^^
 
 error: the `#[pointee]` attribute may only be used on generic parameters
-  --> $DIR/deriving-coerce-pointee-neg.rs:98:25
+  --> $DIR/deriving-coerce-pointee-neg.rs:101:25
    |
 LL |             struct UhOh<#[pointee] T>(T);
    |                         ^^^^^^^^^^
 
 error[E0392]: lifetime parameter `'a` is never used
-  --> $DIR/deriving-coerce-pointee-neg.rs:15:16
+  --> $DIR/deriving-coerce-pointee-neg.rs:18:16
    |
 LL | struct NoField<'a, #[pointee] T: ?Sized> {}
    |                ^^ unused lifetime parameter
@@ -91,7 +83,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {}
    = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
 
 error[E0392]: type parameter `T` is never used
-  --> $DIR/deriving-coerce-pointee-neg.rs:15:31
+  --> $DIR/deriving-coerce-pointee-neg.rs:18:31
    |
 LL | struct NoField<'a, #[pointee] T: ?Sized> {}
    |                               ^ unused type parameter
@@ -99,7 +91,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {}
    = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
 
 error[E0392]: lifetime parameter `'a` is never used
-  --> $DIR/deriving-coerce-pointee-neg.rs:22:20
+  --> $DIR/deriving-coerce-pointee-neg.rs:25:20
    |
 LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
    |                    ^^ unused lifetime parameter
@@ -107,13 +99,26 @@ LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
    = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
 
 error[E0392]: type parameter `T` is never used
-  --> $DIR/deriving-coerce-pointee-neg.rs:22:35
+  --> $DIR/deriving-coerce-pointee-neg.rs:25:35
    |
 LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
    |                                   ^ unused type parameter
    |
    = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
 
-error: aborting due to 16 previous errors
+error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
+  --> $DIR/deriving-coerce-pointee-neg.rs:47:1
+   |
+LL | struct NotTransparent<'a, #[pointee] T: ?Sized> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
+  --> $DIR/deriving-coerce-pointee-neg.rs:140:1
+   |
+LL | struct TryToWipeRepr<'a, #[pointee] T: ?Sized> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
 
-For more information about this error, try `rustc --explain E0392`.
+Some errors have detailed explanations: E0392, E0802.
+For more information about an error, try `rustc --explain E0392`.