]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | use rustc_data_structures::fx::FxHashSet; |
2 | use rustc_errors::struct_span_err; | |
3 | use rustc_hir as hir; | |
f9f354fc XL |
4 | use rustc_hir::def_id::{DefId, LocalDefId}; |
5 | use rustc_hir::hir_id::HirId; | |
dfeec247 XL |
6 | use rustc_hir::intravisit; |
7 | use rustc_hir::Node; | |
ba9703b0 XL |
8 | use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor}; |
9 | use rustc_middle::mir::*; | |
10 | use rustc_middle::ty::cast::CastTy; | |
11 | use rustc_middle::ty::query::Providers; | |
12 | use rustc_middle::ty::{self, TyCtxt}; | |
f9f354fc XL |
13 | use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; |
14 | use rustc_session::lint::Level; | |
dfeec247 | 15 | use rustc_span::symbol::{sym, Symbol}; |
ea8adc8c | 16 | |
0731742a XL |
17 | use std::ops::Bound; |
18 | ||
f9f354fc | 19 | use crate::const_eval::is_min_const_fn; |
9fa01778 | 20 | use crate::util; |
ea8adc8c | 21 | |
dc9dc135 XL |
22 | pub struct UnsafetyChecker<'a, 'tcx> { |
23 | body: &'a Body<'tcx>, | |
f9f354fc | 24 | body_did: LocalDefId, |
9fa01778 | 25 | const_context: bool, |
b7449926 | 26 | min_const_fn: bool, |
ea8adc8c XL |
27 | violations: Vec<UnsafetyViolation>, |
28 | source_info: SourceInfo, | |
dc9dc135 | 29 | tcx: TyCtxt<'tcx>, |
ea8adc8c | 30 | param_env: ty::ParamEnv<'tcx>, |
9fa01778 | 31 | /// Mark an `unsafe` block as used, so we don't lint it. |
532ac7d7 XL |
32 | used_unsafe: FxHashSet<hir::HirId>, |
33 | inherited_blocks: Vec<(hir::HirId, bool)>, | |
ea8adc8c XL |
34 | } |
35 | ||
dc9dc135 | 36 | impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { |
b7449926 | 37 | fn new( |
9fa01778 | 38 | const_context: bool, |
b7449926 | 39 | min_const_fn: bool, |
dc9dc135 | 40 | body: &'a Body<'tcx>, |
f9f354fc | 41 | body_did: LocalDefId, |
dc9dc135 | 42 | tcx: TyCtxt<'tcx>, |
b7449926 XL |
43 | param_env: ty::ParamEnv<'tcx>, |
44 | ) -> Self { | |
9fa01778 XL |
45 | // sanity check |
46 | if min_const_fn { | |
47 | assert!(const_context); | |
48 | } | |
ea8adc8c | 49 | Self { |
dc9dc135 | 50 | body, |
f9f354fc | 51 | body_did, |
9fa01778 | 52 | const_context, |
b7449926 | 53 | min_const_fn, |
ea8adc8c | 54 | violations: vec![], |
f9f354fc | 55 | source_info: SourceInfo::outermost(body.span), |
ea8adc8c XL |
56 | tcx, |
57 | param_env, | |
0bf4aa26 | 58 | used_unsafe: Default::default(), |
ea8adc8c XL |
59 | inherited_blocks: vec![], |
60 | } | |
61 | } | |
62 | } | |
63 | ||
64 | impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { | |
dfeec247 | 65 | fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { |
ea8adc8c XL |
66 | self.source_info = terminator.source_info; |
67 | match terminator.kind { | |
dfeec247 XL |
68 | TerminatorKind::Goto { .. } |
69 | | TerminatorKind::SwitchInt { .. } | |
70 | | TerminatorKind::Drop { .. } | |
71 | | TerminatorKind::Yield { .. } | |
72 | | TerminatorKind::Assert { .. } | |
73 | | TerminatorKind::DropAndReplace { .. } | |
74 | | TerminatorKind::GeneratorDrop | |
75 | | TerminatorKind::Resume | |
76 | | TerminatorKind::Abort | |
77 | | TerminatorKind::Return | |
78 | | TerminatorKind::Unreachable | |
79 | | TerminatorKind::FalseEdges { .. } | |
80 | | TerminatorKind::FalseUnwind { .. } => { | |
ea8adc8c XL |
81 | // safe (at least as emitted during MIR construction) |
82 | } | |
83 | ||
84 | TerminatorKind::Call { ref func, .. } => { | |
dc9dc135 | 85 | let func_ty = func.ty(self.body, self.tcx); |
ea8adc8c XL |
86 | let sig = func_ty.fn_sig(self.tcx); |
87 | if let hir::Unsafety::Unsafe = sig.unsafety() { | |
dfeec247 XL |
88 | self.require_unsafe( |
89 | "call to unsafe function", | |
8faf50e0 | 90 | "consult the function's documentation for information on how to avoid \ |
dfeec247 XL |
91 | undefined behavior", |
92 | UnsafetyViolationKind::GeneralAndConstFn, | |
93 | ) | |
ea8adc8c | 94 | } |
f9f354fc XL |
95 | |
96 | if let ty::FnDef(func_id, _) = func_ty.kind { | |
97 | self.check_target_features(func_id); | |
98 | } | |
ea8adc8c | 99 | } |
f9f354fc XL |
100 | |
101 | TerminatorKind::InlineAsm { .. } => self.require_unsafe( | |
102 | "use of inline assembly", | |
103 | "inline assembly is entirely unchecked and can cause undefined behavior", | |
104 | UnsafetyViolationKind::General, | |
105 | ), | |
ea8adc8c | 106 | } |
48663c56 | 107 | self.super_terminator(terminator, location); |
ea8adc8c XL |
108 | } |
109 | ||
dfeec247 | 110 | fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { |
ea8adc8c XL |
111 | self.source_info = statement.source_info; |
112 | match statement.kind { | |
dfeec247 XL |
113 | StatementKind::Assign(..) |
114 | | StatementKind::FakeRead(..) | |
115 | | StatementKind::SetDiscriminant { .. } | |
116 | | StatementKind::StorageLive(..) | |
117 | | StatementKind::StorageDead(..) | |
118 | | StatementKind::Retag { .. } | |
119 | | StatementKind::AscribeUserType(..) | |
120 | | StatementKind::Nop => { | |
ea8adc8c XL |
121 | // safe (at least as emitted during MIR construction) |
122 | } | |
123 | ||
ba9703b0 | 124 | StatementKind::LlvmInlineAsm { .. } => self.require_unsafe( |
dfeec247 XL |
125 | "use of inline assembly", |
126 | "inline assembly is entirely unchecked and can cause undefined behavior", | |
127 | UnsafetyViolationKind::General, | |
128 | ), | |
ea8adc8c | 129 | } |
48663c56 | 130 | self.super_statement(statement, location); |
ea8adc8c XL |
131 | } |
132 | ||
dfeec247 | 133 | fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { |
9fa01778 | 134 | match rvalue { |
dfeec247 XL |
135 | Rvalue::Aggregate(box ref aggregate, _) => match aggregate { |
136 | &AggregateKind::Array(..) | &AggregateKind::Tuple => {} | |
137 | &AggregateKind::Adt(ref def, ..) => { | |
138 | match self.tcx.layout_scalar_valid_range(def.did) { | |
139 | (Bound::Unbounded, Bound::Unbounded) => {} | |
140 | _ => self.require_unsafe( | |
141 | "initializing type with `rustc_layout_scalar_valid_range` attr", | |
142 | "initializing a layout restricted type's field with a value \ | |
9fa01778 | 143 | outside the valid range is undefined behavior", |
dfeec247 XL |
144 | UnsafetyViolationKind::GeneralAndConstFn, |
145 | ), | |
0731742a XL |
146 | } |
147 | } | |
dfeec247 XL |
148 | &AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => { |
149 | let UnsafetyCheckResult { violations, unsafe_blocks } = | |
f9f354fc | 150 | self.tcx.unsafety_check_result(def_id.expect_local()); |
dfeec247 XL |
151 | self.register_violations(&violations, &unsafe_blocks); |
152 | } | |
9fa01778 XL |
153 | }, |
154 | // casting pointers to ints is unsafe in const fn because the const evaluator cannot | |
155 | // possibly know what the result of various operations like `address / 2` would be | |
156 | // pointers during const evaluation have no integral address, only an abstract one | |
157 | Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) | |
dfeec247 XL |
158 | if self.const_context && self.tcx.features().const_raw_ptr_to_usize_cast => |
159 | { | |
dc9dc135 | 160 | let operand_ty = operand.ty(self.body, self.tcx); |
9fa01778 XL |
161 | let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); |
162 | let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); | |
163 | match (cast_in, cast_out) { | |
ba9703b0 | 164 | (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { |
74b04a01 XL |
165 | self.require_unsafe( |
166 | "cast of pointer to int", | |
167 | "casting pointers to integers in constants", | |
168 | UnsafetyViolationKind::General, | |
dfeec247 XL |
169 | ); |
170 | } | |
171 | _ => {} | |
ea8adc8c XL |
172 | } |
173 | } | |
9fa01778 XL |
174 | // raw pointer and fn pointer operations are unsafe as it is not clear whether one |
175 | // pointer would be "less" or "equal" to another, because we cannot know where llvm | |
176 | // or the linker will place various statics in memory. Without this information the | |
177 | // result of a comparison of addresses would differ between runtime and compile-time. | |
178 | Rvalue::BinaryOp(_, ref lhs, _) | |
dfeec247 XL |
179 | if self.const_context && self.tcx.features().const_compare_raw_pointers => |
180 | { | |
e74abb32 | 181 | if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind { |
74b04a01 XL |
182 | self.require_unsafe( |
183 | "pointer operation", | |
184 | "operations on pointers in constants", | |
185 | UnsafetyViolationKind::General, | |
dfeec247 | 186 | ); |
9fa01778 XL |
187 | } |
188 | } | |
dfeec247 | 189 | _ => {} |
ea8adc8c XL |
190 | } |
191 | self.super_rvalue(rvalue, location); | |
192 | } | |
193 | ||
dfeec247 | 194 | fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { |
f9f354fc | 195 | // On types with `scalar_valid_range`, prevent |
74b04a01 XL |
196 | // * `&mut x.field` |
197 | // * `x.field = y;` | |
198 | // * `&x.field` if `field`'s type has interior mutability | |
199 | // because either of these would allow modifying the layout constrained field and | |
200 | // insert values that violate the layout constraints. | |
201 | if context.is_mutating_use() || context.is_borrow() { | |
ba9703b0 | 202 | self.check_mut_borrowing_layout_constrained_field(*place, context.is_mutating_use()); |
74b04a01 XL |
203 | } |
204 | ||
f9f354fc XL |
205 | if context.is_borrow() { |
206 | if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { | |
207 | self.require_unsafe( | |
208 | "borrow of packed field", | |
209 | "fields of packed structs might be misaligned: dereferencing a \ | |
210 | misaligned pointer or even just creating a misaligned reference \ | |
211 | is undefined behavior", | |
212 | UnsafetyViolationKind::BorrowPacked, | |
213 | ); | |
214 | } | |
215 | } | |
216 | ||
e1599b0c XL |
217 | for (i, elem) in place.projection.iter().enumerate() { |
218 | let proj_base = &place.projection[..i]; | |
e1599b0c | 219 | if context.is_borrow() { |
ba9703b0 | 220 | if util::is_disaligned(self.tcx, self.body, self.param_env, *place) { |
74b04a01 XL |
221 | self.require_unsafe( |
222 | "borrow of packed field", | |
223 | "fields of packed structs might be misaligned: dereferencing a \ | |
224 | misaligned pointer or even just creating a misaligned reference \ | |
225 | is undefined behavior", | |
f9f354fc | 226 | UnsafetyViolationKind::BorrowPacked, |
dfeec247 | 227 | ); |
0731742a | 228 | } |
e1599b0c | 229 | } |
f9f354fc | 230 | let source_info = self.source_info; |
74b04a01 XL |
231 | if let [] = proj_base { |
232 | let decl = &self.body.local_decls[place.local]; | |
60c5eb7d | 233 | if decl.internal { |
f9f354fc | 234 | if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { |
60c5eb7d XL |
235 | if self.tcx.is_mutable_static(def_id) { |
236 | self.require_unsafe( | |
237 | "use of mutable static", | |
238 | "mutable statics can be mutated by multiple threads: aliasing \ | |
239 | violations or data races will cause undefined behavior", | |
240 | UnsafetyViolationKind::General, | |
241 | ); | |
242 | return; | |
243 | } else if self.tcx.is_foreign_item(def_id) { | |
244 | self.require_unsafe( | |
245 | "use of extern static", | |
246 | "extern statics are not controlled by the Rust type system: \ | |
247 | invalid data, aliasing violations or data races will cause \ | |
248 | undefined behavior", | |
249 | UnsafetyViolationKind::General, | |
250 | ); | |
251 | return; | |
252 | } | |
253 | } else { | |
254 | // Internal locals are used in the `move_val_init` desugaring. | |
255 | // We want to check unsafety against the source info of the | |
256 | // desugaring, rather than the source info of the RHS. | |
74b04a01 | 257 | self.source_info = self.body.local_decls[place.local].source_info; |
60c5eb7d | 258 | } |
0731742a | 259 | } |
e1599b0c | 260 | } |
74b04a01 | 261 | let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; |
e74abb32 | 262 | match base_ty.kind { |
dfeec247 XL |
263 | ty::RawPtr(..) => self.require_unsafe( |
264 | "dereference of raw pointer", | |
265 | "raw pointers may be NULL, dangling or unaligned; they can violate \ | |
e1599b0c | 266 | aliasing rules and cause data races: all of these are undefined \ |
dfeec247 XL |
267 | behavior", |
268 | UnsafetyViolationKind::General, | |
269 | ), | |
e1599b0c XL |
270 | ty::Adt(adt, _) => { |
271 | if adt.is_union() { | |
dfeec247 XL |
272 | if context == PlaceContext::MutatingUse(MutatingUseContext::Store) |
273 | || context == PlaceContext::MutatingUse(MutatingUseContext::Drop) | |
274 | || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) | |
e1599b0c XL |
275 | { |
276 | let elem_ty = match elem { | |
277 | ProjectionElem::Field(_, ty) => ty, | |
278 | _ => span_bug!( | |
0731742a | 279 | self.source_info.span, |
e1599b0c | 280 | "non-field projection {:?} from union?", |
dfeec247 XL |
281 | place |
282 | ), | |
e1599b0c XL |
283 | }; |
284 | if !elem_ty.is_copy_modulo_regions( | |
285 | self.tcx, | |
286 | self.param_env, | |
287 | self.source_info.span, | |
288 | ) { | |
289 | self.require_unsafe( | |
290 | "assignment to non-`Copy` union field", | |
291 | "the previous content of the field will be dropped, which \ | |
292 | causes undefined behavior if the field was not properly \ | |
dfeec247 XL |
293 | initialized", |
294 | UnsafetyViolationKind::GeneralAndConstFn, | |
295 | ) | |
ea8adc8c | 296 | } else { |
e1599b0c | 297 | // write to non-move union, safe |
ea8adc8c | 298 | } |
e1599b0c | 299 | } else { |
dfeec247 XL |
300 | self.require_unsafe( |
301 | "access to union field", | |
e1599b0c XL |
302 | "the field may not be properly initialized: using \ |
303 | uninitialized data will cause undefined behavior", | |
dfeec247 XL |
304 | UnsafetyViolationKind::GeneralAndConstFn, |
305 | ) | |
ea8adc8c XL |
306 | } |
307 | } | |
ea8adc8c | 308 | } |
e1599b0c | 309 | _ => {} |
ea8adc8c | 310 | } |
f9f354fc | 311 | self.source_info = source_info; |
e1599b0c | 312 | } |
ea8adc8c XL |
313 | } |
314 | } | |
315 | ||
316 | impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> { | |
0731742a XL |
317 | fn require_unsafe( |
318 | &mut self, | |
319 | description: &'static str, | |
320 | details: &'static str, | |
321 | kind: UnsafetyViolationKind, | |
322 | ) { | |
ea8adc8c | 323 | let source_info = self.source_info; |
f9f354fc XL |
324 | let lint_root = self.body.source_scopes[self.source_info.scope] |
325 | .local_data | |
326 | .as_ref() | |
327 | .assert_crate_local() | |
328 | .lint_root; | |
dfeec247 XL |
329 | self.register_violations( |
330 | &[UnsafetyViolation { | |
331 | source_info, | |
f9f354fc | 332 | lint_root, |
dfeec247 XL |
333 | description: Symbol::intern(description), |
334 | details: Symbol::intern(details), | |
335 | kind, | |
336 | }], | |
337 | &[], | |
338 | ); | |
ea8adc8c XL |
339 | } |
340 | ||
dfeec247 XL |
341 | fn register_violations( |
342 | &mut self, | |
343 | violations: &[UnsafetyViolation], | |
344 | unsafe_blocks: &[(hir::HirId, bool)], | |
345 | ) { | |
60c5eb7d XL |
346 | let safety = self.body.source_scopes[self.source_info.scope] |
347 | .local_data | |
348 | .as_ref() | |
349 | .assert_crate_local() | |
350 | .safety; | |
0731742a XL |
351 | let within_unsafe = match safety { |
352 | // `unsafe` blocks are required in safe code | |
ea8adc8c XL |
353 | Safety::Safe => { |
354 | for violation in violations { | |
dfeec247 | 355 | let mut violation = *violation; |
0731742a | 356 | match violation.kind { |
dfeec247 XL |
357 | UnsafetyViolationKind::GeneralAndConstFn |
358 | | UnsafetyViolationKind::General => {} | |
f9f354fc | 359 | UnsafetyViolationKind::BorrowPacked => { |
dfeec247 XL |
360 | if self.min_const_fn { |
361 | // const fns don't need to be backwards compatible and can | |
362 | // emit these violations as a hard error instead of a backwards | |
363 | // compat lint | |
364 | violation.kind = UnsafetyViolationKind::General; | |
365 | } | |
366 | } | |
f9f354fc XL |
367 | UnsafetyViolationKind::UnsafeFn |
368 | | UnsafetyViolationKind::UnsafeFnBorrowPacked => { | |
369 | bug!("`UnsafetyViolationKind::UnsafeFn` in an `Safe` context") | |
370 | } | |
0731742a XL |
371 | } |
372 | if !self.violations.contains(&violation) { | |
373 | self.violations.push(violation) | |
ea8adc8c XL |
374 | } |
375 | } | |
ea8adc8c XL |
376 | false |
377 | } | |
f9f354fc XL |
378 | // With the RFC 2585, no longer allow `unsafe` operations in `unsafe fn`s |
379 | Safety::FnUnsafe if self.tcx.features().unsafe_block_in_unsafe_fn => { | |
380 | for violation in violations { | |
381 | let mut violation = *violation; | |
382 | ||
383 | if violation.kind == UnsafetyViolationKind::BorrowPacked { | |
384 | violation.kind = UnsafetyViolationKind::UnsafeFnBorrowPacked; | |
385 | } else { | |
386 | violation.kind = UnsafetyViolationKind::UnsafeFn; | |
387 | } | |
388 | if !self.violations.contains(&violation) { | |
389 | self.violations.push(violation) | |
390 | } | |
391 | } | |
392 | false | |
393 | } | |
394 | // `unsafe` function bodies allow unsafe without additional unsafe blocks (before RFC 2585) | |
ea8adc8c | 395 | Safety::BuiltinUnsafe | Safety::FnUnsafe => true, |
532ac7d7 | 396 | Safety::ExplicitUnsafe(hir_id) => { |
0731742a | 397 | // mark unsafe block as used if there are any unsafe operations inside |
ea8adc8c | 398 | if !violations.is_empty() { |
532ac7d7 | 399 | self.used_unsafe.insert(hir_id); |
ea8adc8c | 400 | } |
0731742a XL |
401 | // only some unsafety is allowed in const fn |
402 | if self.min_const_fn { | |
403 | for violation in violations { | |
404 | match violation.kind { | |
405 | // these unsafe things are stable in const fn | |
dfeec247 | 406 | UnsafetyViolationKind::GeneralAndConstFn => {} |
0731742a | 407 | // these things are forbidden in const fns |
dfeec247 | 408 | UnsafetyViolationKind::General |
f9f354fc | 409 | | UnsafetyViolationKind::BorrowPacked => { |
dfeec247 | 410 | let mut violation = *violation; |
0731742a XL |
411 | // const fns don't need to be backwards compatible and can |
412 | // emit these violations as a hard error instead of a backwards | |
413 | // compat lint | |
414 | violation.kind = UnsafetyViolationKind::General; | |
415 | if !self.violations.contains(&violation) { | |
416 | self.violations.push(violation) | |
417 | } | |
dfeec247 | 418 | } |
f9f354fc XL |
419 | UnsafetyViolationKind::UnsafeFn |
420 | | UnsafetyViolationKind::UnsafeFnBorrowPacked => bug!( | |
421 | "`UnsafetyViolationKind::UnsafeFn` in an `ExplicitUnsafe` context" | |
422 | ), | |
0731742a XL |
423 | } |
424 | } | |
425 | } | |
ea8adc8c XL |
426 | true |
427 | } | |
428 | }; | |
dfeec247 XL |
429 | self.inherited_blocks.extend( |
430 | unsafe_blocks.iter().map(|&(hir_id, is_used)| (hir_id, is_used && !within_unsafe)), | |
431 | ); | |
ea8adc8c | 432 | } |
0731742a XL |
433 | fn check_mut_borrowing_layout_constrained_field( |
434 | &mut self, | |
ba9703b0 | 435 | place: Place<'tcx>, |
0731742a XL |
436 | is_mut_use: bool, |
437 | ) { | |
e74abb32 XL |
438 | let mut cursor = place.projection.as_ref(); |
439 | while let &[ref proj_base @ .., elem] = cursor { | |
e1599b0c XL |
440 | cursor = proj_base; |
441 | ||
442 | match elem { | |
74b04a01 XL |
443 | // Modifications behind a dereference don't affect the value of |
444 | // the pointer. | |
445 | ProjectionElem::Deref => return, | |
0731742a | 446 | ProjectionElem::Field(..) => { |
416331ca | 447 | let ty = |
74b04a01 | 448 | Place::ty_from(place.local, proj_base, &self.body.local_decls, self.tcx).ty; |
ba9703b0 XL |
449 | if let ty::Adt(def, _) = ty.kind { |
450 | if self.tcx.layout_scalar_valid_range(def.did) | |
451 | != (Bound::Unbounded, Bound::Unbounded) | |
452 | { | |
453 | let (description, details) = if is_mut_use { | |
454 | ( | |
455 | "mutation of layout constrained field", | |
456 | "mutating layout constrained fields cannot statically be \ | |
0731742a | 457 | checked for valid values", |
ba9703b0 | 458 | ) |
74b04a01 | 459 | |
ba9703b0 XL |
460 | // Check `is_freeze` as late as possible to avoid cycle errors |
461 | // with opaque types. | |
462 | } else if !place.ty(self.body, self.tcx).ty.is_freeze( | |
463 | self.tcx, | |
464 | self.param_env, | |
465 | self.source_info.span, | |
466 | ) { | |
467 | ( | |
468 | "borrow of layout constrained field with interior \ | |
0731742a | 469 | mutability", |
ba9703b0 | 470 | "references to fields of layout constrained fields \ |
0731742a XL |
471 | lose the constraints. Coupled with interior mutability, \ |
472 | the field can be changed to invalid values", | |
ba9703b0 XL |
473 | ) |
474 | } else { | |
475 | continue; | |
476 | }; | |
477 | self.require_unsafe( | |
478 | description, | |
479 | details, | |
480 | UnsafetyViolationKind::GeneralAndConstFn, | |
481 | ); | |
482 | } | |
0731742a XL |
483 | } |
484 | } | |
485 | _ => {} | |
486 | } | |
0731742a XL |
487 | } |
488 | } | |
f9f354fc XL |
489 | |
490 | /// Checks whether calling `func_did` needs an `unsafe` context or not, i.e. whether | |
491 | /// the called function has target features the calling function hasn't. | |
492 | fn check_target_features(&mut self, func_did: DefId) { | |
493 | let callee_features = &self.tcx.codegen_fn_attrs(func_did).target_features; | |
494 | let self_features = &self.tcx.codegen_fn_attrs(self.body_did).target_features; | |
495 | ||
496 | // Is `callee_features` a subset of `calling_features`? | |
497 | if !callee_features.iter().all(|feature| self_features.contains(feature)) { | |
498 | self.require_unsafe( | |
499 | "call to function with `#[target_feature]`", | |
500 | "can only be called if the required target features are available", | |
501 | UnsafetyViolationKind::GeneralAndConstFn, | |
502 | ) | |
503 | } | |
504 | } | |
ea8adc8c XL |
505 | } |
506 | ||
9fa01778 | 507 | pub(crate) fn provide(providers: &mut Providers<'_>) { |
dfeec247 | 508 | *providers = Providers { unsafety_check_result, unsafe_derive_on_repr_packed, ..*providers }; |
ea8adc8c XL |
509 | } |
510 | ||
511 | struct UnusedUnsafeVisitor<'a> { | |
532ac7d7 XL |
512 | used_unsafe: &'a FxHashSet<hir::HirId>, |
513 | unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>, | |
ea8adc8c XL |
514 | } |
515 | ||
dfeec247 | 516 | impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> { |
ba9703b0 | 517 | type Map = intravisit::ErasedMap<'tcx>; |
dfeec247 | 518 | |
ba9703b0 | 519 | fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> { |
dfeec247 | 520 | intravisit::NestedVisitorMap::None |
ea8adc8c XL |
521 | } |
522 | ||
dfeec247 XL |
523 | fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) { |
524 | intravisit::walk_block(self, block); | |
ea8adc8c | 525 | |
dfeec247 | 526 | if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = block.rules { |
532ac7d7 | 527 | self.unsafe_blocks.push((block.hir_id, self.used_unsafe.contains(&block.hir_id))); |
ea8adc8c XL |
528 | } |
529 | } | |
530 | } | |
531 | ||
416331ca XL |
532 | fn check_unused_unsafe( |
533 | tcx: TyCtxt<'_>, | |
f9f354fc | 534 | def_id: LocalDefId, |
dc9dc135 | 535 | used_unsafe: &FxHashSet<hir::HirId>, |
416331ca | 536 | unsafe_blocks: &mut Vec<(hir::HirId, bool)>, |
dc9dc135 | 537 | ) { |
f9f354fc | 538 | let body_id = tcx.hir().maybe_body_owned_by(tcx.hir().as_local_hir_id(def_id)); |
ea8adc8c XL |
539 | |
540 | let body_id = match body_id { | |
541 | Some(body) => body, | |
542 | None => { | |
543 | debug!("check_unused_unsafe({:?}) - no body found", def_id); | |
dfeec247 | 544 | return; |
ea8adc8c XL |
545 | } |
546 | }; | |
0731742a | 547 | let body = tcx.hir().body(body_id); |
dfeec247 | 548 | debug!("check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})", def_id, body, used_unsafe); |
ea8adc8c | 549 | |
dfeec247 XL |
550 | let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks }; |
551 | intravisit::Visitor::visit_body(&mut visitor, body); | |
ea8adc8c XL |
552 | } |
553 | ||
f9f354fc | 554 | fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: LocalDefId) -> UnsafetyCheckResult { |
ea8adc8c XL |
555 | debug!("unsafety_violations({:?})", def_id); |
556 | ||
0731742a | 557 | // N.B., this borrow is valid because all the consumers of |
ea8adc8c | 558 | // `mir_built` force this. |
dc9dc135 | 559 | let body = &tcx.mir_built(def_id).borrow(); |
ea8adc8c | 560 | |
ea8adc8c | 561 | let param_env = tcx.param_env(def_id); |
9fa01778 | 562 | |
f9f354fc | 563 | let id = tcx.hir().as_local_hir_id(def_id); |
dc9dc135 | 564 | let (const_context, min_const_fn) = match tcx.hir().body_owner_kind(id) { |
9fa01778 | 565 | hir::BodyOwnerKind::Closure => (false, false), |
f9f354fc XL |
566 | hir::BodyOwnerKind::Fn => { |
567 | (tcx.is_const_fn_raw(def_id.to_def_id()), is_min_const_fn(tcx, def_id.to_def_id())) | |
568 | } | |
dfeec247 | 569 | hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => (true, false), |
9fa01778 | 570 | }; |
f9f354fc XL |
571 | let mut checker = |
572 | UnsafetyChecker::new(const_context, min_const_fn, body, def_id, tcx, param_env); | |
ba9703b0 | 573 | checker.visit_body(&body); |
ea8adc8c XL |
574 | |
575 | check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks); | |
576 | UnsafetyCheckResult { | |
577 | violations: checker.violations.into(), | |
dfeec247 | 578 | unsafe_blocks: checker.inherited_blocks.into(), |
ea8adc8c XL |
579 | } |
580 | } | |
581 | ||
f9f354fc XL |
582 | fn unsafe_derive_on_repr_packed(tcx: TyCtxt<'_>, def_id: LocalDefId) { |
583 | let lint_hir_id = tcx.hir().as_local_hir_id(def_id); | |
ff7c6d11 | 584 | |
74b04a01 XL |
585 | tcx.struct_span_lint_hir(SAFE_PACKED_BORROWS, lint_hir_id, tcx.def_span(def_id), |lint| { |
586 | // FIXME: when we make this a hard error, this should have its | |
587 | // own error code. | |
588 | let message = if tcx.generics_of(def_id).own_requires_monomorphization() { | |
589 | "`#[derive]` can't be used on a `#[repr(packed)]` struct with \ | |
590 | type or const parameters (error E0133)" | |
591 | .to_string() | |
592 | } else { | |
593 | "`#[derive]` can't be used on a `#[repr(packed)]` struct that \ | |
594 | does not derive Copy (error E0133)" | |
595 | .to_string() | |
596 | }; | |
597 | lint.build(&message).emit() | |
598 | }); | |
ff7c6d11 XL |
599 | } |
600 | ||
532ac7d7 | 601 | /// Returns the `HirId` for an enclosing scope that is also `unsafe`. |
dc9dc135 XL |
602 | fn is_enclosed( |
603 | tcx: TyCtxt<'_>, | |
604 | used_unsafe: &FxHashSet<hir::HirId>, | |
605 | id: hir::HirId, | |
606 | ) -> Option<(String, hir::HirId)> { | |
607 | let parent_id = tcx.hir().get_parent_node(id); | |
ea8adc8c XL |
608 | if parent_id != id { |
609 | if used_unsafe.contains(&parent_id) { | |
610 | Some(("block".to_string(), parent_id)) | |
b7449926 | 611 | } else if let Some(Node::Item(&hir::Item { |
dfeec247 XL |
612 | kind: hir::ItemKind::Fn(ref sig, _, _), .. |
613 | })) = tcx.hir().find(parent_id) | |
614 | { | |
f9f354fc XL |
615 | if sig.header.unsafety == hir::Unsafety::Unsafe |
616 | && !tcx.features().unsafe_block_in_unsafe_fn | |
617 | { | |
618 | Some(("fn".to_string(), parent_id)) | |
619 | } else { | |
620 | None | |
0531ce1d | 621 | } |
ea8adc8c XL |
622 | } else { |
623 | is_enclosed(tcx, used_unsafe, parent_id) | |
624 | } | |
625 | } else { | |
626 | None | |
627 | } | |
628 | } | |
629 | ||
dc9dc135 | 630 | fn report_unused_unsafe(tcx: TyCtxt<'_>, used_unsafe: &FxHashSet<hir::HirId>, id: hir::HirId) { |
ba9703b0 | 631 | let span = tcx.sess.source_map().guess_head_span(tcx.hir().span(id)); |
74b04a01 XL |
632 | tcx.struct_span_lint_hir(UNUSED_UNSAFE, id, span, |lint| { |
633 | let msg = "unnecessary `unsafe` block"; | |
634 | let mut db = lint.build(msg); | |
635 | db.span_label(span, msg); | |
636 | if let Some((kind, id)) = is_enclosed(tcx, used_unsafe, id) { | |
637 | db.span_label( | |
ba9703b0 | 638 | tcx.sess.source_map().guess_head_span(tcx.hir().span(id)), |
74b04a01 XL |
639 | format!("because it's nested under this `unsafe` {}", kind), |
640 | ); | |
641 | } | |
642 | db.emit(); | |
643 | }); | |
ea8adc8c XL |
644 | } |
645 | ||
416331ca | 646 | fn builtin_derive_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<DefId> { |
ff7c6d11 XL |
647 | debug!("builtin_derive_def_id({:?})", def_id); |
648 | if let Some(impl_def_id) = tcx.impl_of_method(def_id) { | |
48663c56 | 649 | if tcx.has_attr(impl_def_id, sym::automatically_derived) { |
ff7c6d11 XL |
650 | debug!("builtin_derive_def_id({:?}) - is {:?}", def_id, impl_def_id); |
651 | Some(impl_def_id) | |
652 | } else { | |
653 | debug!("builtin_derive_def_id({:?}) - not automatically derived", def_id); | |
654 | None | |
655 | } | |
656 | } else { | |
657 | debug!("builtin_derive_def_id({:?}) - not a method", def_id); | |
658 | None | |
659 | } | |
660 | } | |
661 | ||
f9f354fc | 662 | pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { |
ea8adc8c | 663 | debug!("check_unsafety({:?})", def_id); |
abe05a73 XL |
664 | |
665 | // closures are handled by their parent fn. | |
f9f354fc | 666 | if tcx.is_closure(def_id.to_def_id()) { |
abe05a73 XL |
667 | return; |
668 | } | |
ea8adc8c | 669 | |
dfeec247 | 670 | let UnsafetyCheckResult { violations, unsafe_blocks } = tcx.unsafety_check_result(def_id); |
ea8adc8c | 671 | |
f9f354fc XL |
672 | for &UnsafetyViolation { source_info, lint_root, description, details, kind } in |
673 | violations.iter() | |
674 | { | |
ea8adc8c | 675 | // Report an error. |
f9f354fc XL |
676 | let unsafe_fn_msg = |
677 | if unsafe_op_in_unsafe_fn_allowed(tcx, lint_root) { " function or" } else { "" }; | |
678 | ||
ff7c6d11 | 679 | match kind { |
dfeec247 | 680 | UnsafetyViolationKind::GeneralAndConstFn | UnsafetyViolationKind::General => { |
f9f354fc | 681 | // once |
ff7c6d11 | 682 | struct_span_err!( |
dfeec247 XL |
683 | tcx.sess, |
684 | source_info.span, | |
685 | E0133, | |
f9f354fc XL |
686 | "{} is unsafe and requires unsafe{} block", |
687 | description, | |
688 | unsafe_fn_msg, | |
dfeec247 XL |
689 | ) |
690 | .span_label(source_info.span, &*description.as_str()) | |
691 | .note(&details.as_str()) | |
692 | .emit(); | |
ff7c6d11 | 693 | } |
f9f354fc XL |
694 | UnsafetyViolationKind::BorrowPacked => { |
695 | if let Some(impl_def_id) = builtin_derive_def_id(tcx, def_id.to_def_id()) { | |
696 | // If a method is defined in the local crate, | |
697 | // the impl containing that method should also be. | |
698 | tcx.ensure().unsafe_derive_on_repr_packed(impl_def_id.expect_local()); | |
ff7c6d11 | 699 | } else { |
dfeec247 XL |
700 | tcx.struct_span_lint_hir( |
701 | SAFE_PACKED_BORROWS, | |
f9f354fc | 702 | lint_root, |
dfeec247 | 703 | source_info.span, |
74b04a01 XL |
704 | |lint| { |
705 | lint.build(&format!( | |
f9f354fc XL |
706 | "{} is unsafe and requires unsafe{} block (error E0133)", |
707 | description, unsafe_fn_msg, | |
74b04a01 XL |
708 | )) |
709 | .note(&details.as_str()) | |
710 | .emit() | |
711 | }, | |
dfeec247 | 712 | ) |
ff7c6d11 XL |
713 | } |
714 | } | |
f9f354fc XL |
715 | UnsafetyViolationKind::UnsafeFn => tcx.struct_span_lint_hir( |
716 | UNSAFE_OP_IN_UNSAFE_FN, | |
717 | lint_root, | |
718 | source_info.span, | |
719 | |lint| { | |
720 | lint.build(&format!( | |
721 | "{} is unsafe and requires unsafe block (error E0133)", | |
722 | description, | |
723 | )) | |
724 | .span_label(source_info.span, &*description.as_str()) | |
725 | .note(&details.as_str()) | |
726 | .emit(); | |
727 | }, | |
728 | ), | |
729 | UnsafetyViolationKind::UnsafeFnBorrowPacked => { | |
730 | // When `unsafe_op_in_unsafe_fn` is disallowed, the behavior of safe and unsafe functions | |
731 | // should be the same in terms of warnings and errors. Therefore, with `#[warn(safe_packed_borrows)]`, | |
732 | // a safe packed borrow should emit a warning *but not an error* in an unsafe function, | |
733 | // just like in a safe function, even if `unsafe_op_in_unsafe_fn` is `deny`. | |
734 | // | |
735 | // Also, `#[warn(unsafe_op_in_unsafe_fn)]` can't cause any new errors. Therefore, with | |
736 | // `#[deny(safe_packed_borrows)]` and `#[warn(unsafe_op_in_unsafe_fn)]`, a packed borrow | |
737 | // should only issue a warning for the sake of backwards compatibility. | |
738 | // | |
739 | // The solution those 2 expectations is to always take the minimum of both lints. | |
740 | // This prevent any new errors (unless both lints are explicitely set to `deny`). | |
741 | let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0 | |
742 | <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0 | |
743 | { | |
744 | SAFE_PACKED_BORROWS | |
745 | } else { | |
746 | UNSAFE_OP_IN_UNSAFE_FN | |
747 | }; | |
748 | tcx.struct_span_lint_hir(&lint, lint_root, source_info.span, |lint| { | |
749 | lint.build(&format!( | |
750 | "{} is unsafe and requires unsafe block (error E0133)", | |
751 | description, | |
752 | )) | |
753 | .span_label(source_info.span, &*description.as_str()) | |
754 | .note(&details.as_str()) | |
755 | .emit(); | |
756 | }) | |
757 | } | |
ea8adc8c XL |
758 | } |
759 | } | |
760 | ||
ba9703b0 XL |
761 | let (mut unsafe_used, mut unsafe_unused): (FxHashSet<_>, Vec<_>) = Default::default(); |
762 | for &(block_id, is_used) in unsafe_blocks.iter() { | |
763 | if is_used { | |
764 | unsafe_used.insert(block_id); | |
765 | } else { | |
766 | unsafe_unused.push(block_id); | |
ea8adc8c XL |
767 | } |
768 | } | |
ba9703b0 XL |
769 | // The unused unsafe blocks might not be in source order; sort them so that the unused unsafe |
770 | // error messages are properly aligned and the issue-45107 and lint-unused-unsafe tests pass. | |
771 | unsafe_unused.sort_by_cached_key(|hir_id| tcx.hir().span(*hir_id)); | |
772 | ||
773 | for &block_id in &unsafe_unused { | |
774 | report_unused_unsafe(tcx, &unsafe_used, block_id); | |
775 | } | |
ea8adc8c | 776 | } |
f9f354fc XL |
777 | |
778 | fn unsafe_op_in_unsafe_fn_allowed(tcx: TyCtxt<'_>, id: HirId) -> bool { | |
779 | tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, id).0 == Level::Allow | |
780 | } |