Skip to content

Commit 7b97a5c

Browse files
committed
Auto merge of #117511 - gurry:117406-err-packed-structs, r=compiler-errors
Emit explanatory note for move errors in packed struct derives Derive expansions for packed structs with non-`Copy` fields cause move errors because they prefer copying over borrowing since borrowing the fields of a packed struct can result in unaligned access. This underlying cause of the errors, however, is not apparent to the user. This PR adds a diagnostic note to make it clear to the user (the new note is on the second last line): ``` tests/ui/derives/deriving-with-repr-packed-move-errors.rs:13:16 | 12 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] | ----- in this derive macro expansion 13 | struct StructA(String); | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait | = note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) ``` Fixes #117406 Partially addresses #110777
2 parents 189d6c7 + 4b3ece4 commit 7b97a5c

File tree

4 files changed

+290
-1
lines changed

4 files changed

+290
-1
lines changed

compiler/rustc_borrowck/src/diagnostics/move_errors.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed
22
use rustc_middle::mir::*;
33
use rustc_middle::ty::{self, Ty};
44
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
5-
use rustc_span::{BytePos, Span};
5+
use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
66

77
use crate::diagnostics::CapturedMessageOpt;
88
use crate::diagnostics::{DescribePlaceOpt, UseSpans};
@@ -488,6 +488,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
488488
args_span,
489489
}
490490
});
491+
492+
self.add_note_for_packed_struct_derive(err, original_path.local);
491493
}
492494
}
493495
}
@@ -594,4 +596,20 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
594596
);
595597
}
596598
}
599+
600+
/// Adds an explanatory note if the move error occurs in a derive macro
601+
/// expansion of a packed struct.
602+
/// Such errors happen because derive macro expansions shy away from taking
603+
/// references to the struct's fields since doing so would be undefined behaviour
604+
fn add_note_for_packed_struct_derive(&self, err: &mut Diagnostic, local: Local) {
605+
let local_place: PlaceRef<'tcx> = local.into();
606+
let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs();
607+
608+
if let Some(adt) = local_ty.ty_adt_def()
609+
&& adt.repr().packed()
610+
&& let ExpnKind::Macro(MacroKind::Derive, name) = self.body.span.ctxt().outer_expn_data().kind
611+
{
612+
err.note(format!("`#[derive({name})]` triggers a move because taking references to the fields of a packed struct is undefined behaviour"));
613+
}
614+
}
597615
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// Check that deriving builtin traits for a packed struct with
2+
// non-Copy fields emits move errors along with an additional
3+
// diagnostic note explaining the reason
4+
// See issue #117406
5+
6+
use std::fmt::{Debug, Formatter, Result};
7+
use std::cmp::Ordering;
8+
9+
// Packed + derives: additional diagnostic should be emitted
10+
// for each of Debug, PartialEq and PartialOrd
11+
#[repr(packed)]
12+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
13+
struct StructA(String);
14+
//~^ ERROR: cannot move out of `self` which is behind a shared reference
15+
//~| ERROR: cannot move out of `self` which is behind a shared reference
16+
//~| ERROR: cannot move out of `other` which is behind a shared reference
17+
//~| ERROR: cannot move out of `self` which is behind a shared reference
18+
//~| ERROR: cannot move out of `other` which is behind a shared reference
19+
//~| ERROR: cannot move out of `self` which is behind a shared reference
20+
//~| ERROR: cannot move out of `other` which is behind a shared reference
21+
//~| ERROR: cannot move out of `self` which is behind a shared reference
22+
//~| ERROR: cannot move out of `self` which is behind a shared reference
23+
24+
25+
// Unrelated impl: additinal diagnostic should NOT be emitted
26+
impl StructA {
27+
fn fmt(&self) -> String {
28+
self.0 //~ ERROR: cannot move out of `self` which is behind a shared reference
29+
}
30+
}
31+
32+
// Packed + manual impls: additional diagnostic should NOT be emitted
33+
#[repr(packed)]
34+
struct StructB(String);
35+
36+
impl Debug for StructB {
37+
fn fmt(&self, f: &mut Formatter) -> Result {
38+
let x = &{ self.0 }; //~ ERROR: cannot move out of `self` which is behind a shared reference
39+
write!(f, "{}", x)
40+
}
41+
}
42+
43+
impl PartialEq for StructB {
44+
fn eq(&self, other: &StructB) -> bool {
45+
({ self.0 }) == ({ other.0 })
46+
//~^ ERROR: cannot move out of `self` which is behind a shared reference
47+
//~| ERROR: cannot move out of `other` which is behind a shared reference
48+
}
49+
}
50+
51+
impl PartialOrd for StructB {
52+
fn partial_cmp(&self, other: &StructB) -> Option<Ordering> {
53+
PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
54+
//~^ ERROR: cannot move out of `self` which is behind a shared reference
55+
//~| ERROR: cannot move out of `other` which is behind a shared reference
56+
}
57+
}
58+
59+
// NOT packed + derives: additinal diagnostic should NOT be emitted
60+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
61+
struct StructC(String);
62+
63+
// NOT packed + manual impls: additinal dignostic should NOT be emitted
64+
struct StructD(String);
65+
66+
impl Debug for StructD {
67+
fn fmt(&self, f: &mut Formatter) -> Result {
68+
let x = &{ self.0 }; //~ ERROR: cannot move out of `self` which is behind a shared reference
69+
write!(f, "{}", x)
70+
}
71+
}
72+
73+
impl PartialEq for StructD {
74+
fn eq(&self, other: &StructD) -> bool {
75+
({ self.0 }) == ({ other.0 })
76+
//~^ ERROR: cannot move out of `self` which is behind a shared reference
77+
//~| ERROR: cannot move out of `other` which is behind a shared reference
78+
}
79+
}
80+
81+
impl PartialOrd for StructD {
82+
fn partial_cmp(&self, other: &StructD) -> Option<Ordering> {
83+
PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
84+
//~^ ERROR: cannot move out of `self` which is behind a shared reference
85+
//~| ERROR: cannot move out of `other` which is behind a shared reference
86+
}
87+
}
88+
89+
// Packed + derives but the move is outside of a derive
90+
// expansion: additinal diagnostic should NOT be emitted
91+
fn func(arg: &StructA) -> String {
92+
arg.0 //~ ERROR: cannot move out of `arg` which is behind a shared reference
93+
}
94+
95+
fn main(){
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
error[E0507]: cannot move out of `self` which is behind a shared reference
2+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
3+
|
4+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
5+
| ----- in this derive macro expansion
6+
LL | struct StructA(String);
7+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
8+
|
9+
= note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
10+
= note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
11+
12+
error[E0507]: cannot move out of `self` which is behind a shared reference
13+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
14+
|
15+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
16+
| --------- in this derive macro expansion
17+
LL | struct StructA(String);
18+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
19+
|
20+
= note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
21+
= note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
22+
23+
error[E0507]: cannot move out of `other` which is behind a shared reference
24+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
25+
|
26+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
27+
| --------- in this derive macro expansion
28+
LL | struct StructA(String);
29+
| ^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
30+
|
31+
= note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
32+
= note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
33+
34+
error[E0507]: cannot move out of `self` which is behind a shared reference
35+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
36+
|
37+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
38+
| ---------- in this derive macro expansion
39+
LL | struct StructA(String);
40+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
41+
|
42+
= note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
43+
= note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)
44+
45+
error[E0507]: cannot move out of `other` which is behind a shared reference
46+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
47+
|
48+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
49+
| ---------- in this derive macro expansion
50+
LL | struct StructA(String);
51+
| ^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
52+
|
53+
= note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
54+
= note: this error originates in the derive macro `PartialOrd` (in Nightly builds, run with -Z macro-backtrace for more info)
55+
56+
error[E0507]: cannot move out of `self` which is behind a shared reference
57+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
58+
|
59+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
60+
| --- in this derive macro expansion
61+
LL | struct StructA(String);
62+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
63+
|
64+
= note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
65+
= note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info)
66+
67+
error[E0507]: cannot move out of `other` which is behind a shared reference
68+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
69+
|
70+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
71+
| --- in this derive macro expansion
72+
LL | struct StructA(String);
73+
| ^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
74+
|
75+
= note: `#[derive(Ord)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
76+
= note: this error originates in the derive macro `Ord` (in Nightly builds, run with -Z macro-backtrace for more info)
77+
78+
error[E0507]: cannot move out of `self` which is behind a shared reference
79+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
80+
|
81+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
82+
| ---- in this derive macro expansion
83+
LL | struct StructA(String);
84+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
85+
|
86+
= note: `#[derive(Hash)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
87+
= note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
88+
89+
error[E0507]: cannot move out of `self` which is behind a shared reference
90+
--> $DIR/deriving-with-repr-packed-move-errors.rs:13:16
91+
|
92+
LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)]
93+
| ----- in this derive macro expansion
94+
LL | struct StructA(String);
95+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
96+
|
97+
= note: `#[derive(Clone)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
98+
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
99+
100+
error[E0507]: cannot move out of `self` which is behind a shared reference
101+
--> $DIR/deriving-with-repr-packed-move-errors.rs:28:9
102+
|
103+
LL | self.0
104+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
105+
106+
error[E0507]: cannot move out of `self` which is behind a shared reference
107+
--> $DIR/deriving-with-repr-packed-move-errors.rs:38:20
108+
|
109+
LL | let x = &{ self.0 };
110+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
111+
112+
error[E0507]: cannot move out of `self` which is behind a shared reference
113+
--> $DIR/deriving-with-repr-packed-move-errors.rs:45:12
114+
|
115+
LL | ({ self.0 }) == ({ other.0 })
116+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
117+
118+
error[E0507]: cannot move out of `other` which is behind a shared reference
119+
--> $DIR/deriving-with-repr-packed-move-errors.rs:45:28
120+
|
121+
LL | ({ self.0 }) == ({ other.0 })
122+
| ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
123+
124+
error[E0507]: cannot move out of `self` which is behind a shared reference
125+
--> $DIR/deriving-with-repr-packed-move-errors.rs:53:36
126+
|
127+
LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
128+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
129+
130+
error[E0507]: cannot move out of `other` which is behind a shared reference
131+
--> $DIR/deriving-with-repr-packed-move-errors.rs:53:49
132+
|
133+
LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
134+
| ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
135+
136+
error[E0507]: cannot move out of `self` which is behind a shared reference
137+
--> $DIR/deriving-with-repr-packed-move-errors.rs:68:20
138+
|
139+
LL | let x = &{ self.0 };
140+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
141+
142+
error[E0507]: cannot move out of `self` which is behind a shared reference
143+
--> $DIR/deriving-with-repr-packed-move-errors.rs:75:12
144+
|
145+
LL | ({ self.0 }) == ({ other.0 })
146+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
147+
148+
error[E0507]: cannot move out of `other` which is behind a shared reference
149+
--> $DIR/deriving-with-repr-packed-move-errors.rs:75:28
150+
|
151+
LL | ({ self.0 }) == ({ other.0 })
152+
| ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
153+
154+
error[E0507]: cannot move out of `self` which is behind a shared reference
155+
--> $DIR/deriving-with-repr-packed-move-errors.rs:83:36
156+
|
157+
LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
158+
| ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait
159+
160+
error[E0507]: cannot move out of `other` which is behind a shared reference
161+
--> $DIR/deriving-with-repr-packed-move-errors.rs:83:49
162+
|
163+
LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 })
164+
| ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait
165+
166+
error[E0507]: cannot move out of `arg` which is behind a shared reference
167+
--> $DIR/deriving-with-repr-packed-move-errors.rs:92:5
168+
|
169+
LL | arg.0
170+
| ^^^^^ move occurs because `arg.0` has type `String`, which does not implement the `Copy` trait
171+
172+
error: aborting due to 21 previous errors
173+
174+
For more information about this error, try `rustc --explain E0507`.

tests/ui/derives/deriving-with-repr-packed.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ LL | #[repr(packed)]
3636
LL | struct X(Y);
3737
| ^ move occurs because `self.0` has type `Y`, which does not implement the `Copy` trait
3838
|
39+
= note: `#[derive(Debug)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour
3940
= note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
4041

4142
error: aborting due to previous error; 2 warnings emitted

0 commit comments

Comments
 (0)