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