rustc_hir_analysis/coherence/
unsafety.rs

1//! Unsafety checker: every impl either implements a trait defined in this
2//! crate or pertains to a type defined in this crate.
3
4use rustc_errors::codes::*;
5use rustc_errors::struct_span_code_err;
6use rustc_hir::{LangItem, Safety};
7use rustc_middle::ty::ImplPolarity::*;
8use rustc_middle::ty::print::PrintTraitRefExt as _;
9use rustc_middle::ty::{ImplTraitHeader, TraitDef, TyCtxt};
10use rustc_span::ErrorGuaranteed;
11use rustc_span::def_id::LocalDefId;
12
13pub(super) fn check_item(
14    tcx: TyCtxt<'_>,
15    def_id: LocalDefId,
16    trait_header: ImplTraitHeader<'_>,
17    trait_def: &TraitDef,
18) -> Result<(), ErrorGuaranteed> {
19    let unsafe_attr =
20        tcx.generics_of(def_id).own_params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
21    let trait_ref = trait_header.trait_ref.instantiate_identity();
22
23    let is_copy = tcx.is_lang_item(trait_def.def_id, LangItem::Copy);
24    let trait_def_safety = if is_copy {
25        // If `Self` has unsafe fields, `Copy` is unsafe to implement.
26        if trait_header.trait_ref.skip_binder().self_ty().has_unsafe_fields() {
27            rustc_hir::Safety::Unsafe
28        } else {
29            rustc_hir::Safety::Safe
30        }
31    } else {
32        trait_def.safety
33    };
34
35    match (trait_def_safety, unsafe_attr, trait_header.safety, trait_header.polarity) {
36        (Safety::Safe, None, Safety::Unsafe, Positive | Reservation) => {
37            let span = tcx.def_span(def_id);
38            return Err(struct_span_code_err!(
39                tcx.dcx(),
40                tcx.def_span(def_id),
41                E0199,
42                "implementing the trait `{}` is not unsafe",
43                trait_ref.print_trait_sugared()
44            )
45            .with_span_suggestion_verbose(
46                span.with_hi(span.lo() + rustc_span::BytePos(7)),
47                "remove `unsafe` from this trait implementation",
48                "",
49                rustc_errors::Applicability::MachineApplicable,
50            )
51            .emit());
52        }
53
54        (Safety::Unsafe, _, Safety::Safe, Positive | Reservation) => {
55            let span = tcx.def_span(def_id);
56            return Err(struct_span_code_err!(
57                tcx.dcx(),
58                span,
59                E0200,
60                "the trait `{}` requires an `unsafe impl` declaration",
61                trait_ref.print_trait_sugared()
62            )
63            .with_note(if is_copy {
64                format!(
65                    "the trait `{}` cannot be safely implemented for `{}` \
66                        because it has unsafe fields. Review the invariants \
67                        of those fields before adding an `unsafe impl`",
68                    trait_ref.print_trait_sugared(),
69                    trait_ref.self_ty(),
70                )
71            } else {
72                format!(
73                    "the trait `{}` enforces invariants that the compiler can't check. \
74                        Review the trait documentation and make sure this implementation \
75                        upholds those invariants before adding the `unsafe` keyword",
76                    trait_ref.print_trait_sugared()
77                )
78            })
79            .with_span_suggestion_verbose(
80                span.shrink_to_lo(),
81                "add `unsafe` to this trait implementation",
82                "unsafe ",
83                rustc_errors::Applicability::MaybeIncorrect,
84            )
85            .emit());
86        }
87
88        (Safety::Safe, Some(attr_name), Safety::Safe, Positive | Reservation) => {
89            let span = tcx.def_span(def_id);
90            return Err(struct_span_code_err!(
91                tcx.dcx(),
92                span,
93                E0569,
94                "requires an `unsafe impl` declaration due to `#[{}]` attribute",
95                attr_name
96            )
97            .with_note(format!(
98                "the trait `{}` enforces invariants that the compiler can't check. \
99                    Review the trait documentation and make sure this implementation \
100                    upholds those invariants before adding the `unsafe` keyword",
101                trait_ref.print_trait_sugared()
102            ))
103            .with_span_suggestion_verbose(
104                span.shrink_to_lo(),
105                "add `unsafe` to this trait implementation",
106                "unsafe ",
107                rustc_errors::Applicability::MaybeIncorrect,
108            )
109            .emit());
110        }
111
112        (_, _, Safety::Unsafe, Negative) => {
113            // Reported in AST validation
114            assert!(tcx.dcx().has_errors().is_some(), "unsafe negative impl");
115            Ok(())
116        }
117        (_, _, Safety::Safe, Negative)
118        | (Safety::Unsafe, _, Safety::Unsafe, Positive | Reservation)
119        | (Safety::Safe, Some(_), Safety::Unsafe, Positive | Reservation)
120        | (Safety::Safe, None, Safety::Safe, _) => Ok(()),
121    }
122}