//! Unsafety checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir::{LangItem, Safety}; use rustc_middle::ty::ImplPolarity::*; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{ImplTraitHeader, TraitDef, TyCtxt}; use rustc_span::ErrorGuaranteed; use rustc_span::def_id::LocalDefId; pub(super) fn check_item( tcx: TyCtxt<'_>, def_id: LocalDefId, trait_header: ImplTraitHeader<'_>, trait_def: &TraitDef, ) -> Result<(), ErrorGuaranteed> { let unsafe_attr = tcx.generics_of(def_id).own_params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); let trait_ref = trait_header.trait_ref.instantiate_identity(); let is_copy = tcx.is_lang_item(trait_def.def_id, LangItem::Copy); let trait_def_safety = if is_copy { // If `Self` has unsafe fields, `Copy` is unsafe to implement. if trait_header.trait_ref.skip_binder().self_ty().has_unsafe_fields() { rustc_hir::Safety::Unsafe } else { rustc_hir::Safety::Safe } } else { trait_def.safety }; match (trait_def_safety, unsafe_attr, trait_header.safety, trait_header.polarity) { (Safety::Safe, None, Safety::Unsafe, Positive | Reservation) => { let span = tcx.def_span(def_id); return Err(struct_span_code_err!( tcx.dcx(), tcx.def_span(def_id), E0199, "implementing the trait `{}` is not unsafe", trait_ref.print_trait_sugared() ) .with_span_suggestion_verbose( span.with_hi(span.lo() + rustc_span::BytePos(7)), "remove `unsafe` from this trait implementation", "", rustc_errors::Applicability::MachineApplicable, ) .emit()); } (Safety::Unsafe, _, Safety::Safe, Positive | Reservation) => { let span = tcx.def_span(def_id); return Err(struct_span_code_err!( tcx.dcx(), span, E0200, "the trait `{}` requires an `unsafe impl` declaration", trait_ref.print_trait_sugared() ) .with_note(if is_copy { format!( "the trait `{}` cannot be safely implemented for `{}` \ because it has unsafe fields. Review the invariants \ of those fields before adding an `unsafe impl`", trait_ref.print_trait_sugared(), trait_ref.self_ty(), ) } else { format!( "the trait `{}` enforces invariants that the compiler can't check. \ Review the trait documentation and make sure this implementation \ upholds those invariants before adding the `unsafe` keyword", trait_ref.print_trait_sugared() ) }) .with_span_suggestion_verbose( span.shrink_to_lo(), "add `unsafe` to this trait implementation", "unsafe ", rustc_errors::Applicability::MaybeIncorrect, ) .emit()); } (Safety::Safe, Some(attr_name), Safety::Safe, Positive | Reservation) => { let span = tcx.def_span(def_id); return Err(struct_span_code_err!( tcx.dcx(), span, E0569, "requires an `unsafe impl` declaration due to `#[{}]` attribute", attr_name ) .with_note(format!( "the trait `{}` enforces invariants that the compiler can't check. \ Review the trait documentation and make sure this implementation \ upholds those invariants before adding the `unsafe` keyword", trait_ref.print_trait_sugared() )) .with_span_suggestion_verbose( span.shrink_to_lo(), "add `unsafe` to this trait implementation", "unsafe ", rustc_errors::Applicability::MaybeIncorrect, ) .emit()); } (_, _, Safety::Unsafe, Negative) => { // Reported in AST validation assert!(tcx.dcx().has_errors().is_some(), "unsafe negative impl"); Ok(()) } (_, _, Safety::Safe, Negative) | (Safety::Unsafe, _, Safety::Unsafe, Positive | Reservation) | (Safety::Safe, Some(_), Safety::Unsafe, Positive | Reservation) | (Safety::Safe, None, Safety::Safe, _) => Ok(()), } }