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