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