Skip to content

Commit 4b3ece4

Browse files
committed
Emit explanatory note for move errors in packed struct derives
Derive expansions for packed structs cause move errors because they prefer copying over borrowing since borrowing the fields of a packed struct can result in unaligned access and therefore undefined behaviour. This underlying cause of the errors, however, is not apparent to the user. We add a diagnostic note here to remedy that.
1 parent a395214 commit 4b3ece4

File tree

4 files changed

+290
-1
lines changed

4 files changed

+290
-1
lines changed

Diff for: 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
}
+96
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`.

Diff for: 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)