]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use rustc_hir::def_id::{DefId, LocalDefId}; |
f9f354fc XL |
2 | use rustc_middle::mir::visit::{PlaceContext, Visitor}; |
3 | use rustc_middle::mir::*; | |
cdc7bbd5 | 4 | use rustc_middle::ty::query::Providers; |
f9f354fc XL |
5 | use rustc_middle::ty::{self, TyCtxt}; |
6 | use rustc_session::lint::builtin::UNALIGNED_REFERENCES; | |
cdc7bbd5 | 7 | use rustc_span::symbol::sym; |
f9f354fc | 8 | |
f9f354fc | 9 | use crate::util; |
a2a8927a | 10 | use crate::MirLint; |
f9f354fc | 11 | |
cdc7bbd5 XL |
12 | pub(crate) fn provide(providers: &mut Providers) { |
13 | *providers = Providers { unsafe_derive_on_repr_packed, ..*providers }; | |
14 | } | |
15 | ||
f9f354fc XL |
16 | pub struct CheckPackedRef; |
17 | ||
a2a8927a XL |
18 | impl<'tcx> MirLint<'tcx> for CheckPackedRef { |
19 | fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { | |
29967ef6 | 20 | let param_env = tcx.param_env(body.source.def_id()); |
f9f354fc XL |
21 | let source_info = SourceInfo::outermost(body.span); |
22 | let mut checker = PackedRefChecker { body, tcx, param_env, source_info }; | |
23 | checker.visit_body(&body); | |
24 | } | |
25 | } | |
26 | ||
27 | struct PackedRefChecker<'a, 'tcx> { | |
28 | body: &'a Body<'tcx>, | |
29 | tcx: TyCtxt<'tcx>, | |
30 | param_env: ty::ParamEnv<'tcx>, | |
31 | source_info: SourceInfo, | |
32 | } | |
33 | ||
cdc7bbd5 XL |
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 | }; | |
5e7ed085 | 49 | lint.build(&message).emit(); |
cdc7bbd5 XL |
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 | ||
a2a8927a | 69 | impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { |
f9f354fc XL |
70 | fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { |
71 | // Make sure we know where in the MIR we are. | |
72 | self.source_info = terminator.source_info; | |
73 | self.super_terminator(terminator, location); | |
74 | } | |
75 | ||
76 | fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { | |
77 | // Make sure we know where in the MIR we are. | |
78 | self.source_info = statement.source_info; | |
79 | self.super_statement(statement, location); | |
80 | } | |
81 | ||
82 | fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { | |
83 | if context.is_borrow() { | |
84 | if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { | |
cdc7bbd5 XL |
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 | ) | |
a2a8927a XL |
108 | .help( |
109 | "copy the field contents to a local variable, or replace the \ | |
110 | reference with a raw pointer and use `read_unaligned`/`write_unaligned` \ | |
111 | (loads and stores via `*p` must be properly aligned even when using raw pointers)" | |
112 | ) | |
5e7ed085 | 113 | .emit(); |
cdc7bbd5 XL |
114 | }, |
115 | ); | |
116 | } | |
f9f354fc XL |
117 | } |
118 | } | |
119 | } | |
120 | } |