Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4334,6 +4334,7 @@ dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_hashes",
"rustc_hir",
"rustc_index",
"rustc_infer",
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_lint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ late_lint_methods!(
ImplTraitOvercaptures: ImplTraitOvercaptures,
ImplicitAutorefs: ImplicitAutorefs,
ImplicitProvenanceCasts: ImplicitProvenanceCasts,
ImproperCTypesLint: ImproperCTypesLint,
ImproperCTypesLint: ImproperCTypesLint::default(),
ImproperGpuKernelLint: ImproperGpuKernelLint,
InteriorMutableConsts: InteriorMutableConsts,
InternalEqTraitMethodImpls: InternalEqTraitMethodImpls,
Expand Down
73 changes: 60 additions & 13 deletions compiler/rustc_lint/src/types/improper_ctypes.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use std::cell::RefCell;
use std::iter;
use std::ops::ControlFlow;

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,
Expand Down Expand Up @@ -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<FxHashSet<(Hash128, u8)>>,
}

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
Expand Down Expand Up @@ -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".
Expand All @@ -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)
});
}
}

Expand Down
Loading