Skip to content

Commit 0bb00e2

Browse files
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.
2 parents 748af6b + b46acc0 commit 0bb00e2

22 files changed

+308
-310
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
`CoerceUnsized` was implemented on a struct which does not contain a field with
2-
an unsized type.
1+
`CoerceUnsized` or `DispatchFromDyn` was implemented on a struct which does not
2+
contain a field that is being unsized.
33

44
Example of erroneous code:
55

@@ -11,47 +11,20 @@ struct Foo<T: ?Sized> {
1111
a: i32,
1212
}
1313
14-
// error: Struct `Foo` has no unsized fields that need `CoerceUnsized`.
14+
// error: Struct `Foo` has no unsized fields that need to be coerced.
1515
impl<T, U> CoerceUnsized<Foo<U>> for Foo<T>
1616
where T: CoerceUnsized<U> {}
1717
```
1818

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

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

25-
`CoerceUnsized` is used to coerce one struct containing an unsized type
26-
into another struct containing a different unsized type. If the struct
27-
doesn't have any fields of unsized types then you don't need explicit
28-
coercion to get the types you want. To fix this you can either
29-
not try to implement `CoerceUnsized` or you can add a field that is
30-
unsized to the struct.
31-
32-
Example:
33-
34-
```
35-
#![feature(coerce_unsized)]
36-
use std::ops::CoerceUnsized;
37-
38-
// We don't need to impl `CoerceUnsized` here.
39-
struct Foo {
40-
a: i32,
41-
}
42-
43-
// We add the unsized type field to the struct.
44-
struct Bar<T: ?Sized> {
45-
a: i32,
46-
b: T,
47-
}
48-
49-
// The struct has an unsized field so we can implement
50-
// `CoerceUnsized` for it.
51-
impl<T, U> CoerceUnsized<Bar<U>> for Bar<T>
52-
where T: CoerceUnsized<U> {}
53-
```
54-
55-
Note that `CoerceUnsized` is mainly used by smart pointers like `Box`, `Rc`
56-
and `Arc` to be able to mark that they can coerce unsized types that they
57-
are pointing at.
28+
Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
29+
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
30+
that they are pointing at.
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
`CoerceUnsized` was implemented on a struct which contains more than one field
2-
with an unsized type.
1+
`CoerceUnsized` or `DispatchFromDyn` was implemented on a struct which contains
2+
more than one field that is being unsized.
33

44
Erroneous code example:
55

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

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

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

36-
Example:
37-
38-
```
39-
#![feature(coerce_unsized)]
40-
use std::ops::CoerceUnsized;
41-
42-
struct Foo<T: ?Sized> {
43-
a: i32,
44-
b: T,
45-
}
46-
47-
impl <T, U> CoerceUnsized<Foo<U>> for Foo<T>
48-
where T: CoerceUnsized<U> {}
49-
50-
fn coerce_foo<T: CoerceUnsized<U>, U>(t: T) -> Foo<U> {
51-
Foo { a: 12i32, b: t } // we use coercion to get the `Foo<U>` type we need
52-
}
53-
```
54-
55-
[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
28+
Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
29+
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
30+
that they are pointing at.
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
`CoerceUnsized` was implemented on something that isn't a struct.
1+
#### Note: this error code is no longer emitted by the compiler.
2+
3+
`CoerceUnsized` or `DispatchFromDyn` was implemented between two types that
4+
are not structs.
25

36
Erroneous code example:
47

5-
```compile_fail,E0376
8+
```compile_fail,E0377
69
#![feature(coerce_unsized)]
710
use std::ops::CoerceUnsized;
811
@@ -14,33 +17,4 @@ struct Foo<T: ?Sized> {
1417
impl<T, U> CoerceUnsized<U> for Foo<T> {}
1518
```
1619

17-
`CoerceUnsized` can only be implemented for a struct. Unsized types are
18-
already able to be coerced without an implementation of `CoerceUnsized`
19-
whereas a struct containing an unsized type needs to know the unsized type
20-
field it's containing is able to be coerced. An [unsized type][1]
21-
is any type that the compiler doesn't know the length or alignment of at
22-
compile time. Any struct containing an unsized type is also unsized.
23-
24-
[1]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait
25-
26-
The `CoerceUnsized` trait takes a struct type. Make sure the type you are
27-
providing to `CoerceUnsized` is a struct with only the last field containing an
28-
unsized type.
29-
30-
Example:
31-
32-
```
33-
#![feature(coerce_unsized)]
34-
use std::ops::CoerceUnsized;
35-
36-
struct Foo<T> {
37-
a: T,
38-
}
39-
40-
// The `Foo<U>` is a struct so `CoerceUnsized` can be implemented
41-
impl<T, U> CoerceUnsized<Foo<U>> for Foo<T> where T: CoerceUnsized<U> {}
42-
```
43-
44-
Note that in Rust, structs can only contain an unsized type if the field
45-
containing the unsized type is the last and only unsized type field in the
46-
struct.
20+
`CoerceUnsized` or `DispatchFromDyn` can only be implemented between structs.

compiler/rustc_error_codes/src/error_codes/E0377.md

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
The trait `CoerceUnsized` may only be implemented for a coercion between
2-
structures with the same definition.
1+
`CoerceUnsized` or `DispatchFromDyn` may only be implemented between structs
2+
of the same type.
33

44
Example of erroneous code:
55

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

23-
When attempting to implement `CoerceUnsized`, the `impl` signature must look
24-
like: `impl CoerceUnsized<Type<U>> for Type<T> where T: CoerceUnsized<U>`;
25-
the *implementer* and *`CoerceUnsized` type parameter* must be the same
26-
type. In this example, `Bar` and `Foo` (even though structurally identical)
27-
are *not* the same type and are rejected. Learn more about the `CoerceUnsized`
28-
trait and DST coercion in
29-
[the `CoerceUnsized` docs](../std/ops/trait.CoerceUnsized.html).
23+
`CoerceUnsized` is used to coerce structs that have a field that can be unsized,
24+
like a custom `MyBox<T>` being unsized to `MyBox<dyn Trait>`. `DispatchFromDyn`
25+
is used to dispatch from `MyBox<dyn Trait>` to `MyBox<Self>` in a dyn-compatible
26+
trait.
27+
28+
The compiler cannot support coercions between structs of different types, so
29+
a valid implementation of `CoerceUnsized` or `DispatchFromDyn` should be
30+
implemented between the same struct with different generic parameters.
31+
32+
Note that `CoerceUnsized` and `DispatchFromDyn` is mainly used by smart pointers
33+
like `Box`, `Rc` and `Arc` to be able to mark that they can coerce unsized types
34+
that they are pointing at.

compiler/rustc_hir_analysis/messages.ftl

+8-8
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ hir_analysis_cmse_output_stack_spill =
8585
.note1 = functions with the `{$abi}` ABI must pass their result via the available return registers
8686
.note2 = the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size
8787
88+
hir_analysis_coerce_multi = implementing `{$trait_name}` does not allow multiple fields to be coerced
89+
.note = the trait `{$trait_name}` may only be implemented when a single field is being coerced
90+
.label = these fields must be coerced for `{$trait_name}` to be valid
91+
8892
hir_analysis_coerce_pointee_no_field = `CoercePointee` can only be derived on `struct`s with at least one field
8993
9094
hir_analysis_coerce_pointee_no_user_validity_assertion = asserting applicability of `derive(CoercePointee)` on a target data is forbidden
@@ -95,12 +99,12 @@ hir_analysis_coerce_pointee_not_struct = `derive(CoercePointee)` is only applica
9599
96100
hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only applicable to `struct` with `repr(transparent)` layout
97101
102+
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}`
103+
.label = `{$field_ty}` must be a pointer, reference, or smart pointer that is allowed to be unsized
104+
98105
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
99106
100-
hir_analysis_coerce_unsized_multi = implementing the trait `CoerceUnsized` requires multiple coercions
101-
.note = `CoerceUnsized` may only be implemented for a coercion between structures with one field being coerced
102-
.coercions_note = currently, {$number} fields need coercions: {$coercions}
103-
.label = requires multiple coercions
107+
hir_analysis_coerce_zero = implementing `{$trait_name}` requires a field to be coerced
104108
105109
hir_analysis_coercion_between_struct_same_note = expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}`
106110
@@ -139,10 +143,6 @@ hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `
139143
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
140144
.label = can't implement cross-crate trait for type in another crate
141145
142-
hir_analysis_dispatch_from_dyn_multi = implementing the `DispatchFromDyn` trait requires multiple coercions
143-
.note = the trait `DispatchFromDyn` may only be implemented for a coercion between structures with a single field being coerced
144-
.coercions_note = currently, {$number} fields need coercions: {$coercions}
145-
146146
hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`
147147
148148
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

0 commit comments

Comments
 (0)