1 use crate::build::ExprCategory;
3 use rustc_middle::thir::visit::{self, Visitor};
6 use rustc_middle::mir::BorrowKind;
7 use rustc_middle::thir::*;
8 use rustc_middle::ty::print::with_no_trimmed_paths;
9 use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
10 use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
11 use rustc_session::lint::Level;
12 use rustc_span::def_id::{DefId, LocalDefId};
13 use rustc_span::symbol::Symbol;
18 struct UnsafetyVisitor<'a, 'tcx> {
21 /// The `HirId` of the current scope, which would be the `HirId`
22 /// of the current HIR node, modulo adjustments. Used for lint levels.
23 hir_context: hir::HirId,
24 /// The current "safety context". This notably tracks whether we are in an
25 /// `unsafe` block, and whether it has been used.
26 safety_context: SafetyContext,
27 body_unsafety: BodyUnsafety,
28 /// The `#[target_feature]` attributes of the body. Used for checking
29 /// calls to functions with `#[target_feature]` (RFC 2396).
30 body_target_features: &'tcx [Symbol],
31 /// When inside the LHS of an assignment to a field, this is the type
32 /// of the LHS and the span of the assignment expression.
33 assignment_info: Option<(Ty<'tcx>, Span)>,
34 in_union_destructure: bool,
35 param_env: ParamEnv<'tcx>,
39 impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
40 fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
42 SafetyContext::UnsafeBlock { span: enclosing_span, .. },
43 SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
44 ) = (self.safety_context, safety_context)
46 self.warn_unused_unsafe(
49 Some(UnusedUnsafeEnclosing::Block {
50 span: self.tcx.sess.source_map().guess_head_span(enclosing_span),
55 let prev_context = self.safety_context;
56 self.safety_context = safety_context;
60 if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
61 self.warn_unused_unsafe(
64 if self.unsafe_op_in_unsafe_fn_allowed() {
67 .map(|span| UnusedUnsafeEnclosing::Function { span })
73 self.safety_context = prev_context;
77 fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
78 let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
79 match self.safety_context {
80 SafetyContext::BuiltinUnsafeBlock => {}
81 SafetyContext::UnsafeBlock { ref mut used, .. } => {
82 // Mark this block as useful (even inside `unsafe fn`, where it is technically
83 // redundant -- but we want to eventually enable `unsafe_op_in_unsafe_fn` by
84 // default which will require those blocks:
85 // https://github.com/rust-lang/rust/issues/71668#issuecomment-1203075594).
88 SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
89 SafetyContext::UnsafeFn => {
90 // unsafe_op_in_unsafe_fn is disallowed
91 kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span);
93 SafetyContext::Safe => {
94 kind.emit_requires_unsafe_err(self.tcx, span, unsafe_op_in_unsafe_fn_allowed);
99 fn warn_unused_unsafe(
103 enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
105 let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
106 self.tcx.emit_spanned_lint(
110 UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
114 /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
115 fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
116 self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
119 /// Handle closures/generators/inline-consts, which is unsafecked with their parent body.
120 fn visit_inner_body(&mut self, def: ty::WithOptConstParam<LocalDefId>) {
121 if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) {
122 let inner_thir = &inner_thir.borrow();
123 let hir_context = self.tcx.hir().local_def_id_to_hir_id(def.did);
124 let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self };
125 inner_visitor.visit_expr(&inner_thir[expr]);
126 // Unsafe blocks can be used in the inner body, make sure to take it into account
127 self.safety_context = inner_visitor.safety_context;
132 // Searches for accesses to layout constrained fields.
133 struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
135 thir: &'a Thir<'tcx>,
139 impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
140 fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
141 Self { found: false, thir, tcx }
145 impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
146 fn thir(&self) -> &'a Thir<'tcx> {
150 fn visit_expr(&mut self, expr: &Expr<'tcx>) {
152 ExprKind::Field { lhs, .. } => {
153 if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
154 if (Bound::Unbounded, Bound::Unbounded)
155 != self.tcx.layout_scalar_valid_range(adt_def.did())
160 visit::walk_expr(self, expr);
163 // Keep walking through the expression as long as we stay in the same
164 // place, i.e. the expression is a place expression and not a dereference
165 // (since dereferencing something leads us to a different place).
166 ExprKind::Deref { .. } => {}
167 ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => {
168 visit::walk_expr(self, expr);
176 impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
177 fn thir(&self) -> &'a Thir<'tcx> {
181 fn visit_block(&mut self, block: &Block) {
182 match block.safety_mode {
183 // compiler-generated unsafe code should not count towards the usefulness of
184 // an outer unsafe block
185 BlockSafety::BuiltinUnsafe => {
186 self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
187 visit::walk_block(this, block)
190 BlockSafety::ExplicitUnsafe(hir_id) => {
191 self.in_safety_context(
192 SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
193 |this| visit::walk_block(this, block),
196 BlockSafety::Safe => {
197 visit::walk_block(self, block);
202 fn visit_pat(&mut self, pat: &Pat<'tcx>) {
203 if self.in_union_destructure {
205 // binding to a variable allows getting stuff out of variable
206 PatKind::Binding { .. }
207 // match is conditional on having this value
208 | PatKind::Constant { .. }
209 | PatKind::Variant { .. }
210 | PatKind::Leaf { .. }
211 | PatKind::Deref { .. }
212 | PatKind::Range { .. }
213 | PatKind::Slice { .. }
214 | PatKind::Array { .. } => {
215 self.requires_unsafe(pat.span, AccessToUnionField);
216 return; // we can return here since this already requires unsafe
218 // wildcard doesn't take anything
220 // these just wrap other patterns
222 PatKind::AscribeUserType { .. } => {}
227 PatKind::Leaf { .. } => {
228 if let ty::Adt(adt_def, ..) = pat.ty.kind() {
229 if adt_def.is_union() {
230 let old_in_union_destructure =
231 std::mem::replace(&mut self.in_union_destructure, true);
232 visit::walk_pat(self, pat);
233 self.in_union_destructure = old_in_union_destructure;
234 } else if (Bound::Unbounded, Bound::Unbounded)
235 != self.tcx.layout_scalar_valid_range(adt_def.did())
237 let old_inside_adt = std::mem::replace(&mut self.inside_adt, true);
238 visit::walk_pat(self, pat);
239 self.inside_adt = old_inside_adt;
241 visit::walk_pat(self, pat);
244 visit::walk_pat(self, pat);
247 PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => {
249 let ty::Ref(_, ty, _) = ty.kind() else {
252 "BindingMode::ByRef in pattern, but found non-reference type {}",
257 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
258 if !ty.is_freeze(self.tcx, self.param_env) {
259 self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
262 BorrowKind::Mut { .. } => {
263 self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
267 visit::walk_pat(self, pat);
269 PatKind::Deref { .. } => {
270 let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
271 visit::walk_pat(self, pat);
272 self.inside_adt = old_inside_adt;
275 visit::walk_pat(self, pat);
280 fn visit_expr(&mut self, expr: &Expr<'tcx>) {
281 // could we be in the LHS of an assignment to a field?
283 ExprKind::Field { .. }
284 | ExprKind::VarRef { .. }
285 | ExprKind::UpvarRef { .. }
286 | ExprKind::Scope { .. }
287 | ExprKind::Cast { .. } => {}
289 ExprKind::AddressOf { .. }
290 | ExprKind::Adt { .. }
291 | ExprKind::Array { .. }
292 | ExprKind::Binary { .. }
293 | ExprKind::Block { .. }
294 | ExprKind::Borrow { .. }
295 | ExprKind::Literal { .. }
296 | ExprKind::NamedConst { .. }
297 | ExprKind::NonHirLiteral { .. }
298 | ExprKind::ZstLiteral { .. }
299 | ExprKind::ConstParam { .. }
300 | ExprKind::ConstBlock { .. }
301 | ExprKind::Deref { .. }
302 | ExprKind::Index { .. }
303 | ExprKind::NeverToAny { .. }
304 | ExprKind::PlaceTypeAscription { .. }
305 | ExprKind::ValueTypeAscription { .. }
306 | ExprKind::Pointer { .. }
307 | ExprKind::Repeat { .. }
308 | ExprKind::StaticRef { .. }
309 | ExprKind::ThreadLocalRef { .. }
310 | ExprKind::Tuple { .. }
311 | ExprKind::Unary { .. }
312 | ExprKind::Call { .. }
313 | ExprKind::Assign { .. }
314 | ExprKind::AssignOp { .. }
315 | ExprKind::Break { .. }
316 | ExprKind::Closure { .. }
317 | ExprKind::Continue { .. }
318 | ExprKind::Return { .. }
319 | ExprKind::Yield { .. }
320 | ExprKind::Loop { .. }
321 | ExprKind::Let { .. }
322 | ExprKind::Match { .. }
323 | ExprKind::Box { .. }
324 | ExprKind::If { .. }
325 | ExprKind::InlineAsm { .. }
326 | ExprKind::LogicalOp { .. }
327 | ExprKind::Use { .. } => {
328 // We don't need to save the old value and restore it
329 // because all the place expressions can't have more
331 self.assignment_info = None;
335 ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
336 let prev_id = self.hir_context;
337 self.hir_context = hir_id;
338 self.visit_expr(&self.thir[value]);
339 self.hir_context = prev_id;
340 return; // don't visit the whole expression
342 ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
343 if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
344 let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
349 self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
350 } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
351 // If the called function has target features the calling function hasn't,
352 // the call requires `unsafe`. Don't check this on wasm
353 // targets, though. For more information on wasm see the
354 // is_like_wasm check in hir_analysis/src/collect.rs
355 if !self.tcx.sess.target.options.is_like_wasm
358 .codegen_fn_attrs(func_did)
361 .all(|feature| self.body_target_features.contains(feature))
363 self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
367 ExprKind::Deref { arg } => {
368 if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
369 if self.tcx.is_mutable_static(def_id) {
370 self.requires_unsafe(expr.span, UseOfMutableStatic);
371 } else if self.tcx.is_foreign_item(def_id) {
372 self.requires_unsafe(expr.span, UseOfExternStatic);
374 } else if self.thir[arg].ty.is_unsafe_ptr() {
375 self.requires_unsafe(expr.span, DerefOfRawPointer);
378 ExprKind::InlineAsm { .. } => {
379 self.requires_unsafe(expr.span, UseOfInlineAssembly);
381 ExprKind::Adt(box AdtExpr {
388 }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) {
389 (Bound::Unbounded, Bound::Unbounded) => {}
390 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
392 ExprKind::Closure(box ClosureExpr {
399 let closure_def = if let Some((did, const_param_id)) =
400 ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
402 ty::WithOptConstParam { did, const_param_did: Some(const_param_id) }
404 ty::WithOptConstParam::unknown(closure_id)
406 self.visit_inner_body(closure_def);
408 ExprKind::ConstBlock { did, substs: _ } => {
409 let def_id = did.expect_local();
410 self.visit_inner_body(ty::WithOptConstParam::unknown(def_id));
412 ExprKind::Field { lhs, .. } => {
413 let lhs = &self.thir[lhs];
414 if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
415 if let Some((assigned_ty, assignment_span)) = self.assignment_info {
416 if assigned_ty.needs_drop(self.tcx, self.param_env) {
417 // This would be unsafe, but should be outright impossible since we reject such unions.
418 self.tcx.sess.delay_span_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}"));
421 self.requires_unsafe(expr.span, AccessToUnionField);
425 ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
426 let lhs = &self.thir[lhs];
427 // First, check whether we are mutating a layout constrained field
428 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
429 visit::walk_expr(&mut visitor, lhs);
431 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
434 // Second, check for accesses to union fields
435 // don't have any special handling for AssignOp since it causes a read *and* write to lhs
436 if matches!(expr.kind, ExprKind::Assign { .. }) {
437 self.assignment_info = Some((lhs.ty, expr.span));
438 visit::walk_expr(self, lhs);
439 self.assignment_info = None;
440 visit::walk_expr(self, &self.thir()[rhs]);
441 return; // we have already visited everything by now
444 ExprKind::Borrow { borrow_kind, arg } => {
445 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
446 visit::walk_expr(&mut visitor, expr);
449 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique
450 if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
452 self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
454 BorrowKind::Mut { .. } => {
455 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
457 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {}
461 ExprKind::Let { expr: expr_id, .. } => {
462 let let_expr = &self.thir[expr_id];
463 if let ty::Adt(adt_def, _) = let_expr.ty.kind() && adt_def.is_union() {
464 self.requires_unsafe(expr.span, AccessToUnionField);
469 visit::walk_expr(self, expr);
473 #[derive(Clone, Copy)]
478 UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
481 #[derive(Clone, Copy)]
483 /// The body is not unsafe.
485 /// The body is an unsafe function. The span points to
486 /// the signature of the function.
491 /// Returns whether the body is unsafe.
492 fn is_unsafe(&self) -> bool {
493 matches!(self, BodyUnsafety::Unsafe(_))
496 /// If the body is unsafe, returns the `Span` of its signature.
497 fn unsafe_fn_sig_span(self) -> Option<Span> {
499 BodyUnsafety::Unsafe(span) => Some(span),
500 BodyUnsafety::Safe => None,
505 #[derive(Clone, Copy, PartialEq)]
507 CallToUnsafeFunction(Option<DefId>),
509 InitializingTypeWith,
514 MutationOfLayoutConstrainedField,
515 BorrowOfLayoutConstrainedField,
516 CallToFunctionWith(DefId),
522 pub fn emit_unsafe_op_in_unsafe_fn_lint(
528 // FIXME: ideally we would want to trim the def paths, but this is not
529 // feasible with the current lint emission API (see issue #106126).
531 CallToUnsafeFunction(Some(did)) => tcx.emit_spanned_lint(
532 UNSAFE_OP_IN_UNSAFE_FN,
535 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
537 function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
540 CallToUnsafeFunction(None) => tcx.emit_spanned_lint(
541 UNSAFE_OP_IN_UNSAFE_FN,
544 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span },
546 UseOfInlineAssembly => tcx.emit_spanned_lint(
547 UNSAFE_OP_IN_UNSAFE_FN,
550 UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span },
552 InitializingTypeWith => tcx.emit_spanned_lint(
553 UNSAFE_OP_IN_UNSAFE_FN,
556 UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
558 UseOfMutableStatic => tcx.emit_spanned_lint(
559 UNSAFE_OP_IN_UNSAFE_FN,
562 UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
564 UseOfExternStatic => tcx.emit_spanned_lint(
565 UNSAFE_OP_IN_UNSAFE_FN,
568 UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
570 DerefOfRawPointer => tcx.emit_spanned_lint(
571 UNSAFE_OP_IN_UNSAFE_FN,
574 UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
576 AccessToUnionField => tcx.emit_spanned_lint(
577 UNSAFE_OP_IN_UNSAFE_FN,
580 UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
582 MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
583 UNSAFE_OP_IN_UNSAFE_FN,
586 UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
588 BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
589 UNSAFE_OP_IN_UNSAFE_FN,
592 UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
594 CallToFunctionWith(did) => tcx.emit_spanned_lint(
595 UNSAFE_OP_IN_UNSAFE_FN,
598 UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
600 function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
606 pub fn emit_requires_unsafe_err(
610 unsafe_op_in_unsafe_fn_allowed: bool,
613 CallToUnsafeFunction(Some(did)) if unsafe_op_in_unsafe_fn_allowed => {
614 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
616 function: &tcx.def_path_str(*did),
619 CallToUnsafeFunction(Some(did)) => {
620 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe {
622 function: &tcx.def_path_str(*did),
625 CallToUnsafeFunction(None) if unsafe_op_in_unsafe_fn_allowed => {
627 CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span },
630 CallToUnsafeFunction(None) => {
631 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span });
633 UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
635 .emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
637 UseOfInlineAssembly => {
638 tcx.sess.emit_err(UseOfInlineAssemblyRequiresUnsafe { span });
640 InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => {
642 .emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
644 InitializingTypeWith => {
645 tcx.sess.emit_err(InitializingTypeWithRequiresUnsafe { span });
647 UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
649 .emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
651 UseOfMutableStatic => {
652 tcx.sess.emit_err(UseOfMutableStaticRequiresUnsafe { span });
654 UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
656 .emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
658 UseOfExternStatic => {
659 tcx.sess.emit_err(UseOfExternStaticRequiresUnsafe { span });
661 DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
663 .emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
665 DerefOfRawPointer => {
666 tcx.sess.emit_err(DerefOfRawPointerRequiresUnsafe { span });
668 AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
670 .emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
672 AccessToUnionField => {
673 tcx.sess.emit_err(AccessToUnionFieldRequiresUnsafe { span });
675 MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
677 MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
682 MutationOfLayoutConstrainedField => {
683 tcx.sess.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe { span });
685 BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
687 BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span },
690 BorrowOfLayoutConstrainedField => {
691 tcx.sess.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe { span });
693 CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => {
694 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
696 function: &tcx.def_path_str(*did),
699 CallToFunctionWith(did) => {
700 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe {
702 function: &tcx.def_path_str(*did),
709 pub fn check_unsafety(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) {
710 // THIR unsafeck is gated under `-Z thir-unsafeck`
711 if !tcx.sess.opts.unstable_opts.thir_unsafeck {
715 // Closures and inline consts are handled by their owner, if it has a body
716 if tcx.is_typeck_child(def.did.to_def_id()) {
720 let Ok((thir, expr)) = tcx.thir_body(def) else {
723 let thir = &thir.borrow();
724 // If `thir` is empty, a type error occurred, skip this body.
725 if thir.exprs.is_empty() {
729 let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
730 let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
731 if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
732 BodyUnsafety::Unsafe(fn_sig.span)
737 let body_target_features = &tcx.body_codegen_attrs(def.did.to_def_id()).target_features;
739 if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
740 let mut visitor = UnsafetyVisitor {
746 body_target_features,
747 assignment_info: None,
748 in_union_destructure: false,
749 param_env: tcx.param_env(def.did),
752 visitor.visit_expr(&thir[expr]);
755 pub(crate) fn thir_check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
756 if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
757 tcx.thir_check_unsafety_for_const_arg(def)
759 check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
763 pub(crate) fn thir_check_unsafety_for_const_arg(
765 (did, param_did): (LocalDefId, DefId),
767 check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })