]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/check_unsafety.rs
Preparing for merge from rustc
[rust.git] / compiler / rustc_mir_build / src / check_unsafety.rs
1 use crate::build::ExprCategory;
2 use rustc_middle::thir::visit::{self, Visitor};
3
4 use rustc_errors::struct_span_err;
5 use rustc_hir as hir;
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;
13 use rustc_span::Span;
14
15 use std::borrow::Cow;
16 use std::ops::Bound;
17
18 struct UnsafetyVisitor<'a, 'tcx> {
19     tcx: TyCtxt<'tcx>,
20     thir: &'a Thir<'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>,
36     inside_adt: bool,
37 }
38
39 impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
40     fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
41         if let (
42             SafetyContext::UnsafeBlock { span: enclosing_span, .. },
43             SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
44         ) = (self.safety_context, safety_context)
45         {
46             self.warn_unused_unsafe(
47                 hir_id,
48                 block_span,
49                 Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
50             );
51             f(self);
52         } else {
53             let prev_context = self.safety_context;
54             self.safety_context = safety_context;
55
56             f(self);
57
58             if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
59                 self.warn_unused_unsafe(
60                     hir_id,
61                     span,
62                     if self.unsafe_op_in_unsafe_fn_allowed() {
63                         self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
64                     } else {
65                         None
66                     },
67                 );
68             }
69             self.safety_context = prev_context;
70         }
71     }
72
73     fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
74         let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
75         match self.safety_context {
76             SafetyContext::BuiltinUnsafeBlock => {}
77             SafetyContext::UnsafeBlock { ref mut used, .. } => {
78                 // Mark this block as useful (even inside `unsafe fn`, where it is technically
79                 // redundant -- but we want to eventually enable `unsafe_op_in_unsafe_fn` by
80                 // default which will require those blocks:
81                 // https://github.com/rust-lang/rust/issues/71668#issuecomment-1203075594).
82                 *used = true;
83             }
84             SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
85             SafetyContext::UnsafeFn => {
86                 let (description, note) = kind.description_and_note(self.tcx);
87                 // unsafe_op_in_unsafe_fn is disallowed
88                 self.tcx.struct_span_lint_hir(
89                     UNSAFE_OP_IN_UNSAFE_FN,
90                     self.hir_context,
91                     span,
92                     format!("{} is unsafe and requires unsafe block (error E0133)", description,),
93                     |lint| lint.span_label(span, kind.simple_description()).note(note),
94                 )
95             }
96             SafetyContext::Safe => {
97                 let (description, note) = kind.description_and_note(self.tcx);
98                 let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
99                 struct_span_err!(
100                     self.tcx.sess,
101                     span,
102                     E0133,
103                     "{} is unsafe and requires unsafe{} block",
104                     description,
105                     fn_sugg,
106                 )
107                 .span_label(span, kind.simple_description())
108                 .note(note)
109                 .emit();
110             }
111         }
112     }
113
114     fn warn_unused_unsafe(
115         &self,
116         hir_id: hir::HirId,
117         block_span: Span,
118         enclosing_unsafe: Option<(Span, &'static str)>,
119     ) {
120         let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
121         let msg = "unnecessary `unsafe` block";
122         self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, msg, |lint| {
123             lint.span_label(block_span, msg);
124             if let Some((span, kind)) = enclosing_unsafe {
125                 lint.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
126             }
127             lint
128         });
129     }
130
131     /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
132     fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
133         self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow
134     }
135
136     /// Handle closures/generators/inline-consts, which is unsafecked with their parent body.
137     fn visit_inner_body(&mut self, def: ty::WithOptConstParam<LocalDefId>) {
138         if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) {
139             let inner_thir = &inner_thir.borrow();
140             let hir_context = self.tcx.hir().local_def_id_to_hir_id(def.did);
141             let mut inner_visitor = UnsafetyVisitor { thir: inner_thir, hir_context, ..*self };
142             inner_visitor.visit_expr(&inner_thir[expr]);
143             // Unsafe blocks can be used in the inner body, make sure to take it into account
144             self.safety_context = inner_visitor.safety_context;
145         }
146     }
147 }
148
149 // Searches for accesses to layout constrained fields.
150 struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
151     found: bool,
152     thir: &'a Thir<'tcx>,
153     tcx: TyCtxt<'tcx>,
154 }
155
156 impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
157     fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
158         Self { found: false, thir, tcx }
159     }
160 }
161
162 impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
163     fn thir(&self) -> &'a Thir<'tcx> {
164         self.thir
165     }
166
167     fn visit_expr(&mut self, expr: &Expr<'tcx>) {
168         match expr.kind {
169             ExprKind::Field { lhs, .. } => {
170                 if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
171                     if (Bound::Unbounded, Bound::Unbounded)
172                         != self.tcx.layout_scalar_valid_range(adt_def.did())
173                     {
174                         self.found = true;
175                     }
176                 }
177                 visit::walk_expr(self, expr);
178             }
179
180             // Keep walking through the expression as long as we stay in the same
181             // place, i.e. the expression is a place expression and not a dereference
182             // (since dereferencing something leads us to a different place).
183             ExprKind::Deref { .. } => {}
184             ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => {
185                 visit::walk_expr(self, expr);
186             }
187
188             _ => {}
189         }
190     }
191 }
192
193 impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
194     fn thir(&self) -> &'a Thir<'tcx> {
195         &self.thir
196     }
197
198     fn visit_block(&mut self, block: &Block) {
199         match block.safety_mode {
200             // compiler-generated unsafe code should not count towards the usefulness of
201             // an outer unsafe block
202             BlockSafety::BuiltinUnsafe => {
203                 self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
204                     visit::walk_block(this, block)
205                 });
206             }
207             BlockSafety::ExplicitUnsafe(hir_id) => {
208                 self.in_safety_context(
209                     SafetyContext::UnsafeBlock { span: block.span, hir_id, used: false },
210                     |this| visit::walk_block(this, block),
211                 );
212             }
213             BlockSafety::Safe => {
214                 visit::walk_block(self, block);
215             }
216         }
217     }
218
219     fn visit_pat(&mut self, pat: &Pat<'tcx>) {
220         if self.in_union_destructure {
221             match pat.kind {
222                 // binding to a variable allows getting stuff out of variable
223                 PatKind::Binding { .. }
224                 // match is conditional on having this value
225                 | PatKind::Constant { .. }
226                 | PatKind::Variant { .. }
227                 | PatKind::Leaf { .. }
228                 | PatKind::Deref { .. }
229                 | PatKind::Range { .. }
230                 | PatKind::Slice { .. }
231                 | PatKind::Array { .. } => {
232                     self.requires_unsafe(pat.span, AccessToUnionField);
233                     return; // we can return here since this already requires unsafe
234                 }
235                 // wildcard doesn't take anything
236                 PatKind::Wild |
237                 // these just wrap other patterns
238                 PatKind::Or { .. } |
239                 PatKind::AscribeUserType { .. } => {}
240             }
241         };
242
243         match &pat.kind {
244             PatKind::Leaf { .. } => {
245                 if let ty::Adt(adt_def, ..) = pat.ty.kind() {
246                     if adt_def.is_union() {
247                         let old_in_union_destructure =
248                             std::mem::replace(&mut self.in_union_destructure, true);
249                         visit::walk_pat(self, pat);
250                         self.in_union_destructure = old_in_union_destructure;
251                     } else if (Bound::Unbounded, Bound::Unbounded)
252                         != self.tcx.layout_scalar_valid_range(adt_def.did())
253                     {
254                         let old_inside_adt = std::mem::replace(&mut self.inside_adt, true);
255                         visit::walk_pat(self, pat);
256                         self.inside_adt = old_inside_adt;
257                     } else {
258                         visit::walk_pat(self, pat);
259                     }
260                 } else {
261                     visit::walk_pat(self, pat);
262                 }
263             }
264             PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => {
265                 if self.inside_adt {
266                     let ty::Ref(_, ty, _) = ty.kind() else {
267                         span_bug!(
268                             pat.span,
269                             "BindingMode::ByRef in pattern, but found non-reference type {}",
270                             ty
271                         );
272                     };
273                     match borrow_kind {
274                         BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
275                             if !ty.is_freeze(self.tcx, self.param_env) {
276                                 self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
277                             }
278                         }
279                         BorrowKind::Mut { .. } => {
280                             self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
281                         }
282                     }
283                 }
284                 visit::walk_pat(self, pat);
285             }
286             PatKind::Deref { .. } => {
287                 let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
288                 visit::walk_pat(self, pat);
289                 self.inside_adt = old_inside_adt;
290             }
291             _ => {
292                 visit::walk_pat(self, pat);
293             }
294         }
295     }
296
297     fn visit_expr(&mut self, expr: &Expr<'tcx>) {
298         // could we be in the LHS of an assignment to a field?
299         match expr.kind {
300             ExprKind::Field { .. }
301             | ExprKind::VarRef { .. }
302             | ExprKind::UpvarRef { .. }
303             | ExprKind::Scope { .. }
304             | ExprKind::Cast { .. } => {}
305
306             ExprKind::AddressOf { .. }
307             | ExprKind::Adt { .. }
308             | ExprKind::Array { .. }
309             | ExprKind::Binary { .. }
310             | ExprKind::Block { .. }
311             | ExprKind::Borrow { .. }
312             | ExprKind::Literal { .. }
313             | ExprKind::NamedConst { .. }
314             | ExprKind::NonHirLiteral { .. }
315             | ExprKind::ZstLiteral { .. }
316             | ExprKind::ConstParam { .. }
317             | ExprKind::ConstBlock { .. }
318             | ExprKind::Deref { .. }
319             | ExprKind::Index { .. }
320             | ExprKind::NeverToAny { .. }
321             | ExprKind::PlaceTypeAscription { .. }
322             | ExprKind::ValueTypeAscription { .. }
323             | ExprKind::Pointer { .. }
324             | ExprKind::Repeat { .. }
325             | ExprKind::StaticRef { .. }
326             | ExprKind::ThreadLocalRef { .. }
327             | ExprKind::Tuple { .. }
328             | ExprKind::Unary { .. }
329             | ExprKind::Call { .. }
330             | ExprKind::Assign { .. }
331             | ExprKind::AssignOp { .. }
332             | ExprKind::Break { .. }
333             | ExprKind::Closure { .. }
334             | ExprKind::Continue { .. }
335             | ExprKind::Return { .. }
336             | ExprKind::Yield { .. }
337             | ExprKind::Loop { .. }
338             | ExprKind::Let { .. }
339             | ExprKind::Match { .. }
340             | ExprKind::Box { .. }
341             | ExprKind::If { .. }
342             | ExprKind::InlineAsm { .. }
343             | ExprKind::LogicalOp { .. }
344             | ExprKind::Use { .. } => {
345                 // We don't need to save the old value and restore it
346                 // because all the place expressions can't have more
347                 // than one child.
348                 self.assignment_info = None;
349             }
350         };
351         match expr.kind {
352             ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
353                 let prev_id = self.hir_context;
354                 self.hir_context = hir_id;
355                 self.visit_expr(&self.thir[value]);
356                 self.hir_context = prev_id;
357                 return; // don't visit the whole expression
358             }
359             ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
360                 if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
361                     let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
362                         Some(*func_id)
363                     } else {
364                         None
365                     };
366                     self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
367                 } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
368                     // If the called function has target features the calling function hasn't,
369                     // the call requires `unsafe`. Don't check this on wasm
370                     // targets, though. For more information on wasm see the
371                     // is_like_wasm check in hir_analysis/src/collect.rs
372                     if !self.tcx.sess.target.options.is_like_wasm
373                         && !self
374                             .tcx
375                             .codegen_fn_attrs(func_did)
376                             .target_features
377                             .iter()
378                             .all(|feature| self.body_target_features.contains(feature))
379                     {
380                         self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
381                     }
382                 }
383             }
384             ExprKind::Deref { arg } => {
385                 if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
386                     if self.tcx.is_mutable_static(def_id) {
387                         self.requires_unsafe(expr.span, UseOfMutableStatic);
388                     } else if self.tcx.is_foreign_item(def_id) {
389                         self.requires_unsafe(expr.span, UseOfExternStatic);
390                     }
391                 } else if self.thir[arg].ty.is_unsafe_ptr() {
392                     self.requires_unsafe(expr.span, DerefOfRawPointer);
393                 }
394             }
395             ExprKind::InlineAsm { .. } => {
396                 self.requires_unsafe(expr.span, UseOfInlineAssembly);
397             }
398             ExprKind::Adt(box AdtExpr {
399                 adt_def,
400                 variant_index: _,
401                 substs: _,
402                 user_ty: _,
403                 fields: _,
404                 base: _,
405             }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) {
406                 (Bound::Unbounded, Bound::Unbounded) => {}
407                 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
408             },
409             ExprKind::Closure(box ClosureExpr {
410                 closure_id,
411                 substs: _,
412                 upvars: _,
413                 movability: _,
414                 fake_reads: _,
415             }) => {
416                 let closure_def = if let Some((did, const_param_id)) =
417                     ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
418                 {
419                     ty::WithOptConstParam { did, const_param_did: Some(const_param_id) }
420                 } else {
421                     ty::WithOptConstParam::unknown(closure_id)
422                 };
423                 self.visit_inner_body(closure_def);
424             }
425             ExprKind::ConstBlock { did, substs: _ } => {
426                 let def_id = did.expect_local();
427                 self.visit_inner_body(ty::WithOptConstParam::unknown(def_id));
428             }
429             ExprKind::Field { lhs, .. } => {
430                 let lhs = &self.thir[lhs];
431                 if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
432                     if let Some((assigned_ty, assignment_span)) = self.assignment_info {
433                         if assigned_ty.needs_drop(self.tcx, self.param_env) {
434                             // This would be unsafe, but should be outright impossible since we reject such unions.
435                             self.tcx.sess.delay_span_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}"));
436                         }
437                     } else {
438                         self.requires_unsafe(expr.span, AccessToUnionField);
439                     }
440                 }
441             }
442             ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
443                 let lhs = &self.thir[lhs];
444                 // First, check whether we are mutating a layout constrained field
445                 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
446                 visit::walk_expr(&mut visitor, lhs);
447                 if visitor.found {
448                     self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
449                 }
450
451                 // Second, check for accesses to union fields
452                 // don't have any special handling for AssignOp since it causes a read *and* write to lhs
453                 if matches!(expr.kind, ExprKind::Assign { .. }) {
454                     self.assignment_info = Some((lhs.ty, expr.span));
455                     visit::walk_expr(self, lhs);
456                     self.assignment_info = None;
457                     visit::walk_expr(self, &self.thir()[rhs]);
458                     return; // we have already visited everything by now
459                 }
460             }
461             ExprKind::Borrow { borrow_kind, arg } => {
462                 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
463                 visit::walk_expr(&mut visitor, expr);
464                 if visitor.found {
465                     match borrow_kind {
466                         BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique
467                             if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
468                         {
469                             self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
470                         }
471                         BorrowKind::Mut { .. } => {
472                             self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
473                         }
474                         BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {}
475                     }
476                 }
477             }
478             ExprKind::Let { expr: expr_id, .. } => {
479                 let let_expr = &self.thir[expr_id];
480                 if let ty::Adt(adt_def, _) = let_expr.ty.kind() && adt_def.is_union() {
481                     self.requires_unsafe(expr.span, AccessToUnionField);
482                 }
483             }
484             _ => {}
485         }
486         visit::walk_expr(self, expr);
487     }
488 }
489
490 #[derive(Clone, Copy)]
491 enum SafetyContext {
492     Safe,
493     BuiltinUnsafeBlock,
494     UnsafeFn,
495     UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
496 }
497
498 #[derive(Clone, Copy)]
499 enum BodyUnsafety {
500     /// The body is not unsafe.
501     Safe,
502     /// The body is an unsafe function. The span points to
503     /// the signature of the function.
504     Unsafe(Span),
505 }
506
507 impl BodyUnsafety {
508     /// Returns whether the body is unsafe.
509     fn is_unsafe(&self) -> bool {
510         matches!(self, BodyUnsafety::Unsafe(_))
511     }
512
513     /// If the body is unsafe, returns the `Span` of its signature.
514     fn unsafe_fn_sig_span(self) -> Option<Span> {
515         match self {
516             BodyUnsafety::Unsafe(span) => Some(span),
517             BodyUnsafety::Safe => None,
518         }
519     }
520 }
521
522 #[derive(Clone, Copy, PartialEq)]
523 enum UnsafeOpKind {
524     CallToUnsafeFunction(Option<DefId>),
525     UseOfInlineAssembly,
526     InitializingTypeWith,
527     UseOfMutableStatic,
528     UseOfExternStatic,
529     DerefOfRawPointer,
530     AccessToUnionField,
531     MutationOfLayoutConstrainedField,
532     BorrowOfLayoutConstrainedField,
533     CallToFunctionWith(DefId),
534 }
535
536 use UnsafeOpKind::*;
537
538 impl UnsafeOpKind {
539     pub fn simple_description(&self) -> &'static str {
540         match self {
541             CallToUnsafeFunction(..) => "call to unsafe function",
542             UseOfInlineAssembly => "use of inline assembly",
543             InitializingTypeWith => "initializing type with `rustc_layout_scalar_valid_range` attr",
544             UseOfMutableStatic => "use of mutable static",
545             UseOfExternStatic => "use of extern static",
546             DerefOfRawPointer => "dereference of raw pointer",
547             AccessToUnionField => "access to union field",
548             MutationOfLayoutConstrainedField => "mutation of layout constrained field",
549             BorrowOfLayoutConstrainedField => {
550                 "borrow of layout constrained field with interior mutability"
551             }
552             CallToFunctionWith(..) => "call to function with `#[target_feature]`",
553         }
554     }
555
556     pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) {
557         match self {
558             CallToUnsafeFunction(did) => (
559                 if let Some(did) = did {
560                     Cow::from(format!("call to unsafe function `{}`", tcx.def_path_str(*did)))
561                 } else {
562                     Cow::Borrowed(self.simple_description())
563                 },
564                 "consult the function's documentation for information on how to avoid undefined \
565                  behavior",
566             ),
567             UseOfInlineAssembly => (
568                 Cow::Borrowed(self.simple_description()),
569                 "inline assembly is entirely unchecked and can cause undefined behavior",
570             ),
571             InitializingTypeWith => (
572                 Cow::Borrowed(self.simple_description()),
573                 "initializing a layout restricted type's field with a value outside the valid \
574                  range is undefined behavior",
575             ),
576             UseOfMutableStatic => (
577                 Cow::Borrowed(self.simple_description()),
578                 "mutable statics can be mutated by multiple threads: aliasing violations or data \
579                  races will cause undefined behavior",
580             ),
581             UseOfExternStatic => (
582                 Cow::Borrowed(self.simple_description()),
583                 "extern statics are not controlled by the Rust type system: invalid data, \
584                  aliasing violations or data races will cause undefined behavior",
585             ),
586             DerefOfRawPointer => (
587                 Cow::Borrowed(self.simple_description()),
588                 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
589                  and cause data races: all of these are undefined behavior",
590             ),
591             AccessToUnionField => (
592                 Cow::Borrowed(self.simple_description()),
593                 "the field may not be properly initialized: using uninitialized data will cause \
594                  undefined behavior",
595             ),
596             MutationOfLayoutConstrainedField => (
597                 Cow::Borrowed(self.simple_description()),
598                 "mutating layout constrained fields cannot statically be checked for valid values",
599             ),
600             BorrowOfLayoutConstrainedField => (
601                 Cow::Borrowed(self.simple_description()),
602                 "references to fields of layout constrained fields lose the constraints. Coupled \
603                  with interior mutability, the field can be changed to invalid values",
604             ),
605             CallToFunctionWith(did) => (
606                 Cow::from(format!(
607                     "call to function `{}` with `#[target_feature]`",
608                     tcx.def_path_str(*did)
609                 )),
610                 "can only be called if the required target features are available",
611             ),
612         }
613     }
614 }
615
616 pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
617     // THIR unsafeck is gated under `-Z thir-unsafeck`
618     if !tcx.sess.opts.unstable_opts.thir_unsafeck {
619         return;
620     }
621
622     // Closures and inline consts are handled by their owner, if it has a body
623     if tcx.is_typeck_child(def.did.to_def_id()) {
624         return;
625     }
626
627     let Ok((thir, expr)) = tcx.thir_body(def) else {
628         return
629     };
630     let thir = &thir.borrow();
631     // If `thir` is empty, a type error occurred, skip this body.
632     if thir.exprs.is_empty() {
633         return;
634     }
635
636     let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
637     let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
638         if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
639             BodyUnsafety::Unsafe(fn_sig.span)
640         } else {
641             BodyUnsafety::Safe
642         }
643     });
644     let body_target_features = &tcx.body_codegen_attrs(def.did.to_def_id()).target_features;
645     let safety_context =
646         if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
647     let mut visitor = UnsafetyVisitor {
648         tcx,
649         thir,
650         safety_context,
651         hir_context: hir_id,
652         body_unsafety,
653         body_target_features,
654         assignment_info: None,
655         in_union_destructure: false,
656         param_env: tcx.param_env(def.did),
657         inside_adt: false,
658     };
659     visitor.visit_expr(&thir[expr]);
660 }
661
662 pub(crate) fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
663     if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
664         tcx.thir_check_unsafety_for_const_arg(def)
665     } else {
666         check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
667     }
668 }
669
670 pub(crate) fn thir_check_unsafety_for_const_arg<'tcx>(
671     tcx: TyCtxt<'tcx>,
672     (did, param_did): (LocalDefId, DefId),
673 ) {
674     check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
675 }