diff --git a/Cargo.lock b/Cargo.lock index 5e57835c9cfef..5183eb88a437f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4334,6 +4334,7 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", + "rustc_hashes", "rustc_hir", "rustc_index", "rustc_infer", diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index a672f242449e4..5fc0b333eccad 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -14,6 +14,7 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } +rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 95033707cf110..9a91e14fbdcb0 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -238,7 +238,7 @@ late_lint_methods!( ImplTraitOvercaptures: ImplTraitOvercaptures, ImplicitAutorefs: ImplicitAutorefs, ImplicitProvenanceCasts: ImplicitProvenanceCasts, - ImproperCTypesLint: ImproperCTypesLint, + ImproperCTypesLint: ImproperCTypesLint::default(), ImproperGpuKernelLint: ImproperGpuKernelLint, InteriorMutableConsts: InteriorMutableConsts, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index e7fa0fe2960db..04b381261e428 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::iter; use std::ops::ControlFlow; @@ -5,10 +6,11 @@ use bitflags::bitflags; use rustc_abi::VariantIdx; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{DiagMessage, msg}; +use rustc_hashes::Hash128; use rustc_hir::def::CtorKind; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, AmbigArg}; -use rustc_lint_defs::{declare_lint, declare_lint_pass}; +use rustc_lint_defs::{declare_lint, impl_lint_pass}; use rustc_middle::ty::{ self, Adt, AdtDef, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, Unnormalized, @@ -131,7 +133,48 @@ declare_lint! { "Structs do not follow the power alignment rule under repr(C)" } -declare_lint_pass!(ImproperCTypesLint => [ +/// Remembers types we already checked and found FFI-safe, so we don't check the +/// same type again every time it shows up in another function. +/// We only remember "safe" types, never "unsafe" ones. That way we never need to +/// keep the actual type around, just a fingerprint of it. +#[derive(Default)] +pub(crate) struct ImproperCTypesLint { + /// A fingerprint of the type, not the type itself. Uses a big 128-bit + /// fingerprint (not a smaller 64-bit one) so two different types can't + /// accidentally get treated as the same one. + known_safe: RefCell>, +} + +impl<'tcx> ImproperCTypesLint { + fn cache_key(cx: &LateContext<'tcx>, ty: Ty<'tcx>, flags: RootUseFlags) -> (Hash128, u8) { + (cx.tcx.type_id_hash(ty), flags.bits()) + } + + fn check_ffi_type( + &self, + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + state: VisitorState, + span: Span, + mode: CItemKind, + check: impl FnOnce() -> FfiResult<'tcx>, + ) { + let key = Self::cache_key(cx, ty, state.root_use_flags); + if self.known_safe.borrow().contains(&key) { + return; + } + + let ffi_res = check(); + + if matches!(ffi_res, FfiResult::FfiSafe) { + self.known_safe.borrow_mut().insert(key); + } + + self.process_ffi_result(cx, span, ffi_res, mode); + } +} + +impl_lint_pass!(ImproperCTypesLint => [ IMPROPER_CTYPES, IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT @@ -1102,9 +1145,11 @@ impl<'tcx> ImproperCTypesLint { fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { let ty = cx.tcx.type_of(id).instantiate_identity(); - let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); - let ffi_res = visitor.check_type(VisitorState::static_entry_point(), ty); - self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration); + let state = VisitorState::static_entry_point(); + self.check_ffi_type(cx, ty.skip_norm_wip(), state, span, CItemKind::Declaration, || { + let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration); + visitor.check_type(state, ty) + }); } /// Check if a function's argument types and result type are "ffi-safe". @@ -1119,19 +1164,21 @@ impl<'tcx> ImproperCTypesLint { let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - let input_ty = Unnormalized::new_wip(*input_ty); let state = VisitorState::fn_entry_point(fn_mode, FnPos::Arg); - let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode); - let ffi_res = visitor.check_type(state, input_ty); - self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode); + self.check_ffi_type(cx, *input_ty, state, input_hir.span, fn_mode, || { + let input_ty = Unnormalized::new_wip(*input_ty); + let mut visitor = ImproperCTypesVisitor::new(cx, input_ty, fn_mode); + visitor.check_type(state, input_ty) + }); } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let output_ty = Unnormalized::new_wip(sig.output()); let state = VisitorState::fn_entry_point(fn_mode, FnPos::Ret); - let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode); - let ffi_res = visitor.check_type(state, output_ty); - self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); + self.check_ffi_type(cx, sig.output(), state, ret_hir.span, fn_mode, || { + let output_ty = Unnormalized::new_wip(sig.output()); + let mut visitor = ImproperCTypesVisitor::new(cx, output_ty, fn_mode); + visitor.check_type(state, output_ty) + }); } }