about summary refs log tree commit diff
diff options
context:
space:
mode:
authorniacdoial <niac.notirl@gmail.com>2024-11-08 01:28:16 +0100
committerniacdoial <niac.notirl@gmail.com>2024-12-06 22:23:13 +0100
commit7962a2de3a37c97cc08789a6db2cdebe5d465a95 (patch)
treeafa481fbbdb1d62e25796822779a2dd459c4b173
parent1d521310439ecb0e243a5056768175a51e40a9b6 (diff)
downloadrust-7962a2de3a37c97cc08789a6db2cdebe5d465a95.tar.gz
rust-7962a2de3a37c97cc08789a6db2cdebe5d465a95.zip
lint: fix ImproperCTypes edge case for unsized structs due to foreign types
-rw-r--r--compiler/rustc_lint/src/types.rs67
-rw-r--r--tests/ui/lint/lint-ctypes.rs16
-rw-r--r--tests/ui/lint/lint-ctypes.stderr68
3 files changed, 119 insertions, 32 deletions
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 111be742696..842dc2baae4 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -740,6 +740,69 @@ enum FfiResult<'tcx> {
     },
 }
 
+pub(crate) fn is_unsized_because_foreign<'tcx, 'a>(
+    cx: &'a LateContext<'tcx>,
+    ty: Ty<'tcx>,
+) -> Result<bool, ()> {
+    let tcx = cx.tcx;
+
+    if ty.is_sized(tcx, cx.typing_env()) {
+        Err(())
+    } else {
+        Ok(match ty.kind() {
+            ty::Slice(_) => false,
+            ty::Str => false,
+            ty::Dynamic(..) => false,
+            ty::Foreign(..) => true,
+            ty::Alias(ty::Opaque, ..) => todo!("why"),
+            ty::Adt(def, args) => {
+                // for now assume: boxes and phantoms don't mess with this
+                match def.adt_kind() {
+                    AdtKind::Union | AdtKind::Enum => true,
+                    AdtKind::Struct => {
+                        if let Some(sym::cstring_type | sym::cstr_type) =
+                            tcx.get_diagnostic_name(def.did())
+                        {
+                            return Ok(false);
+                        }
+                        // FIXME: how do we deal with non-exhaustive unsized structs/unions?
+
+                        if def.non_enum_variant().fields.is_empty() {
+                            bug!("empty unsized struct/union. what?");
+                        }
+
+                        let variant = def.non_enum_variant();
+
+                        // only the last field may be unsized
+                        let n_fields = variant.fields.len();
+                        let last_field = &variant.fields[(n_fields - 1).into()];
+                        let field_ty = last_field.ty(cx.tcx, args);
+                        let field_ty = cx
+                            .tcx
+                            .try_normalize_erasing_regions(cx.typing_env(), field_ty)
+                            .unwrap_or(field_ty);
+                        return Ok(is_unsized_because_foreign(cx, field_ty).unwrap());
+                    }
+                }
+            }
+            ty::Tuple(tuple) => {
+                // only the last field may be unsized
+                let n_fields = tuple.len();
+                let field_ty: Ty<'tcx> = tuple[n_fields - 1];
+                //let field_ty = last_field.ty(cx.tcx, args);
+                let field_ty = cx
+                    .tcx
+                    .try_normalize_erasing_regions(cx.typing_env(), field_ty)
+                    .unwrap_or(field_ty);
+                is_unsized_because_foreign(cx, field_ty).unwrap()
+            }
+            t => {
+                bug!("we shouldn't be looking if this is unsized for a reason or another: {:?}", t)
+            }
+        })
+    }
+}
+
 pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
     tcx: TyCtxt<'tcx>,
     def: ty::AdtDef<'tcx>,
@@ -1044,7 +1107,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             ty::Adt(def, args) => {
                 if let Some(inner_ty) = ty.boxed_ty() {
                     if inner_ty.is_sized(tcx, self.cx.typing_env())
-                        || matches!(inner_ty.kind(), ty::Foreign(..))
+                        || is_unsized_because_foreign(self.cx, inner_ty).unwrap()
                     {
                         // discussion on declaration vs definition:
                         // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
@@ -1249,7 +1312,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
             ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => {
                 if inner_ty.is_sized(tcx, self.cx.typing_env())
-                    || matches!(inner_ty.kind(), ty::Foreign(..))
+                    || is_unsized_because_foreign(self.cx, inner_ty).unwrap()
                 {
                     // there's a nuance on what this lint should do for function definitions
                     // (touched upon in https://github.com/rust-lang/rust/issues/66220 and https://github.com/rust-lang/rust/pull/72700)
diff --git a/tests/ui/lint/lint-ctypes.rs b/tests/ui/lint/lint-ctypes.rs
index 615cdfd738a..28ff0381349 100644
--- a/tests/ui/lint/lint-ctypes.rs
+++ b/tests/ui/lint/lint-ctypes.rs
@@ -1,4 +1,5 @@
 #![feature(rustc_private)]
+#![feature(extern_types)]
 
 #![allow(private_interfaces)]
 #![deny(improper_ctypes)]
@@ -6,7 +7,9 @@
 use std::cell::UnsafeCell;
 use std::marker::PhantomData;
 use std::ffi::{c_int, c_uint};
+use std::fmt::Debug;
 
+unsafe extern "C" {type UnsizedOpaque;}
 trait Bar { }
 trait Mirror { type It: ?Sized; }
 impl<T: ?Sized> Mirror for T { type It = Self; }
@@ -39,6 +42,16 @@ pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>);
 pub struct TransparentUnit<U>(f32, PhantomData<U>);
 #[repr(transparent)]
 pub struct TransparentCustomZst(i32, ZeroSize);
+#[repr(C)]
+pub struct UnsizedStructBecauseForeign {
+    sized: u32,
+    unszd: UnsizedOpaque,
+}
+#[repr(C)]
+pub struct UnsizedStructBecauseDyn {
+    sized: u32,
+    unszd: dyn Debug,
+}
 
 #[repr(C)]
 pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
@@ -72,6 +85,9 @@ extern "C" {
     pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `Box<u32>`
     pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]`
 
+    pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign);
+    pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn`
+
     pub fn no_niche_a(a: Option<UnsafeCell<extern fn()>>);
     //~^ ERROR: uses type `Option<UnsafeCell<extern "C" fn()>>`
     pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
diff --git a/tests/ui/lint/lint-ctypes.stderr b/tests/ui/lint/lint-ctypes.stderr
index fdb7ef12610..59f6243e01f 100644
--- a/tests/ui/lint/lint-ctypes.stderr
+++ b/tests/ui/lint/lint-ctypes.stderr
@@ -1,5 +1,5 @@
 error: `extern` block uses type `Foo`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:47:28
+  --> $DIR/lint-ctypes.rs:60:28
    |
 LL |     pub fn ptr_type1(size: *const Foo);
    |                            ^^^^^^^^^^ not FFI-safe
@@ -8,18 +8,18 @@ LL |     pub fn ptr_type1(size: *const Foo);
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
    = note: this struct has unspecified layout
 note: the type is defined here
-  --> $DIR/lint-ctypes.rs:25:1
+  --> $DIR/lint-ctypes.rs:28:1
    |
 LL | pub struct Foo;
    | ^^^^^^^^^^^^^^
 note: the lint level is defined here
-  --> $DIR/lint-ctypes.rs:4:9
+  --> $DIR/lint-ctypes.rs:5:9
    |
 LL | #![deny(improper_ctypes)]
    |         ^^^^^^^^^^^^^^^
 
 error: `extern` block uses type `Foo`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:48:28
+  --> $DIR/lint-ctypes.rs:61:28
    |
 LL |     pub fn ptr_type2(size: *const Foo);
    |                            ^^^^^^^^^^ not FFI-safe
@@ -28,13 +28,13 @@ LL |     pub fn ptr_type2(size: *const Foo);
    = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
    = note: this struct has unspecified layout
 note: the type is defined here
-  --> $DIR/lint-ctypes.rs:25:1
+  --> $DIR/lint-ctypes.rs:28:1
    |
 LL | pub struct Foo;
    | ^^^^^^^^^^^^^^
 
 error: `extern` block uses type `((),)`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:50:25
+  --> $DIR/lint-ctypes.rs:63:25
    |
 LL |     pub fn ptr_tuple(p: *const ((),));
    |                         ^^^^^^^^^^^^ not FFI-safe
@@ -44,7 +44,7 @@ LL |     pub fn ptr_tuple(p: *const ((),));
    = note: tuples have unspecified layout
 
 error: `extern` block uses type `&[u32]`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:51:26
+  --> $DIR/lint-ctypes.rs:64:26
    |
 LL |     pub fn slice_type(p: &[u32]);
    |                          ^^^^^^ not FFI-safe
@@ -53,7 +53,7 @@ LL |     pub fn slice_type(p: &[u32]);
    = note: this reference to an unsized rust type contains metadata, which makes it incompatible with a C pointer
 
 error: `extern` block uses type `&str`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:52:24
+  --> $DIR/lint-ctypes.rs:65:24
    |
 LL |     pub fn str_type(p: &str);
    |                        ^^^^ not FFI-safe
@@ -71,7 +71,7 @@ LL |     pub fn box_type(p: Box<u32>);
    = note: this struct has unspecified layout
 
 error: `extern` block uses type `Option<Box<u32>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:54:28
+  --> $DIR/lint-ctypes.rs:67:28
    |
 LL |     pub fn opt_box_type(p: Option<Box<u32>>);
    |                            ^^^^^^^^^^^^^^^^ not FFI-safe
@@ -80,7 +80,7 @@ LL |     pub fn opt_box_type(p: Option<Box<u32>>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `char`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:56:25
+  --> $DIR/lint-ctypes.rs:69:25
    |
 LL |     pub fn char_type(p: char);
    |                         ^^^^ not FFI-safe
@@ -89,7 +89,7 @@ LL |     pub fn char_type(p: char);
    = note: the `char` type has no C equivalent
 
 error: `extern` block uses type `i128`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:57:25
+  --> $DIR/lint-ctypes.rs:70:25
    |
 LL |     pub fn i128_type(p: i128);
    |                         ^^^^ not FFI-safe
@@ -97,7 +97,7 @@ LL |     pub fn i128_type(p: i128);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:58:25
+  --> $DIR/lint-ctypes.rs:71:25
    |
 LL |     pub fn u128_type(p: u128);
    |                         ^^^^ not FFI-safe
@@ -105,7 +105,7 @@ LL |     pub fn u128_type(p: u128);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `&dyn Bar`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:59:26
+  --> $DIR/lint-ctypes.rs:72:26
    |
 LL |     pub fn trait_type(p: &dyn Bar);
    |                          ^^^^^^^^ not FFI-safe
@@ -113,7 +113,7 @@ LL |     pub fn trait_type(p: &dyn Bar);
    = note: this reference to an unsized rust type contains metadata, which makes it incompatible with a C pointer
 
 error: `extern` block uses type `(i32, i32)`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:60:26
+  --> $DIR/lint-ctypes.rs:73:26
    |
 LL |     pub fn tuple_type(p: (i32, i32));
    |                          ^^^^^^^^^^ not FFI-safe
@@ -122,7 +122,7 @@ LL |     pub fn tuple_type(p: (i32, i32));
    = note: tuples have unspecified layout
 
 error: `extern` block uses type `(i32, i32)`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:61:27
+  --> $DIR/lint-ctypes.rs:74:27
    |
 LL |     pub fn tuple_type2(p: I32Pair);
    |                           ^^^^^^^ not FFI-safe
@@ -131,7 +131,7 @@ LL |     pub fn tuple_type2(p: I32Pair);
    = note: tuples have unspecified layout
 
 error: `extern` block uses type `ZeroSize`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:62:25
+  --> $DIR/lint-ctypes.rs:75:25
    |
 LL |     pub fn zero_size(p: ZeroSize);
    |                         ^^^^^^^^ not FFI-safe
@@ -139,26 +139,26 @@ LL |     pub fn zero_size(p: ZeroSize);
    = help: consider adding a member to this struct
    = note: this struct has no fields
 note: the type is defined here
-  --> $DIR/lint-ctypes.rs:21:1
+  --> $DIR/lint-ctypes.rs:24:1
    |
 LL | pub struct ZeroSize;
    | ^^^^^^^^^^^^^^^^^^^
 
 error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:63:33
+  --> $DIR/lint-ctypes.rs:76:33
    |
 LL |     pub fn zero_size_phantom(p: ZeroSizeWithPhantomData);
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
    |
    = note: composed only of `PhantomData`
 note: the type is defined here
-  --> $DIR/lint-ctypes.rs:44:1
+  --> $DIR/lint-ctypes.rs:57:1
    |
 LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData<i32>);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `extern` block uses type `PhantomData<bool>`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:66:12
+  --> $DIR/lint-ctypes.rs:79:12
    |
 LL |         -> ::std::marker::PhantomData<bool>;
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -166,7 +166,7 @@ LL |         -> ::std::marker::PhantomData<bool>;
    = note: composed only of `PhantomData`
 
 error: `extern` block uses type `fn()`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:67:23
+  --> $DIR/lint-ctypes.rs:80:23
    |
 LL |     pub fn fn_type(p: RustFn);
    |                       ^^^^^^ not FFI-safe
@@ -175,7 +175,7 @@ LL |     pub fn fn_type(p: RustFn);
    = note: this function pointer has Rust-specific calling convention
 
 error: `extern` block uses type `fn()`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:68:24
+  --> $DIR/lint-ctypes.rs:81:24
    |
 LL |     pub fn fn_type2(p: fn());
    |                        ^^^^ not FFI-safe
@@ -193,7 +193,7 @@ LL |     pub fn fn_contained(p: RustBadRet);
    = note: this struct has unspecified layout
 
 error: `extern` block uses type `i128`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:70:32
+  --> $DIR/lint-ctypes.rs:83:32
    |
 LL |     pub fn transparent_i128(p: TransparentI128);
    |                                ^^^^^^^^^^^^^^^ not FFI-safe
@@ -201,7 +201,7 @@ LL |     pub fn transparent_i128(p: TransparentI128);
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `&str`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:71:31
+  --> $DIR/lint-ctypes.rs:84:31
    |
 LL |     pub fn transparent_str(p: TransparentStr);
    |                               ^^^^^^^^^^^^^^ not FFI-safe
@@ -219,7 +219,7 @@ LL |     pub fn transparent_fn(p: TransparentBadFn);
    = note: this struct has unspecified layout
 
 error: `extern` block uses type `[u8; 8]`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:73:27
+  --> $DIR/lint-ctypes.rs:86:27
    |
 LL |     pub fn raw_array(arr: [u8; 8]);
    |                           ^^^^^^^ not FFI-safe
@@ -227,8 +227,16 @@ LL |     pub fn raw_array(arr: [u8; 8]);
    = help: consider passing a pointer to the array
    = note: passing raw arrays by value is not FFI-safe
 
+error: `extern` block uses type `&UnsizedStructBecauseDyn`, which is not FFI-safe
+  --> $DIR/lint-ctypes.rs:89:47
+   |
+LL |     pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn);
+   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
+   |
+   = note: this reference to an unsized rust type contains metadata, which makes it incompatible with a C pointer
+
 error: `extern` block uses type `Option<UnsafeCell<extern "C" fn()>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:75:26
+  --> $DIR/lint-ctypes.rs:91:26
    |
 LL |     pub fn no_niche_a(a: Option<UnsafeCell<extern fn()>>);
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -237,7 +245,7 @@ LL |     pub fn no_niche_a(a: Option<UnsafeCell<extern fn()>>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `Option<UnsafeCell<&i32>>`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:77:26
+  --> $DIR/lint-ctypes.rs:93:26
    |
 LL |     pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe
@@ -246,7 +254,7 @@ LL |     pub fn no_niche_b(b: Option<UnsafeCell<&i32>>);
    = note: enum has no representation hint
 
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:80:34
+  --> $DIR/lint-ctypes.rs:96:34
    |
 LL |     pub static static_u128_type: u128;
    |                                  ^^^^ not FFI-safe
@@ -254,11 +262,11 @@ LL |     pub static static_u128_type: u128;
    = note: 128-bit integers don't currently have a known stable ABI
 
 error: `extern` block uses type `u128`, which is not FFI-safe
-  --> $DIR/lint-ctypes.rs:81:40
+  --> $DIR/lint-ctypes.rs:97:40
    |
 LL |     pub static static_u128_array_type: [u128; 16];
    |                                        ^^^^^^^^^^ not FFI-safe
    |
    = note: 128-bit integers don't currently have a known stable ABI
 
-error: aborting due to 27 previous errors
+error: aborting due to 29 previous errors