]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_mir_build/src/check_unsafety.rs
New upstream version 1.61.0+dfsg1
[rustc.git] / compiler / rustc_mir_build / src / check_unsafety.rs
CommitLineData
136023e0 1use crate::build::ExprCategory;
c295e0f8 2use rustc_middle::thir::visit::{self, Visitor};
17df50a5
XL
3
4use rustc_errors::struct_span_err;
5use rustc_hir as hir;
136023e0 6use rustc_middle::mir::BorrowKind;
17df50a5 7use rustc_middle::thir::*;
94222f64 8use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
17df50a5
XL
9use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
10use rustc_session::lint::Level;
11use rustc_span::def_id::{DefId, LocalDefId};
12use rustc_span::symbol::Symbol;
13use rustc_span::Span;
14
15use std::ops::Bound;
16
17struct 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>,
94222f64
XL
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)>,
136023e0
XL
33 in_union_destructure: bool,
34 param_env: ParamEnv<'tcx>,
35 inside_adt: bool,
17df50a5
XL
36}
37
38impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
136023e0 39 fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
17df50a5
XL
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;
17df50a5
XL
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 {
136023e0 76 SafetyContext::BuiltinUnsafeBlock => {}
17df50a5
XL
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
136023e0
XL
142// Searches for accesses to layout constrained fields.
143struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
144 found: bool,
145 thir: &'a Thir<'tcx>,
146 tcx: TyCtxt<'tcx>,
147}
148
149impl<'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
155impl<'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)
5e7ed085 165 != self.tcx.layout_scalar_valid_range(adt_def.did())
136023e0
XL
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
17df50a5
XL
186impl<'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) {
136023e0
XL
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)
5e7ed085 245 != self.tcx.layout_scalar_valid_range(adt_def.did())
136023e0
XL
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 {
3c0e092e 259 let ty::Ref(_, ty, _) = ty.kind() else {
136023e0
XL
260 span_bug!(
261 pat.span,
262 "BindingMode::ByRef in pattern, but found non-reference type {}",
263 ty
264 );
3c0e092e
XL
265 };
266 match borrow_kind {
267 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
268 if !ty.is_freeze(self.tcx.at(pat.span), self.param_env) {
269 self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
270 }
271 }
272 BorrowKind::Mut { .. } => {
273 self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
274 }
136023e0
XL
275 }
276 }
277 visit::walk_pat(self, pat);
278 }
279 PatKind::Deref { .. } => {
280 let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
281 visit::walk_pat(self, pat);
282 self.inside_adt = old_inside_adt;
283 }
284 _ => {
285 visit::walk_pat(self, pat);
286 }
17df50a5
XL
287 }
288 }
289
290 fn visit_expr(&mut self, expr: &Expr<'tcx>) {
94222f64 291 // could we be in the LHS of an assignment to a field?
136023e0
XL
292 match expr.kind {
293 ExprKind::Field { .. }
294 | ExprKind::VarRef { .. }
295 | ExprKind::UpvarRef { .. }
296 | ExprKind::Scope { .. }
297 | ExprKind::Cast { .. } => {}
298
299 ExprKind::AddressOf { .. }
300 | ExprKind::Adt { .. }
301 | ExprKind::Array { .. }
302 | ExprKind::Binary { .. }
303 | ExprKind::Block { .. }
304 | ExprKind::Borrow { .. }
305 | ExprKind::Literal { .. }
5e7ed085
FG
306 | ExprKind::NamedConst { .. }
307 | ExprKind::NonHirLiteral { .. }
308 | ExprKind::ConstParam { .. }
136023e0
XL
309 | ExprKind::ConstBlock { .. }
310 | ExprKind::Deref { .. }
311 | ExprKind::Index { .. }
312 | ExprKind::NeverToAny { .. }
313 | ExprKind::PlaceTypeAscription { .. }
314 | ExprKind::ValueTypeAscription { .. }
315 | ExprKind::Pointer { .. }
316 | ExprKind::Repeat { .. }
317 | ExprKind::StaticRef { .. }
318 | ExprKind::ThreadLocalRef { .. }
319 | ExprKind::Tuple { .. }
320 | ExprKind::Unary { .. }
321 | ExprKind::Call { .. }
322 | ExprKind::Assign { .. }
323 | ExprKind::AssignOp { .. }
324 | ExprKind::Break { .. }
325 | ExprKind::Closure { .. }
326 | ExprKind::Continue { .. }
327 | ExprKind::Return { .. }
328 | ExprKind::Yield { .. }
329 | ExprKind::Loop { .. }
94222f64 330 | ExprKind::Let { .. }
136023e0
XL
331 | ExprKind::Match { .. }
332 | ExprKind::Box { .. }
333 | ExprKind::If { .. }
334 | ExprKind::InlineAsm { .. }
136023e0 335 | ExprKind::LogicalOp { .. }
94222f64
XL
336 | ExprKind::Use { .. } => {
337 // We don't need to save the old value and restore it
338 // because all the place expressions can't have more
339 // than one child.
340 self.assignment_info = None;
341 }
136023e0 342 };
17df50a5
XL
343 match expr.kind {
344 ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
345 let prev_id = self.hir_context;
346 self.hir_context = hir_id;
347 self.visit_expr(&self.thir[value]);
348 self.hir_context = prev_id;
136023e0 349 return; // don't visit the whole expression
17df50a5
XL
350 }
351 ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
352 if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
353 self.requires_unsafe(expr.span, CallToUnsafeFunction);
354 } else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
355 // If the called function has target features the calling function hasn't,
356 // the call requires `unsafe`. Don't check this on wasm
357 // targets, though. For more information on wasm see the
358 // is_like_wasm check in typeck/src/collect.rs
359 if !self.tcx.sess.target.options.is_like_wasm
360 && !self
361 .tcx
362 .codegen_fn_attrs(func_did)
363 .target_features
364 .iter()
365 .all(|feature| self.body_target_features.contains(feature))
366 {
367 self.requires_unsafe(expr.span, CallToFunctionWith);
368 }
369 }
370 }
371 ExprKind::Deref { arg } => {
372 if let ExprKind::StaticRef { def_id, .. } = self.thir[arg].kind {
373 if self.tcx.is_mutable_static(def_id) {
374 self.requires_unsafe(expr.span, UseOfMutableStatic);
375 } else if self.tcx.is_foreign_item(def_id) {
376 self.requires_unsafe(expr.span, UseOfExternStatic);
377 }
378 } else if self.thir[arg].ty.is_unsafe_ptr() {
379 self.requires_unsafe(expr.span, DerefOfRawPointer);
380 }
381 }
5099ac24 382 ExprKind::InlineAsm { .. } => {
17df50a5
XL
383 self.requires_unsafe(expr.span, UseOfInlineAssembly);
384 }
136023e0 385 ExprKind::Adt(box Adt {
17df50a5
XL
386 adt_def,
387 variant_index: _,
388 substs: _,
389 user_ty: _,
390 fields: _,
391 base: _,
5e7ed085 392 }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) {
17df50a5
XL
393 (Bound::Unbounded, Bound::Unbounded) => {}
394 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
395 },
17df50a5
XL
396 ExprKind::Closure {
397 closure_id,
398 substs: _,
399 upvars: _,
400 movability: _,
401 fake_reads: _,
402 } => {
403 let closure_id = closure_id.expect_local();
404 let closure_def = if let Some((did, const_param_id)) =
405 ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
406 {
407 ty::WithOptConstParam { did, const_param_did: Some(const_param_id) }
408 } else {
409 ty::WithOptConstParam::unknown(closure_id)
410 };
5e7ed085
FG
411 let (closure_thir, expr) = self.tcx.thir_body(closure_def).unwrap_or_else(|_| {
412 (self.tcx.alloc_steal_thir(Thir::new()), ExprId::from_u32(0))
413 });
17df50a5
XL
414 let closure_thir = &closure_thir.borrow();
415 let hir_context = self.tcx.hir().local_def_id_to_hir_id(closure_id);
416 let mut closure_visitor =
417 UnsafetyVisitor { thir: closure_thir, hir_context, ..*self };
418 closure_visitor.visit_expr(&closure_thir[expr]);
419 // Unsafe blocks can be used in closures, make sure to take it into account
420 self.safety_context = closure_visitor.safety_context;
421 }
136023e0 422 ExprKind::Field { lhs, .. } => {
94222f64 423 let lhs = &self.thir[lhs];
5e7ed085
FG
424 if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
425 if let Some((assigned_ty, assignment_span)) = self.assignment_info {
426 // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
427 if !(assigned_ty
428 .ty_adt_def()
429 .map_or(false, |adt| adt.is_manually_drop())
430 || assigned_ty
431 .is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env))
432 {
433 self.requires_unsafe(assignment_span, AssignToDroppingUnionField);
136023e0 434 } else {
5e7ed085 435 // write to non-drop union field, safe
136023e0 436 }
5e7ed085
FG
437 } else {
438 self.requires_unsafe(expr.span, AccessToUnionField);
136023e0
XL
439 }
440 }
441 }
442 ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
94222f64 443 let lhs = &self.thir[lhs];
136023e0
XL
444 // First, check whether we are mutating a layout constrained field
445 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
94222f64 446 visit::walk_expr(&mut visitor, lhs);
136023e0
XL
447 if visitor.found {
448 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
449 }
450
451 // Second, check for accesses to union fields
452 // don't have any special handling for AssignOp since it causes a read *and* write to lhs
453 if matches!(expr.kind, ExprKind::Assign { .. }) {
94222f64
XL
454 self.assignment_info = Some((lhs.ty, expr.span));
455 visit::walk_expr(self, lhs);
456 self.assignment_info = None;
136023e0
XL
457 visit::walk_expr(self, &self.thir()[rhs]);
458 return; // we have already visited everything by now
459 }
460 }
94222f64
XL
461 ExprKind::Borrow { borrow_kind, arg } => {
462 let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
463 visit::walk_expr(&mut visitor, expr);
464 if visitor.found {
465 match borrow_kind {
466 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique
467 if !self.thir[arg]
468 .ty
469 .is_freeze(self.tcx.at(self.thir[arg].span), self.param_env) =>
470 {
471 self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
472 }
473 BorrowKind::Mut { .. } => {
474 self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
136023e0 475 }
94222f64 476 BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {}
136023e0
XL
477 }
478 }
94222f64
XL
479 }
480 ExprKind::Let { expr: expr_id, .. } => {
481 let let_expr = &self.thir[expr_id];
5e7ed085
FG
482 if let ty::Adt(adt_def, _) = let_expr.ty.kind() && adt_def.is_union() {
483 self.requires_unsafe(expr.span, AccessToUnionField);
136023e0 484 }
94222f64 485 }
17df50a5
XL
486 _ => {}
487 }
17df50a5
XL
488 visit::walk_expr(self, expr);
489 }
490}
491
492#[derive(Clone, Copy)]
493enum SafetyContext {
494 Safe,
136023e0 495 BuiltinUnsafeBlock,
17df50a5
XL
496 UnsafeFn,
497 UnsafeBlock { span: Span, hir_id: hir::HirId, used: bool },
498}
499
500#[derive(Clone, Copy)]
501enum BodyUnsafety {
502 /// The body is not unsafe.
503 Safe,
504 /// The body is an unsafe function. The span points to
505 /// the signature of the function.
506 Unsafe(Span),
507}
508
509impl BodyUnsafety {
510 /// Returns whether the body is unsafe.
511 fn is_unsafe(&self) -> bool {
512 matches!(self, BodyUnsafety::Unsafe(_))
513 }
514
515 /// If the body is unsafe, returns the `Span` of its signature.
516 fn unsafe_fn_sig_span(self) -> Option<Span> {
517 match self {
518 BodyUnsafety::Unsafe(span) => Some(span),
519 BodyUnsafety::Safe => None,
520 }
521 }
522}
523
524#[derive(Clone, Copy, PartialEq)]
525enum UnsafeOpKind {
526 CallToUnsafeFunction,
527 UseOfInlineAssembly,
528 InitializingTypeWith,
17df50a5
XL
529 UseOfMutableStatic,
530 UseOfExternStatic,
531 DerefOfRawPointer,
17df50a5 532 AssignToDroppingUnionField,
17df50a5 533 AccessToUnionField,
17df50a5 534 MutationOfLayoutConstrainedField,
17df50a5
XL
535 BorrowOfLayoutConstrainedField,
536 CallToFunctionWith,
537}
538
539use UnsafeOpKind::*;
540
541impl UnsafeOpKind {
542 pub fn description_and_note(&self) -> (&'static str, &'static str) {
543 match self {
544 CallToUnsafeFunction => (
545 "call to unsafe function",
546 "consult the function's documentation for information on how to avoid undefined \
547 behavior",
548 ),
549 UseOfInlineAssembly => (
550 "use of inline assembly",
551 "inline assembly is entirely unchecked and can cause undefined behavior",
552 ),
553 InitializingTypeWith => (
554 "initializing type with `rustc_layout_scalar_valid_range` attr",
555 "initializing a layout restricted type's field with a value outside the valid \
556 range is undefined behavior",
557 ),
17df50a5
XL
558 UseOfMutableStatic => (
559 "use of mutable static",
560 "mutable statics can be mutated by multiple threads: aliasing violations or data \
561 races will cause undefined behavior",
562 ),
563 UseOfExternStatic => (
564 "use of extern static",
565 "extern statics are not controlled by the Rust type system: invalid data, \
566 aliasing violations or data races will cause undefined behavior",
567 ),
568 DerefOfRawPointer => (
569 "dereference of raw pointer",
570 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
571 and cause data races: all of these are undefined behavior",
572 ),
573 AssignToDroppingUnionField => (
574 "assignment to union field that might need dropping",
575 "the previous content of the field will be dropped, which causes undefined \
576 behavior if the field was not properly initialized",
577 ),
578 AccessToUnionField => (
579 "access to union field",
580 "the field may not be properly initialized: using uninitialized data will cause \
581 undefined behavior",
582 ),
583 MutationOfLayoutConstrainedField => (
584 "mutation of layout constrained field",
585 "mutating layout constrained fields cannot statically be checked for valid values",
586 ),
587 BorrowOfLayoutConstrainedField => (
588 "borrow of layout constrained field with interior mutability",
589 "references to fields of layout constrained fields lose the constraints. Coupled \
590 with interior mutability, the field can be changed to invalid values",
591 ),
592 CallToFunctionWith => (
593 "call to function with `#[target_feature]`",
594 "can only be called if the required target features are available",
595 ),
596 }
597 }
598}
599
600pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
601 // THIR unsafeck is gated under `-Z thir-unsafeck`
602 if !tcx.sess.opts.debugging_opts.thir_unsafeck {
603 return;
604 }
605
136023e0 606 // Closures are handled by their owner, if it has a body
17df50a5 607 if tcx.is_closure(def.did.to_def_id()) {
94222f64
XL
608 let hir = tcx.hir();
609 let owner = hir.enclosing_body_owner(hir.local_def_id_to_hir_id(def.did));
610 tcx.ensure().thir_check_unsafety(hir.local_def_id(owner));
611 return;
17df50a5
XL
612 }
613
5e7ed085
FG
614 let (thir, expr) = match tcx.thir_body(def) {
615 Ok(body) => body,
616 Err(_) => return,
617 };
17df50a5 618 let thir = &thir.borrow();
5e7ed085 619 // If `thir` is empty, a type error occurred, skip this body.
17df50a5
XL
620 if thir.exprs.is_empty() {
621 return;
622 }
623
624 let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
625 let body_unsafety = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(BodyUnsafety::Safe, |fn_sig| {
626 if fn_sig.header.unsafety == hir::Unsafety::Unsafe {
627 BodyUnsafety::Unsafe(fn_sig.span)
628 } else {
629 BodyUnsafety::Safe
630 }
631 });
632 let body_target_features = &tcx.codegen_fn_attrs(def.did).target_features;
633 let safety_context =
634 if body_unsafety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe };
17df50a5
XL
635 let mut visitor = UnsafetyVisitor {
636 tcx,
637 thir,
638 safety_context,
639 hir_context: hir_id,
640 body_unsafety,
641 body_target_features,
94222f64 642 assignment_info: None,
136023e0
XL
643 in_union_destructure: false,
644 param_env: tcx.param_env(def.did),
645 inside_adt: false,
17df50a5
XL
646 };
647 visitor.visit_expr(&thir[expr]);
648}
649
650crate fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
651 if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
652 tcx.thir_check_unsafety_for_const_arg(def)
653 } else {
654 check_unsafety(tcx, ty::WithOptConstParam::unknown(def_id))
655 }
656}
657
658crate fn thir_check_unsafety_for_const_arg<'tcx>(
659 tcx: TyCtxt<'tcx>,
660 (did, param_did): (LocalDefId, DefId),
661) {
662 check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
663}