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