From 7962a2de3a37c97cc08789a6db2cdebe5d465a95 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Fri, 8 Nov 2024 01:28:16 +0100 Subject: [PATCH] lint: fix ImproperCTypes edge case for unsized structs due to foreign types --- compiler/rustc_lint/src/types.rs | 67 ++++++++++++++++++++++++++++++- tests/ui/lint/lint-ctypes.rs | 16 ++++++++ tests/ui/lint/lint-ctypes.stderr | 68 ++++++++++++++++++-------------- 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 { + 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 Mirror for T { type It = Self; } @@ -39,6 +42,16 @@ pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); pub struct TransparentUnit(f32, PhantomData); #[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); @@ -72,6 +85,9 @@ extern "C" { pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `Box` 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>); //~^ ERROR: uses type `Option>` pub fn no_niche_b(b: Option>); 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); = note: this struct has unspecified layout error: `extern` block uses type `Option>`, 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>); | ^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +80,7 @@ LL | pub fn opt_box_type(p: Option>); = 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); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:66:12 + --> $DIR/lint-ctypes.rs:79:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -166,7 +166,7 @@ LL | -> ::std::marker::PhantomData; = 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>`, 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>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -237,7 +245,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, 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>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -246,7 +254,7 @@ LL | pub fn no_niche_b(b: Option>); = 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