]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_build/src/check_unsafety.rs
Auto merge of #105651 - tgross35:once-cell-inline, r=m-ou-se
[rust.git] / compiler / rustc_mir_build / src / check_unsafety.rs
1 use crate::build::ExprCategory;
2 use crate::errors::*;
3 use rustc_middle::thir::visit::{self, Visitor};
4
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::ops::Bound;
16
17 struct UnsafetyVisitor<'a, 'tcx> {
18     tcx: TyCtxt<'tcx>,
19     thir: &'a Thir<'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>,
35     inside_adt: bool,
36 }
37
38 impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
39     fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
40         if let (
41             SafetyContext::UnsafeBlock { span: enclosing_span, .. },
42             SafetyContext::UnsafeBlock { span: block_span, hir_id, .. },
43         ) = (self.safety_context, safety_context)
44         {
45             self.warn_unused_unsafe(
46                 hir_id,
47                 block_span,
48                 Some(UnusedUnsafeEnclosing::Block {
49                     span: self.tcx.sess.source_map().guess_head_span(enclosing_span),
50                 }),
51             );
52             f(self);
53         } else {
54             let prev_context = self.safety_context;
55             self.safety_context = safety_context;
56
57             f(self);
58
59             if let SafetyContext::UnsafeBlock { used: false, span, hir_id } = self.safety_context {
60                 self.warn_unused_unsafe(
61                     hir_id,
62                     span,
63                     if self.unsafe_op_in_unsafe_fn_allowed() {
64                         self.body_unsafety
65                             .unsafe_fn_sig_span()
66                             .map(|span| UnusedUnsafeEnclosing::Function { span })
67                     } else {
68                         None
69                     },
70                 );
71             }
72             self.safety_context = prev_context;
73         }
74     }
75
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).
85                 *used = true;
86             }
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);
91             }
92             SafetyContext::Safe => {
93                 kind.emit_requires_unsafe_err(self.tcx, span, unsafe_op_in_unsafe_fn_allowed);
94             }
95         }
96     }
97
98     fn warn_unused_unsafe(
99         &self,
100         hir_id: hir::HirId,
101         block_span: Span,
102         enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
103     ) {
104         let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
105         self.tcx.emit_spanned_lint(
106             UNUSED_UNSAFE,
107             hir_id,
108             block_span,
109             UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
110         );
111     }
112
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
116     }
117
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;
127         }
128     }
129 }
130
131 // Searches for accesses to layout constrained fields.
132 struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
133     found: bool,
134     thir: &'a Thir<'tcx>,
135     tcx: TyCtxt<'tcx>,
136 }
137
138 impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
139     fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
140         Self { found: false, thir, tcx }
141     }
142 }
143
144 impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
145     fn thir(&self) -> &'a Thir<'tcx> {
146         self.thir
147     }
148
149     fn visit_expr(&mut self, expr: &Expr<'tcx>) {
150         match expr.kind {
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())
155                     {
156                         self.found = true;
157                     }
158                 }
159                 visit::walk_expr(self, expr);
160             }
161
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);
168             }
169
170             _ => {}
171         }
172     }
173 }
174
175 impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
176     fn thir(&self) -> &'a Thir<'tcx> {
177         &self.thir
178     }
179
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)
187                 });
188             }
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),
193                 );
194             }
195             BlockSafety::Safe => {
196                 visit::walk_block(self, block);
197             }
198         }
199     }
200
201     fn visit_pat(&mut self, pat: &Pat<'tcx>) {
202         if self.in_union_destructure {
203             match pat.kind {
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
216                 }
217                 // wildcard doesn't take anything
218                 PatKind::Wild |
219                 // these just wrap other patterns
220                 PatKind::Or { .. } |
221                 PatKind::AscribeUserType { .. } => {}
222             }
223         };
224
225         match &pat.kind {
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())
235                     {
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;
239                     } else {
240                         visit::walk_pat(self, pat);
241                     }
242                 } else {
243                     visit::walk_pat(self, pat);
244                 }
245             }
246             PatKind::Binding { mode: BindingMode::ByRef(borrow_kind), ty, .. } => {
247                 if self.inside_adt {
248                     let ty::Ref(_, ty, _) = ty.kind() else {
249                         span_bug!(
250                             pat.span,
251                             "BindingMode::ByRef in pattern, but found non-reference type {}",
252                             ty
253                         );
254                     };
255                     match borrow_kind {
256                         BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
257                             if !ty.is_freeze(self.tcx, self.param_env) {
258                                 self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
259                             }
260                         }
261                         BorrowKind::Mut { .. } => {
262                             self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
263                         }
264                     }
265                 }
266                 visit::walk_pat(self, pat);
267             }
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;
272             }
273             _ => {
274                 visit::walk_pat(self, pat);
275             }
276         }
277     }
278
279     fn visit_expr(&mut self, expr: &Expr<'tcx>) {
280         // could we be in the LHS of an assignment to a field?
281         match expr.kind {
282             ExprKind::Field { .. }
283             | ExprKind::VarRef { .. }
284             | ExprKind::UpvarRef { .. }
285             | ExprKind::Scope { .. }
286             | ExprKind::Cast { .. } => {}
287
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
329                 // than one child.
330                 self.assignment_info = None;
331             }
332         };
333         match expr.kind {
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
340             }
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() {
344                         Some(*func_id)
345                     } else {
346                         None
347                     };
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
355                         && !self
356                             .tcx
357                             .codegen_fn_attrs(func_did)
358                             .target_features
359                             .iter()
360                             .all(|feature| self.body_target_features.contains(feature))
361                     {
362                         self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
363                     }
364                 }
365             }
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);
372                     }
373                 } else if self.thir[arg].ty.is_unsafe_ptr() {
374                     self.requires_unsafe(expr.span, DerefOfRawPointer);
375                 }
376             }
377             ExprKind::InlineAsm { .. } => {
378                 self.requires_unsafe(expr.span, UseOfInlineAssembly);
379             }
380             ExprKind::Adt(box AdtExpr {
381                 adt_def,
382                 variant_index: _,
383                 substs: _,
384                 user_ty: _,
385                 fields: _,
386                 base: _,
387             }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) {
388                 (Bound::Unbounded, Bound::Unbounded) => {}
389                 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
390             },
391             ExprKind::Closure(box ClosureExpr {
392                 closure_id,
393                 substs: _,
394                 upvars: _,
395                 movability: _,
396                 fake_reads: _,
397             }) => {
398                 let closure_def = if let Some((did, const_param_id)) =
399                     ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
400                 {
401                     ty::WithOptConstParam { did, const_param_did: Some(const_param_id) }
402                 } else {
403                     ty::WithOptConstParam::unknown(closure_id)
404                 };
405                 self.visit_inner_body(closure_def);
406             }
407             ExprKind::ConstBlock { did, substs: _ } => {
408                 let def_id = did.expect_local();
409                 self.visit_inner_body(ty::WithOptConstParam::unknown(def_id));
410             }
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}"));
418                         }
419                     } else {
420                         self.requires_unsafe(expr.span, AccessToUnionField);
421                     }
422                 }
423             }
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);
429                 if visitor.found {
430                     self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
431                 }
432
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
441                 }
442             }
443             ExprKind::Borrow { borrow_kind, arg } => {
444                 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
445                 visit::walk_expr(&mut visitor, expr);
446                 if visitor.found {
447                     match borrow_kind {
448                         BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique
449                             if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
450                         {
451                             self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
452                         }
453                         BorrowKind::Mut { .. } => {
454                             self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
455                         }
456                         BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {}
457                     }
458                 }
459             }
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);
464                 }
465             }
466             _ => {}
467         }
468         visit::walk_expr(self, expr);
469     }
470 }
471
472 #[derive(Clone, Copy)]
473 enum SafetyContext {
474     Safe,
475     BuiltinUnsafeBlock,
476     UnsafeFn,
477     UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
478 }
479
480 #[derive(Clone, Copy)]
481 enum BodyUnsafety {
482     /// The body is not unsafe.
483     Safe,
484     /// The body is an unsafe function. The span points to
485     /// the signature of the function.
486     Unsafe(Span),
487 }
488
489 impl BodyUnsafety {
490     /// Returns whether the body is unsafe.
491     fn is_unsafe(&self) -> bool {
492         matches!(self, BodyUnsafety::Unsafe(_))
493     }
494
495     /// If the body is unsafe, returns the `Span` of its signature.
496     fn unsafe_fn_sig_span(self) -> Option<Span> {
497         match self {
498             BodyUnsafety::Unsafe(span) => Some(span),
499             BodyUnsafety::Safe => None,
500         }
501     }
502 }
503
504 #[derive(Clone, Copy, PartialEq)]
505 enum UnsafeOpKind {
506     CallToUnsafeFunction(Option<DefId>),
507     UseOfInlineAssembly,
508     InitializingTypeWith,
509     UseOfMutableStatic,
510     UseOfExternStatic,
511     DerefOfRawPointer,
512     AccessToUnionField,
513     MutationOfLayoutConstrainedField,
514     BorrowOfLayoutConstrainedField,
515     CallToFunctionWith(DefId),
516 }
517
518 use UnsafeOpKind::*;
519
520 impl UnsafeOpKind {
521     pub fn emit_unsafe_op_in_unsafe_fn_lint(
522         &self,
523         tcx: TyCtxt<'_>,
524         hir_id: hir::HirId,
525         span: Span,
526     ) {
527         match self {
528             CallToUnsafeFunction(did) if did.is_some() => tcx.emit_spanned_lint(
529                 UNSAFE_OP_IN_UNSAFE_FN,
530                 hir_id,
531                 span,
532                 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
533                     span,
534                     function: &tcx.def_path_str(did.unwrap()),
535                 },
536             ),
537             CallToUnsafeFunction(..) => tcx.emit_spanned_lint(
538                 UNSAFE_OP_IN_UNSAFE_FN,
539                 hir_id,
540                 span,
541                 UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span },
542             ),
543             UseOfInlineAssembly => tcx.emit_spanned_lint(
544                 UNSAFE_OP_IN_UNSAFE_FN,
545                 hir_id,
546                 span,
547                 UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span },
548             ),
549             InitializingTypeWith => tcx.emit_spanned_lint(
550                 UNSAFE_OP_IN_UNSAFE_FN,
551                 hir_id,
552                 span,
553                 UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
554             ),
555             UseOfMutableStatic => tcx.emit_spanned_lint(
556                 UNSAFE_OP_IN_UNSAFE_FN,
557                 hir_id,
558                 span,
559                 UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
560             ),
561             UseOfExternStatic => tcx.emit_spanned_lint(
562                 UNSAFE_OP_IN_UNSAFE_FN,
563                 hir_id,
564                 span,
565                 UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
566             ),
567             DerefOfRawPointer => tcx.emit_spanned_lint(
568                 UNSAFE_OP_IN_UNSAFE_FN,
569                 hir_id,
570                 span,
571                 UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
572             ),
573             AccessToUnionField => tcx.emit_spanned_lint(
574                 UNSAFE_OP_IN_UNSAFE_FN,
575                 hir_id,
576                 span,
577                 UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
578             ),
579             MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
580                 UNSAFE_OP_IN_UNSAFE_FN,
581                 hir_id,
582                 span,
583                 UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
584             ),
585             BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
586                 UNSAFE_OP_IN_UNSAFE_FN,
587                 hir_id,
588                 span,
589                 UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
590             ),
591             CallToFunctionWith(did) => tcx.emit_spanned_lint(
592                 UNSAFE_OP_IN_UNSAFE_FN,
593                 hir_id,
594                 span,
595                 UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
596                     span,
597                     function: &tcx.def_path_str(*did),
598                 },
599             ),
600         }
601     }
602
603     pub fn emit_requires_unsafe_err(
604         &self,
605         tcx: TyCtxt<'_>,
606         span: Span,
607         unsafe_op_in_unsafe_fn_allowed: bool,
608     ) {
609         match self {
610             CallToUnsafeFunction(did) if did.is_some() && unsafe_op_in_unsafe_fn_allowed => {
611                 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
612                     span,
613                     function: &tcx.def_path_str(did.unwrap()),
614                 });
615             }
616             CallToUnsafeFunction(did) if did.is_some() => {
617                 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe {
618                     span,
619                     function: &tcx.def_path_str(did.unwrap()),
620                 });
621             }
622             CallToUnsafeFunction(..) if unsafe_op_in_unsafe_fn_allowed => {
623                 tcx.sess.emit_err(
624                     CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span },
625                 );
626             }
627             CallToUnsafeFunction(..) => {
628                 tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span });
629             }
630             UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
631                 tcx.sess
632                     .emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
633             }
634             UseOfInlineAssembly => {
635                 tcx.sess.emit_err(UseOfInlineAssemblyRequiresUnsafe { span });
636             }
637             InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => {
638                 tcx.sess
639                     .emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
640             }
641             InitializingTypeWith => {
642                 tcx.sess.emit_err(InitializingTypeWithRequiresUnsafe { span });
643             }
644             UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
645                 tcx.sess
646                     .emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
647             }
648             UseOfMutableStatic => {
649                 tcx.sess.emit_err(UseOfMutableStaticRequiresUnsafe { span });
650             }
651             UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
652                 tcx.sess
653                     .emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
654             }
655             UseOfExternStatic => {
656                 tcx.sess.emit_err(UseOfExternStaticRequiresUnsafe { span });
657             }
658             DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
659                 tcx.sess
660                     .emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
661             }
662             DerefOfRawPointer => {
663                 tcx.sess.emit_err(DerefOfRawPointerRequiresUnsafe { span });
664             }
665             AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
666                 tcx.sess
667                     .emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
668             }
669             AccessToUnionField => {
670                 tcx.sess.emit_err(AccessToUnionFieldRequiresUnsafe { span });
671             }
672             MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
673                 tcx.sess.emit_err(
674                     MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
675                         span,
676                     },
677                 );
678             }
679             MutationOfLayoutConstrainedField => {
680                 tcx.sess.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe { span });
681             }
682             BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
683                 tcx.sess.emit_err(
684                     BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span },
685                 );
686             }
687             BorrowOfLayoutConstrainedField => {
688                 tcx.sess.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe { span });
689             }
690             CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => {
691                 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
692                     span,
693                     function: &tcx.def_path_str(*did),
694                 });
695             }
696             CallToFunctionWith(did) => {
697                 tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe {
698                     span,
699                     function: &tcx.def_path_str(*did),
700                 });
701             }
702         }
703     }
704 }
705
706 pub fn check_unsafety(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) {
707     // THIR unsafeck is gated under `-Z thir-unsafeck`
708     if !tcx.sess.opts.unstable_opts.thir_unsafeck {
709         return;
710     }
711
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()) {
714         return;
715     }
716
717     let Ok((thir, expr)) = tcx.thir_body(def) else {
718         return
719     };
720     let thir = &thir.borrow();
721     // If `thir` is empty, a type error occurred, skip this body.
722     if thir.exprs.is_empty() {
723         return;
724     }
725
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)
730         } else {
731             BodyUnsafety::Safe
732         }
733     });
734     let body_target_features = &tcx.body_codegen_attrs(def.did.to_def_id()).target_features;
735     let safety_context =
736         if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
737     let mut visitor = UnsafetyVisitor {
738         tcx,
739         thir,
740         safety_context,
741         hir_context: hir_id,
742         body_unsafety,
743         body_target_features,
744         assignment_info: None,
745         in_union_destructure: false,
746         param_env: tcx.param_env(def.did),
747         inside_adt: false,
748     };
749     visitor.visit_expr(&thir[expr]);
750 }
751
752 pub(crate) fn thir_check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
753     if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
754         tcx.thir_check_unsafety_for_const_arg(def)
755     } else {
756         check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
757     }
758 }
759
760 pub(crate) fn thir_check_unsafety_for_const_arg(
761     tcx: TyCtxt<'_>,
762     (did, param_did): (LocalDefId, DefId),
763 ) {
764     check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
765 }