Skip to content

Commit

Permalink
Unrolled build for rust-lang#137289
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#137289 - compiler-errors:coerce-unsized-errors, r=oli-obk

Consolidate and improve error messaging for `CoerceUnsized` and `DispatchFromDyn`

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

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

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

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

This is best reviewed per commit.

r? `@oli-obk` perhaps?

cc `@dingxiangfei2009` -- sorry for making my own attempt at this PR, but I wanted to see if I could implement a fix for rust-lang#136796 in a less complicated way, since communicating over github review comments can be a bit slow. I'll leave comments inline to explain my thinking about the diagnostics changes.
  • Loading branch information
rust-timer authored Feb 25, 2025
2 parents 7d8c6e7 + b46acc0 commit f8edf60
Show file tree
Hide file tree
Showing 22 changed files with 308 additions and 310 deletions.
53 changes: 13 additions & 40 deletions compiler/rustc_error_codes/src/error_codes/E0374.md
Original file line number Diff line number Diff line change
@@ -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:

Expand All @@ -11,47 +11,20 @@ struct Foo<T: ?Sized> {
a: i32,
}
// error: Struct `Foo` has no unsized fields that need `CoerceUnsized`.
// error: Struct `Foo` has no unsized fields that need to be coerced.
impl<T, U> CoerceUnsized<Foo<U>> for Foo<T>
where T: CoerceUnsized<U> {}
```

An [unsized type][1] is any type where the compiler does not know the length or
alignment of at compile time. Any struct containing an unsized type is also
unsized.
`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
trait.

[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
If the struct doesn't have any fields of unsized types then there is no
meaningful way to implement `CoerceUnsized` or `DispatchFromDyn`, since
there is no coercion taking place.

`CoerceUnsized` is used to coerce one struct containing an unsized type
into another struct containing a different unsized type. If the struct
doesn't have any fields of unsized types then you don't need explicit
coercion to get the types you want. To fix this you can either
not try to implement `CoerceUnsized` or you can add a field that is
unsized to the struct.

Example:

```
#![feature(coerce_unsized)]
use std::ops::CoerceUnsized;
// We don't need to impl `CoerceUnsized` here.
struct Foo {
a: i32,
}
// We add the unsized type field to the struct.
struct Bar<T: ?Sized> {
a: i32,
b: T,
}
// The struct has an unsized field so we can implement
// `CoerceUnsized` for it.
impl<T, U> CoerceUnsized<Bar<U>> for Bar<T>
where T: CoerceUnsized<U> {}
```

Note that `CoerceUnsized` is mainly used by smart pointers like `Box`, `Rc`
and `Arc` to be able to mark that they can coerce unsized types that they
are pointing at.
Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
that they are pointing at.
47 changes: 11 additions & 36 deletions compiler/rustc_error_codes/src/error_codes/E0375.md
Original file line number Diff line number Diff line change
@@ -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:

Expand All @@ -17,39 +17,14 @@ struct Foo<T: ?Sized, U: ?Sized> {
impl<T, U> CoerceUnsized<Foo<U, T>> for Foo<T, U> {}
```

A struct with more than one field containing an unsized type cannot implement
`CoerceUnsized`. This only occurs when you are trying to coerce one of the
types in your struct to another type in the struct. In this case we try to
impl `CoerceUnsized` from `T` to `U` which are both types that the struct
takes. An [unsized type][1] is any type that the compiler doesn't know the
length or alignment of at compile time. Any struct containing an unsized type
is also unsized.
`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
trait.

`CoerceUnsized` only allows for coercion from a structure with a single
unsized type field to another struct with a single unsized type field.
In fact Rust only allows for a struct to have one unsized type in a struct
and that unsized type must be the last field in the struct. So having two
unsized types in a single struct is not allowed by the compiler. To fix this
use only one field containing an unsized type in the struct and then use
multiple structs to manage each unsized type field you need.
If the struct has multiple fields that must be unsized, then the compiler has no
way to generate a valid implementation of `CoerceUnsized` or `DispatchFromDyn`.

Example:

```
#![feature(coerce_unsized)]
use std::ops::CoerceUnsized;
struct Foo<T: ?Sized> {
a: i32,
b: T,
}
impl <T, U> CoerceUnsized<Foo<U>> for Foo<T>
where T: CoerceUnsized<U> {}
fn coerce_foo<T: CoerceUnsized<U>, U>(t: T) -> Foo<U> {
Foo { a: 12i32, b: t } // we use coercion to get the `Foo<U>` type we need
}
```

[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
that they are pointing at.
38 changes: 6 additions & 32 deletions compiler/rustc_error_codes/src/error_codes/E0376.md
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,33 +17,4 @@ struct Foo<T: ?Sized> {
impl<T, U> CoerceUnsized<U> for Foo<T> {}
```

`CoerceUnsized` can only be implemented for a struct. Unsized types are
already able to be coerced without an implementation of `CoerceUnsized`
whereas a struct containing an unsized type needs to know the unsized type
field it's containing is able to be coerced. An [unsized type][1]
is any type that the compiler doesn't know the length or alignment of at
compile time. Any struct containing an unsized type is also unsized.

[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait

The `CoerceUnsized` trait takes a struct type. Make sure the type you are
providing to `CoerceUnsized` is a struct with only the last field containing an
unsized type.

Example:

```
#![feature(coerce_unsized)]
use std::ops::CoerceUnsized;
struct Foo<T> {
a: T,
}
// The `Foo<U>` is a struct so `CoerceUnsized` can be implemented
impl<T, U> CoerceUnsized<Foo<U>> for Foo<T> where T: CoerceUnsized<U> {}
```

Note that in Rust, structs can only contain an unsized type if the field
containing the unsized type is the last and only unsized type field in the
struct.
`CoerceUnsized` or `DispatchFromDyn` can only be implemented between structs.
23 changes: 14 additions & 9 deletions compiler/rustc_error_codes/src/error_codes/E0377.md
Original file line number Diff line number Diff line change
@@ -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:

Expand All @@ -20,10 +20,15 @@ pub struct Bar<T: ?Sized> {
impl<T, U> CoerceUnsized<Bar<U>> for Foo<T> where T: CoerceUnsized<U> {}
```

When attempting to implement `CoerceUnsized`, the `impl` signature must look
like: `impl CoerceUnsized<Type<U>> for Type<T> where T: CoerceUnsized<U>`;
the *implementer* and *`CoerceUnsized` type parameter* must be the same
type. In this example, `Bar` and `Foo` (even though structurally identical)
are *not* the same type and are rejected. Learn more about the `CoerceUnsized`
trait and DST coercion in
[the `CoerceUnsized` docs](../std/ops/trait.CoerceUnsized.html).
`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
trait.

The compiler cannot support coercions between structs of different types, so
a valid implementation of `CoerceUnsized` or `DispatchFromDyn` should be
implemented between the same struct with different generic parameters.

Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
that they are pointing at.
16 changes: 8 additions & 8 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -95,12 +99,12 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
hir_analysis_coerce_unsized_field_validity = for `{$ty}` to have a valid implementation of `{$trait_name}`, it must be possible to coerce the field of type `{$field_ty}`
.label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions
.note = `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced
.coercions_note = currently, {$number} fields need coercions: {$coercions}
.label = requires multiple coercions
hir_analysis_coerce_zero = implementing `{$trait_name}` requires a field to be coerced
hir_analysis_coercion_between_struct_same_note = expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}`
Expand Down Expand Up @@ -139,10 +143,6 @@ hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `
hir_analysis_cross_crate_traits_defined = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type defined in the current crate
.label = can't implement cross-crate trait for type in another crate
hir_analysis_dispatch_from_dyn_multi = implementing the `DispatchFromDyn` trait requires multiple coercions
.note = the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
.coercions_note = currently, {$number} fields need coercions: {$coercions}
hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`
hir_analysis_dispatch_from_dyn_zst = the trait `DispatchFromDyn` may only be implemented for structs containing the field being coerced, ZST fields with 1 byte alignment that don't mention type/const generics, and nothing else
Expand Down
Loading

0 comments on commit f8edf60

Please sign in to comment.