Skip to content

Commit a900677

Browse files
authored
Rollup merge of #82525 - RalfJung:unaligned-ref-warn, r=petrochenkov
make unaligned_references future-incompat lint warn-by-default and also remove the safe_packed_borrows lint that it replaces. `std::ptr::addr_of!` has hit beta now and will hit stable in a month, so I propose we start fixing #27060 for real: creating a reference to a field of a packed struct needs to eventually become a hard error; this PR makes it a warn-by-default future-incompat lint. (The lint already existed, this just raises its default level.) At the same time I removed the corresponding code from unsafety checking; really there's no reason an `unsafe` block should make any difference here. For references to packed fields outside `unsafe` blocks, this means `unaligned_refereces` replaces the previous `safe_packed_borrows` warning with a link to #82523 (and no more talk about unsafe blocks making any difference). So behavior barely changes, the warning is just worded differently. For references to packed fields inside `unsafe` blocks, this PR shows a new future-incompat warning. Closes #46043 because that lint no longer exists.
2 parents 520c9a2 + fb4f48e commit a900677

25 files changed

+251
-415
lines changed

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
325325
store.register_renamed("exceeding_bitshifts", "arithmetic_overflow");
326326
store.register_renamed("redundant_semicolon", "redundant_semicolons");
327327
store.register_renamed("overlapping_patterns", "overlapping_range_endpoints");
328+
store.register_renamed("safe_packed_borrows", "unaligned_references");
328329

329330
// These were moved to tool lints, but rustc still sees them when compiling normally, before
330331
// tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use

compiler/rustc_lint_defs/src/builtin.rs

+11-54
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,7 @@ declare_lint! {
10571057
/// unsafe {
10581058
/// let foo = Foo { field1: 0, field2: 0 };
10591059
/// let _ = &foo.field1;
1060+
/// println!("{}", foo.field1); // An implicit `&` is added here, triggering the lint.
10601061
/// }
10611062
/// }
10621063
/// ```
@@ -1065,20 +1066,20 @@ declare_lint! {
10651066
///
10661067
/// ### Explanation
10671068
///
1068-
/// Creating a reference to an insufficiently aligned packed field is
1069-
/// [undefined behavior] and should be disallowed.
1070-
///
1071-
/// This lint is "allow" by default because there is no stable
1072-
/// alternative, and it is not yet certain how widespread existing code
1073-
/// will trigger this lint.
1074-
///
1075-
/// See [issue #27060] for more discussion.
1069+
/// Creating a reference to an insufficiently aligned packed field is [undefined behavior] and
1070+
/// should be disallowed. Using an `unsafe` block does not change anything about this. Instead,
1071+
/// the code should do a copy of the data in the packed field or use raw pointers and unaligned
1072+
/// accesses. See [issue #82523] for more information.
10761073
///
10771074
/// [undefined behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
1078-
/// [issue #27060]: https://github.com/rust-lang/rust/issues/27060
1075+
/// [issue #82523]: https://github.com/rust-lang/rust/issues/82523
10791076
pub UNALIGNED_REFERENCES,
1080-
Allow,
1077+
Warn,
10811078
"detects unaligned references to fields of packed structs",
1079+
@future_incompatible = FutureIncompatibleInfo {
1080+
reference: "issue #82523 <https://github.com/rust-lang/rust/issues/82523>",
1081+
edition: None,
1082+
};
10821083
report_in_external_macro
10831084
}
10841085

@@ -1150,49 +1151,6 @@ declare_lint! {
11501151
"detects attempts to mutate a `const` item",
11511152
}
11521153

1153-
declare_lint! {
1154-
/// The `safe_packed_borrows` lint detects borrowing a field in the
1155-
/// interior of a packed structure with alignment other than 1.
1156-
///
1157-
/// ### Example
1158-
///
1159-
/// ```rust
1160-
/// #[repr(packed)]
1161-
/// pub struct Unaligned<T>(pub T);
1162-
///
1163-
/// pub struct Foo {
1164-
/// start: u8,
1165-
/// data: Unaligned<u32>,
1166-
/// }
1167-
///
1168-
/// fn main() {
1169-
/// let x = Foo { start: 0, data: Unaligned(1) };
1170-
/// let y = &x.data.0;
1171-
/// }
1172-
/// ```
1173-
///
1174-
/// {{produces}}
1175-
///
1176-
/// ### Explanation
1177-
///
1178-
/// This type of borrow is unsafe and can cause errors on some platforms
1179-
/// and violates some assumptions made by the compiler. This was
1180-
/// previously allowed unintentionally. This is a [future-incompatible]
1181-
/// lint to transition this to a hard error in the future. See [issue
1182-
/// #46043] for more details, including guidance on how to solve the
1183-
/// problem.
1184-
///
1185-
/// [issue #46043]: https://github.com/rust-lang/rust/issues/46043
1186-
/// [future-incompatible]: ../index.md#future-incompatible-lints
1187-
pub SAFE_PACKED_BORROWS,
1188-
Warn,
1189-
"safe borrows of fields of packed structs were erroneously allowed",
1190-
@future_incompatible = FutureIncompatibleInfo {
1191-
reference: "issue #46043 <https://github.com/rust-lang/rust/issues/46043>",
1192-
edition: None,
1193-
};
1194-
}
1195-
11961154
declare_lint! {
11971155
/// The `patterns_in_fns_without_body` lint detects `mut` identifier
11981156
/// patterns as a parameter in functions without a body.
@@ -2953,7 +2911,6 @@ declare_lint_pass! {
29532911
RENAMED_AND_REMOVED_LINTS,
29542912
UNALIGNED_REFERENCES,
29552913
CONST_ITEM_MUTATION,
2956-
SAFE_PACKED_BORROWS,
29572914
PATTERNS_IN_FNS_WITHOUT_BODY,
29582915
MISSING_FRAGMENT_SPECIFIER,
29592916
LATE_BOUND_LIFETIME_ARGUMENTS,

compiler/rustc_middle/src/mir/query.rs

-6
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,9 @@ pub enum UnsafetyViolationKind {
2323
General,
2424
/// Permitted both in `const fn`s and regular `fn`s.
2525
GeneralAndConstFn,
26-
/// Borrow of packed field.
27-
/// Has to be handled as a lint for backwards compatibility.
28-
BorrowPacked,
2926
/// Unsafe operation in an `unsafe fn` but outside an `unsafe` block.
3027
/// Has to be handled as a lint for backwards compatibility.
3128
UnsafeFn,
32-
/// Borrow of packed field in an `unsafe fn` but outside an `unsafe` block.
33-
/// Has to be handled as a lint for backwards compatibility.
34-
UnsafeFnBorrowPacked,
3529
}
3630

3731
#[derive(Copy, Clone, PartialEq, TyEncodable, TyDecodable, HashStable, Debug)]

compiler/rustc_mir/src/transform/check_packed_ref.rs

+69-20
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
use rustc_hir::def_id::{DefId, LocalDefId};
12
use rustc_middle::mir::visit::{PlaceContext, Visitor};
23
use rustc_middle::mir::*;
4+
use rustc_middle::ty::query::Providers;
35
use rustc_middle::ty::{self, TyCtxt};
46
use rustc_session::lint::builtin::UNALIGNED_REFERENCES;
7+
use rustc_span::symbol::sym;
58

69
use crate::transform::MirPass;
710
use crate::util;
811

12+
pub(crate) fn provide(providers: &mut Providers) {
13+
*providers = Providers { unsafe_derive_on_repr_packed, ..*providers };
14+
}
15+
916
pub struct CheckPackedRef;
1017

1118
impl<'tcx> MirPass<'tcx> for CheckPackedRef {
@@ -24,6 +31,41 @@ struct PackedRefChecker<'a, 'tcx> {
2431
source_info: SourceInfo,
2532
}
2633

34+
fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
35+
let lint_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
36+
37+
tcx.struct_span_lint_hir(UNALIGNED_REFERENCES, lint_hir_id, tcx.def_span(def_id), |lint| {
38+
// FIXME: when we make this a hard error, this should have its
39+
// own error code.
40+
let message = if tcx.generics_of(def_id).own_requires_monomorphization() {
41+
"`#[derive]` can't be used on a `#[repr(packed)]` struct with \
42+
type or const parameters (error E0133)"
43+
.to_string()
44+
} else {
45+
"`#[derive]` can't be used on a `#[repr(packed)]` struct that \
46+
does not derive Copy (error E0133)"
47+
.to_string()
48+
};
49+
lint.build(&message).emit()
50+
});
51+
}
52+
53+
fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> {
54+
debug!("builtin_derive_def_id({:?})", def_id);
55+
if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
56+
if tcx.has_attr(impl_def_id, sym::automatically_derived) {
57+
debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id);
58+
Some(impl_def_id)
59+
} else {
60+
debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id);
61+
None
62+
}
63+
} else {
64+
debug!("builtin_derive_def_id({:?}) - not a method", def_id);
65+
None
66+
}
67+
}
68+
2769
impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> {
2870
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
2971
// Make sure we know where in the MIR we are.
@@ -40,26 +82,33 @@ impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> {
4082
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) {
4183
if context.is_borrow() {
4284
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
43-
let source_info = self.source_info;
44-
let lint_root = self.body.source_scopes[source_info.scope]
45-
.local_data
46-
.as_ref()
47-
.assert_crate_local()
48-
.lint_root;
49-
self.tcx.struct_span_lint_hir(
50-
UNALIGNED_REFERENCES,
51-
lint_root,
52-
source_info.span,
53-
|lint| {
54-
lint.build("reference to packed field is unaligned")
55-
.note(
56-
"fields of packed structs are not properly aligned, and creating \
57-
a misaligned reference is undefined behavior (even if that \
58-
reference is never dereferenced)",
59-
)
60-
.emit()
61-
},
62-
);
85+
let def_id = self.body.source.instance.def_id();
86+
if let Some(impl_def_id) = builtin_derive_def_id(self.tcx, def_id) {
87+
// If a method is defined in the local crate,
88+
// the impl containing that method should also be.
89+
self.tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local());
90+
} else {
91+
let source_info = self.source_info;
92+
let lint_root = self.body.source_scopes[source_info.scope]
93+
.local_data
94+
.as_ref()
95+
.assert_crate_local()
96+
.lint_root;
97+
self.tcx.struct_span_lint_hir(
98+
UNALIGNED_REFERENCES,
99+
lint_root,
100+
source_info.span,
101+
|lint| {
102+
lint.build("reference to packed field is unaligned")
103+
.note(
104+
"fields of packed structs are not properly aligned, and creating \
105+
a misaligned reference is undefined behavior (even if that \
106+
reference is never dereferenced)",
107+
)
108+
.emit()
109+
},
110+
);
111+
}
63112
}
64113
}
65114
}

0 commit comments

Comments
 (0)