authorbors <bors@rust-lang.org> 2026-06-29 09:55:40 UTC
committerbors <bors@rust-lang.org> 2026-06-29 09:55:40 UTC
log7dc2c162b9c197aaa76a6f9e7534569537830a01
tree2489a612529b3f73fc837cb708851a82436d17eb
parent63f05e3635171e7ac3f9ca78bad6c71052cda5a3
parentd0cdf757bf925b025cada614369ea1b4f0ab8f94

Auto merge of #146275 - niacdoial:improperctypes-refactor3, r=jdonszelmann

lint ImproperCTypes: refactor linting architecture (part 3) This is the third PR in an effort to split https://github.com/rust-lang/rust/pull/134697 (refactor plus overhaul of the ImproperCTypes family of lints) into individually-mergeable parts. Contains: - the changes of the first two PRs - other user-invisible changes, - the prevention of stack overflows while checking irregular recursive types. Fixes: https://github.com/rust-lang/rust/issues/130310 Superset of: https://github.com/rust-lang/rust/pull/146271 and its superset rust-lang/rust#146273

5 files changed, 76 insertions(+), 27 deletions(-)

compiler/rustc_lint/src/types/improper_ctypes.rs+30-9
......@@ -358,6 +358,8 @@ struct VisitorState {
358358 /// Flags describing both the immediate context in which the current Ty is,
359359 /// linked to how it relates to its parent Ty (or lack thereof).
360360 outer_ty_kind: OuterTyKind,
361 /// Type recursion depth, to prevent infinite recursion
362 depth: usize,
361363}
362364
363365impl RootUseFlags {
......@@ -385,6 +387,7 @@ impl VisitorState {
385387 VisitorState {
386388 root_use_flags: self.root_use_flags,
387389 outer_ty_kind: OuterTyKind::from_ty(current_ty),
390 depth: self.depth + 1,
388391 }
389392 }
390393
......@@ -399,6 +402,7 @@ impl VisitorState {
399402 FnPos::Arg => RootUseFlags::ARGUMENT_TY_IN_FNPTR,
400403 },
401404 outer_ty_kind: OuterTyKind::from_ty(current_ty),
405 depth: self.depth + 1,
402406 }
403407 }
404408
......@@ -410,12 +414,16 @@ impl VisitorState {
410414 (CItemKind::Definition, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DEFINITION,
411415 (CItemKind::Declaration, FnPos::Arg) => RootUseFlags::ARGUMENT_TY_IN_DECLARATION,
412416 };
413 VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None }
417 VisitorState { root_use_flags: p_flags, outer_ty_kind: OuterTyKind::None, depth: 0 }
414418 }
415419
416420 /// Get the proper visitor state for a static variable's type
417421 fn static_entry_point() -> Self {
418 VisitorState { root_use_flags: RootUseFlags::STATIC_TY, outer_ty_kind: OuterTyKind::None }
422 VisitorState {
423 root_use_flags: RootUseFlags::STATIC_TY,
424 outer_ty_kind: OuterTyKind::None,
425 depth: 0,
426 }
419427 }
420428
421429 /// Whether the type is used in a function.
......@@ -737,9 +745,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
737745
738746 // Protect against infinite recursion, for example
739747 // `struct S(*mut S);`.
740 // FIXME: A recursion limit is necessary as well, for irregular
741 // recursive types.
742 if !self.cache.insert(ty) {
748 if !(self.cache.insert(ty) && self.cx.tcx.recursion_limit().value_within_limit(state.depth))
749 {
743750 return FfiSafe;
744751 }
745752
......@@ -994,6 +1001,8 @@ impl<'tcx> ImproperCTypesLint {
9941001 fn_mode: CItemKind,
9951002 ) {
9961003 struct FnPtrFinder<'tcx> {
1004 current_depth: usize,
1005 depths: Vec<usize>,
9971006 spans: Vec<Span>,
9981007 tys: Vec<Ty<'tcx>>,
9991008 }
......@@ -1001,13 +1010,16 @@ impl<'tcx> ImproperCTypesLint {
10011010 impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> {
10021011 fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
10031012 debug!(?ty);
1013 self.current_depth += 1;
10041014 if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind
10051015 && !abi.is_rustic_abi()
10061016 {
1017 self.depths.push(self.current_depth);
10071018 self.spans.push(ty.span);
10081019 }
10091020
10101021 hir::intravisit::walk_ty(self, ty);
1022 self.current_depth -= 1;
10111023 }
10121024 }
10131025
......@@ -1025,16 +1037,25 @@ impl<'tcx> ImproperCTypesLint {
10251037 }
10261038 }
10271039
1028 let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() };
1040 let mut visitor = FnPtrFinder {
1041 spans: Vec::new(),
1042 tys: Vec::new(),
1043 depths: Vec::new(),
1044 current_depth: 0,
1045 };
10291046 ty.visit_with(&mut visitor);
10301047 visitor.visit_ty_unambig(hir_ty);
10311048
1032 let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
1033 for (fn_ptr_ty, span) in all_types {
1049 let all_types = iter::zip(
1050 visitor.depths.drain(..),
1051 iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)),
1052 );
1053 for (depth, (fn_ptr_ty, span)) in all_types {
10341054 let fn_ptr_ty = Unnormalized::new_wip(fn_ptr_ty);
10351055 let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
1056 let bridge_state = VisitorState { depth, ..state };
10361057 // FIXME(ctypes): make a check_for_fnptr
1037 let ffi_res = visitor.check_type(state, fn_ptr_ty);
1058 let ffi_res = visitor.check_type(bridge_state, fn_ptr_ty);
10381059
10391060 self.process_ffi_result(cx, span, ffi_res, fn_mode);
10401061 }
tests/crashes/130310.rs deleted-15
......@@ -1,15 +0,0 @@
1//@ known-bug: rust-lang/rust#130310
2
3use std::marker::PhantomData;
4
5#[repr(C)]
6struct A<T> {
7 a: *const A<A<T>>,
8 p: PhantomData<T>,
9}
10
11extern "C" {
12 fn f(a: *const A<()>);
13}
14
15fn main() {}
tests/ui/lint/improper-ctypes/ice-irregular-recursive-types.rs created+21
......@@ -0,0 +1,21 @@
1//@ check-pass
2
3//! this test checks that irregular recursive types do not cause stack overflow in ImproperCTypes
4//! Issue: https://github.com/rust-lang/rust/issues/94223
5
6#![deny(improper_ctypes, improper_ctypes_definitions)]
7
8use std::marker::PhantomData;
9
10#[repr(C)]
11struct A<T> {
12 a: *const A<A<T>>, // without a recursion limit, checking this ends up creating checks for
13 // infinitely deep types the likes of `A<A<A<A<A<A<...>>>>>>`
14 p: PhantomData<T>,
15}
16
17extern "C" {
18 fn f(a: *const A<()>);
19}
20
21fn main() {}
tests/ui/lint/improper-ctypes/lint-non-recursion-limit.rs+9-3
......@@ -1,8 +1,10 @@
1//@ check-pass
1//! This test checks that the depth limit of the ImproperCTypes lints counts the depth
2//! of a type properly.
3//! Issue: https://github.com/rust-lang/rust/issues/130757
24
35#![recursion_limit = "5"]
46#![allow(unused)]
5#![deny(improper_ctypes)]
7#![deny(improper_ctypes_definitions)]
68
79#[repr(C)]
810struct F1(*const ());
......@@ -15,7 +17,7 @@ struct F4(*const ());
1517#[repr(C)]
1618struct F5(*const ());
1719#[repr(C)]
18struct F6(*const ());
20struct F6([char;8]); //oops!
1921
2022#[repr(C)]
2123struct B {
......@@ -24,9 +26,13 @@ struct B {
2426 f3: F3,
2527 f4: F4,
2628 f5: F5,
29 // If the depth counter misbehaves, it will believe `f6` is "too deep" without good reason.
30 // And in response, it will assume `B` is safe.
31 // so, the error linked to F6 means the test succeeds.
2732 f6: F6,
2833}
2934
3035extern "C" fn foo(_: B) {}
36//~^ ERROR: uses type `char`
3137
3238fn main() {}
tests/ui/lint/improper-ctypes/lint-non-recursion-limit.stderr created+16
......@@ -0,0 +1,16 @@
1error: `extern` fn uses type `char`, which is not FFI-safe
2 --> $DIR/lint-non-recursion-limit.rs:35:22
3 |
4LL | extern "C" fn foo(_: B) {}
5 | ^ not FFI-safe
6 |
7 = help: consider using `u32` or `libc::wchar_t` instead
8 = note: the `char` type has no C equivalent
9note: the lint level is defined here
10 --> $DIR/lint-non-recursion-limit.rs:7:9
11 |
12LL | #![deny(improper_ctypes_definitions)]
13 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
14
15error: aborting due to 1 previous error
16