From b6899ab9216d63762ba7f97c8a457dc9047e0a2b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Feb 2025 18:22:27 +0000 Subject: [PATCH 1/4] More eagerly bail in DispatchFromDyn validation --- .../src/coherence/builtin.rs | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index b46b805f0a9d..5d70d537505f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -252,16 +252,16 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() })); } - 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 +272,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 +290,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,40 +299,36 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() ty: ty_a, })); - return false; + None + } else { + Some((i, ty_a, ty_b)) } - - true }) .collect::>(); + res?; if coerced_fields.is_empty() { - res = Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle { + return Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle { span, trait_name: "DispatchFromDyn", note: true, })); } else if coerced_fields.len() > 1 { - res = Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti { + return 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), - ) + .map(|&(i, ty_a, ty_b)| { + format!("`{}` (`{}` to `{}`)", fields[i].name, ty_a, ty_b,) }) .collect::>() .join(", "), })); } else { let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - for field in coerced_fields { + for (_, ty_a, ty_b) in coerced_fields { ocx.register_obligation(Obligation::new( tcx, cause.clone(), @@ -340,19 +336,20 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() ty::TraitRef::new( tcx, dispatch_from_dyn_trait, - [field.ty(tcx, args_a), field.ty(tcx, args_b)], + [ty_a, ty_b], ), )); } let errors = ocx.select_all_or_error(); if !errors.is_empty() { - res = Err(infcx.err_ctxt().report_fulfillment_errors(errors)); + 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, [])?; } - res + + Ok(()) } _ => Err(tcx .dcx() @@ -558,11 +555,11 @@ 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); + 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 }) } From 96d966b07aabbf0438489b848b8f039f88bb518e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Feb 2025 19:10:01 +0000 Subject: [PATCH 2/4] Consolidate and rework CoercePointee and DispatchFromDyn errors --- .../src/error_codes/E0374.md | 53 ++++---------- .../src/error_codes/E0375.md | 47 +++---------- .../src/error_codes/E0376.md | 38 ++-------- .../src/error_codes/E0377.md | 23 +++--- compiler/rustc_hir_analysis/messages.ftl | 13 ++-- .../src/coherence/builtin.rs | 67 +++++++----------- compiler/rustc_hir_analysis/src/errors.rs | 70 ++++--------------- tests/ui/coercion/issue-26905.stderr | 13 ++-- tests/ui/error-codes/E0374.stderr | 2 +- tests/ui/error-codes/E0375.stderr | 13 ++-- tests/ui/error-codes/E0376.rs | 10 --- tests/ui/error-codes/E0376.stderr | 9 --- tests/ui/invalid_dispatch_from_dyn_impls.rs | 14 ++-- .../ui/invalid_dispatch_from_dyn_impls.stderr | 28 +++++--- ...patch-from-dyn-zst-transmute-zst-nonzst.rs | 2 +- ...h-from-dyn-zst-transmute-zst-nonzst.stderr | 13 ++-- .../self/dispatch-from-dyn-zst-transmute.rs | 2 +- .../dispatch-from-dyn-zst-transmute.stderr | 13 ++-- tests/ui/traits/issue-78372.stderr | 4 +- 19 files changed, 158 insertions(+), 276 deletions(-) delete mode 100644 tests/ui/error-codes/E0376.rs delete mode 100644 tests/ui/error-codes/E0376.stderr diff --git a/compiler/rustc_error_codes/src/error_codes/E0374.md b/compiler/rustc_error_codes/src/error_codes/E0374.md index 6d7dc88823c9..63c243b54ff2 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 { 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 CoerceUnsized> for Foo where T: CoerceUnsized {} ``` -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` being unsized to `MyBox`. `DispatchFromDyn` +is used to dispatch from `MyBox` to `MyBox` 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 { - a: i32, - b: T, -} - -// The struct has an unsized field so we can implement -// `CoerceUnsized` for it. -impl CoerceUnsized> for Bar - where T: CoerceUnsized {} -``` - -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 71e530571650..7abb3b6afd02 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 { impl CoerceUnsized> for Foo {} ``` -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` being unsized to `MyBox`. `DispatchFromDyn` +is used to dispatch from `MyBox` to `MyBox` 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 { - a: i32, - b: T, -} - -impl CoerceUnsized> for Foo - where T: CoerceUnsized {} - -fn coerce_foo, U>(t: T) -> Foo { - Foo { a: 12i32, b: t } // we use coercion to get the `Foo` 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 50de15bd30f0..5b564ec22fcb 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 { impl CoerceUnsized for Foo {} ``` -`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 { - a: T, -} - -// The `Foo` is a struct so `CoerceUnsized` can be implemented -impl CoerceUnsized> for Foo where T: CoerceUnsized {} -``` - -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 b1d36406332b..cd2b26260a8a 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 { impl CoerceUnsized> for Foo where T: CoerceUnsized {} ``` -When attempting to implement `CoerceUnsized`, the `impl` signature must look -like: `impl CoerceUnsized> for Type where T: CoerceUnsized`; -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` being unsized to `MyBox`. `DispatchFromDyn` +is used to dispatch from `MyBox` to `MyBox` 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 47d5976be09e..98eb4f65e22b 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 @@ -97,10 +101,7 @@ hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only ap 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 +140,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 5d70d537505f..dc9a5a3db5df 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -240,16 +240,17 @@ 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 { - 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 { - span, - trait_name: "DispatchFromDyn", - note: true, - source_path, - target_path, - })); + 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, + })); + } } if def_a.repr().c() || def_a.repr().packed() { @@ -301,43 +302,33 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() None } else { - Some((i, ty_a, ty_b)) + Some((i, ty_a, ty_b, tcx.def_span(field.did))) } }) .collect::>(); res?; if coerced_fields.is_empty() { - return Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle { + return Err(tcx.dcx().emit_err(errors::CoerceNoField { span, trait_name: "DispatchFromDyn", note: true, })); } else if coerced_fields.len() > 1 { - return Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti { + return Err(tcx.dcx().emit_err(errors::CoerceMulti { span, - coercions_note: true, + trait_name: "DispatchFromDyn", number: coerced_fields.len(), - coercions: coerced_fields - .iter() - .map(|&(i, ty_a, ty_b)| { - format!("`{}` (`{}` to `{}`)", fields[i].name, ty_a, ty_b,) - }) - .collect::>() - .join(", "), + fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::>().into(), })); } else { let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - for (_, ty_a, ty_b) in coerced_fields { + 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], - ), + ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]), )); } let errors = ocx.select_all_or_error(); @@ -353,7 +344,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() } _ => Err(tcx .dcx() - .emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" })), + .emit_err(errors::CoerceUnsizedNonStruct { span, trait_name: "DispatchFromDyn" })), } } @@ -419,7 +410,7 @@ 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", note: true, @@ -501,12 +492,12 @@ 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::>(); 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", note: true, @@ -519,19 +510,15 @@ 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: "CoerceUnsized", number: diff_fields.len(), - coercions: diff_fields - .iter() - .map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b)) - .collect::>() - .join(", "), + fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::>().into(), })); } - let (i, a, b) = diff_fields[0]; + let (i, a, b, _) = diff_fields[0]; let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); (a, b, coerce_unsized_trait, Some(kind)) } @@ -539,7 +526,7 @@ pub(crate) fn coerce_unsized_info<'tcx>( _ => { return Err(tcx .dcx() - .emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" })); + .emit_err(errors::CoerceUnsizedNonStruct { span, trait_name: "CoerceUnsized" })); } }; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 1a0b0edb2570..a74d9b95e0e6 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1164,18 +1164,6 @@ pub(crate) struct InherentTyOutside { pub span: Span, } -#[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 { @@ -1293,78 +1281,46 @@ 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, pub target_path: String, } -#[derive(Diagnostic)] -#[diag(hir_analysis_coerce_unsized_may, code = E0374)] -pub(crate) struct CoerceUnsizedOneField<'a> { - #[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] - #[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, -} - #[derive(Diagnostic)] #[diag(hir_analysis_trait_cannot_impl_for_ty, code = E0204)] pub(crate) struct TraitCannotImplForTy { diff --git a/tests/ui/coercion/issue-26905.stderr b/tests/ui/coercion/issue-26905.stderr index 86f6a14cd105..17387ae992b4 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, U: ?Sized> CoerceUnsized> for MyRc{ } - | ^^^^^^^^^^^^^^^^^^^^^^ 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` to `NotPhantomData`) +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, + | ^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0374.stderr b/tests/ui/error-codes/E0374.stderr index 71eec4c16fd3..95e6b95e0d56 100644 --- a/tests/ui/error-codes/E0374.stderr +++ b/tests/ui/error-codes/E0374.stderr @@ -6,7 +6,7 @@ LL | struct Foo { | = 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 CoerceUnsized> for Foo diff --git a/tests/ui/error-codes/E0375.stderr b/tests/ui/error-codes/E0375.stderr index af720bd40e7e..a797ba9d4612 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, | ++++ + -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 CoerceUnsized> for Foo {} - | ^^^^^^^^^^^^^^^^^^^^^^^^ 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 f092eb02c2bc..000000000000 --- a/tests/ui/error-codes/E0376.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![feature(coerce_unsized)] -use std::ops::CoerceUnsized; - -struct Foo { - a: T, -} - -impl CoerceUnsized for Foo {} //~ 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 46668e05a42f..000000000000 --- 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 CoerceUnsized for Foo {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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 b7bc766fbe02..972465d71978 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, i32); impl DispatchFromDyn> for WrapperWithExtraField +//~^ ERROR [E0378] where T: DispatchFromDyn, -{} //~^^^ ERROR [E0378] +{} struct MultiplePointers{ @@ -19,9 +20,10 @@ struct MultiplePointers{ } impl DispatchFromDyn> for MultiplePointers +//~^ implementing `DispatchFromDyn` does not allow multiple fields to be coerced where T: Unsize, -{} //~^^^ ERROR [E0378] +{} struct NothingToCoerce { @@ -29,23 +31,25 @@ struct NothingToCoerce { } impl DispatchFromDyn> for NothingToCoerce {} -//~^ ERROR [E0378] +//~^ ERROR implementing `DispatchFromDyn` requires a field to be coerced #[repr(C)] struct HasReprC(Box); impl DispatchFromDyn> for HasReprC +//~^ ERROR [E0378] where T: Unsize, -{} //~^^^ ERROR [E0378] +{} #[repr(align(64))] struct OverAlignedZst; struct OverAligned(Box, OverAlignedZst); impl DispatchFromDyn> for OverAligned +//~^ ERROR [E0378] where T: Unsize, -{} //~^^^ 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 02718334c733..93ec6bbe0896 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 DispatchFromDyn> for WrapperWithExtraField +LL | | LL | | where LL | | T: DispatchFromDyn, | |__________________________^ | = 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 DispatchFromDyn> for MultiplePointers +LL | | LL | | where LL | | T: Unsize, | |_________________^ | - = 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 DispatchFromDyn> for NothingToCoerce {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,17 +35,19 @@ LL | impl DispatchFromDyn> 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 DispatchFromDyn> for HasReprC +LL | | LL | | where LL | | T: Unsize, | |_________________^ 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 DispatchFromDyn> for OverAligned +LL | | LL | | where LL | | T: Unsize, | |_____________________^ @@ -47,4 +56,5 @@ LL | | T: Unsize, 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 71f198f7dc70..94b76fe96854 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 { } impl DispatchFromDyn> for Dispatchable -//~^ ERROR implementing the `DispatchFromDyn` trait requires multiple coercions +//~^ ERROR implementing `DispatchFromDyn` does not allow multiple fields to be coerced where T: Unsize + ?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 1f13c51f6799..91760b9e2eab 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 DispatchFromDyn> for Dispatchable @@ -8,9 +8,14 @@ LL | | T: Unsize + ?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` to `Box`), `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, + | ^^^^^^^^^^^^ +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 57c255b4d7bc..967958ab4869 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> 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 + ?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 5a8ae88b5f1b..cc8be45e99d8 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> for Foo<'a, T> @@ -8,9 +8,14 @@ LL | | T: Unsize + ?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` to `IsSendToken`), `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, + | ^^^^^^^^^^^^^^^^^^^^^ +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 d4dfba4f039f..fbc60ce5d83e 100644 --- a/tests/ui/traits/issue-78372.stderr +++ b/tests/ui/traits/issue-78372.stderr @@ -65,7 +65,7 @@ LL | fn foo(self: Smaht); = 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: Rc`, `self: Arc`, or `self: Pin

` (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 DispatchFromDyn> for T {} @@ -73,5 +73,5 @@ LL | impl DispatchFromDyn> 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`. From 5c5ed92c37a70603e9d45c698898841afacfb25f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Feb 2025 19:51:06 +0000 Subject: [PATCH 3/4] Simplify trait error message for CoercePointee validation --- compiler/rustc_hir_analysis/messages.ftl | 3 + .../src/coherence/builtin.rs | 116 +++++++++++------- compiler/rustc_hir_analysis/src/errors.rs | 12 ++ library/core/src/marker.rs | 1 + .../deriving/deriving-coerce-pointee-neg.rs | 26 ++++ .../deriving-coerce-pointee-neg.stderr | 91 +++++++++++++- 6 files changed, 199 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 98eb4f65e22b..6badd2909175 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 dc9a5a3db5df..9d6bb1cf6bff 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::>().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::>().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 { 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::>().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 a74d9b95e0e6..99262f9871e0 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1321,6 +1321,18 @@ pub(crate) struct CoerceSameStruct { pub target_path: String, } +#[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 { diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 842a48e1606d..b0571bf7247a 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 6577500d8eb0..e660e7baacb5 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` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `Rc<(i32, Box)>` +//~| ERROR for `RcWithId` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `Rc<(i32, Box)>` +struct RcWithId { + inner: std::rc::Rc<(i32, Box)>, +} + +#[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 { + //~^ ERROR transparent struct needs at most one field with non-trivial size or alignment, but has 2 + inner1: Box, + inner2: Box, +} + +struct NotCoercePointeeData(T); + +#[repr(transparent)] +#[derive(CoercePointee)] +//~^ ERROR for `UsingNonCoercePointeeData` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData` +//~| ERROR for `UsingNonCoercePointeeData` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `NotCoercePointeeData` +struct UsingNonCoercePointeeData(NotCoercePointeeData); + fn main() {} diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr index 999214bfa9fe..e346d13ff5c0 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` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `Rc<(i32, Box)>` + --> $DIR/deriving-coerce-pointee-neg.rs:146:10 + | +LL | #[derive(CoercePointee)] + | ^^^^^^^^^^^^^ +... +LL | inner: std::rc::Rc<(i32, Box)>, + | --------------------------------- `Rc<(i32, Box)>` 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, + | ^^^^^^^^^^^^^^ +LL | inner2: Box, + | ^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: for `UsingNonCoercePointeeData` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `NotCoercePointeeData` + --> $DIR/deriving-coerce-pointee-neg.rs:166:10 + | +LL | #[derive(CoercePointee)] + | ^^^^^^^^^^^^^ +... +LL | struct UsingNonCoercePointeeData(NotCoercePointeeData); + | ----------------------- `NotCoercePointeeData` 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` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `Rc<(i32, Box)>` + --> $DIR/deriving-coerce-pointee-neg.rs:146:10 + | +LL | #[derive(CoercePointee)] + | ^^^^^^^^^^^^^ +... +LL | inner: std::rc::Rc<(i32, Box)>, + | --------------------------------- `Rc<(i32, Box)>` 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, + | ^^^^^^^^^^^^^^ +LL | inner2: Box, + | ^^^^^^^^^^^^^^ + = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: for `UsingNonCoercePointeeData` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData` + --> $DIR/deriving-coerce-pointee-neg.rs:166:10 + | +LL | #[derive(CoercePointee)] + | ^^^^^^^^^^^^^ +... +LL | struct UsingNonCoercePointeeData(NotCoercePointeeData); + | ----------------------- `NotCoercePointeeData` 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 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 +LL | +LL | inner1: Box, + | -------------- this field has non-zero size or requires alignment +LL | inner2: Box, + | -------------- 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`. From b46acc01916ba3e8b8f8ab9d89608861d1d4cb87 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Feb 2025 19:55:12 +0000 Subject: [PATCH 4/4] Deduplicate CoerceUnsized and DispatchFromDyn impl errors --- .../src/coherence/builtin.rs | 16 ++++-- .../deriving/deriving-coerce-pointee-neg.rs | 3 -- .../deriving-coerce-pointee-neg.stderr | 49 +++---------------- 3 files changed, 19 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 9d6bb1cf6bff..cee2f487639d 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -195,8 +195,7 @@ 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 { @@ -218,13 +217,24 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() 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); diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.rs b/tests/ui/deriving/deriving-coerce-pointee-neg.rs index e660e7baacb5..2713366945e9 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.rs +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.rs @@ -145,7 +145,6 @@ struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { #[repr(transparent)] #[derive(CoercePointee)] //~^ ERROR for `RcWithId` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `Rc<(i32, Box)>` -//~| ERROR for `RcWithId` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `Rc<(i32, Box)>` struct RcWithId { inner: std::rc::Rc<(i32, Box)>, } @@ -153,7 +152,6 @@ struct RcWithId { #[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 { //~^ ERROR transparent struct needs at most one field with non-trivial size or alignment, but has 2 inner1: Box, @@ -165,7 +163,6 @@ struct NotCoercePointeeData(T); #[repr(transparent)] #[derive(CoercePointee)] //~^ ERROR for `UsingNonCoercePointeeData` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData` -//~| ERROR for `UsingNonCoercePointeeData` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `NotCoercePointeeData` struct UsingNonCoercePointeeData(NotCoercePointeeData); fn main() {} diff --git a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr index e346d13ff5c0..d3d731320781 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-neg.stderr +++ b/tests/ui/deriving/deriving-coerce-pointee-neg.stderr @@ -118,43 +118,6 @@ error[E0802]: `derive(CoercePointee)` is only applicable to `struct` with `repr( LL | struct TryToWipeRepr<'a, #[pointee] T: ?Sized> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: for `RcWithId` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `Rc<(i32, Box)>` - --> $DIR/deriving-coerce-pointee-neg.rs:146:10 - | -LL | #[derive(CoercePointee)] - | ^^^^^^^^^^^^^ -... -LL | inner: std::rc::Rc<(i32, Box)>, - | --------------------------------- `Rc<(i32, Box)>` 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, - | ^^^^^^^^^^^^^^ -LL | inner2: Box, - | ^^^^^^^^^^^^^^ - = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: for `UsingNonCoercePointeeData` to have a valid implementation of `DispatchFromDyn`, it must be possible to coerce the field of type `NotCoercePointeeData` - --> $DIR/deriving-coerce-pointee-neg.rs:166:10 - | -LL | #[derive(CoercePointee)] - | ^^^^^^^^^^^^^ -... -LL | struct UsingNonCoercePointeeData(NotCoercePointeeData); - | ----------------------- `NotCoercePointeeData` 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` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `Rc<(i32, Box)>` --> $DIR/deriving-coerce-pointee-neg.rs:146:10 | @@ -167,13 +130,13 @@ LL | inner: std::rc::Rc<(i32, Box)>, = 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 + --> $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:159:5 + --> $DIR/deriving-coerce-pointee-neg.rs:157:5 | LL | inner1: Box, | ^^^^^^^^^^^^^^ @@ -182,18 +145,18 @@ LL | inner2: Box, = note: this error originates in the derive macro `CoercePointee` (in Nightly builds, run with -Z macro-backtrace for more info) error: for `UsingNonCoercePointeeData` to have a valid implementation of `CoerceUnsized`, it must be possible to coerce the field of type `NotCoercePointeeData` - --> $DIR/deriving-coerce-pointee-neg.rs:166:10 + --> $DIR/deriving-coerce-pointee-neg.rs:164:10 | LL | #[derive(CoercePointee)] | ^^^^^^^^^^^^^ -... +LL | LL | struct UsingNonCoercePointeeData(NotCoercePointeeData); | ----------------------- `NotCoercePointeeData` 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 + --> $DIR/deriving-coerce-pointee-neg.rs:155:1 | LL | struct MoreThanOneField { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2 @@ -203,7 +166,7 @@ LL | inner1: Box, LL | inner2: Box, | -------------- this field has non-zero size or requires alignment -error: aborting due to 24 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0375, E0392, E0690, E0802. For more information about an error, try `rustc --explain E0375`.