Skip to content

Commit f24f274

Browse files
Consolidate and rework CoercePointee and DispatchFromDyn errors
1 parent d0b7393 commit f24f274

19 files changed

+157
-275
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,4 +1,7 @@
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

@@ -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

+5-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
@@ -97,10 +101,7 @@ hir_analysis_coerce_pointee_not_transparent = `derive(CoercePointee)` is only ap
97101
98102
hir_analysis_coerce_unsized_may = the trait `{$trait_name}` may only be implemented for a coercion between structures
99103
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
104+
hir_analysis_coerce_zero = implementing `{$trait_name}` requires a field to be coerced
104105
105106
hir_analysis_coercion_between_struct_same_note = expected coercion between the same definition; expected `{$source_path}`, found `{$target_path}`
106107
@@ -139,10 +140,6 @@ hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `
139140
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
140141
.label = can't implement cross-crate trait for type in another crate
141142
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-
146143
hir_analysis_dispatch_from_dyn_repr = structs implementing `DispatchFromDyn` may not have `#[repr(packed)]` or `#[repr(C)]`
147144
148145
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

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+27-40
Original file line numberDiff line numberDiff line change
@@ -240,16 +240,17 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
240240
(&RawPtr(_, a_mutbl), &RawPtr(_, b_mutbl)) if a_mutbl == b_mutbl => Ok(()),
241241
(&Adt(def_a, args_a), &Adt(def_b, args_b)) if def_a.is_struct() && def_b.is_struct() => {
242242
if def_a != def_b {
243-
let source_path = tcx.def_path_str(def_a.did());
244-
let target_path = tcx.def_path_str(def_b.did());
245-
246-
return Err(tcx.dcx().emit_err(errors::DispatchFromDynCoercion {
247-
span,
248-
trait_name: "DispatchFromDyn",
249-
note: true,
250-
source_path,
251-
target_path,
252-
}));
243+
if def_a != def_b {
244+
let source_path = tcx.def_path_str(def_a.did());
245+
let target_path = tcx.def_path_str(def_b.did());
246+
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
247+
span,
248+
trait_name: "DispatchFromDyn",
249+
note: true,
250+
source_path,
251+
target_path,
252+
}));
253+
}
253254
}
254255

255256
if def_a.repr().c() || def_a.repr().packed() {
@@ -301,43 +302,33 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
301302

302303
None
303304
} else {
304-
Some((i, ty_a, ty_b))
305+
Some((i, ty_a, ty_b, tcx.def_span(field.did)))
305306
}
306307
})
307308
.collect::<Vec<_>>();
308309
res?;
309310

310311
if coerced_fields.is_empty() {
311-
return Err(tcx.dcx().emit_err(errors::DispatchFromDynSingle {
312+
return Err(tcx.dcx().emit_err(errors::CoerceNoField {
312313
span,
313314
trait_name: "DispatchFromDyn",
314315
note: true,
315316
}));
316317
} else if coerced_fields.len() > 1 {
317-
return Err(tcx.dcx().emit_err(errors::DispatchFromDynMulti {
318+
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
318319
span,
319-
coercions_note: true,
320+
trait_name: "DispatchFromDyn",
320321
number: coerced_fields.len(),
321-
coercions: coerced_fields
322-
.iter()
323-
.map(|&(i, ty_a, ty_b)| {
324-
format!("`{}` (`{}` to `{}`)", fields[i].name, ty_a, ty_b,)
325-
})
326-
.collect::<Vec<_>>()
327-
.join(", "),
322+
fields: coerced_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
328323
}));
329324
} else {
330325
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
331-
for (_, ty_a, ty_b) in coerced_fields {
326+
for (_, ty_a, ty_b, _) in coerced_fields {
332327
ocx.register_obligation(Obligation::new(
333328
tcx,
334329
cause.clone(),
335330
param_env,
336-
ty::TraitRef::new(
337-
tcx,
338-
dispatch_from_dyn_trait,
339-
[ty_a, ty_b],
340-
),
331+
ty::TraitRef::new(tcx, dispatch_from_dyn_trait, [ty_a, ty_b]),
341332
));
342333
}
343334
let errors = ocx.select_all_or_error();
@@ -353,7 +344,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
353344
}
354345
_ => Err(tcx
355346
.dcx()
356-
.emit_err(errors::CoerceUnsizedMay { span, trait_name: "DispatchFromDyn" })),
347+
.emit_err(errors::CoerceUnsizedNonStruct { span, trait_name: "DispatchFromDyn" })),
357348
}
358349
}
359350

@@ -419,7 +410,7 @@ pub(crate) fn coerce_unsized_info<'tcx>(
419410
if def_a != def_b {
420411
let source_path = tcx.def_path_str(def_a.did());
421412
let target_path = tcx.def_path_str(def_b.did());
422-
return Err(tcx.dcx().emit_err(errors::DispatchFromDynSame {
413+
return Err(tcx.dcx().emit_err(errors::CoerceSameStruct {
423414
span,
424415
trait_name: "CoerceUnsized",
425416
note: true,
@@ -501,12 +492,12 @@ pub(crate) fn coerce_unsized_info<'tcx>(
501492

502493
// Collect up all fields that were significantly changed
503494
// i.e., those that contain T in coerce_unsized T -> U
504-
Some((i, a, b))
495+
Some((i, a, b, tcx.def_span(f.did)))
505496
})
506497
.collect::<Vec<_>>();
507498

508499
if diff_fields.is_empty() {
509-
return Err(tcx.dcx().emit_err(errors::CoerceUnsizedOneField {
500+
return Err(tcx.dcx().emit_err(errors::CoerceNoField {
510501
span,
511502
trait_name: "CoerceUnsized",
512503
note: true,
@@ -519,27 +510,23 @@ pub(crate) fn coerce_unsized_info<'tcx>(
519510
tcx.def_span(impl_did)
520511
};
521512

522-
return Err(tcx.dcx().emit_err(errors::CoerceUnsizedMulti {
513+
return Err(tcx.dcx().emit_err(errors::CoerceMulti {
523514
span,
524-
coercions_note: true,
515+
trait_name: "CoerceUnsized",
525516
number: diff_fields.len(),
526-
coercions: diff_fields
527-
.iter()
528-
.map(|&(i, a, b)| format!("`{}` (`{}` to `{}`)", fields[i].name, a, b))
529-
.collect::<Vec<_>>()
530-
.join(", "),
517+
fields: diff_fields.iter().map(|(_, _, _, s)| *s).collect::<Vec<_>>().into(),
531518
}));
532519
}
533520

534-
let (i, a, b) = diff_fields[0];
521+
let (i, a, b, _) = diff_fields[0];
535522
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
536523
(a, b, coerce_unsized_trait, Some(kind))
537524
}
538525

539526
_ => {
540527
return Err(tcx
541528
.dcx()
542-
.emit_err(errors::DispatchFromDynStruct { span, trait_name: "CoerceUnsized" }));
529+
.emit_err(errors::CoerceUnsizedNonStruct { span, trait_name: "CoerceUnsized" }));
543530
}
544531
};
545532

0 commit comments

Comments
 (0)