rustc_mir_build/
check_unsafety.rs

1use std::borrow::Cow;
2use std::mem;
3use std::ops::Bound;
4
5use rustc_ast::AsmMacro;
6use rustc_data_structures::stack::ensure_sufficient_stack;
7use rustc_errors::DiagArgValue;
8use rustc_hir::def::DefKind;
9use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability};
10use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
11use rustc_middle::mir::BorrowKind;
12use rustc_middle::span_bug;
13use rustc_middle::thir::visit::Visitor;
14use rustc_middle::thir::*;
15use rustc_middle::ty::print::with_no_trimmed_paths;
16use rustc_middle::ty::{self, Ty, TyCtxt};
17use rustc_session::lint::Level;
18use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
19use rustc_span::def_id::{DefId, LocalDefId};
20use rustc_span::{Span, Symbol, sym};
21
22use crate::builder::ExprCategory;
23use crate::errors::*;
24
25struct UnsafetyVisitor<'a, 'tcx> {
26    tcx: TyCtxt<'tcx>,
27    thir: &'a Thir<'tcx>,
28    /// The `HirId` of the current scope, which would be the `HirId`
29    /// of the current HIR node, modulo adjustments. Used for lint levels.
30    hir_context: HirId,
31    /// The current "safety context". This notably tracks whether we are in an
32    /// `unsafe` block, and whether it has been used.
33    safety_context: SafetyContext,
34    /// The `#[target_feature]` attributes of the body. Used for checking
35    /// calls to functions with `#[target_feature]` (RFC 2396).
36    body_target_features: &'tcx [TargetFeature],
37    /// When inside the LHS of an assignment to a field, this is the type
38    /// of the LHS and the span of the assignment expression.
39    assignment_info: Option<Ty<'tcx>>,
40    in_union_destructure: bool,
41    typing_env: ty::TypingEnv<'tcx>,
42    inside_adt: bool,
43    warnings: &'a mut Vec<UnusedUnsafeWarning>,
44
45    /// Flag to ensure that we only suggest wrapping the entire function body in
46    /// an unsafe block once.
47    suggest_unsafe_block: bool,
48}
49
50impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
51    fn in_safety_context(&mut self, safety_context: SafetyContext, f: impl FnOnce(&mut Self)) {
52        let prev_context = mem::replace(&mut self.safety_context, safety_context);
53
54        f(self);
55
56        let safety_context = mem::replace(&mut self.safety_context, prev_context);
57        if let SafetyContext::UnsafeBlock { used, span, hir_id, nested_used_blocks } =
58            safety_context
59        {
60            if !used {
61                self.warn_unused_unsafe(hir_id, span, None);
62
63                if let SafetyContext::UnsafeBlock {
64                    nested_used_blocks: ref mut prev_nested_used_blocks,
65                    ..
66                } = self.safety_context
67                {
68                    prev_nested_used_blocks.extend(nested_used_blocks);
69                }
70            } else {
71                for block in nested_used_blocks {
72                    self.warn_unused_unsafe(
73                        block.hir_id,
74                        block.span,
75                        Some(UnusedUnsafeEnclosing::Block {
76                            span: self.tcx.sess.source_map().guess_head_span(span),
77                        }),
78                    );
79                }
80
81                match self.safety_context {
82                    SafetyContext::UnsafeBlock {
83                        nested_used_blocks: ref mut prev_nested_used_blocks,
84                        ..
85                    } => {
86                        prev_nested_used_blocks.push(NestedUsedBlock { hir_id, span });
87                    }
88                    _ => (),
89                }
90            }
91        }
92    }
93
94    fn emit_deprecated_safe_fn_call(&self, span: Span, kind: &UnsafeOpKind) -> bool {
95        match kind {
96            // Allow calls to deprecated-safe unsafe functions if the caller is
97            // from an edition before 2024.
98            &UnsafeOpKind::CallToUnsafeFunction(Some(id))
99                if !span.at_least_rust_2024()
100                    && let Some(attr) = self.tcx.get_attr(id, sym::rustc_deprecated_safe_2024) =>
101            {
102                let suggestion = attr
103                    .meta_item_list()
104                    .unwrap_or_default()
105                    .into_iter()
106                    .find(|item| item.has_name(sym::audit_that))
107                    .map(|item| {
108                        item.value_str().expect(
109                            "`#[rustc_deprecated_safe_2024(audit_that)]` must have a string value",
110                        )
111                    });
112
113                let sm = self.tcx.sess.source_map();
114                let guarantee = suggestion
115                    .as_ref()
116                    .map(|suggestion| format!("that {}", suggestion))
117                    .unwrap_or_else(|| String::from("its unsafe preconditions"));
118                let suggestion = suggestion
119                    .and_then(|suggestion| {
120                        sm.indentation_before(span).map(|indent| {
121                            format!("{}// TODO: Audit that {}.\n", indent, suggestion) // ignore-tidy-todo
122                        })
123                    })
124                    .unwrap_or_default();
125
126                self.tcx.emit_node_span_lint(
127                    DEPRECATED_SAFE_2024,
128                    self.hir_context,
129                    span,
130                    CallToDeprecatedSafeFnRequiresUnsafe {
131                        span,
132                        function: with_no_trimmed_paths!(self.tcx.def_path_str(id)),
133                        guarantee,
134                        sub: CallToDeprecatedSafeFnRequiresUnsafeSub {
135                            start_of_line_suggestion: suggestion,
136                            start_of_line: sm.span_extend_to_line(span).shrink_to_lo(),
137                            left: span.shrink_to_lo(),
138                            right: span.shrink_to_hi(),
139                        },
140                    },
141                );
142                true
143            }
144            _ => false,
145        }
146    }
147
148    fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
149        let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
150        match self.safety_context {
151            SafetyContext::BuiltinUnsafeBlock => {}
152            SafetyContext::UnsafeBlock { ref mut used, .. } => {
153                // Mark this block as useful (even inside `unsafe fn`, where it is technically
154                // redundant -- but we want to eventually enable `unsafe_op_in_unsafe_fn` by
155                // default which will require those blocks:
156                // https://github.com/rust-lang/rust/issues/71668#issuecomment-1203075594).
157                *used = true;
158            }
159            SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
160            SafetyContext::UnsafeFn => {
161                let deprecated_safe_fn = self.emit_deprecated_safe_fn_call(span, &kind);
162                if !deprecated_safe_fn {
163                    // unsafe_op_in_unsafe_fn is disallowed
164                    kind.emit_unsafe_op_in_unsafe_fn_lint(
165                        self.tcx,
166                        self.hir_context,
167                        span,
168                        self.suggest_unsafe_block,
169                    );
170                    self.suggest_unsafe_block = false;
171                }
172            }
173            SafetyContext::Safe => {
174                let deprecated_safe_fn = self.emit_deprecated_safe_fn_call(span, &kind);
175                if !deprecated_safe_fn {
176                    kind.emit_requires_unsafe_err(
177                        self.tcx,
178                        span,
179                        self.hir_context,
180                        unsafe_op_in_unsafe_fn_allowed,
181                    );
182                }
183            }
184        }
185    }
186
187    fn warn_unused_unsafe(
188        &mut self,
189        hir_id: HirId,
190        block_span: Span,
191        enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
192    ) {
193        self.warnings.push(UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe });
194    }
195
196    /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
197    fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
198        self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level == Level::Allow
199    }
200
201    /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body.
202    fn visit_inner_body(&mut self, def: LocalDefId) {
203        if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) {
204            // Runs all other queries that depend on THIR.
205            self.tcx.ensure_done().mir_built(def);
206            let inner_thir = &inner_thir.steal();
207            let hir_context = self.tcx.local_def_id_to_hir_id(def);
208            let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe);
209            let mut inner_visitor = UnsafetyVisitor {
210                tcx: self.tcx,
211                thir: inner_thir,
212                hir_context,
213                safety_context,
214                body_target_features: self.body_target_features,
215                assignment_info: self.assignment_info,
216                in_union_destructure: false,
217                typing_env: self.typing_env,
218                inside_adt: false,
219                warnings: self.warnings,
220                suggest_unsafe_block: self.suggest_unsafe_block,
221            };
222            // params in THIR may be unsafe, e.g. a union pattern.
223            for param in &inner_thir.params {
224                if let Some(param_pat) = param.pat.as_deref() {
225                    inner_visitor.visit_pat(param_pat);
226                }
227            }
228            // Visit the body.
229            inner_visitor.visit_expr(&inner_thir[expr]);
230            // Unsafe blocks can be used in the inner body, make sure to take it into account
231            self.safety_context = inner_visitor.safety_context;
232        }
233    }
234}
235
236// Searches for accesses to layout constrained fields.
237struct LayoutConstrainedPlaceVisitor<'a, 'tcx> {
238    found: bool,
239    thir: &'a Thir<'tcx>,
240    tcx: TyCtxt<'tcx>,
241}
242
243impl<'a, 'tcx> LayoutConstrainedPlaceVisitor<'a, 'tcx> {
244    fn new(thir: &'a Thir<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
245        Self { found: false, thir, tcx }
246    }
247}
248
249impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> {
250    fn thir(&self) -> &'a Thir<'tcx> {
251        self.thir
252    }
253
254    fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
255        match expr.kind {
256            ExprKind::Field { lhs, .. } => {
257                if let ty::Adt(adt_def, _) = self.thir[lhs].ty.kind() {
258                    if (Bound::Unbounded, Bound::Unbounded)
259                        != self.tcx.layout_scalar_valid_range(adt_def.did())
260                    {
261                        self.found = true;
262                    }
263                }
264                visit::walk_expr(self, expr);
265            }
266
267            // Keep walking through the expression as long as we stay in the same
268            // place, i.e. the expression is a place expression and not a dereference
269            // (since dereferencing something leads us to a different place).
270            ExprKind::Deref { .. } => {}
271            ref kind if ExprCategory::of(kind).is_none_or(|cat| cat == ExprCategory::Place) => {
272                visit::walk_expr(self, expr);
273            }
274
275            _ => {}
276        }
277    }
278}
279
280impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
281    fn thir(&self) -> &'a Thir<'tcx> {
282        self.thir
283    }
284
285    fn visit_block(&mut self, block: &'a Block) {
286        match block.safety_mode {
287            // compiler-generated unsafe code should not count towards the usefulness of
288            // an outer unsafe block
289            BlockSafety::BuiltinUnsafe => {
290                self.in_safety_context(SafetyContext::BuiltinUnsafeBlock, |this| {
291                    visit::walk_block(this, block)
292                });
293            }
294            BlockSafety::ExplicitUnsafe(hir_id) => {
295                let used = matches!(
296                    self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level,
297                    Level::Allow
298                );
299                self.in_safety_context(
300                    SafetyContext::UnsafeBlock {
301                        span: block.span,
302                        hir_id,
303                        used,
304                        nested_used_blocks: Vec::new(),
305                    },
306                    |this| visit::walk_block(this, block),
307                );
308            }
309            BlockSafety::Safe => {
310                visit::walk_block(self, block);
311            }
312        }
313    }
314
315    fn visit_pat(&mut self, pat: &'a Pat<'tcx>) {
316        if self.in_union_destructure {
317            match pat.kind {
318                PatKind::Missing => unreachable!(),
319                // binding to a variable allows getting stuff out of variable
320                PatKind::Binding { .. }
321                // match is conditional on having this value
322                | PatKind::Constant { .. }
323                | PatKind::Variant { .. }
324                | PatKind::Leaf { .. }
325                | PatKind::Deref { .. }
326                | PatKind::DerefPattern { .. }
327                | PatKind::Range { .. }
328                | PatKind::Slice { .. }
329                | PatKind::Array { .. }
330                // Never constitutes a witness of uninhabitedness.
331                | PatKind::Never => {
332                    self.requires_unsafe(pat.span, AccessToUnionField);
333                    return; // we can return here since this already requires unsafe
334                }
335                // wildcard doesn't read anything.
336                PatKind::Wild |
337                // these just wrap other patterns, which we recurse on below.
338                PatKind::Or { .. } |
339                PatKind::ExpandedConstant { .. } |
340                PatKind::AscribeUserType { .. } |
341                PatKind::Error(_) => {}
342            }
343        };
344
345        match &pat.kind {
346            PatKind::Leaf { subpatterns, .. } => {
347                if let ty::Adt(adt_def, ..) = pat.ty.kind() {
348                    for pat in subpatterns {
349                        if adt_def.non_enum_variant().fields[pat.field].safety.is_unsafe() {
350                            self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
351                        }
352                    }
353                    if adt_def.is_union() {
354                        let old_in_union_destructure =
355                            std::mem::replace(&mut self.in_union_destructure, true);
356                        visit::walk_pat(self, pat);
357                        self.in_union_destructure = old_in_union_destructure;
358                    } else if (Bound::Unbounded, Bound::Unbounded)
359                        != self.tcx.layout_scalar_valid_range(adt_def.did())
360                    {
361                        let old_inside_adt = std::mem::replace(&mut self.inside_adt, true);
362                        visit::walk_pat(self, pat);
363                        self.inside_adt = old_inside_adt;
364                    } else {
365                        visit::walk_pat(self, pat);
366                    }
367                } else {
368                    visit::walk_pat(self, pat);
369                }
370            }
371            PatKind::Variant { adt_def, args: _, variant_index, subpatterns } => {
372                for pat in subpatterns {
373                    let field = &pat.field;
374                    if adt_def.variant(*variant_index).fields[*field].safety.is_unsafe() {
375                        self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
376                    }
377                }
378                visit::walk_pat(self, pat);
379            }
380            PatKind::Binding { mode: BindingMode(ByRef::Yes(rm), _), ty, .. } => {
381                if self.inside_adt {
382                    let ty::Ref(_, ty, _) = ty.kind() else {
383                        span_bug!(
384                            pat.span,
385                            "ByRef::Yes in pattern, but found non-reference type {}",
386                            ty
387                        );
388                    };
389                    match rm {
390                        Mutability::Not => {
391                            if !ty.is_freeze(self.tcx, self.typing_env) {
392                                self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
393                            }
394                        }
395                        Mutability::Mut { .. } => {
396                            self.requires_unsafe(pat.span, MutationOfLayoutConstrainedField);
397                        }
398                    }
399                }
400                visit::walk_pat(self, pat);
401            }
402            PatKind::Deref { .. } | PatKind::DerefPattern { .. } => {
403                let old_inside_adt = std::mem::replace(&mut self.inside_adt, false);
404                visit::walk_pat(self, pat);
405                self.inside_adt = old_inside_adt;
406            }
407            PatKind::ExpandedConstant { def_id, .. } => {
408                if let Some(def) = def_id.as_local()
409                    && matches!(self.tcx.def_kind(def_id), DefKind::InlineConst)
410                {
411                    self.visit_inner_body(def);
412                }
413                visit::walk_pat(self, pat);
414            }
415            _ => {
416                visit::walk_pat(self, pat);
417            }
418        }
419    }
420
421    fn visit_expr(&mut self, expr: &'a Expr<'tcx>) {
422        // could we be in the LHS of an assignment to a field?
423        match expr.kind {
424            ExprKind::Field { .. }
425            | ExprKind::VarRef { .. }
426            | ExprKind::UpvarRef { .. }
427            | ExprKind::Scope { .. }
428            | ExprKind::Cast { .. } => {}
429
430            ExprKind::RawBorrow { .. }
431            | ExprKind::Adt { .. }
432            | ExprKind::Array { .. }
433            | ExprKind::Binary { .. }
434            | ExprKind::Block { .. }
435            | ExprKind::Borrow { .. }
436            | ExprKind::Literal { .. }
437            | ExprKind::NamedConst { .. }
438            | ExprKind::NonHirLiteral { .. }
439            | ExprKind::ZstLiteral { .. }
440            | ExprKind::ConstParam { .. }
441            | ExprKind::ConstBlock { .. }
442            | ExprKind::Deref { .. }
443            | ExprKind::Index { .. }
444            | ExprKind::NeverToAny { .. }
445            | ExprKind::PlaceTypeAscription { .. }
446            | ExprKind::ValueTypeAscription { .. }
447            | ExprKind::PlaceUnwrapUnsafeBinder { .. }
448            | ExprKind::ValueUnwrapUnsafeBinder { .. }
449            | ExprKind::WrapUnsafeBinder { .. }
450            | ExprKind::PointerCoercion { .. }
451            | ExprKind::Repeat { .. }
452            | ExprKind::StaticRef { .. }
453            | ExprKind::ThreadLocalRef { .. }
454            | ExprKind::Tuple { .. }
455            | ExprKind::Unary { .. }
456            | ExprKind::Call { .. }
457            | ExprKind::ByUse { .. }
458            | ExprKind::Assign { .. }
459            | ExprKind::AssignOp { .. }
460            | ExprKind::Break { .. }
461            | ExprKind::Closure { .. }
462            | ExprKind::Continue { .. }
463            | ExprKind::Return { .. }
464            | ExprKind::Become { .. }
465            | ExprKind::Yield { .. }
466            | ExprKind::Loop { .. }
467            | ExprKind::Let { .. }
468            | ExprKind::Match { .. }
469            | ExprKind::Box { .. }
470            | ExprKind::If { .. }
471            | ExprKind::InlineAsm { .. }
472            | ExprKind::OffsetOf { .. }
473            | ExprKind::LogicalOp { .. }
474            | ExprKind::Use { .. } => {
475                // We don't need to save the old value and restore it
476                // because all the place expressions can't have more
477                // than one child.
478                self.assignment_info = None;
479            }
480        };
481        match expr.kind {
482            ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
483                let prev_id = self.hir_context;
484                self.hir_context = hir_id;
485                ensure_sufficient_stack(|| {
486                    self.visit_expr(&self.thir[value]);
487                });
488                self.hir_context = prev_id;
489                return; // don't visit the whole expression
490            }
491            ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
492                let fn_ty = self.thir[fun].ty;
493                let sig = fn_ty.fn_sig(self.tcx);
494                let (callee_features, safe_target_features): (&[_], _) = match fn_ty.kind() {
495                    ty::FnDef(func_id, ..) => {
496                        let cg_attrs = self.tcx.codegen_fn_attrs(func_id);
497                        (&cg_attrs.target_features, cg_attrs.safe_target_features)
498                    }
499                    _ => (&[], false),
500                };
501                if sig.safety().is_unsafe() && !safe_target_features {
502                    let func_id = if let ty::FnDef(func_id, _) = fn_ty.kind() {
503                        Some(*func_id)
504                    } else {
505                        None
506                    };
507                    self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
508                } else if let &ty::FnDef(func_did, _) = fn_ty.kind() {
509                    if !self
510                        .tcx
511                        .is_target_feature_call_safe(callee_features, self.body_target_features)
512                    {
513                        let missing: Vec<_> = callee_features
514                            .iter()
515                            .copied()
516                            .filter(|feature| {
517                                !feature.implied
518                                    && !self
519                                        .body_target_features
520                                        .iter()
521                                        .any(|body_feature| body_feature.name == feature.name)
522                            })
523                            .map(|feature| feature.name)
524                            .collect();
525                        let build_enabled = self
526                            .tcx
527                            .sess
528                            .target_features
529                            .iter()
530                            .copied()
531                            .filter(|feature| missing.contains(feature))
532                            .collect();
533                        self.requires_unsafe(
534                            expr.span,
535                            CallToFunctionWith { function: func_did, missing, build_enabled },
536                        );
537                    }
538                }
539            }
540            ExprKind::RawBorrow { arg, .. } => {
541                if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind
542                    && let ExprKind::Deref { arg } = self.thir[arg].kind
543                {
544                    // Taking a raw ref to a deref place expr is always safe.
545                    // Make sure the expression we're deref'ing is safe, though.
546                    visit::walk_expr(self, &self.thir[arg]);
547                    return;
548                }
549            }
550            ExprKind::Deref { arg } => {
551                if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) =
552                    self.thir[arg].kind
553                {
554                    if self.tcx.is_mutable_static(def_id) {
555                        self.requires_unsafe(expr.span, UseOfMutableStatic);
556                    } else if self.tcx.is_foreign_item(def_id) {
557                        match self.tcx.def_kind(def_id) {
558                            DefKind::Static { safety: hir::Safety::Safe, .. } => {}
559                            _ => self.requires_unsafe(expr.span, UseOfExternStatic),
560                        }
561                    }
562                } else if self.thir[arg].ty.is_raw_ptr() {
563                    self.requires_unsafe(expr.span, DerefOfRawPointer);
564                }
565            }
566            ExprKind::InlineAsm(box InlineAsmExpr {
567                asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
568                ref operands,
569                template: _,
570                options: _,
571                line_spans: _,
572            }) => {
573                // The `naked` attribute and the `naked_asm!` block form one atomic unit of
574                // unsafety, and `naked_asm!` does not itself need to be wrapped in an unsafe block.
575                if let AsmMacro::Asm = asm_macro {
576                    self.requires_unsafe(expr.span, UseOfInlineAssembly);
577                }
578
579                // For inline asm, do not use `walk_expr`, since we want to handle the label block
580                // specially.
581                for op in &**operands {
582                    use rustc_middle::thir::InlineAsmOperand::*;
583                    match op {
584                        In { expr, reg: _ }
585                        | Out { expr: Some(expr), reg: _, late: _ }
586                        | InOut { expr, reg: _, late: _ } => self.visit_expr(&self.thir()[*expr]),
587                        SplitInOut { in_expr, out_expr, reg: _, late: _ } => {
588                            self.visit_expr(&self.thir()[*in_expr]);
589                            if let Some(out_expr) = out_expr {
590                                self.visit_expr(&self.thir()[*out_expr]);
591                            }
592                        }
593                        Out { expr: None, reg: _, late: _ }
594                        | Const { value: _, span: _ }
595                        | SymFn { value: _ }
596                        | SymStatic { def_id: _ } => {}
597                        Label { block } => {
598                            // Label blocks are safe context.
599                            // `asm!()` is forced to be wrapped inside unsafe. If there's no special
600                            // treatment, the label blocks would also always be unsafe with no way
601                            // of opting out.
602                            self.in_safety_context(SafetyContext::Safe, |this| {
603                                visit::walk_block(this, &this.thir()[*block])
604                            });
605                        }
606                    }
607                }
608                return;
609            }
610            ExprKind::Adt(box AdtExpr {
611                adt_def,
612                variant_index,
613                args: _,
614                user_ty: _,
615                fields: _,
616                base: _,
617            }) => {
618                if adt_def.variant(variant_index).has_unsafe_fields() {
619                    self.requires_unsafe(expr.span, InitializingTypeWithUnsafeField)
620                }
621                match self.tcx.layout_scalar_valid_range(adt_def.did()) {
622                    (Bound::Unbounded, Bound::Unbounded) => {}
623                    _ => self.requires_unsafe(expr.span, InitializingTypeWith),
624                }
625            }
626            ExprKind::Closure(box ClosureExpr {
627                closure_id,
628                args: _,
629                upvars: _,
630                movability: _,
631                fake_reads: _,
632            }) => {
633                self.visit_inner_body(closure_id);
634            }
635            ExprKind::ConstBlock { did, args: _ } => {
636                let def_id = did.expect_local();
637                self.visit_inner_body(def_id);
638            }
639            ExprKind::Field { lhs, variant_index, name } => {
640                let lhs = &self.thir[lhs];
641                if let ty::Adt(adt_def, _) = lhs.ty.kind() {
642                    if adt_def.variant(variant_index).fields[name].safety.is_unsafe() {
643                        self.requires_unsafe(expr.span, UseOfUnsafeField);
644                    } else if adt_def.is_union() {
645                        if let Some(assigned_ty) = self.assignment_info {
646                            if assigned_ty.needs_drop(self.tcx, self.typing_env) {
647                                // This would be unsafe, but should be outright impossible since we
648                                // reject such unions.
649                                assert!(
650                                    self.tcx.dcx().has_errors().is_some(),
651                                    "union fields that need dropping should be impossible: {assigned_ty}"
652                                );
653                            }
654                        } else {
655                            self.requires_unsafe(expr.span, AccessToUnionField);
656                        }
657                    }
658                }
659            }
660            ExprKind::Assign { lhs, rhs } | ExprKind::AssignOp { lhs, rhs, .. } => {
661                let lhs = &self.thir[lhs];
662                // First, check whether we are mutating a layout constrained field
663                let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
664                visit::walk_expr(&mut visitor, lhs);
665                if visitor.found {
666                    self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
667                }
668
669                // Second, check for accesses to union fields. Don't have any
670                // special handling for AssignOp since it causes a read *and*
671                // write to lhs.
672                if matches!(expr.kind, ExprKind::Assign { .. }) {
673                    self.assignment_info = Some(lhs.ty);
674                    visit::walk_expr(self, lhs);
675                    self.assignment_info = None;
676                    visit::walk_expr(self, &self.thir()[rhs]);
677                    return; // We have already visited everything by now.
678                }
679            }
680            ExprKind::Borrow { borrow_kind, arg } => {
681                let mut visitor = LayoutConstrainedPlaceVisitor::new(self.thir, self.tcx);
682                visit::walk_expr(&mut visitor, expr);
683                if visitor.found {
684                    match borrow_kind {
685                        BorrowKind::Fake(_) | BorrowKind::Shared
686                            if !self.thir[arg].ty.is_freeze(self.tcx, self.typing_env) =>
687                        {
688                            self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
689                        }
690                        BorrowKind::Mut { .. } => {
691                            self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
692                        }
693                        BorrowKind::Fake(_) | BorrowKind::Shared => {}
694                    }
695                }
696            }
697            ExprKind::PlaceUnwrapUnsafeBinder { .. }
698            | ExprKind::ValueUnwrapUnsafeBinder { .. }
699            | ExprKind::WrapUnsafeBinder { .. } => {
700                self.requires_unsafe(expr.span, UnsafeBinderCast);
701            }
702            _ => {}
703        }
704        visit::walk_expr(self, expr);
705    }
706}
707
708#[derive(Clone)]
709enum SafetyContext {
710    Safe,
711    BuiltinUnsafeBlock,
712    UnsafeFn,
713    UnsafeBlock { span: Span, hir_id: HirId, used: bool, nested_used_blocks: Vec<NestedUsedBlock> },
714}
715
716#[derive(Clone, Copy)]
717struct NestedUsedBlock {
718    hir_id: HirId,
719    span: Span,
720}
721
722struct UnusedUnsafeWarning {
723    hir_id: HirId,
724    block_span: Span,
725    enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
726}
727
728#[derive(Clone, PartialEq)]
729enum UnsafeOpKind {
730    CallToUnsafeFunction(Option<DefId>),
731    UseOfInlineAssembly,
732    InitializingTypeWith,
733    InitializingTypeWithUnsafeField,
734    UseOfMutableStatic,
735    UseOfExternStatic,
736    UseOfUnsafeField,
737    DerefOfRawPointer,
738    AccessToUnionField,
739    MutationOfLayoutConstrainedField,
740    BorrowOfLayoutConstrainedField,
741    CallToFunctionWith {
742        function: DefId,
743        /// Target features enabled in callee's `#[target_feature]` but missing in
744        /// caller's `#[target_feature]`.
745        missing: Vec<Symbol>,
746        /// Target features in `missing` that are enabled at compile time
747        /// (e.g., with `-C target-feature`).
748        build_enabled: Vec<Symbol>,
749    },
750    UnsafeBinderCast,
751}
752
753use UnsafeOpKind::*;
754
755impl UnsafeOpKind {
756    fn emit_unsafe_op_in_unsafe_fn_lint(
757        &self,
758        tcx: TyCtxt<'_>,
759        hir_id: HirId,
760        span: Span,
761        suggest_unsafe_block: bool,
762    ) {
763        if tcx.hir_opt_delegation_sig_id(hir_id.owner.def_id).is_some() {
764            // The body of the delegation item is synthesized, so it makes no sense
765            // to emit this lint.
766            return;
767        }
768        let parent_id = tcx.hir_get_parent_item(hir_id);
769        let parent_owner = tcx.hir_owner_node(parent_id);
770        let should_suggest = parent_owner.fn_sig().is_some_and(|sig| {
771            // Do not suggest for safe target_feature functions
772            matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
773        });
774        let unsafe_not_inherited_note = if should_suggest {
775            suggest_unsafe_block.then(|| {
776                let body_span = tcx.hir_body(parent_owner.body_id().unwrap()).value.span;
777                UnsafeNotInheritedLintNote {
778                    signature_span: tcx.def_span(parent_id.def_id),
779                    body_span,
780                }
781            })
782        } else {
783            None
784        };
785        // FIXME: ideally we would want to trim the def paths, but this is not
786        // feasible with the current lint emission API (see issue #106126).
787        match self {
788            CallToUnsafeFunction(Some(did)) => tcx.emit_node_span_lint(
789                UNSAFE_OP_IN_UNSAFE_FN,
790                hir_id,
791                span,
792                UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
793                    span,
794                    function: with_no_trimmed_paths!(tcx.def_path_str(*did)),
795                    unsafe_not_inherited_note,
796                },
797            ),
798            CallToUnsafeFunction(None) => tcx.emit_node_span_lint(
799                UNSAFE_OP_IN_UNSAFE_FN,
800                hir_id,
801                span,
802                UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
803                    span,
804                    unsafe_not_inherited_note,
805                },
806            ),
807            UseOfInlineAssembly => tcx.emit_node_span_lint(
808                UNSAFE_OP_IN_UNSAFE_FN,
809                hir_id,
810                span,
811                UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
812                    span,
813                    unsafe_not_inherited_note,
814                },
815            ),
816            InitializingTypeWith => tcx.emit_node_span_lint(
817                UNSAFE_OP_IN_UNSAFE_FN,
818                hir_id,
819                span,
820                UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
821                    span,
822                    unsafe_not_inherited_note,
823                },
824            ),
825            InitializingTypeWithUnsafeField => tcx.emit_node_span_lint(
826                UNSAFE_OP_IN_UNSAFE_FN,
827                hir_id,
828                span,
829                UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe {
830                    span,
831                    unsafe_not_inherited_note,
832                },
833            ),
834            UseOfMutableStatic => tcx.emit_node_span_lint(
835                UNSAFE_OP_IN_UNSAFE_FN,
836                hir_id,
837                span,
838                UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
839                    span,
840                    unsafe_not_inherited_note,
841                },
842            ),
843            UseOfExternStatic => tcx.emit_node_span_lint(
844                UNSAFE_OP_IN_UNSAFE_FN,
845                hir_id,
846                span,
847                UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
848                    span,
849                    unsafe_not_inherited_note,
850                },
851            ),
852            UseOfUnsafeField => tcx.emit_node_span_lint(
853                UNSAFE_OP_IN_UNSAFE_FN,
854                hir_id,
855                span,
856                UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe {
857                    span,
858                    unsafe_not_inherited_note,
859                },
860            ),
861            DerefOfRawPointer => tcx.emit_node_span_lint(
862                UNSAFE_OP_IN_UNSAFE_FN,
863                hir_id,
864                span,
865                UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
866                    span,
867                    unsafe_not_inherited_note,
868                },
869            ),
870            AccessToUnionField => tcx.emit_node_span_lint(
871                UNSAFE_OP_IN_UNSAFE_FN,
872                hir_id,
873                span,
874                UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
875                    span,
876                    unsafe_not_inherited_note,
877                },
878            ),
879            MutationOfLayoutConstrainedField => tcx.emit_node_span_lint(
880                UNSAFE_OP_IN_UNSAFE_FN,
881                hir_id,
882                span,
883                UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
884                    span,
885                    unsafe_not_inherited_note,
886                },
887            ),
888            BorrowOfLayoutConstrainedField => tcx.emit_node_span_lint(
889                UNSAFE_OP_IN_UNSAFE_FN,
890                hir_id,
891                span,
892                UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
893                    span,
894                    unsafe_not_inherited_note,
895                },
896            ),
897            CallToFunctionWith { function, missing, build_enabled } => tcx.emit_node_span_lint(
898                UNSAFE_OP_IN_UNSAFE_FN,
899                hir_id,
900                span,
901                UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
902                    span,
903                    function: with_no_trimmed_paths!(tcx.def_path_str(*function)),
904                    missing_target_features: DiagArgValue::StrListSepByAnd(
905                        missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
906                    ),
907                    missing_target_features_count: missing.len(),
908                    note: !build_enabled.is_empty(),
909                    build_target_features: DiagArgValue::StrListSepByAnd(
910                        build_enabled
911                            .iter()
912                            .map(|feature| Cow::from(feature.to_string()))
913                            .collect(),
914                    ),
915                    build_target_features_count: build_enabled.len(),
916                    unsafe_not_inherited_note,
917                },
918            ),
919            UnsafeBinderCast => tcx.emit_node_span_lint(
920                UNSAFE_OP_IN_UNSAFE_FN,
921                hir_id,
922                span,
923                UnsafeOpInUnsafeFnUnsafeBinderCastRequiresUnsafe {
924                    span,
925                    unsafe_not_inherited_note,
926                },
927            ),
928        }
929    }
930
931    fn emit_requires_unsafe_err(
932        &self,
933        tcx: TyCtxt<'_>,
934        span: Span,
935        hir_context: HirId,
936        unsafe_op_in_unsafe_fn_allowed: bool,
937    ) {
938        let note_non_inherited = tcx.hir_parent_iter(hir_context).find(|(id, node)| {
939            if let hir::Node::Expr(block) = node
940                && let hir::ExprKind::Block(block, _) = block.kind
941                && let hir::BlockCheckMode::UnsafeBlock(_) = block.rules
942            {
943                true
944            } else if let Some(sig) = tcx.hir_fn_sig_by_hir_id(*id)
945                && matches!(sig.header.safety, hir::HeaderSafety::Normal(hir::Safety::Unsafe))
946            {
947                true
948            } else {
949                false
950            }
951        });
952        let unsafe_not_inherited_note = if let Some((id, _)) = note_non_inherited {
953            let span = tcx.hir_span(id);
954            let span = tcx.sess.source_map().guess_head_span(span);
955            Some(UnsafeNotInheritedNote { span })
956        } else {
957            None
958        };
959
960        let dcx = tcx.dcx();
961        match self {
962            CallToUnsafeFunction(Some(did)) if unsafe_op_in_unsafe_fn_allowed => {
963                dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
964                    span,
965                    unsafe_not_inherited_note,
966                    function: tcx.def_path_str(*did),
967                });
968            }
969            CallToUnsafeFunction(Some(did)) => {
970                dcx.emit_err(CallToUnsafeFunctionRequiresUnsafe {
971                    span,
972                    unsafe_not_inherited_note,
973                    function: tcx.def_path_str(*did),
974                });
975            }
976            CallToUnsafeFunction(None) if unsafe_op_in_unsafe_fn_allowed => {
977                dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed {
978                    span,
979                    unsafe_not_inherited_note,
980                });
981            }
982            CallToUnsafeFunction(None) => {
983                dcx.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless {
984                    span,
985                    unsafe_not_inherited_note,
986                });
987            }
988            UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
989                dcx.emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
990                    span,
991                    unsafe_not_inherited_note,
992                });
993            }
994            UseOfInlineAssembly => {
995                dcx.emit_err(UseOfInlineAssemblyRequiresUnsafe { span, unsafe_not_inherited_note });
996            }
997            InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => {
998                dcx.emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
999                    span,
1000                    unsafe_not_inherited_note,
1001                });
1002            }
1003            InitializingTypeWith => {
1004                dcx.emit_err(InitializingTypeWithRequiresUnsafe {
1005                    span,
1006                    unsafe_not_inherited_note,
1007                });
1008            }
1009            InitializingTypeWithUnsafeField if unsafe_op_in_unsafe_fn_allowed => {
1010                dcx.emit_err(
1011                    InitializingTypeWithUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1012                        span,
1013                        unsafe_not_inherited_note,
1014                    },
1015                );
1016            }
1017            InitializingTypeWithUnsafeField => {
1018                dcx.emit_err(InitializingTypeWithUnsafeFieldRequiresUnsafe {
1019                    span,
1020                    unsafe_not_inherited_note,
1021                });
1022            }
1023            UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
1024                dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1025                    span,
1026                    unsafe_not_inherited_note,
1027                });
1028            }
1029            UseOfMutableStatic => {
1030                dcx.emit_err(UseOfMutableStaticRequiresUnsafe { span, unsafe_not_inherited_note });
1031            }
1032            UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
1033                dcx.emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1034                    span,
1035                    unsafe_not_inherited_note,
1036                });
1037            }
1038            UseOfExternStatic => {
1039                dcx.emit_err(UseOfExternStaticRequiresUnsafe { span, unsafe_not_inherited_note });
1040            }
1041            UseOfUnsafeField if unsafe_op_in_unsafe_fn_allowed => {
1042                dcx.emit_err(UseOfUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1043                    span,
1044                    unsafe_not_inherited_note,
1045                });
1046            }
1047            UseOfUnsafeField => {
1048                dcx.emit_err(UseOfUnsafeFieldRequiresUnsafe { span, unsafe_not_inherited_note });
1049            }
1050            DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
1051                dcx.emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1052                    span,
1053                    unsafe_not_inherited_note,
1054                });
1055            }
1056            DerefOfRawPointer => {
1057                dcx.emit_err(DerefOfRawPointerRequiresUnsafe { span, unsafe_not_inherited_note });
1058            }
1059            AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
1060                dcx.emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1061                    span,
1062                    unsafe_not_inherited_note,
1063                });
1064            }
1065            AccessToUnionField => {
1066                dcx.emit_err(AccessToUnionFieldRequiresUnsafe { span, unsafe_not_inherited_note });
1067            }
1068            MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
1069                dcx.emit_err(
1070                    MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1071                        span,
1072                        unsafe_not_inherited_note,
1073                    },
1074                );
1075            }
1076            MutationOfLayoutConstrainedField => {
1077                dcx.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe {
1078                    span,
1079                    unsafe_not_inherited_note,
1080                });
1081            }
1082            BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
1083                dcx.emit_err(
1084                    BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1085                        span,
1086                        unsafe_not_inherited_note,
1087                    },
1088                );
1089            }
1090            BorrowOfLayoutConstrainedField => {
1091                dcx.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe {
1092                    span,
1093                    unsafe_not_inherited_note,
1094                });
1095            }
1096            CallToFunctionWith { function, missing, build_enabled }
1097                if unsafe_op_in_unsafe_fn_allowed =>
1098            {
1099                dcx.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1100                    span,
1101                    missing_target_features: DiagArgValue::StrListSepByAnd(
1102                        missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
1103                    ),
1104                    missing_target_features_count: missing.len(),
1105                    note: !build_enabled.is_empty(),
1106                    build_target_features: DiagArgValue::StrListSepByAnd(
1107                        build_enabled
1108                            .iter()
1109                            .map(|feature| Cow::from(feature.to_string()))
1110                            .collect(),
1111                    ),
1112                    build_target_features_count: build_enabled.len(),
1113                    unsafe_not_inherited_note,
1114                    function: tcx.def_path_str(*function),
1115                });
1116            }
1117            CallToFunctionWith { function, missing, build_enabled } => {
1118                dcx.emit_err(CallToFunctionWithRequiresUnsafe {
1119                    span,
1120                    missing_target_features: DiagArgValue::StrListSepByAnd(
1121                        missing.iter().map(|feature| Cow::from(feature.to_string())).collect(),
1122                    ),
1123                    missing_target_features_count: missing.len(),
1124                    note: !build_enabled.is_empty(),
1125                    build_target_features: DiagArgValue::StrListSepByAnd(
1126                        build_enabled
1127                            .iter()
1128                            .map(|feature| Cow::from(feature.to_string()))
1129                            .collect(),
1130                    ),
1131                    build_target_features_count: build_enabled.len(),
1132                    unsafe_not_inherited_note,
1133                    function: tcx.def_path_str(*function),
1134                });
1135            }
1136            UnsafeBinderCast if unsafe_op_in_unsafe_fn_allowed => {
1137                dcx.emit_err(UnsafeBinderCastRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
1138                    span,
1139                    unsafe_not_inherited_note,
1140                });
1141            }
1142            UnsafeBinderCast => {
1143                dcx.emit_err(UnsafeBinderCastRequiresUnsafe { span, unsafe_not_inherited_note });
1144            }
1145        }
1146    }
1147}
1148
1149pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
1150    // Closures and inline consts are handled by their owner, if it has a body
1151    assert!(!tcx.is_typeck_child(def.to_def_id()));
1152    // Also, don't safety check custom MIR
1153    if tcx.has_attr(def, sym::custom_mir) {
1154        return;
1155    }
1156
1157    let Ok((thir, expr)) = tcx.thir_body(def) else { return };
1158    // Runs all other queries that depend on THIR.
1159    tcx.ensure_done().mir_built(def);
1160    let thir = &thir.steal();
1161
1162    let hir_id = tcx.local_def_id_to_hir_id(def);
1163    let safety_context = tcx.hir_fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
1164        match fn_sig.header.safety {
1165            // We typeck the body as safe, but otherwise treat it as unsafe everywhere else.
1166            // Call sites to other SafeTargetFeatures functions are checked explicitly and don't need
1167            // to care about safety of the body.
1168            hir::HeaderSafety::SafeTargetFeatures => SafetyContext::Safe,
1169            hir::HeaderSafety::Normal(safety) => match safety {
1170                hir::Safety::Unsafe => SafetyContext::UnsafeFn,
1171                hir::Safety::Safe => SafetyContext::Safe,
1172            },
1173        }
1174    });
1175    let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
1176    let mut warnings = Vec::new();
1177    let mut visitor = UnsafetyVisitor {
1178        tcx,
1179        thir,
1180        safety_context,
1181        hir_context: hir_id,
1182        body_target_features,
1183        assignment_info: None,
1184        in_union_destructure: false,
1185        // FIXME(#132279): we're clearly in a body here.
1186        typing_env: ty::TypingEnv::non_body_analysis(tcx, def),
1187        inside_adt: false,
1188        warnings: &mut warnings,
1189        suggest_unsafe_block: true,
1190    };
1191    // params in THIR may be unsafe, e.g. a union pattern.
1192    for param in &thir.params {
1193        if let Some(param_pat) = param.pat.as_deref() {
1194            visitor.visit_pat(param_pat);
1195        }
1196    }
1197    // Visit the body.
1198    visitor.visit_expr(&thir[expr]);
1199
1200    warnings.sort_by_key(|w| w.block_span);
1201    for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings {
1202        let block_span = tcx.sess.source_map().guess_head_span(block_span);
1203        tcx.emit_node_span_lint(
1204            UNUSED_UNSAFE,
1205            hir_id,
1206            block_span,
1207            UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
1208        );
1209    }
1210}