ImproperCTypes: Use more Unnormalized type wrappers

In order to follow along with the efforts to properly distinguish
already-normalised and unnormalized types, we separate the internal
interfaces of this lint that rely on normalized types from those that
do not. We do that by adding the `Unnormalized` wrapper to some
interfaces.
This commit is contained in:
niacdoial
2026-04-25 00:00:01 +02:00
parent 411a932b38
commit 6839797f18
@@ -138,17 +138,16 @@
USES_POWER_ALIGNMENT
]);
/// Getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple).
#[inline]
fn get_type_from_field<'tcx>(
/// A common pattern in this lint is to attempt normalize_erasing_regions,
/// but keep the original type if it were to fail.
/// This may or may not be supported in the logic behind the `Unnormalized` wrapper,
/// (FIXME?)
/// but it should be enough for non-wrapped types to be as normalised as this lint needs them to be.
fn maybe_normalize_erasing_regions<'tcx>(
cx: &LateContext<'tcx>,
field: &ty::FieldDef,
args: GenericArgsRef<'tcx>,
value: Unnormalized<'tcx, Ty<'tcx>>,
) -> Ty<'tcx> {
let field_ty = field.ty(cx.tcx, args);
cx.tcx
.try_normalize_erasing_regions(cx.typing_env(), Unnormalized::new_wip(field_ty))
.unwrap_or(field_ty)
cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value.skip_norm_wip())
}
/// Check a variant of a non-exhaustive enum for improper ctypes
@@ -464,8 +463,17 @@ struct ImproperCTypesVisitor<'a, 'tcx> {
}
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self {
ImproperCTypesVisitor { cx, base_ty, base_fn_mode, cache: FxHashSet::default() }
fn new(
cx: &'a LateContext<'tcx>,
base_ty: Unnormalized<'tcx, Ty<'tcx>>,
base_fn_mode: CItemKind,
) -> Self {
ImproperCTypesVisitor {
cx,
base_ty: maybe_normalize_erasing_regions(cx, base_ty),
base_fn_mode,
cache: FxHashSet::default(),
}
}
/// Checks if the given indirection (box,ref,pointer) is "ffi-safe".
@@ -554,7 +562,10 @@ fn visit_variant_fields(
let transparent_with_all_zst_fields = if def.repr().transparent() {
if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) {
// Transparent newtypes have at most one non-ZST field which needs to be checked..
let field_ty = get_type_from_field(self.cx, field, args);
let field_ty = maybe_normalize_erasing_regions(
self.cx,
Unnormalized::new_wip(field.ty(self.cx.tcx, args)),
);
match self.visit_type(state.next(ty), field_ty) {
FfiUnsafe { ty, .. } if ty.is_unit() => (),
r => return r,
@@ -573,7 +584,10 @@ fn visit_variant_fields(
// We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
let mut all_phantom = !variant.fields.is_empty();
for field in &variant.fields {
let field_ty = get_type_from_field(self.cx, field, args);
let field_ty = maybe_normalize_erasing_regions(
self.cx,
Unnormalized::new_wip(field.ty(self.cx.tcx, args)),
);
all_phantom &= match self.visit_type(state.next(ty), field_ty) {
FfiSafe => false,
// `()` fields are FFI-safe!
@@ -927,12 +941,12 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
})
}
fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
let ty = self
.cx
.tcx
.try_normalize_erasing_regions(self.cx.typing_env(), Unnormalized::new_wip(ty))
.unwrap_or(ty);
fn check_type(
&mut self,
state: VisitorState,
ty: Unnormalized<'tcx, Ty<'tcx>>,
) -> FfiResult<'tcx> {
let ty = maybe_normalize_erasing_regions(self.cx, ty);
if let Some(res) = self.visit_for_opaque_ty(ty) {
return res;
}
@@ -990,6 +1004,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
for (fn_ptr_ty, span) in all_types {
let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty);
let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
// FIXME(ctypes): make a check_for_fnptr
let ffi_res = visitor.check_type(state, fn_ptr_ty);
@@ -1039,7 +1054,7 @@ fn check_reprc_adt(
}
fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
let ty = cx.tcx.type_of(id).instantiate_identity().skip_norm_wip();
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);
@@ -1057,16 +1072,18 @@ fn check_foreign_fn(
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);
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);
}
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, sig.output(), fn_mode);
let ffi_res = visitor.check_type(state, sig.output());
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);
}
}