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::{self, ParamEnv, Ty, TyCtxt};
9 use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
10 use rustc_session::lint::Level;
11 use rustc_span::def_id::{DefId, LocalDefId};
12 use rustc_span::symbol::Symbol;
17 struct UnsafetyVisitor<'a, 'tcx> {
20 /// The `HirId` of the current scope, which would be the `HirId`
21 /// of the current HIR node, modulo adjustments. Used for lint levels.
22 hir_context: hir::HirId,
23 /// The current "safety context". This notably tracks whether we are in an
24 /// `unsafe` block, and whether it has been used.
25 safety_context: SafetyContext,
26 body_unsafety: BodyUnsafety,
27 /// The `#[target_feature]` attributes of the body. Used for checking
28 /// calls to functions with `#[target_feature]` (RFC 2396).
29 body_target_features: &'tcx [Symbol],
30 /// When inside the LHS of an assignment to a field, this is the type
31 /// of the LHS and the span of the assignment expression.
32 assignment_info: Option<(Ty<'tcx>, Span)>,
33 in_union_destructure: bool,
34 param_env: ParamEnv<'tcx>,
38 impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
39 fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
41 SafetyContext::UnsafeBlock { span: enclosing_span, .. },
42 SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
43 ) = (self.safety_context, safety_context)
45 self.warn_unused_unsafe(
48 Some(UnusedUnsafeEnclosing::Block {
49 span: self.tcx.sess.source_map().guess_head_span(enclosing_span),
54 let prev_context = self.safety_context;
55 self.safety_context = safety_context;
59 if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
60 self.warn_unused_unsafe(
63 if self.unsafe_op_in_unsafe_fn_allowed() {
66 .map(|span| UnusedUnsafeEnclosing::Function { span })
72 self.safety_context = prev_context;
76 fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
77 let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
78 match self.safety_context {
79 SafetyContext::BuiltinUnsafeBlock => {}
80 SafetyContext::UnsafeBlock { ref mut used, .. } => {
81 // Mark this block as useful (even inside `unsafe fn`, where it is technically
82 // redundant -- but we want to eventually enable `unsafe_op_in_unsafe_fn` by
83 // default which will require those blocks:
84 // https://github.com/rust-lang/rust/issues/71668#issuecomment-1203075594).
87 SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
88 SafetyContext::UnsafeFn => {
89 // unsafe_op_in_unsafe_fn is disallowed
90 kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span);
92 SafetyContext::Safe => {
93 kind.emit_requires_unsafe_err(self.tcx, span, unsafe_op_in_unsafe_fn_allowed);
98 fn warn_unused_unsafe(
102 enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
104 let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
105 self.tcx.emit_spanned_lint(
109 UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
113 /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
114 fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
115 self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
118 /// Handle closures/generators/inline-consts, which is unsafecked with their parent body.
119 fn visit_inner_body(&mut self, def: ty::WithOptConstParam<LocalDefId>) {
120 if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) {
121 let inner_thir = &inner_thir.borrow();
122 let hir_context = self.tcx.hir().local_def_id_to_hir_id(def.did);
123 let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self };
124 inner_visitor.visit_expr(&inner_thir[expr]);
125 // Unsafe blocks can be used in the inner body, make sure to take it into account
126 self.safety_context = inner_visitor.safety_context;
131 // Searches for accesses to layout constrained fields.
132 struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
134 thir: &'a Thir<'tcx>,
138 impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
139 fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
140 Self { found: false, thir, tcx }
144 impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
145 fn thir(&self) -> &'a Thir<'tcx> {
149 fn visit_expr(&mut self, expr: &Expr<'tcx>) {
151 ExprKind::Field { lhs, .. } => {
152 if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
153 if (Bound::Unbounded, Bound::Unbounded)
154 != self.tcx.layout_scalar_valid_range(adt_def.did())
159 visit::walk_expr(self, expr);
162 // Keep walking through the expression as long as we stay in the same
163 // place, i.e. the expression is a place expression and not a dereference
164 // (since dereferencing something leads us to a different place).
165 ExprKind::Deref { .. } => {}
166 ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => {
167 visit::walk_expr(self, expr);
175 impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
176 fn thir(&self) -> &'a Thir<'tcx> {
180 fn visit_block(&mut self, block: &Block) {
181 match block.safety_mode {
182 // compiler-generated unsafe code should not count towards the usefulness of
183 // an outer unsafe block
184 BlockSafety::BuiltinUnsafe => {
185 self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
186 visit::walk_block(this, block)
189 BlockSafety::ExplicitUnsafe(hir_id) => {
190 self.in_safety_context(
191 SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
192 |this| visit::walk_block(this, block),
195 BlockSafety::Safe => {
196 visit::walk_block(self, block);
201 fn visit_pat(&mut self, pat: &Pat<'tcx>) {
202 if self.in_union_destructure {
204 // binding to a variable allows getting stuff out of variable
205 PatKind::Binding { .. }
206 // match is conditional on having this value
207 | PatKind::Constant { .. }
208 | PatKind::Variant { .. }
209 | PatKind::Leaf { .. }
210 | PatKind::Deref { .. }
211 | PatKind::Range { .. }
212 | PatKind::Slice { .. }
213 | PatKind::Array { .. } => {
214 self.requires_unsafe(pat.span, AccessToUnionField);
215 return; // we can return here since this already requires unsafe
217 // wildcard doesn't take anything
219 // these just wrap other patterns
221 PatKind::AscribeUserType { .. } => {}
226 PatKind::Leaf { .. } => {
227 if let ty::Adt(adt_def, ..) = pat.ty.kind() {
228 if adt_def.is_union() {
229 let old_in_union_destructure =
230 std::mem::replace(&mut self.in_union_destructure, true);
231 visit::walk_pat(self, pat);
232 self.in_union_destructure = old_in_union_destructure;
233 } else if (Bound::Unbounded, Bound::Unbounded)
234 != self.tcx.layout_scalar_valid_range(adt_def.did())
236 let old_inside_adt = std::mem::replace(&mut self.inside_adt, true);
237 visit::walk_pat(self, pat);
238 self.inside_adt = old_inside_adt;
240 visit::walk_pat(self, pat);
243 visit::walk_pat(self, pat);
246 PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => {
248 let ty::Ref(_, ty, _) = ty.kind() else {
251 "BindingMode::ByRef in pattern, but found non-reference type {}",
256 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
257 if !ty.is_freeze(self.tcx, self.param_env) {
258 self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
261 BorrowKind::Mut { .. } => {
262 self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
266 visit::walk_pat(self, pat);
268 PatKind::Deref { .. } => {
269 let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
270 visit::walk_pat(self, pat);
271 self.inside_adt = old_inside_adt;
274 visit::walk_pat(self, pat);
279 fn visit_expr(&mut self, expr: &Expr<'tcx>) {
280 // could we be in the LHS of an assignment to a field?
282 ExprKind::Field { .. }
283 | ExprKind::VarRef { .. }
284 | ExprKind::UpvarRef { .. }
285 | ExprKind::Scope { .. }
286 | ExprKind::Cast { .. } => {}
288 ExprKind::AddressOf { .. }
289 | ExprKind::Adt { .. }
290 | ExprKind::Array { .. }
291 | ExprKind::Binary { .. }
292 | ExprKind::Block { .. }
293 | ExprKind::Borrow { .. }
294 | ExprKind::Literal { .. }
295 | ExprKind::NamedConst { .. }
296 | ExprKind::NonHirLiteral { .. }
297 | ExprKind::ZstLiteral { .. }
298 | ExprKind::ConstParam { .. }
299 | ExprKind::ConstBlock { .. }
300 | ExprKind::Deref { .. }
301 | ExprKind::Index { .. }
302 | ExprKind::NeverToAny { .. }
303 | ExprKind::PlaceTypeAscription { .. }
304 | ExprKind::ValueTypeAscription { .. }
305 | ExprKind::Pointer { .. }
306 | ExprKind::Repeat { .. }
307 | ExprKind::StaticRef { .. }
308 | ExprKind::ThreadLocalRef { .. }
309 | ExprKind::Tuple { .. }
310 | ExprKind::Unary { .. }
311 | ExprKind::Call { .. }
312 | ExprKind::Assign { .. }
313 | ExprKind::AssignOp { .. }
314 | ExprKind::Break { .. }
315 | ExprKind::Closure { .. }
316 | ExprKind::Continue { .. }
317 | ExprKind::Return { .. }
318 | ExprKind::Yield { .. }
319 | ExprKind::Loop { .. }
320 | ExprKind::Let { .. }
321 | ExprKind::Match { .. }
322 | ExprKind::Box { .. }
323 | ExprKind::If { .. }
324 | ExprKind::InlineAsm { .. }
325 | ExprKind::LogicalOp { .. }
326 | ExprKind::Use { .. } => {
327 // We don't need to save the old value and restore it
328 // because all the place expressions can't have more
330 self.assignment_info = None;
334 ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
335 let prev_id = self.hir_context;
336 self.hir_context = hir_id;
337 self.visit_expr(&self.thir[value]);
338 self.hir_context = prev_id;
339 return; // don't visit the whole expression
341 ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
342 if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
343 let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
348 self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
349 } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
350 // If the called function has target features the calling function hasn't,
351 // the call requires `unsafe`. Don't check this on wasm
352 // targets, though. For more information on wasm see the
353 // is_like_wasm check in hir_analysis/src/collect.rs
354 if !self.tcx.sess.target.options.is_like_wasm
357 .codegen_fn_attrs(func_did)
360 .all(|feature| self.body_target_features.contains(feature))
362 self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
366 ExprKind::Deref { arg } => {
367 if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
368 if self.tcx.is_mutable_static(def_id) {
369 self.requires_unsafe(expr.span, UseOfMutableStatic);
370 } else if self.tcx.is_foreign_item(def_id) {
371 self.requires_unsafe(expr.span, UseOfExternStatic);
373 } else if self.thir[arg].ty.is_unsafe_ptr() {
374 self.requires_unsafe(expr.span, DerefOfRawPointer);
377 ExprKind::InlineAsm { .. } => {
378 self.requires_unsafe(expr.span, UseOfInlineAssembly);
380 ExprKind::Adt(box AdtExpr {
387 }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) {
388 (Bound::Unbounded, Bound::Unbounded) => {}
389 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
391 ExprKind::Closure(box ClosureExpr {
398 let closure_def = if let Some((did, const_param_id)) =
399 ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
401 ty::WithOptConstParam { did, const_param_did: Some(const_param_id) }
403 ty::WithOptConstParam::unknown(closure_id)
405 self.visit_inner_body(closure_def);
407 ExprKind::ConstBlock { did, substs: _ } => {
408 let def_id = did.expect_local();
409 self.visit_inner_body(ty::WithOptConstParam::unknown(def_id));
411 ExprKind::Field { lhs, .. } => {
412 let lhs = &self.thir[lhs];
413 if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
414 if let Some((assigned_ty, assignment_span)) = self.assignment_info {
415 if assigned_ty.needs_drop(self.tcx, self.param_env) {
416 // This would be unsafe, but should be outright impossible since we reject such unions.
417 self.tcx.sess.delay_span_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}"));
420 self.requires_unsafe(expr.span, AccessToUnionField);
424 ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
425 let lhs = &self.thir[lhs];
426 // First, check whether we are mutating a layout constrained field
427 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
428 visit::walk_expr(&mut visitor, lhs);
430 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
433 // Second, check for accesses to union fields
434 // don't have any special handling for AssignOp since it causes a read *and* write to lhs
435 if matches!(expr.kind, ExprKind::Assign { .. }) {
436 self.assignment_info = Some((lhs.ty, expr.span));
437 visit::walk_expr(self, lhs);
438 self.assignment_info = None;
439 visit::walk_expr(self, &self.thir()[rhs]);
440 return; // we have already visited everything by now
443 ExprKind::Borrow { borrow_kind, arg } => {
444 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
445 visit::walk_expr(&mut visitor, expr);
448 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique
449 if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
451 self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
453 BorrowKind::Mut { .. } => {
454 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
456 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {}
460 ExprKind::Let { expr: expr_id, .. } => {
461 let let_expr = &self.thir[expr_id];
462 if let ty::Adt(adt_def, _) = let_expr.ty.kind() && adt_def.is_union() {
463 self.requires_unsafe(expr.span, AccessToUnionField);
468 visit::walk_expr(self, expr);
472 #[derive(Clone, Copy)]
477 UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
480 #[derive(Clone, Copy)]
482 /// The body is not unsafe.
484 /// The body is an unsafe function. The span points to
485 /// the signature of the function.
490 /// Returns whether the body is unsafe.
491 fn is_unsafe(&self) -> bool {
492 matches!(self, BodyUnsafety::Unsafe(_))
495 /// If the body is unsafe, returns the `Span` of its signature.
496 fn unsafe_fn_sig_span(self) -> Option<Span> {
498 BodyUnsafety::Unsafe(span) => Some(span),
499 BodyUnsafety::Safe => None,
504 #[derive(Clone, Copy, PartialEq)]
506 CallToUnsafeFunction(Option<DefId>),
508 InitializingTypeWith,
513 MutationOfLayoutConstrainedField,
514 BorrowOfLayoutConstrainedField,
515 CallToFunctionWith(DefId),
521 pub fn emit_unsafe_op_in_unsafe_fn_lint(
528 CallToUnsafeFunction(did) if did.is_some() => tcx.emit_spanned_lint(
529 UNSAFE_OP_IN_UNSAFE_FN,
532 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
534 function: &tcx.def_path_str(did.unwrap()),
537 CallToUnsafeFunction(..) => tcx.emit_spanned_lint(
538 UNSAFE_OP_IN_UNSAFE_FN,
541 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span },
543 UseOfInlineAssembly => tcx.emit_spanned_lint(
544 UNSAFE_OP_IN_UNSAFE_FN,
547 UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span },
549 InitializingTypeWith => tcx.emit_spanned_lint(
550 UNSAFE_OP_IN_UNSAFE_FN,
553 UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
555 UseOfMutableStatic => tcx.emit_spanned_lint(
556 UNSAFE_OP_IN_UNSAFE_FN,
559 UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
561 UseOfExternStatic => tcx.emit_spanned_lint(
562 UNSAFE_OP_IN_UNSAFE_FN,
565 UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
567 DerefOfRawPointer => tcx.emit_spanned_lint(
568 UNSAFE_OP_IN_UNSAFE_FN,
571 UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
573 AccessToUnionField => tcx.emit_spanned_lint(
574 UNSAFE_OP_IN_UNSAFE_FN,
577 UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
579 MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
580 UNSAFE_OP_IN_UNSAFE_FN,
583 UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
585 BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
586 UNSAFE_OP_IN_UNSAFE_FN,
589 UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
591 CallToFunctionWith(did) => tcx.emit_spanned_lint(
592 UNSAFE_OP_IN_UNSAFE_FN,
595 UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
597 function: &tcx.def_path_str(*did),
603 pub fn emit_requires_unsafe_err(
607 unsafe_op_in_unsafe_fn_allowed: bool,
610 CallToUnsafeFunction(did) if did.is_some() && unsafe_op_in_unsafe_fn_allowed => {
611 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
613 function: &tcx.def_path_str(did.unwrap()),
616 CallToUnsafeFunction(did) if did.is_some() => {
617 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe {
619 function: &tcx.def_path_str(did.unwrap()),
622 CallToUnsafeFunction(..) if unsafe_op_in_unsafe_fn_allowed => {
624 CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span },
627 CallToUnsafeFunction(..) => {
628 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span });
630 UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
632 .emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
634 UseOfInlineAssembly => {
635 tcx.sess.emit_err(UseOfInlineAssemblyRequiresUnsafe { span });
637 InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => {
639 .emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
641 InitializingTypeWith => {
642 tcx.sess.emit_err(InitializingTypeWithRequiresUnsafe { span });
644 UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
646 .emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
648 UseOfMutableStatic => {
649 tcx.sess.emit_err(UseOfMutableStaticRequiresUnsafe { span });
651 UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
653 .emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
655 UseOfExternStatic => {
656 tcx.sess.emit_err(UseOfExternStaticRequiresUnsafe { span });
658 DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
660 .emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
662 DerefOfRawPointer => {
663 tcx.sess.emit_err(DerefOfRawPointerRequiresUnsafe { span });
665 AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
667 .emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
669 AccessToUnionField => {
670 tcx.sess.emit_err(AccessToUnionFieldRequiresUnsafe { span });
672 MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
674 MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
679 MutationOfLayoutConstrainedField => {
680 tcx.sess.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe { span });
682 BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
684 BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span },
687 BorrowOfLayoutConstrainedField => {
688 tcx.sess.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe { span });
690 CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => {
691 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
693 function: &tcx.def_path_str(*did),
696 CallToFunctionWith(did) => {
697 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe {
699 function: &tcx.def_path_str(*did),
706 pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
707 // THIR unsafeck is gated under `-Z thir-unsafeck`
708 if !tcx.sess.opts.unstable_opts.thir_unsafeck {
712 // Closures and inline consts are handled by their owner, if it has a body
713 if tcx.is_typeck_child(def.did.to_def_id()) {
717 let Ok((thir, expr)) = tcx.thir_body(def) else {
720 let thir = &thir.borrow();
721 // If `thir` is empty, a type error occurred, skip this body.
722 if thir.exprs.is_empty() {
726 let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
727 let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
728 if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
729 BodyUnsafety::Unsafe(fn_sig.span)
734 let body_target_features = &tcx.body_codegen_attrs(def.did.to_def_id()).target_features;
736 if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
737 let mut visitor = UnsafetyVisitor {
743 body_target_features,
744 assignment_info: None,
745 in_union_destructure: false,
746 param_env: tcx.param_env(def.did),
749 visitor.visit_expr(&thir[expr]);
752 pub(crate) fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
753 if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
754 tcx.thir_check_unsafety_for_const_arg(def)
756 check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
760 pub(crate) fn thir_check_unsafety_for_const_arg<'tcx>(
762 (did, param_did): (LocalDefId, DefId),
764 check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })