diff --git a/Cargo.lock b/Cargo.lock index 83d7a6a9ab134..7cec5685ec206 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3952,6 +3952,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", + "rustc_type_ir", "smallvec", "tracing", "unicode-security", diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs index 2d9de2a5b8d6b..5bb57e4f579ed 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -206,7 +206,7 @@ fn rust_call_abi() { #[repr(simd)] struct I64X2([i64; 2]); -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] extern "C" fn foo(_a: I64X2) {} #[cfg(target_arch = "x86_64")] diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 7718f16984dab..d6014f5006ad0 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -23,6 +23,7 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } +rustc_type_ir = { path = "../rustc_type_ir" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" unicode-security = "0.1.0" diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 99b42ee548029..5bb078053f4cc 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -364,7 +364,7 @@ lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dere .note = creating a reference requires the pointer target to be valid and imposes aliasing requirements .suggestion = try using a raw pointer method instead; or if this reference is intentional, make it explicit -lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe +lint_improper_ctypes = {$desc} uses type `{$ty}`, which is not FFI-safe .label = not FFI-safe .note = the type is defined here @@ -373,14 +373,22 @@ lint_improper_ctypes_128bit = 128-bit integers don't currently have a known stab lint_improper_ctypes_array_help = consider passing a pointer to the array lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe -lint_improper_ctypes_box = box cannot be represented as a single pointer lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead lint_improper_ctypes_char_reason = the `char` type has no C equivalent -lint_improper_ctypes_cstr_help = - consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` +lint_improper_ctypes_cstr_help_const = + consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` or `CString::as_ptr()` +lint_improper_ctypes_cstr_help_mut = + consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer +lint_improper_ctypes_cstr_help_owned = + consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, + and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + (note that `CString::into_raw()`'s output must not be `libc::free()`'d) +lint_improper_ctypes_cstr_help_unknown = + consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, + and use (depending on the use case) `CStr::as_ptr()`, `CString::into_raw()` then `CString::from_raw()`, or a dedicated buffer lint_improper_ctypes_cstr_reason = `CStr`/`CString` do not have a guaranteed layout lint_improper_ctypes_dyn = trait objects have no C equivalent @@ -391,7 +399,9 @@ lint_improper_ctypes_enum_repr_help = lint_improper_ctypes_enum_repr_reason = enum has no representation hint lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead +lint_improper_ctypes_fnptr_indirect_reason = the function pointer to `{$ty}` is FFI-unsafe due to `{$inner_ty}` lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention + lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants @@ -399,32 +409,54 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` lint_improper_ctypes_opaque = opaque types have no C equivalent -lint_improper_ctypes_slice_help = consider using a raw pointer instead +lint_improper_ctypes_pat_int1_help = consider using the base type instead, or wrapping `{$ty}` in an `Option<_>` +lint_improper_ctypes_pat_int1_reason = integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code +lint_improper_ctypes_pat_int2_help = consider using the base type instead +lint_improper_ctypes_pat_int2_reason = integer-pattern types with more than one disallowed value cannot have their value be provided by non-rust code + +lint_improper_ctypes_ptr_validity_help = consider using a raw pointer, or wrapping `{$ty}` in an `Option<_>` +lint_improper_ctypes_ptr_validity_reason = + boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code +lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead lint_improper_ctypes_slice_reason = slices have no C equivalent -lint_improper_ctypes_str_help = consider using `*const u8` and a length instead +lint_improper_ctypes_str_help = consider using `*const u8` and a length instead lint_improper_ctypes_str_reason = string slices have no C equivalent -lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct -lint_improper_ctypes_struct_fieldless_reason = this struct has no fields -lint_improper_ctypes_struct_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct +lint_improper_ctypes_struct_consider_transparent = `{$ty}` has exactly one non-zero-sized field, consider making it `#[repr(transparent)]` instead +lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field + +lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct +lint_improper_ctypes_struct_fieldless_reason = `{$ty}` has no fields -lint_improper_ctypes_struct_layout_reason = this struct has unspecified layout -lint_improper_ctypes_struct_non_exhaustive = this struct is non-exhaustive -lint_improper_ctypes_struct_zst = this struct contains only zero-sized fields +lint_improper_ctypes_struct_layout_help = consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `{$ty}` +lint_improper_ctypes_struct_layout_reason = `{$ty}` has unspecified layout +lint_improper_ctypes_struct_non_exhaustive = `{$ty}` is non-exhaustive +lint_improper_ctypes_struct_zst = `{$ty}` contains only zero-sized fields lint_improper_ctypes_tuple_help = consider using a struct instead - lint_improper_ctypes_tuple_reason = tuples have unspecified layout -lint_improper_ctypes_union_fieldless_help = consider adding a member to this union +lint_improper_ctypes_uninhabited_enum = zero-variant enums and other uninhabited types are not allowed in function arguments and static variables +lint_improper_ctypes_uninhabited_enum_deep = zero-variant enums and other uninhabited types are only allowed in function returns if used directly +lint_improper_ctypes_uninhabited_never = the never type (`!`) and other uninhabited types are not allowed in function arguments and static variables +lint_improper_ctypes_uninhabited_never_deep = the never type (`!`) and other uninhabited types are only allowed in function returns if used directly +lint_improper_ctypes_uninhabited_use_direct = if you meant to have a function that never returns, consider setting its return type to the never type (`!`) or a zero-variant enum + +lint_improper_ctypes_union_consider_transparent = `{$ty}` has exactly one non-zero-sized field, consider making it `#[repr(transparent)]` instead +lint_improper_ctypes_union_fieldless_help = consider adding a member to this union lint_improper_ctypes_union_fieldless_reason = this union has no fields -lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union +lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` attribute to this union lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive +lint_improper_ctypes_unsized_box = this box for an unsized type contains metadata, which makes it incompatible with a C pointer +lint_improper_ctypes_unsized_ptr = this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer +lint_improper_ctypes_unsized_ref = this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + lint_incomplete_include = include macro expected single expression in source diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index d0668794198ac..49b02a0d503a3 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -134,7 +134,6 @@ impl ClashingExternDeclarations { ty::TypingEnv::non_body_analysis(tcx, this_fi.owner_id), existing_decl_ty, this_decl_ty, - types::CItemKind::Declaration, ) { let orig = name_of_extern_decl(tcx, existing_did); @@ -208,10 +207,9 @@ fn structurally_same_type<'tcx>( typing_env: ty::TypingEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>, - ckind: types::CItemKind, ) -> bool { let mut seen_types = UnordSet::default(); - let result = structurally_same_type_impl(&mut seen_types, tcx, typing_env, a, b, ckind); + let result = structurally_same_type_impl(&mut seen_types, tcx, typing_env, a, b); if cfg!(debug_assertions) && result { // Sanity-check: must have same ABI, size and alignment. // `extern` blocks cannot be generic, so we'll always get a layout here. @@ -230,7 +228,6 @@ fn structurally_same_type_impl<'tcx>( typing_env: ty::TypingEnv<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>, - ckind: types::CItemKind, ) -> bool { debug!("structurally_same_type_impl(tcx, a = {:?}, b = {:?})", a, b); @@ -301,7 +298,6 @@ fn structurally_same_type_impl<'tcx>( typing_env, tcx.type_of(a_did).instantiate(tcx, a_gen_args), tcx.type_of(b_did).instantiate(tcx, b_gen_args), - ckind, ) }, ) @@ -309,25 +305,19 @@ fn structurally_same_type_impl<'tcx>( (ty::Array(a_ty, a_len), ty::Array(b_ty, b_len)) => { // For arrays, we also check the length. a_len == b_len - && structurally_same_type_impl( - seen_types, tcx, typing_env, *a_ty, *b_ty, ckind, - ) + && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty) } (ty::Slice(a_ty), ty::Slice(b_ty)) => { - structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty, ckind) + structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty) } (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => { a_mutbl == b_mutbl - && structurally_same_type_impl( - seen_types, tcx, typing_env, *a_ty, *b_ty, ckind, - ) + && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty) } (ty::Ref(_a_region, a_ty, a_mut), ty::Ref(_b_region, b_ty, b_mut)) => { // For structural sameness, we don't need the region to be same. a_mut == b_mut - && structurally_same_type_impl( - seen_types, tcx, typing_env, *a_ty, *b_ty, ckind, - ) + && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty) } (ty::FnDef(..), ty::FnDef(..)) => { let a_poly_sig = a.fn_sig(tcx); @@ -341,7 +331,7 @@ fn structurally_same_type_impl<'tcx>( (a_sig.abi, a_sig.safety, a_sig.c_variadic) == (b_sig.abi, b_sig.safety, b_sig.c_variadic) && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| { - structurally_same_type_impl(seen_types, tcx, typing_env, *a, *b, ckind) + structurally_same_type_impl(seen_types, tcx, typing_env, *a, *b) }) && structurally_same_type_impl( seen_types, @@ -349,7 +339,6 @@ fn structurally_same_type_impl<'tcx>( typing_env, a_sig.output(), b_sig.output(), - ckind, ) } (ty::Tuple(..), ty::Tuple(..)) => { @@ -377,14 +366,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. (ty::Adt(..) | ty::Pat(..), _) if is_primitive_or_pointer(b) => { - if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, ckind) { + if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, false) { a_inner == b } else { false } } (_, ty::Adt(..) | ty::Pat(..)) if is_primitive_or_pointer(a) => { - if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, ckind) { + if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, false) { b_inner == a } else { false diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index b910d6a138e11..711c9c44eb89a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -195,8 +195,9 @@ late_lint_methods!( DefaultCouldBeDerived: DefaultCouldBeDerived::default(), DerefIntoDynSupertrait: DerefIntoDynSupertrait, DropForgetUseless: DropForgetUseless, - ImproperCTypesDeclarations: ImproperCTypesDeclarations, - ImproperCTypesDefinitions: ImproperCTypesDefinitions, + //ImproperCTypesDeclarations: ImproperCTypesDeclarations, + //ImproperCTypesDefinitions: ImproperCTypesDefinitions, + ImproperCTypesLint: ImproperCTypesLint, InvalidFromUtf8: InvalidFromUtf8, VariantSizeDifferences: VariantSizeDifferences, PathStatements: PathStatements, @@ -335,6 +336,14 @@ fn register_builtins(store: &mut LintStore) { REFINING_IMPL_TRAIT_INTERNAL ); + add_lint_group!( + "improper_c_boundaries", + IMPROPER_C_CALLBACKS, + IMPROPER_C_FN_DEFINITIONS, + IMPROPER_CTYPE_DEFINITIONS, + IMPROPER_CTYPES + ); + add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024); // Register renamed and removed lints. @@ -353,6 +362,7 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("unused_tuple_struct_fields", "dead_code"); store.register_renamed("static_mut_ref", "static_mut_refs"); store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); + store.register_renamed("improper_ctypes_definitions", "improper_c_fn_definitions"); // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 487184b836a43..777b19ad3201f 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1887,29 +1887,50 @@ pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> { }, } +pub(crate) struct ImproperCTypesLayer<'a> { + pub ty: Ty<'a>, + pub inner_ty: Option>, + pub note: DiagMessage, + pub span_note: Option, + pub help: Option, +} + +impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { + diag.arg("ty", self.ty); + if let Some(ty) = self.inner_ty { + diag.arg("inner_ty", ty); + } + + if let Some(help) = self.help { + diag.help(diag.eagerly_translate(help)); + } + + diag.note(diag.eagerly_translate(self.note)); + if let Some(note) = self.span_note { + diag.span_note(note, fluent::lint_note); + }; + } +} + pub(crate) struct ImproperCTypes<'a> { pub ty: Ty<'a>, pub desc: &'a str, pub label: Span, - pub help: Option, - pub note: DiagMessage, - pub span_note: Option, + pub reasons: Vec>, } // Used because of the complexity of Option, DiagMessage, and Option impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> { fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { diag.primary_message(fluent::lint_improper_ctypes); - diag.arg("ty", self.ty); - diag.arg("desc", self.desc); diag.span_label(self.label, fluent::lint_label); - if let Some(help) = self.help { - diag.help(help); - } - diag.note(self.note); - if let Some(note) = self.span_note { - diag.span_note(note, fluent::lint_note); + for reason in self.reasons.into_iter() { + diag.subdiagnostic(reason); } + // declare the arguments at the end to avoid them being clobbered in the subdiagnostics + diag.arg("ty", self.ty); + diag.arg("desc", self.desc); } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f1c06dfe6ce0e..e716e23e4e635 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,36 +1,30 @@ use std::iter; -use std::ops::ControlFlow; -use rustc_abi::{ - BackendRepr, Integer, IntegerType, TagEncoding, VariantIdx, Variants, WrappingRange, -}; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::DiagMessage; -use rustc_hir::intravisit::VisitorExt; -use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem}; +use rustc_abi::{BackendRepr, Size, TagEncoding, Variants, WrappingRange}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; -use rustc_middle::ty::{ - self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, -}; +use rustc_middle::ty::{self, AdtKind, Const, ScalarInt, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; -use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; -mod improper_ctypes; +mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations +pub(crate) use improper_ctypes::{ + IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, IMPROPER_CTYPE_DEFINITIONS, IMPROPER_CTYPES, + ImproperCTypesLint, +}; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, - AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, + AtomicOrderingStore, InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, - UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment, + UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag, }; -use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; +use crate::{LateContext, LateLintPass, LintContext}; mod literal; @@ -688,144 +682,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } -declare_lint! { - /// The `improper_ctypes` lint detects incorrect use of types in foreign - /// modules. - /// - /// ### Example - /// - /// ```rust - /// unsafe extern "C" { - /// static STATIC: String; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The compiler has several checks to verify that types used in `extern` - /// blocks are safe and follow certain rules to ensure proper - /// compatibility with the foreign interfaces. This lint is issued when it - /// detects a probable mistake in a definition. The lint usually should - /// provide a description of the issue, along with possibly a hint on how - /// to resolve it. - IMPROPER_CTYPES, - Warn, - "proper use of libc types in foreign modules" -} - -declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]); - -declare_lint! { - /// The `improper_ctypes_definitions` lint detects incorrect use of - /// [`extern` function] definitions. - /// - /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier - /// - /// ### Example - /// - /// ```rust - /// # #![allow(unused)] - /// pub extern "C" fn str_type(p: &str) { } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// There are many parameter and return types that may be specified in an - /// `extern` function that are not compatible with the given ABI. This - /// lint is an alert that these types should not be used. The lint usually - /// should provide a description of the issue, along with possibly a hint - /// on how to resolve it. - IMPROPER_CTYPES_DEFINITIONS, - Warn, - "proper use of libc types in foreign item definitions" -} - -declare_lint! { - /// The `uses_power_alignment` lint detects specific `repr(C)` - /// aggregates on AIX. - /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment - /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment), - /// which can also be set for XLC by `#pragma align(power)` or - /// `-qalign=power`. Aggregates with a floating-point type as the - /// recursively first field (as in "at offset 0") modify the layout of - /// *subsequent* fields of the associated structs to use an alignment value - /// where the floating-point type is aligned on a 4-byte boundary. - /// - /// Effectively, subsequent floating-point fields act as-if they are `repr(packed(4))`. This - /// would be unsound to do in a `repr(C)` type without all the restrictions that come with - /// `repr(packed)`. Rust instead chooses a layout that maintains soundness of Rust code, at the - /// expense of incompatibility with C code. - /// - /// ### Example - /// - /// ```rust,ignore (fails on non-powerpc64-ibm-aix) - /// #[repr(C)] - /// pub struct Floats { - /// a: f64, - /// b: u8, - /// c: f64, - /// } - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - /// --> :5:3 - /// | - /// 5 | c: f64, - /// | ^^^^^^ - /// | - /// = note: `#[warn(uses_power_alignment)]` on by default - /// ``` - /// - /// ### Explanation - /// - /// The power alignment rule specifies that the above struct has the - /// following alignment: - /// - offset_of!(Floats, a) == 0 - /// - offset_of!(Floats, b) == 8 - /// - offset_of!(Floats, c) == 12 - /// - /// However, Rust currently aligns `c` at `offset_of!(Floats, c) == 16`. - /// Using offset 12 would be unsound since `f64` generally must be 8-aligned on this target. - /// Thus, a warning is produced for the above struct. - USES_POWER_ALIGNMENT, - Warn, - "Structs do not follow the power alignment rule under repr(C)" -} - -declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]); - -#[derive(Clone, Copy)] -pub(crate) enum CItemKind { - Declaration, - Definition, -} - -struct ImproperCTypesVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - mode: CItemKind, -} - -/// Accumulator for recursive ffi type checking -struct CTypesVisitorState<'tcx> { - cache: FxHashSet>, - /// The original type being checked, before we recursed - /// to any other types it contains. - base_ty: Ty<'tcx>, -} - -enum FfiResult<'tcx> { - FfiSafe, - FfiPhantom(Ty<'tcx>), - FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option }, -} - pub(crate) fn nonnull_optimization_guaranteed<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, @@ -848,19 +704,38 @@ pub(crate) fn transparent_newtype_field<'a, 'tcx>( }) } +/// for a given ADT variant, list which fields are non-1ZST +/// (`repr(transparent)` guarantees that there is at most one) +pub(crate) fn map_non_1zst_fields<'a, 'tcx>( + tcx: TyCtxt<'tcx>, + variant: &'a ty::VariantDef, +) -> Vec { + let typing_env = ty::TypingEnv::non_body_analysis(tcx, variant.def_id); + variant + .fields + .iter() + .map(|field| { + let field_ty = tcx.type_of(field.did).instantiate_identity(); + let is_1zst = tcx + .layout_of(typing_env.as_query_input(field_ty)) + .is_ok_and(|layout| layout.is_1zst()); + !is_1zst + }) + .collect() +} + /// Is type known to be non-null? fn ty_is_known_nonnull<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, - mode: CItemKind, ) -> bool { let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); match ty.kind() { ty::FnPtr(..) => true, ty::Ref(..) => true, - ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true, + ty::Adt(def, _) if def.is_box() => true, ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => { let marked_non_null = nonnull_optimization_guaranteed(tcx, *def); @@ -876,10 +751,10 @@ fn ty_is_known_nonnull<'tcx>( def.variants() .iter() .filter_map(|variant| transparent_newtype_field(tcx, variant)) - .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode)) + .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args))) } ty::Pat(base, pat) => { - ty_is_known_nonnull(tcx, typing_env, *base, mode) + ty_is_known_nonnull(tcx, typing_env, *base) || pat_ty_is_known_nonnull(tcx, typing_env, *pat) } _ => false, @@ -983,14 +858,14 @@ fn is_niche_optimization_candidate<'tcx>( } /// Check if this enum can be safely exported based on the "nullable pointer optimization". If it -/// can, return the type that `ty` can be safely converted to, otherwise return `None`. +/// can, return the type that `ty` can be safely converted to/from, otherwise return `None`. /// Currently restricted to function pointers, boxes, references, `core::num::NonZero`, -/// `core::ptr::NonNull`, and `#[repr(transparent)]` newtypes. +/// `core::ptr::NonNull`, `#[repr(transparent)]` newtypes, and int-range pattern types. pub(crate) fn repr_nullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, - ckind: CItemKind, + checked_conversion_is_from: bool, ) -> Option> { debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty); match ty.kind() { @@ -1015,7 +890,21 @@ pub(crate) fn repr_nullable_ptr<'tcx>( _ => return None, }; - if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) { + if let ty::Pat(base, pat) = field_ty.kind() { + return if let Some(disallowed) = get_pat_disallowed_value_count(*pat) { + if disallowed != 1 && checked_conversion_is_from { + // if there are values not taken into account by the optionlike Enum + // then we can't safely convert from the base type, only the pattern type + Some(field_ty) + } else { + get_nullable_type_from_pat(tcx, typing_env, *base, *pat) + } + } else { + None + }; + } + + if !ty_is_known_nonnull(tcx, typing_env, field_ty) { return None; } @@ -1051,742 +940,238 @@ pub(crate) fn repr_nullable_ptr<'tcx>( } None } - ty::Pat(base, pat) => get_nullable_type_from_pat(tcx, typing_env, *base, *pat), - _ => None, - } -} - -fn get_nullable_type_from_pat<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - base: Ty<'tcx>, - pat: ty::Pattern<'tcx>, -) -> Option> { - match *pat { - ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base), - ty::PatternKind::Or(patterns) => { - let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?; - for &pat in &patterns[1..] { - assert_eq!(first, get_nullable_type_from_pat(tcx, typing_env, base, pat)?); + ty::Pat(base, pat) => { + if checked_conversion_is_from && get_pat_disallowed_value_count(*pat).is_some() { + // if there are values not taken into account by the pattern (the usual case) + // then we can't safely convert from the base type + None + } else { + get_nullable_type_from_pat(tcx, typing_env, *base, *pat) } - Some(first) } + _ => None, } } -impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - /// Check if the type is array and emit an unsafe type lint. - fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - if let ty::Array(..) = ty.kind() { - self.emit_ffi_unsafe_type_lint( - ty, - sp, - fluent::lint_improper_ctypes_array_reason, - Some(fluent::lint_improper_ctypes_array_help), - ); - true - } else { - false - } - } - - /// Checks if the given field's type is "ffi-safe". - fn check_field_type_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, - field: &ty::FieldDef, - args: GenericArgsRef<'tcx>, - ) -> FfiResult<'tcx> { - let field_ty = field.ty(self.cx.tcx, args); - let field_ty = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), field_ty) - .unwrap_or(field_ty); - self.check_type_for_ffi(acc, field_ty) - } - - /// Checks if the given `VariantDef`'s field types are "ffi-safe". - fn check_variant_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, - ty: Ty<'tcx>, - def: ty::AdtDef<'tcx>, - variant: &ty::VariantDef, - args: GenericArgsRef<'tcx>, - ) -> FfiResult<'tcx> { - use FfiResult::*; - let transparent_with_all_zst_fields = if def.repr().transparent() { - if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { - // Transparent newtypes have at most one non-ZST field which needs to be checked.. - match self.check_field_type_for_ffi(acc, field, args) { - FfiUnsafe { ty, .. } if ty.is_unit() => (), - r => return r, - } +/// return the number of disallowed values in a pattern type +/// note that Some(0) actually maps to 2^128 rather than 0 +pub(crate) fn get_pat_disallowed_value_count<'tcx>(pat: ty::Pattern<'tcx>) -> Option { + // note the logic in this function assumes that signed ints use one's complement representation, + // which I believe is a requirement for rust + + /// find numeric metadata on a pair of range bounds + /// if None, assume that there are no bounds specified + /// and that this is a usize. in other words, all values are allowed + fn unwrap_start_end<'tcx>( + start: Const<'tcx>, + end: Const<'tcx>, + ) -> (bool, Size, ScalarInt, ScalarInt) { + let usable_bound = match (start.try_to_value(), end.try_to_value()) { + (Some(ty), _) | (_, Some(ty)) => ty, + (None, None) => bug!( + "pattern range should have at least one defined value: {:?} - {:?}", + start, + end, + ), + }; + let usable_size = usable_bound.valtree.unwrap_leaf().size(); + let is_signed = match usable_bound.ty.kind() { + ty::Int(_) => true, + ty::Uint(_) | ty::Char => false, + kind @ _ => bug!("unexpected non-scalar base for pattern bounds: {:?}", kind), + }; - false - } else { - // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all - // `PhantomData`). - true + let end = match end.try_to_value() { + Some(end) => end.valtree.unwrap_leaf(), + None => { + let max_val = if is_signed { + usable_size.signed_int_max() as u128 + } else { + usable_size.unsigned_int_max() + }; + ScalarInt::try_from_uint(max_val, usable_size).unwrap() } - } else { - false }; - - // 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 { - all_phantom &= match self.check_field_type_for_ffi(acc, field, args) { - FfiSafe => false, - // `()` fields are FFI-safe! - FfiUnsafe { ty, .. } if ty.is_unit() => false, - FfiPhantom(..) => true, - r @ FfiUnsafe { .. } => return r, + let start = match start.try_to_value() { + Some(start) => start.valtree.unwrap_leaf(), + None => { + let min_val = if is_signed { + (usable_size.signed_int_min() as u128) & usable_size.unsigned_int_max() + } else { + 0_u128 + }; + ScalarInt::try_from_uint(min_val, usable_size).unwrap() } - } - - if all_phantom { - FfiPhantom(ty) - } else if transparent_with_all_zst_fields { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } - } else { - FfiSafe - } + }; + (is_signed, usable_size, start, end) } - /// Checks if the given type is "ffi-safe" (has a stable, well-defined - /// representation which can be exported to C code). - fn check_type_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { - use FfiResult::*; - - let tcx = self.cx.tcx; - - // Protect against infinite recursion, for example - // `struct S(*mut S);`. - // FIXME: A recursion limit is necessary as well, for irregular - // recursive types. - if !acc.cache.insert(ty) { - return FfiSafe; - } - - match *ty.kind() { - ty::Adt(def, args) => { - if let Some(boxed) = ty.boxed_ty() - && matches!(self.mode, CItemKind::Definition) - { - if boxed.is_sized(tcx, self.cx.typing_env()) { - return FfiSafe; - } else { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_box, - help: None, - }; - } - } - if def.is_phantom_data() { - return FfiPhantom(ty); - } - match def.adt_kind() { - AdtKind::Struct | AdtKind::Union => { - if let Some(sym::cstring_type | sym::cstr_type) = - tcx.get_diagnostic_name(def.did()) - && !acc.base_ty.is_mutable_ptr() - { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_cstr_reason, - help: Some(fluent::lint_improper_ctypes_cstr_help), - }; - } - - if !def.repr().c() && !def.repr().transparent() { - return FfiUnsafe { - ty, - reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_layout_reason - } else { - fluent::lint_improper_ctypes_union_layout_reason - }, - help: if def.is_struct() { - Some(fluent::lint_improper_ctypes_struct_layout_help) - } else { - Some(fluent::lint_improper_ctypes_union_layout_help) - }, - }; - } - - if def.non_enum_variant().field_list_has_applicable_non_exhaustive() { - return FfiUnsafe { - ty, - reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_non_exhaustive - } else { - fluent::lint_improper_ctypes_union_non_exhaustive - }, - help: None, - }; - } - - if def.non_enum_variant().fields.is_empty() { - return FfiUnsafe { - ty, - reason: if def.is_struct() { - fluent::lint_improper_ctypes_struct_fieldless_reason - } else { - fluent::lint_improper_ctypes_union_fieldless_reason - }, - help: if def.is_struct() { - Some(fluent::lint_improper_ctypes_struct_fieldless_help) - } else { - Some(fluent::lint_improper_ctypes_union_fieldless_help) - }, - }; - } - - self.check_variant_for_ffi(acc, ty, def, def.non_enum_variant(), args) - } - AdtKind::Enum => { - if def.variants().is_empty() { - // Empty enums are okay... although sort of useless. - return FfiSafe; - } - // Check for a repr() attribute to specify the size of the - // discriminant. - if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() - { - // Special-case types like `Option` and `Result` - if let Some(ty) = - repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty, self.mode) - { - return self.check_type_for_ffi(acc, ty); - } - - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_enum_repr_reason, - help: Some(fluent::lint_improper_ctypes_enum_repr_help), - }; - } - - if let Some(IntegerType::Fixed(Integer::I128, _)) = def.repr().int { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_128bit, - help: None, - }; - } + match *pat { + ty::PatternKind::Range { start, end } => { + let (is_signed, scalar_size, start, end) = unwrap_start_end(start, end); + let (scalar_min, scalar_max) = if is_signed { + ( + (scalar_size.signed_int_min() as u128) & scalar_size.unsigned_int_max(), + scalar_size.signed_int_max() as u128, + ) + } else { + (0, scalar_size.unsigned_int_max()) + }; - use improper_ctypes::check_non_exhaustive_variant; - - let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); - // Check the contained variants. - let ret = def.variants().iter().try_for_each(|variant| { - check_non_exhaustive_variant(non_exhaustive, variant) - .map_break(|reason| FfiUnsafe { ty, reason, help: None })?; - - match self.check_variant_for_ffi(acc, ty, def, variant, args) { - FfiSafe => ControlFlow::Continue(()), - r => ControlFlow::Break(r), - } - }); - if let ControlFlow::Break(result) = ret { - return result; - } + if (start.to_bits(scalar_size), end.to_bits(scalar_size)) == (scalar_min, scalar_max) { + return None; + } - FfiSafe + // note: allow overflow here because negative values are allowed in the scalars represented here + let allowed_value_count_minus1 = + u128::overflowing_sub(end.to_bits(scalar_size), start.to_bits(scalar_size)).0 + & scalar_size.unsigned_int_max(); + let disallowed_value_count = + u128::overflowing_sub(scalar_size.unsigned_int_max(), allowed_value_count_minus1).0; + Some(disallowed_value_count) + } + ty::PatternKind::Or(patterns) => { + // first, get a simplified an sorted view of the ranges + let (is_signed, scalar_size, mut ranges) = { + let (is_signed, size, start, end) = match &*patterns[0] { + ty::PatternKind::Range { start, end } => unwrap_start_end(*start, *end), + ty::PatternKind::Or(_) => bug!("recursive \"or\" patterns?"), + }; + (is_signed, size, vec![(start, end)]) + }; + let scalar_max = if is_signed { + scalar_size.signed_int_max() as u128 + } else { + scalar_size.unsigned_int_max() + }; + ranges.reserve(patterns.len() - 1); + for pat in patterns.iter().skip(1) { + match *pat { + ty::PatternKind::Range { start, end } => { + let (is_this_signed, this_scalar_size, start, end) = + unwrap_start_end(start, end); + assert_eq!(is_signed, is_this_signed); + assert_eq!(scalar_size, this_scalar_size); + ranges.push((start, end)) } + ty::PatternKind::Or(_) => bug!("recursive \"or\" patterns?"), } } - - ty::Char => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_char_reason, - help: Some(fluent::lint_improper_ctypes_char_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 } - } - - // Primitive types with a stable representation. - ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, - - ty::Slice(_) => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_slice_reason, - help: Some(fluent::lint_improper_ctypes_slice_help), - }, - - ty::Dynamic(..) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None } - } - - ty::Str => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_str_reason, - help: Some(fluent::lint_improper_ctypes_str_help), - }, - - ty::Tuple(..) => FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_tuple_reason, - help: Some(fluent::lint_improper_ctypes_tuple_help), - }, - - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) - if { - matches!(self.mode, CItemKind::Definition) - && ty.is_sized(self.cx.tcx, self.cx.typing_env()) - } => - { - FfiSafe - } - - ty::RawPtr(ty, _) - if match ty.kind() { - ty::Tuple(tuple) => tuple.is_empty(), - _ => false, - } => - { - FfiSafe - } - - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty), - - ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty), - - ty::FnPtr(sig_tys, hdr) => { - let sig = sig_tys.with(hdr); - if sig.abi().is_rustic_abi() { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_fnptr_reason, - help: Some(fluent::lint_improper_ctypes_fnptr_help), + ranges.sort_by_key(|(start, _end)| { + let is_positive = + if is_signed { start.to_bits(scalar_size) <= scalar_max } else { true }; + (is_positive, start.to_bits(scalar_size)) + }); + + // then, range per range, look at the sizes of the gaps left in between + // (`prev_tail` is the highest value currently accounted for by the ranges, + // unless the first range has not been dealt with yet) + let mut prev_tail = scalar_max; + let mut disallowed_value_count = 0_u128; + let mut only_had_overlaps = true; + + for (range_i, (start, end)) in ranges.into_iter().enumerate() { + let (start, end) = (start.to_bits(scalar_size), end.to_bits(scalar_size)); + + // if the start of the current range is lower + // than the current-highest-range-end, ... + let current_range_overlap = + if is_signed && prev_tail > scalar_max && start <= scalar_max { + false + } else if start <= u128::overflowing_add(prev_tail, 1).0 { + range_i > 0 // no overlap possible when dealing with the first range + } else { + false }; - } - - let sig = tcx.instantiate_bound_regions_with_erased(sig); - for arg in sig.inputs() { - match self.check_type_for_ffi(acc, *arg) { - FfiSafe => {} - r => return r, + if current_range_overlap { + // update the curent-highest-range-end, if the current range has a higher end + if is_signed { + if prev_tail > scalar_max && end <= scalar_max { + prev_tail = end; + } else if prev_tail <= scalar_max && end > scalar_max { + // nothing to do here + } else { + // prev_tail and end have the same sign + prev_tail = u128::max(prev_tail, end) + } + } else { + // prev_tail and end have the same sign + prev_tail = u128::max(prev_tail, end) } - } - - let ret_ty = sig.output(); - if ret_ty.is_unit() { - return FfiSafe; - } - - self.check_type_for_ffi(acc, ret_ty) - } - - ty::Foreign(..) => FfiSafe, - - // While opaque types are checked for earlier, if a projection in a struct field - // normalizes to an opaque type, then it will reach this branch. - ty::Alias(ty::Opaque, ..) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None } - } - - // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe, - // so they are currently ignored for the purposes of this lint. - ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..) - if matches!(self.mode, CItemKind::Definition) => - { - FfiSafe - } - - ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), - - ty::Param(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) - | ty::Infer(..) - | ty::Bound(..) - | ty::Error(_) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Placeholder(..) - | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), - } - } - - fn emit_ffi_unsafe_type_lint( - &mut self, - ty: Ty<'tcx>, - sp: Span, - note: DiagMessage, - help: Option, - ) { - let lint = match self.mode { - CItemKind::Declaration => IMPROPER_CTYPES, - CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, - }; - let desc = match self.mode { - CItemKind::Declaration => "block", - CItemKind::Definition => "fn", - }; - let span_note = if let ty::Adt(def, _) = ty.kind() - && let Some(sp) = self.cx.tcx.hir_span_if_local(def.did()) - { - Some(sp) - } else { - None - }; - self.cx.emit_span_lint( - lint, - sp, - ImproperCTypes { ty, desc, label: sp, help, note, span_note }, - ); - } - - fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - struct ProhibitOpaqueTypes; - impl<'tcx> ty::TypeVisitor> for ProhibitOpaqueTypes { - type Result = ControlFlow>; - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if !ty.has_opaque_types() { - return ControlFlow::Continue(()); - } - - if let ty::Alias(ty::Opaque, ..) = ty.kind() { - ControlFlow::Break(ty) } else { - ty.super_visit_with(self) + // no range overlap: first, add the newfound disallowed values to the count + only_had_overlaps = false; + let new_gap = u128::overflowing_sub( + start, + u128::overflowing_add(prev_tail, 1).0 & scalar_size.unsigned_int_max(), + ) + .0 & scalar_size.unsigned_int_max(); + disallowed_value_count = + u128::overflowing_add(disallowed_value_count, new_gap).0; + prev_tail = end; } } - } - - if let Some(ty) = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), ty) - .unwrap_or(ty) - .visit_with(&mut ProhibitOpaqueTypes) - .break_value() - { - self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None); - true - } else { - false - } - } - - fn check_type_for_ffi_and_report_errors( - &mut self, - sp: Span, - ty: Ty<'tcx>, - is_static: bool, - is_return_type: bool, - ) { - if self.check_for_opaque_ty(sp, ty) { - // We've already emitted an error due to an opaque type. - return; - } - - let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty); - - // C doesn't really support passing arrays by value - the only way to pass an array by value - // is through a struct. So, first test that the top level isn't an array, and then - // recursively check the types inside. - if !is_static && self.check_for_array_ty(sp, ty) { - return; - } - - // Don't report FFI errors for unit return types. This check exists here, and not in - // the caller (where it would make more sense) so that normalization has definitely - // happened. - if is_return_type && ty.is_unit() { - return; - } - - let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty }; - match self.check_type_for_ffi(&mut acc, ty) { - FfiResult::FfiSafe => {} - FfiResult::FfiPhantom(ty) => { - self.emit_ffi_unsafe_type_lint( - ty, - sp, - fluent::lint_improper_ctypes_only_phantomdata, - None, - ); - } - FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); - } - } - } - - /// Check if a function's argument types and result type are "ffi-safe". - /// - /// For a external ABI function, argument types and the result type are walked to find fn-ptr - /// types that have external ABIs, as these still need checked. - fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) { - let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity(); - let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); - - for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) { - self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false); - } - } - - if let hir::FnRetTy::Return(ret_hir) = decl.output { - for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) { - self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true); - } - } - } - - /// Check if a function's argument types and result type are "ffi-safe". - fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) { - let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity(); - let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); - - for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { - self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false); - } - - if let hir::FnRetTy::Return(ret_hir) = decl.output { - self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true); - } - } - - fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) { - let ty = self.cx.tcx.type_of(id).instantiate_identity(); - self.check_type_for_ffi_and_report_errors(span, ty, true, false); - } - - /// Find any fn-ptr types with external ABIs in `ty`. - /// - /// For example, `Option` returns `extern "C" fn()` - fn find_fn_ptr_ty_with_external_abi( - &self, - hir_ty: &hir::Ty<'tcx>, - ty: Ty<'tcx>, - ) -> Vec<(Ty<'tcx>, Span)> { - struct FnPtrFinder<'tcx> { - spans: Vec, - tys: Vec>, - } - - impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> { - fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { - debug!(?ty); - if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind - && !abi.is_rustic_abi() - { - self.spans.push(ty.span); - } - - hir::intravisit::walk_ty(self, ty) + if prev_tail != scalar_max { + disallowed_value_count = u128::overflowing_add( + disallowed_value_count, + u128::overflowing_sub(scalar_max, prev_tail).0, + ) + .0; + only_had_overlaps = false; } - } - - impl<'tcx> ty::TypeVisitor> for FnPtrFinder<'tcx> { - type Result = (); - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if let ty::FnPtr(_, hdr) = ty.kind() - && !hdr.abi.is_rustic_abi() - { - self.tys.push(ty); - } - - ty.super_visit_with(self) - } + if only_had_overlaps { None } else { Some(disallowed_value_count) } } - - let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() }; - ty.visit_with(&mut visitor); - visitor.visit_ty_unambig(hir_ty); - - iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect() } } -impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { - fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) { - let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration }; - let abi = cx.tcx.hir_get_foreign_abi(it.hir_id()); - - match it.kind { - hir::ForeignItemKind::Fn(sig, _, _) => { - if abi.is_rustic_abi() { - vis.check_fn(it.owner_id.def_id, sig.decl) - } else { - vis.check_foreign_fn(it.owner_id.def_id, sig.decl); - } - } - hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => { - vis.check_foreign_static(it.owner_id, ty.span); +fn get_nullable_type_from_pat<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + base: Ty<'tcx>, + pat: ty::Pattern<'tcx>, +) -> Option> { + match *pat { + ty::PatternKind::Range { .. } => get_nullable_type(tcx, typing_env, base), + ty::PatternKind::Or(patterns) => { + let first = get_nullable_type_from_pat(tcx, typing_env, base, patterns[0])?; + for &pat in &patterns[1..] { + assert_eq!(first, get_nullable_type_from_pat(tcx, typing_env, base, pat)?); } - hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), + Some(first) } } } -impl ImproperCTypesDefinitions { - fn check_ty_maybe_containing_foreign_fnptr<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - hir_ty: &'tcx hir::Ty<'_>, - ty: Ty<'tcx>, - ) { - let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; - for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) { - vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false); - } - } - - fn check_arg_for_power_alignment<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - ) -> bool { - assert!(cx.tcx.sess.target.os == "aix"); - // Structs (under repr(C)) follow the power alignment rule if: - // - the first field of the struct is a floating-point type that - // is greater than 4-bytes, or - // - the first field of the struct is an aggregate whose - // recursively first field is a floating-point type greater than - // 4 bytes. - if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 { - return true; - } else if let Adt(adt_def, _) = ty.kind() - && adt_def.is_struct() - && adt_def.repr().c() - && !adt_def.repr().packed() - && adt_def.repr().align.is_none() - { - let struct_variant = adt_def.variant(VariantIdx::ZERO); - // Within a nested struct, all fields are examined to correctly - // report if any fields after the nested struct within the - // original struct are misaligned. - for struct_field in &struct_variant.fields { - let field_ty = cx.tcx.type_of(struct_field.did).instantiate_identity(); - if self.check_arg_for_power_alignment(cx, field_ty) { - return true; - } - } - } - return false; - } - - fn check_struct_for_power_alignment<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - item: &'tcx hir::Item<'tcx>, - ) { - let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id()); - // repr(C) structs also with packed or aligned representation - // should be ignored. - if adt_def.repr().c() - && !adt_def.repr().packed() - && adt_def.repr().align.is_none() - && cx.tcx.sess.target.os == "aix" - && !adt_def.all_fields().next().is_none() +/// determines wether or not `outer_ty` is an option-like enum, with the same size as its contained type, `ty`. +/// this ASSUMES that `ty` is a type that is already 'inside' of `outer_ty`. +fn is_outer_optionlike_around_ty<'tcx>( + cx: &LateContext<'tcx>, + outer_ty: Ty<'tcx>, + ty: Ty<'tcx>, +) -> bool { + // three things to check to be sure outer_ty is option-like (since we know we reached the current ty from there) + // That outer_ty is an enum, that this enum doesn't have a defined discriminant representation, + // and the the outer_ty's size is that of ty. + if let ty::Adt(def, _) = outer_ty.kind() { + if (!matches!(def.adt_kind(), AdtKind::Enum)) + || def.repr().c() + || def.repr().transparent() + || def.repr().int.is_some() { - let struct_variant_data = item.expect_struct().1; - for field_def in struct_variant_data.fields().iter().skip(1) { - // Struct fields (after the first field) are checked for the - // power alignment rule, as fields after the first are likely - // to be the fields that are misaligned. - let def_id = field_def.def_id; - let ty = cx.tcx.type_of(def_id).instantiate_identity(); - if self.check_arg_for_power_alignment(cx, ty) { - cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment); - } - } - } - } -} + false + } else { + let (tcx, typing_env) = (cx.tcx, cx.typing_env()); -/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in -/// `extern "C" { }` blocks): -/// -/// - `extern "" fn` definitions are checked in the same way as the -/// `ImproperCtypesDeclarations` visitor checks functions if `` is external (e.g. "C"). -/// - All other items which contain types (e.g. other functions, struct definitions, etc) are -/// checked for extern fn-ptrs with external ABIs. -impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { - match item.kind { - hir::ItemKind::Static(_, ty, ..) - | hir::ItemKind::Const(_, ty, ..) - | hir::ItemKind::TyAlias(_, ty, ..) => { - self.check_ty_maybe_containing_foreign_fnptr( - cx, - ty, - cx.tcx.type_of(item.owner_id).instantiate_identity(), - ); + // see the insides of super::repr_nullable_ptr() + let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env); + match (compute_size_skeleton(ty), compute_size_skeleton(outer_ty)) { + (Ok(sk1), Ok(sk2)) => sk1.same_size(sk2), + _ => false, } - // See `check_fn`.. - hir::ItemKind::Fn { .. } => {} - // Structs are checked based on if they follow the power alignment - // rule (under repr(C)). - hir::ItemKind::Struct(..) => { - self.check_struct_for_power_alignment(cx, item); - } - // See `check_field_def`.. - hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} - // Doesn't define something that can contain a external type to be checked. - hir::ItemKind::Impl(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::Trait(..) - | hir::ItemKind::GlobalAsm { .. } - | hir::ItemKind::ForeignMod { .. } - | hir::ItemKind::Mod(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Use(..) - | hir::ItemKind::ExternCrate(..) => {} - } - } - - fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { - self.check_ty_maybe_containing_foreign_fnptr( - cx, - field.ty, - cx.tcx.type_of(field.def_id).instantiate_identity(), - ); - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: hir::intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'_>, - _: &'tcx hir::Body<'_>, - _: Span, - id: LocalDefId, - ) { - use hir::intravisit::FnKind; - - let abi = match kind { - FnKind::ItemFn(_, _, header, ..) => header.abi, - FnKind::Method(_, sig, ..) => sig.header.abi, - _ => return, - }; - - let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; - if abi.is_rustic_abi() { - vis.check_fn(id, decl); - } else { - vis.check_foreign_fn(id, decl); } + } else { + false } } diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 13afa540afcf6..f2211d00ce1b2 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -1,12 +1,61 @@ +use std::cmp::{Eq, PartialEq}; +use std::iter; use std::ops::ControlFlow; +use rustc_abi::{Integer, IntegerType, VariantIdx}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::def::CtorKind; -use rustc_middle::ty; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg}; +use rustc_middle::bug; +use rustc_middle::ty::{ + self, Adt, AdtDef, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, +}; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, sym}; +use rustc_type_ir::{Binder, FnSig}; +use tracing::debug; -use crate::fluent_generated as fluent; +use super::repr_nullable_ptr; +use crate::lints::{ImproperCTypes, ImproperCTypesLayer, UsesPowerAlignment}; +use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; + +type Sig<'tcx> = Binder, FnSig>>; + +// FIXME: it seems that tests/ui/lint/opaque-ty-ffi-normalization-cycle.rs relies this: +// we consider opaque aliases that normalise to something else to be unsafe. +// ...is it the behaviour we want? +/// a modified version of cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty) +/// so that opaque types prevent normalisation once region erasure occurs +fn erase_and_maybe_normalize<'tcx>(cx: &LateContext<'tcx>, value: Ty<'tcx>) -> Ty<'tcx> { + if (!value.has_aliases()) || value.has_opaque_types() { + cx.tcx.erase_regions(value) + } else { + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), value).unwrap_or(value) + // note: the code above ^^^ would only cause a call to the commented code below vvv + //let value = cx.tcx.erase_regions(value); + //let mut folder = TryNormalizeAfterErasingRegionsFolder::new(cx.tcx, cx.typing_env()); + //value.try_fold_with(&mut folder).unwrap_or(value) + } +} + +// getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple) +#[inline] +fn get_type_from_field<'tcx>( + cx: &LateContext<'tcx>, + field: &ty::FieldDef, + args: GenericArgsRef<'tcx>, +) -> Ty<'tcx> { + let field_ty = field.ty(cx.tcx, args); + erase_and_maybe_normalize(cx, field_ty) +} /// Check a variant of a non-exhaustive enum for improper ctypes +/// returns two bools: "we have FFI-unsafety due to non-exhaustive enum" and +/// "we have FFI-unsafety due to a non-exhaustive enum variant" /// /// We treat `#[non_exhaustive] enum` as "ensure that code will compile if new variants are added". /// This includes linting, on a best-effort basis. There are valid additions that are unlikely. @@ -14,10 +63,10 @@ use crate::fluent_generated as fluent; /// Adding a data-carrying variant to an existing C-like enum that is passed to C is "unlikely", /// so we don't need the lint to account for it. /// e.g. going from enum Foo { A, B, C } to enum Foo { A, B, C, D(u32) }. -pub(crate) fn check_non_exhaustive_variant( +pub(crate) fn flag_non_exhaustive_variant( non_exhaustive_variant_list: bool, variant: &ty::VariantDef, -) -> ControlFlow { +) -> (bool, bool) { // non_exhaustive suggests it is possible that someone might break ABI // see: https://github.com/rust-lang/rust/issues/44109#issuecomment-537583344 // so warn on complex enums being used outside their crate @@ -26,18 +75,2089 @@ pub(crate) fn check_non_exhaustive_variant( // with an enum like `#[repr(u8)] enum Enum { A(DataA), B(DataB), }` // but exempt enums with unit ctors like C's (e.g. from rust-bindgen) if variant_has_complex_ctor(variant) { - return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive); + return (true, false); } } if variant.field_list_has_applicable_non_exhaustive() { - return ControlFlow::Break(fluent::lint_improper_ctypes_non_exhaustive_variant); + return (false, true); } - ControlFlow::Continue(()) + (false, false) } fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { // CtorKind::Const means a "unit" ctor !matches!(variant.ctor_kind(), Some(CtorKind::Const)) } + +/// a way to keep track of what we want to lint for FFI-safety +/// in other words, the nature of the "original item" being checked, and its relation +/// to FFI boundaries +#[derive(Clone, Copy, Debug)] +enum CItemKind { + /// Imported items in an `extern "C"` block (function declarations, static variables) -> IMPROPER_CTYPES + ImportedExtern, + /// `extern "C"` function definitions, to be used elsewhere -> IMPROPER_C_FN_DEFINITIONS, + /// (FIXME: can we detect static variables made to be exported?) + ExportedFunction, + /// `extern "C"` function pointers -> IMPROPER_C_CALLBACKS, + Callback, + /// `repr(C)` structs/enums/unions -> IMPROPER_CTYPE_DEFINITIONS + AdtDef, +} + +#[derive(Clone, Debug)] +struct FfiUnsafeReason<'tcx> { + ty: Ty<'tcx>, + note: DiagMessage, + help: Option, + inner: Option>>, +} + +/// A single explanation (out of possibly multiple) +/// telling why a given element is rendered FFI-unsafe. +/// This goes as deep as the 'core cause', but it might be located elsewhere, possibly in a different crate. +/// So, we also track the 'smallest' type in the explanation that appears in the span of the unsafe element. +/// (we call this the 'cause' or the 'local cause' of the unsafety) +#[derive(Clone, Debug)] +struct FfiUnsafeExplanation<'tcx> { + /// a stack of incrementally "smaller" types, justifications and help messages, + /// ending with the 'core reason' why something is FFI-unsafe, making everything around it also unsafe + reason: Box>, + /// override the type considered the local cause of the FFI-unsafety + /// (e.g.: even if the lint goes into detail as to why a struct used as a function arguement + /// is unsafe, have the first lint line say that the fault lies in the use of said struct) + override_cause_ty: Option>, +} + +/// the result describing the safety (or lack thereof) of a given type. +#[derive(Clone, Debug)] +enum FfiResult<'tcx> { + /// the type is known to be safe + FfiSafe, + /// the type is only a phantom annotation + /// (safe in some contexts, unsafe in others) + FfiPhantom(Ty<'tcx>), + /// the type is not safe. + /// there might be any number of "explanations" as to why, + /// each being a stack of "reasons" going from the type + /// to a core cause of FFI-unsafety + FfiUnsafe(Vec>), +} + +impl<'tcx> FfiResult<'tcx> { + /// Simplified creation of the FfiUnsafe variant for a single unsafety reason + fn new_with_reason(ty: Ty<'tcx>, note: DiagMessage, help: Option) -> Self { + Self::FfiUnsafe(vec![FfiUnsafeExplanation { + override_cause_ty: None, + reason: Box::new(FfiUnsafeReason { ty, help, note, inner: None }), + }]) + } + + /// If the FfiUnsafe variant, 'wraps' all reasons, + /// creating new `FfiUnsafeReason`s, putting the originals as their `inner` fields. + /// Otherwise, keep unchanged + fn wrap_all(self, ty: Ty<'tcx>, note: DiagMessage, help: Option) -> Self { + match self { + Self::FfiUnsafe(this) => { + let unsafeties = this + .into_iter() + .map(|FfiUnsafeExplanation { reason, override_cause_ty }| { + let reason = Box::new(FfiUnsafeReason { + ty, + help: help.clone(), + note: note.clone(), + inner: Some(reason), + }); + FfiUnsafeExplanation { reason, override_cause_ty } + }) + .collect::>(); + Self::FfiUnsafe(unsafeties) + } + r @ _ => r, + } + } + /// If the FfiPhantom variant, turns it into a FfiUnsafe version. + /// Otherwise, keep unchanged. + fn forbid_phantom(self) -> Self { + match self { + Self::FfiPhantom(ty) => { + Self::new_with_reason(ty, fluent::lint_improper_ctypes_only_phantomdata, None) + } + _ => self, + } + } + + /// Selectively "pluck" some explanations out of a FfiResult::FfiUnsafe, + /// if the note at their core reason is one in a provided list. + /// if the FfiResult is not FfiUnsafe, or if no reasons are plucked, + /// then return FfiSafe. + fn take_with_core_note(&mut self, notes: &[DiagMessage]) -> Self { + match self { + Self::FfiUnsafe(this) => { + let mut remaining_explanations = vec![]; + std::mem::swap(this, &mut remaining_explanations); + let mut filtered_explanations = vec![]; + let mut remaining_explanations = remaining_explanations + .into_iter() + .filter_map(|explanation| { + let mut reason = explanation.reason.as_ref(); + while let Some(ref inner) = reason.inner { + reason = inner.as_ref(); + } + let mut does_remain = true; + for note_match in notes { + if note_match == &reason.note { + does_remain = false; + break; + } + } + if does_remain { + Some(explanation) + } else { + filtered_explanations.push(explanation); + None + } + }) + .collect::>(); + std::mem::swap(this, &mut remaining_explanations); + if filtered_explanations.len() > 0 { + Self::FfiUnsafe(filtered_explanations) + } else { + Self::FfiSafe + } + } + _ => Self::FfiSafe, + } + } + + /// wrap around code that generates FfiResults "from a different cause". + /// for instance, if we have a repr(C) struct in a function's argument, FFI unsafeties inside the struct + /// are to be blamed on the struct and not the members. + /// This is where we use this wrapper, to tell "all FFI-unsafeties in there are caused by this `ty`" + fn with_overrides(mut self, override_cause_ty: Option>) -> FfiResult<'tcx> { + use FfiResult::*; + + if let FfiUnsafe(ref mut explanations) = self { + explanations.iter_mut().for_each(|explanation| { + explanation.override_cause_ty = override_cause_ty; + }); + } + self + } +} + +impl<'tcx> std::ops::AddAssign> for FfiResult<'tcx> { + fn add_assign(&mut self, other: Self) { + // note: we shouldn't really encounter FfiPhantoms here, they should be dealt with beforehand + // still, this function deals with them in a reasonable way, I think + + match (self, other) { + (Self::FfiUnsafe(myself), Self::FfiUnsafe(mut other_reasons)) => { + myself.append(&mut other_reasons); + } + (Self::FfiUnsafe(_), _) => { + // nothing to do + } + (myself, other @ Self::FfiUnsafe(_)) => { + *myself = other; + } + (Self::FfiPhantom(ty1), Self::FfiPhantom(ty2)) => { + debug!("whoops, both FfiPhantom: self({:?}) += other({:?})", ty1, ty2); + } + (myself @ Self::FfiSafe, other @ Self::FfiPhantom(_)) => { + *myself = other; + } + (_, Self::FfiSafe) => { + // nothing to do + } + } + } +} +impl<'tcx> std::ops::Add> for FfiResult<'tcx> { + type Output = FfiResult<'tcx>; + fn add(mut self, other: Self) -> Self::Output { + self += other; + self + } +} + +/// Determine if a type is sized or not, and whether it affects references/pointers/boxes to it +#[derive(Clone, Copy)] +enum TypeSizedness { + /// type of definite size (pointers are C-compatible) + Definite, + /// unsized type because it includes an opaque/foreign type (pointers are C-compatible) + UnsizedWithExternType, + /// unsized type for other reasons (slice, string, dyn Trait, closure, ...) (pointers are not C-compatible) + UnsizedWithMetadata, + /// not known, usually for placeholder types (Self in non-impl trait functions, type parameters, aliases, the like) + NotYetKnown, +} + +/// what type indirection points to a given type +#[derive(Clone, Copy)] +enum IndirectionType { + /// box (valid non-null pointer, owns pointee) + Box, + /// ref (valid non-null pointer, borrows pointee) + Ref, + /// raw pointer (not necessarily non-null or valid. no info on ownership) + RawPtr, +} + +/// Is this type unsized because it contains (or is) a foreign type? +/// (Returns Err if the type happens to be sized after all) +fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness { + let tcx = cx.tcx; + + // note that sizedness is unrelated to inhabitedness + if ty.is_sized(tcx, cx.typing_env()) { + TypeSizedness::Definite + } else { + // the overall type is !Sized or ?Sized + match ty.kind() { + ty::Slice(_) => TypeSizedness::UnsizedWithMetadata, + ty::Str => TypeSizedness::UnsizedWithMetadata, + ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata, + ty::Foreign(..) => TypeSizedness::UnsizedWithExternType, + ty::Adt(def, args) => { + // for now assume: boxes and phantoms don't mess with this + match def.adt_kind() { + AdtKind::Union | AdtKind::Enum => { + bug!("unions and enums are necessarily sized") + } + AdtKind::Struct => { + if let Some(sym::cstring_type | sym::cstr_type) = + tcx.get_diagnostic_name(def.did()) + { + return TypeSizedness::UnsizedWithMetadata; + } + + // note: non-exhaustive structs from other crates are not assumed to be ?Sized + // for the purpose of sizedness, it seems we are allowed to look at its current contents. + + if def.non_enum_variant().fields.is_empty() { + bug!("an empty struct is necessarily sized"); + } + + let variant = def.non_enum_variant(); + + // only the last field may be !Sized (or ?Sized in the case of type params) + let last_field = match (&variant.fields).iter().last(){ + Some(last_field) => last_field, + // even nonexhaustive-empty structs from another crate are considered Sized + // (eventhough one could add a !Sized field to them) + None => bug!("Empty struct should be Sized, right?"), // + }; + let field_ty = get_type_from_field(cx, last_field, args); + match get_type_sizedness(cx, field_ty) { + s @ (TypeSizedness::UnsizedWithMetadata + | TypeSizedness::UnsizedWithExternType + | TypeSizedness::NotYetKnown) => s, + TypeSizedness::Definite => { + bug!("failed to find the reason why struct `{:?}` is unsized", ty) + } + } + } + } + } + ty::Tuple(tuple) => { + // only the last field may be !Sized (or ?Sized in the case of type params) + let item_ty: Ty<'tcx> = match tuple.last() { + Some(item_ty) => *item_ty, + None => bug!("Empty tuple (AKA unit type) should be Sized, right?"), + }; + let item_ty = erase_and_maybe_normalize(cx, item_ty); + match get_type_sizedness(cx, item_ty) { + s @ (TypeSizedness::UnsizedWithMetadata + | TypeSizedness::UnsizedWithExternType + | TypeSizedness::NotYetKnown) => s, + TypeSizedness::Definite => { + bug!("failed to find the reason why tuple `{:?}` is unsized", ty) + } + } + } + + ty_kind @ (ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Array(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnPtr(..) + | ty::Never + | ty::Pat(..) // these are (for now) numeric types with a range-based restriction + ) => { + // those types are all sized, right? + bug!( + "This ty_kind (`{:?}`) should be sized, yet we are in a branch of code that deals with unsized types.", + ty_kind, + ) + } + + // While opaque types are checked for earlier, if a projection in a struct field + // normalizes to an opaque type, then it will reach ty::Alias(ty::Opaque) here. + ty::Param(..) | ty::Alias(ty::Opaque | ty::Projection | ty::Inherent, ..) => { + return TypeSizedness::NotYetKnown; + } + + ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), + + ty::Alias(ty::Free, ..) + | ty::Infer(..) + | ty::Bound(..) + | ty::Error(_) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Placeholder(..) + | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), + } + } +} + +#[allow(non_snake_case)] +mod CTypesVisitorStateFlags { + pub(super) const NO_FLAGS: u8 = 0b00000; + /// for use in (externally-linked) static variables + pub(super) const STATIC: u8 = 0b00001; + /// for use in functions in general + pub(super) const FUNC: u8 = 0b00010; + /// for variables in function returns (implicitly: not for static variables) + pub(super) const FN_RETURN: u8 = 0b00100; + /// for variables in functions which are defined in rust (implicitly: not for static variables) + pub(super) const FN_DEFINED: u8 = 0b01000; + /// for time where we are only defining the type of something + /// (struct/enum/union definitions, FnPtrs) + pub(super) const THEORETICAL: u8 = 0b10000; +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum CTypesVisitorState { + None = CTypesVisitorStateFlags::NO_FLAGS, + // uses bitflags from CTypesVisitorStateFlags + StaticTy = CTypesVisitorStateFlags::STATIC, + AdtDef = CTypesVisitorStateFlags::THEORETICAL, + ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_DEFINED, + ReturnTyInDefinition = CTypesVisitorStateFlags::FUNC + | CTypesVisitorStateFlags::FN_RETURN + | CTypesVisitorStateFlags::FN_DEFINED, + ArgumentTyInDeclaration = CTypesVisitorStateFlags::FUNC, + ReturnTyInDeclaration = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_RETURN, + ArgumentTyInFnPtr = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::THEORETICAL, + ReturnTyInFnPtr = CTypesVisitorStateFlags::FUNC + | CTypesVisitorStateFlags::THEORETICAL + | CTypesVisitorStateFlags::FN_RETURN, +} + +impl CTypesVisitorState { + /// whether the type is used in a static variable + fn is_in_static(self) -> bool { + use CTypesVisitorStateFlags::*; + let ret = ((self as u8) & STATIC) != 0; + if ret { + assert!(((self as u8) & FUNC) == 0); + } + ret + } + /// whether the type is used in a function + fn is_in_function(self) -> bool { + use CTypesVisitorStateFlags::*; + let ret = ((self as u8) & FUNC) != 0; + if ret { + assert!(((self as u8) & STATIC) == 0); + } + ret + } + /// whether the type is used (directly or not) in a function, in return position + fn is_in_function_return(self) -> bool { + use CTypesVisitorStateFlags::*; + let ret = ((self as u8) & FN_RETURN) != 0; + #[cfg(debug_assertions)] + if ret { + assert!(self.is_in_function()); + } + ret + } + /// whether the type is used (directly or not) in a defined function + /// in other words, whether or not we allow non-FFI-safe types behind a C pointer, + /// to be treated as an opaque type on the other side of the FFI boundary + fn is_in_defined_function(self) -> bool { + use CTypesVisitorStateFlags::*; + let ret = ((self as u8) & FN_DEFINED) != 0; + #[cfg(debug_assertions)] + if ret { + assert!(self.is_in_function()); + } + ret + } + /// whether we the type is used (directly or not) in a function pointer type + fn is_in_fn_ptr(self) -> bool { + use CTypesVisitorStateFlags::*; + ((self as u8) & THEORETICAL) != 0 && self.is_in_function() + } + + /// whether the type is currently being defined + fn is_being_defined(self) -> bool { + self == Self::AdtDef + } + + /// whether we can expect type parameters and co in a given type + fn can_expect_ty_params(self) -> bool { + use CTypesVisitorStateFlags::*; + // rust-defined functions, as well as FnPtrs and ADT definitions + ((self as u8) & (FN_DEFINED | THEORETICAL)) != 0 + } + + /// whether the value for that type might come from the non-rust side of a FFI boundary + /// this is particularly useful for non-raw pointers, since rust assume they are non-null + fn value_may_be_unchecked(self) -> bool { + if self == Self::AdtDef { + // some ADTs are only used to go through the FFI boundary in one direction, + // so let's not make hasty judgement + false + } else if self.is_in_static() { + true + } else if self.is_in_defined_function() { + // function definitions are assumed to be maybe-not-rust-caller, rust-callee + !self.is_in_function_return() + } else if self.is_in_fn_ptr() { + // 4 cases for function pointers: + // - rust caller, rust callee: everything comes from rust + // - non-rust-caller, non-rust callee: declaring invariants that are not valid + // is suboptimal, but ultimately not our problem + // - non-rust-caller, rust callee: there will be a function declaration somewhere, + // let's assume it will raise the appropriate warning in our stead + // - rust caller, non-rust callee: it's possible that the function is a callback, + // not something from a pre-declared API. + // so, in theory, we need to care about the function return being possibly non-rust-controlled. + // sadly, we need to ignore this because making pointers out of rust-defined functions + // would force to systematically cast or overwrap their return types... + // FIXME: is there anything better we can do here? + false + } else { + // function declarations are assumed to be rust-caller, non-rust-callee + self.is_in_function_return() + } + } +} + +/// visitor used to recursively traverse MIR types and evaluate FFI-safety +/// It uses ``check_*`` methods as entrypoints to be called elsewhere, +/// and ``visit_*`` methods to recurse +struct ImproperCTypesVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// type cache to prevent infinite recursion + ty_cache: FxHashSet>, +} + +impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { cx, ty_cache: FxHashSet::default() } + } + + /// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call + fn visit_fnptr( + &mut self, + _state: CTypesVisitorState, + _outer_ty: Option>, + ty: Ty<'tcx>, + sig: Sig<'tcx>, + ) -> FfiResult<'tcx> { + use FfiResult::*; + debug_assert!(!sig.abi().is_rustic_abi()); + + let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); + + let mut all_ffires = FfiSafe; + + for arg in sig.inputs() { + let ffi_res = self.visit_type(CTypesVisitorState::ArgumentTyInFnPtr, Some(ty), *arg); + all_ffires += ffi_res.forbid_phantom().wrap_all( + ty, + fluent::lint_improper_ctypes_fnptr_indirect_reason, + None, + ); + } + + let ret_ty = sig.output(); + + let ffi_res = self.visit_type(CTypesVisitorState::ReturnTyInFnPtr, Some(ty), ret_ty); + all_ffires += ffi_res.forbid_phantom().wrap_all( + ty, + fluent::lint_improper_ctypes_fnptr_indirect_reason, + None, + ); + all_ffires + } + + /// Checks whether an uninhabited type (one without valid values) is safe-ish to have here + fn visit_uninhabited( + &mut self, + state: CTypesVisitorState, + outer_ty: Option>, + ty: Ty<'tcx>, + ) -> FfiResult<'tcx> { + if state.is_being_defined() + || (state.is_in_function_return() + && matches!(outer_ty.map(|ty| ty.kind()), None | Some(ty::FnPtr(..)),)) + { + FfiResult::FfiSafe + } else { + let help = if state.is_in_function_return() { + Some(fluent::lint_improper_ctypes_uninhabited_use_direct) + } else { + None + }; + let desc = match ty.kind() { + ty::Adt(..) => { + if state.is_in_function_return() { + fluent::lint_improper_ctypes_uninhabited_enum_deep + } else { + fluent::lint_improper_ctypes_uninhabited_enum + } + } + ty::Never => { + if state.is_in_function_return() { + fluent::lint_improper_ctypes_uninhabited_never_deep + } else { + fluent::lint_improper_ctypes_uninhabited_never + } + } + r @ _ => bug!("unexpected ty_kind in uninhabited type handling: {:?}", r), + }; + FfiResult::new_with_reason(ty, desc, help) + } + } + + /// Checks if a simple numeric (int, float) type has an actual portable definition + /// for the compile target + fn visit_numeric(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + // FIXME: for now, this is very incomplete, and seems to assume a x86_64 target + match ty.kind() { + ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_128bit, None) + } + ty::Int(..) | ty::Uint(..) | ty::Float(..) => FfiResult::FfiSafe, + + ty::Char => FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_char_reason, + Some(fluent::lint_improper_ctypes_char_help), + ), + _ => bug!("visit_numeric is to be called with numeric (int, float) types"), + } + } + + /// Return the right help for Cstring and Cstr-linked unsafety + fn visit_cstr(&mut self, outer_ty: Option>, ty: Ty<'tcx>) -> FfiResult<'tcx> { + debug_assert!(matches!(ty.kind(), ty::Adt(def, _) + if matches!( + self.cx.tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + )); + + let help = if let Some(outer_ty) = outer_ty { + match outer_ty.kind() { + ty::Ref(..) | ty::RawPtr(..) => { + if outer_ty.is_mutable_ptr() { + fluent::lint_improper_ctypes_cstr_help_mut + } else { + fluent::lint_improper_ctypes_cstr_help_const + } + } + ty::Adt(..) if outer_ty.boxed_ty().is_some() => { + fluent::lint_improper_ctypes_cstr_help_owned + } + _ => fluent::lint_improper_ctypes_cstr_help_unknown, + } + } else { + fluent::lint_improper_ctypes_cstr_help_owned + }; + + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_cstr_reason, Some(help)) + } + + /// Checks if the given indirection (box,ref,pointer) is "ffi-safe" + fn visit_indirection( + &mut self, + state: CTypesVisitorState, + outer_ty: Option>, + ty: Ty<'tcx>, + inner_ty: Ty<'tcx>, + indirection_type: IndirectionType, + ) -> FfiResult<'tcx> { + let tcx = self.cx.tcx; + + if let ty::Adt(def, _) = inner_ty.kind() { + if let Some(diag_name @ (sym::cstring_type | sym::cstr_type)) = + tcx.get_diagnostic_name(def.did()) + { + // we have better error messages when checking for C-strings directly + let mut cstr_res = self.visit_cstr(Some(ty), inner_ty); // always unsafe with one depth-one reason. + + // Cstr pointer have metadata, CString is Sized + if diag_name == sym::cstr_type { + // we need to override the "type" part of `cstr_res`'s only FfiResultReason + // so it says that it's the use of the indirection that is unsafe + match cstr_res { + FfiResult::FfiUnsafe(ref mut reasons) => { + reasons.first_mut().unwrap().reason.ty = ty; + } + _ => unreachable!(), + } + let note = match indirection_type { + IndirectionType::RawPtr => fluent::lint_improper_ctypes_unsized_ptr, + IndirectionType::Ref => fluent::lint_improper_ctypes_unsized_ref, + IndirectionType::Box => fluent::lint_improper_ctypes_unsized_box, + }; + return cstr_res.wrap_all(ty, note, None); + } else { + return cstr_res; + } + } + } + + // there are three remaining concerns with the pointer: + // - is the pointer compatible with a C pointer in the first place? (if not, only send that error message) + // - is the pointee FFI-safe? (it might not matter, see mere lines below) + // - does the pointer type contain a non-zero assumption, but has a value given by non-rust code? + // this block deals with the first two. + let mut ffi_res = match get_type_sizedness(self.cx, inner_ty) { + TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => { + // FIXME: + // for now, we consider this to be safe even in the case of a FFI-unsafe pointee + // this is technically only safe if the pointer is never dereferenced on the non-rust + // side of the FFI boundary, i.e. if the type is to be treated as opaque + // there are techniques to flag those pointees as opaque, but not always, so we can only enforce this + // in some cases. + FfiResult::FfiSafe + } + TypeSizedness::NotYetKnown => { + // types with sizedness NotYetKnown: + // - Type params (with `variable: impl Trait` shorthand or not) + // (function definitions only, let's see how this interacts with monomorphisation) + // - Self in trait functions/methods + // - Opaque return types + // (always FFI-unsafe) + // - non-exhaustive structs/enums/unions from other crates + // (always FFI-unsafe) + // (for the three first, this is unless there is a `+Sized` bound involved) + + // whether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box), + // so let's not wrap the current context around a potential FfiUnsafe type param. + self.visit_type(state, Some(ty), inner_ty) + } + TypeSizedness::UnsizedWithMetadata => { + let help = match inner_ty.kind() { + ty::Str => Some(fluent::lint_improper_ctypes_str_help), + ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), + _ => None, + }; + let reason = match indirection_type { + IndirectionType::RawPtr => fluent::lint_improper_ctypes_unsized_ptr, + IndirectionType::Ref => fluent::lint_improper_ctypes_unsized_ref, + IndirectionType::Box => fluent::lint_improper_ctypes_unsized_box, + }; + return FfiResult::new_with_reason(ty, reason, help); + } + }; + + // and now the third concern (does the pointer type contain a non-zero assumption, and is the value given by non-rust code?) + // technically, pointers with non-rust-given values could also be misaligned, pointing to the wrong thing, or outright dangling, but we assume they never are + ffi_res += if state.value_may_be_unchecked() { + let has_nonnull_assumption = match indirection_type { + IndirectionType::RawPtr => false, + IndirectionType::Ref | IndirectionType::Box => true, + }; + let has_optionlike_wrapper = if let Some(outer_ty) = outer_ty { + super::is_outer_optionlike_around_ty(self.cx, outer_ty, ty) + } else { + false + }; + + if has_nonnull_assumption && !has_optionlike_wrapper { + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_ptr_validity_reason, + Some(fluent::lint_improper_ctypes_ptr_validity_help), + ) + } else { + FfiResult::FfiSafe + } + } else { + FfiResult::FfiSafe + }; + + ffi_res + } + + /// Checks if the given `VariantDef`'s field types are "ffi-safe". + fn visit_variant_fields( + &mut self, + state: CTypesVisitorState, + ty: Ty<'tcx>, + def: ty::AdtDef<'tcx>, + variant: &ty::VariantDef, + args: GenericArgsRef<'tcx>, + ) -> FfiResult<'tcx> { + use FfiResult::*; + + let mut ffires_accumulator = FfiSafe; + + let (transparent_with_all_zst_fields, field_list) = + if !matches!(def.adt_kind(), AdtKind::Enum) && def.repr().transparent() { + // determine if there is 0 or 1 non-1ZST field, and which it is. + // (note: for enums, "transparent" means 1-variant) + if ty.is_privately_uninhabited(self.cx.tcx, self.cx.typing_env()) { + // let's consider transparent structs are considered unsafe if uninhabited, + // even if that is because of fields otherwise ignored in FFI-safety checks + // FIXME: and also maybe this should be "!is_inhabited_from" but from where? + ffires_accumulator += variant + .fields + .iter() + .map(|field| { + let field_ty = get_type_from_field(self.cx, field, args); + let mut field_res = self.visit_type(state, Some(ty), field_ty); + field_res.take_with_core_note(&[ + fluent::lint_improper_ctypes_uninhabited_enum, + fluent::lint_improper_ctypes_uninhabited_enum_deep, + fluent::lint_improper_ctypes_uninhabited_never, + fluent::lint_improper_ctypes_uninhabited_never_deep, + ]) + }) + .reduce(|r1, r2| r1 + r2) + .unwrap() // if uninhabited, then >0 fields + } + 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 later + (false, vec![field]) + } else { + // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all + // `PhantomData`). + (true, variant.fields.iter().collect::>()) + } + } else { + (false, variant.fields.iter().collect::>()) + }; + + // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. + let mut all_phantom = !variant.fields.is_empty(); + let mut fields_ok_list = vec![true; field_list.len()]; + + for (field_i, field) in field_list.into_iter().enumerate() { + let field_ty = get_type_from_field(self.cx, field, args); + let ffi_res = self.visit_type(state, Some(ty), field_ty); + + // checking that this is not an FfiUnsafe due to an unit type: + // visit_type should be smart enough to not consider it unsafe if called from another ADT + #[cfg(debug_assertions)] + if let FfiUnsafe(ref reasons) = ffi_res { + if let (1, Some(FfiUnsafeExplanation { reason, .. })) = + (reasons.len(), reasons.first()) + { + let FfiUnsafeReason { ty, .. } = reason.as_ref(); + debug_assert!(!ty.is_unit()); + } + } + + all_phantom &= match ffi_res { + FfiPhantom(..) => true, + FfiSafe => false, + r @ FfiUnsafe { .. } => { + fields_ok_list[field_i] = false; + ffires_accumulator += r; + false + } + } + } + + // if we have bad fields, also report a possible transparent_with_all_zst_fields + // (if this combination is somehow possible) + // otherwide, having all fields be phantoms + // takes priority over transparent_with_all_zst_fields + if let FfiUnsafe(explanations) = ffires_accumulator { + // we assume the repr() of this ADT is either non-packed C or transparent. + debug_assert!( + (def.repr().c() && !def.repr().packed()) + || def.repr().transparent() + || def.repr().int.is_some() + ); + + if def.repr().transparent() || matches!(def.adt_kind(), AdtKind::Enum) { + let field_ffires = FfiUnsafe(explanations).wrap_all( + ty, + fluent::lint_improper_ctypes_struct_dueto, + None, + ); + if transparent_with_all_zst_fields { + field_ffires + + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_struct_zst, + None, + ) + } else { + field_ffires + } + } else { + // since we have a repr(C) struct/union, there's a chance that we have some unsafe fields, + // but also exactly one non-1ZST field that is FFI-safe: + // we want to suggest repr(transparent) here. + // (FIXME: confirm that this makes sense for unions once #60405 / RFC2645 stabilises) + let non_1zst_fields = super::map_non_1zst_fields(self.cx.tcx, variant); + let (last_non_1zst, non_1zst_count) = non_1zst_fields.into_iter().enumerate().fold( + (None, 0_usize), + |(prev_nz, count), (field_i, is_nz)| { + if is_nz { (Some(field_i), count + 1) } else { (prev_nz, count) } + }, + ); + let help = if non_1zst_count == 1 + && last_non_1zst.map(|field_i| fields_ok_list[field_i]) == Some(true) + { + if ty.is_privately_uninhabited(self.cx.tcx, self.cx.typing_env()) { + // uninhabited types can't be helped by being turned transparent + None + } else { + match def.adt_kind() { + AdtKind::Struct => { + Some(fluent::lint_improper_ctypes_struct_consider_transparent) + } + AdtKind::Union => { + Some(fluent::lint_improper_ctypes_union_consider_transparent) + } + AdtKind::Enum => bug!("cannot suggest an enum to be repr(transparent)"), + } + } + } else { + None + }; + + FfiUnsafe(explanations).wrap_all( + ty, + fluent::lint_improper_ctypes_struct_dueto, + help, + ) + } + } else if all_phantom { + FfiPhantom(ty) + } else if transparent_with_all_zst_fields { + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_struct_zst, None) + } else { + FfiSafe + } + } + + fn visit_struct_or_union( + &mut self, + state: CTypesVisitorState, + outer_ty: Option>, + ty: Ty<'tcx>, + def: ty::AdtDef<'tcx>, + args: GenericArgsRef<'tcx>, + ) -> FfiResult<'tcx> { + debug_assert!(matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)); + + if !((def.repr().c() && !def.repr().packed()) || def.repr().transparent()) { + // FIXME packed reprs prevent C compatibility, right? + return FfiResult::new_with_reason( + ty, + if def.is_struct() { + fluent::lint_improper_ctypes_struct_layout_reason + } else { + fluent::lint_improper_ctypes_union_layout_reason + }, + if def.is_struct() { + Some(fluent::lint_improper_ctypes_struct_layout_help) + } else { + // (FIXME: confirm that this makes sense for unions once #60405 / RFC2645 stabilises) + Some(fluent::lint_improper_ctypes_union_layout_help) + }, + ); + } + + if def.non_enum_variant().field_list_has_applicable_non_exhaustive() { + return FfiResult::new_with_reason( + ty, + if def.is_struct() { + fluent::lint_improper_ctypes_struct_non_exhaustive + } else { + fluent::lint_improper_ctypes_union_non_exhaustive + }, + None, + ); + } + + let ffires = if def.non_enum_variant().fields.is_empty() { + FfiResult::new_with_reason( + ty, + if def.is_struct() { + fluent::lint_improper_ctypes_struct_fieldless_reason + } else { + fluent::lint_improper_ctypes_union_fieldless_reason + }, + if def.is_struct() { + Some(fluent::lint_improper_ctypes_struct_fieldless_help) + } else { + Some(fluent::lint_improper_ctypes_union_fieldless_help) + }, + ) + } else { + self.visit_variant_fields(state, ty, def, def.non_enum_variant(), args) + }; + + // from now on in the function, we lint the actual insides of the struct/union: if something is wrong, + // then the "fault" comes from inside the struct itself. + // even if we add more details to the lint, the initial line must specify that the FFI-unsafety is because of the struct + // - if the struct is from the same crate, there is another warning on its definition anyway + // (unless it's about Boxes and references without Option<_> + // which is partly why we keep the details as to why that struct is FFI-unsafe) + // - if the struct is from another crate, then there's not much that can be done anyways + // + // if outer_ty.is_some() || !state.is_being_defined() then this enum is visited in the middle of another lint, + // so we override the "cause type" of the lint + let override_cause_ty = + if state.is_being_defined() { outer_ty.and(Some(ty)) } else { Some(ty) }; + + ffires.with_overrides(override_cause_ty) + } + + fn visit_enum( + &mut self, + state: CTypesVisitorState, + outer_ty: Option>, + ty: Ty<'tcx>, + def: ty::AdtDef<'tcx>, + args: GenericArgsRef<'tcx>, + ) -> FfiResult<'tcx> { + debug_assert!(matches!(def.adt_kind(), AdtKind::Enum)); + use FfiResult::*; + + if def.variants().is_empty() { + // Empty enums are implicitely handled as the never type: + return self.visit_uninhabited(state, outer_ty, ty); + } + // Check for a repr() attribute to specify the size of the + // discriminant. + if !(def.repr().c() && !def.repr().packed()) + && !def.repr().transparent() + && def.repr().int.is_none() + { + // Special-case types like `Option` and `Result` + if let Some(inner_ty) = repr_nullable_ptr( + self.cx.tcx, + self.cx.typing_env(), + ty, + state.value_may_be_unchecked(), + ) { + return self.visit_type(state, Some(ty), inner_ty); + } + + return FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_enum_repr_reason, + Some(fluent::lint_improper_ctypes_enum_repr_help), + ); + } + + if let Some(IntegerType::Fixed(Integer::I128, _)) = def.repr().int { + return FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_128bit, None); + } + + let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); + // Check the contained variants. + + let (mut nonexhaustive_flag, mut nonexhaustive_variant_flag) = (false, false); + def.variants().iter().for_each(|variant| { + let (nonex_enum, nonex_var) = flag_non_exhaustive_variant(non_exhaustive, variant); + nonexhaustive_flag |= nonex_enum; + nonexhaustive_variant_flag |= nonex_var; + }); + + // "nonexhaustive" lints only happen outside of the crate defining the enum, so no CItemKind override + // (meaning: the fault lies in the function call, not the enum) + if nonexhaustive_flag { + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_non_exhaustive, None) + } else if nonexhaustive_variant_flag { + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_non_exhaustive_variant, + None, + ) + } else { + // small caveat to checking the variants: we authorise up to n-1 invariants + // to be unsafe because uninhabited. + // so for now let's isolate those unsafeties + let mut variants_uninhabited_ffires = vec![FfiSafe; def.variants().len()]; + + let mut ffires = def + .variants() + .iter() + .enumerate() + .map(|(variant_i, variant)| { + let mut variant_res = self.visit_variant_fields(state, ty, def, variant, args); + variants_uninhabited_ffires[variant_i] = variant_res.take_with_core_note(&[ + fluent::lint_improper_ctypes_uninhabited_enum, + fluent::lint_improper_ctypes_uninhabited_enum_deep, + fluent::lint_improper_ctypes_uninhabited_never, + fluent::lint_improper_ctypes_uninhabited_never_deep, + ]); + // FIXME: check that enums allow any (up to all) variants to be phantoms? + // (previous code says no, but I don't know why? the problem with phantoms is that they're ZSTs, right?) + variant_res.forbid_phantom() + }) + .reduce(|r1, r2| r1 + r2) + .unwrap(); // always at least one variant if we hit this branch + + if variants_uninhabited_ffires.iter().all(|res| matches!(res, FfiUnsafe(..))) { + // if the enum is uninhabited, because all its variants are uninhabited + ffires += variants_uninhabited_ffires.into_iter().reduce(|r1, r2| r1 + r2).unwrap(); + } + + // if outer_ty.is_some() || !state.is_being_defined() then this enum is visited in the middle of another lint, + // so we override the "cause type" of the lint + // (for more detail, see comment in ``visit_struct_union`` before its call to ``ffires.with_overrides``) + let override_cause_ty = + if state.is_being_defined() { outer_ty.and(Some(ty)) } else { Some(ty) }; + ffires.with_overrides(override_cause_ty) + } + } + + /// Checks if the given type is "ffi-safe" (has a stable, well-defined + /// representation which can be exported to C code). + fn visit_type( + &mut self, + state: CTypesVisitorState, + outer_ty: Option>, + ty: Ty<'tcx>, + ) -> FfiResult<'tcx> { + use FfiResult::*; + + let tcx = self.cx.tcx; + + // Protect against infinite recursion, for example + // `struct S(*mut S);`. + // FIXME: A recursion limit is necessary as well, for irregular + // recursive types. + if !self.ty_cache.insert(ty) { + return FfiSafe; + } + + match *ty.kind() { + ty::Adt(def, args) => { + if let Some(inner_ty) = ty.boxed_ty() { + return self.visit_indirection( + state, + outer_ty, + ty, + inner_ty, + IndirectionType::Box, + ); + } + if def.is_phantom_data() { + return FfiPhantom(ty); + } + match def.adt_kind() { + AdtKind::Struct | AdtKind::Union => { + // I thought CStr (not CString) here could only be reached in non-compiling code: + // - not using an indirection would cause a compile error (this lint *currently* seems to not get triggered on such non-compiling code) + // - and using one would cause the lint to catch on the indirection before reaching its pointee + // but function *pointers* don't seem to have the same no-unsized-parameters requirement to compile + if let Some(sym::cstring_type | sym::cstr_type) = + tcx.get_diagnostic_name(def.did()) + { + return self.visit_cstr(outer_ty, ty); + } + self.visit_struct_or_union(state, outer_ty, ty, def, args) + } + AdtKind::Enum => self.visit_enum(state, outer_ty, ty, def, args), + } + } + + ty::Pat(pat_ty, pat) => { + #[cfg(debug_assertions)] + if !matches!(pat_ty.kind(), ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Char) { + bug!( + "this lint was written when pattern types could only be integers constrained to ranges" + ) + } + + let mut ffires = self.visit_numeric(pat_ty); + if state.value_may_be_unchecked() { + // if the pattern type's value can come from non-rust code, + // ensure all values of `pat_ty` are accounted for + + if matches!( + outer_ty.map(|outer_ty| super::is_outer_optionlike_around_ty( + self.cx, outer_ty, ty + )), + Some(true) + ) { + // if this is the case, then super::get_pat_disallowed_value_count has been called already + // for the optionlike wrapper, and had returned 2 or more disallowed values + debug_assert!( + matches!(super::get_pat_disallowed_value_count(pat), Some(i) if i != 1) + ); + ffires += FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_pat_int2_reason, + Some(fluent::lint_improper_ctypes_pat_int2_help), + ); + } else { + match super::get_pat_disallowed_value_count(pat) { + None => {} + Some(1) => { + ffires += FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_pat_int1_reason, + Some(fluent::lint_improper_ctypes_pat_int1_help), + ); + } + Some(_) => { + ffires += FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_pat_int2_reason, + Some(fluent::lint_improper_ctypes_pat_int2_help), + ); + } + } + } + } + ffires + } + + // types which likely have a stable representation, depending on the target architecture + ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) => self.visit_numeric(ty), + + // Primitive types with a stable representation. + ty::Bool => FfiSafe, + + ty::Slice(_) => FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_slice_reason, + Some(fluent::lint_improper_ctypes_slice_help), + ), + + ty::Dynamic(..) => { + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_dyn, None) + } + + ty::Str => FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_str_reason, + Some(fluent::lint_improper_ctypes_str_help), + ), + + ty::Tuple(tuple) => { + let empty_and_safe = if tuple.is_empty() { + match outer_ty.map(|ty| ty.kind()) { + // C functions can return void + None | Some(ty::FnPtr(..)) => state.is_in_function_return(), + // `()` fields are FFI-safe! + Some(ty::Adt(..)) => true, + Some(ty::RawPtr(..)) => true, + // most of those are not even reachable, + // but let's not worry about checking that here + _ => false, + } + } else { + false + }; + + if empty_and_safe { + FfiSafe + } else { + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_tuple_reason, + Some(fluent::lint_improper_ctypes_tuple_help), + ) + } + } + + ty::RawPtr(ty, _) + if match ty.kind() { + ty::Tuple(tuple) => tuple.is_empty(), + _ => false, + } => + { + FfiSafe + } + + ty::RawPtr(inner_ty, _) => { + return self.visit_indirection( + state, + outer_ty, + ty, + inner_ty, + IndirectionType::RawPtr, + ); + } + ty::Ref(_, inner_ty, _) => { + return self.visit_indirection(state, outer_ty, ty, inner_ty, IndirectionType::Ref); + } + + ty::Array(inner_ty, _) => { + if state.is_in_function() + && matches!(outer_ty.map(|ty| ty.kind()), None | Some(ty::FnPtr(..))) + { + // C doesn't really support passing arrays by value - the only way to pass an array by value + // is through a struct. + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_array_reason, + Some(fluent::lint_improper_ctypes_array_help), + ) + } else { + // let's allow phantoms to go through, + // since an array of 1-ZSTs is also a 1-ZST + self.visit_type(state, Some(ty), inner_ty) + } + } + + // fnptrs are a special case, they always need to be treated as + // "the element rendered unsafe" because their unsafety doesn't affect + // their surroundings, and their type is often declared inline + // as a result, don't go into them when scanning for the safety of something else + ty::FnPtr(sig_tys, hdr) => { + let sig = sig_tys.with(hdr); + let inherent_safety = if sig.abi().is_rustic_abi() { + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_fnptr_reason, + Some(fluent::lint_improper_ctypes_fnptr_help), + ) + } else { + FfiSafe + }; + + if let (Some(outer_ty), true) = (outer_ty, state.value_may_be_unchecked()) { + if !super::is_outer_optionlike_around_ty(self.cx, outer_ty, ty) { + inherent_safety + + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_ptr_validity_reason, + Some(fluent::lint_improper_ctypes_ptr_validity_help), + ) + } else { + inherent_safety + } + } else { + inherent_safety + } + } + + ty::Foreign(..) => FfiSafe, + + ty::Never => self.visit_uninhabited(state, outer_ty, ty), + + // This is only half of the checking-for-opaque-aliases story: + // since they are liable to vanish on normalisation, we need a specific to find them through + // other aliases, which is called in the next branch of this `match ty.kind()` statement + ty::Alias(ty::Opaque, ..) => { + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_opaque, None) + } + + // `extern "C" fn` function definitions can have type parameters, which may or may not be FFI-safe, + // so they are currently ignored for the purposes of this lint. + // function pointers can do the same + // + // however, these ty_kind:s can also be encountered because the type isn't normalized yet. + ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) => { + if ty.has_opaque_types() { + // FIXME: this is suboptimal because we give up + // on reporting anything *else* than the opaque part of the type + // but this is better than not reporting anything, or crashing + self.visit_for_opaque_ty(ty) + } else { + // in theory, thanks to erase_and_maybe_normalize, + // normalisation has already occured + debug_assert_eq!( + self.cx + .tcx + .try_normalize_erasing_regions(self.cx.typing_env(), ty,) + .unwrap_or(ty), + ty, + ); + + if matches!( + ty.kind(), + ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..) + ) && state.can_expect_ty_params() + { + FfiSafe + } else { + // ty::Alias(ty::Free), and all params/aliases for something + // defined beyond the FFI boundary + bug!("unexpected type in foreign function: {:?}", ty) + } + } + } + + ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), + + ty::Infer(..) + | ty::Bound(..) + | ty::Error(_) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Placeholder(..) + | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty), + } + } + + fn visit_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + struct ProhibitOpaqueTypes; + impl<'tcx> ty::TypeVisitor> for ProhibitOpaqueTypes { + type Result = ControlFlow>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if !ty.has_opaque_types() { + return ControlFlow::Continue(()); + } + + if let ty::Alias(ty::Opaque, ..) = ty.kind() { + ControlFlow::Break(ty) + } else { + ty.super_visit_with(self) + } + } + } + + if let Some(ty) = ty.visit_with(&mut ProhibitOpaqueTypes).break_value() { + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_opaque, None) + } else { + FfiResult::FfiSafe + } + } + + fn check_for_type(&mut self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { + let ty = erase_and_maybe_normalize(self.cx, ty); + self.visit_type(state, None, ty) + } + + fn check_for_fnptr(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + let ty = erase_and_maybe_normalize(self.cx, ty); + + match *ty.kind() { + ty::FnPtr(sig_tys, hdr) => { + let sig = sig_tys.with(hdr); + if sig.abi().is_rustic_abi() { + bug!( + "expected to inspect the type of an `extern \"ABI\"` FnPtr, not an internal-ABI one" + ) + } else { + self.visit_fnptr(CTypesVisitorState::None, None, ty, sig) + } + } + r @ _ => { + bug!("expected to inspect the type of an `extern \"ABI\"` FnPtr, not {:?}", r,) + } + } + } + + fn check_for_adtdef(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + use FfiResult::*; + let ty = erase_and_maybe_normalize(self.cx, ty); + + let mut ffires = match *ty.kind() { + ty::Adt(def, args) => { + if !def.did().is_local() { + bug!( + "check_adtdef expected to visit a locally-defined struct/enum/union not {:?}", + def + ); + } + + // question: how does this behave when running for "special" ADTs in the stdlib? + // answer: none of CStr, CString, Box, and PhantomData are repr(C) + let state = CTypesVisitorState::AdtDef; + match def.adt_kind() { + AdtKind::Struct | AdtKind::Union => { + self.visit_struct_or_union(state, None, ty, def, args) + } + AdtKind::Enum => self.visit_enum(state, None, ty, def, args), + } + } + r @ _ => { + bug!("expected to inspect the type of an `extern \"ABI\"` FnPtr, not {:?}", r,) + } + }; + + match &mut ffires { + // due to the way type visits work, any unsafeness that comes from the fields inside an ADT + // is uselessly "prefixed" with the fact that yes, the error occurs in that ADT + // we remove the prefixes here. + FfiUnsafe(explanations) => { + explanations.iter_mut().for_each(|explanation| { + if let Some(inner_reason) = explanation.reason.inner.take() { + debug_assert_eq!(explanation.reason.ty, ty); + debug_assert_eq!( + explanation.reason.note, + fluent::lint_improper_ctypes_struct_dueto + ); + if let Some(help) = &explanation.reason.help { + // there is an actual help message in the normally useless prefix + // make sure it gets through + debug_assert_eq!( + help, + &fluent::lint_improper_ctypes_struct_consider_transparent + ); + explanation.override_cause_ty = Some(inner_reason.ty); + explanation.reason.inner = Some(inner_reason); + } else { + explanation.reason = inner_reason; + } + } + }); + } + + // also, turn FfiPhantom into FfiSafe: unlike other places we can check, we don't want + // FfiPhantom to end up emitting a lint + ffires @ FfiPhantom(_) => *ffires = FfiSafe, + FfiSafe => {} + } + ffires + } + + fn check_arg_for_power_alignment(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + let tcx = cx.tcx; + assert!(tcx.sess.target.os == "aix"); + + // Structs (under repr(C)) follow the power alignment rule if: + // - the first field of the struct is a floating-point type that + // is greater than 4-bytes, or + // - the first field of the struct is an aggregate whose + // recursively first field is a floating-point type greater than + // 4 bytes. + if ty.is_floating_point() && ty.primitive_size(tcx).bytes() > 4 { + return true; + } else if let Adt(adt_def, _) = ty.kind() + && adt_def.is_struct() + && adt_def.repr().c() + && !adt_def.repr().packed() + && adt_def.repr().align.is_none() + { + let struct_variant = adt_def.variant(VariantIdx::ZERO); + // Within a nested struct, all fields are examined to correctly + // report if any fields after the nested struct within the + // original struct are misaligned. + for struct_field in &struct_variant.fields { + let field_ty = tcx.type_of(struct_field.did).instantiate_identity(); + if self.check_arg_for_power_alignment(cx, field_ty) { + return true; + } + } + } + return false; + } + + fn check_struct_for_power_alignment( + &self, + cx: &LateContext<'tcx>, + item: &'tcx hir::Item<'tcx>, + adt_def: AdtDef<'tcx>, + ) { + // repr(C) structs also with packed or aligned representation + // should be ignored. + debug_assert!( + adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none() + ); + if cx.tcx.sess.target.os == "aix" && !adt_def.all_fields().next().is_none() { + let struct_variant_data = item.expect_struct().1; + for field_def in struct_variant_data.fields().iter().skip(1) { + // Struct fields (after the first field) are checked for the + // power alignment rule, as fields after the first are likely + // to be the fields that are misaligned. + let def_id = field_def.def_id; + let ty = cx.tcx.type_of(def_id).instantiate_identity(); + if self.check_arg_for_power_alignment(cx, ty) { + cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment); + } + } + } + } +} + +impl ImproperCTypesLint { + /// Find and check any fn-ptr types with external ABIs in `ty`. + /// For example, `Option` checks `extern "C" fn()` + fn check_type_for_external_abi_fnptr<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + hir_ty: &hir::Ty<'tcx>, + ty: Ty<'tcx>, + ) { + struct FnPtrFinder<'tcx> { + spans: Vec, + tys: Vec>, + } + + impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> { + fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { + debug!(?ty); + if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind + && !abi.is_rustic_abi() + { + self.spans.push(ty.span); + } + + hir::intravisit::walk_ty(self, ty) + } + } + + impl<'tcx> ty::TypeVisitor> for FnPtrFinder<'tcx> { + type Result = (); + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::FnPtr(_, hdr) = ty.kind() + && !hdr.abi.is_rustic_abi() + { + self.tys.push(ty); + } + + ty.super_visit_with(self) + } + } + + let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() }; + ty.visit_with(&mut visitor); + visitor.visit_ty_unambig(hir_ty); + + let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); + all_types.for_each(|(fn_ptr_ty, span)| { + let mut visitor = ImproperCTypesVisitor::new(cx); + let ffi_res = visitor.check_for_fnptr(fn_ptr_ty); + + self.process_ffi_result(cx, span, ffi_res, CItemKind::Callback) + }); + } + + /// For a function that doesn't need to be "ffi-safe", look for fn-ptr argument/return types + /// that need to be checked for ffi-safety + fn check_fn_for_external_abi_fnptr<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + def_id: LocalDefId, + decl: &'tcx hir::FnDecl<'_>, + ) { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); + let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); + + for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { + self.check_type_for_external_abi_fnptr(cx, input_hir, *input_ty); + } + + if let hir::FnRetTy::Return(ret_hir) = decl.output { + self.check_type_for_external_abi_fnptr(cx, ret_hir, sig.output()); + } + } + + /// For a local definition of a #[repr(C)] struct/enum/union, check that it is indeed FFI-safe + fn check_reprc_adt<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + item: &'tcx hir::Item<'tcx>, + adt_def: AdtDef<'tcx>, + ) { + debug_assert!( + adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none() + ); + + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + let mut visitor = ImproperCTypesVisitor::new(cx); + + // FIXME: this following call is awkward. + // is there a way to perform its logic in MIR space rather than HIR space? + // (so that its logic can be absorbed into visitor.visit_struct_or_union) + visitor.check_struct_for_power_alignment(cx, item, adt_def); + let ffi_res = visitor.check_for_adtdef(ty); + + self.process_ffi_result(cx, item.span, ffi_res, CItemKind::AdtDef); + } + + /// Check that an extern "ABI" static variable is of a ffi-safe type + fn check_foreign_static<'tcx>(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { + let ty = cx.tcx.type_of(id).instantiate_identity(); + let mut visitor = ImproperCTypesVisitor::new(cx); + let ffi_res = visitor.check_for_type(CTypesVisitorState::StaticTy, ty); + self.process_ffi_result(cx, span, ffi_res, CItemKind::ImportedExtern); + } + + /// Check if a function's argument types and result type are "ffi-safe". + fn check_foreign_fn<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + fn_mode: CItemKind, + def_id: LocalDefId, + decl: &'tcx hir::FnDecl<'_>, + ) { + let sig = cx.tcx.fn_sig(def_id).instantiate_identity(); + let sig = cx.tcx.instantiate_bound_regions_with_erased(sig); + + for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { + let mut visitor = ImproperCTypesVisitor::new(cx); + let visit_state = match fn_mode { + CItemKind::ExportedFunction => CTypesVisitorState::ArgumentTyInDefinition, + CItemKind::ImportedExtern => CTypesVisitorState::ArgumentTyInDeclaration, + _ => bug!("check_foreign_fn cannot be called with CItemKind::{:?}", fn_mode), + }; + let ffi_res = visitor.check_for_type(visit_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 mut visitor = ImproperCTypesVisitor::new(cx); + let visit_state = match fn_mode { + CItemKind::ExportedFunction => CTypesVisitorState::ReturnTyInDefinition, + CItemKind::ImportedExtern => CTypesVisitorState::ReturnTyInDeclaration, + _ => bug!("check_foreign_fn cannot be called with CItemKind::{:?}", fn_mode), + }; + let ffi_res = visitor.check_for_type(visit_state, sig.output()); + self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); + } + } + + fn process_ffi_result<'tcx>( + &self, + cx: &LateContext<'tcx>, + sp: Span, + res: FfiResult<'tcx>, + fn_mode: CItemKind, + ) { + match res { + FfiResult::FfiSafe => {} + FfiResult::FfiPhantom(ty) => { + self.emit_ffi_unsafe_type_lint( + cx, + ty.clone(), + sp, + vec![ImproperCTypesLayer { + ty, + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + help: None, + inner_ty: None, + }], + fn_mode, + ); + } + FfiResult::FfiUnsafe(explanations) => { + for explanation in explanations { + let mut ffiresult_recursor = ControlFlow::Continue(explanation.reason.as_ref()); + let mut cimproper_layers: Vec> = vec![]; + + // this whole while block converts the arbitrarily-deep + // FfiResult stack to an ImproperCTypesLayer Vec + while let ControlFlow::Continue(FfiUnsafeReason { ty, note, help, inner }) = + ffiresult_recursor + { + if let Some(layer) = cimproper_layers.last_mut() { + layer.inner_ty = Some(ty.clone()); + } + cimproper_layers.push(ImproperCTypesLayer { + ty: ty.clone(), + inner_ty: None, + help: help.clone(), + note: note.clone(), + span_note: None, // filled later + }); + + if let Some(inner) = inner { + ffiresult_recursor = ControlFlow::Continue(inner.as_ref()); + } else { + ffiresult_recursor = ControlFlow::Break(()); + } + } + let cause_ty = if let Some(cause_ty) = explanation.override_cause_ty { + cause_ty + } else { + // should always have at least one type + cimproper_layers.last().unwrap().ty.clone() + }; + self.emit_ffi_unsafe_type_lint(cx, cause_ty, sp, cimproper_layers, fn_mode); + } + } + } + } + + fn emit_ffi_unsafe_type_lint<'tcx>( + &self, + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + sp: Span, + mut reasons: Vec>, + fn_mode: CItemKind, + ) { + let lint = match fn_mode { + CItemKind::ImportedExtern => IMPROPER_CTYPES, + CItemKind::ExportedFunction => IMPROPER_C_FN_DEFINITIONS, + CItemKind::AdtDef => IMPROPER_CTYPE_DEFINITIONS, + CItemKind::Callback => IMPROPER_C_CALLBACKS, + }; + let desc = match fn_mode { + CItemKind::ImportedExtern => "`extern` block", + CItemKind::ExportedFunction => "`extern` fn", + CItemKind::Callback => "`extern` callback", + CItemKind::AdtDef => "`repr(C)` type", + }; + for reason in reasons.iter_mut() { + reason.span_note = if let ty::Adt(def, _) = reason.ty.kind() + && let Some(sp) = cx.tcx.hir_span_if_local(def.did()) + { + Some(sp) + } else { + None + }; + } + + cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons }); + } +} + +/// IMPROPER_CTYPES checks items that are part of a header to a non-rust library +/// Namely, functions and static variables in `extern "" { }`, +/// if `` is external (e.g. "C"). +/// +/// `IMPROPER_C_CALLBACKS` checks for function pointers marked with an external ABI. +/// (fields of type `extern "" fn`, where e.g. `` is `C`) +/// these pointers are searched in all other items which contain types +/// (e.g.functions, struct definitions, etc) +/// +/// `IMPROPER_C_FN_DEFINITIONS` checks rust-defined functions that are marked +/// to be used from the other side of a FFI boundary. +/// In other words, `extern "" fn` definitions and trait-method declarations. +/// This only matters if `` is external (e.g. `C`). +/// +/// `IMPROPER_CTYPE_DEFINITIONS` checks structs/enums/unions marked with `repr(C)`, +/// assuming they are to have a fully C-compatible layout. +/// +/// and now combinatorics for pointees +impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { + fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) { + let abi = cx.tcx.hir_get_foreign_abi(it.hir_id()); + + match it.kind { + hir::ForeignItemKind::Fn(sig, _, _) => { + // fnptrs are a special case, they always need to be treated as + // "the element rendered unsafe" because their unsafety doesn't affect + // their surroundings, and their type is often declared inline + self.check_fn_for_external_abi_fnptr(cx, it.owner_id.def_id, sig.decl); + if !abi.is_rustic_abi() { + self.check_foreign_fn( + cx, + CItemKind::ImportedExtern, + it.owner_id.def_id, + sig.decl, + ); + } + } + hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => { + self.check_foreign_static(cx, it.owner_id, ty.span); + } + hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + match item.kind { + hir::ItemKind::Static(_, ty, ..) + | hir::ItemKind::Const(_, ty, ..) + | hir::ItemKind::TyAlias(_, ty, ..) => { + self.check_type_for_external_abi_fnptr( + cx, + ty, + cx.tcx.type_of(item.owner_id).instantiate_identity(), + ); + } + // See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks + hir::ItemKind::Fn { .. } => {} + hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => { + // looking for extern FnPtr:s is delegated to `check_field_def`. + let adt_def: AdtDef<'tcx> = cx.tcx.adt_def(item.owner_id.to_def_id()); + + if adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none() + { + self.check_reprc_adt(cx, item, adt_def); + } + } + + // Doesn't define something that can contain a external type to be checked. + hir::ItemKind::Impl(..) + | hir::ItemKind::TraitAlias(..) + | hir::ItemKind::Trait(..) + | hir::ItemKind::GlobalAsm { .. } + | hir::ItemKind::ForeignMod { .. } + | hir::ItemKind::Mod(..) + | hir::ItemKind::Macro(..) + | hir::ItemKind::Use(..) + | hir::ItemKind::ExternCrate(..) => {} + } + } + + fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { + self.check_type_for_external_abi_fnptr( + cx, + field.ty, + cx.tcx.type_of(field.def_id).instantiate_identity(), + ); + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: hir::intravisit::FnKind<'tcx>, + decl: &'tcx hir::FnDecl<'_>, + _: &'tcx hir::Body<'_>, + _: Span, + id: LocalDefId, + ) { + use hir::intravisit::FnKind; + + let abi = match kind { + FnKind::ItemFn(_, _, header, ..) => header.abi, + FnKind::Method(_, sig, ..) => sig.header.abi, + _ => return, + }; + + // fnptrs are a special case, they always need to be treated as + // "the element rendered unsafe" because their unsafety doesn't affect + // their surroundings, and their type is often declared inline + self.check_fn_for_external_abi_fnptr(cx, id, decl); + if !abi.is_rustic_abi() { + self.check_foreign_fn(cx, CItemKind::ExportedFunction, id, decl); + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, tr_it: &hir::TraitItem<'tcx>) { + match tr_it.kind { + hir::TraitItemKind::Const(hir_ty, _) => { + let ty = cx.tcx.type_of(hir_ty.hir_id.owner.def_id).instantiate_identity(); + self.check_type_for_external_abi_fnptr(cx, hir_ty, ty); + } + hir::TraitItemKind::Fn(sig, trait_fn) => { + match trait_fn { + // if the method is defined here, + // there is a matching ``LateLintPass::check_fn`` call, + // let's not redo that work + hir::TraitFn::Provided(_) => return, + hir::TraitFn::Required(_) => (), + } + let local_id = tr_it.owner_id.def_id; + if sig.header.abi.is_rustic_abi() { + self.check_fn_for_external_abi_fnptr(cx, local_id, sig.decl); + } else { + self.check_foreign_fn(cx, CItemKind::ExportedFunction, local_id, sig.decl); + } + } + hir::TraitItemKind::Type(_, ty_maybe) => { + if let Some(hir_ty) = ty_maybe { + let ty = cx.tcx.type_of(hir_ty.hir_id.owner.def_id).instantiate_identity(); + self.check_type_for_external_abi_fnptr(cx, hir_ty, ty); + } + } + } + } + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, im_it: &hir::ImplItem<'tcx>) { + // note: we do not skip these checks eventhough they might generate dupe warnings because: + // - the corresponding trait might be in another crate + // - the corresponding trait might have some templating involved, so only the impl has the full type information + match im_it.kind { + hir::ImplItemKind::Type(hir_ty) => { + let ty = cx.tcx.type_of(hir_ty.hir_id.owner.def_id).instantiate_identity(); + self.check_type_for_external_abi_fnptr(cx, hir_ty, ty); + } + hir::ImplItemKind::Fn(_sig, _) => { + // see ``LateLintPass::check_fn`` + } + hir::ImplItemKind::Const(hir_ty, _) => { + let ty = cx.tcx.type_of(hir_ty.hir_id.owner.def_id).instantiate_identity(); + self.check_type_for_external_abi_fnptr(cx, hir_ty, ty); + } + } + } +} + +declare_lint! { + /// The `improper_ctypes` lint detects incorrect use of types in foreign + /// modules. + /// (in other words, declarations of items defined in foreign code) + /// + /// ### Example + /// + /// ```rust + /// unsafe extern "C" { + /// static STATIC: String; + /// fn some_func(a:String); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler has several checks to verify that types used in `extern` + /// blocks are safe and follow certain rules to ensure proper + /// compatibility with the foreign interfaces. This lint is issued when it + /// detects a probable mistake in a definition. The lint usually should + /// provide a description of the issue, along with possibly a hint on how + /// to resolve it. + pub(crate) IMPROPER_CTYPES, + Warn, + "proper use of libc types in foreign modules" +} + +declare_lint! { + /// The `improper_c_fn_definitions` lint detects incorrect use of + /// [`extern` function] definitions. + /// (in other words, functions to be used by foreign code) + /// + /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// pub extern "C" fn str_type(p: &str) { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There are many parameter and return types that may be specified in an + /// `extern` function that are not compatible with the given ABI. This + /// lint is an alert that these types should not be used. The lint usually + /// should provide a description of the issue, along with possibly a hint + /// on how to resolve it. + pub(crate) IMPROPER_C_FN_DEFINITIONS, + Warn, + "proper use of libc types in foreign item definitions" +} + +declare_lint! { + /// The `improper_c_callbacks` lint detects incorrect use of + /// [`extern` function] pointers. + /// (in other words, function signatures for callbacks) + /// + /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// pub fn str_emmiter(call_me_back: extern "C" fn(&str)) { } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// There are many parameter and return types that may be specified in an + /// `extern` function that are not compatible with the given ABI. This + /// lint is an alert that these types should not be used. The lint usually + /// should provide a description of the issue, along with possibly a hint + /// on how to resolve it. + pub(crate) IMPROPER_C_CALLBACKS, + Warn, + "proper use of libc types in foreign-code-compatible callbacks" +} + +declare_lint! { + /// The `improper_ctype_definitions` lint detects incorrect use of types in + /// foreign-compatible structs, enums, and union definitions. + /// + /// ### Example + /// + /// ```rust + /// repr(C) struct StringWrapper{ + /// length: usize, + /// strung: &str, + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler has several checks to verify that types designed to be + /// compatible with foreign interfaces follow certain rules to be safe. + /// This lint is issued when it detects a probable mistake in a definition. + /// The lint usually should provide a description of the issue, + /// along with possibly a hint on how to resolve it. + pub(crate) IMPROPER_CTYPE_DEFINITIONS, + Warn, + "proper use of libc types when defining foreign-code-compatible structs" +} + +declare_lint! { + /// The `uses_power_alignment` lint detects specific `repr(C)` + /// aggregates on AIX. + /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment + /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment), + /// which can also be set for XLC by `#pragma align(power)` or + /// `-qalign=power`. Aggregates with a floating-point type as the + /// recursively first field (as in "at offset 0") modify the layout of + /// *subsequent* fields of the associated structs to use an alignment value + /// where the floating-point type is aligned on a 4-byte boundary. + /// + /// Effectively, subsequent floating-point fields act as-if they are `repr(packed(4))`. This + /// would be unsound to do in a `repr(C)` type without all the restrictions that come with + /// `repr(packed)`. Rust instead chooses a layout that maintains soundness of Rust code, at the + /// expense of incompatibility with C code. + /// + /// ### Example + /// + /// ```rust,ignore (fails on non-powerpc64-ibm-aix) + /// #[repr(C)] + /// pub struct Floats { + /// a: f64, + /// b: u8, + /// c: f64, + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + /// --> :5:3 + /// | + /// 5 | c: f64, + /// | ^^^^^^ + /// | + /// = note: `#[warn(uses_power_alignment)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// The power alignment rule specifies that the above struct has the + /// following alignment: + /// - offset_of!(Floats, a) == 0 + /// - offset_of!(Floats, b) == 8 + /// - offset_of!(Floats, c) == 12 + /// + /// However, Rust currently aligns `c` at `offset_of!(Floats, c) == 16`. + /// Using offset 12 would be unsound since `f64` generally must be 8-aligned on this target. + /// Thus, a warning is produced for the above struct. + USES_POWER_ALIGNMENT, + Warn, + "Structs do not follow the power alignment rule under repr(C)" +} + +declare_lint_pass!(ImproperCTypesLint => [ + IMPROPER_CTYPES, + IMPROPER_C_FN_DEFINITIONS, + IMPROPER_C_CALLBACKS, + IMPROPER_CTYPE_DEFINITIONS, + USES_POWER_ALIGNMENT, +]); diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index ed5edeef1617d..40c680e2c4c27 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -64,10 +64,11 @@ impl RustStringInner { /// `rustc_codegen_llvm`. #[unsafe(no_mangle)] pub unsafe extern "C" fn LLVMRustStringWriteImpl( - buf: &RustString, + buf: Option<&RustString>, slice_ptr: *const u8, // Same ABI as `*const c_char` slice_len: size_t, ) { + let buf = buf.unwrap(); let slice = unsafe { slice::from_raw_parts(slice_ptr, slice_len) }; RustStringInner::from_opaque(buf).bytes.borrow_mut().extend_from_slice(slice); } diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 37f784a322cad..7bd4d6030cae2 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -47,6 +47,7 @@ const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1; const EDGE_IDX_RIGHT_OF_CENTER: usize = B; /// The underlying representation of leaf nodes and part of the representation of internal nodes. +#[repr(C)] struct LeafNode { /// We want to be covariant in `K` and `V`. parent: Option>>, diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index b2ad0f4ac3d04..a937d2a7501b1 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -27,7 +27,8 @@ use core::any::Any; use core::panic::PanicPayload; #[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] +#[allow(unknown_lints, renamed_and_removed_lints, improper_ctypes_definitions)] // FIXME delete line once improper_c_fn_definitions exists upstream +#[allow(improper_c_fn_definitions)] pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) { unreachable!() } diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index 5f95870069dc5..b385e4e5e574f 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -52,6 +52,8 @@ static CANARY: u8 = 0; // The first two field must be `_Unwind_Exception` and `canary`, // as it may be accessed by a different version of the std with a different compiler. #[repr(C)] +#[allow(unknown_lints, renamed_and_removed_lints, improper_ctypes_definitions)] // FIXME delete line once improper_c_fn_definitions exists upstream +#[allow(improper_ctype_definitions)] // Boxed dyn is a fat pointer struct Exception { _uwe: uw::_Unwind_Exception, canary: *const u8, diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 50bd933aca204..d9e11ea52a4aa 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -88,7 +88,8 @@ unsafe extern "C" { } #[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] +#[allow(unknown_lints, renamed_and_removed_lints, improper_ctypes_definitions)] // FIXME delete line once improper_c_fn_definitions exists upstream +#[allow(improper_c_fn_definitions)] pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) { unsafe { Box::into_raw(imp::cleanup(payload)) } } diff --git a/library/proc_macro/src/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs index 3760749d83a54..3bc255e4d8926 100644 --- a/library/proc_macro/src/bridge/buffer.rs +++ b/library/proc_macro/src/bridge/buffer.rs @@ -10,8 +10,8 @@ pub struct Buffer { data: *mut u8, len: usize, capacity: usize, - reserve: extern "C" fn(Buffer, usize) -> Buffer, - drop: extern "C" fn(Buffer), + reserve: Option Buffer>, + drop: Option, } unsafe impl Sync for Buffer {} @@ -63,7 +63,7 @@ impl Buffer { pub(super) fn extend_from_array(&mut self, xs: &[u8; N]) { if xs.len() > (self.capacity - self.len) { let b = self.take(); - *self = (b.reserve)(b, xs.len()); + *self = (b.reserve.unwrap())(b, xs.len()); } unsafe { xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len()); @@ -75,7 +75,7 @@ impl Buffer { pub(super) fn extend_from_slice(&mut self, xs: &[u8]) { if xs.len() > (self.capacity - self.len) { let b = self.take(); - *self = (b.reserve)(b, xs.len()); + *self = (b.reserve.unwrap())(b, xs.len()); } unsafe { xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len()); @@ -90,7 +90,7 @@ impl Buffer { // to check for overflow. if self.len == self.capacity { let b = self.take(); - *self = (b.reserve)(b, 1); + *self = (b.reserve.unwrap())(b, 1); } unsafe { *self.data.add(self.len) = v; @@ -122,7 +122,7 @@ impl Drop for Buffer { #[inline] fn drop(&mut self) { let b = self.take(); - (b.drop)(b); + (b.drop.unwrap())(b); } } @@ -150,6 +150,6 @@ impl From> for Buffer { mem::drop(to_vec(b)); } - Buffer { data, len, capacity, reserve, drop } + Buffer { data, len, capacity, reserve: Some(reserve), drop: Some(drop) } } } diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index e7d547966a5d5..b225df6e39c61 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -358,6 +358,8 @@ impl Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream> { #[repr(C)] #[derive(Copy, Clone)] +#[allow(unknown_lints, renamed_and_removed_lints, improper_ctypes_definitions)] // FIXME delete line once improper_c_fn_definitions exists upstream +#[allow(improper_ctype_definitions)] // so many C-incompatible double-width pointers pub enum ProcMacro { CustomDerive { trait_name: &'static str, diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index e0e688434dce5..7909e3315ab2b 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; #[repr(C)] pub(super) struct Closure<'a, A, R> { - call: unsafe extern "C" fn(*mut Env, A) -> R, + call: Option R>, env: *mut Env, // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing // this, but that requires unstable features. rust-analyzer uses this code @@ -21,12 +21,12 @@ impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { unsafe extern "C" fn call R>(env: *mut Env, arg: A) -> R { unsafe { (*(env as *mut _ as *mut F))(arg) } } - Closure { call: call::, env: f as *mut _ as *mut Env, _marker: PhantomData } + Closure { call: Some(call::), env: f as *mut _ as *mut Env, _marker: PhantomData } } } impl<'a, A, R> Closure<'a, A, R> { pub(super) fn call(&mut self, arg: A) -> R { - unsafe { (self.call)(self.env, arg) } + unsafe { (self.call.unwrap_unchecked())(self.env, arg) } } } diff --git a/src/tools/clippy/tests/ui/inherent_to_string.rs b/src/tools/clippy/tests/ui/inherent_to_string.rs index 30977f2d93ac6..4a57a4a8b425f 100644 --- a/src/tools/clippy/tests/ui/inherent_to_string.rs +++ b/src/tools/clippy/tests/ui/inherent_to_string.rs @@ -1,4 +1,4 @@ -#![allow(improper_ctypes_definitions)] +#![allow(improper_c_fn_definitions)] use std::fmt; diff --git a/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.rs b/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.rs index 200f1062a3e80..f7323faf0cca3 100644 --- a/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.rs +++ b/src/tools/miri/tests/fail/function_calls/simd_feature_flag_difference.rs @@ -1,5 +1,5 @@ //@only-target: x86_64 -#![allow(improper_ctypes_definitions)] +#![allow(improper_c_fn_definitions)] use std::arch::x86_64::*; use std::mem::transmute; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 706d04484f6fe..129339bdad1db 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -446,6 +446,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, deny_since: None, }, + Lint { + label: "improper_c_callbacks", + description: r##"proper use of libc types in foreign-code-compatible callbacks"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, + Lint { + label: "improper_c_fn_definitions", + description: r##"proper use of libc types in foreign item definitions"##, + default_severity: Severity::Warning, + warn_since: None, + deny_since: None, + }, Lint { label: "improper_ctypes", description: r##"proper use of libc types in foreign modules"##, @@ -454,8 +468,8 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "improper_ctypes_definitions", - description: r##"proper use of libc types in foreign item definitions"##, + label: "improper_ctype_definitions", + description: r##"proper use of libc types when defining foreign-code-compatible structs"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 941c4abed4679..373c76f4ca3e6 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -27,7 +27,7 @@ asm_experimental_arch, unboxed_closures )] -#![allow(unused, improper_ctypes_definitions, internal_features)] +#![allow(unused, improper_ctype_definitions, internal_features)] #![no_std] #![no_core] diff --git a/tests/ui/abi/abi-sysv64-arg-passing.rs b/tests/ui/abi/abi-sysv64-arg-passing.rs index c18752418a1d6..cfc4c7200b3cc 100644 --- a/tests/ui/abi/abi-sysv64-arg-passing.rs +++ b/tests/ui/abi/abi-sysv64-arg-passing.rs @@ -33,7 +33,7 @@ // the sysv64 ABI on Windows. #[allow(dead_code)] -#[allow(improper_ctypes)] +#[allow(improper_ctypes, improper_ctype_definitions)] #[cfg(target_arch = "x86_64")] mod tests { @@ -72,6 +72,7 @@ mod tests { } #[repr(C)] + #[allow(improper_ctype_definitions)] pub struct Empty; #[repr(C)] diff --git a/tests/ui/abi/abi-sysv64-register-usage.rs b/tests/ui/abi/abi-sysv64-register-usage.rs index d2fb2ae53ac73..6f31e0533d5cd 100644 --- a/tests/ui/abi/abi-sysv64-register-usage.rs +++ b/tests/ui/abi/abi-sysv64-register-usage.rs @@ -48,7 +48,7 @@ pub struct LargeStruct(i64, i64, i64, i64, i64, i64, i64, i64); #[cfg(target_arch = "x86_64")] #[inline(never)] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "sysv64" fn large_struct_by_val(mut foo: LargeStruct) -> LargeStruct { foo.0 *= 1; foo.1 *= 2; diff --git a/tests/ui/abi/arm-unadjusted-intrinsic.rs b/tests/ui/abi/arm-unadjusted-intrinsic.rs index dcf0d9f39f673..150c1626ab348 100644 --- a/tests/ui/abi/arm-unadjusted-intrinsic.rs +++ b/tests/ui/abi/arm-unadjusted-intrinsic.rs @@ -25,6 +25,7 @@ pub struct int8x16_t(pub(crate) [i8; 16]); impl Copy for int8x16_t {} #[repr(C)] +#[allow(improper_ctype_definitions)] pub struct int8x16x4_t(pub int8x16_t, pub int8x16_t, pub int8x16_t, pub int8x16_t); impl Copy for int8x16x4_t {} diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 68706f1e821ac..c8a9d6727f21a 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -62,7 +62,8 @@ #![feature(no_core, rustc_attrs, lang_items)] #![feature(unsized_fn_params, transparent_unions)] #![no_core] -#![allow(unused, improper_ctypes_definitions, internal_features)] +#![allow(unused, internal_features)] +#![allow(improper_ctype_definitions, improper_c_fn_definitions, improper_c_callbacks)] // FIXME: some targets are broken in various ways. // Hence there are `cfg` throughout this test to disable parts of it on those targets. diff --git a/tests/ui/abi/extern/extern-pass-empty.rs b/tests/ui/abi/extern/extern-pass-empty.rs index 1ad52b128ad93..f2cb894c3cd35 100644 --- a/tests/ui/abi/extern/extern-pass-empty.rs +++ b/tests/ui/abi/extern/extern-pass-empty.rs @@ -1,5 +1,6 @@ //@ run-pass -#![allow(improper_ctypes)] // FIXME: this test is inherently not FFI-safe. +#![allow(improper_ctypes, improper_ctype_definitions)] +// FIXME: this test is inherently not FFI-safe. // Test a foreign function that accepts empty struct. diff --git a/tests/ui/abi/foreign/foreign-fn-with-byval.rs b/tests/ui/abi/foreign/foreign-fn-with-byval.rs index 9908ec2d2c01a..06b57481bb571 100644 --- a/tests/ui/abi/foreign/foreign-fn-with-byval.rs +++ b/tests/ui/abi/foreign/foreign-fn-with-byval.rs @@ -1,5 +1,5 @@ //@ run-pass -#![allow(improper_ctypes, improper_ctypes_definitions)] +#![allow(improper_ctypes, improper_c_callbacks)] #[derive(Copy, Clone)] pub struct S { diff --git a/tests/ui/abi/large-byval-align.rs b/tests/ui/abi/large-byval-align.rs index c1de841178fcd..64c5ee392351d 100644 --- a/tests/ui/abi/large-byval-align.rs +++ b/tests/ui/abi/large-byval-align.rs @@ -5,7 +5,7 @@ #[repr(align(536870912))] pub struct A(i64); -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C" fn foo(x: A) {} fn main() { diff --git a/tests/ui/abi/nullable-pointer-ffi-compat.rs b/tests/ui/abi/nullable-pointer-ffi-compat.rs index f94f838723a56..428e018103599 100644 --- a/tests/ui/abi/nullable-pointer-ffi-compat.rs +++ b/tests/ui/abi/nullable-pointer-ffi-compat.rs @@ -14,6 +14,7 @@ use std::mem; +#[allow(improper_c_fn_definitions)] // it's worried about invalid pointers given as values of x #[inline(never)] extern "C" fn foo(x: &isize) -> Option<&isize> { Some(x) } diff --git a/tests/ui/abi/simd-abi-checks-avx.rs b/tests/ui/abi/simd-abi-checks-avx.rs index 772512702ece1..eee96329b30b7 100644 --- a/tests/ui/abi/simd-abi-checks-avx.rs +++ b/tests/ui/abi/simd-abi-checks-avx.rs @@ -5,7 +5,7 @@ #![feature(avx512_target_feature)] #![feature(portable_simd)] #![feature(simd_ffi)] -#![allow(improper_ctypes_definitions)] +#![allow(improper_c_fn_definitions)] use std::arch::x86_64::*; diff --git a/tests/ui/abi/simd-abi-checks-empty-list.rs b/tests/ui/abi/simd-abi-checks-empty-list.rs index d00445b29e055..2026ccbe4ea7f 100644 --- a/tests/ui/abi/simd-abi-checks-empty-list.rs +++ b/tests/ui/abi/simd-abi-checks-empty-list.rs @@ -6,7 +6,7 @@ //@ build-fail #![no_core] #![feature(no_core, repr_simd)] -#![allow(improper_ctypes_definitions)] +#![allow(improper_c_fn_definitions)] extern crate minicore; use minicore::*; diff --git a/tests/ui/abi/simd-abi-checks-s390x.rs b/tests/ui/abi/simd-abi-checks-s390x.rs index 2d4eb7a350f25..69a640c033881 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.rs +++ b/tests/ui/abi/simd-abi-checks-s390x.rs @@ -12,7 +12,7 @@ #![feature(no_core, repr_simd, s390x_target_feature)] #![no_core] #![crate_type = "lib"] -#![allow(non_camel_case_types, improper_ctypes_definitions)] +#![allow(non_camel_case_types, improper_c_fn_definitions)] extern crate minicore; use minicore::*; diff --git a/tests/ui/abi/simd-abi-checks-sse.rs b/tests/ui/abi/simd-abi-checks-sse.rs index 817f9b6d13bc6..bcb9da0ceb4b5 100644 --- a/tests/ui/abi/simd-abi-checks-sse.rs +++ b/tests/ui/abi/simd-abi-checks-sse.rs @@ -7,7 +7,7 @@ //@ needs-llvm-components: x86 #![feature(no_core, repr_simd)] #![no_core] -#![allow(improper_ctypes_definitions)] +#![allow(improper_c_fn_definitions)] extern crate minicore; use minicore::*; diff --git a/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs b/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs index 7d21307e1b2d9..0bd51ac5e610f 100644 --- a/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs +++ b/tests/ui/abi/unsized-args-in-c-abi-issues-94223-115845.rs @@ -1,5 +1,5 @@ //@ check-pass -#![allow(improper_ctypes_definitions)] +#![allow(improper_c_fn_definitions, improper_c_callbacks)] #![feature(unsized_fn_params)] #![crate_type = "lib"] diff --git a/tests/ui/asm/issue-97490.rs b/tests/ui/asm/issue-97490.rs index 5f364a22bc437..72827cd2d60b1 100644 --- a/tests/ui/asm/issue-97490.rs +++ b/tests/ui/asm/issue-97490.rs @@ -2,6 +2,8 @@ //@ only-x86_64 //@ needs-asm-support +#[allow(improper_c_fn_definitions)] // it's worried about invalid pointers being given as the + // argument value pub type Yes = extern "sysv64" fn(&'static u8) -> !; fn main() { diff --git a/tests/ui/asm/naked-functions-ffi.stderr b/tests/ui/asm/naked-functions-ffi.stderr index 9df6185498ed6..afa6666427714 100644 --- a/tests/ui/asm/naked-functions-ffi.stderr +++ b/tests/ui/asm/naked-functions-ffi.stderr @@ -6,7 +6,7 @@ LL | pub extern "C" fn naked(p: char) -> u128 { | = help: consider using `u32` or `libc::wchar_t` instead = note: the `char` type has no C equivalent - = note: `#[warn(improper_ctypes_definitions)]` on by default + = note: `#[warn(improper_c_fn_definitions)]` on by default warning: `extern` fn uses type `u128`, which is not FFI-safe --> $DIR/naked-functions-ffi.rs:8:37 diff --git a/tests/ui/asm/naked-functions-unused.aarch64.stderr b/tests/ui/asm/naked-functions-unused.aarch64.stderr index bfb2923b0b8d6..b93aa3f2f4725 100644 --- a/tests/ui/asm/naked-functions-unused.aarch64.stderr +++ b/tests/ui/asm/naked-functions-unused.aarch64.stderr @@ -1,5 +1,5 @@ error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:16:32 + --> $DIR/naked-functions-unused.rs:17:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` @@ -12,58 +12,108 @@ LL | #![deny(unused)] = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:16:42 + --> $DIR/naked-functions-unused.rs:17:42 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:27:38 + --> $DIR/naked-functions-unused.rs:28:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:27:48 + --> $DIR/naked-functions-unused.rs:28:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:35:41 + --> $DIR/naked-functions-unused.rs:36:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:35:51 + --> $DIR/naked-functions-unused.rs:36:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:45:40 + --> $DIR/naked-functions-unused.rs:47:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:45:50 + --> $DIR/naked-functions-unused.rs:47:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:53:43 + --> $DIR/naked-functions-unused.rs:55:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:53:53 + --> $DIR/naked-functions-unused.rs:55:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` -error: aborting due to 10 previous errors +warning: `extern` fn uses type `&Self`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:11:32 + | +LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize; + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Self` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + = note: `#[warn(improper_c_fn_definitions)]` on by default + +warning: `extern` fn uses type `&normal::Normal`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:37:34 + | +LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&normal::Normal` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +warning: `extern` fn uses type `&normal::Normal`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:56:36 + | +LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&normal::Normal` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +warning: `extern` fn uses type `&Naked`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:88:34 + | +LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Naked` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +warning: `extern` fn uses type `&Naked`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:105:36 + | +LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Naked` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code +error: aborting due to 10 previous errors; 5 warnings emitted diff --git a/tests/ui/asm/naked-functions-unused.rs b/tests/ui/asm/naked-functions-unused.rs index 945ab1a40ad0c..91db27e5e1662 100644 --- a/tests/ui/asm/naked-functions-unused.rs +++ b/tests/ui/asm/naked-functions-unused.rs @@ -8,6 +8,7 @@ pub trait Trait { extern "C" fn trait_associated(a: usize, b: usize) -> usize; extern "C" fn trait_method(&self, a: usize, b: usize) -> usize; + //~^ WARN uses type `&Self` } pub mod normal { @@ -33,7 +34,8 @@ pub mod normal { } pub extern "C" fn method(&self, a: usize, b: usize) -> usize { - //~^ ERROR unused variable: `a` + //~^ WARN uses type `&normal::Normal` + //~| ERROR unused variable: `a` //~| ERROR unused variable: `b` unsafe { asm!("", options(noreturn)); @@ -51,7 +53,8 @@ pub mod normal { } extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { - //~^ ERROR unused variable: `a` + //~^ WARN uses type `&normal::Normal` + //~| ERROR unused variable: `a` //~| ERROR unused variable: `b` unsafe { asm!("", options(noreturn)); @@ -78,6 +81,7 @@ pub mod naked { #[unsafe(naked)] pub extern "C" fn method(&self, a: usize, b: usize) -> usize { + //~^ WARN uses type `&Naked` naked_asm!("") } } @@ -90,6 +94,7 @@ pub mod naked { #[unsafe(naked)] extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { + //~^ WARN uses type `&Naked` naked_asm!("") } } diff --git a/tests/ui/asm/naked-functions-unused.x86_64.stderr b/tests/ui/asm/naked-functions-unused.x86_64.stderr index bfb2923b0b8d6..1b7dca0ec6bc6 100644 --- a/tests/ui/asm/naked-functions-unused.x86_64.stderr +++ b/tests/ui/asm/naked-functions-unused.x86_64.stderr @@ -1,5 +1,5 @@ error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:16:32 + --> $DIR/naked-functions-unused.rs:17:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` @@ -12,58 +12,109 @@ LL | #![deny(unused)] = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:16:42 + --> $DIR/naked-functions-unused.rs:17:42 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:27:38 + --> $DIR/naked-functions-unused.rs:28:38 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:27:48 + --> $DIR/naked-functions-unused.rs:28:48 | LL | pub extern "C" fn associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:35:41 + --> $DIR/naked-functions-unused.rs:36:41 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:35:51 + --> $DIR/naked-functions-unused.rs:36:51 | LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:45:40 + --> $DIR/naked-functions-unused.rs:47:40 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:45:50 + --> $DIR/naked-functions-unused.rs:47:50 | LL | extern "C" fn trait_associated(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` error: unused variable: `a` - --> $DIR/naked-functions-unused.rs:53:43 + --> $DIR/naked-functions-unused.rs:55:43 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:53:53 + --> $DIR/naked-functions-unused.rs:55:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` -error: aborting due to 10 previous errors +warning: `extern` fn uses type `&Self`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:10:32 + | +LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize; + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Self` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + = note: `#[warn(improper_c_fn_definitions)]` on by default + +warning: `extern` fn uses type `&normal::Normal`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:36:34 + | +LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&normal::Normal` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +warning: `extern` fn uses type `&normal::Normal`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:55:36 + | +LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&normal::Normal` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +warning: `extern` fn uses type `&Naked`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:83:34 + | +LL | pub extern "C" fn method(&self, a: usize, b: usize) -> usize { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Naked` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +warning: `extern` fn uses type `&Naked`, which is not FFI-safe + --> $DIR/naked-functions-unused.rs:96:36 + | +LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Naked` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: aborting due to 10 previous errors; 5 warnings emitted diff --git a/tests/ui/attributes/export/exportable.rs b/tests/ui/attributes/export/exportable.rs index f592fce88cd49..cf8ef0501a66c 100644 --- a/tests/ui/attributes/export/exportable.rs +++ b/tests/ui/attributes/export/exportable.rs @@ -1,7 +1,7 @@ //@ compile-flags: -Zunstable-options -Csymbol-mangling-version=v0 #![crate_type = "sdylib"] -#![allow(incomplete_features, improper_ctypes_definitions)] +#![allow(incomplete_features, improper_c_fn_definitions)] #![feature(export_stable)] #![feature(inherent_associated_types)] diff --git a/tests/ui/backtrace/auxiliary/dylib-dep-helper.rs b/tests/ui/backtrace/auxiliary/dylib-dep-helper.rs index 565d8b65de055..75f15574522b1 100644 --- a/tests/ui/backtrace/auxiliary/dylib-dep-helper.rs +++ b/tests/ui/backtrace/auxiliary/dylib-dep-helper.rs @@ -3,7 +3,7 @@ #![crate_type = "cdylib"] #![crate_type = "rlib"] -#![allow(improper_ctypes_definitions)] +#![allow(improper_ctype_definitions, improper_c_fn_definitions)] type Pos = (&'static str, u32); diff --git a/tests/ui/backtrace/dylib-dep.rs b/tests/ui/backtrace/dylib-dep.rs index a41931ad54891..5ba13550053c4 100644 --- a/tests/ui/backtrace/dylib-dep.rs +++ b/tests/ui/backtrace/dylib-dep.rs @@ -15,7 +15,7 @@ //@ run-pass #![allow(improper_ctypes)] -#![allow(improper_ctypes_definitions)] +#![allow(improper_c_fn_definitions)] extern crate dylib_dep_helper; extern crate auxiliary; diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs index 890ec4b00f6a0..d7207a7580807 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/return-via-stack.rs @@ -31,7 +31,7 @@ pub fn test( ) { } -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_callbacks)] struct Test { u128: extern "C-cmse-nonsecure-call" fn() -> u128, //~ ERROR [E0798] i128: extern "C-cmse-nonsecure-call" fn() -> i128, //~ ERROR [E0798] diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs index 7dfe6cf9672ab..7d13ef123b2ac 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -25,7 +25,7 @@ pub enum ReprTransparentEnumU64 { pub struct U32Compound(u16, u16); #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_callbacks)] pub fn params( f1: extern "C-cmse-nonsecure-call" fn(), f2: extern "C-cmse-nonsecure-call" fn(u32, u32, u32, u32), @@ -38,6 +38,7 @@ pub fn params( } #[no_mangle] +#[allow(improper_c_callbacks)] pub fn returns( f1: extern "C-cmse-nonsecure-call" fn() -> u32, f2: extern "C-cmse-nonsecure-call" fn() -> u64, diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs index 4c53f9422dab4..9a62c0b2d983c 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/params-via-stack.rs @@ -21,5 +21,5 @@ pub extern "C-cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {} //~ ERROR [ pub extern "C-cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {} //~ ERROR [E0798] #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C-cmse-nonsecure-entry" fn f5(_: [u32; 5]) {} //~ ERROR [E0798] diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs index 735eab10fa154..d470608d77bec 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/return-via-stack.rs @@ -43,19 +43,19 @@ pub extern "C-cmse-nonsecure-entry" fn f4() -> ReprCAlign16 { } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C-cmse-nonsecure-entry" fn f5() -> [u8; 5] { //~^ ERROR [E0798] [0xAA; 5] } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C-cmse-nonsecure-entry" fn u128() -> u128 { //~^ ERROR [E0798] 123 } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C-cmse-nonsecure-entry" fn i128() -> i128 { //~^ ERROR [E0798] 456 @@ -72,7 +72,7 @@ pub union ReprCUnionU64 { } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C-cmse-nonsecure-entry" fn union_rust() -> ReprRustUnionU64 { //~^ ERROR [E0798] ReprRustUnionU64 { _unused: 1 } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs index 912fc8b85ebd2..c0031717412de 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/via-registers.rs @@ -32,14 +32,14 @@ pub extern "C-cmse-nonsecure-entry" fn inputs2(_: u32, _: u32, _: u32, _: u32) { #[no_mangle] pub extern "C-cmse-nonsecure-entry" fn inputs3(_: u64, _: u64) {} #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C-cmse-nonsecure-entry" fn inputs4(_: u128) {} #[no_mangle] pub extern "C-cmse-nonsecure-entry" fn inputs5(_: f64, _: f32, _: f32) {} #[no_mangle] pub extern "C-cmse-nonsecure-entry" fn inputs6(_: ReprTransparentStruct, _: U32Compound) {} #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C-cmse-nonsecure-entry" fn inputs7(_: [u32; 4]) {} #[no_mangle] @@ -59,7 +59,7 @@ pub extern "C-cmse-nonsecure-entry" fn outputs4() -> f64 { 0.0 } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C-cmse-nonsecure-entry" fn outputs5() -> [u8; 4] { [0xAA; 4] } diff --git a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs index c23187598bceb..d0e5c9d47e740 100644 --- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs +++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs @@ -3,6 +3,7 @@ //@ run-pass #[repr(C)] +#[allow(improper_ctype_definitions)] pub struct Loaf { head: [T; N], slice: [T], diff --git a/tests/ui/consts/const-extern-fn/const-extern-fn.rs b/tests/ui/consts/const-extern-fn/const-extern-fn.rs index 75ffa783a117b..9b188ab6e057e 100644 --- a/tests/ui/consts/const-extern-fn/const-extern-fn.rs +++ b/tests/ui/consts/const-extern-fn/const-extern-fn.rs @@ -16,12 +16,12 @@ const unsafe extern "C" fn bar2(val: bool) -> bool { !val } -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] const extern "C" fn unsize(x: &[u8; 3]) -> &[u8] { x } -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] const unsafe extern "C" fn closure() -> fn() { || {} } diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs index c3bd8301d5ce4..8bf320b344ec7 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -74,6 +74,7 @@ const PARTIAL_POINTER: () = unsafe { } // `Align` ensures that the entire thing has pointer alignment again. #[repr(C)] + #[allow(improper_ctype_definitions)] struct Align { p: Packed, align: usize, diff --git a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr index ea3b0e70b8285..a48fbb2f775bb 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.with_flag.stderr @@ -44,7 +44,7 @@ LL | let v = *addr_of!(data).cast::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .: encountered an uninhabited enum variant error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:83:16 + --> $DIR/detect-extra-ub.rs:84:16 | LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered a partial pointer or a mix of pointers @@ -53,7 +53,7 @@ LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported error[E0080]: evaluation of constant value failed - --> $DIR/detect-extra-ub.rs:98:16 + --> $DIR/detect-extra-ub.rs:99:16 | LL | let _val = &*slice; | ^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object diff --git a/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.rs b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.rs index 33d295f7ebe19..8e037a9b413fd 100644 --- a/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.rs +++ b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.rs @@ -5,10 +5,10 @@ //@ normalize-stderr: "\[u8\]" -> "[i8 or u8 (arch dependant)]" type Foo = extern "C" fn(::std::ffi::CStr); -//~^ WARN `extern` fn uses type +//~^ WARN `extern` callback uses type extern "C" { fn meh(blah: Foo); - //~^ WARN `extern` block uses type + // ^ FIXME: the error isn't seen here but at least it's reported elsewhere } fn main() { diff --git a/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr index 044c1ae2dd42f..f8d3c1a3b612e 100644 --- a/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr +++ b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr @@ -1,22 +1,14 @@ -warning: `extern` fn uses type `CStr`, which is not FFI-safe +warning: `extern` callback uses type `CStr`, which is not FFI-safe --> $DIR/extern-C-non-FFI-safe-arg-ice-52334.rs:7:12 | LL | type Foo = extern "C" fn(::std::ffi::CStr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = note: the function pointer to `extern "C" fn(CStr)` is FFI-unsafe due to `CStr` + = help: consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, + and use (depending on the use case) `CStr::as_ptr()`, `CString::into_raw()` then `CString::from_raw()`, or a dedicated buffer = note: `CStr`/`CString` do not have a guaranteed layout - = note: `#[warn(improper_ctypes_definitions)]` on by default + = note: `#[warn(improper_c_callbacks)]` on by default -warning: `extern` block uses type `CStr`, which is not FFI-safe - --> $DIR/extern-C-non-FFI-safe-arg-ice-52334.rs:10:18 - | -LL | fn meh(blah: Foo); - | ^^^ not FFI-safe - | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - = note: `CStr`/`CString` do not have a guaranteed layout - = note: `#[warn(improper_ctypes)]` on by default - -warning: 2 warnings emitted +warning: 1 warning emitted diff --git a/tests/ui/extern/extern-C-str-arg-ice-80125.rs b/tests/ui/extern/extern-C-str-arg-ice-80125.rs index 0908d6199efb8..fa300ba9d173b 100644 --- a/tests/ui/extern/extern-C-str-arg-ice-80125.rs +++ b/tests/ui/extern/extern-C-str-arg-ice-80125.rs @@ -1,13 +1,13 @@ // issue: rust-lang/rust#80125 //@ check-pass type ExternCallback = extern "C" fn(*const u8, u32, str); -//~^ WARN `extern` fn uses type `str`, which is not FFI-safe +//~^ WARN `extern` callback uses type `str`, which is not FFI-safe pub struct Struct(ExternCallback); #[no_mangle] pub extern "C" fn register_something(bind: ExternCallback) -> Struct { -//~^ WARN `extern` fn uses type `str`, which is not FFI-safe +// ^ FIXME: the error isn't seen here, but at least it's reported elsewhere //~^^ WARN `extern` fn uses type `Struct`, which is not FFI-safe Struct(bind) } diff --git a/tests/ui/extern/extern-C-str-arg-ice-80125.stderr b/tests/ui/extern/extern-C-str-arg-ice-80125.stderr index ebd6cec6ecd3f..6c19c9edbd257 100644 --- a/tests/ui/extern/extern-C-str-arg-ice-80125.stderr +++ b/tests/ui/extern/extern-C-str-arg-ice-80125.stderr @@ -1,21 +1,13 @@ -warning: `extern` fn uses type `str`, which is not FFI-safe +warning: `extern` callback uses type `str`, which is not FFI-safe --> $DIR/extern-C-str-arg-ice-80125.rs:3:23 | LL | type ExternCallback = extern "C" fn(*const u8, u32, str); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | + = note: the function pointer to `extern "C" fn(*const u8, u32, str)` is FFI-unsafe due to `str` = help: consider using `*const u8` and a length instead = note: string slices have no C equivalent - = note: `#[warn(improper_ctypes_definitions)]` on by default - -warning: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/extern-C-str-arg-ice-80125.rs:9:44 - | -LL | pub extern "C" fn register_something(bind: ExternCallback) -> Struct { - | ^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using `*const u8` and a length instead - = note: string slices have no C equivalent + = note: `#[warn(improper_c_callbacks)]` on by default warning: `extern` fn uses type `Struct`, which is not FFI-safe --> $DIR/extern-C-str-arg-ice-80125.rs:9:63 @@ -23,13 +15,14 @@ warning: `extern` fn uses type `Struct`, which is not FFI-safe LL | pub extern "C" fn register_something(bind: ExternCallback) -> Struct { | ^^^^^^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `Struct` + = note: `Struct` has unspecified layout note: the type is defined here --> $DIR/extern-C-str-arg-ice-80125.rs:6:1 | LL | pub struct Struct(ExternCallback); | ^^^^^^^^^^^^^^^^^ + = note: `#[warn(improper_c_fn_definitions)]` on by default -warning: 3 warnings emitted +warning: 2 warnings emitted diff --git a/tests/ui/extern/issue-16250.stderr b/tests/ui/extern/issue-16250.stderr index 9d3e88114616b..f2a3dfc1e8eb3 100644 --- a/tests/ui/extern/issue-16250.stderr +++ b/tests/ui/extern/issue-16250.stderr @@ -4,8 +4,8 @@ error: `extern` block uses type `Foo`, which is not FFI-safe LL | pub fn foo(x: (Foo)); | ^^^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `Foo` + = note: `Foo` has unspecified layout note: the type is defined here --> $DIR/issue-16250.rs:3:1 | diff --git a/tests/ui/hashmap/hashmap-memory.rs b/tests/ui/hashmap/hashmap-memory.rs index 6db5d2e7bef35..51301cd90fab7 100644 --- a/tests/ui/hashmap/hashmap-memory.rs +++ b/tests/ui/hashmap/hashmap-memory.rs @@ -1,6 +1,6 @@ //@ run-pass -#![allow(improper_ctypes_definitions)] +#![allow(improper_c_callbacks)] #![allow(non_camel_case_types)] #![allow(dead_code)] #![allow(unused_mut)] diff --git a/tests/ui/issues/issue-16441.rs b/tests/ui/issues/issue-16441.rs index 58cfb3892975b..aa825e1c25c0a 100644 --- a/tests/ui/issues/issue-16441.rs +++ b/tests/ui/issues/issue-16441.rs @@ -4,7 +4,7 @@ struct Empty; // This used to cause an ICE -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] extern "C" fn ice(_a: Empty) {} fn main() { diff --git a/tests/ui/issues/issue-26997.rs b/tests/ui/issues/issue-26997.rs index 5441dc68bae6c..1138f17753762 100644 --- a/tests/ui/issues/issue-26997.rs +++ b/tests/ui/issues/issue-26997.rs @@ -6,7 +6,7 @@ pub struct Foo { } impl Foo { - #[allow(improper_ctypes_definitions)] + #[allow(improper_c_fn_definitions)] pub extern "C" fn foo_new() -> Foo { Foo { x: 21, y: 33 } } diff --git a/tests/ui/issues/issue-28600.rs b/tests/ui/issues/issue-28600.rs index a5427b94a57c5..3bbd40bfeb741 100644 --- a/tests/ui/issues/issue-28600.rs +++ b/tests/ui/issues/issue-28600.rs @@ -6,7 +6,7 @@ struct Test; impl Test { #[allow(dead_code)] #[allow(unused_variables)] - #[allow(improper_ctypes_definitions)] + #[allow(improper_c_fn_definitions)] pub extern "C" fn test(val: &str) { } diff --git a/tests/ui/issues/issue-38763.rs b/tests/ui/issues/issue-38763.rs index 87c758db1723c..3a0b177edde8d 100644 --- a/tests/ui/issues/issue-38763.rs +++ b/tests/ui/issues/issue-38763.rs @@ -2,10 +2,11 @@ //@ needs-threads #[repr(C)] +#[allow(improper_ctype_definitions)] pub struct Foo(i128); #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C" fn foo(x: Foo) -> Foo { x } fn main() { diff --git a/tests/ui/issues/issue-51907.rs b/tests/ui/issues/issue-51907.rs index bf3f629df4970..8f43fdf03126d 100644 --- a/tests/ui/issues/issue-51907.rs +++ b/tests/ui/issues/issue-51907.rs @@ -1,14 +1,16 @@ //@ run-pass trait Foo { + #[allow(improper_c_fn_definitions)] extern "C" fn borrow(&self); + #[allow(improper_c_fn_definitions)] extern "C" fn take(self: Box); } struct Bar; impl Foo for Bar { - #[allow(improper_ctypes_definitions)] + #[allow(improper_c_fn_definitions)] extern "C" fn borrow(&self) {} - #[allow(improper_ctypes_definitions)] + #[allow(improper_c_fn_definitions)] extern "C" fn take(self: Box) {} } diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs index f144094d43fbc..5456bc92d264c 100644 --- a/tests/ui/layout/reprc-power-alignment.rs +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -10,6 +10,7 @@ extern crate minicore; use minicore::*; #[warn(uses_power_alignment)] +#[warn(improper_ctype_definitions)] #[repr(C)] pub struct Floats { @@ -149,7 +150,7 @@ pub struct I { e: f64, } #[repr(C)] -pub struct J { +pub struct J { //~ WARNING `repr(C)` type uses type `I` a: u8, b: I, } diff --git a/tests/ui/layout/reprc-power-alignment.stderr b/tests/ui/layout/reprc-power-alignment.stderr index 18664e4d655d3..2917705e50363 100644 --- a/tests/ui/layout/reprc-power-alignment.stderr +++ b/tests/ui/layout/reprc-power-alignment.stderr @@ -1,5 +1,5 @@ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:18:5 + --> $DIR/reprc-power-alignment.rs:19:5 | LL | c: f64, | ^^^^^^ @@ -11,7 +11,7 @@ LL | #[warn(uses_power_alignment)] | ^^^^^^^^^^^^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:45:5 + --> $DIR/reprc-power-alignment.rs:46:5 | LL | b: f64, | ^^^^^^ @@ -19,94 +19,112 @@ LL | b: f64, = note: `#[warn(uses_power_alignment)]` on by default warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:52:5 + --> $DIR/reprc-power-alignment.rs:53:5 | LL | y: f64, | ^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:58:5 + --> $DIR/reprc-power-alignment.rs:59:5 | LL | y: Floats, | ^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:65:5 + --> $DIR/reprc-power-alignment.rs:66:5 | LL | y: FloatAgg2, | ^^^^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:66:5 + --> $DIR/reprc-power-alignment.rs:67:5 | LL | z: FloatAgg2, | ^^^^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:72:5 + --> $DIR/reprc-power-alignment.rs:73:5 | LL | y: FloatAgg2, | ^^^^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:78:5 + --> $DIR/reprc-power-alignment.rs:79:5 | LL | y: FloatAgg2, | ^^^^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:79:5 + --> $DIR/reprc-power-alignment.rs:80:5 | LL | z: FloatAgg3, | ^^^^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:85:5 + --> $DIR/reprc-power-alignment.rs:86:5 | LL | y: Floats, | ^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:92:5 + --> $DIR/reprc-power-alignment.rs:93:5 | LL | y: Floats, | ^^^^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:105:3 + --> $DIR/reprc-power-alignment.rs:106:3 | LL | d: f64, | ^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:110:3 + --> $DIR/reprc-power-alignment.rs:111:3 | LL | b: B, | ^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:119:3 + --> $DIR/reprc-power-alignment.rs:120:3 | LL | d: D, | ^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:124:3 + --> $DIR/reprc-power-alignment.rs:125:3 | LL | b: f64, | ^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:130:5 + --> $DIR/reprc-power-alignment.rs:131:5 | LL | c: f64, | ^^^^^^ warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type - --> $DIR/reprc-power-alignment.rs:132:5 + --> $DIR/reprc-power-alignment.rs:133:5 | LL | e: f64, | ^^^^^^ -warning: 17 warnings emitted +warning: `repr(C)` type uses type `I`, which is not FFI-safe + --> $DIR/reprc-power-alignment.rs:153:1 + | +LL | / pub struct J { +LL | | a: u8, +LL | | b: I, +LL | | } + | |_^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `I` + = note: `I` has unspecified layout +note: the type is defined here + --> $DIR/reprc-power-alignment.rs:145:1 + | +LL | pub struct I { + | ^^^^^^^^^^^^ + = note: `#[warn(improper_ctype_definitions)]` on by default + +warning: 18 warnings emitted diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs index e4477c9620221..b6090d730a454 100644 --- a/tests/ui/lint/clashing-extern-fn.rs +++ b/tests/ui/lint/clashing-extern-fn.rs @@ -498,11 +498,14 @@ mod pattern_types { struct NonZeroUsize(pattern_type!(usize is 1..)); extern "C" { fn pt_non_zero_usize() -> pattern_type!(usize is 1..); + //~^ WARN not FFI-safe fn pt_non_zero_usize_opt() -> Option; fn pt_non_zero_usize_opt_full_range() -> Option; //~^ WARN not FFI-safe fn pt_non_null_ptr() -> pattern_type!(usize is 1..); + //~^ WARN not FFI-safe fn pt_non_zero_usize_wrapper() -> NonZeroUsize; + //~^ WARN not FFI-safe fn pt_non_zero_usize_wrapper_opt() -> Option; } } diff --git a/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index 0c27547a6ed8f..14a9cd7be42a1 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -17,8 +17,17 @@ LL | fn hidden_niche_unsafe_cell() -> Option $DIR/clashing-extern-fn.rs:500:39 + | +LL | fn pt_non_zero_usize() -> pattern_type!(usize is 1..); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead, or wrapping `(usize) is 1..` in an `Option<_>` + = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code + warning: `extern` block uses type `Option<(usize) is 0..>`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:502:54 + --> $DIR/clashing-extern-fn.rs:503:54 | LL | fn pt_non_zero_usize_opt_full_range() -> Option; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -26,6 +35,30 @@ LL | fn pt_non_zero_usize_opt_full_range() -> Option $DIR/clashing-extern-fn.rs:505:37 + | +LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead, or wrapping `(usize) is 1..` in an `Option<_>` + = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code + +warning: `extern` block uses type `NonZeroUsize`, which is not FFI-safe + --> $DIR/clashing-extern-fn.rs:507:47 + | +LL | fn pt_non_zero_usize_wrapper() -> NonZeroUsize; + | ^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`NonZeroUsize`) is FFI-unsafe due to a `(usize) is 1..` field +note: the type is defined here + --> $DIR/clashing-extern-fn.rs:498:9 + | +LL | struct NonZeroUsize(pattern_type!(usize is 1..)); + | ^^^^^^^^^^^^^^^^^^^ + = help: consider using the base type instead, or wrapping `(usize) is 1..` in an `Option<_>` + = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code + warning: `clash` redeclared with a different signature --> $DIR/clashing-extern-fn.rs:13:13 | @@ -268,7 +301,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option Option>>` warning: `pt_non_null_ptr` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:516:13 + --> $DIR/clashing-extern-fn.rs:519:13 | LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); | ---------------------------------------------------- `pt_non_null_ptr` previously declared here @@ -279,5 +312,5 @@ LL | fn pt_non_null_ptr() -> *const (); = note: expected `unsafe extern "C" fn() -> (usize) is 1..=` found `unsafe extern "C" fn() -> *const ()` -warning: 24 warnings emitted +warning: 27 warnings emitted diff --git a/tests/ui/lint/extern-C-fnptr-lints-slices.rs b/tests/ui/lint/extern-C-fnptr-lints-slices.rs index 0c35eb37a4890..ee1676cabb51b 100644 --- a/tests/ui/lint/extern-C-fnptr-lints-slices.rs +++ b/tests/ui/lint/extern-C-fnptr-lints-slices.rs @@ -1,9 +1,9 @@ -#[deny(improper_ctypes_definitions)] +#[deny(improper_c_callbacks)] // It's an improper ctype (a slice) arg in an extern "C" fnptr. pub type F = extern "C" fn(&[u8]); -//~^ ERROR: `extern` fn uses type `[u8]`, which is not FFI-safe +//~^ ERROR: `extern` callback uses type `&[u8]`, which is not FFI-safe fn main() {} diff --git a/tests/ui/lint/extern-C-fnptr-lints-slices.stderr b/tests/ui/lint/extern-C-fnptr-lints-slices.stderr index d13f93ca96f22..fa488d14d0304 100644 --- a/tests/ui/lint/extern-C-fnptr-lints-slices.stderr +++ b/tests/ui/lint/extern-C-fnptr-lints-slices.stderr @@ -1,16 +1,17 @@ -error: `extern` fn uses type `[u8]`, which is not FFI-safe +error: `extern` callback uses type `&[u8]`, which is not FFI-safe --> $DIR/extern-C-fnptr-lints-slices.rs:5:14 | LL | pub type F = extern "C" fn(&[u8]); | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead - = note: slices have no C equivalent + = note: the function pointer to `for<'a> extern "C" fn(&'a [u8])` is FFI-unsafe due to `&[u8]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer note: the lint level is defined here --> $DIR/extern-C-fnptr-lints-slices.rs:1:8 | -LL | #[deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[deny(improper_c_callbacks)] + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/improper_ctypes/allow-phantomdata-in-ffi.rs b/tests/ui/lint/improper_ctypes/allow-phantomdata-in-ffi.rs index a90159d2b5894..b6c365536d208 100644 --- a/tests/ui/lint/improper_ctypes/allow-phantomdata-in-ffi.rs +++ b/tests/ui/lint/improper_ctypes/allow-phantomdata-in-ffi.rs @@ -1,5 +1,5 @@ //@ run-pass -#![forbid(improper_ctypes)] +#![forbid(improper_ctypes, improper_ctype_definitions)] #![allow(dead_code)] // issue https://github.com/rust-lang/rust/issues/34798 // We allow PhantomData in FFI so bindgen can bind templated C++ structs with "unused generic args" diff --git a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs new file mode 100644 index 0000000000000..91efb9d8ad2c1 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs @@ -0,0 +1,160 @@ +#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_c_callbacks)] + +//@ aux-build: outer_crate_types.rs +//@ compile-flags:--extern outer_crate_types +extern crate outer_crate_types as outer; + +// //////////////////////////////////////////////////////// +// first, the same bank of types as in the extern crate + +#[repr(C)] +struct SafeStruct (i32); + +#[repr(C)] +struct UnsafeStruct (String); +//~^ ERROR: `repr(C)` type uses type `String` + +#[repr(C)] +#[allow(improper_ctype_definitions)] +struct AllowedUnsafeStruct (String); + +// refs are only unsafe if the value comes from the other side of the FFI boundary +// due to the non-null assumption +// (technically there are also assumptions about non-dandling, alignment, +// aliasing, lifetimes, etc...) +// the lint is not raised here, but will be if used in the wrong place +#[repr(C)] +struct UnsafeFromForeignStruct<'a> (&'a u32); + +#[repr(C)] +#[allow(improper_ctype_definitions)] +struct AllowedUnsafeFromForeignStruct<'a> (&'a u32); + + +type SafeFnPtr = extern "C" fn(i32)->i32; + +type UnsafeFnPtr = extern "C" fn((i32, i32))->i32; +//~^ ERROR: `extern` callback uses type `(i32, i32)` + + +// for now, let's not lint on the nonzero assumption, +// because: +// - we don't know if the callback is rust-callee-foreign-caller or the other way around +// - having to cast around function signatures to get function pointers +// would be an awful experience +// so, let's assume that the unsafety in this fnptr +// will be pointed out indirectly by a lint elsewhere +// (note: there's one case where the error would be missed altogether: +// a rust-caller,non-rust-callee callback where the fnptr +// is given as an argument to a rust-callee,non-rust-caller +// FFI boundary) +#[allow(improper_c_callbacks)] +type AllowedUnsafeFnPtr = extern "C" fn(&[i32])->i32; + +type UnsafeRustCalleeFnPtr = extern "C" fn(i32)->&'static i32; + +#[allow(improper_c_callbacks)] +type AllowedUnsafeRustCalleeFnPtr = extern "C" fn(i32)->&'static i32; + +type UnsafeForeignCalleeFnPtr = extern "C" fn(&i32); + +#[allow(improper_c_callbacks)] +type AllowedUnsafeForeignCalleeFnPtr = extern "C" fn(&i32); + + +// //////////////////////////////////////////////////////// +// then, some functions that use them + +static INT: u32 = 42; + +#[allow(improper_c_fn_definitions)] +extern "C" fn fn1a(e: &String) -> &str {&*e} +extern "C" fn fn1u(e: &String) -> &str {&*e} +//~^ ERROR: `extern` fn uses type `&str` +//~^^ ERROR: `extern` fn uses type `&String` + +#[allow(improper_c_fn_definitions)] +extern "C" fn fn2a(e: UnsafeStruct) {} +extern "C" fn fn2u(e: UnsafeStruct) {} +//~^ ERROR: `extern` fn uses type `UnsafeStruct` +#[allow(improper_c_fn_definitions)] +extern "C" fn fn2oa(e: outer::UnsafeStruct) {} +extern "C" fn fn2ou(e: outer::UnsafeStruct) {} +//~^ ERROR: `extern` fn uses type `outer::UnsafeStruct` + +#[allow(improper_c_fn_definitions)] +extern "C" fn fn3a(e: AllowedUnsafeStruct) {} +extern "C" fn fn3u(e: AllowedUnsafeStruct) {} +//~^ ERROR: `extern` fn uses type `AllowedUnsafeStruct` +// ^^ FIXME: ...ideally the lint should not trigger here +#[allow(improper_c_fn_definitions)] +extern "C" fn fn3oa(e: outer::AllowedUnsafeStruct) {} +extern "C" fn fn3ou(e: outer::AllowedUnsafeStruct) {} +//~^ ERROR: `extern` fn uses type `outer::AllowedUnsafeStruct` +// ^^ FIXME: ...ideally the lint should not trigger here + +#[allow(improper_c_fn_definitions)] +extern "C" fn fn4a(e: UnsafeFromForeignStruct) {} +extern "C" fn fn4u(e: UnsafeFromForeignStruct) {} +//~^ ERROR: `extern` fn uses type `UnsafeFromForeignStruct<'_>` +#[allow(improper_c_fn_definitions)] +extern "C" fn fn4oa(e: outer::UnsafeFromForeignStruct) {} +extern "C" fn fn4ou(e: outer::UnsafeFromForeignStruct) {} +//~^ ERROR: `extern` fn uses type `outer::UnsafeFromForeignStruct<'_>` + +#[allow(improper_c_fn_definitions)] +extern "C" fn fn5a() -> UnsafeFromForeignStruct<'static> { UnsafeFromForeignStruct(&INT)} +extern "C" fn fn5u() -> UnsafeFromForeignStruct<'static> { UnsafeFromForeignStruct(&INT)} +#[allow(improper_c_fn_definitions)] +extern "C" fn fn5oa() -> outer::UnsafeFromForeignStruct<'static> { + outer::UnsafeFromForeignStruct(&INT) +} +extern "C" fn fn5ou() -> outer::UnsafeFromForeignStruct<'static> { + outer::UnsafeFromForeignStruct(&INT) +} + +#[allow(improper_c_fn_definitions)] +extern "C" fn fn6a() -> AllowedUnsafeFromForeignStruct<'static> { + AllowedUnsafeFromForeignStruct(&INT) +} +extern "C" fn fn6u() -> AllowedUnsafeFromForeignStruct<'static> { + AllowedUnsafeFromForeignStruct(&INT) +} +#[allow(improper_c_fn_definitions)] +extern "C" fn fn6oa() -> outer::AllowedUnsafeFromForeignStruct<'static> { + outer::AllowedUnsafeFromForeignStruct(&INT) +} +extern "C" fn fn6ou() -> outer::AllowedUnsafeFromForeignStruct<'static> { + outer::AllowedUnsafeFromForeignStruct(&INT) +} + +// //////////////////////////////////////////////////////// +// special cases: struct-in-fnptr and fnptr-in-struct + +#[repr(C)] +struct FakeVTable{ +//~^ ERROR: `repr(C)` type uses type `(A, usize)` + make_new: extern "C" fn() -> A, + combine: extern "C" fn(&[A]) -> A, + //~^ ERROR: `extern` callback uses type `&[A]` + drop: extern "C" fn(A), + something_else: (A, usize), +} + +type FakeVTableMaker = extern "C" fn() -> FakeVTable; +//~^ ERROR: `extern` callback uses type `FakeVTable` + +#[repr(C)] +#[allow(improper_c_callbacks, improper_ctype_definitions)] +struct FakeVTableAllowed{ + make_new: extern "C" fn() -> A, + combine: extern "C" fn(&[A]) -> A, + drop: extern "C" fn(A), + something_else: (A, usize), +} + +#[allow(improper_c_callbacks)] +type FakeVTableMakerAllowed = extern "C" fn() -> FakeVTable; + +fn main(){} diff --git a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr new file mode 100644 index 0000000000000..4759494b351b4 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr @@ -0,0 +1,173 @@ +error: `repr(C)` type uses type `String`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:15:1 + | +LL | struct UnsafeStruct (String); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `String` + = note: `String` has unspecified layout +note: the lint level is defined here + --> $DIR/allow_improper_ctypes.rs:1:53 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` callback uses type `(i32, i32)`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:37:20 + | +LL | type UnsafeFnPtr = extern "C" fn((i32, i32))->i32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn((i32, i32)) -> i32` is FFI-unsafe due to `(i32, i32)` + = help: consider using a struct instead + = note: tuples have unspecified layout +note: the lint level is defined here + --> $DIR/allow_improper_ctypes.rs:2:9 + | +LL | #![deny(improper_c_callbacks)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `&String`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:73:23 + | +LL | extern "C" fn fn1u(e: &String) -> &str {&*e} + | ^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&String` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code +note: the lint level is defined here + --> $DIR/allow_improper_ctypes.rs:1:26 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `&str`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:73:35 + | +LL | extern "C" fn fn1u(e: &String) -> &str {&*e} + | ^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `UnsafeStruct`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:79:23 + | +LL | extern "C" fn fn2u(e: UnsafeStruct) {} + | ^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`UnsafeStruct`) is FFI-unsafe due to a `String` field +note: the type is defined here + --> $DIR/allow_improper_ctypes.rs:15:1 + | +LL | struct UnsafeStruct (String); + | ^^^^^^^^^^^^^^^^^^^ + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `String` + = note: `String` has unspecified layout + +error: `extern` fn uses type `outer::UnsafeStruct`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:83:24 + | +LL | extern "C" fn fn2ou(e: outer::UnsafeStruct) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`outer::UnsafeStruct`) is FFI-unsafe due to a `String` field + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `String` + = note: `String` has unspecified layout + +error: `extern` fn uses type `AllowedUnsafeStruct`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:88:23 + | +LL | extern "C" fn fn3u(e: AllowedUnsafeStruct) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`AllowedUnsafeStruct`) is FFI-unsafe due to a `String` field +note: the type is defined here + --> $DIR/allow_improper_ctypes.rs:20:1 + | +LL | struct AllowedUnsafeStruct (String); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `String` + = note: `String` has unspecified layout + +error: `extern` fn uses type `outer::AllowedUnsafeStruct`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:93:24 + | +LL | extern "C" fn fn3ou(e: outer::AllowedUnsafeStruct) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`outer::AllowedUnsafeStruct`) is FFI-unsafe due to a `String` field + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `String` + = note: `String` has unspecified layout + +error: `extern` fn uses type `UnsafeFromForeignStruct<'_>`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:99:23 + | +LL | extern "C" fn fn4u(e: UnsafeFromForeignStruct) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`UnsafeFromForeignStruct<'_>`) is FFI-unsafe due to a `&u32` field +note: the type is defined here + --> $DIR/allow_improper_ctypes.rs:28:1 + | +LL | struct UnsafeFromForeignStruct<'a> (&'a u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using a raw pointer, or wrapping `&u32` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `outer::UnsafeFromForeignStruct<'_>`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:103:24 + | +LL | extern "C" fn fn4ou(e: outer::UnsafeFromForeignStruct) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`outer::UnsafeFromForeignStruct<'_>`) is FFI-unsafe due to a `&u32` field + = help: consider using a raw pointer, or wrapping `&u32` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `repr(C)` type uses type `(A, usize)`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:136:1 + | +LL | / struct FakeVTable{ +LL | | +LL | | make_new: extern "C" fn() -> A, +LL | | combine: extern "C" fn(&[A]) -> A, +... | +LL | | something_else: (A, usize), +LL | | } + | |_^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` callback uses type `&[A]`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:139:14 + | +LL | combine: extern "C" fn(&[A]) -> A, + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `for<'a> extern "C" fn(&'a [A]) -> A` is FFI-unsafe due to `&[A]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` callback uses type `FakeVTable`, which is not FFI-safe + --> $DIR/allow_improper_ctypes.rs:145:24 + | +LL | type FakeVTableMaker = extern "C" fn() -> FakeVTable; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn() -> FakeVTable` is FFI-unsafe due to `FakeVTable` + = note: this struct/enum/union (`FakeVTable`) is FFI-unsafe due to a `(u32, usize)` field +note: the type is defined here + --> $DIR/allow_improper_ctypes.rs:136:1 + | +LL | struct FakeVTable{ + | ^^^^^^^^^^^^^^^^^^^^ + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: aborting due to 13 previous errors + diff --git a/tests/ui/lint/improper_ctypes/auxiliary/outer_crate_types.rs b/tests/ui/lint/improper_ctypes/auxiliary/outer_crate_types.rs new file mode 100644 index 0000000000000..104281065fd9c --- /dev/null +++ b/tests/ui/lint/improper_ctypes/auxiliary/outer_crate_types.rs @@ -0,0 +1,41 @@ +/// a bank of types (structs, function pointers) that are safe or unsafe for whatever reason, +/// with or without said unsafety being explicitely ignored + +#[repr(C)] +pub struct SafeStruct (pub i32); + +#[repr(C)] +pub struct UnsafeStruct (pub String); + +#[repr(C)] +#[allow(improper_ctype_definitions)] +pub struct AllowedUnsafeStruct (pub String); + +// refs are only unsafe if the value comes from the other side of the FFI boundary +// due to the non-null assumption +// (technically there are also assumptions about non-dandling, alignment, aliasing, +// lifetimes, etc...) +#[repr(C)] +pub struct UnsafeFromForeignStruct<'a> (pub &'a u32); + +#[repr(C)] +#[allow(improper_ctype_definitions)] +pub struct AllowedUnsafeFromForeignStruct<'a> (pub &'a u32); + + +pub type SafeFnPtr = extern "C" fn(i32)->i32; + +pub type UnsafeFnPtr = extern "C" fn((i32,i32))->i32; + +#[allow(improper_c_callbacks)] +pub type AllowedUnsafeFnPtr = extern "C" fn(&[i32])->i32; + +pub type UnsafeRustCalleeFnPtr = extern "C" fn(i32)->&'static i32; + +#[allow(improper_c_callbacks)] +pub type AllowedUnsafeRustCalleeFnPtr = extern "C" fn(i32)->&'static i32; + +pub type UnsafeForeignCalleeFnPtr = extern "C" fn(&i32); + +#[allow(improper_c_callbacks)] +pub type AllowedUnsafeForeignCalleeFnPtr = extern "C" fn(&i32); diff --git a/tests/ui/lint/lint-ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs similarity index 63% rename from tests/ui/lint/lint-ctypes.rs rename to tests/ui/lint/improper_ctypes/ctypes.rs index 6dd9be10a48f8..20b95375689a9 100644 --- a/tests/ui/lint/lint-ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -1,12 +1,19 @@ #![feature(rustc_private)] +#![feature(extern_types)] +#![feature(pattern_types, rustc_attrs)] +#![feature(pattern_type_macro)] #![allow(private_interfaces)] -#![deny(improper_ctypes)] +#![deny(improper_ctypes, improper_c_callbacks)] +#![deny(improper_c_fn_definitions, improper_ctype_definitions)] use std::cell::UnsafeCell; use std::marker::PhantomData; use std::ffi::{c_int, c_uint}; +use std::fmt::Debug; +use std::pat::pattern_type; +unsafe extern "C" {type UnsizedOpaque;} trait Bar { } trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } @@ -18,17 +25,17 @@ pub struct StructWithProjectionAndLifetime<'a>( ); pub type I32Pair = (i32, i32); #[repr(C)] -pub struct ZeroSize; +pub struct ZeroSize; //~ ERROR: `repr(C)` type uses type `ZeroSize` pub type RustFn = fn(); -pub type RustBadRet = extern "C" fn() -> Box; +pub type RustBoxRet = extern "C" fn() -> Box; pub type CVoidRet = (); pub struct Foo; #[repr(transparent)] pub struct TransparentI128(i128); -#[repr(transparent)] +#[repr(transparent)] // reminder: repr(transparent) struct defs are not scanned pub struct TransparentStr(&'static str); #[repr(transparent)] -pub struct TransparentBadFn(RustBadRet); +pub struct TransparentBoxFn(RustBoxRet); #[repr(transparent)] pub struct TransparentInt(u32); #[repr(transparent)] @@ -39,24 +46,44 @@ 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 { + //~^ ERROR: `repr(C)` type uses type `dyn Debug` + sized: u32, + unszd: dyn Debug, +} + +#[repr(C)] +pub struct TwoBadTypes<'a> { + //~^ ERROR: `repr(C)` type uses type `char` + //~| ERROR: `repr(C)` type uses type `&[u8]` + non_c_type: char, + ref_with_mdata: &'a [u8], +} #[repr(C)] pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); extern "C" { - pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo` - pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo` + pub fn ptr_type1(size: *const Foo); + pub fn ptr_type2(size: *const Foo); pub fn ptr_unit(p: *const ()); - pub fn ptr_tuple(p: *const ((),)); //~ ERROR: uses type `((),)` - pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]` - pub fn str_type(p: &str); //~ ERROR: uses type `str` - pub fn box_type(p: Box); //~ ERROR uses type `Box` + pub fn ptr_tuple(p: *const ((),)); + pub fn slice_type(p: &[u32]); //~ ERROR: uses type `&[u32]` + pub fn str_type(p: &str); //~ ERROR: uses type `&str` + pub fn box_type(p: Box); pub fn opt_box_type(p: Option>); - //~^ ERROR uses type `Option>` pub fn char_type(p: char); //~ ERROR uses type `char` pub fn i128_type(p: i128); //~ ERROR uses type `i128` pub fn u128_type(p: u128); //~ ERROR uses type `u128` - pub fn trait_type(p: &dyn Bar); //~ ERROR uses type `dyn Bar` + pub fn pat_type1() -> pattern_type!(u32 is 1..); //~ ERROR uses type `(u32) is 1..` + pub fn pat_type2(p: pattern_type!(u32 is 1..)); // no error! + pub fn trait_type(p: &dyn Bar); //~ ERROR uses type `&dyn Bar` pub fn tuple_type(p: (i32, i32)); //~ ERROR uses type `(i32, i32)` pub fn tuple_type2(p: I32Pair); //~ ERROR uses type `(i32, i32)` pub fn zero_size(p: ZeroSize); //~ ERROR uses type `ZeroSize` @@ -66,11 +93,20 @@ extern "C" { -> ::std::marker::PhantomData; //~ ERROR uses type `PhantomData` pub fn fn_type(p: RustFn); //~ ERROR uses type `fn()` pub fn fn_type2(p: fn()); //~ ERROR uses type `fn()` - pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `Box` - pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128` - pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str` - pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `Box` + pub fn fn_contained(p: RustBoxRet); + pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `TransparentI128` + pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `TransparentStr` + pub fn transparent_fn(p: TransparentBoxFn); pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]` + pub fn multi_errors_per_arg( + f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) + ); + //~^^ ERROR: uses type `char` + //~| ERROR: uses type `&dyn Debug` + //~| ERROR: uses type `TwoBadTypes<'_>` + + 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>` diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr new file mode 100644 index 0000000000000..ef5219cb92bb5 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -0,0 +1,317 @@ +error: `repr(C)` type uses type `ZeroSize`, which is not FFI-safe + --> $DIR/ctypes.rs:28:1 + | +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a member to this struct + = note: `ZeroSize` has no fields +note: the type is defined here + --> $DIR/ctypes.rs:28:1 + | +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/ctypes.rs:8:36 + | +LL | #![deny(improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `repr(C)` type uses type `dyn Debug`, which is not FFI-safe + --> $DIR/ctypes.rs:55:1 + | +LL | / pub struct UnsizedStructBecauseDyn { +LL | | +LL | | sized: u32, +LL | | unszd: dyn Debug, +LL | | } + | |_^ not FFI-safe + | + = note: trait objects have no C equivalent + +error: `repr(C)` type uses type `char`, which is not FFI-safe + --> $DIR/ctypes.rs:62:1 + | +LL | / pub struct TwoBadTypes<'a> { +LL | | +LL | | +LL | | non_c_type: char, +LL | | ref_with_mdata: &'a [u8], +LL | | } + | |_^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `repr(C)` type uses type `&[u8]`, which is not FFI-safe + --> $DIR/ctypes.rs:62:1 + | +LL | / pub struct TwoBadTypes<'a> { +LL | | +LL | | +LL | | non_c_type: char, +LL | | ref_with_mdata: &'a [u8], +LL | | } + | |_^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `&[u32]`, which is not FFI-safe + --> $DIR/ctypes.rs:77:26 + | +LL | pub fn slice_type(p: &[u32]); + | ^^^^^^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer +note: the lint level is defined here + --> $DIR/ctypes.rs:7:9 + | +LL | #![deny(improper_ctypes, improper_c_callbacks)] + | ^^^^^^^^^^^^^^^ + +error: `extern` block uses type `&str`, which is not FFI-safe + --> $DIR/ctypes.rs:78:24 + | +LL | pub fn str_type(p: &str); + | ^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `char`, which is not FFI-safe + --> $DIR/ctypes.rs:81:25 + | +LL | pub fn char_type(p: char); + | ^^^^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `extern` block uses type `i128`, which is not FFI-safe + --> $DIR/ctypes.rs:82:25 + | +LL | pub fn i128_type(p: i128); + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` block uses type `u128`, which is not FFI-safe + --> $DIR/ctypes.rs:83:25 + | +LL | pub fn u128_type(p: u128); + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` block uses type `(u32) is 1..`, which is not FFI-safe + --> $DIR/ctypes.rs:84:27 + | +LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead, or wrapping `(u32) is 1..` in an `Option<_>` + = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code + +error: `extern` block uses type `&dyn Bar`, which is not FFI-safe + --> $DIR/ctypes.rs:86:26 + | +LL | pub fn trait_type(p: &dyn Bar); + | ^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `(i32, i32)`, which is not FFI-safe + --> $DIR/ctypes.rs:87:26 + | +LL | pub fn tuple_type(p: (i32, i32)); + | ^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` block uses type `(i32, i32)`, which is not FFI-safe + --> $DIR/ctypes.rs:88:27 + | +LL | pub fn tuple_type2(p: I32Pair); + | ^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` block uses type `ZeroSize`, which is not FFI-safe + --> $DIR/ctypes.rs:89:25 + | +LL | pub fn zero_size(p: ZeroSize); + | ^^^^^^^^ not FFI-safe + | + = help: consider adding a member to this struct + = note: `ZeroSize` has no fields +note: the type is defined here + --> $DIR/ctypes.rs:28:1 + | +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^ + +error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe + --> $DIR/ctypes.rs:90:33 + | +LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); + | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: composed only of `PhantomData` +note: the type is defined here + --> $DIR/ctypes.rs:70:1 + | +LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` block uses type `PhantomData`, which is not FFI-safe + --> $DIR/ctypes.rs:93:12 + | +LL | -> ::std::marker::PhantomData; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: composed only of `PhantomData` + +error: `extern` block uses type `fn()`, which is not FFI-safe + --> $DIR/ctypes.rs:94:23 + | +LL | pub fn fn_type(p: RustFn); + | ^^^^^^ not FFI-safe + | + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` block uses type `fn()`, which is not FFI-safe + --> $DIR/ctypes.rs:95:24 + | +LL | pub fn fn_type2(p: fn()); + | ^^^^ not FFI-safe + | + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` block uses type `TransparentI128`, which is not FFI-safe + --> $DIR/ctypes.rs:97:32 + | +LL | pub fn transparent_i128(p: TransparentI128); + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`TransparentI128`) is FFI-unsafe due to a `i128` field +note: the type is defined here + --> $DIR/ctypes.rs:34:1 + | +LL | pub struct TransparentI128(i128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` block uses type `TransparentStr`, which is not FFI-safe + --> $DIR/ctypes.rs:98:31 + | +LL | pub fn transparent_str(p: TransparentStr); + | ^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`TransparentStr`) is FFI-unsafe due to a `&str` field +note: the type is defined here + --> $DIR/ctypes.rs:36:1 + | +LL | pub struct TransparentStr(&'static str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using `*const u8` and a length instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `[u8; 8]`, which is not FFI-safe + --> $DIR/ctypes.rs:100:27 + | +LL | pub fn raw_array(arr: [u8; 8]); + | ^^^^^^^ not FFI-safe + | + = help: consider passing a pointer to the array + = note: passing raw arrays by value is not FFI-safe + +error: `extern` callback uses type `char`, which is not FFI-safe + --> $DIR/ctypes.rs:102:12 + | +LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), TwoBadTypes<'b>)` is FFI-unsafe due to `char` + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent +note: the lint level is defined here + --> $DIR/ctypes.rs:7:26 + | +LL | #![deny(improper_ctypes, improper_c_callbacks)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` callback uses type `&dyn Debug`, which is not FFI-safe + --> $DIR/ctypes.rs:102:12 + | +LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), TwoBadTypes<'b>)` is FFI-unsafe due to `&dyn Debug` + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` callback uses type `TwoBadTypes<'_>`, which is not FFI-safe + --> $DIR/ctypes.rs:102:12 + | +LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), TwoBadTypes<'b>)` is FFI-unsafe due to `TwoBadTypes<'_>` + = note: this struct/enum/union (`TwoBadTypes<'_>`) is FFI-unsafe due to a `&[u8]` field +note: the type is defined here + --> $DIR/ctypes.rs:62:1 + | +LL | pub struct TwoBadTypes<'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `&UnsizedStructBecauseDyn`, which is not FFI-safe + --> $DIR/ctypes.rs:109:47 + | +LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `Option>`, which is not FFI-safe + --> $DIR/ctypes.rs:111:26 + | +LL | pub fn no_niche_a(a: Option>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 `Option>`, which is not FFI-safe + --> $DIR/ctypes.rs:113:26 + | +LL | pub fn no_niche_b(b: Option>); + | ^^^^^^^^^^^^^^^^^^^^^^^^ 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/ctypes.rs:116:34 + | +LL | pub static static_u128_type: u128; + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` block uses type `u128`, which is not FFI-safe + --> $DIR/ctypes.rs:117: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 29 previous errors + diff --git a/tests/ui/lint/improper_ctypes/lint-113436-1.rs b/tests/ui/lint/improper_ctypes/lint-113436-1.rs new file mode 100644 index 0000000000000..11dae42fa538b --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.rs @@ -0,0 +1,28 @@ +#![deny(improper_c_fn_definitions, improper_ctype_definitions)] + +#[repr(C)] +pub struct Foo { + a: u8, + b: (), +} + +extern "C" fn foo(x: Foo) -> Foo { + todo!() +} + +struct NotSafe(u32); + +#[repr(C)] +pub struct Bar { //~ ERROR `repr(C)` type uses type `NotSafe` + a: u8, + b: (), + c: NotSafe, +} + +extern "C" fn bar(x: Bar) -> Bar { + //~^ ERROR `extern` fn uses type `Bar`, which is not FFI-safe + //~^^ ERROR `extern` fn uses type `Bar`, which is not FFI-safe + todo!() +} + +fn main() {} diff --git a/tests/ui/lint/improper_ctypes/lint-113436-1.stderr b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr new file mode 100644 index 0000000000000..255063e42204d --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr @@ -0,0 +1,70 @@ +error: `repr(C)` type uses type `NotSafe`, which is not FFI-safe + --> $DIR/lint-113436-1.rs:16:1 + | +LL | / pub struct Bar { +LL | | a: u8, +LL | | b: (), +LL | | c: NotSafe, +LL | | } + | |_^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `NotSafe` + = note: `NotSafe` has unspecified layout +note: the type is defined here + --> $DIR/lint-113436-1.rs:13:1 + | +LL | struct NotSafe(u32); + | ^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint-113436-1.rs:1:36 + | +LL | #![deny(improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `Bar`, which is not FFI-safe + --> $DIR/lint-113436-1.rs:22:22 + | +LL | extern "C" fn bar(x: Bar) -> Bar { + | ^^^ not FFI-safe + | + = note: this struct/enum/union (`Bar`) is FFI-unsafe due to a `NotSafe` field +note: the type is defined here + --> $DIR/lint-113436-1.rs:16:1 + | +LL | pub struct Bar { + | ^^^^^^^^^^^^^^ + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `NotSafe` + = note: `NotSafe` has unspecified layout +note: the type is defined here + --> $DIR/lint-113436-1.rs:13:1 + | +LL | struct NotSafe(u32); + | ^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint-113436-1.rs:1:9 + | +LL | #![deny(improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `Bar`, which is not FFI-safe + --> $DIR/lint-113436-1.rs:22:30 + | +LL | extern "C" fn bar(x: Bar) -> Bar { + | ^^^ not FFI-safe + | + = note: this struct/enum/union (`Bar`) is FFI-unsafe due to a `NotSafe` field +note: the type is defined here + --> $DIR/lint-113436-1.rs:16:1 + | +LL | pub struct Bar { + | ^^^^^^^^^^^^^^ + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `NotSafe` + = note: `NotSafe` has unspecified layout +note: the type is defined here + --> $DIR/lint-113436-1.rs:13:1 + | +LL | struct NotSafe(u32); + | ^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/lint-ctypes-73249-2.rs b/tests/ui/lint/improper_ctypes/lint-73249-2.rs similarity index 59% rename from tests/ui/lint/lint-ctypes-73249-2.rs rename to tests/ui/lint/improper_ctypes/lint-73249-2.rs index 31af0e3d381ef..5f1f9c7386037 100644 --- a/tests/ui/lint/lint-ctypes-73249-2.rs +++ b/tests/ui/lint/improper_ctypes/lint-73249-2.rs @@ -1,5 +1,5 @@ #![feature(type_alias_impl_trait)] -#![deny(improper_ctypes)] +#![deny(improper_ctypes, improper_ctype_definitions)] trait Baz {} @@ -24,7 +24,9 @@ struct A { } extern "C" { - fn lint_me() -> A<()>; //~ ERROR: uses type `Qux` + // possible FIXME: currenty, the error comes from the non-option'd reference, not the unsafety + // of Qux + fn lint_me() -> A<()>; //~ ERROR: uses type `A<()>` } fn main() {} diff --git a/tests/ui/lint/improper_ctypes/lint-73249-2.stderr b/tests/ui/lint/improper_ctypes/lint-73249-2.stderr new file mode 100644 index 0000000000000..9f52eefc686e5 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-73249-2.stderr @@ -0,0 +1,23 @@ +error: `extern` block uses type `A<()>`, which is not FFI-safe + --> $DIR/lint-73249-2.rs:29:21 + | +LL | fn lint_me() -> A<()>; + | ^^^^^ not FFI-safe + | + = note: this struct/enum/union (`A<()>`) is FFI-unsafe due to a `&Qux` field +note: the type is defined here + --> $DIR/lint-73249-2.rs:22:1 + | +LL | struct A { + | ^^^^^^^^^^^^^^^^ + = help: consider using a raw pointer, or wrapping `&Qux` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code +note: the lint level is defined here + --> $DIR/lint-73249-2.rs:2:9 + | +LL | #![deny(improper_ctypes, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/lint-ctypes-73249-3.rs b/tests/ui/lint/improper_ctypes/lint-73249-3.rs similarity index 53% rename from tests/ui/lint/lint-ctypes-73249-3.rs rename to tests/ui/lint/improper_ctypes/lint-73249-3.rs index 8bdf536bf77e6..e3e7659ba16c9 100644 --- a/tests/ui/lint/lint-ctypes-73249-3.rs +++ b/tests/ui/lint/improper_ctypes/lint-73249-3.rs @@ -1,5 +1,5 @@ #![feature(type_alias_impl_trait)] -#![deny(improper_ctypes)] +#![deny(improper_ctypes, improper_ctype_definitions)] pub trait Baz {} @@ -13,12 +13,12 @@ fn assign() -> Qux { } #[repr(C)] -pub struct A { +pub struct A { //~ ERROR: `repr(C)` type uses type `Qux` x: Qux, } extern "C" { - pub fn lint_me() -> A; //~ ERROR: uses type `Qux` + pub fn lint_me() -> A; //~ ERROR: `extern` block uses type `A` } fn main() {} diff --git a/tests/ui/lint/improper_ctypes/lint-73249-3.stderr b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr new file mode 100644 index 0000000000000..bd449c1fa6e77 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr @@ -0,0 +1,36 @@ +error: `repr(C)` type uses type `Qux`, which is not FFI-safe + --> $DIR/lint-73249-3.rs:16:1 + | +LL | / pub struct A { +LL | | x: Qux, +LL | | } + | |_^ not FFI-safe + | + = note: opaque types have no C equivalent +note: the lint level is defined here + --> $DIR/lint-73249-3.rs:2:26 + | +LL | #![deny(improper_ctypes, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` block uses type `A`, which is not FFI-safe + --> $DIR/lint-73249-3.rs:21:25 + | +LL | pub fn lint_me() -> A; + | ^ not FFI-safe + | + = note: this struct/enum/union (`A`) is FFI-unsafe due to a `Qux` field +note: the type is defined here + --> $DIR/lint-73249-3.rs:16:1 + | +LL | pub struct A { + | ^^^^^^^^^^^^ + = note: opaque types have no C equivalent +note: the lint level is defined here + --> $DIR/lint-73249-3.rs:2:9 + | +LL | #![deny(improper_ctypes, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/lint-ctypes-73249-5.rs b/tests/ui/lint/improper_ctypes/lint-73249-5.rs similarity index 68% rename from tests/ui/lint/lint-ctypes-73249-5.rs rename to tests/ui/lint/improper_ctypes/lint-73249-5.rs index cc6da59950d7a..8272256b5a83a 100644 --- a/tests/ui/lint/lint-ctypes-73249-5.rs +++ b/tests/ui/lint/improper_ctypes/lint-73249-5.rs @@ -1,5 +1,5 @@ #![feature(type_alias_impl_trait)] -#![deny(improper_ctypes)] +#![deny(improper_ctypes, improper_ctype_definitions)] pub trait Baz {} @@ -18,7 +18,7 @@ pub struct A { } extern "C" { - pub fn lint_me() -> A; //~ ERROR: uses type `Qux` + pub fn lint_me() -> A; //~ ERROR: uses type `A` } fn main() {} diff --git a/tests/ui/lint/improper_ctypes/lint-73249-5.stderr b/tests/ui/lint/improper_ctypes/lint-73249-5.stderr new file mode 100644 index 0000000000000..9172039afa104 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-73249-5.stderr @@ -0,0 +1,21 @@ +error: `extern` block uses type `A`, which is not FFI-safe + --> $DIR/lint-73249-5.rs:21:25 + | +LL | pub fn lint_me() -> A; + | ^ not FFI-safe + | + = note: this struct/enum/union (`A`) is FFI-unsafe due to a `Qux` field +note: the type is defined here + --> $DIR/lint-73249-5.rs:16:1 + | +LL | pub struct A { + | ^^^^^^^^^^^^ + = note: opaque types have no C equivalent +note: the lint level is defined here + --> $DIR/lint-73249-5.rs:2:9 + | +LL | #![deny(improper_ctypes, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/lint-ctypes-73251-1.rs b/tests/ui/lint/improper_ctypes/lint-73251-1.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73251-1.rs rename to tests/ui/lint/improper_ctypes/lint-73251-1.rs diff --git a/tests/ui/lint/lint-ctypes-73251-1.stderr b/tests/ui/lint/improper_ctypes/lint-73251-1.stderr similarity index 82% rename from tests/ui/lint/lint-ctypes-73251-1.stderr rename to tests/ui/lint/improper_ctypes/lint-73251-1.stderr index 675a9de51cd02..749722f0e2203 100644 --- a/tests/ui/lint/lint-ctypes-73251-1.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73251-1.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73251-1.rs:24:21 + --> $DIR/lint-73251-1.rs:24:21 | LL | fn lint_me() -> ::Assoc; | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: opaque types have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-73251-1.rs:2:9 + --> $DIR/lint-73251-1.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73251-2.rs b/tests/ui/lint/improper_ctypes/lint-73251-2.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73251-2.rs rename to tests/ui/lint/improper_ctypes/lint-73251-2.rs diff --git a/tests/ui/lint/lint-ctypes-73251-2.stderr b/tests/ui/lint/improper_ctypes/lint-73251-2.stderr similarity index 82% rename from tests/ui/lint/lint-ctypes-73251-2.stderr rename to tests/ui/lint/improper_ctypes/lint-73251-2.stderr index 634950b29ed43..3770b7d789f67 100644 --- a/tests/ui/lint/lint-ctypes-73251-2.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73251-2.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `AliasA`, which is not FFI-safe - --> $DIR/lint-ctypes-73251-2.rs:38:21 + --> $DIR/lint-73251-2.rs:38:21 | LL | fn lint_me() -> ::Assoc; | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: opaque types have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-73251-2.rs:2:9 + --> $DIR/lint-73251-2.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/improper_ctypes/lint-94223.rs b/tests/ui/lint/improper_ctypes/lint-94223.rs new file mode 100644 index 0000000000000..d6a03f19d543d --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-94223.rs @@ -0,0 +1,50 @@ +#![crate_type = "lib"] +#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_c_callbacks)] + +pub fn bad(f: extern "C" fn([u8])) {} +//~^ ERROR `extern` callback uses type `[u8]`, which is not FFI-safe + +pub fn bad_twice(f: Result) {} +//~^ ERROR `extern` callback uses type `[u8]`, which is not FFI-safe +//~^^ ERROR `extern` callback uses type `[u8]`, which is not FFI-safe + +struct BadStruct(extern "C" fn([u8])); +//~^ ERROR `extern` callback uses type `[u8]`, which is not FFI-safe + +enum BadEnum { + A(extern "C" fn([u8])), + //~^ ERROR `extern` callback uses type `[u8]`, which is not FFI-safe +} + +enum BadUnion { + A(extern "C" fn([u8])), + //~^ ERROR `extern` callback uses type `[u8]`, which is not FFI-safe +} + +type Foo = extern "C" fn([u8]); +//~^ ERROR `extern` callback uses type `[u8]`, which is not FFI-safe + +pub trait FooTrait { + type FooType; +} + +pub type Foo2 = extern "C" fn(Option<&::FooType>); +//~^ ERROR `extern` callback uses type `Option<&::FooType>`, which is not FFI-safe + +pub struct FfiUnsafe; + +#[allow(improper_c_fn_definitions)] +extern "C" fn f(_: FfiUnsafe) { + unimplemented!() +} + +pub static BAD: extern "C" fn(FfiUnsafe) = f; +//~^ ERROR `extern` callback uses type `FfiUnsafe`, which is not FFI-safe + +pub static BAD_TWICE: Result = Ok(f); +//~^ ERROR `extern` callback uses type `FfiUnsafe`, which is not FFI-safe +//~^^ ERROR `extern` callback uses type `FfiUnsafe`, which is not FFI-safe + +pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; +//~^ ERROR `extern` callback uses type `FfiUnsafe`, which is not FFI-safe diff --git a/tests/ui/lint/improper_ctypes/lint-94223.stderr b/tests/ui/lint/improper_ctypes/lint-94223.stderr new file mode 100644 index 0000000000000..cbde25517ec82 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-94223.stderr @@ -0,0 +1,147 @@ +error: `extern` callback uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-94223.rs:5:15 + | +LL | pub fn bad(f: extern "C" fn([u8])) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: slices have no C equivalent +note: the lint level is defined here + --> $DIR/lint-94223.rs:3:9 + | +LL | #![deny(improper_c_callbacks)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` callback uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-94223.rs:8:28 + | +LL | pub fn bad_twice(f: Result) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: slices have no C equivalent + +error: `extern` callback uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-94223.rs:8:49 + | +LL | pub fn bad_twice(f: Result) {} + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: slices have no C equivalent + +error: `extern` callback uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-94223.rs:12:18 + | +LL | struct BadStruct(extern "C" fn([u8])); + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: slices have no C equivalent + +error: `extern` callback uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-94223.rs:16:7 + | +LL | A(extern "C" fn([u8])), + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: slices have no C equivalent + +error: `extern` callback uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-94223.rs:21:7 + | +LL | A(extern "C" fn([u8])), + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: slices have no C equivalent + +error: `extern` callback uses type `[u8]`, which is not FFI-safe + --> $DIR/lint-94223.rs:25:12 + | +LL | type Foo = extern "C" fn([u8]); + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn([u8])` is FFI-unsafe due to `[u8]` + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: slices have no C equivalent + +error: `extern` callback uses type `Option<&::FooType>`, which is not FFI-safe + --> $DIR/lint-94223.rs:32:20 + | +LL | pub type Foo2 = extern "C" fn(Option<&::FooType>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `for<'a> extern "C" fn(Option<&'a ::FooType>)` is FFI-unsafe due to `Option<&::FooType>` + = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum + = note: enum has no representation hint + +error: `extern` callback uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-94223.rs:42:17 + | +LL | pub static BAD: extern "C" fn(FfiUnsafe) = f; + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn(FfiUnsafe)` is FFI-unsafe due to `FfiUnsafe` + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `FfiUnsafe` + = note: `FfiUnsafe` has unspecified layout +note: the type is defined here + --> $DIR/lint-94223.rs:35:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` callback uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-94223.rs:45:30 + | +LL | pub static BAD_TWICE: Result = Ok(f); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn(FfiUnsafe)` is FFI-unsafe due to `FfiUnsafe` + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `FfiUnsafe` + = note: `FfiUnsafe` has unspecified layout +note: the type is defined here + --> $DIR/lint-94223.rs:35:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` callback uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-94223.rs:45:56 + | +LL | pub static BAD_TWICE: Result = Ok(f); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn(FfiUnsafe)` is FFI-unsafe due to `FfiUnsafe` + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `FfiUnsafe` + = note: `FfiUnsafe` has unspecified layout +note: the type is defined here + --> $DIR/lint-94223.rs:35:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` callback uses type `FfiUnsafe`, which is not FFI-safe + --> $DIR/lint-94223.rs:49:22 + | +LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn(FfiUnsafe)` is FFI-unsafe due to `FfiUnsafe` + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `FfiUnsafe` + = note: `FfiUnsafe` has unspecified layout +note: the type is defined here + --> $DIR/lint-94223.rs:35:1 + | +LL | pub struct FfiUnsafe; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + diff --git a/tests/ui/lint/improper_ctypes/lint-cstr.rs b/tests/ui/lint/improper_ctypes/lint-cstr.rs new file mode 100644 index 0000000000000..a0b818a553e44 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-cstr.rs @@ -0,0 +1,40 @@ +#![crate_type = "lib"] +#![deny(improper_ctypes, improper_c_fn_definitions)] + +use std::ffi::{CStr, CString}; + +extern "C" { + fn take_cstr(s: CStr); + //~^ ERROR `extern` block uses type `CStr`, which is not FFI-safe + //~| HELP consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead + fn take_cstr_ref(s: &CStr); + //~^ ERROR `extern` block uses type `&CStr`, which is not FFI-safe + //~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + fn take_cstring(s: CString); + //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe + //~| HELP consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead + fn take_cstring_ref(s: &CString); + //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe + //~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + + fn take_cstring_ptr_mut(s: *mut CString); + //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe + //~| HELP consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + + fn take_cstring_ref_mut(s: &mut CString); + //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe + //~| HELP consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer +} + +extern "C" fn rust_take_cstr_ref(s: &CStr) {} +//~^ ERROR `extern` fn uses type `&CStr`, which is not FFI-safe +//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` +extern "C" fn rust_take_cstring(s: CString) {} +//~^ ERROR `extern` fn uses type `CString`, which is not FFI-safe +//~| HELP consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead +extern "C" fn rust_take_cstring_ptr_mut(s: *mut CString) {} +//~^ ERROR `extern` fn uses type `CString`, which is not FFI-safe +//~| HELP consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer +extern "C" fn rust_take_cstring_ref_mut(s: &mut CString) {} +//~^ ERROR `extern` fn uses type `CString`, which is not FFI-safe +//~| HELP consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer diff --git a/tests/ui/lint/improper_ctypes/lint-cstr.stderr b/tests/ui/lint/improper_ctypes/lint-cstr.stderr new file mode 100644 index 0000000000000..281c94875c8c5 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-cstr.stderr @@ -0,0 +1,110 @@ +error: `extern` block uses type `CStr`, which is not FFI-safe + --> $DIR/lint-cstr.rs:7:21 + | +LL | fn take_cstr(s: CStr); + | ^^^^ not FFI-safe + | + = help: consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, + and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + (note that `CString::into_raw()`'s output must not be `libc::free()`'d) + = note: `CStr`/`CString` do not have a guaranteed layout +note: the lint level is defined here + --> $DIR/lint-cstr.rs:2:9 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] + | ^^^^^^^^^^^^^^^ + +error: `extern` block uses type `&CStr`, which is not FFI-safe + --> $DIR/lint-cstr.rs:10:25 + | +LL | fn take_cstr_ref(s: &CStr); + | ^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` or `CString::as_ptr()` + = note: `CStr`/`CString` do not have a guaranteed layout + +error: `extern` block uses type `CString`, which is not FFI-safe + --> $DIR/lint-cstr.rs:13:24 + | +LL | fn take_cstring(s: CString); + | ^^^^^^^ not FFI-safe + | + = help: consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, + and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + (note that `CString::into_raw()`'s output must not be `libc::free()`'d) + = note: `CStr`/`CString` do not have a guaranteed layout + +error: `extern` block uses type `CString`, which is not FFI-safe + --> $DIR/lint-cstr.rs:16:28 + | +LL | fn take_cstring_ref(s: &CString); + | ^^^^^^^^ not FFI-safe + | + = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` or `CString::as_ptr()` + = note: `CStr`/`CString` do not have a guaranteed layout + +error: `extern` block uses type `CString`, which is not FFI-safe + --> $DIR/lint-cstr.rs:20:32 + | +LL | fn take_cstring_ptr_mut(s: *mut CString); + | ^^^^^^^^^^^^ not FFI-safe + | + = help: consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + = note: `CStr`/`CString` do not have a guaranteed layout + +error: `extern` block uses type `CString`, which is not FFI-safe + --> $DIR/lint-cstr.rs:24:32 + | +LL | fn take_cstring_ref_mut(s: &mut CString); + | ^^^^^^^^^^^^ not FFI-safe + | + = help: consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + = note: `CStr`/`CString` do not have a guaranteed layout + +error: `extern` fn uses type `&CStr`, which is not FFI-safe + --> $DIR/lint-cstr.rs:29:37 + | +LL | extern "C" fn rust_take_cstr_ref(s: &CStr) {} + | ^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` or `CString::as_ptr()` + = note: `CStr`/`CString` do not have a guaranteed layout +note: the lint level is defined here + --> $DIR/lint-cstr.rs:2:26 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `CString`, which is not FFI-safe + --> $DIR/lint-cstr.rs:32:36 + | +LL | extern "C" fn rust_take_cstring(s: CString) {} + | ^^^^^^^ not FFI-safe + | + = help: consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, + and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + (note that `CString::into_raw()`'s output must not be `libc::free()`'d) + = note: `CStr`/`CString` do not have a guaranteed layout + +error: `extern` fn uses type `CString`, which is not FFI-safe + --> $DIR/lint-cstr.rs:35:44 + | +LL | extern "C" fn rust_take_cstring_ptr_mut(s: *mut CString) {} + | ^^^^^^^^^^^^ not FFI-safe + | + = help: consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + = note: `CStr`/`CString` do not have a guaranteed layout + +error: `extern` fn uses type `CString`, which is not FFI-safe + --> $DIR/lint-cstr.rs:38:44 + | +LL | extern "C" fn rust_take_cstring_ref_mut(s: &mut CString) {} + | ^^^^^^^^^^^^ not FFI-safe + | + = help: consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer + = note: `CStr`/`CString` do not have a guaranteed layout + +error: aborting due to 10 previous errors + diff --git a/tests/ui/lint/lint-ctypes-enum.rs b/tests/ui/lint/improper_ctypes/lint-enum.rs similarity index 99% rename from tests/ui/lint/lint-ctypes-enum.rs rename to tests/ui/lint/improper_ctypes/lint-enum.rs index b2ef27b833bdb..1e56b829b9242 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/improper_ctypes/lint-enum.rs @@ -55,7 +55,6 @@ enum I128 { B, C, } - #[repr(transparent)] struct TransparentStruct(T, std::marker::PhantomData); @@ -80,7 +79,7 @@ struct Field(()); enum NonExhaustive {} extern "C" { - fn zf(x: Z); + fn zf(x: Z); //~ ERROR `extern` block uses type `Z` fn uf(x: U); //~ ERROR `extern` block uses type `U` fn bf(x: B); //~ ERROR `extern` block uses type `B` fn tf(x: T); //~ ERROR `extern` block uses type `T` diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/improper_ctypes/lint-enum.stderr similarity index 88% rename from tests/ui/lint/lint-ctypes-enum.stderr rename to tests/ui/lint/improper_ctypes/lint-enum.stderr index d5fc844f75607..410ce4eaef1f4 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/improper_ctypes/lint-enum.stderr @@ -1,5 +1,23 @@ +error: `extern` block uses type `Z`, which is not FFI-safe + --> $DIR/lint-enum.rs:82:14 + | +LL | fn zf(x: Z); + | ^ not FFI-safe + | + = note: zero-variant enums and other uninhabited types are not allowed in function arguments and static variables +note: the type is defined here + --> $DIR/lint-enum.rs:10:1 + | +LL | enum Z {} + | ^^^^^^ +note: the lint level is defined here + --> $DIR/lint-enum.rs:2:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:84:14 + --> $DIR/lint-enum.rs:83:14 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,18 +25,13 @@ LL | fn uf(x: U); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:11:1 + --> $DIR/lint-enum.rs:11:1 | LL | enum U { | ^^^^^^ -note: the lint level is defined here - --> $DIR/lint-ctypes-enum.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:85:14 + --> $DIR/lint-enum.rs:84:14 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +39,13 @@ LL | fn bf(x: B); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:14:1 + --> $DIR/lint-enum.rs:14:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:86:14 + --> $DIR/lint-enum.rs:85:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,39 +53,39 @@ LL | fn tf(x: T); = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:18:1 + --> $DIR/lint-enum.rs:18:1 | LL | enum T { | ^^^^^^ error: `extern` block uses type `U128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:90:21 + --> $DIR/lint-enum.rs:89:21 | LL | fn repr_u128(x: U128); | ^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:46:1 + --> $DIR/lint-enum.rs:46:1 | LL | enum U128 { | ^^^^^^^^^ error: `extern` block uses type `I128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:91:21 + --> $DIR/lint-enum.rs:90:21 | LL | fn repr_i128(x: I128); | ^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI note: the type is defined here - --> $DIR/lint-ctypes-enum.rs:53:1 + --> $DIR/lint-enum.rs:53:1 | LL | enum I128 { | ^^^^^^^^^ error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:100:31 + --> $DIR/lint-enum.rs:99:31 | LL | fn option_nonzero_u128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +93,7 @@ LL | fn option_nonzero_u128(x: Option>); = 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:107:31 + --> $DIR/lint-enum.rs:106:31 | LL | fn option_nonzero_i128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -88,7 +101,7 @@ LL | fn option_nonzero_i128(x: Option>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:112:36 + --> $DIR/lint-enum.rs:111:36 | LL | fn option_transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -97,7 +110,7 @@ LL | fn option_transparent_union(x: Option = note: enum has no representation hint error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:114:28 + --> $DIR/lint-enum.rs:113:28 | LL | fn option_repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -106,7 +119,7 @@ LL | fn option_repr_rust(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Option`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:115:21 + --> $DIR/lint-enum.rs:114:21 | LL | fn option_u8(x: Option); | ^^^^^^^^^^ not FFI-safe @@ -115,7 +128,7 @@ LL | fn option_u8(x: Option); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:125:33 + --> $DIR/lint-enum.rs:124:33 | LL | fn result_nonzero_u128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,7 +136,7 @@ LL | fn result_nonzero_u128_t(x: Result, ()>); = 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:132:33 + --> $DIR/lint-enum.rs:131:33 | LL | fn result_nonzero_i128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -131,7 +144,7 @@ LL | fn result_nonzero_i128_t(x: Result, ()>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:137:38 + --> $DIR/lint-enum.rs:136:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -140,7 +153,7 @@ LL | fn result_transparent_union_t(x: Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:139:30 + --> $DIR/lint-enum.rs:138:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -149,7 +162,7 @@ LL | fn result_repr_rust_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result, U>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:143:51 + --> $DIR/lint-enum.rs:142:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -158,7 +171,7 @@ LL | fn result_1zst_exhaustive_single_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, B>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:145:53 + --> $DIR/lint-enum.rs:144:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -167,7 +180,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result = note: enum has no representation hint error: `extern` block uses type `Result, NonExhaustive>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:147:51 + --> $DIR/lint-enum.rs:146:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +189,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, = note: enum has no representation hint error: `extern` block uses type `Result, Field>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:150:49 + --> $DIR/lint-enum.rs:149:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -185,7 +198,7 @@ LL | fn result_1zst_exhaustive_single_field_t(x: Result, Fi = note: enum has no representation hint error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:152:30 + --> $DIR/lint-enum.rs:151:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -194,7 +207,7 @@ LL | fn result_cascading_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:163:33 + --> $DIR/lint-enum.rs:162:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -202,7 +215,7 @@ LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); = 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:170:33 + --> $DIR/lint-enum.rs:169:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -210,7 +223,7 @@ LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:175:38 + --> $DIR/lint-enum.rs:174:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -219,7 +232,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:177:30 + --> $DIR/lint-enum.rs:176:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -228,7 +241,7 @@ LL | fn result_repr_rust_e(x: Result<(), Rust>>); = note: enum has no representation hint error: `extern` block uses type `Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:181:51 + --> $DIR/lint-enum.rs:180:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -237,7 +250,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:183:53 + --> $DIR/lint-enum.rs:182:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -246,7 +259,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:185:51 + --> $DIR/lint-enum.rs:184:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -255,7 +268,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:188:49 + --> $DIR/lint-enum.rs:187:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -264,7 +277,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:190:30 + --> $DIR/lint-enum.rs:189:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -273,7 +286,7 @@ LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); = note: enum has no representation hint error: `extern` block uses type `Result<(), ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:192:27 + --> $DIR/lint-enum.rs:191:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe @@ -281,5 +294,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 29 previous errors +error: aborting due to 30 previous errors diff --git a/tests/ui/lint/lint-ctypes-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs similarity index 77% rename from tests/ui/lint/lint-ctypes-fn.rs rename to tests/ui/lint/improper_ctypes/lint-fn.rs index 73820c86d1a02..ecf21618e12ef 100644 --- a/tests/ui/lint/lint-ctypes-fn.rs +++ b/tests/ui/lint/improper_ctypes/lint-fn.rs @@ -1,5 +1,6 @@ #![allow(private_interfaces)] -#![deny(improper_ctypes_definitions)] +#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_c_callbacks)] use std::default::Default; use std::marker::PhantomData; @@ -22,11 +23,11 @@ pub struct StructWithProjectionAndLifetime<'a>( pub type I32Pair = (i32, i32); #[repr(C)] -pub struct ZeroSize; +pub struct ZeroSize; //~ ERROR uses type `ZeroSize` pub type RustFn = fn(); -pub type RustBadRet = extern "C" fn() -> Box; +pub type RustBadRet = extern "C" fn() -> (u32,u64); //~ ERROR uses type `(u32, u64)` pub type CVoidRet = (); @@ -39,7 +40,7 @@ pub struct TransparentI128(i128); pub struct TransparentStr(&'static str); #[repr(transparent)] -pub struct TransparentBadFn(RustBadRet); +pub struct TransparentBadFn(RustBadRet); // note: non-null ptr assumption #[repr(transparent)] pub struct TransparentInt(u32); @@ -68,14 +69,16 @@ pub extern "C" fn ptr_unit(p: *const ()) { } pub extern "C" fn ptr_tuple(p: *const ((),)) { } pub extern "C" fn slice_type(p: &[u32]) { } -//~^ ERROR: uses type `[u32]` +//~^ ERROR: uses type `&[u32]` pub extern "C" fn str_type(p: &str) { } -//~^ ERROR: uses type `str` +//~^ ERROR: uses type `&str` pub extern "C" fn box_type(p: Box) { } +//~^ ERROR: uses type `Box` pub extern "C" fn opt_box_type(p: Option>) { } +// no error here! pub extern "C" fn boxed_slice(p: Box<[u8]>) { } //~^ ERROR: uses type `Box<[u8]>` @@ -119,22 +122,29 @@ pub extern "C" fn fn_type2(p: fn()) { } //~^ ERROR uses type `fn()` pub extern "C" fn fn_contained(p: RustBadRet) { } +// ^ FIXME it doesn't see the error... but at least it reports it elsewhere? pub extern "C" fn transparent_i128(p: TransparentI128) { } -//~^ ERROR: uses type `i128` +//~^ ERROR: uses type `TransparentI128` pub extern "C" fn transparent_str(p: TransparentStr) { } -//~^ ERROR: uses type `str` +//~^ ERROR: uses type `TransparentStr` pub extern "C" fn transparent_fn(p: TransparentBadFn) { } +//~^ ERROR: uses type `TransparentBadFn` +// ^ FIXME it doesn't see the actual FnPtr's error... but at least it reports it elsewhere? pub extern "C" fn good3(fptr: Option) { } -pub extern "C" fn good4(aptr: &[u8; 4 as usize]) { } +pub extern "C" fn argument_with_assumptions_4(aptr: &[u8; 4 as usize]) { } +//~^ ERROR: uses type `&[u8; 4]` pub extern "C" fn good5(s: StructWithProjection) { } -pub extern "C" fn good6(s: StructWithProjectionAndLifetime) { } +pub extern "C" fn argument_with_assumptions_6(s: StructWithProjectionAndLifetime) { } +//~^ ERROR: uses type `StructWithProjectionAndLifetime<'_>` +// note: the type translation might be a little eager for +// `::It` pub extern "C" fn good7(fptr: extern "C" fn() -> ()) { } @@ -150,7 +160,8 @@ pub extern "C" fn good12(size: usize) { } pub extern "C" fn good13(n: TransparentInt) { } -pub extern "C" fn good14(p: TransparentRef) { } +pub extern "C" fn argument_with_assumptions_14(p: TransparentRef) { } +//~^ ERROR: uses type `TransparentRef<'_>` pub extern "C" fn good15(p: TransparentLifetime) { } @@ -158,7 +169,7 @@ pub extern "C" fn good16(p: TransparentUnit) { } pub extern "C" fn good17(p: TransparentCustomZst) { } -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C" fn good18(_: &String) { } #[cfg(not(target_arch = "wasm32"))] diff --git a/tests/ui/lint/improper_ctypes/lint-fn.stderr b/tests/ui/lint/improper_ctypes/lint-fn.stderr new file mode 100644 index 0000000000000..06086de892cec --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-fn.stderr @@ -0,0 +1,304 @@ +error: `repr(C)` type uses type `ZeroSize`, which is not FFI-safe + --> $DIR/lint-fn.rs:26:1 + | +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a member to this struct + = note: `ZeroSize` has no fields +note: the type is defined here + --> $DIR/lint-fn.rs:26:1 + | +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint-fn.rs:2:53 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` callback uses type `(u32, u64)`, which is not FFI-safe + --> $DIR/lint-fn.rs:30:23 + | +LL | pub type RustBadRet = extern "C" fn() -> (u32,u64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `extern "C" fn() -> (u32, u64)` is FFI-unsafe due to `(u32, u64)` + = help: consider using a struct instead + = note: tuples have unspecified layout +note: the lint level is defined here + --> $DIR/lint-fn.rs:3:9 + | +LL | #![deny(improper_c_callbacks)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `&[u32]`, which is not FFI-safe + --> $DIR/lint-fn.rs:71:33 + | +LL | pub extern "C" fn slice_type(p: &[u32]) { } + | ^^^^^^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer +note: the lint level is defined here + --> $DIR/lint-fn.rs:2:26 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `&str`, which is not FFI-safe + --> $DIR/lint-fn.rs:74:31 + | +LL | pub extern "C" fn str_type(p: &str) { } + | ^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-fn.rs:77:31 + | +LL | pub extern "C" fn box_type(p: Box) { } + | ^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe + --> $DIR/lint-fn.rs:83:34 + | +LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } + | ^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-fn.rs:86:35 + | +LL | pub extern "C" fn boxed_string(p: Box) { } + | ^^^^^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-fn.rs:89:34 + | +LL | pub extern "C" fn boxed_trait(p: Box) { } + | ^^^^^^^^^^^^^^ not FFI-safe + | + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `char`, which is not FFI-safe + --> $DIR/lint-fn.rs:92:32 + | +LL | pub extern "C" fn char_type(p: char) { } + | ^^^^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-fn.rs:95:32 + | +LL | pub extern "C" fn i128_type(p: i128) { } + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `u128`, which is not FFI-safe + --> $DIR/lint-fn.rs:98:32 + | +LL | pub extern "C" fn u128_type(p: u128) { } + | ^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe + --> $DIR/lint-fn.rs:101:33 + | +LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } + | ^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe + --> $DIR/lint-fn.rs:104:34 + | +LL | pub extern "C" fn tuple_type2(p: I32Pair) { } + | ^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `ZeroSize`, which is not FFI-safe + --> $DIR/lint-fn.rs:107:32 + | +LL | pub extern "C" fn zero_size(p: ZeroSize) { } + | ^^^^^^^^ not FFI-safe + | + = help: consider adding a member to this struct + = note: `ZeroSize` has no fields +note: the type is defined here + --> $DIR/lint-fn.rs:26:1 + | +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe + --> $DIR/lint-fn.rs:110:40 + | +LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } + | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: composed only of `PhantomData` +note: the type is defined here + --> $DIR/lint-fn.rs:61:1 + | +LL | pub struct ZeroSizeWithPhantomData(PhantomData); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `PhantomData`, which is not FFI-safe + --> $DIR/lint-fn.rs:113:51 + | +LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { + | ^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: composed only of `PhantomData` + +error: `extern` fn uses type `fn()`, which is not FFI-safe + --> $DIR/lint-fn.rs:118:30 + | +LL | pub extern "C" fn fn_type(p: RustFn) { } + | ^^^^^^ not FFI-safe + | + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` fn uses type `fn()`, which is not FFI-safe + --> $DIR/lint-fn.rs:121:31 + | +LL | pub extern "C" fn fn_type2(p: fn()) { } + | ^^^^ not FFI-safe + | + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` fn uses type `TransparentI128`, which is not FFI-safe + --> $DIR/lint-fn.rs:127:39 + | +LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`TransparentI128`) is FFI-unsafe due to a `i128` field +note: the type is defined here + --> $DIR/lint-fn.rs:37:1 + | +LL | pub struct TransparentI128(i128); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `TransparentStr`, which is not FFI-safe + --> $DIR/lint-fn.rs:130:38 + | +LL | pub extern "C" fn transparent_str(p: TransparentStr) { } + | ^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`TransparentStr`) is FFI-unsafe due to a `&str` field +note: the type is defined here + --> $DIR/lint-fn.rs:40:1 + | +LL | pub struct TransparentStr(&'static str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using `*const u8` and a length instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `TransparentBadFn`, which is not FFI-safe + --> $DIR/lint-fn.rs:133:37 + | +LL | pub extern "C" fn transparent_fn(p: TransparentBadFn) { } + | ^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`TransparentBadFn`) is FFI-unsafe due to a `extern "C" fn() -> (u32, u64)` field +note: the type is defined here + --> $DIR/lint-fn.rs:43:1 + | +LL | pub struct TransparentBadFn(RustBadRet); // note: non-null ptr assumption + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using a raw pointer, or wrapping `extern "C" fn() -> (u32, u64)` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `&[u8; 4]`, which is not FFI-safe + --> $DIR/lint-fn.rs:139:53 + | +LL | pub extern "C" fn argument_with_assumptions_4(aptr: &[u8; 4 as usize]) { } + | ^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&[u8; 4]` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `StructWithProjectionAndLifetime<'_>`, which is not FFI-safe + --> $DIR/lint-fn.rs:144:50 + | +LL | pub extern "C" fn argument_with_assumptions_6(s: StructWithProjectionAndLifetime) { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`StructWithProjectionAndLifetime<'_>`) is FFI-unsafe due to a `&mut StructWithProjectionAndLifetime<'_>` field +note: the type is defined here + --> $DIR/lint-fn.rs:19:1 + | +LL | pub struct StructWithProjectionAndLifetime<'a>( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using a raw pointer, or wrapping `&mut StructWithProjectionAndLifetime<'_>` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `TransparentRef<'_>`, which is not FFI-safe + --> $DIR/lint-fn.rs:163:51 + | +LL | pub extern "C" fn argument_with_assumptions_14(p: TransparentRef) { } + | ^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`TransparentRef<'_>`) is FFI-unsafe due to a `&TransparentInt` field +note: the type is defined here + --> $DIR/lint-fn.rs:49:1 + | +LL | pub struct TransparentRef<'a>(&'a TransparentInt); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using a raw pointer, or wrapping `&TransparentInt` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `PhantomData`, which is not FFI-safe + --> $DIR/lint-fn.rs:183:43 + | +LL | pub extern "C" fn unused_generic2() -> PhantomData { + | ^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: composed only of `PhantomData` + +error: `extern` fn uses type `Vec`, which is not FFI-safe + --> $DIR/lint-fn.rs:196:39 + | +LL | pub extern "C" fn used_generic4(x: Vec) { } + | ^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `Vec` + = note: `Vec` has unspecified layout + +error: `extern` fn uses type `Vec`, which is not FFI-safe + --> $DIR/lint-fn.rs:199:41 + | +LL | pub extern "C" fn used_generic5() -> Vec { + | ^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `Vec` + = note: `Vec` has unspecified layout + +error: aborting due to 27 previous errors + diff --git a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs b/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.rs similarity index 81% rename from tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs rename to tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.rs index ca08eb23a57eb..57addd449ee3a 100644 --- a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs +++ b/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.rs @@ -1,4 +1,4 @@ -#![deny(improper_ctypes_definitions)] +#![deny(improper_c_fn_definitions)] extern "C" fn foo() -> Option<&'static T> { //~^ ERROR `extern` fn uses type `Option<&T>`, which is not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr b/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.stderr similarity index 71% rename from tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr rename to tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.stderr index 746304694167f..57a557b5d68e5 100644 --- a/tests/ui/lint/lint-ctypes-option-nonnull-unsized.stderr +++ b/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `Option<&T>`, which is not FFI-safe - --> $DIR/lint-ctypes-option-nonnull-unsized.rs:3:45 + --> $DIR/lint-option-nonnull-unsized.rs:3:45 | LL | extern "C" fn foo() -> Option<&'static T> { | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -7,10 +7,10 @@ LL | extern "C" fn foo() -> Option<&'static T> { = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum = note: enum has no representation hint note: the lint level is defined here - --> $DIR/lint-ctypes-option-nonnull-unsized.rs:1:9 + --> $DIR/lint-option-nonnull-unsized.rs:1:9 | -LL | #![deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(improper_c_fn_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/improper_ctypes/lint-pattern-types.rs b/tests/ui/lint/improper_ctypes/lint-pattern-types.rs new file mode 100644 index 0000000000000..8076646a06126 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-pattern-types.rs @@ -0,0 +1,92 @@ +#![feature(pattern_types, rustc_attrs)] +#![feature(pattern_type_macro)] +#![feature(pattern_type_range_trait,const_trait_impl)] +#![deny(improper_c_fn_definitions)] +#![allow(unused)] +use std::pat::pattern_type; +use std::mem::transmute; + +macro_rules! mini_tr { + ($name:ident : $type:ty) => { + let $name: $type = unsafe {transmute($name)}; + }; +} + +const USZM1: usize = usize::MAX -1; +const ISZP1: isize = -isize::MAX; + +extern "C" fn test_me( + // "standard" tests (see if option works as intended) + a: pattern_type!(u32 is 0..), + ao: Option, //~ ERROR: not FFI-safe + b: pattern_type!(u32 is 1..), //~ ERROR: not FFI-safe + bo: Option, + c: pattern_type!(u32 is 2..), //~ ERROR: not FFI-safe + co: Option, //~ ERROR: not FFI-safe + + // fuzz-testing (see if the disallowed-value-count function works) + e1: Option, + e2: Option, + e3: Option, + //~^ ERROR: uses type `i128` + e4: Option, + //~^ ERROR: uses type `u128` + f1: Option, + f2: Option, + f3: Option, + //~^ ERROR: uses type `i128` + f4: Option, + //~^ ERROR: uses type `u128` + g11: Option, + g12: Option, + g13: Option, + g14: Option, + //g2: Option, + // ^ error: only signed integer base types are allowed for or-pattern pattern types at present + g31: Option, + //~^ ERROR: uses type `i128` + g32: Option, + //~^ ERROR: uses type `i128` + g33: Option, + //~^ ERROR: uses type `i128` + g34: Option, + //~^ ERROR: uses type `i128` + //g4: Option, + // ^ ERROR: uses type `u128` + + // because usize patterns have "unevaluated const" implicit bounds and this needs to not ICE + h1: pattern_type!(usize is 1..), //~ ERROR: not FFI-safe + h2: pattern_type!(usize is ..USZM1), //~ ERROR: not FFI-safe + // h3: pattern_type!(usize is ..), // not allowed + h4: pattern_type!(isize is ISZP1..), //~ ERROR: not FFI-safe + + h: pattern_type!(char is '\0'..), + //~^ ERROR: uses type `char` + //~| ERROR: uses type `(char) is '\0'..` +){ + // triple-check that the options with supposed layout optimisations actually have them + mini_tr!(bo: u32); + mini_tr!(co: u32); + + mini_tr!(e1: i32); + mini_tr!(e2: u32); + mini_tr!(e3: i128); + mini_tr!(e4: u128); + mini_tr!(f1: i32); + mini_tr!(f2: u32); + mini_tr!(f3: i128); + mini_tr!(f4: u128); + + mini_tr!(g11: i32); + mini_tr!(g12: i32); + mini_tr!(g13: i32); + mini_tr!(g14: i32); + //mini_tr!(g2: u32); + mini_tr!(g31: i128); + mini_tr!(g32: i128); + mini_tr!(g33: i128); + mini_tr!(g34: i128); + //mini_tr!(g4: u128); +} + +fn main(){} diff --git a/tests/ui/lint/improper_ctypes/lint-pattern-types.stderr b/tests/ui/lint/improper_ctypes/lint-pattern-types.stderr new file mode 100644 index 0000000000000..787bba917438d --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-pattern-types.stderr @@ -0,0 +1,152 @@ +error: `extern` fn uses type `Option<(u32) is 0..>`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:21:9 + | +LL | ao: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum + = note: enum has no representation hint +note: the lint level is defined here + --> $DIR/lint-pattern-types.rs:4:9 + | +LL | #![deny(improper_c_fn_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `(u32) is 1..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:22:8 + | +LL | b: pattern_type!(u32 is 1..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead, or wrapping `(u32) is 1..` in an `Option<_>` + = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code + +error: `extern` fn uses type `(u32) is 2..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:24:8 + | +LL | c: pattern_type!(u32 is 2..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead + = note: integer-pattern types with more than one disallowed value cannot have their value be provided by non-rust code + +error: `extern` fn uses type `(u32) is 2..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:25:9 + | +LL | co: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead + = note: integer-pattern types with more than one disallowed value cannot have their value be provided by non-rust code + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:30:9 + | +LL | e3: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `u128`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:32:9 + | +LL | e4: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:36:9 + | +LL | f3: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `u128`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:38:9 + | +LL | f4: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:46:10 + | +LL | g31: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:48:10 + | +LL | g32: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:50:10 + | +LL | g33: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `i128`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:52:10 + | +LL | g34: Option, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: 128-bit integers don't currently have a known stable ABI + +error: `extern` fn uses type `(usize) is 1..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:58:9 + | +LL | h1: pattern_type!(usize is 1..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead, or wrapping `(usize) is 1..` in an `Option<_>` + = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code + +error: `extern` fn uses type `(usize) is 0..=18446744073709551613`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:59:9 + | +LL | h2: pattern_type!(usize is ..USZM1), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead + = note: integer-pattern types with more than one disallowed value cannot have their value be provided by non-rust code + +error: `extern` fn uses type `(isize) is -9223372036854775807..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:61:9 + | +LL | h4: pattern_type!(isize is ISZP1..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead, or wrapping `(isize) is -9223372036854775807..` in an `Option<_>` + = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code + +error: `extern` fn uses type `char`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:63:8 + | +LL | h: pattern_type!(char is '\0'..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `extern` fn uses type `(char) is '\0'..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:63:8 + | +LL | h: pattern_type!(char is '\0'..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead + = note: integer-pattern types with more than one disallowed value cannot have their value be provided by non-rust code + +error: aborting due to 17 previous errors + diff --git a/tests/ui/lint/improper_ctypes/lint-transparent-help.rs b/tests/ui/lint/improper_ctypes/lint-transparent-help.rs new file mode 100644 index 0000000000000..36e6e76097b6f --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-transparent-help.rs @@ -0,0 +1,21 @@ +#![deny(improper_c_fn_definitions, improper_ctype_definitions)] +use std::marker::PhantomData; +use std::collections::HashMap; +use std::ffi::c_void; + +// [option 1] oops, we forgot repr(C) +struct DictPhantom<'a, A,B:'a>{ + value_info: PhantomData<&'a B>, + full_dict_info: PhantomData>, +} + +#[repr(C)] // [option 2] oops, we meant repr(transparent) +struct MyTypedRawPointer<'a,T:'a>{ //~ ERROR: uses type `DictPhantom<'_, T, T>` + ptr: *const c_void, + metadata: DictPhantom<'a,T,T>, +} + +extern "C" fn example_use(_e: MyTypedRawPointer) {} +//~^ ERROR: uses type `MyTypedRawPointer<'_, i32>` + +fn main() {} diff --git a/tests/ui/lint/improper_ctypes/lint-transparent-help.stderr b/tests/ui/lint/improper_ctypes/lint-transparent-help.stderr new file mode 100644 index 0000000000000..4739d53255534 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-transparent-help.stderr @@ -0,0 +1,57 @@ +error: `repr(C)` type uses type `DictPhantom<'_, T, T>`, which is not FFI-safe + --> $DIR/lint-transparent-help.rs:13:1 + | +LL | / struct MyTypedRawPointer<'a,T:'a>{ +LL | | ptr: *const c_void, +LL | | metadata: DictPhantom<'a,T,T>, +LL | | } + | |_^ not FFI-safe + | + = help: `MyTypedRawPointer<'_, T>` has exactly one non-zero-sized field, consider making it `#[repr(transparent)]` instead + = note: this struct/enum/union (`MyTypedRawPointer<'_, T>`) is FFI-unsafe due to a `DictPhantom<'_, T, T>` field +note: the type is defined here + --> $DIR/lint-transparent-help.rs:13:1 + | +LL | struct MyTypedRawPointer<'a,T:'a>{ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `DictPhantom<'_, T, T>` + = note: `DictPhantom<'_, T, T>` has unspecified layout +note: the type is defined here + --> $DIR/lint-transparent-help.rs:7:1 + | +LL | struct DictPhantom<'a, A,B:'a>{ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint-transparent-help.rs:1:36 + | +LL | #![deny(improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `MyTypedRawPointer<'_, i32>`, which is not FFI-safe + --> $DIR/lint-transparent-help.rs:18:31 + | +LL | extern "C" fn example_use(_e: MyTypedRawPointer) {} + | ^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: `MyTypedRawPointer<'_, i32>` has exactly one non-zero-sized field, consider making it `#[repr(transparent)]` instead + = note: this struct/enum/union (`MyTypedRawPointer<'_, i32>`) is FFI-unsafe due to a `DictPhantom<'_, i32, i32>` field +note: the type is defined here + --> $DIR/lint-transparent-help.rs:13:1 + | +LL | struct MyTypedRawPointer<'a,T:'a>{ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `DictPhantom<'_, i32, i32>` + = note: `DictPhantom<'_, i32, i32>` has unspecified layout +note: the type is defined here + --> $DIR/lint-transparent-help.rs:7:1 + | +LL | struct DictPhantom<'a, A,B:'a>{ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint-transparent-help.rs:1:9 + | +LL | #![deny(improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs new file mode 100644 index 0000000000000..6e4f10d8704c0 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs @@ -0,0 +1,375 @@ +// Trying to cover as many ty_kinds as possible in the code for ImproperCTypes lint +//@ edition:2018 + +#![allow(dead_code,unused_variables)] +#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_c_callbacks)] + +// we want ALL the ty_kinds, including the feature-gated ones +#![feature(extern_types)] +#![feature(never_type)] +#![feature(inherent_associated_types)] //~ WARN: is incomplete +#![feature(async_trait_bounds)] +#![feature(pattern_types, rustc_attrs)] +#![feature(pattern_type_macro)] + +// ty_kinds not found so far: +// Placeholder, Bound, Infer, Error, +// Alias +// FnDef, Closure, Coroutine, ClosureCoroutine, CoroutineWitness, + +use std::ptr::from_ref; +use std::ptr::NonNull; +use std::mem::{MaybeUninit, size_of}; +use std::num::NonZero; +use std::pat::pattern_type; + +#[repr(C)] +struct SomeStruct{ + a: u8, + b: i32, +} +impl SomeStruct{ + extern "C" fn klol( + // Ref[Struct] + &self //~ ERROR: `extern` fn uses type `&SomeStruct` + ){} +} + +#[repr(C)] +#[derive(Clone,Copy)] +struct TemplateStruct where T: std::ops::Add+Copy { + one: T, + two: T, +} +impl TemplateStruct { + type Out = ::Output; +} + +extern "C" fn tstruct_sum( + // Ref[Struct] + slf: Option<&TemplateStruct> + // Option> ...not Inherent. dangit +) -> Option::Out>> { + Some(Box::new(slf?.one + slf?.two)) +} + +#[repr(C)] +union SomeUnion{ + sz: u8, + us: i8, +} +#[repr(C)] +enum SomeEnum{ + Everything=42, + NotAU8=256, + SomePrimeNumber=23, +} + +pub trait TimesTwo: std::ops::Add + Sized + Clone + where for<'a> &'a Self: std::ops::Add<&'a Self>, + *const Self: std::ops::Add<*const Self>, + Box: std::ops::Add>, +{ + extern "C" fn t2_own( + // Param + self + // Alias + ) -> >::Output { + self.clone() + self + } + // it ICEs (https://github.com/rust-lang/rust/issues/134587) :( + //extern "C" fn t2_ptr( + // // Ref[Param] + // slf: *const Self + // // Alias + //) -> <*const Self as std::ops::Add<*const Self>>::Output { + // slf + slf + //} + extern "C" fn t2_box( + // Box[Param] + self: Box, //~ ERROR: `extern` fn uses type `Box` + // Alias + ) -> as std::ops::Add>>::Output { + self.clone() + self + } + extern "C" fn t2_ref( + // Ref[Param] + &self //~ ERROR: `extern` fn uses type `&Self` + // Alias + ) -> <&Self as std::ops::Add<&Self>>::Output { + self + self + } +} + +extern "C" {type ExtType;} + +#[repr(C)] +pub struct StructWithDyn(dyn std::fmt::Debug); +//~^ ERROR: `repr(C)` type uses type `dyn Debug` + +extern "C" { + // variadic args aren't listed as args in a way that allows type checking. + // this is fine (TM) + fn variadic_function(e: ...); +} + +extern "C" fn all_ty_kinds<'a,const N:usize,T>( + // UInt, Int, Float, Bool + u:u8, i:i8, f:f64, b:bool, + // Struct + s:String, //~ ERROR: uses type `String` + // Ref[Str] + s2:&str, //~ ERROR: uses type `&str` + // Char + c: char, //~ ERROR: uses type `char` + // Ref[Slice] + s3:&[u8], //~ ERROR: uses type `&[u8]` + // Array (this gets caught outside of the code we want to test) + s4:[u8;N], //~ ERROR: uses type `[u8; N]` + // Tuple + p:(u8, u8), //~ ERROR: uses type `(u8, u8)` + // also Tuple + (p2, p3):(u8, u8), //~ ERROR: uses type `(u8, u8)` + // Pat + nz: pattern_type!(u32 is 1..), //~ ERROR: uses type `(u32) is 1..` + // Struct + SomeStruct{b:p4,..}: SomeStruct, + // Union + u2: SomeUnion, + // Enum, + e: SomeEnum, + // Param + d: impl Clone, + // Param + t: T, + // Ptr[Foreign] + e2: *mut ExtType, + // Ref[Struct] + e3: &StructWithDyn, //~ ERROR: uses type `&StructWithDyn` + // Never + x:!, //~ ERROR: uses type `!` + //r1: &u8, r2: *const u8, r3: Box, + // FnPtr + f2: fn(u8)->u8, //~ ERROR: uses type `fn(u8) -> u8` + // Ref[Dynamic] + f3: &'a dyn Fn(u8)->u8, //~ ERROR: uses type `&dyn Fn(u8) -> u8` + // Ref[Dynamic] + d2: &dyn std::cmp::PartialOrd, //~ ERROR: uses type `&dyn PartialOrd` + // Param, + a: impl async Fn(u8)->u8, //FIXME: eventually, be able to peer into type params + // Alias +) -> impl std::fmt::Debug { //~ ERROR: uses type `impl Debug` + 3_usize +} + +extern "C" fn all_ty_kinds_in_ptr( + // Ptr[UInt], Ptr[Int], Ptr[Float], Ptr[Bool] + u: *const u8, i: *const i8, f: *const f64, b: *const bool, + // Ptr[Struct] + s: *const String, + // Ptr[Str] + s2: *const str, //~ ERROR: uses type `*const str` + // Ptr[Char] + c: *const char, + // Ptr[Slice] + s3: *const [u8], //~ ERROR: uses type `*const [u8]` + // Ptr[Array] (this gets caught outside of the code we want to test) + s4: *const [u8;N], + // Ptr[Tuple] + p: *const (u8,u8), + // Tuple + (p2, p3):(*const u8, *const u8), //~ ERROR: uses type `(*const u8, *const u8)` + // Pat + nz: *const pattern_type!(u32 is 1..), + // Ptr[Struct] + SomeStruct{b: ref p4,..}: & SomeStruct, //~ ERROR: uses type `&SomeStruct` + // Ptr[Union] + u2: *const SomeUnion, + // Ptr[Enum], + e: *const SomeEnum, + // Param + d: *const impl Clone, + // Param + t: *const T, + // Ptr[Foreign] + e2: *mut ExtType, + // Ptr[Struct] + e3: *const StructWithDyn, //~ ERROR: uses type `*const StructWithDyn` + // Ptr[Never] + x: *const !, + //r1: &u8, r2: *const u8, r3: Box, + // Ptr[FnPtr] + f2: *const fn(u8)->u8, + // Ptr[Dynamic] + f3: *const dyn Fn(u8)->u8, //~ ERROR: uses type `*const dyn Fn(u8) -> u8` + // Ptr[Dynamic] + d2: *const dyn std::cmp::PartialOrd, //~ ERROR: uses type `*const dyn PartialOrd` + // Ptr[Param], + a: *const impl async Fn(u8)->u8, + // Alias +) -> *const dyn std::fmt::Debug { //~ ERROR: uses type `*const dyn Debug` + todo!() +} + +extern "C" { +fn all_ty_kinds_in_ref<'a>( + // Ref[UInt], Ref[Int], Ref[Float], Ref[Bool] + u: &u8, i: &'a i8, f: &f64, b: &bool, + // Ref[Struct] + s: &String, + // Ref[Str] + s2: &str, //~ ERROR: uses type `&str` + // Ref[Char] + c: &char, + // Ref[Slice] + s3: &[u8], //~ ERROR: uses type `&[u8]` + // deactivated here, because this is a function *declaration* (param N unacceptable) + // s4: &[u8;N], + // Ref[Tuple] + p: &(u8, u8), + // deactivated here, because this is a function *declaration* (patterns unacceptable) + // (p2, p3):(&u8, &u8), + // Pat + nz: &pattern_type!(u32 is 1..), + // deactivated here, because this is a function *declaration* (pattern unacceptable) + // SomeStruct{b: ref p4,..}: &SomeStruct, + // Ref[Union] + u2: &SomeUnion, + // Ref[Enum], + e: &SomeEnum, + // deactivated here, because this is a function *declaration* (impl type unacceptable) + // d: &impl Clone, + // deactivated here, because this is a function *declaration* (type param unacceptable) + // t: &T, + // Ref[Foreign] + e2: &ExtType, + // Ref[Struct] + e3: &StructWithDyn, //~ ERROR: uses type `&StructWithDyn` + // Ref[Never] + x: &!, + //r1: &u8, r2: &u8, r3: Box, + // Ref[FnPtr] + f2: &fn(u8)->u8, + // Ref[Dynamic] + f3: &dyn Fn(u8)->u8, //~ ERROR: uses type `&dyn Fn(u8) -> u8` + // Ref[Dynamic] + d2: &dyn std::cmp::PartialOrd, //~ ERROR: uses type `&dyn PartialOrd` + // deactivated here, because this is a function *declaration* (impl type unacceptable) + // a: &impl async Fn(u8)->u8, + // Ref[Dynamic] +) -> &'a dyn std::fmt::Debug; //~ ERROR: uses type `&dyn Debug` +} + +extern "C" fn all_ty_kinds_in_box( + // Box[UInt], Box[Int], Box[Float], Box[Bool] + u: Option>, i: Option>, f: Option>, b: Option>, + // Box[Struct] + s: Option>, + // Box[Str] + s2: Box, //~ ERROR: uses type `Box` + // Box[Char] + c: Box, //~ ERROR: uses type `Box` + // Box[Slice] + s3: Box<[u8]>, //~ ERROR: uses type `Box<[u8]>` + // Box[Array] (this gets caught outside of the code we want to test) + s4: Option>, + // Box[Tuple] + p: Option>, + // also Tuple + (p2,p3):(Box, Box), //~ ERROR: uses type `(Box, Box)` + // Pat + nz: Option>, + // Ref[Struct] + SomeStruct{b: ref p4,..}: &SomeStruct, //~ ERROR: uses type `&SomeStruct` + // Box[Union] + u2: Option>, + // Box[Enum], + e: Option>, + // Box[Param] + d: Option>, + // Box[Param] + t: Option>, + // Box[Foreign] + e2: Option>, + // Box[Struct] + e3: Box, //~ ERROR: uses type `Box` + // Box[Never] + // (considered FFI-unsafe because of null pointers, not the litteral uninhabited type. smh.) + x: Box, //~ ERROR: uses type `Box` + //r1: Box, + // Box[FnPtr] + f2: Boxu8>, //~ ERROR: uses type `Box u8>` + // Box[Dynamic] + f3: Boxu8>, //~ ERROR: uses type `Box u8>` + // Box[Dynamic] + d2: Box>, //~ ERROR: uses type `Box>` + // Option[Box[Param]], + a: Optionu8>>, + // Box[Dynamic] +) -> Box { //~ ERROR: uses type `Box` + u.unwrap() +} + + +// FIXME: all the errors are apparently on the same line... +#[repr(C)] +struct AllTyKinds<'a,const N:usize,T>{ + //~^ ERROR: uses type `String` + //~| ERROR: uses type `&str` + //~| ERROR: uses type `char` + //~| ERROR: uses type `&[u8]` + //~| ERROR: uses type `(u8, u8)` + //~| ERROR: uses type `&StructWithDyn` + //~| ERROR: uses type `fn(u8) -> u8` + //~| ERROR: uses type `&dyn Fn(u8) -> u8` + //~| ERROR: uses type `&dyn PartialOrd` + + // UInt, Int, Float, Bool + u:u8, i:i8, f:f64, b:bool, + // Struct + s:String, + // Ref[Str] + s2:&'a str, + // Char + c: char, + // Ref[Slice] + s3:&'a[u8], + // Array (this gets caught outside of the code we want to test) + s4:[u8;N], + // Tuple + p:(u8, u8), + // deactivated here (patterns unacceptable) + // (p2, p3):(&u8, &u8), + // Pat + nz: pattern_type!(u32 is 1..), + // deactivated here, because this is a function *declaration* (pattern unacceptable) + // SomeStruct{b: ref p4,..}: &SomeStruct, + // Union + u2: SomeUnion, + // Enum, + e: SomeEnum, + // deactivated here (impl type unacceptable) + // d: impl Clone, + // Param + t: T, + // Ptr[Foreign] + e2: *mut ExtType, + // Ref[Struct] + e3: &'a StructWithDyn, + // Never + x:!, + //r1: &u8, r2: *const u8, r3: Box, + // FnPtr + f2: fn(u8)->u8, + // Ref[Dynamic] + f3: &'a dyn Fn(u8)->u8, + // Ref[Dynamic] + d2: &'a dyn std::cmp::PartialOrd, + // deactivated here (impl type unacceptable), + //a: impl async Fn(u8)->u8, //FIXME: eventually, be able to peer into type params + // deactivated here (impl type unacceptable) + //d3: impl std::fmt::Debug, +} + +fn main() {} diff --git a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr new file mode 100644 index 0000000000000..02bb8b9f76671 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -0,0 +1,499 @@ +warning: the feature `inherent_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lint-tykind-fuzz.rs:11:12 + | +LL | #![feature(inherent_associated_types)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #8995 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:35:7 + | +LL | &self + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&SomeStruct` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code +note: the lint level is defined here + --> $DIR/lint-tykind-fuzz.rs:5:26 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:91:14 + | +LL | self: Box, + | ^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `&Self`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:98:8 + | +LL | &self + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Self` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `repr(C)` type uses type `dyn Debug`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:108:1 + | +LL | pub struct StructWithDyn(dyn std::fmt::Debug); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: trait objects have no C equivalent +note: the lint level is defined here + --> $DIR/lint-tykind-fuzz.rs:5:53 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `String`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:121:5 + | +LL | s:String, + | ^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `String` + = note: `String` has unspecified layout + +error: `extern` fn uses type `&str`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:123:6 + | +LL | s2:&str, + | ^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `char`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:125:6 + | +LL | c: char, + | ^^^^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `extern` fn uses type `&[u8]`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:127:6 + | +LL | s3:&[u8], + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `[u8; N]`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:129:6 + | +LL | s4:[u8;N], + | ^^^^^^ not FFI-safe + | + = help: consider passing a pointer to the array + = note: passing raw arrays by value is not FFI-safe + +error: `extern` fn uses type `(u8, u8)`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:131:5 + | +LL | p:(u8, u8), + | ^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `(u8, u8)`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:133:12 + | +LL | (p2, p3):(u8, u8), + | ^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `(u32) is 1..`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:135:7 + | +LL | nz: pattern_type!(u32 is 1..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead, or wrapping `(u32) is 1..` in an `Option<_>` + = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code + +error: `extern` fn uses type `&StructWithDyn`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:149:7 + | +LL | e3: &StructWithDyn, + | ^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `!`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:151:5 + | +LL | x:!, + | ^ not FFI-safe + | + = note: the never type (`!`) and other uninhabited types are not allowed in function arguments and static variables + +error: `extern` fn uses type `fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:154:7 + | +LL | f2: fn(u8)->u8, + | ^^^^^^^^^^ not FFI-safe + | + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` fn uses type `&dyn Fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:156:7 + | +LL | f3: &'a dyn Fn(u8)->u8, + | ^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `&dyn PartialOrd`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:158:7 + | +LL | d2: &dyn std::cmp::PartialOrd, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `impl Debug`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:162:6 + | +LL | ) -> impl std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: opaque types have no C equivalent + +error: `extern` fn uses type `*const str`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:172:7 + | +LL | s2: *const str, + | ^^^^^^^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `*const [u8]`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:176:7 + | +LL | s3: *const [u8], + | ^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `(*const u8, *const u8)`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:182:12 + | +LL | (p2, p3):(*const u8, *const u8), + | ^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:186:29 + | +LL | SomeStruct{b: ref p4,..}: & SomeStruct, + | ^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&SomeStruct` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `*const StructWithDyn`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:198:7 + | +LL | e3: *const StructWithDyn, + | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `*const dyn Fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:205:7 + | +LL | f3: *const dyn Fn(u8)->u8, + | ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `*const dyn PartialOrd`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:207:7 + | +LL | d2: *const dyn std::cmp::PartialOrd, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `*const dyn Debug`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:211:6 + | +LL | ) -> *const dyn std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `&str`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:222:7 + | +LL | s2: &str, + | ^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer +note: the lint level is defined here + --> $DIR/lint-tykind-fuzz.rs:5:9 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^ + +error: `extern` block uses type `&[u8]`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:226:7 + | +LL | s3: &[u8], + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `&StructWithDyn`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:248:7 + | +LL | e3: &StructWithDyn, + | ^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `&dyn Fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:255:7 + | +LL | f3: &dyn Fn(u8)->u8, + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `&dyn PartialOrd`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:257:7 + | +LL | d2: &dyn std::cmp::PartialOrd, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` block uses type `&dyn Debug`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:261:6 + | +LL | ) -> &'a dyn std::fmt::Debug; + | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:270:7 + | +LL | s2: Box, + | ^^^^^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:272:6 + | +LL | c: Box, + | ^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:274:7 + | +LL | s3: Box<[u8]>, + | ^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `(Box, Box)`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:280:11 + | +LL | (p2,p3):(Box, Box), + | ^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:284:29 + | +LL | SomeStruct{b: ref p4,..}: &SomeStruct, + | ^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&SomeStruct` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:296:7 + | +LL | e3: Box, + | ^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:299:6 + | +LL | x: Box, + | ^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `Box u8>`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:302:7 + | +LL | f2: Boxu8>, + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `Box u8>` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + +error: `extern` fn uses type `Box u8>`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:304:7 + | +LL | f3: Boxu8>, + | ^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `Box>`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:306:7 + | +LL | d2: Box>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:310:6 + | +LL | ) -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `repr(C)` type uses type `String`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `String` + = note: `String` has unspecified layout + +error: `repr(C)` type uses type `&str`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `repr(C)` type uses type `char`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `repr(C)` type uses type `&[u8]`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = help: consider using a raw pointer to the slice's first element (and a length) instead + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `repr(C)` type uses type `(u8, u8)`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `repr(C)` type uses type `&StructWithDyn`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `repr(C)` type uses type `fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `repr(C)` type uses type `&dyn Fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: `repr(C)` type uses type `&dyn PartialOrd`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:317:1 + | +LL | / struct AllTyKinds<'a,const N:usize,T>{ +... | +LL | | } + | |_^ not FFI-safe + | + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer + +error: aborting due to 52 previous errors; 1 warning emitted + diff --git a/tests/ui/lint/improper_ctypes/lint_uninhabited.rs b/tests/ui/lint/improper_ctypes/lint_uninhabited.rs new file mode 100644 index 0000000000000..a328dceeb5765 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint_uninhabited.rs @@ -0,0 +1,75 @@ +#![feature(never_type)] + +#![allow(dead_code, unused_variables)] +#![deny(improper_ctypes,improper_ctype_definitions)] +#![deny(improper_c_fn_definitions, improper_c_callbacks)] + +use std::mem::transmute; + +enum Uninhabited{} + +#[repr(C)] +struct AlsoUninhabited{ + a: Uninhabited, + b: i32, +} + +#[repr(C)] +enum Inhabited{ + OhNo(Uninhabited), + OhYes(i32), +} + +struct EmptyRust; + +#[repr(transparent)] +struct HalfHiddenUninhabited { + is_this_a_tuple: (i8,i8), + zst_inh: EmptyRust, + zst_uninh: !, +} + +extern "C" { + +fn bad_entry(e: AlsoUninhabited); //~ ERROR: uses type `AlsoUninhabited` +fn bad_exit()->AlsoUninhabited; //~ ERROR: uses type `AlsoUninhabited` + +fn bad0_entry(e: Uninhabited); //~ ERROR: uses type `Uninhabited` +fn bad0_exit()->Uninhabited; + +fn good_entry(e: Inhabited); +fn good_exit()->Inhabited; + +fn never_entry(e:!); //~ ERROR: uses type `!` +fn never_exit()->!; + +} + +extern "C" fn impl_bad_entry(e: AlsoUninhabited) {} //~ ERROR: uses type `AlsoUninhabited` +extern "C" fn impl_bad_exit()->AlsoUninhabited { //~ ERROR: uses type `AlsoUninhabited` + AlsoUninhabited{ + a: impl_bad0_exit(), + b: 0, + } +} + +extern "C" fn impl_bad0_entry(e: Uninhabited) {} //~ ERROR: uses type `Uninhabited` +extern "C" fn impl_bad0_exit()->Uninhabited { + unsafe{transmute(())} //~ WARN: does not permit zero-initialization +} + +extern "C" fn impl_good_entry(e: Inhabited) {} +extern "C" fn impl_good_exit() -> Inhabited { + Inhabited::OhYes(0) +} + +extern "C" fn impl_never_entry(e:!){} //~ ERROR: uses type `!` +extern "C" fn impl_never_exit()->! { + loop{} +} + +extern "C" fn weird_pattern(e:HalfHiddenUninhabited){} +//~^ ERROR: uses type `HalfHiddenUninhabited` + + +fn main(){} diff --git a/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr b/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr new file mode 100644 index 0000000000000..7863b3996e846 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr @@ -0,0 +1,159 @@ +error: `extern` block uses type `AlsoUninhabited`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:34:17 + | +LL | fn bad_entry(e: AlsoUninhabited); + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`AlsoUninhabited`) is FFI-unsafe due to a `Uninhabited` field +note: the type is defined here + --> $DIR/lint_uninhabited.rs:12:1 + | +LL | struct AlsoUninhabited{ + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: zero-variant enums and other uninhabited types are not allowed in function arguments and static variables +note: the type is defined here + --> $DIR/lint_uninhabited.rs:9:1 + | +LL | enum Uninhabited{} + | ^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint_uninhabited.rs:4:9 + | +LL | #![deny(improper_ctypes,improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^ + +error: `extern` block uses type `AlsoUninhabited`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:35:16 + | +LL | fn bad_exit()->AlsoUninhabited; + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`AlsoUninhabited`) is FFI-unsafe due to a `Uninhabited` field +note: the type is defined here + --> $DIR/lint_uninhabited.rs:12:1 + | +LL | struct AlsoUninhabited{ + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: if you meant to have a function that never returns, consider setting its return type to the never type (`!`) or a zero-variant enum + = note: zero-variant enums and other uninhabited types are only allowed in function returns if used directly +note: the type is defined here + --> $DIR/lint_uninhabited.rs:9:1 + | +LL | enum Uninhabited{} + | ^^^^^^^^^^^^^^^^ + +error: `extern` block uses type `Uninhabited`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:37:18 + | +LL | fn bad0_entry(e: Uninhabited); + | ^^^^^^^^^^^ not FFI-safe + | + = note: zero-variant enums and other uninhabited types are not allowed in function arguments and static variables +note: the type is defined here + --> $DIR/lint_uninhabited.rs:9:1 + | +LL | enum Uninhabited{} + | ^^^^^^^^^^^^^^^^ + +error: `extern` block uses type `!`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:43:18 + | +LL | fn never_entry(e:!); + | ^ not FFI-safe + | + = note: the never type (`!`) and other uninhabited types are not allowed in function arguments and static variables + +error: `extern` fn uses type `AlsoUninhabited`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:48:33 + | +LL | extern "C" fn impl_bad_entry(e: AlsoUninhabited) {} + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`AlsoUninhabited`) is FFI-unsafe due to a `Uninhabited` field +note: the type is defined here + --> $DIR/lint_uninhabited.rs:12:1 + | +LL | struct AlsoUninhabited{ + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: zero-variant enums and other uninhabited types are not allowed in function arguments and static variables +note: the type is defined here + --> $DIR/lint_uninhabited.rs:9:1 + | +LL | enum Uninhabited{} + | ^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/lint_uninhabited.rs:5:9 + | +LL | #![deny(improper_c_fn_definitions, improper_c_callbacks)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `AlsoUninhabited`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:49:32 + | +LL | extern "C" fn impl_bad_exit()->AlsoUninhabited { + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`AlsoUninhabited`) is FFI-unsafe due to a `Uninhabited` field +note: the type is defined here + --> $DIR/lint_uninhabited.rs:12:1 + | +LL | struct AlsoUninhabited{ + | ^^^^^^^^^^^^^^^^^^^^^^ + = help: if you meant to have a function that never returns, consider setting its return type to the never type (`!`) or a zero-variant enum + = note: zero-variant enums and other uninhabited types are only allowed in function returns if used directly +note: the type is defined here + --> $DIR/lint_uninhabited.rs:9:1 + | +LL | enum Uninhabited{} + | ^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `Uninhabited`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:56:34 + | +LL | extern "C" fn impl_bad0_entry(e: Uninhabited) {} + | ^^^^^^^^^^^ not FFI-safe + | + = note: zero-variant enums and other uninhabited types are not allowed in function arguments and static variables +note: the type is defined here + --> $DIR/lint_uninhabited.rs:9:1 + | +LL | enum Uninhabited{} + | ^^^^^^^^^^^^^^^^ + +warning: the type `Uninhabited` does not permit zero-initialization + --> $DIR/lint_uninhabited.rs:58:12 + | +LL | unsafe{transmute(())} + | ^^^^^^^^^^^^^ this code causes undefined behavior when executed + | +note: enums with no inhabited variants have no valid value + --> $DIR/lint_uninhabited.rs:9:1 + | +LL | enum Uninhabited{} + | ^^^^^^^^^^^^^^^^ + = note: `#[warn(invalid_value)]` on by default + +error: `extern` fn uses type `!`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:66:34 + | +LL | extern "C" fn impl_never_entry(e:!){} + | ^ not FFI-safe + | + = note: the never type (`!`) and other uninhabited types are not allowed in function arguments and static variables + +error: `extern` fn uses type `HalfHiddenUninhabited`, which is not FFI-safe + --> $DIR/lint_uninhabited.rs:71:31 + | +LL | extern "C" fn weird_pattern(e:HalfHiddenUninhabited){} + | ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this struct/enum/union (`HalfHiddenUninhabited`) is FFI-unsafe due to a `!` field +note: the type is defined here + --> $DIR/lint_uninhabited.rs:26:1 + | +LL | struct HalfHiddenUninhabited { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: the never type (`!`) and other uninhabited types are not allowed in function arguments and static variables + +error: aborting due to 9 previous errors; 1 warning emitted + diff --git a/tests/ui/lint/lint-ctypes-113436.rs b/tests/ui/lint/improper_ctypes/mustpass-113436.rs similarity index 90% rename from tests/ui/lint/lint-ctypes-113436.rs rename to tests/ui/lint/improper_ctypes/mustpass-113436.rs index d5acdc45f92e5..b36013f4482cd 100644 --- a/tests/ui/lint/lint-ctypes-113436.rs +++ b/tests/ui/lint/improper_ctypes/mustpass-113436.rs @@ -1,5 +1,5 @@ //@ check-pass -#![deny(improper_ctypes_definitions)] +#![deny(improper_c_fn_definitions,improper_ctype_definitions)] #[repr(C)] pub struct Wrap(T); diff --git a/tests/ui/lint/lint-ctypes-113900.rs b/tests/ui/lint/improper_ctypes/mustpass-113900.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-113900.rs rename to tests/ui/lint/improper_ctypes/mustpass-113900.rs diff --git a/tests/ui/lint/lint-ctypes-66202.rs b/tests/ui/lint/improper_ctypes/mustpass-66202.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-66202.rs rename to tests/ui/lint/improper_ctypes/mustpass-66202.rs diff --git a/tests/ui/lint/lint-ctypes-73249-1.rs b/tests/ui/lint/improper_ctypes/mustpass-73249-1.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73249-1.rs rename to tests/ui/lint/improper_ctypes/mustpass-73249-1.rs diff --git a/tests/ui/lint/lint-ctypes-73249-4.rs b/tests/ui/lint/improper_ctypes/mustpass-73249-4.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73249-4.rs rename to tests/ui/lint/improper_ctypes/mustpass-73249-4.rs diff --git a/tests/ui/lint/lint-ctypes-73249.rs b/tests/ui/lint/improper_ctypes/mustpass-73249.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73249.rs rename to tests/ui/lint/improper_ctypes/mustpass-73249.rs diff --git a/tests/ui/lint/lint-ctypes-73251.rs b/tests/ui/lint/improper_ctypes/mustpass-73251.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73251.rs rename to tests/ui/lint/improper_ctypes/mustpass-73251.rs diff --git a/tests/ui/lint/lint-ctypes-73747.rs b/tests/ui/lint/improper_ctypes/mustpass-73747.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73747.rs rename to tests/ui/lint/improper_ctypes/mustpass-73747.rs diff --git a/tests/ui/lint/lint-ctypes-non-recursion-limit.rs b/tests/ui/lint/improper_ctypes/mustpass-non-recursion-limit.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-non-recursion-limit.rs rename to tests/ui/lint/improper_ctypes/mustpass-non-recursion-limit.rs diff --git a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.rs b/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.rs index 379c4132404bf..47fd95c15b253 100644 --- a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.rs +++ b/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.rs @@ -1,4 +1,4 @@ -#![deny(improper_ctypes)] +#![deny(improper_ctypes, improper_ctype_definitions)] #![allow(dead_code)] // issue https://github.com/rust-lang/rust/issues/14309 @@ -29,15 +29,16 @@ struct D { x: C, y: A } +//~^^^^ ERROR type `A` extern "C" { fn foo(x: A); //~ ERROR type `A`, which is not FFI-safe - fn bar(x: B); //~ ERROR type `A` + fn bar(x: B); //~ ERROR type `B` fn baz(x: C); fn qux(x: A2); //~ ERROR type `A` - fn quux(x: B2); //~ ERROR type `A` + fn quux(x: B2); //~ ERROR type `B` fn corge(x: C2); - fn fred(x: D); //~ ERROR type `A` + fn fred(x: D); //~ ERROR type `D` } fn main() { } diff --git a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr b/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr index 5f0465bcf00c7..994b27c112f1a 100644 --- a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr +++ b/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr @@ -1,11 +1,33 @@ +error: `repr(C)` type uses type `A`, which is not FFI-safe + --> $DIR/repr-rust-is-undefined.rs:28:1 + | +LL | / struct D { +LL | | x: C, +LL | | y: A +LL | | } + | |_^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `A` + = note: `A` has unspecified layout +note: the type is defined here + --> $DIR/repr-rust-is-undefined.rs:8:1 + | +LL | struct A { + | ^^^^^^^^ +note: the lint level is defined here + --> $DIR/repr-rust-is-undefined.rs:1:26 + | +LL | #![deny(improper_ctypes, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: `extern` block uses type `A`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:34:15 + --> $DIR/repr-rust-is-undefined.rs:35:15 | LL | fn foo(x: A); | ^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `A` + = note: `A` has unspecified layout note: the type is defined here --> $DIR/repr-rust-is-undefined.rs:8:1 | @@ -14,64 +36,70 @@ LL | struct A { note: the lint level is defined here --> $DIR/repr-rust-is-undefined.rs:1:9 | -LL | #![deny(improper_ctypes)] +LL | #![deny(improper_ctypes, improper_ctype_definitions)] | ^^^^^^^^^^^^^^^ -error: `extern` block uses type `A`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:35:15 +error: `extern` block uses type `B`, which is not FFI-safe + --> $DIR/repr-rust-is-undefined.rs:36:15 | LL | fn bar(x: B); | ^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `B` + = note: `B` has unspecified layout note: the type is defined here - --> $DIR/repr-rust-is-undefined.rs:8:1 + --> $DIR/repr-rust-is-undefined.rs:13:1 | -LL | struct A { +LL | struct B { | ^^^^^^^^ error: `extern` block uses type `A`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:37:15 + --> $DIR/repr-rust-is-undefined.rs:38:15 | LL | fn qux(x: A2); | ^^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `A` + = note: `A` has unspecified layout note: the type is defined here --> $DIR/repr-rust-is-undefined.rs:8:1 | LL | struct A { | ^^^^^^^^ -error: `extern` block uses type `A`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:38:16 +error: `extern` block uses type `B`, which is not FFI-safe + --> $DIR/repr-rust-is-undefined.rs:39:16 | LL | fn quux(x: B2); | ^^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `B` + = note: `B` has unspecified layout note: the type is defined here - --> $DIR/repr-rust-is-undefined.rs:8:1 + --> $DIR/repr-rust-is-undefined.rs:13:1 | -LL | struct A { +LL | struct B { | ^^^^^^^^ -error: `extern` block uses type `A`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:40:16 +error: `extern` block uses type `D`, which is not FFI-safe + --> $DIR/repr-rust-is-undefined.rs:41:16 | LL | fn fred(x: D); | ^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = note: this struct/enum/union (`D`) is FFI-unsafe due to a `A` field +note: the type is defined here + --> $DIR/repr-rust-is-undefined.rs:28:1 + | +LL | struct D { + | ^^^^^^^^ + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `A` + = note: `A` has unspecified layout note: the type is defined here --> $DIR/repr-rust-is-undefined.rs:8:1 | LL | struct A { | ^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/lint/improper_ctypes_definitions_ice_134060.rs b/tests/ui/lint/improper_ctypes_definitions_ice_134060.rs index b30be99673687..17ec17a0373f8 100644 --- a/tests/ui/lint/improper_ctypes_definitions_ice_134060.rs +++ b/tests/ui/lint/improper_ctypes_definitions_ice_134060.rs @@ -10,6 +10,7 @@ pub trait Foo { extern "C" fn foo_(&self, _: ()) -> i64 { //~^ WARN `extern` fn uses type `()`, which is not FFI-safe + //~^^ WARN `extern` fn uses type `&Self` 0 } } diff --git a/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr b/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr index f6ac9a92cd5f0..d3180621ff5f9 100644 --- a/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr +++ b/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr @@ -1,3 +1,14 @@ +warning: `extern` fn uses type `&Self`, which is not FFI-safe + --> $DIR/improper_ctypes_definitions_ice_134060.rs:11:24 + | +LL | extern "C" fn foo_(&self, _: ()) -> i64 { + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Self` in an `Option<_>` + = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, + which cannot be garanteed if their values are produced by non-rust code + = note: `#[warn(improper_c_fn_definitions)]` on by default + warning: `extern` fn uses type `()`, which is not FFI-safe --> $DIR/improper_ctypes_definitions_ice_134060.rs:11:34 | @@ -6,7 +17,6 @@ LL | extern "C" fn foo_(&self, _: ()) -> i64 { | = help: consider using a struct instead = note: tuples have unspecified layout - = note: `#[warn(improper_ctypes_definitions)]` on by default -warning: 1 warning emitted +warning: 2 warnings emitted diff --git a/tests/ui/lint/lint-ctypes-113436-1.rs b/tests/ui/lint/lint-ctypes-113436-1.rs deleted file mode 100644 index 1ca59c6868d6d..0000000000000 --- a/tests/ui/lint/lint-ctypes-113436-1.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![deny(improper_ctypes_definitions)] - -#[repr(C)] -pub struct Foo { - a: u8, - b: (), -} - -extern "C" fn foo(x: Foo) -> Foo { - todo!() -} - -struct NotSafe(u32); - -#[repr(C)] -pub struct Bar { - a: u8, - b: (), - c: NotSafe, -} - -extern "C" fn bar(x: Bar) -> Bar { - //~^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe - //~^^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe - todo!() -} - -fn main() {} diff --git a/tests/ui/lint/lint-ctypes-113436-1.stderr b/tests/ui/lint/lint-ctypes-113436-1.stderr deleted file mode 100644 index 7b63043f05756..0000000000000 --- a/tests/ui/lint/lint-ctypes-113436-1.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: `extern` fn uses type `NotSafe`, which is not FFI-safe - --> $DIR/lint-ctypes-113436-1.rs:22:22 - | -LL | extern "C" fn bar(x: Bar) -> Bar { - | ^^^ not FFI-safe - | - = 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-113436-1.rs:13:1 - | -LL | struct NotSafe(u32); - | ^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/lint-ctypes-113436-1.rs:1:9 - | -LL | #![deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `NotSafe`, which is not FFI-safe - --> $DIR/lint-ctypes-113436-1.rs:22:30 - | -LL | extern "C" fn bar(x: Bar) -> Bar { - | ^^^ not FFI-safe - | - = 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-113436-1.rs:13:1 - | -LL | struct NotSafe(u32); - | ^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/lint/lint-ctypes-73249-2.stderr b/tests/ui/lint/lint-ctypes-73249-2.stderr deleted file mode 100644 index 2d0dfe94f097e..0000000000000 --- a/tests/ui/lint/lint-ctypes-73249-2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-2.rs:27:21 - | -LL | fn lint_me() -> A<()>; - | ^^^^^ not FFI-safe - | - = note: opaque types have no C equivalent -note: the lint level is defined here - --> $DIR/lint-ctypes-73249-2.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/lint-ctypes-73249-3.stderr b/tests/ui/lint/lint-ctypes-73249-3.stderr deleted file mode 100644 index e1a313a290651..0000000000000 --- a/tests/ui/lint/lint-ctypes-73249-3.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-3.rs:21:25 - | -LL | pub fn lint_me() -> A; - | ^ not FFI-safe - | - = note: opaque types have no C equivalent -note: the lint level is defined here - --> $DIR/lint-ctypes-73249-3.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/lint-ctypes-73249-5.stderr b/tests/ui/lint/lint-ctypes-73249-5.stderr deleted file mode 100644 index c4fa955de05ed..0000000000000 --- a/tests/ui/lint/lint-ctypes-73249-5.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-5.rs:21:25 - | -LL | pub fn lint_me() -> A; - | ^ not FFI-safe - | - = note: opaque types have no C equivalent -note: the lint level is defined here - --> $DIR/lint-ctypes-73249-5.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/lint-ctypes-94223.rs b/tests/ui/lint/lint-ctypes-94223.rs deleted file mode 100644 index ac24f61b0ac7a..0000000000000 --- a/tests/ui/lint/lint-ctypes-94223.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![crate_type = "lib"] -#![deny(improper_ctypes_definitions)] - -pub fn bad(f: extern "C" fn([u8])) {} -//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe - -pub fn bad_twice(f: Result) {} -//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe -//~^^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe - -struct BadStruct(extern "C" fn([u8])); -//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe - -enum BadEnum { - A(extern "C" fn([u8])), - //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe -} - -enum BadUnion { - A(extern "C" fn([u8])), - //~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe -} - -type Foo = extern "C" fn([u8]); -//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe - -pub trait FooTrait { - type FooType; -} - -pub type Foo2 = extern "C" fn(Option<&::FooType>); -//~^ ERROR `extern` fn uses type `Option<&::FooType>`, which is not FFI-safe - -pub struct FfiUnsafe; - -#[allow(improper_ctypes_definitions)] -extern "C" fn f(_: FfiUnsafe) { - unimplemented!() -} - -pub static BAD: extern "C" fn(FfiUnsafe) = f; -//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - -pub static BAD_TWICE: Result = Ok(f); -//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe -//~^^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - -pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; -//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/lint-ctypes-94223.stderr deleted file mode 100644 index bd127cf60044c..0000000000000 --- a/tests/ui/lint/lint-ctypes-94223.stderr +++ /dev/null @@ -1,135 +0,0 @@ -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:4:15 - | -LL | pub fn bad(f: extern "C" fn([u8])) {} - | ^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent -note: the lint level is defined here - --> $DIR/lint-ctypes-94223.rs:2:9 - | -LL | #![deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:7:28 - | -LL | pub fn bad_twice(f: Result) {} - | ^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent - -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:7:49 - | -LL | pub fn bad_twice(f: Result) {} - | ^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent - -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:11:18 - | -LL | struct BadStruct(extern "C" fn([u8])); - | ^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent - -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:15:7 - | -LL | A(extern "C" fn([u8])), - | ^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent - -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:20:7 - | -LL | A(extern "C" fn([u8])), - | ^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent - -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:24:12 - | -LL | type Foo = extern "C" fn([u8]); - | ^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent - -error: `extern` fn uses type `Option<&::FooType>`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:31:20 - | -LL | pub type Foo2 = extern "C" fn(Option<&::FooType>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:41:17 - | -LL | pub static BAD: extern "C" fn(FfiUnsafe) = f; - | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = 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-94223.rs:34:1 - | -LL | pub struct FfiUnsafe; - | ^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:44:30 - | -LL | pub static BAD_TWICE: Result = Ok(f); - | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = 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-94223.rs:34:1 - | -LL | pub struct FfiUnsafe; - | ^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:44:56 - | -LL | pub static BAD_TWICE: Result = Ok(f); - | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = 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-94223.rs:34:1 - | -LL | pub struct FfiUnsafe; - | ^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:48:22 - | -LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; - | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = 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-94223.rs:34:1 - | -LL | pub struct FfiUnsafe; - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 12 previous errors - diff --git a/tests/ui/lint/lint-ctypes-cstr.rs b/tests/ui/lint/lint-ctypes-cstr.rs deleted file mode 100644 index b04decd0bcacc..0000000000000 --- a/tests/ui/lint/lint-ctypes-cstr.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![crate_type = "lib"] -#![deny(improper_ctypes, improper_ctypes_definitions)] - -use std::ffi::{CStr, CString}; - -extern "C" { - fn take_cstr(s: CStr); - //~^ ERROR `extern` block uses type `CStr`, which is not FFI-safe - //~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - fn take_cstr_ref(s: &CStr); - //~^ ERROR `extern` block uses type `CStr`, which is not FFI-safe - //~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - fn take_cstring(s: CString); - //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe - //~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - fn take_cstring_ref(s: &CString); - //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe - //~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - - fn no_special_help_for_mut_cstring(s: *mut CString); - //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe - //~| HELP consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - - fn no_special_help_for_mut_cstring_ref(s: &mut CString); - //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe - //~| HELP consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct -} - -extern "C" fn rust_take_cstr_ref(s: &CStr) {} -//~^ ERROR `extern` fn uses type `CStr`, which is not FFI-safe -//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` -extern "C" fn rust_take_cstring(s: CString) {} -//~^ ERROR `extern` fn uses type `CString`, which is not FFI-safe -//~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` -extern "C" fn rust_no_special_help_for_mut_cstring(s: *mut CString) {} -extern "C" fn rust_no_special_help_for_mut_cstring_ref(s: &mut CString) {} diff --git a/tests/ui/lint/lint-ctypes-cstr.stderr b/tests/ui/lint/lint-ctypes-cstr.stderr deleted file mode 100644 index 8957758d57732..0000000000000 --- a/tests/ui/lint/lint-ctypes-cstr.stderr +++ /dev/null @@ -1,84 +0,0 @@ -error: `extern` block uses type `CStr`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:7:21 - | -LL | fn take_cstr(s: CStr); - | ^^^^ not FFI-safe - | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - = note: `CStr`/`CString` do not have a guaranteed layout -note: the lint level is defined here - --> $DIR/lint-ctypes-cstr.rs:2:9 - | -LL | #![deny(improper_ctypes, improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^ - -error: `extern` block uses type `CStr`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:10:25 - | -LL | fn take_cstr_ref(s: &CStr); - | ^^^^^ not FFI-safe - | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - = note: `CStr`/`CString` do not have a guaranteed layout - -error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:13:24 - | -LL | fn take_cstring(s: CString); - | ^^^^^^^ not FFI-safe - | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - = note: `CStr`/`CString` do not have a guaranteed layout - -error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:16:28 - | -LL | fn take_cstring_ref(s: &CString); - | ^^^^^^^^ not FFI-safe - | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - = note: `CStr`/`CString` do not have a guaranteed layout - -error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:20:43 - | -LL | fn no_special_help_for_mut_cstring(s: *mut CString); - | ^^^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - -error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:24:47 - | -LL | fn no_special_help_for_mut_cstring_ref(s: &mut CString); - | ^^^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - -error: `extern` fn uses type `CStr`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:29:37 - | -LL | extern "C" fn rust_take_cstr_ref(s: &CStr) {} - | ^^^^^ not FFI-safe - | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - = note: `CStr`/`CString` do not have a guaranteed layout -note: the lint level is defined here - --> $DIR/lint-ctypes-cstr.rs:2:26 - | -LL | #![deny(improper_ctypes, improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:32:36 - | -LL | extern "C" fn rust_take_cstring(s: CString) {} - | ^^^^^^^ not FFI-safe - | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` - = note: `CStr`/`CString` do not have a guaranteed layout - -error: aborting due to 8 previous errors - diff --git a/tests/ui/lint/lint-ctypes-fn.stderr b/tests/ui/lint/lint-ctypes-fn.stderr deleted file mode 100644 index a62533a4be17b..0000000000000 --- a/tests/ui/lint/lint-ctypes-fn.stderr +++ /dev/null @@ -1,188 +0,0 @@ -error: `extern` fn uses type `[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:70:33 - | -LL | pub extern "C" fn slice_type(p: &[u32]) { } - | ^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent -note: the lint level is defined here - --> $DIR/lint-ctypes-fn.rs:2:9 - | -LL | #![deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:73:31 - | -LL | pub extern "C" fn str_type(p: &str) { } - | ^^^^ not FFI-safe - | - = help: consider using `*const u8` and a length instead - = note: string slices have no C equivalent - -error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:80:34 - | -LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } - | ^^^^^^^^^ not FFI-safe - | - = note: box cannot be represented as a single pointer - -error: `extern` fn uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:83:35 - | -LL | pub extern "C" fn boxed_string(p: Box) { } - | ^^^^^^^^ not FFI-safe - | - = note: box cannot be represented as a single pointer - -error: `extern` fn uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:86:34 - | -LL | pub extern "C" fn boxed_trait(p: Box) { } - | ^^^^^^^^^^^^^^ not FFI-safe - | - = note: box cannot be represented as a single pointer - -error: `extern` fn uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:89:32 - | -LL | pub extern "C" fn char_type(p: char) { } - | ^^^^ not FFI-safe - | - = help: consider using `u32` or `libc::wchar_t` instead - = note: the `char` type has no C equivalent - -error: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:92:32 - | -LL | pub extern "C" fn i128_type(p: i128) { } - | ^^^^ not FFI-safe - | - = note: 128-bit integers don't currently have a known stable ABI - -error: `extern` fn uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:95:32 - | -LL | pub extern "C" fn u128_type(p: u128) { } - | ^^^^ not FFI-safe - | - = note: 128-bit integers don't currently have a known stable ABI - -error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:98:33 - | -LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } - | ^^^^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - -error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:101:34 - | -LL | pub extern "C" fn tuple_type2(p: I32Pair) { } - | ^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - -error: `extern` fn uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:104:32 - | -LL | pub extern "C" fn zero_size(p: ZeroSize) { } - | ^^^^^^^^ not FFI-safe - | - = help: consider adding a member to this struct - = note: this struct has no fields -note: the type is defined here - --> $DIR/lint-ctypes-fn.rs:25:1 - | -LL | pub struct ZeroSize; - | ^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:107:40 - | -LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } - | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = note: composed only of `PhantomData` -note: the type is defined here - --> $DIR/lint-ctypes-fn.rs:60:1 - | -LL | pub struct ZeroSizeWithPhantomData(PhantomData); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:110:51 - | -LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { - | ^^^^^^^^^^^^^^^^^ not FFI-safe - | - = note: composed only of `PhantomData` - -error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:115:30 - | -LL | pub extern "C" fn fn_type(p: RustFn) { } - | ^^^^^^ not FFI-safe - | - = help: consider using an `extern fn(...) -> ...` function pointer instead - = note: this function pointer has Rust-specific calling convention - -error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:118:31 - | -LL | pub extern "C" fn fn_type2(p: fn()) { } - | ^^^^ not FFI-safe - | - = help: consider using an `extern fn(...) -> ...` function pointer instead - = note: this function pointer has Rust-specific calling convention - -error: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:123:39 - | -LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } - | ^^^^^^^^^^^^^^^ not FFI-safe - | - = note: 128-bit integers don't currently have a known stable ABI - -error: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:126:38 - | -LL | pub extern "C" fn transparent_str(p: TransparentStr) { } - | ^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using `*const u8` and a length instead - = note: string slices have no C equivalent - -error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:172:43 - | -LL | pub extern "C" fn unused_generic2() -> PhantomData { - | ^^^^^^^^^^^^^^^^^ not FFI-safe - | - = note: composed only of `PhantomData` - -error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:185:39 - | -LL | pub extern "C" fn used_generic4(x: Vec) { } - | ^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - -error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:188:41 - | -LL | pub extern "C" fn used_generic5() -> Vec { - | ^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - -error: aborting due to 20 previous errors - diff --git a/tests/ui/lint/lint-ctypes.stderr b/tests/ui/lint/lint-ctypes.stderr deleted file mode 100644 index 8137ae868d356..0000000000000 --- a/tests/ui/lint/lint-ctypes.stderr +++ /dev/null @@ -1,262 +0,0 @@ -error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:47:28 - | -LL | pub fn ptr_type1(size: *const Foo); - | ^^^^^^^^^^ not FFI-safe - | - = 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 - | -LL | pub struct Foo; - | ^^^^^^^^^^^^^^ -note: the lint level is defined here - --> $DIR/lint-ctypes.rs:4:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - -error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:48:28 - | -LL | pub fn ptr_type2(size: *const Foo); - | ^^^^^^^^^^ not FFI-safe - | - = 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 - | -LL | pub struct Foo; - | ^^^^^^^^^^^^^^ - -error: `extern` block uses type `((),)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:50:25 - | -LL | pub fn ptr_tuple(p: *const ((),)); - | ^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - -error: `extern` block uses type `[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:51:26 - | -LL | pub fn slice_type(p: &[u32]); - | ^^^^^^ not FFI-safe - | - = help: consider using a raw pointer instead - = note: slices have no C equivalent - -error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:52:24 - | -LL | pub fn str_type(p: &str); - | ^^^^ not FFI-safe - | - = help: consider using `*const u8` and a length instead - = note: string slices have no C equivalent - -error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:53:24 - | -LL | pub fn box_type(p: Box); - | ^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - -error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:54:28 - | -LL | pub fn opt_box_type(p: Option>); - | ^^^^^^^^^^^^^^^^ 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 `char`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:56:25 - | -LL | pub fn char_type(p: char); - | ^^^^ not FFI-safe - | - = help: consider using `u32` or `libc::wchar_t` instead - = 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 - | -LL | pub fn i128_type(p: i128); - | ^^^^ not FFI-safe - | - = 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 - | -LL | pub fn u128_type(p: u128); - | ^^^^ not FFI-safe - | - = 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 - | -LL | pub fn trait_type(p: &dyn Bar); - | ^^^^^^^^ not FFI-safe - | - = note: trait objects have no C equivalent - -error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:60:26 - | -LL | pub fn tuple_type(p: (i32, i32)); - | ^^^^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - -error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:61:27 - | -LL | pub fn tuple_type2(p: I32Pair); - | ^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - -error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:62:25 - | -LL | pub fn zero_size(p: ZeroSize); - | ^^^^^^^^ not FFI-safe - | - = 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 - | -LL | pub struct ZeroSize; - | ^^^^^^^^^^^^^^^^^^^ - -error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:63: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 - | -LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:66:12 - | -LL | -> ::std::marker::PhantomData; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = note: composed only of `PhantomData` - -error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:67:23 - | -LL | pub fn fn_type(p: RustFn); - | ^^^^^^ not FFI-safe - | - = help: consider using an `extern fn(...) -> ...` function pointer instead - = 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 - | -LL | pub fn fn_type2(p: fn()); - | ^^^^ not FFI-safe - | - = help: consider using an `extern fn(...) -> ...` function pointer instead - = note: this function pointer has Rust-specific calling convention - -error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:69:28 - | -LL | pub fn fn_contained(p: RustBadRet); - | ^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - -error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:70:32 - | -LL | pub fn transparent_i128(p: TransparentI128); - | ^^^^^^^^^^^^^^^ not FFI-safe - | - = 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 - | -LL | pub fn transparent_str(p: TransparentStr); - | ^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using `*const u8` and a length instead - = note: string slices have no C equivalent - -error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:72:30 - | -LL | pub fn transparent_fn(p: TransparentBadFn); - | ^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout - -error: `extern` block uses type `[u8; 8]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:73:27 - | -LL | pub fn raw_array(arr: [u8; 8]); - | ^^^^^^^ not FFI-safe - | - = help: consider passing a pointer to the array - = note: passing raw arrays by value is not FFI-safe - -error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:75:26 - | -LL | pub fn no_niche_a(a: Option>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:77:26 - | -LL | pub fn no_niche_b(b: Option>); - | ^^^^^^^^^^^^^^^^^^^^^^^^ 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.rs:80:34 - | -LL | pub static static_u128_type: u128; - | ^^^^ not FFI-safe - | - = 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 - | -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 - diff --git a/tests/ui/lint/unused/unused-attr-duplicate.rs b/tests/ui/lint/unused/unused-attr-duplicate.rs index 407af40654e8b..86082adf9069e 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.rs +++ b/tests/ui/lint/unused/unused-attr-duplicate.rs @@ -64,7 +64,7 @@ fn t1() {} #[repr(C)] #[non_exhaustive] #[non_exhaustive] //~ ERROR unused attribute -pub struct X; +pub struct X(i32); #[automatically_derived] #[automatically_derived] //~ ERROR unused attribute diff --git a/tests/ui/lint/wasm_c_abi_transition.rs b/tests/ui/lint/wasm_c_abi_transition.rs index 411772ae890b7..74bd246d82f29 100644 --- a/tests/ui/lint/wasm_c_abi_transition.rs +++ b/tests/ui/lint/wasm_c_abi_transition.rs @@ -41,9 +41,10 @@ pub fn call_other_fun(x: MyType) { } // Zero-sized types are safe in both ABIs +#[allow(improper_ctype_definitions)] #[repr(C)] pub struct MyZstType; -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C" fn zst_safe(_x: (), _y: MyZstType) {} // The old and new wasm ABI treats simd types like `v128` the same way, so no diff --git a/tests/ui/lint/wasm_c_abi_transition.stderr b/tests/ui/lint/wasm_c_abi_transition.stderr index b4526bf8d6873..699eed10e3957 100644 --- a/tests/ui/lint/wasm_c_abi_transition.stderr +++ b/tests/ui/lint/wasm_c_abi_transition.stderr @@ -1,28 +1,28 @@ warning: `extern` fn uses type `v128`, which is not FFI-safe - --> $DIR/wasm_c_abi_transition.rs:55:35 + --> $DIR/wasm_c_abi_transition.rs:56:35 | LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } | ^^^^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `v128` + = note: `v128` has unspecified layout note: the type is defined here - --> $DIR/wasm_c_abi_transition.rs:53:1 + --> $DIR/wasm_c_abi_transition.rs:54:1 | LL | pub struct v128([i32; 4]); | ^^^^^^^^^^^^^^^ - = note: `#[warn(improper_ctypes_definitions)]` on by default + = note: `#[warn(improper_c_fn_definitions)]` on by default warning: `extern` fn uses type `v128`, which is not FFI-safe - --> $DIR/wasm_c_abi_transition.rs:55:44 + --> $DIR/wasm_c_abi_transition.rs:56:44 | LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } | ^^^^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `v128` + = note: `v128` has unspecified layout note: the type is defined here - --> $DIR/wasm_c_abi_transition.rs:53:1 + --> $DIR/wasm_c_abi_transition.rs:54:1 | LL | pub struct v128([i32; 4]); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/mir/mir_cast_fn_ret.rs b/tests/ui/mir/mir_cast_fn_ret.rs index eebc6c03f4481..24fef4e38e172 100644 --- a/tests/ui/mir/mir_cast_fn_ret.rs +++ b/tests/ui/mir/mir_cast_fn_ret.rs @@ -1,10 +1,10 @@ //@ run-pass -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C" fn tuple2() -> (u16, u8) { (1, 2) } -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C" fn tuple3() -> (u8, u8, u8) { (1, 2, 3) } diff --git a/tests/ui/mir/mir_codegen_calls.rs b/tests/ui/mir/mir_codegen_calls.rs index b0749f565da08..451e1351971b3 100644 --- a/tests/ui/mir/mir_codegen_calls.rs +++ b/tests/ui/mir/mir_codegen_calls.rs @@ -74,7 +74,7 @@ fn test8() -> isize { Two::two() } -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] extern "C" fn simple_extern(x: u32, y: (u32, u32)) -> u32 { x + y.0 * y.1 } diff --git a/tests/ui/offset-of/offset-of-slice-normalized.rs b/tests/ui/offset-of/offset-of-slice-normalized.rs index 9d1fd9dd2ee14..4f95fa1fb119f 100644 --- a/tests/ui/offset-of/offset-of-slice-normalized.rs +++ b/tests/ui/offset-of/offset-of-slice-normalized.rs @@ -4,6 +4,7 @@ //@ run-pass #![feature(offset_of_slice)] +#![allow(improper_ctype_definitions)] use std::mem::offset_of; @@ -17,7 +18,7 @@ impl Mirror for T { #[repr(C)] struct S { a: u8, - b: (u8, u8), + b: [u8;2], c: <[i32] as Mirror>::Assoc, } diff --git a/tests/ui/offset-of/offset-of-slice.rs b/tests/ui/offset-of/offset-of-slice.rs index e6eb12abd7bbc..a7a0956206384 100644 --- a/tests/ui/offset-of/offset-of-slice.rs +++ b/tests/ui/offset-of/offset-of-slice.rs @@ -1,12 +1,13 @@ //@run-pass #![feature(offset_of_slice)] +#![allow(improper_ctype_definitions)] use std::mem::offset_of; #[repr(C)] struct S { a: u8, - b: (u8, u8), + b: [u8;2], c: [i32], } diff --git a/tests/ui/repr/align-with-extern-c-fn.rs b/tests/ui/repr/align-with-extern-c-fn.rs index 4d17d1e8816f2..9aecb53891775 100644 --- a/tests/ui/repr/align-with-extern-c-fn.rs +++ b/tests/ui/repr/align-with-extern-c-fn.rs @@ -10,7 +10,7 @@ #[repr(align(16))] pub struct A(#[allow(dead_code)] i64); -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "C" fn foo(x: A) {} fn main() { diff --git a/tests/ui/repr/repr-transparent-issue-87496.stderr b/tests/ui/repr/repr-transparent-issue-87496.stderr index aee31212b4ed2..38227d93a4fbb 100644 --- a/tests/ui/repr/repr-transparent-issue-87496.stderr +++ b/tests/ui/repr/repr-transparent-issue-87496.stderr @@ -4,7 +4,7 @@ warning: `extern` block uses type `TransparentCustomZst`, which is not FFI-safe LL | fn good17(p: TransparentCustomZst); | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = note: this struct contains only zero-sized fields + = note: `TransparentCustomZst` contains only zero-sized fields note: the type is defined here --> $DIR/repr-transparent-issue-87496.rs:6:1 | diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr index afc3d3838ad38..75801ea4134e6 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/extern_crate_improper.stderr @@ -17,7 +17,7 @@ error: `extern` block uses type `NormalStruct`, which is not FFI-safe LL | pub fn non_exhaustive_normal_struct(_: NormalStruct); | ^^^^^^^^^^^^ not FFI-safe | - = note: this struct is non-exhaustive + = note: `NormalStruct` is non-exhaustive error: `extern` block uses type `UnitStruct`, which is not FFI-safe --> $DIR/extern_crate_improper.rs:19:42 @@ -25,7 +25,7 @@ error: `extern` block uses type `UnitStruct`, which is not FFI-safe LL | pub fn non_exhaustive_unit_struct(_: UnitStruct); | ^^^^^^^^^^ not FFI-safe | - = note: this struct is non-exhaustive + = note: `UnitStruct` is non-exhaustive error: `extern` block uses type `TupleStruct`, which is not FFI-safe --> $DIR/extern_crate_improper.rs:21:43 @@ -33,7 +33,7 @@ error: `extern` block uses type `TupleStruct`, which is not FFI-safe LL | pub fn non_exhaustive_tuple_struct(_: TupleStruct); | ^^^^^^^^^^^ not FFI-safe | - = note: this struct is non-exhaustive + = note: `TupleStruct` is non-exhaustive error: `extern` block uses type `NonExhaustiveVariants`, which is not FFI-safe --> $DIR/extern_crate_improper.rs:23:38 diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs index cf5ab5123f679..6cd4607248705 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/improper_ctypes/same_crate_proper.rs @@ -19,9 +19,9 @@ pub struct NormalStruct { pub second_field: u16, } -#[non_exhaustive] -#[repr(C)] -pub struct UnitStruct; +//#[non_exhaustive] +//#[repr(C)] +//pub struct UnitStruct; #[non_exhaustive] #[repr(C)] diff --git a/tests/ui/structs-enums/align-struct.rs b/tests/ui/structs-enums/align-struct.rs index 3d8dad6e324e3..0f94e1553c7a5 100644 --- a/tests/ui/structs-enums/align-struct.rs +++ b/tests/ui/structs-enums/align-struct.rs @@ -33,6 +33,7 @@ enum Enum { } // Nested alignment - use `#[repr(C)]` to suppress field reordering for sizeof test +#[allow(improper_ctype_definitions)] #[repr(C)] struct Nested { a: i32, diff --git a/tests/ui/structs-enums/enum-non-c-like-repr-c-and-int.rs b/tests/ui/structs-enums/enum-non-c-like-repr-c-and-int.rs index 142d0ee32872e..6bdce52e15276 100644 --- a/tests/ui/structs-enums/enum-non-c-like-repr-c-and-int.rs +++ b/tests/ui/structs-enums/enum-non-c-like-repr-c-and-int.rs @@ -4,6 +4,8 @@ // independently. This verifies that `repr(some_int)` has a stable representation, // and that we don't miscompile these kinds of manipulations. +#![allow(improper_ctype_definitions)] + use std::time::Duration; use std::mem; diff --git a/tests/ui/structs-enums/enum-non-c-like-repr-c.rs b/tests/ui/structs-enums/enum-non-c-like-repr-c.rs index 15c9784dbb9ad..11f70abe0e11f 100644 --- a/tests/ui/structs-enums/enum-non-c-like-repr-c.rs +++ b/tests/ui/structs-enums/enum-non-c-like-repr-c.rs @@ -4,6 +4,8 @@ // independently. This verifies that `repr(some_int)` has a stable representation, // and that we don't miscompile these kinds of manipulations. +#![allow(improper_ctype_definitions)] + use std::time::Duration; use std::mem; diff --git a/tests/ui/structs-enums/enum-non-c-like-repr-int.rs b/tests/ui/structs-enums/enum-non-c-like-repr-int.rs index 64338b2aba765..714bd689ebc5c 100644 --- a/tests/ui/structs-enums/enum-non-c-like-repr-int.rs +++ b/tests/ui/structs-enums/enum-non-c-like-repr-int.rs @@ -4,6 +4,8 @@ // independently. This verifies that `repr(some_int)` has a stable representation, // and that we don't miscompile these kinds of manipulations. +#![allow(improper_ctype_definitions)] + use std::time::Duration; use std::mem; diff --git a/tests/ui/structs-enums/foreign-struct.rs b/tests/ui/structs-enums/foreign-struct.rs index f339c191ae806..763996de63643 100644 --- a/tests/ui/structs-enums/foreign-struct.rs +++ b/tests/ui/structs-enums/foreign-struct.rs @@ -1,17 +1,22 @@ //@ run-pass #![allow(dead_code)] -#![allow(non_camel_case_types)] // Passing enums by value - -pub enum void {} +#[repr(C)] +pub enum PoorQualityAnyEnum { + None = 0, + Int = 1, + Long = 2, + Float = 17, + Double = 18, +} mod bindgen { - use super::void; + use super::PoorQualityAnyEnum; extern "C" { - pub fn printf(v: void); + pub fn printf(v: PoorQualityAnyEnum); } } diff --git a/tests/ui/transmutability/abstraction/const_generic_fn.rs b/tests/ui/transmutability/abstraction/const_generic_fn.rs index 1ea978ce1bab9..0499afb4eb122 100644 --- a/tests/ui/transmutability/abstraction/const_generic_fn.rs +++ b/tests/ui/transmutability/abstraction/const_generic_fn.rs @@ -4,6 +4,7 @@ #![crate_type = "lib"] #![feature(transmutability)] #![allow(dead_code, incomplete_features, non_camel_case_types)] +#![allow(improper_ctype_definitions)] mod assert { use std::mem::{Assume, TransmuteFrom}; diff --git a/tests/ui/transmutability/arrays/should_have_correct_length.rs b/tests/ui/transmutability/arrays/should_have_correct_length.rs index 00c0c1122ef6b..8b89cd8fcc91a 100644 --- a/tests/ui/transmutability/arrays/should_have_correct_length.rs +++ b/tests/ui/transmutability/arrays/should_have_correct_length.rs @@ -4,6 +4,7 @@ #![crate_type = "lib"] #![feature(transmutability)] #![allow(dead_code, incomplete_features, non_camel_case_types)] +#![allow(improper_ctype_definitions)] mod assert { use std::mem::{Assume, TransmuteFrom}; diff --git a/tests/ui/transmutability/arrays/should_inherit_alignment.rs b/tests/ui/transmutability/arrays/should_inherit_alignment.rs index 70d2f07c449d3..7aa5e5f23e1d1 100644 --- a/tests/ui/transmutability/arrays/should_inherit_alignment.rs +++ b/tests/ui/transmutability/arrays/should_inherit_alignment.rs @@ -4,6 +4,7 @@ #![crate_type = "lib"] #![feature(transmutability)] #![allow(dead_code, incomplete_features, non_camel_case_types)] +#![allow(improper_ctype_definitions)] mod assert { use std::mem::{Assume, TransmuteFrom}; diff --git a/tests/ui/transmutability/references/u8-to-unit.rs b/tests/ui/transmutability/references/u8-to-unit.rs index 98deb6457cb07..3c89c72eb67b5 100644 --- a/tests/ui/transmutability/references/u8-to-unit.rs +++ b/tests/ui/transmutability/references/u8-to-unit.rs @@ -18,6 +18,7 @@ mod assert { } fn main() { + #[allow(improper_ctype_definitions)] #[repr(C)] struct Unit; assert::is_maybe_transmutable::<&'static u8, &'static Unit>(); } diff --git a/tests/ui/transmutability/references/unit-to-itself.rs b/tests/ui/transmutability/references/unit-to-itself.rs index 789455c03ea17..f0bd578f8419d 100644 --- a/tests/ui/transmutability/references/unit-to-itself.rs +++ b/tests/ui/transmutability/references/unit-to-itself.rs @@ -18,6 +18,7 @@ mod assert { } fn main() { + #[allow(improper_ctype_definitions)] #[repr(C)] struct Unit; assert::is_maybe_transmutable::<&'static Unit, &'static Unit>(); } diff --git a/tests/ui/transmutability/safety/assume/should_accept_if_dst_has_safety_invariant.rs b/tests/ui/transmutability/safety/assume/should_accept_if_dst_has_safety_invariant.rs index 0113049f51e53..998c38755df28 100644 --- a/tests/ui/transmutability/safety/assume/should_accept_if_dst_has_safety_invariant.rs +++ b/tests/ui/transmutability/safety/assume/should_accept_if_dst_has_safety_invariant.rs @@ -19,6 +19,7 @@ mod assert { fn test() { type Src = (); #[repr(C)] + #[allow(improper_ctype_definitions)] struct Dst; assert::is_transmutable::(); } diff --git a/tests/ui/transmutability/safety/assume/should_accept_if_src_has_safety_invariant.rs b/tests/ui/transmutability/safety/assume/should_accept_if_src_has_safety_invariant.rs index 46e84b48044f5..46672d3faf421 100644 --- a/tests/ui/transmutability/safety/assume/should_accept_if_src_has_safety_invariant.rs +++ b/tests/ui/transmutability/safety/assume/should_accept_if_src_has_safety_invariant.rs @@ -18,6 +18,7 @@ mod assert { fn test() { #[repr(C)] + #[allow(improper_ctype_definitions)] struct Src; type Dst = (); assert::is_transmutable::(); diff --git a/tests/ui/transmutability/safety/should_accept_if_src_has_safety_invariant.rs b/tests/ui/transmutability/safety/should_accept_if_src_has_safety_invariant.rs index aaba6febde4e8..cb430621435fd 100644 --- a/tests/ui/transmutability/safety/should_accept_if_src_has_safety_invariant.rs +++ b/tests/ui/transmutability/safety/should_accept_if_src_has_safety_invariant.rs @@ -18,6 +18,7 @@ mod assert { fn test() { #[repr(C)] + #[allow(improper_ctype_definitions)] struct Src; type Dst = (); assert::is_transmutable::(); diff --git a/tests/ui/transmutability/structs/repr/should_handle_all.rs b/tests/ui/transmutability/structs/repr/should_handle_all.rs index e5ca37e68ec6b..1fcf741e0a930 100644 --- a/tests/ui/transmutability/structs/repr/should_handle_all.rs +++ b/tests/ui/transmutability/structs/repr/should_handle_all.rs @@ -4,6 +4,7 @@ #![crate_type = "lib"] #![feature(transmutability)] #![allow(dead_code, incomplete_features, non_camel_case_types)] +#![allow(improper_ctype_definitions)] // turns out empty structs don't C well mod assert { use std::mem::{Assume, TransmuteFrom}; diff --git a/tests/ui/transmutability/unions/repr/should_handle_align.rs b/tests/ui/transmutability/unions/repr/should_handle_align.rs index 0605651bd7bb1..1d5d071742acb 100644 --- a/tests/ui/transmutability/unions/repr/should_handle_align.rs +++ b/tests/ui/transmutability/unions/repr/should_handle_align.rs @@ -4,6 +4,7 @@ #![crate_type = "lib"] #![feature(transmutability)] #![allow(dead_code, incomplete_features, non_camel_case_types)] +#![allow(improper_ctype_definitions)] mod assert { use std::mem::{Assume, TransmuteFrom}; diff --git a/tests/ui/transmutability/unions/repr/should_handle_packed.rs b/tests/ui/transmutability/unions/repr/should_handle_packed.rs index 5e9851ab0c984..83e6b9a8106c1 100644 --- a/tests/ui/transmutability/unions/repr/should_handle_packed.rs +++ b/tests/ui/transmutability/unions/repr/should_handle_packed.rs @@ -4,6 +4,7 @@ #![crate_type = "lib"] #![feature(transmutability)] #![allow(dead_code, incomplete_features, non_camel_case_types)] +#![allow(improper_ctype_definitions)] mod assert { use std::mem::{Assume, TransmuteFrom}; diff --git a/tests/ui/union/union-repr-c.stderr b/tests/ui/union/union-repr-c.stderr index 0beb7c376f3ad..dc8335da09b28 100644 --- a/tests/ui/union/union-repr-c.stderr +++ b/tests/ui/union/union-repr-c.stderr @@ -4,7 +4,7 @@ error: `extern` block uses type `W`, which is not FFI-safe LL | static FOREIGN2: W; | ^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union + = help: consider adding a `#[repr(C)]` attribute to this union = note: this union has unspecified layout note: the type is defined here --> $DIR/union-repr-c.rs:9:1