]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/transform/check_unsafety.rs
New upstream version 1.47.0+dfsg1
[rustc.git] / src / librustc_mir / transform / check_unsafety.rs
CommitLineData
dfeec247
XL
1use rustc_data_structures::fx::FxHashSet;
2use rustc_errors::struct_span_err;
3use rustc_hir as hir;
f9f354fc
XL
4use rustc_hir::def_id::{DefId, LocalDefId};
5use rustc_hir::hir_id::HirId;
dfeec247
XL
6use rustc_hir::intravisit;
7use rustc_hir::Node;
ba9703b0
XL
8use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
9use rustc_middle::mir::*;
10use rustc_middle::ty::cast::CastTy;
11use rustc_middle::ty::query::Providers;
12use rustc_middle::ty::{self, TyCtxt};
f9f354fc
XL
13use rustc_session::lint::builtin::{SAFE_PACKED_BORROWS, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
14use rustc_session::lint::Level;
3dfed10e 15use rustc_span::symbol::sym;
ea8adc8c 16
0731742a
XL
17use std::ops::Bound;
18
f9f354fc 19use crate::const_eval::is_min_const_fn;
9fa01778 20use crate::util;
ea8adc8c 21
dc9dc135
XL
22pub 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 36impl<'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
64impl<'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
275impl<'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 440pub(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
460struct 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 465impl<'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
481fn 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
503fn 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 535fn 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
555fn 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 583fn 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 599fn 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 615pub 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
731fn 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}