about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl3
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs116
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs12
-rw-r--r--library/core/src/marker.rs1
-rw-r--r--tests/ui/deriving/deriving-coerce-pointee-neg.rs26
-rw-r--r--tests/ui/deriving/deriving-coerce-pointee-neg.stderr91
6 files changed, 199 insertions, 50 deletions
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 98eb4f65e22..6badd290917 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -99,6 +99,9 @@ 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_zero = implementing `{$trait_name}` requires a field to be coerced
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index dc9a5a3db5d..9d6bb1cf6bf 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,
@@ -199,6 +199,13 @@ fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), E
     tcx.at(span).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> {
     let tcx = checker.tcx;
     let impl_did = checker.impl_def_id;
@@ -206,6 +213,7 @@ 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));
 
@@ -240,17 +248,15 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
         (&RawPtr(_, a_mutbl), &RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()),
         (&Adt(def_a, args_a), &Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => {
             if def_a != def_b {
-                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::CoerceSameStruct {
-                        span,
-                        trait_name: "DispatchFromDyn",
-                        note: true,
-                        source_path,
-                        target_path,
-                    }));
-                }
+                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::CoerceSameStruct {
+                    span,
+                    trait_name,
+                    note: true,
+                    source_path,
+                    target_path,
+                }));
             }
 
             if def_a.repr().c() || def_a.repr().packed() {
@@ -311,40 +317,46 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
             if coerced_fields.is_empty() {
                 return Err(tcx.dcx().emit_err(errors::CoerceNoField {
                     span,
-                    trait_name: "DispatchFromDyn",
+                    trait_name,
                     note: true,
                 }));
-            } else if coerced_fields.len() > 1 {
-                return Err(tcx.dcx().emit_err(errors::CoerceMulti {
-                    span,
-                    trait_name: "DispatchFromDyn",
-                    number: coerced_fields.len(),
-                    fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
-                }));
-            } else {
+            } else if let &[(_, ty_a, ty_b, field_span)] = &coerced_fields[..] {
                 let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
-                for (_, ty_a, ty_b, _) in coerced_fields {
-                    ocx.register_obligation(Obligation::new(
-                        tcx,
-                        cause.clone(),
-                        param_env,
-                        ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_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() {
-                    return 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.
                 ocx.resolve_regions_and_report_errors(impl_did, param_env, [])?;
-            }
 
-            Ok(())
+                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(),
+                }));
+            }
         }
-        _ => Err(tcx
-            .dcx()
-            .emit_err(errors::CoerceUnsizedNonStruct { span, trait_name: "DispatchFromDyn" })),
+        _ => Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name })),
     }
 }
 
@@ -354,13 +366,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);
@@ -387,9 +400,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 };
@@ -412,7 +425,7 @@ pub(crate) fn coerce_unsized_info<'tcx>(
                 let target_path = tcx.def_path_str(def_b.did());
                 return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
                     span,
-                    trait_name: "CoerceUnsized",
+                    trait_name,
                     note: true,
                     source_path,
                     target_path,
@@ -499,7 +512,7 @@ pub(crate) fn coerce_unsized_info<'tcx>(
             if diff_fields.is_empty() {
                 return Err(tcx.dcx().emit_err(errors::CoerceNoField {
                     span,
-                    trait_name: "CoerceUnsized",
+                    trait_name,
                     note: true,
                 }));
             } else if diff_fields.len() > 1 {
@@ -512,21 +525,19 @@ pub(crate) fn coerce_unsized_info<'tcx>(
 
                 return Err(tcx.dcx().emit_err(errors::CoerceMulti {
                     span,
-                    trait_name: "CoerceUnsized",
+                    trait_name,
                     number: diff_fields.len(),
                     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::CoerceUnsizedNonStruct { span, trait_name: "CoerceUnsized" }));
+            return Err(tcx.dcx().emit_err(errors::CoerceUnsizedNonStruct { span, trait_name }));
         }
     };
 
@@ -541,8 +552,19 @@ pub(crate) fn coerce_unsized_info<'tcx>(
     );
     ocx.register_obligation(obligation);
     let errors = ocx.select_all_or_error();
+
     if !errors.is_empty() {
-        return 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: source,
+            }));
+        } else {
+            return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
+        }
     }
 
     // Finally, resolve all regions.
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index a74d9b95e0e..99262f9871e 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1322,6 +1322,18 @@ pub(crate) struct CoerceSameStruct {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_analysis_coerce_unsized_field_validity)]
+pub(crate) struct CoerceFieldValidity<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'tcx>,
+    pub trait_name: &'static str,
+    #[label]
+    pub field_span: Span,
+    pub field_ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_analysis_trait_cannot_impl_for_ty, code = E0204)]
 pub(crate) struct TraitCannotImplForTy {
     #[primary_span]
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/deriving/deriving-coerce-pointee-neg.rs b/tests/ui/deriving/deriving-coerce-pointee-neg.rs
index 6577500d8eb..e660e7baacb 100644
--- a/tests/ui/deriving/deriving-coerce-pointee-neg.rs
+++ b/tests/ui/deriving/deriving-coerce-pointee-neg.rs
@@ -142,4 +142,30 @@ 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>)>`
+//~| ERROR for `RcWithId<T>` to have a valid implementation of `DispatchFromDyn`, 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
+//~| ERROR implementing `DispatchFromDyn` 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>`
+//~| ERROR for `UsingNonCoercePointeeData<T>` to have a valid implementation of `DispatchFromDyn`, 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..e346d13ff5c 100644
--- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr
+++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr
@@ -118,7 +118,92 @@ 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 `DispatchFromDyn`, 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 `DispatchFromDyn` does not allow multiple fields to be coerced
+  --> $DIR/deriving-coerce-pointee-neg.rs:154:10
+   |
+LL | #[derive(CoercePointee)]
+   |          ^^^^^^^^^^^^^
+   |
+note: the trait `DispatchFromDyn` may only be implemented when a single field is being coerced
+  --> $DIR/deriving-coerce-pointee-neg.rs:159: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 `DispatchFromDyn`, it must be possible to coerce the field of type `NotCoercePointeeData<T>`
+  --> $DIR/deriving-coerce-pointee-neg.rs:166:10
+   |
+LL | #[derive(CoercePointee)]
+   |          ^^^^^^^^^^^^^
+...
+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: 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:154: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:159: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:166:10
+   |
+LL | #[derive(CoercePointee)]
+   |          ^^^^^^^^^^^^^
+...
+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:157: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 24 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`.