Skip to content

Commit af19a50

Browse files
authored
Rollup merge of #94249 - compiler-errors:better-copy-errors, r=davidtwco
Better errors when a Copy impl on a Struct is not self-consistent As discovered in a Zulip thread with `@nnethercote` and `@Mark-Simulacrum,` it's not immediately obvious why a field on an ADT doesn't implement `Copy`. This PR attempts to give slightly more detailed information by spinning up a fulfillment context to try to dig down and discover transitive fulfillment errors that cause `is_copy_modulo_regions` to fail on a ADT field. The error message still kinda sucks, but should only show up in the case that an existing error message was totally missing... so I think it's a good compromise for now?
2 parents 547369d + c8cbd3d commit af19a50

File tree

5 files changed

+100
-4
lines changed

5 files changed

+100
-4
lines changed

compiler/rustc_trait_selection/src/traits/misc.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::traits::error_reporting::InferCtxtExt;
1111

1212
#[derive(Clone)]
1313
pub enum CopyImplementationError<'tcx> {
14-
InfrigingFields(Vec<&'tcx ty::FieldDef>),
14+
InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>)>),
1515
NotAnAdt,
1616
HasDestructor,
1717
}
@@ -67,7 +67,7 @@ pub fn can_type_implement_copy<'tcx>(
6767
match traits::fully_normalize(&infcx, ctx, cause, param_env, ty) {
6868
Ok(ty) => {
6969
if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
70-
infringing.push(field);
70+
infringing.push((field, ty));
7171
}
7272
}
7373
Err(errors) => {

compiler/rustc_typeck/src/coherence/builtin.rs

+34-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,40 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
9191
E0204,
9292
"the trait `Copy` may not be implemented for this type"
9393
);
94-
for span in fields.iter().map(|f| tcx.def_span(f.did)) {
95-
err.span_label(span, "this field does not implement `Copy`");
94+
for (field, ty) in fields {
95+
let field_span = tcx.def_span(field.did);
96+
err.span_label(field_span, "this field does not implement `Copy`");
97+
// Spin up a new FulfillmentContext, so we can get the _precise_ reason
98+
// why this field does not implement Copy. This is useful because sometimes
99+
// it is not immediately clear why Copy is not implemented for a field, since
100+
// all we point at is the field itself.
101+
tcx.infer_ctxt().enter(|infcx| {
102+
let mut fulfill_cx = traits::FulfillmentContext::new_ignoring_regions();
103+
fulfill_cx.register_bound(
104+
&infcx,
105+
param_env,
106+
ty,
107+
tcx.lang_items().copy_trait().unwrap(),
108+
traits::ObligationCause::dummy_with_span(field_span),
109+
);
110+
for error in fulfill_cx.select_all_or_error(&infcx) {
111+
let error_predicate = error.obligation.predicate;
112+
// Only note if it's not the root obligation, otherwise it's trivial and
113+
// should be self-explanatory (i.e. a field literally doesn't implement Copy).
114+
115+
// FIXME: This error could be more descriptive, especially if the error_predicate
116+
// contains a foreign type or if it's a deeply nested type...
117+
if error_predicate != error.root_obligation.predicate {
118+
err.span_note(
119+
error.obligation.cause.span,
120+
&format!(
121+
"the `Copy` impl for `{}` requires that `{}`",
122+
ty, error_predicate
123+
),
124+
);
125+
}
126+
}
127+
});
96128
}
97129
err.emit();
98130
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#![feature(extern_types)]
2+
3+
extern "Rust" {
4+
type OpaqueListContents;
5+
}
6+
7+
pub struct ListS<T> {
8+
len: usize,
9+
data: [T; 0],
10+
opaque: OpaqueListContents,
11+
}
12+
13+
pub struct Interned<'a, T>(&'a T);
14+
15+
impl<'a, T> Clone for Interned<'a, T> {
16+
fn clone(&self) -> Self {
17+
*self
18+
}
19+
}
20+
21+
impl<'a, T> Copy for Interned<'a, T> {}
22+
23+
pub struct List<'tcx, T>(Interned<'tcx, ListS<T>>);
24+
//~^ NOTE this field does not implement `Copy`
25+
//~| NOTE the `Copy` impl for `Interned<'tcx, ListS<T>>` requires that `OpaqueListContents: Sized`
26+
27+
impl<'tcx, T> Clone for List<'tcx, T> {
28+
fn clone(&self) -> Self {
29+
*self
30+
}
31+
}
32+
33+
impl<'tcx, T> Copy for List<'tcx, T> {}
34+
//~^ ERROR the trait `Copy` may not be implemented for this type
35+
36+
fn assert_is_copy<T: Copy>() {}
37+
38+
fn main() {
39+
assert_is_copy::<List<'static, ()>>();
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0204]: the trait `Copy` may not be implemented for this type
2+
--> $DIR/deep-bad-copy-reason.rs:33:15
3+
|
4+
LL | pub struct List<'tcx, T>(Interned<'tcx, ListS<T>>);
5+
| ------------------------ this field does not implement `Copy`
6+
...
7+
LL | impl<'tcx, T> Copy for List<'tcx, T> {}
8+
| ^^^^
9+
|
10+
note: the `Copy` impl for `Interned<'tcx, ListS<T>>` requires that `OpaqueListContents: Sized`
11+
--> $DIR/deep-bad-copy-reason.rs:23:26
12+
|
13+
LL | pub struct List<'tcx, T>(Interned<'tcx, ListS<T>>);
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^
15+
16+
error: aborting due to previous error
17+
18+
For more information about this error, try `rustc --explain E0204`.

src/test/ui/union/union-copy.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ LL | a: std::mem::ManuallyDrop<String>
66
...
77
LL | impl Copy for W {}
88
| ^^^^
9+
|
10+
note: the `Copy` impl for `ManuallyDrop<String>` requires that `String: Copy`
11+
--> $DIR/union-copy.rs:8:5
12+
|
13+
LL | a: std::mem::ManuallyDrop<String>
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
915

1016
error: aborting due to previous error
1117

0 commit comments

Comments
 (0)