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