about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2025-02-12 06:07:37 +0100
committerGitHub <noreply@github.com>2025-02-12 06:07:37 +0100
commit2f3f83a4a34cf2c75143be939648ce77763b01db (patch)
treee1e63a6b5d2412b4c92f00531eaf6505778ccfd7
parent5ebacd1b3c70bd5481009dc283b9f9d723cbc303 (diff)
parentf1f996a4d590e3d75eb5c5dab5098b7ae2ebf7eb (diff)
downloadrust-2f3f83a4a34cf2c75143be939648ce77763b01db.tar.gz
rust-2f3f83a4a34cf2c75143be939648ce77763b01db.zip
Rollup merge of #136193 - oli-obk:pattern-type-ffi-checks, r=chenyukang
Implement pattern type ffi checks

Previously we just rejected pattern types outright in FFI, but that was never meant to be a permanent situation. We'll need them supported to use them as the building block for `NonZero` and `NonNull` after all (both of which are FFI safe).

best reviewed commit by commit.
-rw-r--r--compiler/rustc_lint/messages.ftl3
-rw-r--r--compiler/rustc_lint/src/foreign_modules.rs9
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_lint/src/types.rs148
-rw-r--r--tests/ui/lint/clashing-extern-fn.rs32
-rw-r--r--tests/ui/lint/clashing-extern-fn.stderr23
-rw-r--r--tests/ui/lint/lint-ctypes-enum.rs1
-rw-r--r--tests/ui/lint/lint-ctypes-enum.stderr49
8 files changed, 178 insertions, 88 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 55c6a122d35..480d97e377a 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -390,9 +390,6 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData`
 
 lint_improper_ctypes_opaque = opaque types have no C equivalent
 
-lint_improper_ctypes_pat_help = consider using the base type instead
-
-lint_improper_ctypes_pat_reason = pattern types have no C equivalent
 lint_improper_ctypes_slice_help = consider using a raw pointer instead
 
 lint_improper_ctypes_slice_reason = slices have no C equivalent
diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs
index 45b188205d2..636779fe9b4 100644
--- a/compiler/rustc_lint/src/foreign_modules.rs
+++ b/compiler/rustc_lint/src/foreign_modules.rs
@@ -241,10 +241,7 @@ fn structurally_same_type_impl<'tcx>(
             if let ty::Adt(def, args) = *ty.kind() {
                 let is_transparent = def.repr().transparent();
                 let is_non_null = types::nonnull_optimization_guaranteed(tcx, def);
-                debug!(
-                    "non_transparent_ty({:?}) -- type is transparent? {}, type is non-null? {}",
-                    ty, is_transparent, is_non_null
-                );
+                debug!(?ty, is_transparent, is_non_null);
                 if is_transparent && !is_non_null {
                     debug_assert_eq!(def.variants().len(), 1);
                     let v = &def.variant(FIRST_VARIANT);
@@ -378,14 +375,14 @@ fn structurally_same_type_impl<'tcx>(
 
                 // An Adt and a primitive or pointer type. This can be FFI-safe if non-null
                 // enum layout optimisation is being applied.
-                (Adt(..), _) if is_primitive_or_pointer(b) => {
+                (Adt(..) | Pat(..), _) if is_primitive_or_pointer(b) => {
                     if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, ckind) {
                         a_inner == b
                     } else {
                         false
                     }
                 }
-                (_, Adt(..)) if is_primitive_or_pointer(a) => {
+                (_, Adt(..) | Pat(..)) if is_primitive_or_pointer(a) => {
                     if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, ckind) {
                         b_inner == a
                     } else {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index d89e615e14a..c8de5e87753 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -33,6 +33,7 @@
 #![feature(let_chains)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
+#![feature(try_blocks)]
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
 
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 601d2fbfb67..68b1f435a4c 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -877,6 +877,37 @@ fn ty_is_known_nonnull<'tcx>(
                 .filter_map(|variant| transparent_newtype_field(tcx, variant))
                 .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode))
         }
+        ty::Pat(base, pat) => {
+            ty_is_known_nonnull(tcx, typing_env, *base, mode)
+                || Option::unwrap_or_default(
+                    try {
+                        match **pat {
+                            ty::PatternKind::Range { start, end, include_end } => {
+                                match (start, end) {
+                                    (Some(start), None) => {
+                                        start.try_to_value()?.try_to_bits(tcx, typing_env)? > 0
+                                    }
+                                    (Some(start), Some(end)) => {
+                                        let start =
+                                            start.try_to_value()?.try_to_bits(tcx, typing_env)?;
+                                        let end =
+                                            end.try_to_value()?.try_to_bits(tcx, typing_env)?;
+
+                                        if include_end {
+                                            // This also works for negative numbers, as we just need
+                                            // to ensure we aren't wrapping over zero.
+                                            start > 0 && end >= start
+                                        } else {
+                                            start > 0 && end > start
+                                        }
+                                    }
+                                    _ => false,
+                                }
+                            }
+                        }
+                    },
+                )
+        }
         _ => false,
     }
 }
@@ -907,9 +938,8 @@ fn get_nullable_type<'tcx>(
             };
             return get_nullable_type(tcx, typing_env, inner_field_ty);
         }
-        ty::Int(ty) => Ty::new_int(tcx, ty),
-        ty::Uint(ty) => Ty::new_uint(tcx, ty),
-        ty::RawPtr(ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl),
+        ty::Pat(base, ..) => return get_nullable_type(tcx, typing_env, base),
+        ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => ty,
         // As these types are always non-null, the nullable equivalent of
         // `Option<T>` of these types are their raw pointer counterparts.
         ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty, mutbl),
@@ -965,63 +995,69 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
     ckind: CItemKind,
 ) -> Option<Ty<'tcx>> {
     debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty);
-    if let ty::Adt(ty_def, args) = ty.kind() {
-        let field_ty = match &ty_def.variants().raw[..] {
-            [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
-                ([], [field]) | ([field], []) => field.ty(tcx, args),
-                ([field1], [field2]) => {
-                    let ty1 = field1.ty(tcx, args);
-                    let ty2 = field2.ty(tcx, args);
-
-                    if is_niche_optimization_candidate(tcx, typing_env, ty1) {
-                        ty2
-                    } else if is_niche_optimization_candidate(tcx, typing_env, ty2) {
-                        ty1
-                    } else {
-                        return None;
+    match ty.kind() {
+        ty::Adt(ty_def, args) => {
+            let field_ty = match &ty_def.variants().raw[..] {
+                [var_one, var_two] => match (&var_one.fields.raw[..], &var_two.fields.raw[..]) {
+                    ([], [field]) | ([field], []) => field.ty(tcx, args),
+                    ([field1], [field2]) => {
+                        let ty1 = field1.ty(tcx, args);
+                        let ty2 = field2.ty(tcx, args);
+
+                        if is_niche_optimization_candidate(tcx, typing_env, ty1) {
+                            ty2
+                        } else if is_niche_optimization_candidate(tcx, typing_env, ty2) {
+                            ty1
+                        } else {
+                            return None;
+                        }
                     }
-                }
+                    _ => return None,
+                },
                 _ => return None,
-            },
-            _ => return None,
-        };
+            };
 
-        if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) {
-            return None;
-        }
+            if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) {
+                return None;
+            }
 
-        // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
-        // If the computed size for the field and the enum are different, the nonnull optimization isn't
-        // being applied (and we've got a problem somewhere).
-        let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok();
-        if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
-            bug!("improper_ctypes: Option nonnull optimization not applied?");
-        }
+            // At this point, the field's type is known to be nonnull and the parent enum is Option-like.
+            // If the computed size for the field and the enum are different, the nonnull optimization isn't
+            // being applied (and we've got a problem somewhere).
+            let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok();
+            if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
+                bug!("improper_ctypes: Option nonnull optimization not applied?");
+            }
 
-        // Return the nullable type this Option-like enum can be safely represented with.
-        let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty));
-        if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
-            bug!("should be able to compute the layout of non-polymorphic type");
-        }
+            // Return the nullable type this Option-like enum can be safely represented with.
+            let field_ty_layout = tcx.layout_of(typing_env.as_query_input(field_ty));
+            if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
+                bug!("should be able to compute the layout of non-polymorphic type");
+            }
 
-        let field_ty_abi = &field_ty_layout.ok()?.backend_repr;
-        if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi {
-            match field_ty_scalar.valid_range(&tcx) {
-                WrappingRange { start: 0, end }
-                    if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
-                {
-                    return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
-                }
-                WrappingRange { start: 1, .. } => {
-                    return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
-                }
-                WrappingRange { start, end } => {
-                    unreachable!("Unhandled start and end range: ({}, {})", start, end)
-                }
-            };
+            let field_ty_abi = &field_ty_layout.ok()?.backend_repr;
+            if let BackendRepr::Scalar(field_ty_scalar) = field_ty_abi {
+                match field_ty_scalar.valid_range(&tcx) {
+                    WrappingRange { start: 0, end }
+                        if end == field_ty_scalar.size(&tcx).unsigned_int_max() - 1 =>
+                    {
+                        return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
+                    }
+                    WrappingRange { start: 1, .. } => {
+                        return Some(get_nullable_type(tcx, typing_env, field_ty).unwrap());
+                    }
+                    WrappingRange { start, end } => {
+                        unreachable!("Unhandled start and end range: ({}, {})", start, end)
+                    }
+                };
+            }
+            None
         }
+        ty::Pat(base, pat) => match **pat {
+            ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, *base),
+        },
+        _ => None,
     }
-    None
 }
 
 impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
@@ -1256,11 +1292,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 help: Some(fluent::lint_improper_ctypes_char_help),
             },
 
-            ty::Pat(..) => FfiUnsafe {
-                ty,
-                reason: fluent::lint_improper_ctypes_pat_reason,
-                help: Some(fluent::lint_improper_ctypes_pat_help),
-            },
+            // It's just extra invariants on the type that you need to uphold,
+            // but only the base type is relevant for being representable in FFI.
+            ty::Pat(base, ..) => self.check_type_for_ffi(acc, base),
 
             ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
                 FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None }
diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs
index 9bbb20246df..e4477c96202 100644
--- a/tests/ui/lint/clashing-extern-fn.rs
+++ b/tests/ui/lint/clashing-extern-fn.rs
@@ -1,7 +1,7 @@
 //@ check-pass
 //@ aux-build:external_extern_fn.rs
 #![crate_type = "lib"]
-
+#![feature(pattern_type_macro, pattern_types)]
 mod redeclared_different_signature {
     mod a {
         extern "C" {
@@ -490,3 +490,33 @@ mod hidden_niche {
         }
     }
 }
+
+mod pattern_types {
+    mod a {
+        use std::pat::pattern_type;
+        #[repr(transparent)]
+        struct NonZeroUsize(pattern_type!(usize is 1..));
+        extern "C" {
+            fn pt_non_zero_usize() -> pattern_type!(usize is 1..);
+            fn pt_non_zero_usize_opt() -> Option<pattern_type!(usize is 1..)>;
+            fn pt_non_zero_usize_opt_full_range() -> Option<pattern_type!(usize is 0..)>;
+            //~^ WARN not FFI-safe
+            fn pt_non_null_ptr() -> pattern_type!(usize is 1..);
+            fn pt_non_zero_usize_wrapper() -> NonZeroUsize;
+            fn pt_non_zero_usize_wrapper_opt() -> Option<NonZeroUsize>;
+        }
+    }
+    mod b {
+        extern "C" {
+            // If there's a clash in either of these cases you're either gaining an incorrect
+            // invariant that the value is non-zero, or you're missing out on that invariant. Both
+            // cases are warning for, from both a caller-convenience and optimisation perspective.
+            fn pt_non_zero_usize() -> usize;
+            fn pt_non_zero_usize_opt() -> usize;
+            fn pt_non_null_ptr() -> *const ();
+            //~^ WARN `pt_non_null_ptr` redeclared with a different signature
+            fn pt_non_zero_usize_wrapper() -> usize;
+            fn pt_non_zero_usize_wrapper_opt() -> usize;
+        }
+    }
+}
diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr
index 48dd1adbc1f..118b18b224c 100644
--- a/tests/ui/lint/clashing-extern-fn.stderr
+++ b/tests/ui/lint/clashing-extern-fn.stderr
@@ -17,6 +17,15 @@ LL |             fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZero<usiz
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 
+warning: `extern` block uses type `Option<(usize) is 0..=>`, which is not FFI-safe
+  --> $DIR/clashing-extern-fn.rs:502:54
+   |
+LL |             fn pt_non_zero_usize_opt_full_range() -> Option<pattern_type!(usize is 0..)>;
+   |                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
+
 warning: `clash` redeclared with a different signature
   --> $DIR/clashing-extern-fn.rs:13:13
    |
@@ -258,5 +267,17 @@ LL |             fn hidden_niche_unsafe_cell() -> Option<UnsafeCell<NonZero<usiz
    = note: expected `unsafe extern "C" fn() -> usize`
               found `unsafe extern "C" fn() -> Option<UnsafeCell<NonZero<usize>>>`
 
-warning: 22 warnings emitted
+warning: `pt_non_null_ptr` redeclared with a different signature
+  --> $DIR/clashing-extern-fn.rs:516:13
+   |
+LL |             fn pt_non_null_ptr() -> pattern_type!(usize is 1..);
+   |             ---------------------------------------------------- `pt_non_null_ptr` previously declared here
+...
+LL |             fn pt_non_null_ptr() -> *const ();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+   |
+   = note: expected `unsafe extern "C" fn() -> (usize) is 1..=`
+              found `unsafe extern "C" fn() -> *const ()`
+
+warning: 24 warnings emitted
 
diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/lint-ctypes-enum.rs
index 19af1de9576..0d19d5b5347 100644
--- a/tests/ui/lint/lint-ctypes-enum.rs
+++ b/tests/ui/lint/lint-ctypes-enum.rs
@@ -94,6 +94,7 @@ extern "C" {
     fn option_transparent_union(x: Option<TransparentUnion<num::NonZero<u8>>>);
     //~^ ERROR `extern` block uses type
     fn option_repr_rust(x: Option<Rust<num::NonZero<u8>>>); //~ ERROR `extern` block uses type
+    fn option_u8(x: Option<u8>); //~ ERROR `extern` block uses type
 
     fn result_ref_t(x: Result<&'static u8, ()>);
     fn result_fn_t(x: Result<extern "C" fn(), ()>);
diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/lint-ctypes-enum.stderr
index 8e92e7e6946..a491bd19605 100644
--- a/tests/ui/lint/lint-ctypes-enum.stderr
+++ b/tests/ui/lint/lint-ctypes-enum.stderr
@@ -79,8 +79,17 @@ LL |     fn option_repr_rust(x: Option<Rust<num::NonZero<u8>>>);
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 
+error: `extern` block uses type `Option<u8>`, which is not FFI-safe
+  --> $DIR/lint-ctypes-enum.rs:97:21
+   |
+LL |     fn option_u8(x: Option<u8>);
+   |                     ^^^^^^^^^^ not FFI-safe
+   |
+   = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
+   = note: enum has no representation hint
+
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:106:33
+  --> $DIR/lint-ctypes-enum.rs:107:33
    |
 LL |     fn result_nonzero_u128_t(x: Result<num::NonZero<u128>, ()>);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -88,7 +97,7 @@ LL |     fn result_nonzero_u128_t(x: Result<num::NonZero<u128>, ()>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `i128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:113:33
+  --> $DIR/lint-ctypes-enum.rs:114:33
    |
 LL |     fn result_nonzero_i128_t(x: Result<num::NonZero<i128>, ()>);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -96,7 +105,7 @@ LL |     fn result_nonzero_i128_t(x: Result<num::NonZero<i128>, ()>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `Result<TransparentUnion<NonZero<u8>>, ()>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:118:38
+  --> $DIR/lint-ctypes-enum.rs:119:38
    |
 LL |     fn result_transparent_union_t(x: Result<TransparentUnion<num::NonZero<u8>>, ()>);
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -105,7 +114,7 @@ LL |     fn result_transparent_union_t(x: Result<TransparentUnion<num::NonZero<u
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<Rust<NonZero<u8>>, ()>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:120:30
+  --> $DIR/lint-ctypes-enum.rs:121:30
    |
 LL |     fn result_repr_rust_t(x: Result<Rust<num::NonZero<u8>>, ()>);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -114,7 +123,7 @@ LL |     fn result_repr_rust_t(x: Result<Rust<num::NonZero<u8>>, ()>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonZero<u8>, U>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:124:51
+  --> $DIR/lint-ctypes-enum.rs:125:51
    |
 LL |     fn result_1zst_exhaustive_single_variant_t(x: Result<num::NonZero<u8>, U>);
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -123,7 +132,7 @@ LL |     fn result_1zst_exhaustive_single_variant_t(x: Result<num::NonZero<u8>,
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonZero<u8>, B>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:126:53
+  --> $DIR/lint-ctypes-enum.rs:127:53
    |
 LL |     fn result_1zst_exhaustive_multiple_variant_t(x: Result<num::NonZero<u8>, B>);
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -132,7 +141,7 @@ LL |     fn result_1zst_exhaustive_multiple_variant_t(x: Result<num::NonZero<u8>
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonZero<u8>, NonExhaustive>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:128:51
+  --> $DIR/lint-ctypes-enum.rs:129:51
    |
 LL |     fn result_1zst_non_exhaustive_no_variant_t(x: Result<num::NonZero<u8>, NonExhaustive>);
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -141,7 +150,7 @@ LL |     fn result_1zst_non_exhaustive_no_variant_t(x: Result<num::NonZero<u8>,
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonZero<u8>, Field>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:131:49
+  --> $DIR/lint-ctypes-enum.rs:132:49
    |
 LL |     fn result_1zst_exhaustive_single_field_t(x: Result<num::NonZero<u8>, Field>);
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -150,7 +159,7 @@ LL |     fn result_1zst_exhaustive_single_field_t(x: Result<num::NonZero<u8>, Fi
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<Result<(), NonZero<u8>>, ()>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:133:30
+  --> $DIR/lint-ctypes-enum.rs:134:30
    |
 LL |     fn result_cascading_t(x: Result<Result<(), num::NonZero<u8>>, ()>);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -159,7 +168,7 @@ LL |     fn result_cascading_t(x: Result<Result<(), num::NonZero<u8>>, ()>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:144:33
+  --> $DIR/lint-ctypes-enum.rs:145:33
    |
 LL |     fn result_nonzero_u128_e(x: Result<(), num::NonZero<u128>>);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -167,7 +176,7 @@ LL |     fn result_nonzero_u128_e(x: Result<(), num::NonZero<u128>>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `i128`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:151:33
+  --> $DIR/lint-ctypes-enum.rs:152:33
    |
 LL |     fn result_nonzero_i128_e(x: Result<(), num::NonZero<i128>>);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -175,7 +184,7 @@ LL |     fn result_nonzero_i128_e(x: Result<(), num::NonZero<i128>>);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `Result<(), TransparentUnion<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:156:38
+  --> $DIR/lint-ctypes-enum.rs:157:38
    |
 LL |     fn result_transparent_union_e(x: Result<(), TransparentUnion<num::NonZero<u8>>>);
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -184,7 +193,7 @@ LL |     fn result_transparent_union_e(x: Result<(), TransparentUnion<num::NonZe
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<(), Rust<NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:158:30
+  --> $DIR/lint-ctypes-enum.rs:159:30
    |
 LL |     fn result_repr_rust_e(x: Result<(), Rust<num::NonZero<u8>>>);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -193,7 +202,7 @@ LL |     fn result_repr_rust_e(x: Result<(), Rust<num::NonZero<u8>>>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<U, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:162:51
+  --> $DIR/lint-ctypes-enum.rs:163:51
    |
 LL |     fn result_1zst_exhaustive_single_variant_e(x: Result<U, num::NonZero<u8>>);
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -202,7 +211,7 @@ LL |     fn result_1zst_exhaustive_single_variant_e(x: Result<U, num::NonZero<u8
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<B, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:164:53
+  --> $DIR/lint-ctypes-enum.rs:165:53
    |
 LL |     fn result_1zst_exhaustive_multiple_variant_e(x: Result<B, num::NonZero<u8>>);
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -211,7 +220,7 @@ LL |     fn result_1zst_exhaustive_multiple_variant_e(x: Result<B, num::NonZero<
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<NonExhaustive, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:166:51
+  --> $DIR/lint-ctypes-enum.rs:167:51
    |
 LL |     fn result_1zst_non_exhaustive_no_variant_e(x: Result<NonExhaustive, num::NonZero<u8>>);
    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -220,7 +229,7 @@ LL |     fn result_1zst_non_exhaustive_no_variant_e(x: Result<NonExhaustive, num
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<Field, NonZero<u8>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:169:49
+  --> $DIR/lint-ctypes-enum.rs:170:49
    |
 LL |     fn result_1zst_exhaustive_single_field_e(x: Result<Field, num::NonZero<u8>>);
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -229,7 +238,7 @@ LL |     fn result_1zst_exhaustive_single_field_e(x: Result<Field, num::NonZero<
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<(), Result<(), NonZero<u8>>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:171:30
+  --> $DIR/lint-ctypes-enum.rs:172:30
    |
 LL |     fn result_cascading_e(x: Result<(), Result<(), num::NonZero<u8>>>);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -238,7 +247,7 @@ LL |     fn result_cascading_e(x: Result<(), Result<(), num::NonZero<u8>>>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe
-  --> $DIR/lint-ctypes-enum.rs:173:27
+  --> $DIR/lint-ctypes-enum.rs:174:27
    |
 LL |     fn result_unit_t_e(x: Result<(), ()>);
    |                           ^^^^^^^^^^^^^^ not FFI-safe
@@ -246,5 +255,5 @@ LL |     fn result_unit_t_e(x: Result<(), ()>);
    = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
    = note: enum has no representation hint
 
-error: aborting due to 26 previous errors
+error: aborting due to 27 previous errors