From be58d2f895ec751b6f0caa53e549006b43750d64 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Wed, 11 Dec 2024 23:54:33 +0100 Subject: [PATCH 01/15] Changes to the ImproperCTypes lint, start of take 1 [does not pass tests] This reverts commit 1fcbb1e338f50e752a64c561838aaa7eb111e039. --- compiler/rustc_lint/messages.ftl | 15 +- compiler/rustc_lint/src/lints.rs | 43 +- compiler/rustc_lint/src/types.rs | 370 +++++++++++++++--- ...extern-C-non-FFI-safe-arg-ice-52334.stderr | 2 + .../extern/extern-C-str-arg-ice-80125.stderr | 2 + tests/ui/lint/extern-C-fnptr-lints-slices.rs | 2 +- .../lint/extern-C-fnptr-lints-slices.stderr | 7 +- tests/ui/lint/lint-ctypes-73249-2.stderr | 1 + tests/ui/lint/lint-ctypes-94223.stderr | 26 +- tests/ui/lint/lint-ctypes-cstr.rs | 4 +- tests/ui/lint/lint-ctypes-cstr.stderr | 11 +- tests/ui/lint/lint-ctypes-fn.rs | 6 +- tests/ui/lint/lint-ctypes-fn.stderr | 22 +- tests/ui/lint/lint-ctypes.rs | 35 +- tests/ui/lint/lint-ctypes.stderr | 123 +++--- 15 files changed, 489 insertions(+), 180 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 08180bf8f8b2c..df41bdd432bd9 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -377,7 +377,6 @@ 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 @@ -395,7 +394,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 @@ -403,7 +404,13 @@ 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_help = consider using the base type instead +lint_improper_ctypes_pat_reason = pattern types have no C equivalent + +lint_improper_ctypes_sized_ptr_to_unsafe_type = + this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout + +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 @@ -429,6 +436,10 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re 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/lints.rs b/compiler/rustc_lint/src/lints.rs index 7268a7f704fcb..a13cf67a67393 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1914,29 +1914,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..b5465318695c3 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -25,10 +25,10 @@ mod improper_ctypes; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, - AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, - InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, - UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment, - VariantSizeDifferencesDiag, + AtomicOrderingStore, ImproperCTypes, ImproperCTypesLayer, InvalidAtomicOrderingDiag, + InvalidNanComparisons, InvalidNanComparisonsSuggestion, + UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion, + UnusedComparisons, UsesPowerAlignment, VariantSizeDifferencesDiag, }; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -823,7 +823,110 @@ struct CTypesVisitorState<'tcx> { enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), - FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option }, + FfiUnsafe { + ty: Ty<'tcx>, + reason: DiagMessage, + help: Option, + }, + FfiUnsafeWrapper { + ty: Ty<'tcx>, + reason: DiagMessage, + help: Option, + wrapped: Box>, + }, +} + +/// Determine if a type is sized or not, and wether 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, +} + +/// 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; + + if ty.is_sized(tcx, cx.typing_env()) { + TypeSizedness::Definite + } else { + match ty.kind() { + ty::Slice(_) => TypeSizedness::UnsizedWithMetadata, + ty::Str => TypeSizedness::UnsizedWithMetadata, + ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata, + ty::Foreign(..) => TypeSizedness::UnsizedWithExternType, + // 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, ..) => todo!("We... don't know enough about this type yet?"), + 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; + } + // FIXME: how do we deal with non-exhaustive unsized structs/unions? + + 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 unsized + // (also since !ty.is_sized(), we have at least one field) + let last_field_i = variant.fields.last_index().unwrap(); + let last_field = &variant.fields[last_field_i]; + let field_ty = last_field.ty(cx.tcx, args); + let field_ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), field_ty) + .unwrap_or(field_ty); + match get_type_sizedness(cx, field_ty) { + s @ (TypeSizedness::UnsizedWithMetadata + | TypeSizedness::UnsizedWithExternType) => s, + TypeSizedness::Definite => { + bug!("failed to find the reason why struct `{:?}` is unsized", ty) + } + } + } + } + } + ty::Tuple(tuple) => { + // only the last field may be unsized + let n_fields = tuple.len(); + let field_ty: Ty<'tcx> = tuple[n_fields - 1]; + //let field_ty = last_field.ty(cx.tcx, args); + let field_ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), field_ty) + .unwrap_or(field_ty); + match get_type_sizedness(cx, field_ty) { + s @ (TypeSizedness::UnsizedWithMetadata + | TypeSizedness::UnsizedWithExternType) => s, + TypeSizedness::Definite => { + bug!("failed to find the reason why tuple `{:?}` is unsized", ty) + } + } + } + ty => { + bug!( + "we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`", + ty + ) + } + } + } } pub(crate) fn nonnull_optimization_guaranteed<'tcx>( @@ -860,7 +963,7 @@ fn ty_is_known_nonnull<'tcx>( 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); @@ -1078,12 +1181,13 @@ 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( + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { ty, - sp, - fluent::lint_improper_ctypes_array_reason, - Some(fluent::lint_improper_ctypes_array_help), - ); + note: fluent::lint_improper_ctypes_array_reason, + help: Some(fluent::lint_improper_ctypes_array_help), + inner_ty: None, + span_note: None, + }]); true } else { false @@ -1140,9 +1244,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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, + FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false, FfiPhantom(..) => true, - r @ FfiUnsafe { .. } => return r, + r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r, } } @@ -1176,16 +1280,47 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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; + if let Some(inner_ty) = ty.boxed_ty() { + if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite = + get_type_sizedness(self.cx, inner_ty) + { + // discussion on declaration vs definition: + // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm + // of this `match *ty.kind()` block + if matches!(self.mode, CItemKind::Definition) { + return FfiSafe; + } else { + let inner_res = self.check_type_for_ffi(acc, inner_ty); + return match inner_res { + FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + wrapped: Box::new(inner_res), + help: None, + }, + _ => inner_res, + }; + } } else { + let help = match inner_ty.kind() { + ty::Str => Some(fluent::lint_improper_ctypes_str_help), + ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), + ty::Adt(def, _) + if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) + && matches!( + tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + && !acc.base_ty.is_mutable_ptr() => + { + Some(fluent::lint_improper_ctypes_cstr_help) + } + _ => None, + }; return FfiUnsafe { ty, - reason: fluent::lint_improper_ctypes_box, - help: None, + reason: fluent::lint_improper_ctypes_unsized_box, + help, }; } } @@ -1343,15 +1478,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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(), @@ -1361,7 +1487,70 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty), + ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => { + if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite = + get_type_sizedness(self.cx, inner_ty) + { + // there's a nuance on what this lint should do for + // function definitions (`extern "C" fn fn_name(...) {...}`) + // versus declarations (`unsafe extern "C" {fn fn_name(...);}`). + // This is touched upon in https://github.com/rust-lang/rust/issues/66220 + // and https://github.com/rust-lang/rust/pull/72700 + // + // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer + // (which has a stable layout) but points to FFI-unsafe type, is it safe? + // On one hand, the function's ABI will match that of a similar C-declared function API, + // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful. + // In this code, the opinion on is split between function declarations and function definitions, + // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type. + // For declarations, we see this as unsafe, but for definitions, we see this as safe. + // + // For extern function declarations, the actual definition of the function is written somewhere else, + // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) + // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, + // and having the full type information is necessary to compile the function. + if matches!(self.mode, CItemKind::Definition) { + return FfiSafe; + } else if matches!(ty.kind(), ty::RawPtr(..)) + && matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty()) + { + FfiSafe + } else { + let inner_res = self.check_type_for_ffi(acc, inner_ty); + return match inner_res { + FfiSafe => inner_res, + _ => FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + wrapped: Box::new(inner_res), + help: None, + }, + }; + } + } else { + let help = match inner_ty.kind() { + ty::Str => Some(fluent::lint_improper_ctypes_str_help), + ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), + ty::Adt(def, _) + if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) + && matches!( + tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + && !acc.base_ty.is_mutable_ptr() => + { + Some(fluent::lint_improper_ctypes_cstr_help) + } + _ => None, + }; + let reason = match ty.kind() { + ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr, + ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref, + _ => unreachable!(), + }; + FfiUnsafe { ty, reason, help } + } + } ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty), @@ -1379,7 +1568,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { for arg in sig.inputs() { match self.check_type_for_ffi(acc, *arg) { FfiSafe => {} - r => return r, + r => { + return FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }; + } } } @@ -1388,7 +1584,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return FfiSafe; } - self.check_type_for_ffi(acc, ret_ty) + match self.check_type_for_ffi(acc, ret_ty) { + r @ (FfiSafe | FfiPhantom(_)) => r, + r => FfiUnsafeWrapper { + ty: ty.clone(), + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }, + } } ty::Foreign(..) => FfiSafe, @@ -1427,8 +1631,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { &mut self, ty: Ty<'tcx>, sp: Span, - note: DiagMessage, - help: Option, + mut reasons: Vec>, ) { let lint = match self.mode { CItemKind::Declaration => IMPROPER_CTYPES, @@ -1438,18 +1641,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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 }, - ); + for reason in reasons.iter_mut() { + reason.span_note = if let ty::Adt(def, _) = reason.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, reasons }); } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { @@ -1478,7 +1680,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { .visit_with(&mut ProhibitOpaqueTypes) .break_value() { - self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None); + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { + ty, + note: fluent::lint_improper_ctypes_opaque, + span_note: Some(sp), + help: None, + inner_ty: None, + }]); true } else { false @@ -1517,15 +1725,71 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match self.check_type_for_ffi(&mut acc, ty) { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { - self.emit_ffi_unsafe_type_lint( + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { ty, - sp, - fluent::lint_improper_ctypes_only_phantomdata, - None, - ); + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + help: None, + inner_ty: None, + }]); } FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint(ty, sp, reason, help); + self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { + ty, + help, + note: reason, + span_note: None, // filled later + inner_ty: None, + }]); + } + ffir @ FfiResult::FfiUnsafeWrapper { .. } => { + let mut ffiresult_recursor = ControlFlow::Continue(&ffir); + let mut cimproper_layers: Vec> = vec![]; + + // this whole while block converts the arbitrarily-deep + // FfiResult stack to an ImproperCTypesLayer Vec + while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor { + match ffir_rec { + FfiResult::FfiPhantom(ty) => { + 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: None, + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + }); + ffiresult_recursor = ControlFlow::Break(()); + } + FfiResult::FfiUnsafe { ty, reason, help } + | FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => { + 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: reason.clone(), + span_note: None, // filled later + }); + + if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec { + ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref()); + } else { + ffiresult_recursor = ControlFlow::Break(()); + } + } + FfiResult::FfiSafe => { + bug!("malformed FfiResult stack: it should be unsafe all the way down") + } + }; + } + // should always have at least one type + let last_ty = cimproper_layers.last().unwrap().ty.clone(); + self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers); } } } 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..b5c718ec38147 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 @@ -4,6 +4,7 @@ warning: `extern` fn uses type `CStr`, which is not FFI-safe LL | type Foo = extern "C" fn(::std::ffi::CStr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | + = note: the function pointer to `extern "C" fn(CStr)` is FFI-unsafe due to `CStr` = 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_definitions)]` on by default @@ -14,6 +15,7 @@ warning: `extern` block uses type `CStr`, which is not FFI-safe LL | fn meh(blah: Foo); | ^^^ not FFI-safe | + = note: the function pointer to `extern "C" fn(CStr)` is FFI-unsafe due to `CStr` = 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 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..f2ee21c316658 100644 --- a/tests/ui/extern/extern-C-str-arg-ice-80125.stderr +++ b/tests/ui/extern/extern-C-str-arg-ice-80125.stderr @@ -4,6 +4,7 @@ warning: `extern` fn uses type `str`, which is not FFI-safe 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 @@ -14,6 +15,7 @@ warning: `extern` fn uses type `str`, which is not FFI-safe LL | pub extern "C" fn register_something(bind: ExternCallback) -> Struct { | ^^^^^^^^^^^^^^ 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 diff --git a/tests/ui/lint/extern-C-fnptr-lints-slices.rs b/tests/ui/lint/extern-C-fnptr-lints-slices.rs index 0c35eb37a4890..4e3832ab1b672 100644 --- a/tests/ui/lint/extern-C-fnptr-lints-slices.rs +++ b/tests/ui/lint/extern-C-fnptr-lints-slices.rs @@ -3,7 +3,7 @@ // 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` fn 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..c0923dd96c8c3 100644 --- a/tests/ui/lint/extern-C-fnptr-lints-slices.stderr +++ b/tests/ui/lint/extern-C-fnptr-lints-slices.stderr @@ -1,11 +1,12 @@ -error: `extern` fn uses type `[u8]`, which is not FFI-safe +error: `extern` fn 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 | diff --git a/tests/ui/lint/lint-ctypes-73249-2.stderr b/tests/ui/lint/lint-ctypes-73249-2.stderr index 2d0dfe94f097e..0be04824f3936 100644 --- a/tests/ui/lint/lint-ctypes-73249-2.stderr +++ b/tests/ui/lint/lint-ctypes-73249-2.stderr @@ -4,6 +4,7 @@ error: `extern` block uses type `Qux`, which is not FFI-safe LL | fn lint_me() -> A<()>; | ^^^^^ not FFI-safe | + = note: this reference (`&Qux`) is ABI-compatible with a C pointer, but `Qux` itself does not have a C layout = note: opaque types have no C equivalent note: the lint level is defined here --> $DIR/lint-ctypes-73249-2.rs:2:9 diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/lint-ctypes-94223.stderr index bd127cf60044c..4bebca69b7f3b 100644 --- a/tests/ui/lint/lint-ctypes-94223.stderr +++ b/tests/ui/lint/lint-ctypes-94223.stderr @@ -4,7 +4,8 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe LL | pub fn bad(f: extern "C" fn([u8])) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead + = 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-ctypes-94223.rs:2:9 @@ -18,7 +19,8 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe LL | pub fn bad_twice(f: Result) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead + = 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` fn uses type `[u8]`, which is not FFI-safe @@ -27,7 +29,8 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe LL | pub fn bad_twice(f: Result) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead + = 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` fn uses type `[u8]`, which is not FFI-safe @@ -36,7 +39,8 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe LL | struct BadStruct(extern "C" fn([u8])); | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead + = 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` fn uses type `[u8]`, which is not FFI-safe @@ -45,7 +49,8 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe LL | A(extern "C" fn([u8])), | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead + = 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` fn uses type `[u8]`, which is not FFI-safe @@ -54,7 +59,8 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe LL | A(extern "C" fn([u8])), | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead + = 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` fn uses type `[u8]`, which is not FFI-safe @@ -63,7 +69,8 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe LL | type Foo = extern "C" fn([u8]); | ^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead + = 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` fn uses type `Option<&::FooType>`, which is not FFI-safe @@ -72,6 +79,7 @@ error: `extern` fn uses type `Option<&::FooType>`, which is not F 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 @@ -81,6 +89,7 @@ error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe 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)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here @@ -95,6 +104,7 @@ error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe 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)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here @@ -109,6 +119,7 @@ error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe 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)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here @@ -123,6 +134,7 @@ error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe 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)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here diff --git a/tests/ui/lint/lint-ctypes-cstr.rs b/tests/ui/lint/lint-ctypes-cstr.rs index b04decd0bcacc..c4de5a44a9623 100644 --- a/tests/ui/lint/lint-ctypes-cstr.rs +++ b/tests/ui/lint/lint-ctypes-cstr.rs @@ -8,7 +8,7 @@ extern "C" { //~^ 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 + //~^ 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 @@ -27,7 +27,7 @@ extern "C" { } extern "C" fn rust_take_cstr_ref(s: &CStr) {} -//~^ ERROR `extern` fn uses type `CStr`, which is not FFI-safe +//~^ 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 diff --git a/tests/ui/lint/lint-ctypes-cstr.stderr b/tests/ui/lint/lint-ctypes-cstr.stderr index 8957758d57732..da15b748f2110 100644 --- a/tests/ui/lint/lint-ctypes-cstr.stderr +++ b/tests/ui/lint/lint-ctypes-cstr.stderr @@ -12,14 +12,14 @@ note: the lint level is defined here LL | #![deny(improper_ctypes, improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^ -error: `extern` block uses type `CStr`, which is not FFI-safe +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 + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` block uses type `CString`, which is not FFI-safe --> $DIR/lint-ctypes-cstr.rs:13:24 @@ -36,6 +36,7 @@ error: `extern` block uses type `CString`, which is not FFI-safe LL | fn take_cstring_ref(s: &CString); | ^^^^^^^^ not FFI-safe | + = note: this reference (`&CString`) is ABI-compatible with a C pointer, but `CString` itself does not have a C layout = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` = note: `CStr`/`CString` do not have a guaranteed layout @@ -45,6 +46,7 @@ error: `extern` block uses type `CString`, which is not FFI-safe LL | fn no_special_help_for_mut_cstring(s: *mut CString); | ^^^^^^^^^^^^ not FFI-safe | + = note: this reference (`*mut CString`) is ABI-compatible with a C pointer, but `CString` itself does not have a C layout = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout @@ -54,17 +56,18 @@ error: `extern` block uses type `CString`, which is not FFI-safe LL | fn no_special_help_for_mut_cstring_ref(s: &mut CString); | ^^^^^^^^^^^^ not FFI-safe | + = note: this reference (`&mut CString`) is ABI-compatible with a C pointer, but `CString` itself does not have a C layout = 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 +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: 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-ctypes-cstr.rs:2:26 | diff --git a/tests/ui/lint/lint-ctypes-fn.rs b/tests/ui/lint/lint-ctypes-fn.rs index 73820c86d1a02..e16ff9573fd18 100644 --- a/tests/ui/lint/lint-ctypes-fn.rs +++ b/tests/ui/lint/lint-ctypes-fn.rs @@ -68,10 +68,10 @@ 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) { } @@ -124,7 +124,7 @@ pub extern "C" fn transparent_i128(p: TransparentI128) { } //~^ ERROR: uses type `i128` pub extern "C" fn transparent_str(p: TransparentStr) { } -//~^ ERROR: uses type `str` +//~^ ERROR: uses type `&str` pub extern "C" fn transparent_fn(p: TransparentBadFn) { } diff --git a/tests/ui/lint/lint-ctypes-fn.stderr b/tests/ui/lint/lint-ctypes-fn.stderr index a62533a4be17b..c86c02c80064c 100644 --- a/tests/ui/lint/lint-ctypes-fn.stderr +++ b/tests/ui/lint/lint-ctypes-fn.stderr @@ -1,25 +1,25 @@ -error: `extern` fn uses type `[u32]`, which is not FFI-safe +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 + = 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-ctypes-fn.rs:2:9 | LL | #![deny(improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `extern` fn uses type `str`, which is not FFI-safe +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 + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe --> $DIR/lint-ctypes-fn.rs:80:34 @@ -27,7 +27,8 @@ error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } | ^^^^^^^^^ not FFI-safe | - = note: box cannot be represented as a single pointer + = 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-ctypes-fn.rs:83:35 @@ -35,7 +36,8 @@ error: `extern` fn uses type `Box`, which is not FFI-safe LL | pub extern "C" fn boxed_string(p: Box) { } | ^^^^^^^^ not FFI-safe | - = note: box cannot be represented as a single pointer + = 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-ctypes-fn.rs:86:34 @@ -43,7 +45,7 @@ error: `extern` fn uses type `Box`, which is not FFI-safe LL | pub extern "C" fn boxed_trait(p: Box) { } | ^^^^^^^^^^^^^^ not FFI-safe | - = note: box cannot be represented as a single pointer + = 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-ctypes-fn.rs:89:32 @@ -149,14 +151,14 @@ LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } | = note: 128-bit integers don't currently have a known stable ABI -error: `extern` fn uses type `str`, which is not FFI-safe +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 + = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `PhantomData`, which is not FFI-safe --> $DIR/lint-ctypes-fn.rs:172:43 diff --git a/tests/ui/lint/lint-ctypes.rs b/tests/ui/lint/lint-ctypes.rs index 6dd9be10a48f8..22617e0ebb99b 100644 --- a/tests/ui/lint/lint-ctypes.rs +++ b/tests/ui/lint/lint-ctypes.rs @@ -1,4 +1,5 @@ #![feature(rustc_private)] +#![feature(extern_types)] #![allow(private_interfaces)] #![deny(improper_ctypes)] @@ -6,7 +7,9 @@ use std::cell::UnsafeCell; use std::marker::PhantomData; use std::ffi::{c_int, c_uint}; +use std::fmt::Debug; +unsafe extern "C" {type UnsizedOpaque;} trait Bar { } trait Mirror { type It: ?Sized; } impl Mirror for T { type It = Self; } @@ -20,7 +23,7 @@ pub type I32Pair = (i32, i32); #[repr(C)] pub struct 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)] @@ -28,7 +31,7 @@ pub struct TransparentI128(i128); #[repr(transparent)] pub struct TransparentStr(&'static str); #[repr(transparent)] -pub struct TransparentBadFn(RustBadRet); +pub struct TransparentBoxFn(RustBoxRet); #[repr(transparent)] pub struct TransparentInt(u32); #[repr(transparent)] @@ -39,6 +42,16 @@ pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); pub struct TransparentUnit(f32, PhantomData); #[repr(transparent)] pub struct TransparentCustomZst(i32, ZeroSize); +#[repr(C)] +pub struct UnsizedStructBecauseForeign { + sized: u32, + unszd: UnsizedOpaque, +} +#[repr(C)] +pub struct UnsizedStructBecauseDyn { + sized: u32, + unszd: dyn Debug, +} #[repr(C)] pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); @@ -48,15 +61,14 @@ extern "C" { pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `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 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 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,12 +78,15 @@ 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 fn_contained(p: RustBoxRet); 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 transparent_str(p: TransparentStr); //~ ERROR: uses type `&str` + pub fn transparent_fn(p: TransparentBoxFn); pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]` + pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign); + pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn` + pub fn no_niche_a(a: Option>); //~^ ERROR: uses type `Option>` pub fn no_niche_b(b: Option>); diff --git a/tests/ui/lint/lint-ctypes.stderr b/tests/ui/lint/lint-ctypes.stderr index 8137ae868d356..9ec17b1d94b32 100644 --- a/tests/ui/lint/lint-ctypes.stderr +++ b/tests/ui/lint/lint-ctypes.stderr @@ -1,83 +1,68 @@ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:47:28 + --> $DIR/lint-ctypes.rs:60:28 | LL | pub fn ptr_type1(size: *const Foo); | ^^^^^^^^^^ not FFI-safe | + = note: this reference (`*const Foo`) is ABI-compatible with a C pointer, but `Foo` itself does not have a C layout = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes.rs:25:1 + --> $DIR/lint-ctypes.rs:28:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/lint-ctypes.rs:4:9 + --> $DIR/lint-ctypes.rs:5:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:48:28 + --> $DIR/lint-ctypes.rs:61:28 | LL | pub fn ptr_type2(size: *const Foo); | ^^^^^^^^^^ not FFI-safe | + = note: this reference (`*const Foo`) is ABI-compatible with a C pointer, but `Foo` itself does not have a C layout = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes.rs:25:1 + --> $DIR/lint-ctypes.rs:28:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^ error: `extern` block uses type `((),)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:50:25 + --> $DIR/lint-ctypes.rs:63:25 | LL | pub fn ptr_tuple(p: *const ((),)); | ^^^^^^^^^^^^ not FFI-safe | + = note: this reference (`*const ((),)`) is ABI-compatible with a C pointer, but `((),)` itself does not have a C layout = 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 +error: `extern` block uses type `&[u32]`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:64:26 | LL | pub fn slice_type(p: &[u32]); | ^^^^^^ not FFI-safe | - = help: consider using a raw pointer instead - = note: slices have no C equivalent + = 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 `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:52:24 +error: `extern` block uses type `&str`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:65: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 + = 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/lint-ctypes.rs:56:25 + --> $DIR/lint-ctypes.rs:68:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -86,7 +71,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:57:25 + --> $DIR/lint-ctypes.rs:69:25 | LL | pub fn i128_type(p: i128); | ^^^^ not FFI-safe @@ -94,23 +79,23 @@ LL | pub fn i128_type(p: i128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:58:25 + --> $DIR/lint-ctypes.rs:70: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 +error: `extern` block uses type `&dyn Bar`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:71:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe | - = note: trait objects have no C equivalent + = 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/lint-ctypes.rs:60:26 + --> $DIR/lint-ctypes.rs:72:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -119,7 +104,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:61:27 + --> $DIR/lint-ctypes.rs:73:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -128,7 +113,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:62:25 + --> $DIR/lint-ctypes.rs:74:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -136,26 +121,26 @@ LL | pub fn zero_size(p: ZeroSize); = help: consider adding a member to this struct = note: this struct has no fields note: the type is defined here - --> $DIR/lint-ctypes.rs:21:1 + --> $DIR/lint-ctypes.rs:24:1 | LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:63:33 + --> $DIR/lint-ctypes.rs:75:33 | LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: composed only of `PhantomData` note: the type is defined here - --> $DIR/lint-ctypes.rs:44:1 + --> $DIR/lint-ctypes.rs:57:1 | LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:66:12 + --> $DIR/lint-ctypes.rs:78:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -163,7 +148,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:67:23 + --> $DIR/lint-ctypes.rs:79:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -172,7 +157,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:68:24 + --> $DIR/lint-ctypes.rs:80:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -180,43 +165,25 @@ LL | pub fn fn_type2(p: fn()); = 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 + --> $DIR/lint-ctypes.rs:82: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 +error: `extern` block uses type `&str`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:83: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 + = 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/lint-ctypes.rs:73:27 + --> $DIR/lint-ctypes.rs:85:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -224,8 +191,16 @@ LL | pub fn raw_array(arr: [u8; 8]); = help: consider passing a pointer to the array = note: passing raw arrays by value is not FFI-safe +error: `extern` block uses type `&UnsizedStructBecauseDyn`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:88: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/lint-ctypes.rs:75:26 + --> $DIR/lint-ctypes.rs:90:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -234,7 +209,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:77:26 + --> $DIR/lint-ctypes.rs:92:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -243,7 +218,7 @@ LL | pub fn no_niche_b(b: Option>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:80:34 + --> $DIR/lint-ctypes.rs:95:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -251,12 +226,12 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:81:40 + --> $DIR/lint-ctypes.rs:96:40 | LL | pub static static_u128_array_type: [u128; 16]; | ^^^^^^^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI -error: aborting due to 27 previous errors +error: aborting due to 24 previous errors From be066a56efb1680f9d645b72c94fca446355e991 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Mon, 23 Dec 2024 11:56:55 +0100 Subject: [PATCH 02/15] lint ImproperCTypes: make checking indirection more DRY [commit does not pass tests] --- compiler/rustc_lint/src/types.rs | 193 ++++++++++++++----------------- 1 file changed, 88 insertions(+), 105 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index b5465318695c3..efbc138cf18c6 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -847,6 +847,17 @@ enum TypeSizedness { UnsizedWithMetadata, } +/// 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 { @@ -1259,6 +1270,77 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } + /// Checks if the given indirection (box,ref,pointer) is "ffi-safe" + fn check_indirection_for_ffi( + &self, + acc: &mut CTypesVisitorState<'tcx>, + ty: Ty<'tcx>, + inner_ty: Ty<'tcx>, + indirection_type: IndirectionType, + ) -> FfiResult<'tcx> { + use FfiResult::*; + let tcx = self.cx.tcx; + match get_type_sizedness(self.cx, inner_ty) { + TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => { + // there's a nuance on what this lint should do for + // function definitions (`extern "C" fn fn_name(...) {...}`) + // versus declarations (`unsafe extern "C" {fn fn_name(...);}`). + // This is touched upon in https://github.com/rust-lang/rust/issues/66220 + // and https://github.com/rust-lang/rust/pull/72700 + // + // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer + // (which has a stable layout) but points to FFI-unsafe type, is it safe? + // On one hand, the function's ABI will match that of a similar C-declared function API, + // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful. + // In this code, the opinion on is split between function declarations and function definitions, + // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type. + // For declarations, we see this as unsafe, but for definitions, we see this as safe. + // + // For extern function declarations, the actual definition of the function is written somewhere else, + // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) + // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, + // and having the full type information is necessary to compile the function. + if matches!(self.mode, CItemKind::Definition) { + return FfiSafe; + } else { + let inner_res = self.check_type_for_ffi(acc, inner_ty); + return match inner_res { + FfiSafe => inner_res, + _ => FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + wrapped: Box::new(inner_res), + help: None, + }, + }; + } + } + 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), + ty::Adt(def, _) + if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) + && matches!( + tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + && !acc.base_ty.is_mutable_ptr() => + { + Some(fluent::lint_improper_ctypes_cstr_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, + }; + FfiUnsafe { ty, reason, help } + } + } + } + /// 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( @@ -1281,48 +1363,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match *ty.kind() { ty::Adt(def, args) => { if let Some(inner_ty) = ty.boxed_ty() { - if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite = - get_type_sizedness(self.cx, inner_ty) - { - // discussion on declaration vs definition: - // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm - // of this `match *ty.kind()` block - if matches!(self.mode, CItemKind::Definition) { - return FfiSafe; - } else { - let inner_res = self.check_type_for_ffi(acc, inner_ty); - return match inner_res { - FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper { - ty, - reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, - wrapped: Box::new(inner_res), - help: None, - }, - _ => inner_res, - }; - } - } else { - let help = match inner_ty.kind() { - ty::Str => Some(fluent::lint_improper_ctypes_str_help), - ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), - ty::Adt(def, _) - if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) - && matches!( - tcx.get_diagnostic_name(def.did()), - Some(sym::cstring_type | sym::cstr_type) - ) - && !acc.base_ty.is_mutable_ptr() => - { - Some(fluent::lint_improper_ctypes_cstr_help) - } - _ => None, - }; - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_unsized_box, - help, - }; - } + return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Box); } if def.is_phantom_data() { return FfiPhantom(ty); @@ -1487,69 +1528,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } - ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => { - if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite = - get_type_sizedness(self.cx, inner_ty) - { - // there's a nuance on what this lint should do for - // function definitions (`extern "C" fn fn_name(...) {...}`) - // versus declarations (`unsafe extern "C" {fn fn_name(...);}`). - // This is touched upon in https://github.com/rust-lang/rust/issues/66220 - // and https://github.com/rust-lang/rust/pull/72700 - // - // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer - // (which has a stable layout) but points to FFI-unsafe type, is it safe? - // On one hand, the function's ABI will match that of a similar C-declared function API, - // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful. - // In this code, the opinion on is split between function declarations and function definitions, - // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type. - // For declarations, we see this as unsafe, but for definitions, we see this as safe. - // - // For extern function declarations, the actual definition of the function is written somewhere else, - // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) - // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, - // and having the full type information is necessary to compile the function. - if matches!(self.mode, CItemKind::Definition) { - return FfiSafe; - } else if matches!(ty.kind(), ty::RawPtr(..)) - && matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty()) - { - FfiSafe - } else { - let inner_res = self.check_type_for_ffi(acc, inner_ty); - return match inner_res { - FfiSafe => inner_res, - _ => FfiUnsafeWrapper { - ty, - reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, - wrapped: Box::new(inner_res), - help: None, - }, - }; - } - } else { - let help = match inner_ty.kind() { - ty::Str => Some(fluent::lint_improper_ctypes_str_help), - ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help), - ty::Adt(def, _) - if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) - && matches!( - tcx.get_diagnostic_name(def.did()), - Some(sym::cstring_type | sym::cstr_type) - ) - && !acc.base_ty.is_mutable_ptr() => - { - Some(fluent::lint_improper_ctypes_cstr_help) - } - _ => None, - }; - let reason = match ty.kind() { - ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr, - ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref, - _ => unreachable!(), - }; - FfiUnsafe { ty, reason, help } - } + ty::RawPtr(inner_ty, _) => { + return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::RawPtr); + } + ty::Ref(_, inner_ty, _) => { + return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref); } ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty), From f30f65f17f67a3436be32d981ed4bb222520651c Mon Sep 17 00:00:00 2001 From: niacdoial Date: Mon, 23 Dec 2024 15:14:16 +0100 Subject: [PATCH 03/15] lint ImproperCTypes: fix TypeSizedness code --- compiler/rustc_lint/src/types.rs | 86 ++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index efbc138cf18c6..4e45a354cbb0b 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -845,6 +845,8 @@ enum TypeSizedness { 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 @@ -863,17 +865,16 @@ enum IndirectionType { 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, - // 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, ..) => todo!("We... don't know enough about this type yet?"), ty::Adt(def, args) => { // for now assume: boxes and phantoms don't mess with this match def.adt_kind() { @@ -886,7 +887,13 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type { return TypeSizedness::UnsizedWithMetadata; } - // FIXME: how do we deal with non-exhaustive unsized structs/unions? + + // FIXME: double-check: non-exhaustive structs from other crates are assumed to be ?Sized, right? + let is_non_exhaustive = + def.non_enum_variant().is_field_list_non_exhaustive(); + if is_non_exhaustive && !def.did().is_local() { + return TypeSizedness::NotYetKnown; + } if def.non_enum_variant().fields.is_empty() { bug!("an empty struct is necessarily sized"); @@ -894,7 +901,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type let variant = def.non_enum_variant(); - // only the last field may be unsized + // only the last field may be !Sized (or ?Sized in the case of type params) // (also since !ty.is_sized(), we have at least one field) let last_field_i = variant.fields.last_index().unwrap(); let last_field = &variant.fields[last_field_i]; @@ -905,7 +912,8 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type .unwrap_or(field_ty); match get_type_sizedness(cx, field_ty) { s @ (TypeSizedness::UnsizedWithMetadata - | TypeSizedness::UnsizedWithExternType) => s, + | TypeSizedness::UnsizedWithExternType + | TypeSizedness::NotYetKnown) => s, TypeSizedness::Definite => { bug!("failed to find the reason why struct `{:?}` is unsized", ty) } @@ -914,7 +922,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type } } ty::Tuple(tuple) => { - // only the last field may be unsized + // only the last field may be !Sized (or ?Sized in the case of type params) let n_fields = tuple.len(); let field_ty: Ty<'tcx> = tuple[n_fields - 1]; //let field_ty = last_field.ty(cx.tcx, args); @@ -924,18 +932,49 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type .unwrap_or(field_ty); match get_type_sizedness(cx, field_ty) { s @ (TypeSizedness::UnsizedWithMetadata - | TypeSizedness::UnsizedWithExternType) => s, + | TypeSizedness::UnsizedWithExternType + | TypeSizedness::NotYetKnown) => s, TypeSizedness::Definite => { bug!("failed to find the reason why tuple `{:?}` is unsized", ty) } } } - 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!( - "we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`", - ty + "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::Alias(ty::Weak, ..) + | 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), } } } @@ -1315,6 +1354,26 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } } + 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 + // (FIXME note: function 'declarations' there should be treated as definitions) + // - 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) + // + // FIXME: on a side note, we should separate 'true' declarations (non-rust code), + // 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions. + // (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't) + + // wether 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. + return self.check_type_for_ffi(acc, inner_ty); + } TypeSizedness::UnsizedWithMetadata => { let help = match inner_ty.kind() { ty::Str => Some(fluent::lint_improper_ctypes_str_help), @@ -1490,6 +1549,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // but only the base type is relevant for being representable in FFI. ty::Pat(base, ..) => self.check_type_for_ffi(acc, base), + // FIXME: this should probably be architecture-dependant + // same with some ty::Float variants. ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None } } @@ -1535,6 +1596,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref); } + // having arrays as arguments / return values themselves is not FFI safe, + // but that is checked elsewhere + // if we reach this, we can assume the array is inside a struct, behind an indirection, etc. ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty), ty::FnPtr(sig_tys, hdr) => { From dbcdb85895e8807fe88ee8621a7f12f559cfe423 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Mon, 23 Dec 2024 15:17:49 +0100 Subject: [PATCH 04/15] lint ImproperCTypes: add test to cover all ty_kinds --- tests/ui/lint/lint-ctypes-tykind-fuzz.rs | 311 ++++++++++++++++ tests/ui/lint/lint-ctypes-tykind-fuzz.stderr | 352 +++++++++++++++++++ 2 files changed, 663 insertions(+) create mode 100644 tests/ui/lint/lint-ctypes-tykind-fuzz.rs create mode 100644 tests/ui/lint/lint-ctypes-tykind-fuzz.stderr diff --git a/tests/ui/lint/lint-ctypes-tykind-fuzz.rs b/tests/ui/lint/lint-ctypes-tykind-fuzz.rs new file mode 100644 index 0000000000000..27bcfcf0e9a0c --- /dev/null +++ b/tests/ui/lint/lint-ctypes-tykind-fuzz.rs @@ -0,0 +1,311 @@ +// 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_ctypes_definitions)] + +// 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 + ){} +} + +#[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: &TemplateStruct + // Alias ...not Inherent. dangit +) -> TemplateStruct::Out { + 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 + // Alias + ) -> as std::ops::Add>>::Output { + self.clone() + self + } + extern "C" fn t2_ref( + // Ref[Param] + &self + // Alias + ) -> <&Self as std::ops::Add<&Self>>::Output { + self + self + } +} + +extern "C" {type ExtType;} + +#[repr(C)] +pub struct StructWithDyn(dyn std::fmt::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:!, + //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 (this gets caught outside of the code we want to test) +) -> 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, //~ ERROR: uses type `String` + // Ptr[Str] + s2: *const str, //~ ERROR: uses type `*const str` + // Ptr[Char] + c: *const char, //~ ERROR: uses type `char` + // Ptr[Slice] + s3: *const [u8], //~ ERROR: uses type `*const [u8]` + // deactivated here, because this is a function *declaration* (param N unacceptable) + // s4: *const [u8;N], + // Ptr[Tuple] + p: *const (u8,u8), //~ ERROR: uses type `(u8, u8)` + // deactivated here, because this is a function *declaration* (pattern unacceptable) + // (p2, p3):(*const u8, *const u8), + // Pat + nz: *const pattern_type!(u32 is 1..), //~ ERROR: uses type `(u32) is 1..=` + // deactivated here, because this is a function *declaration* (pattern unacceptable) + //SomeStruct{b: ref p4,..}: & SomeStruct, + // Ptr[Union] + u2: *const SomeUnion, + // Ptr[Enum], + e: *const SomeEnum, + // deactivated here, because this is a function *declaration* (impl type unacceptable) + //d: *const impl Clone, + // deactivated here, because this is a function *declaration* (type param unacceptable) + //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, //~ ERROR: uses type `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` + // deactivated here, because this is a function *declaration* (impl type unacceptable) + //a: *const impl async Fn(u8)->u8, + // Alias (this gets caught outside of the code we want to test) +) -> *const dyn std::fmt::Debug; //~ ERROR: uses type `*const dyn Debug` +} + +extern "C" fn all_ty_kinds_in_ref<'a, const N:usize,T>( + // 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]` + // Ref[Array] (this gets caught outside of the code we want to test) + s4: &[u8;N], + // Ref[Tuple] + p: &(u8, u8), + // also Tuple + (p2, p3):(&u8, &u8), //~ ERROR: uses type `(&u8, &u8)` + // Pat + nz: &pattern_type!(u32 is 1..), + // Ref[Struct] + SomeStruct{b: ref p4,..}: &SomeStruct, + // Ref[Union] + u2: &SomeUnion, + // Ref[Enum], + e: &SomeEnum, + // Ref[Param] + d: &impl Clone, + // Ref[Param] + 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` + // Ref[Param], + a: &impl async Fn(u8)->u8, + // Ref[Dynamic] (this gets caught outside of the code we want to test) +) -> &'a dyn std::fmt::Debug { //~ ERROR: uses type `&dyn Debug` + i +} + +extern "C" fn all_ty_kinds_in_box( + // Box[UInt], Box[Int], Box[Float], Box[Bool] + u: Box, i: Box, f: Box, b: Box, + // Box[Struct] + s: Box, + // Box[Str] + s2: Box, //~ ERROR: uses type `Box` + // Box[Char] + c: 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: Box<[u8;N]>, + // Box[Tuple] + p: Box<(u8,u8)>, + // also Tuple + (p2,p3):(Box, Box), //~ ERROR: uses type `(Box, Box)` + // Pat + nz: Box, + // Ref[Struct] + SomeStruct{b: ref p4,..}: &SomeStruct, + // Box[Union] + u2: Box, + // Box[Enum], + e: Box, + // Box[Param] + d: Box, + // Box[Param] + t: Box, + // Box[Foreign] + e2: Box, + // Box[Struct] + e3: Box, //~ ERROR: uses type `Box` + // Box[Never] + x: Box, + //r1: Box, + // Box[FnPtr] + f2: Boxu8>, + // Box[Dynamic] + f3: Boxu8>, //~ ERROR: uses type `Box u8>` + // Box[Dynamic] + d2: Box>, //~ ERROR: uses type `Box>` + // Box[Param], + a: Boxu8>, + // Box[Dynamic] (this gets caught outside of the code we want to test) +) -> Box { //~ ERROR: uses type `Box` + i +} + +fn main() {} diff --git a/tests/ui/lint/lint-ctypes-tykind-fuzz.stderr b/tests/ui/lint/lint-ctypes-tykind-fuzz.stderr new file mode 100644 index 0000000000000..e807d76bd5264 --- /dev/null +++ b/tests/ui/lint/lint-ctypes-tykind-fuzz.stderr @@ -0,0 +1,352 @@ +warning: the feature `inherent_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/lint-ctypes-tykind-fuzz.rs:10: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 `String`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:119:5 + | +LL | s:String, + | ^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the lint level is defined here + --> $DIR/lint-ctypes-tykind-fuzz.rs:5:25 + | +LL | #![deny(improper_ctypes,improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `&str`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:121: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-ctypes-tykind-fuzz.rs:123: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-ctypes-tykind-fuzz.rs:125: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-ctypes-tykind-fuzz.rs:127: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-ctypes-tykind-fuzz.rs:129: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-ctypes-tykind-fuzz.rs:131: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-ctypes-tykind-fuzz.rs:133:7 + | +LL | nz: pattern_type!(u32 is 1..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead + = note: pattern types have no C equivalent + +error: `extern` fn uses type `&StructWithDyn`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:147: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 `fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:152: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-ctypes-tykind-fuzz.rs:154: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-ctypes-tykind-fuzz.rs:156: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-ctypes-tykind-fuzz.rs:160:6 + | +LL | ) -> impl std::fmt::Debug { + | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: opaque types have no C equivalent + +error: `extern` block uses type `String`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:169:6 + | +LL | s: *const String, + | ^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference (`*const String`) is ABI-compatible with a C pointer, but `String` itself does not have a C layout + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the lint level is defined here + --> $DIR/lint-ctypes-tykind-fuzz.rs:5:9 + | +LL | #![deny(improper_ctypes,improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^ + +error: `extern` block uses type `*const str`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:171: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` block uses type `char`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:173:6 + | +LL | c: *const char, + | ^^^^^^^^^^^^ not FFI-safe + | + = note: this reference (`*const char`) is ABI-compatible with a C pointer, but `char` itself does not have a C layout + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `extern` block uses type `*const [u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:175: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` block uses type `(u8, u8)`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:179:6 + | +LL | p: *const (u8,u8), + | ^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference (`*const (u8, u8)`) is ABI-compatible with a C pointer, but `(u8, u8)` itself does not have a C layout + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` block uses type `(u32) is 1..=`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:183:7 + | +LL | nz: *const pattern_type!(u32 is 1..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference (`*const (u32) is 1..=`) is ABI-compatible with a C pointer, but `(u32) is 1..=` itself does not have a C layout + = help: consider using the base type instead + = note: pattern types have no C equivalent + +error: `extern` block uses type `*const StructWithDyn`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:197: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` block uses type `fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:202:7 + | +LL | f2: *const fn(u8)->u8, + | ^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this reference (`*const fn(u8) -> u8`) is ABI-compatible with a C pointer, but `fn(u8) -> u8` itself does not have a C layout + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` block uses type `*const dyn Fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:204: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` block uses type `*const dyn PartialOrd`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:206: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` block uses type `*const dyn Debug`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:210: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` fn uses type `&str`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:219: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 + +error: `extern` fn uses type `&[u8]`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:223: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` fn uses type `(&u8, &u8)`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:229: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 `&StructWithDyn`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:245: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 `&dyn Fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:252: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` fn uses type `&dyn PartialOrd`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:254: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 `&dyn Debug`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:258: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-ctypes-tykind-fuzz.rs:268: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<[u8]>`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:272: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-ctypes-tykind-fuzz.rs:278: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 `Box`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:294: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 u8>`, which is not FFI-safe + --> $DIR/lint-ctypes-tykind-fuzz.rs:301: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-ctypes-tykind-fuzz.rs:303: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-ctypes-tykind-fuzz.rs:307:6 + | +LL | ) -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer + +error: aborting due to 38 previous errors; 1 warning emitted + From bca037b87ddeb4230f7bffa7b5e4366463a25b95 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sun, 29 Dec 2024 20:32:34 +0100 Subject: [PATCH 05/15] lint ImproperCtypes: move code to new place, prior to full rewrite --- compiler/rustc_lint/src/foreign_modules.rs | 27 +- compiler/rustc_lint/src/types.rs | 1202 +--------------- .../rustc_lint/src/types/improper_ctypes.rs | 1205 ++++++++++++++++- .../ctypes.rs} | 0 .../ctypes.stderr} | 58 +- .../lint-113436-1.rs} | 0 .../lint-113436-1.stderr} | 10 +- .../lint-73249-2.rs} | 0 .../lint-73249-2.stderr} | 4 +- .../lint-73249-3.rs} | 0 .../lint-73249-3.stderr} | 4 +- .../lint-73249-5.rs} | 0 .../lint-73249-5.stderr} | 4 +- .../lint-73251-1.rs} | 0 .../lint-73251-1.stderr} | 4 +- .../lint-73251-2.rs} | 0 .../lint-73251-2.stderr} | 4 +- .../lint-94223.rs} | 0 .../lint-94223.stderr} | 34 +- .../lint-cstr.rs} | 0 .../lint-cstr.stderr} | 20 +- .../lint-enum.rs} | 1 - .../lint-enum.stderr} | 72 +- .../lint-fn.rs} | 0 .../lint-fn.stderr} | 46 +- .../lint-option-nonnull-unsized.rs} | 0 .../lint-option-nonnull-unsized.stderr} | 4 +- .../lint-tykind-fuzz.rs} | 0 .../lint-tykind-fuzz.stderr} | 82 +- .../mustpass-113436.rs} | 0 .../mustpass-113900.rs} | 0 .../mustpass-66202.rs} | 0 .../mustpass-73249-1.rs} | 0 .../mustpass-73249-4.rs} | 0 .../mustpass-73249.rs} | 0 .../mustpass-73251.rs} | 0 .../mustpass-73747.rs} | 0 .../mustpass-non-recursion-limit.rs} | 0 38 files changed, 1396 insertions(+), 1385 deletions(-) rename tests/ui/lint/{lint-ctypes.rs => improper_ctypes/ctypes.rs} (100%) rename tests/ui/lint/{lint-ctypes.stderr => improper_ctypes/ctypes.stderr} (89%) rename tests/ui/lint/{lint-ctypes-113436-1.rs => improper_ctypes/lint-113436-1.rs} (100%) rename tests/ui/lint/{lint-ctypes-113436-1.stderr => improper_ctypes/lint-113436-1.stderr} (82%) rename tests/ui/lint/{lint-ctypes-73249-2.rs => improper_ctypes/lint-73249-2.rs} (100%) rename tests/ui/lint/{lint-ctypes-73249-2.stderr => improper_ctypes/lint-73249-2.stderr} (85%) rename tests/ui/lint/{lint-ctypes-73249-3.rs => improper_ctypes/lint-73249-3.rs} (100%) rename tests/ui/lint/{lint-ctypes-73249-3.stderr => improper_ctypes/lint-73249-3.stderr} (81%) rename tests/ui/lint/{lint-ctypes-73249-5.rs => improper_ctypes/lint-73249-5.rs} (100%) rename tests/ui/lint/{lint-ctypes-73249-5.stderr => improper_ctypes/lint-73249-5.stderr} (81%) rename tests/ui/lint/{lint-ctypes-73251-1.rs => improper_ctypes/lint-73251-1.rs} (100%) rename tests/ui/lint/{lint-ctypes-73251-1.stderr => improper_ctypes/lint-73251-1.stderr} (82%) rename tests/ui/lint/{lint-ctypes-73251-2.rs => improper_ctypes/lint-73251-2.rs} (100%) rename tests/ui/lint/{lint-ctypes-73251-2.stderr => improper_ctypes/lint-73251-2.stderr} (82%) rename tests/ui/lint/{lint-ctypes-94223.rs => improper_ctypes/lint-94223.rs} (100%) rename tests/ui/lint/{lint-ctypes-94223.stderr => improper_ctypes/lint-94223.stderr} (89%) rename tests/ui/lint/{lint-ctypes-cstr.rs => improper_ctypes/lint-cstr.rs} (100%) rename tests/ui/lint/{lint-ctypes-cstr.stderr => improper_ctypes/lint-cstr.stderr} (90%) rename tests/ui/lint/{lint-ctypes-enum.rs => improper_ctypes/lint-enum.rs} (99%) rename tests/ui/lint/{lint-ctypes-enum.stderr => improper_ctypes/lint-enum.stderr} (89%) rename tests/ui/lint/{lint-ctypes-fn.rs => improper_ctypes/lint-fn.rs} (100%) rename tests/ui/lint/{lint-ctypes-fn.stderr => improper_ctypes/lint-fn.stderr} (88%) rename tests/ui/lint/{lint-ctypes-option-nonnull-unsized.rs => improper_ctypes/lint-option-nonnull-unsized.rs} (100%) rename tests/ui/lint/{lint-ctypes-option-nonnull-unsized.stderr => improper_ctypes/lint-option-nonnull-unsized.stderr} (83%) rename tests/ui/lint/{lint-ctypes-tykind-fuzz.rs => improper_ctypes/lint-tykind-fuzz.rs} (100%) rename tests/ui/lint/{lint-ctypes-tykind-fuzz.stderr => improper_ctypes/lint-tykind-fuzz.stderr} (86%) rename tests/ui/lint/{lint-ctypes-113436.rs => improper_ctypes/mustpass-113436.rs} (100%) rename tests/ui/lint/{lint-ctypes-113900.rs => improper_ctypes/mustpass-113900.rs} (100%) rename tests/ui/lint/{lint-ctypes-66202.rs => improper_ctypes/mustpass-66202.rs} (100%) rename tests/ui/lint/{lint-ctypes-73249-1.rs => improper_ctypes/mustpass-73249-1.rs} (100%) rename tests/ui/lint/{lint-ctypes-73249-4.rs => improper_ctypes/mustpass-73249-4.rs} (100%) rename tests/ui/lint/{lint-ctypes-73249.rs => improper_ctypes/mustpass-73249.rs} (100%) rename tests/ui/lint/{lint-ctypes-73251.rs => improper_ctypes/mustpass-73251.rs} (100%) rename tests/ui/lint/{lint-ctypes-73747.rs => improper_ctypes/mustpass-73747.rs} (100%) rename tests/ui/lint/{lint-ctypes-non-recursion-limit.rs => improper_ctypes/mustpass-non-recursion-limit.rs} (100%) diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index d0668794198ac..7e892dd7c413d 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) { 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) { b_inner == a } else { false diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 4e45a354cbb0b..8d4bed0f3635e 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,36 +1,27 @@ 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, 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, 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::{ImproperCTypesDeclarations, ImproperCTypesDefinitions}; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, - AtomicOrderingStore, ImproperCTypes, ImproperCTypesLayer, InvalidAtomicOrderingDiag, - InvalidNanComparisons, InvalidNanComparisonsSuggestion, - UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion, - UnusedComparisons, UsesPowerAlignment, VariantSizeDifferencesDiag, + AtomicOrderingStore, InvalidAtomicOrderingDiag, InvalidNanComparisons, + InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, + UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, + VariantSizeDifferencesDiag, }; -use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; +use crate::{LateContext, LateLintPass, LintContext}; mod literal; @@ -688,297 +679,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, - }, - FfiUnsafeWrapper { - ty: Ty<'tcx>, - reason: DiagMessage, - help: Option, - wrapped: Box>, - }, -} - -/// Determine if a type is sized or not, and wether 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; - } - - // FIXME: double-check: non-exhaustive structs from other crates are assumed to be ?Sized, right? - let is_non_exhaustive = - def.non_enum_variant().is_field_list_non_exhaustive(); - if is_non_exhaustive && !def.did().is_local() { - return TypeSizedness::NotYetKnown; - } - - 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) - // (also since !ty.is_sized(), we have at least one field) - let last_field_i = variant.fields.last_index().unwrap(); - let last_field = &variant.fields[last_field_i]; - let field_ty = last_field.ty(cx.tcx, args); - let field_ty = cx - .tcx - .try_normalize_erasing_regions(cx.typing_env(), field_ty) - .unwrap_or(field_ty); - 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 n_fields = tuple.len(); - let field_ty: Ty<'tcx> = tuple[n_fields - 1]; - //let field_ty = last_field.ty(cx.tcx, args); - let field_ty = cx - .tcx - .try_normalize_erasing_regions(cx.typing_env(), field_ty) - .unwrap_or(field_ty); - match get_type_sizedness(cx, field_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::Alias(ty::Weak, ..) - | 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), - } - } -} - pub(crate) fn nonnull_optimization_guaranteed<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, @@ -1006,7 +706,6 @@ 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); @@ -1029,10 +728,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, @@ -1143,7 +842,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>, - ckind: CItemKind, ) -> Option> { debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty); match ty.kind() { @@ -1168,7 +866,7 @@ pub(crate) fn repr_nullable_ptr<'tcx>( _ => return None, }; - if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) { + if !ty_is_known_nonnull(tcx, typing_env, field_ty) { return None; } @@ -1227,880 +925,6 @@ fn get_nullable_type_from_pat<'tcx>( } } -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.clone(), sp, vec![ImproperCTypesLayer { - ty, - note: fluent::lint_improper_ctypes_array_reason, - help: Some(fluent::lint_improper_ctypes_array_help), - inner_ty: None, - span_note: None, - }]); - 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, - } - - false - } else { - // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all - // `PhantomData`). - true - } - } 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, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false, - FfiPhantom(..) => true, - r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r, - } - } - - 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 - } - } - - /// Checks if the given indirection (box,ref,pointer) is "ffi-safe" - fn check_indirection_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, - ty: Ty<'tcx>, - inner_ty: Ty<'tcx>, - indirection_type: IndirectionType, - ) -> FfiResult<'tcx> { - use FfiResult::*; - let tcx = self.cx.tcx; - match get_type_sizedness(self.cx, inner_ty) { - TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => { - // there's a nuance on what this lint should do for - // function definitions (`extern "C" fn fn_name(...) {...}`) - // versus declarations (`unsafe extern "C" {fn fn_name(...);}`). - // This is touched upon in https://github.com/rust-lang/rust/issues/66220 - // and https://github.com/rust-lang/rust/pull/72700 - // - // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer - // (which has a stable layout) but points to FFI-unsafe type, is it safe? - // On one hand, the function's ABI will match that of a similar C-declared function API, - // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful. - // In this code, the opinion on is split between function declarations and function definitions, - // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type. - // For declarations, we see this as unsafe, but for definitions, we see this as safe. - // - // For extern function declarations, the actual definition of the function is written somewhere else, - // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) - // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, - // and having the full type information is necessary to compile the function. - if matches!(self.mode, CItemKind::Definition) { - return FfiSafe; - } else { - let inner_res = self.check_type_for_ffi(acc, inner_ty); - return match inner_res { - FfiSafe => inner_res, - _ => FfiUnsafeWrapper { - ty, - reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, - wrapped: Box::new(inner_res), - help: None, - }, - }; - } - } - 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 - // (FIXME note: function 'declarations' there should be treated as definitions) - // - 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) - // - // FIXME: on a side note, we should separate 'true' declarations (non-rust code), - // 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions. - // (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't) - - // wether 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. - return self.check_type_for_ffi(acc, 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), - ty::Adt(def, _) - if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) - && matches!( - tcx.get_diagnostic_name(def.did()), - Some(sym::cstring_type | sym::cstr_type) - ) - && !acc.base_ty.is_mutable_ptr() => - { - Some(fluent::lint_improper_ctypes_cstr_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, - }; - FfiUnsafe { ty, reason, help } - } - } - } - - /// 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(inner_ty) = ty.boxed_ty() { - return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Box); - } - 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, - }; - } - - 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; - } - - FfiSafe - } - } - } - - 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), - - // FIXME: this should probably be architecture-dependant - // same with some ty::Float variants. - 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, _) - if match ty.kind() { - ty::Tuple(tuple) => tuple.is_empty(), - _ => false, - } => - { - FfiSafe - } - - ty::RawPtr(inner_ty, _) => { - return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::RawPtr); - } - ty::Ref(_, inner_ty, _) => { - return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref); - } - - // having arrays as arguments / return values themselves is not FFI safe, - // but that is checked elsewhere - // if we reach this, we can assume the array is inside a struct, behind an indirection, etc. - 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), - }; - } - - 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 FfiUnsafeWrapper { - ty, - reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, - help: None, - wrapped: Box::new(r), - }; - } - } - } - - let ret_ty = sig.output(); - if ret_ty.is_unit() { - return FfiSafe; - } - - match self.check_type_for_ffi(acc, ret_ty) { - r @ (FfiSafe | FfiPhantom(_)) => r, - r => FfiUnsafeWrapper { - ty: ty.clone(), - reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, - help: None, - wrapped: Box::new(r), - }, - } - } - - 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, - mut reasons: Vec>, - ) { - 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", - }; - for reason in reasons.iter_mut() { - reason.span_note = if let ty::Adt(def, _) = reason.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, reasons }); - } - - 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) - } - } - } - - 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.clone(), sp, vec![ImproperCTypesLayer { - ty, - note: fluent::lint_improper_ctypes_opaque, - span_note: Some(sp), - help: None, - inner_ty: 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.clone(), sp, vec![ImproperCTypesLayer { - ty, - note: fluent::lint_improper_ctypes_only_phantomdata, - span_note: None, // filled later - help: None, - inner_ty: None, - }]); - } - FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer { - ty, - help, - note: reason, - span_note: None, // filled later - inner_ty: None, - }]); - } - ffir @ FfiResult::FfiUnsafeWrapper { .. } => { - let mut ffiresult_recursor = ControlFlow::Continue(&ffir); - let mut cimproper_layers: Vec> = vec![]; - - // this whole while block converts the arbitrarily-deep - // FfiResult stack to an ImproperCTypesLayer Vec - while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor { - match ffir_rec { - FfiResult::FfiPhantom(ty) => { - 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: None, - note: fluent::lint_improper_ctypes_only_phantomdata, - span_note: None, // filled later - }); - ffiresult_recursor = ControlFlow::Break(()); - } - FfiResult::FfiUnsafe { ty, reason, help } - | FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => { - 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: reason.clone(), - span_note: None, // filled later - }); - - if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec { - ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref()); - } else { - ffiresult_recursor = ControlFlow::Break(()); - } - } - FfiResult::FfiSafe => { - bug!("malformed FfiResult stack: it should be unsafe all the way down") - } - }; - } - // should always have at least one type - let last_ty = cimproper_layers.last().unwrap().ty.clone(); - self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers); - } - } - } - - /// 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) - } - } - - 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); - - 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); - } - hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), - } - } -} - -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() - { - 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); - } - } - } - } -} - -/// `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 `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); - } - } -} - declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]); impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 13afa540afcf6..c896dbfd0680e 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -1,10 +1,26 @@ +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 as hir; +use rustc_hir::AmbigArg; use rustc_hir::def::CtorKind; -use rustc_middle::ty; +use rustc_hir::intravisit::VisitorExt; +use rustc_middle::bug; +use rustc_middle::ty::{ + self, Adt, 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 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}; /// Check a variant of a non-exhaustive enum for improper ctypes /// @@ -41,3 +57,1188 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { // CtorKind::Const means a "unit" ctor !matches!(variant.ctor_kind(), Some(CtorKind::Const)) } + +#[derive(Clone, Copy)] +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, + }, + FfiUnsafeWrapper { + ty: Ty<'tcx>, + reason: DiagMessage, + help: Option, + wrapped: Box>, + }, +} + +/// Determine if a type is sized or not, and wether 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()) { + //let is_inh = ty.is_privately_uninhabited(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; + } + + // FIXME: double-check: non-exhaustive structs from other crates are assumed to be ?Sized, right? + let is_non_exhaustive = + def.non_enum_variant().is_field_list_non_exhaustive(); + if is_non_exhaustive && !def.did().is_local() { + return TypeSizedness::NotYetKnown; + } + + 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) + // (also since !ty.is_sized(), we have at least one field) + let last_field_i = variant.fields.last_index().unwrap(); + let last_field = &variant.fields[last_field_i]; + let field_ty = last_field.ty(cx.tcx, args); + let field_ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), field_ty) + .unwrap_or(field_ty); + 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 n_fields = tuple.len(); + let field_ty: Ty<'tcx> = tuple[n_fields - 1]; + let field_ty = cx + .tcx + .try_normalize_erasing_regions(cx.typing_env(), field_ty) + .unwrap_or(field_ty); + match get_type_sizedness(cx, field_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), + } + } +} + +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.clone(), + sp, + vec![ImproperCTypesLayer { + ty, + note: fluent::lint_improper_ctypes_array_reason, + help: Some(fluent::lint_improper_ctypes_array_help), + inner_ty: None, + span_note: None, + }], + ); + 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) = super::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, + } + + false + } else { + // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all + // `PhantomData`). + true + } + } 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, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false, + FfiPhantom(..) => true, + r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r, + } + } + + 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 + } + } + + /// Checks if the given indirection (box,ref,pointer) is "ffi-safe" + fn check_indirection_for_ffi( + &self, + acc: &mut CTypesVisitorState<'tcx>, + ty: Ty<'tcx>, + inner_ty: Ty<'tcx>, + indirection_type: IndirectionType, + ) -> FfiResult<'tcx> { + use FfiResult::*; + let tcx = self.cx.tcx; + match get_type_sizedness(self.cx, inner_ty) { + TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => { + // there's a nuance on what this lint should do for + // function definitions (`extern "C" fn fn_name(...) {...}`) + // versus declarations (`unsafe extern "C" {fn fn_name(...);}`). + // This is touched upon in https://github.com/rust-lang/rust/issues/66220 + // and https://github.com/rust-lang/rust/pull/72700 + // + // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer + // (which has a stable layout) but points to FFI-unsafe type, is it safe? + // On one hand, the function's ABI will match that of a similar C-declared function API, + // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful. + // In this code, the opinion on is split between function declarations and function definitions, + // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type. + // For declarations, we see this as unsafe, but for definitions, we see this as safe. + // + // For extern function declarations, the actual definition of the function is written somewhere else, + // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) + // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, + // and having the full type information is necessary to compile the function. + if matches!(self.mode, CItemKind::Definition) { + return FfiSafe; + } else { + let inner_res = self.check_type_for_ffi(acc, inner_ty); + return match inner_res { + FfiSafe => inner_res, + _ => FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + wrapped: Box::new(inner_res), + help: None, + }, + }; + } + } + 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 + // (FIXME note: function 'declarations' there should be treated as definitions) + // - 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) + // + // FIXME: on a side note, we should separate 'true' declarations (non-rust code), + // 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions. + // (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't) + + // wether 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. + return self.check_type_for_ffi(acc, 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), + ty::Adt(def, _) + if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) + && matches!( + tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + && !acc.base_ty.is_mutable_ptr() => + { + Some(fluent::lint_improper_ctypes_cstr_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, + }; + FfiUnsafe { ty, reason, help } + } + } + } + + /// 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(inner_ty) = ty.boxed_ty() { + return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Box); + } + 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) + { + 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, + }; + } + + 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; + } + + FfiSafe + } + } + } + + 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), + + // FIXME: this should probably be architecture-dependant + // same with some ty::Float variants. + 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, _) + if match ty.kind() { + ty::Tuple(tuple) => tuple.is_empty(), + _ => false, + } => + { + FfiSafe + } + + ty::RawPtr(inner_ty, _) => { + return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::RawPtr); + } + ty::Ref(_, inner_ty, _) => { + return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref); + } + + // having arrays as arguments / return values themselves is not FFI safe, + // but that is checked elsewhere + // if we reach this, we can assume the array is inside a struct, behind an indirection, etc. + 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), + }; + } + + 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 FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }; + } + } + } + + let ret_ty = sig.output(); + if ret_ty.is_unit() { + return FfiSafe; + } + + match self.check_type_for_ffi(acc, ret_ty) { + r @ (FfiSafe | FfiPhantom(_)) => r, + r => FfiUnsafeWrapper { + ty: ty.clone(), + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }, + } + } + + 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, + mut reasons: Vec>, + ) { + 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", + }; + for reason in reasons.iter_mut() { + reason.span_note = if let ty::Adt(def, _) = reason.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, reasons }); + } + + 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) + } + } + } + + 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.clone(), + sp, + vec![ImproperCTypesLayer { + ty, + note: fluent::lint_improper_ctypes_opaque, + span_note: Some(sp), + help: None, + inner_ty: 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.clone(), + sp, + vec![ImproperCTypesLayer { + ty, + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + help: None, + inner_ty: None, + }], + ); + } + FfiResult::FfiUnsafe { ty, reason, help } => { + self.emit_ffi_unsafe_type_lint( + ty.clone(), + sp, + vec![ImproperCTypesLayer { + ty, + help, + note: reason, + span_note: None, // filled later + inner_ty: None, + }], + ); + } + ffir @ FfiResult::FfiUnsafeWrapper { .. } => { + let mut ffiresult_recursor = ControlFlow::Continue(&ffir); + let mut cimproper_layers: Vec> = vec![]; + + // this whole while block converts the arbitrarily-deep + // FfiResult stack to an ImproperCTypesLayer Vec + while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor { + match ffir_rec { + FfiResult::FfiPhantom(ty) => { + 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: None, + note: fluent::lint_improper_ctypes_only_phantomdata, + span_note: None, // filled later + }); + ffiresult_recursor = ControlFlow::Break(()); + } + FfiResult::FfiUnsafe { ty, reason, help } + | FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => { + 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: reason.clone(), + span_note: None, // filled later + }); + + if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec { + ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref()); + } else { + ffiresult_recursor = ControlFlow::Break(()); + } + } + FfiResult::FfiSafe => { + bug!("malformed FfiResult stack: it should be unsafe all the way down") + } + }; + } + // should always have at least one type + let last_ty = cimproper_layers.last().unwrap().ty.clone(); + self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers); + } + } + } + + /// 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) + } + } + + 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); + + 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); + } + hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), + } + } +} + +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() + { + 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, + ); + } + } + } + } +} + +/// `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 `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); + } + } +} + +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]); diff --git a/tests/ui/lint/lint-ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs similarity index 100% rename from tests/ui/lint/lint-ctypes.rs rename to tests/ui/lint/improper_ctypes/ctypes.rs diff --git a/tests/ui/lint/lint-ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr similarity index 89% rename from tests/ui/lint/lint-ctypes.stderr rename to tests/ui/lint/improper_ctypes/ctypes.stderr index 9ec17b1d94b32..3a91b78a7f434 100644 --- a/tests/ui/lint/lint-ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:60:28 + --> $DIR/ctypes.rs:60:28 | LL | pub fn ptr_type1(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -8,18 +8,18 @@ LL | pub fn ptr_type1(size: *const Foo); = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes.rs:28:1 + --> $DIR/ctypes.rs:28:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/lint-ctypes.rs:5:9 + --> $DIR/ctypes.rs:5:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:61:28 + --> $DIR/ctypes.rs:61:28 | LL | pub fn ptr_type2(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -28,13 +28,13 @@ LL | pub fn ptr_type2(size: *const Foo); = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/lint-ctypes.rs:28:1 + --> $DIR/ctypes.rs:28:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^ error: `extern` block uses type `((),)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:63:25 + --> $DIR/ctypes.rs:63:25 | LL | pub fn ptr_tuple(p: *const ((),)); | ^^^^^^^^^^^^ not FFI-safe @@ -44,7 +44,7 @@ LL | pub fn ptr_tuple(p: *const ((),)); = note: tuples have unspecified layout error: `extern` block uses type `&[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:64:26 + --> $DIR/ctypes.rs:64:26 | LL | pub fn slice_type(p: &[u32]); | ^^^^^^ not FFI-safe @@ -53,7 +53,7 @@ LL | pub fn slice_type(p: &[u32]); = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:65:24 + --> $DIR/ctypes.rs:65:24 | LL | pub fn str_type(p: &str); | ^^^^ not FFI-safe @@ -62,7 +62,7 @@ LL | pub fn str_type(p: &str); = 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/lint-ctypes.rs:68:25 + --> $DIR/ctypes.rs:68:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -71,7 +71,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:69:25 + --> $DIR/ctypes.rs:69:25 | LL | pub fn i128_type(p: i128); | ^^^^ not FFI-safe @@ -79,7 +79,7 @@ LL | pub fn i128_type(p: i128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:70:25 + --> $DIR/ctypes.rs:70:25 | LL | pub fn u128_type(p: u128); | ^^^^ not FFI-safe @@ -87,7 +87,7 @@ LL | pub fn u128_type(p: u128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `&dyn Bar`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:71:26 + --> $DIR/ctypes.rs:71:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -95,7 +95,7 @@ LL | pub fn trait_type(p: &dyn Bar); = 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/lint-ctypes.rs:72:26 + --> $DIR/ctypes.rs:72:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -104,7 +104,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:73:27 + --> $DIR/ctypes.rs:73:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -113,7 +113,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:74:25 + --> $DIR/ctypes.rs:74:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -121,26 +121,26 @@ LL | pub fn zero_size(p: ZeroSize); = help: consider adding a member to this struct = note: this struct has no fields note: the type is defined here - --> $DIR/lint-ctypes.rs:24:1 + --> $DIR/ctypes.rs:24:1 | LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:75:33 + --> $DIR/ctypes.rs:75: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:57:1 + --> $DIR/ctypes.rs:57:1 | LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:78:12 + --> $DIR/ctypes.rs:78:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -148,7 +148,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:79:23 + --> $DIR/ctypes.rs:79:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -157,7 +157,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:80:24 + --> $DIR/ctypes.rs:80:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -166,7 +166,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:82:32 + --> $DIR/ctypes.rs:82:32 | LL | pub fn transparent_i128(p: TransparentI128); | ^^^^^^^^^^^^^^^ not FFI-safe @@ -174,7 +174,7 @@ LL | pub fn transparent_i128(p: TransparentI128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:83:31 + --> $DIR/ctypes.rs:83:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -183,7 +183,7 @@ LL | pub fn transparent_str(p: TransparentStr); = 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/lint-ctypes.rs:85:27 + --> $DIR/ctypes.rs:85:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -192,7 +192,7 @@ LL | pub fn raw_array(arr: [u8; 8]); = note: passing raw arrays by value is not FFI-safe error: `extern` block uses type `&UnsizedStructBecauseDyn`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:88:47 + --> $DIR/ctypes.rs:88:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -200,7 +200,7 @@ LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); = 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/lint-ctypes.rs:90:26 + --> $DIR/ctypes.rs:90:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -209,7 +209,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:92:26 + --> $DIR/ctypes.rs:92:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -218,7 +218,7 @@ LL | pub fn no_niche_b(b: Option>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:95:34 + --> $DIR/ctypes.rs:95:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -226,7 +226,7 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:96:40 + --> $DIR/ctypes.rs:96:40 | LL | pub static static_u128_array_type: [u128; 16]; | ^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-113436-1.rs b/tests/ui/lint/improper_ctypes/lint-113436-1.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-113436-1.rs rename to tests/ui/lint/improper_ctypes/lint-113436-1.rs diff --git a/tests/ui/lint/lint-ctypes-113436-1.stderr b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr similarity index 82% rename from tests/ui/lint/lint-ctypes-113436-1.stderr rename to tests/ui/lint/improper_ctypes/lint-113436-1.stderr index 7b63043f05756..f01dc3b6e0d1e 100644 --- a/tests/ui/lint/lint-ctypes-113436-1.stderr +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `NotSafe`, which is not FFI-safe - --> $DIR/lint-ctypes-113436-1.rs:22:22 + --> $DIR/lint-113436-1.rs:22:22 | LL | extern "C" fn bar(x: Bar) -> Bar { | ^^^ not FFI-safe @@ -7,18 +7,18 @@ LL | extern "C" fn bar(x: Bar) -> Bar { = 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 + --> $DIR/lint-113436-1.rs:13:1 | LL | struct NotSafe(u32); | ^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/lint-ctypes-113436-1.rs:1:9 + --> $DIR/lint-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 + --> $DIR/lint-113436-1.rs:22:30 | LL | extern "C" fn bar(x: Bar) -> Bar { | ^^^ not FFI-safe @@ -26,7 +26,7 @@ LL | extern "C" fn bar(x: Bar) -> Bar { = 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 + --> $DIR/lint-113436-1.rs:13:1 | LL | struct NotSafe(u32); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73249-2.rs b/tests/ui/lint/improper_ctypes/lint-73249-2.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73249-2.rs rename to tests/ui/lint/improper_ctypes/lint-73249-2.rs diff --git a/tests/ui/lint/lint-ctypes-73249-2.stderr b/tests/ui/lint/improper_ctypes/lint-73249-2.stderr similarity index 85% rename from tests/ui/lint/lint-ctypes-73249-2.stderr rename to tests/ui/lint/improper_ctypes/lint-73249-2.stderr index 0be04824f3936..418cf43764e12 100644 --- a/tests/ui/lint/lint-ctypes-73249-2.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-2.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-2.rs:27:21 + --> $DIR/lint-73249-2.rs:27:21 | LL | fn lint_me() -> A<()>; | ^^^^^ not FFI-safe @@ -7,7 +7,7 @@ LL | fn lint_me() -> A<()>; = note: this reference (`&Qux`) is ABI-compatible with a C pointer, but `Qux` itself does not have a C layout = note: opaque types have no C equivalent note: the lint level is defined here - --> $DIR/lint-ctypes-73249-2.rs:2:9 + --> $DIR/lint-73249-2.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73249-3.rs b/tests/ui/lint/improper_ctypes/lint-73249-3.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73249-3.rs rename to tests/ui/lint/improper_ctypes/lint-73249-3.rs diff --git a/tests/ui/lint/lint-ctypes-73249-3.stderr b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr similarity index 81% rename from tests/ui/lint/lint-ctypes-73249-3.stderr rename to tests/ui/lint/improper_ctypes/lint-73249-3.stderr index e1a313a290651..ebc9eb5eb8274 100644 --- a/tests/ui/lint/lint-ctypes-73249-3.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-3.rs:21:25 + --> $DIR/lint-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 + --> $DIR/lint-73249-3.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-73249-5.rs b/tests/ui/lint/improper_ctypes/lint-73249-5.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-73249-5.rs rename to tests/ui/lint/improper_ctypes/lint-73249-5.rs diff --git a/tests/ui/lint/lint-ctypes-73249-5.stderr b/tests/ui/lint/improper_ctypes/lint-73249-5.stderr similarity index 81% rename from tests/ui/lint/lint-ctypes-73249-5.stderr rename to tests/ui/lint/improper_ctypes/lint-73249-5.stderr index c4fa955de05ed..484927f57fead 100644 --- a/tests/ui/lint/lint-ctypes-73249-5.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-5.stderr @@ -1,12 +1,12 @@ error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-ctypes-73249-5.rs:21:25 + --> $DIR/lint-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 + --> $DIR/lint-73249-5.rs:2:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ 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/lint-ctypes-94223.rs b/tests/ui/lint/improper_ctypes/lint-94223.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-94223.rs rename to tests/ui/lint/improper_ctypes/lint-94223.rs diff --git a/tests/ui/lint/lint-ctypes-94223.stderr b/tests/ui/lint/improper_ctypes/lint-94223.stderr similarity index 89% rename from tests/ui/lint/lint-ctypes-94223.stderr rename to tests/ui/lint/improper_ctypes/lint-94223.stderr index 4bebca69b7f3b..ce657e80dd25a 100644 --- a/tests/ui/lint/lint-ctypes-94223.stderr +++ b/tests/ui/lint/improper_ctypes/lint-94223.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:4:15 + --> $DIR/lint-94223.rs:4:15 | LL | pub fn bad(f: extern "C" fn([u8])) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -8,13 +8,13 @@ LL | pub fn bad(f: extern "C" fn([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-ctypes-94223.rs:2:9 + --> $DIR/lint-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 + --> $DIR/lint-94223.rs:7:28 | LL | pub fn bad_twice(f: Result) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -24,7 +24,7 @@ LL | pub fn bad_twice(f: Result) {} = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:7:49 + --> $DIR/lint-94223.rs:7:49 | LL | pub fn bad_twice(f: Result) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -34,7 +34,7 @@ LL | pub fn bad_twice(f: Result) {} = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:11:18 + --> $DIR/lint-94223.rs:11:18 | LL | struct BadStruct(extern "C" fn([u8])); | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -44,7 +44,7 @@ LL | struct BadStruct(extern "C" fn([u8])); = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:15:7 + --> $DIR/lint-94223.rs:15:7 | LL | A(extern "C" fn([u8])), | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -54,7 +54,7 @@ LL | A(extern "C" fn([u8])), = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:20:7 + --> $DIR/lint-94223.rs:20:7 | LL | A(extern "C" fn([u8])), | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -64,7 +64,7 @@ LL | A(extern "C" fn([u8])), = note: slices have no C equivalent error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:24:12 + --> $DIR/lint-94223.rs:24:12 | LL | type Foo = extern "C" fn([u8]); | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -74,7 +74,7 @@ LL | type Foo = extern "C" fn([u8]); = 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 + --> $DIR/lint-94223.rs:31:20 | LL | pub type Foo2 = extern "C" fn(Option<&::FooType>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -84,7 +84,7 @@ LL | pub type Foo2 = extern "C" fn(Option<&::FooType>); = note: enum has no representation hint error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-ctypes-94223.rs:41:17 + --> $DIR/lint-94223.rs:41:17 | LL | pub static BAD: extern "C" fn(FfiUnsafe) = f; | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -93,13 +93,13 @@ LL | pub static BAD: extern "C" fn(FfiUnsafe) = f; = 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 + --> $DIR/lint-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 + --> $DIR/lint-94223.rs:44:30 | LL | pub static BAD_TWICE: Result = Ok(f); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -108,13 +108,13 @@ LL | pub static BAD_TWICE: Result $DIR/lint-ctypes-94223.rs:34:1 + --> $DIR/lint-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 + --> $DIR/lint-94223.rs:44:56 | LL | pub static BAD_TWICE: Result = Ok(f); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,13 +123,13 @@ LL | pub static BAD_TWICE: Result $DIR/lint-ctypes-94223.rs:34:1 + --> $DIR/lint-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 + --> $DIR/lint-94223.rs:48:22 | LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -138,7 +138,7 @@ LL | pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f; = 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 + --> $DIR/lint-94223.rs:34:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-cstr.rs b/tests/ui/lint/improper_ctypes/lint-cstr.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-cstr.rs rename to tests/ui/lint/improper_ctypes/lint-cstr.rs diff --git a/tests/ui/lint/lint-ctypes-cstr.stderr b/tests/ui/lint/improper_ctypes/lint-cstr.stderr similarity index 90% rename from tests/ui/lint/lint-ctypes-cstr.stderr rename to tests/ui/lint/improper_ctypes/lint-cstr.stderr index da15b748f2110..a50d5db4aacc1 100644 --- a/tests/ui/lint/lint-ctypes-cstr.stderr +++ b/tests/ui/lint/improper_ctypes/lint-cstr.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `CStr`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:7:21 + --> $DIR/lint-cstr.rs:7:21 | LL | fn take_cstr(s: CStr); | ^^^^ not FFI-safe @@ -7,13 +7,13 @@ LL | fn take_cstr(s: CStr); = 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 + --> $DIR/lint-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 + --> $DIR/lint-cstr.rs:10:25 | LL | fn take_cstr_ref(s: &CStr); | ^^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | fn take_cstr_ref(s: &CStr); = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:13:24 + --> $DIR/lint-cstr.rs:13:24 | LL | fn take_cstring(s: CString); | ^^^^^^^ not FFI-safe @@ -31,7 +31,7 @@ LL | fn take_cstring(s: CString); = 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 + --> $DIR/lint-cstr.rs:16:28 | LL | fn take_cstring_ref(s: &CString); | ^^^^^^^^ not FFI-safe @@ -41,7 +41,7 @@ LL | fn take_cstring_ref(s: &CString); = 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 + --> $DIR/lint-cstr.rs:20:43 | LL | fn no_special_help_for_mut_cstring(s: *mut CString); | ^^^^^^^^^^^^ not FFI-safe @@ -51,7 +51,7 @@ LL | fn no_special_help_for_mut_cstring(s: *mut CString); = note: this struct has unspecified layout error: `extern` block uses type `CString`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:24:47 + --> $DIR/lint-cstr.rs:24:47 | LL | fn no_special_help_for_mut_cstring_ref(s: &mut CString); | ^^^^^^^^^^^^ not FFI-safe @@ -61,7 +61,7 @@ LL | fn no_special_help_for_mut_cstring_ref(s: &mut CString); = note: this struct has unspecified layout error: `extern` fn uses type `&CStr`, which is not FFI-safe - --> $DIR/lint-ctypes-cstr.rs:29:37 + --> $DIR/lint-cstr.rs:29:37 | LL | extern "C" fn rust_take_cstr_ref(s: &CStr) {} | ^^^^^ not FFI-safe @@ -69,13 +69,13 @@ LL | extern "C" fn rust_take_cstr_ref(s: &CStr) {} = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` = 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-ctypes-cstr.rs:2:26 + --> $DIR/lint-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 + --> $DIR/lint-cstr.rs:32:36 | LL | extern "C" fn rust_take_cstring(s: CString) {} | ^^^^^^^ not FFI-safe 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..3b57433fca153 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); diff --git a/tests/ui/lint/lint-ctypes-enum.stderr b/tests/ui/lint/improper_ctypes/lint-enum.stderr similarity index 89% rename from tests/ui/lint/lint-ctypes-enum.stderr rename to tests/ui/lint/improper_ctypes/lint-enum.stderr index d5fc844f75607..0a834d9943f39 100644 --- a/tests/ui/lint/lint-ctypes-enum.stderr +++ b/tests/ui/lint/improper_ctypes/lint-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:84:14 + --> $DIR/lint-enum.rs:84:14 | LL | fn uf(x: U); | ^ not FFI-safe @@ -7,18 +7,18 @@ 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 + --> $DIR/lint-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:85:14 | LL | fn bf(x: B); | ^ not FFI-safe @@ -26,13 +26,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:86:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,39 +40,38 @@ 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:90: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:91: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:100:31 | LL | fn option_nonzero_u128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +79,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:107:31 | LL | fn option_nonzero_i128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -88,7 +87,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:112:36 | LL | fn option_transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -97,7 +96,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:114:28 | LL | fn option_repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -106,16 +105,15 @@ 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:115:21 | LL | fn option_u8(x: 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-enum.rs:125:33 + --> $DIR/lint-enum.rs:125:33 | LL | fn result_nonzero_u128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -123,7 +121,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:132:33 | LL | fn result_nonzero_i128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -131,7 +129,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:137:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -140,7 +138,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:139:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -149,7 +147,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:143:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -158,7 +156,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:145:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -167,7 +165,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:147:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +174,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:150:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -185,7 +183,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:152:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -194,7 +192,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:163:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -202,7 +200,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:170:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -210,7 +208,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:175:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -219,7 +217,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:177:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -228,7 +226,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:181:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -237,7 +235,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:183:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -246,7 +244,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:185:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -255,7 +253,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:188:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -264,7 +262,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:190:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -273,7 +271,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:192:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-fn.rs rename to tests/ui/lint/improper_ctypes/lint-fn.rs diff --git a/tests/ui/lint/lint-ctypes-fn.stderr b/tests/ui/lint/improper_ctypes/lint-fn.stderr similarity index 88% rename from tests/ui/lint/lint-ctypes-fn.stderr rename to tests/ui/lint/improper_ctypes/lint-fn.stderr index c86c02c80064c..a14c6c6108ece 100644 --- a/tests/ui/lint/lint-ctypes-fn.stderr +++ b/tests/ui/lint/improper_ctypes/lint-fn.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `&[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:70:33 + --> $DIR/lint-fn.rs:70:33 | LL | pub extern "C" fn slice_type(p: &[u32]) { } | ^^^^^^ not FFI-safe @@ -7,13 +7,13 @@ LL | pub extern "C" fn slice_type(p: &[u32]) { } = 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-ctypes-fn.rs:2:9 + --> $DIR/lint-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 + --> $DIR/lint-fn.rs:73:31 | LL | pub extern "C" fn str_type(p: &str) { } | ^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | pub extern "C" fn str_type(p: &str) { } = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:80:34 + --> $DIR/lint-fn.rs:80:34 | LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } | ^^^^^^^^^ not FFI-safe @@ -31,7 +31,7 @@ LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } = 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-ctypes-fn.rs:83:35 + --> $DIR/lint-fn.rs:83:35 | LL | pub extern "C" fn boxed_string(p: Box) { } | ^^^^^^^^ not FFI-safe @@ -40,7 +40,7 @@ LL | pub extern "C" fn boxed_string(p: Box) { } = 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-ctypes-fn.rs:86:34 + --> $DIR/lint-fn.rs:86:34 | LL | pub extern "C" fn boxed_trait(p: Box) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -48,7 +48,7 @@ LL | pub extern "C" fn boxed_trait(p: Box) { } = 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-ctypes-fn.rs:89:32 + --> $DIR/lint-fn.rs:89:32 | LL | pub extern "C" fn char_type(p: char) { } | ^^^^ not FFI-safe @@ -57,7 +57,7 @@ LL | pub extern "C" fn char_type(p: char) { } = 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 + --> $DIR/lint-fn.rs:92:32 | LL | pub extern "C" fn i128_type(p: i128) { } | ^^^^ not FFI-safe @@ -65,7 +65,7 @@ LL | pub extern "C" fn i128_type(p: i128) { } = 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 + --> $DIR/lint-fn.rs:95:32 | LL | pub extern "C" fn u128_type(p: u128) { } | ^^^^ not FFI-safe @@ -73,7 +73,7 @@ LL | pub extern "C" fn u128_type(p: u128) { } = 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 + --> $DIR/lint-fn.rs:98:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -82,7 +82,7 @@ LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } = note: tuples have unspecified layout error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:101:34 + --> $DIR/lint-fn.rs:101:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -91,7 +91,7 @@ LL | pub extern "C" fn tuple_type2(p: I32Pair) { } = note: tuples have unspecified layout error: `extern` fn uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:104:32 + --> $DIR/lint-fn.rs:104:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -99,26 +99,26 @@ LL | pub extern "C" fn zero_size(p: ZeroSize) { } = help: consider adding a member to this struct = note: this struct has no fields note: the type is defined here - --> $DIR/lint-ctypes-fn.rs:25:1 + --> $DIR/lint-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 + --> $DIR/lint-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 + --> $DIR/lint-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 + --> $DIR/lint-fn.rs:110:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -126,7 +126,7 @@ LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:115:30 + --> $DIR/lint-fn.rs:115:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -135,7 +135,7 @@ LL | pub extern "C" fn fn_type(p: RustFn) { } = 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 + --> $DIR/lint-fn.rs:118:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -144,7 +144,7 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = 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 + --> $DIR/lint-fn.rs:123:39 | LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } | ^^^^^^^^^^^^^^^ not FFI-safe @@ -152,7 +152,7 @@ LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } = 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 + --> $DIR/lint-fn.rs:126:38 | LL | pub extern "C" fn transparent_str(p: TransparentStr) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -161,7 +161,7 @@ LL | pub extern "C" fn transparent_str(p: TransparentStr) { } = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:172:43 + --> $DIR/lint-fn.rs:172:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -169,7 +169,7 @@ LL | pub extern "C" fn unused_generic2() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:185:39 + --> $DIR/lint-fn.rs:185:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -178,7 +178,7 @@ LL | pub extern "C" fn used_generic4(x: Vec) { } = note: this struct has unspecified layout error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:188:41 + --> $DIR/lint-fn.rs:188:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe 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 100% rename from tests/ui/lint/lint-ctypes-option-nonnull-unsized.rs rename to tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.rs 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 83% 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..b17fb6bd6145d 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,7 +7,7 @@ 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)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/lint-ctypes-tykind-fuzz.rs b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-tykind-fuzz.rs rename to tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs diff --git a/tests/ui/lint/lint-ctypes-tykind-fuzz.stderr b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr similarity index 86% rename from tests/ui/lint/lint-ctypes-tykind-fuzz.stderr rename to tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr index e807d76bd5264..411086159f615 100644 --- a/tests/ui/lint/lint-ctypes-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -1,5 +1,5 @@ warning: the feature `inherent_associated_types` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/lint-ctypes-tykind-fuzz.rs:10:12 + --> $DIR/lint-tykind-fuzz.rs:10:12 | LL | #![feature(inherent_associated_types)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | #![feature(inherent_associated_types)] = note: `#[warn(incomplete_features)]` on by default error: `extern` fn uses type `String`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:119:5 + --> $DIR/lint-tykind-fuzz.rs:119:5 | LL | s:String, | ^^^^^^ not FFI-safe @@ -16,13 +16,13 @@ LL | s:String, = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the lint level is defined here - --> $DIR/lint-ctypes-tykind-fuzz.rs:5:25 + --> $DIR/lint-tykind-fuzz.rs:5:25 | LL | #![deny(improper_ctypes,improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `&str`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:121:6 + --> $DIR/lint-tykind-fuzz.rs:121:6 | LL | s2:&str, | ^^^^ not FFI-safe @@ -31,7 +31,7 @@ LL | s2:&str, = 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-ctypes-tykind-fuzz.rs:123:6 + --> $DIR/lint-tykind-fuzz.rs:123:6 | LL | c: char, | ^^^^ not FFI-safe @@ -40,7 +40,7 @@ LL | c: char, = note: the `char` type has no C equivalent error: `extern` fn uses type `&[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:125:6 + --> $DIR/lint-tykind-fuzz.rs:125:6 | LL | s3:&[u8], | ^^^^^ not FFI-safe @@ -49,7 +49,7 @@ LL | s3:&[u8], = 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-ctypes-tykind-fuzz.rs:127:6 + --> $DIR/lint-tykind-fuzz.rs:127:6 | LL | s4:[u8;N], | ^^^^^^ not FFI-safe @@ -58,7 +58,7 @@ LL | s4:[u8;N], = note: passing raw arrays by value is not FFI-safe error: `extern` fn uses type `(u8, u8)`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:129:5 + --> $DIR/lint-tykind-fuzz.rs:129:5 | LL | p:(u8, u8), | ^^^^^^^^ not FFI-safe @@ -67,7 +67,7 @@ LL | p:(u8, u8), = note: tuples have unspecified layout error: `extern` fn uses type `(u8, u8)`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:131:12 + --> $DIR/lint-tykind-fuzz.rs:131:12 | LL | (p2, p3):(u8, u8), | ^^^^^^^^ not FFI-safe @@ -76,7 +76,7 @@ LL | (p2, p3):(u8, u8), = note: tuples have unspecified layout error: `extern` fn uses type `(u32) is 1..=`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:133:7 + --> $DIR/lint-tykind-fuzz.rs:133:7 | LL | nz: pattern_type!(u32 is 1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -85,7 +85,7 @@ LL | nz: pattern_type!(u32 is 1..), = note: pattern types have no C equivalent error: `extern` fn uses type `&StructWithDyn`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:147:7 + --> $DIR/lint-tykind-fuzz.rs:147:7 | LL | e3: &StructWithDyn, | ^^^^^^^^^^^^^^ not FFI-safe @@ -93,7 +93,7 @@ LL | e3: &StructWithDyn, = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:152:7 + --> $DIR/lint-tykind-fuzz.rs:152:7 | LL | f2: fn(u8)->u8, | ^^^^^^^^^^ not FFI-safe @@ -102,7 +102,7 @@ LL | f2: fn(u8)->u8, = 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-ctypes-tykind-fuzz.rs:154:7 + --> $DIR/lint-tykind-fuzz.rs:154:7 | LL | f3: &'a dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -110,7 +110,7 @@ LL | f3: &'a dyn Fn(u8)->u8, = 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-ctypes-tykind-fuzz.rs:156:7 + --> $DIR/lint-tykind-fuzz.rs:156:7 | LL | d2: &dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -118,7 +118,7 @@ LL | d2: &dyn std::cmp::PartialOrd, = 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-ctypes-tykind-fuzz.rs:160:6 + --> $DIR/lint-tykind-fuzz.rs:160:6 | LL | ) -> impl std::fmt::Debug { | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -126,7 +126,7 @@ LL | ) -> impl std::fmt::Debug { = note: opaque types have no C equivalent error: `extern` block uses type `String`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:169:6 + --> $DIR/lint-tykind-fuzz.rs:169:6 | LL | s: *const String, | ^^^^^^^^^^^^^ not FFI-safe @@ -135,13 +135,13 @@ LL | s: *const String, = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the lint level is defined here - --> $DIR/lint-ctypes-tykind-fuzz.rs:5:9 + --> $DIR/lint-tykind-fuzz.rs:5:9 | LL | #![deny(improper_ctypes,improper_ctypes_definitions)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `*const str`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:171:7 + --> $DIR/lint-tykind-fuzz.rs:171:7 | LL | s2: *const str, | ^^^^^^^^^^ not FFI-safe @@ -150,7 +150,7 @@ LL | s2: *const str, = note: this pointer 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/lint-ctypes-tykind-fuzz.rs:173:6 + --> $DIR/lint-tykind-fuzz.rs:173:6 | LL | c: *const char, | ^^^^^^^^^^^^ not FFI-safe @@ -160,7 +160,7 @@ LL | c: *const char, = note: the `char` type has no C equivalent error: `extern` block uses type `*const [u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:175:7 + --> $DIR/lint-tykind-fuzz.rs:175:7 | LL | s3: *const [u8], | ^^^^^^^^^^^ not FFI-safe @@ -169,7 +169,7 @@ LL | s3: *const [u8], = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` block uses type `(u8, u8)`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:179:6 + --> $DIR/lint-tykind-fuzz.rs:179:6 | LL | p: *const (u8,u8), | ^^^^^^^^^^^^^^ not FFI-safe @@ -179,7 +179,7 @@ LL | p: *const (u8,u8), = note: tuples have unspecified layout error: `extern` block uses type `(u32) is 1..=`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:183:7 + --> $DIR/lint-tykind-fuzz.rs:183:7 | LL | nz: *const pattern_type!(u32 is 1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -189,7 +189,7 @@ LL | nz: *const pattern_type!(u32 is 1..), = note: pattern types have no C equivalent error: `extern` block uses type `*const StructWithDyn`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:197:7 + --> $DIR/lint-tykind-fuzz.rs:197:7 | LL | e3: *const StructWithDyn, | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -197,7 +197,7 @@ LL | e3: *const StructWithDyn, = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` block uses type `fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:202:7 + --> $DIR/lint-tykind-fuzz.rs:202:7 | LL | f2: *const fn(u8)->u8, | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -207,7 +207,7 @@ LL | f2: *const fn(u8)->u8, = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `*const dyn Fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:204:7 + --> $DIR/lint-tykind-fuzz.rs:204:7 | LL | f3: *const dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -215,7 +215,7 @@ LL | f3: *const dyn Fn(u8)->u8, = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` block uses type `*const dyn PartialOrd`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:206:7 + --> $DIR/lint-tykind-fuzz.rs:206:7 | LL | d2: *const dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -223,7 +223,7 @@ LL | d2: *const dyn std::cmp::PartialOrd, = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` block uses type `*const dyn Debug`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:210:6 + --> $DIR/lint-tykind-fuzz.rs:210:6 | LL | ) -> *const dyn std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -231,7 +231,7 @@ LL | ) -> *const dyn std::fmt::Debug; = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `&str`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:219:7 + --> $DIR/lint-tykind-fuzz.rs:219:7 | LL | s2: &str, | ^^^^ not FFI-safe @@ -240,7 +240,7 @@ LL | s2: &str, = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `&[u8]`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:223:7 + --> $DIR/lint-tykind-fuzz.rs:223:7 | LL | s3: &[u8], | ^^^^^ not FFI-safe @@ -249,7 +249,7 @@ LL | s3: &[u8], = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `(&u8, &u8)`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:229:12 + --> $DIR/lint-tykind-fuzz.rs:229:12 | LL | (p2, p3):(&u8, &u8), | ^^^^^^^^^^ not FFI-safe @@ -258,7 +258,7 @@ LL | (p2, p3):(&u8, &u8), = note: tuples have unspecified layout error: `extern` fn uses type `&StructWithDyn`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:245:7 + --> $DIR/lint-tykind-fuzz.rs:245:7 | LL | e3: &StructWithDyn, | ^^^^^^^^^^^^^^ not FFI-safe @@ -266,7 +266,7 @@ LL | e3: &StructWithDyn, = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `&dyn Fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:252:7 + --> $DIR/lint-tykind-fuzz.rs:252:7 | LL | f3: &dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^ not FFI-safe @@ -274,7 +274,7 @@ LL | f3: &dyn Fn(u8)->u8, = 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-ctypes-tykind-fuzz.rs:254:7 + --> $DIR/lint-tykind-fuzz.rs:254:7 | LL | d2: &dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -282,7 +282,7 @@ LL | d2: &dyn std::cmp::PartialOrd, = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `&dyn Debug`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:258:6 + --> $DIR/lint-tykind-fuzz.rs:258:6 | LL | ) -> &'a dyn std::fmt::Debug { | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -290,7 +290,7 @@ LL | ) -> &'a dyn std::fmt::Debug { = 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-ctypes-tykind-fuzz.rs:268:7 + --> $DIR/lint-tykind-fuzz.rs:268:7 | LL | s2: Box, | ^^^^^^^^ not FFI-safe @@ -299,7 +299,7 @@ LL | s2: Box, = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:272:7 + --> $DIR/lint-tykind-fuzz.rs:272:7 | LL | s3: Box<[u8]>, | ^^^^^^^^^ not FFI-safe @@ -308,7 +308,7 @@ LL | s3: Box<[u8]>, = 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-ctypes-tykind-fuzz.rs:278:11 + --> $DIR/lint-tykind-fuzz.rs:278:11 | LL | (p2,p3):(Box, Box), | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -317,7 +317,7 @@ LL | (p2,p3):(Box, Box), = note: tuples have unspecified layout error: `extern` fn uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:294:7 + --> $DIR/lint-tykind-fuzz.rs:294:7 | LL | e3: Box, | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -325,7 +325,7 @@ LL | e3: Box, = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `Box u8>`, which is not FFI-safe - --> $DIR/lint-ctypes-tykind-fuzz.rs:301:7 + --> $DIR/lint-tykind-fuzz.rs:301:7 | LL | f3: Boxu8>, | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -333,7 +333,7 @@ LL | f3: Boxu8>, = 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-ctypes-tykind-fuzz.rs:303:7 + --> $DIR/lint-tykind-fuzz.rs:303:7 | LL | d2: Box>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -341,7 +341,7 @@ LL | d2: Box>, = 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-ctypes-tykind-fuzz.rs:307:6 + --> $DIR/lint-tykind-fuzz.rs:307:6 | LL | ) -> Box { | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/lint-ctypes-113436.rs b/tests/ui/lint/improper_ctypes/mustpass-113436.rs similarity index 100% rename from tests/ui/lint/lint-ctypes-113436.rs rename to tests/ui/lint/improper_ctypes/mustpass-113436.rs 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 From b147f49128b34c33a674006a3d3ab1d8a9a951fc Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sun, 5 Jan 2025 11:17:28 +0100 Subject: [PATCH 06/15] lint ImproperCTypes: properly separate the lint generation from the type visitation steps [does not pass tests] --- Cargo.lock | 1 + compiler/rustc_lint/Cargo.toml | 1 + .../rustc_lint/src/types/improper_ctypes.rs | 1152 ++++++++++------- .../cmse-nonsecure-call/via-registers.rs | 1 + 4 files changed, 668 insertions(+), 487 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e2b7b58c372ba..0df7085be8e7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3969,6 +3969,7 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", + "rustc_type_ir", "smallvec", "tracing", "unicode-security", diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index 64751eaf1feae..3eb6cbdeffad8 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -24,6 +24,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/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index c896dbfd0680e..2b3eba20cd3d4 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -16,12 +16,34 @@ use rustc_middle::ty::{ 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 super::repr_nullable_ptr; use crate::lints::{ImproperCTypes, ImproperCTypesLayer, UsesPowerAlignment}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; +type Sig<'tcx> = Binder, FnSig>>; + +// a shorthand for an often used lifetime-region normalisation step +#[inline] +fn normalize_if_possible<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx>{ + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) + .unwrap_or(ty) +} + +// 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); + normalize_if_possible(cx, field_ty) +} + + /// Check a variant of a non-exhaustive enum for improper ctypes /// /// We treat `#[non_exhaustive] enum` as "ensure that code will compile if new variants are added". @@ -66,15 +88,7 @@ enum CItemKind { 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> { @@ -147,9 +161,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type } // FIXME: double-check: non-exhaustive structs from other crates are assumed to be ?Sized, right? - let is_non_exhaustive = - def.non_enum_variant().is_field_list_non_exhaustive(); - if is_non_exhaustive && !def.did().is_local() { + if def.non_enum_variant().field_list_has_applicable_non_exhaustive() { return TypeSizedness::NotYetKnown; } @@ -163,11 +175,11 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type // (also since !ty.is_sized(), we have at least one field) let last_field_i = variant.fields.last_index().unwrap(); let last_field = &variant.fields[last_field_i]; - let field_ty = last_field.ty(cx.tcx, args); - let field_ty = cx - .tcx - .try_normalize_erasing_regions(cx.typing_env(), field_ty) - .unwrap_or(field_ty); + // let last_field = match &variant.fields.iter().last(){ // TODO performance + // Some(last_field) => last_field, + // None => bug!("Empty struct should be Sized, right?"), // TODO: nonexhaustive empty struct from another crate/module + // }; + let field_ty = get_type_from_field(cx, last_field, args); match get_type_sizedness(cx, field_ty) { s @ (TypeSizedness::UnsizedWithMetadata | TypeSizedness::UnsizedWithExternType @@ -181,13 +193,12 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type } ty::Tuple(tuple) => { // only the last field may be !Sized (or ?Sized in the case of type params) - let n_fields = tuple.len(); - let field_ty: Ty<'tcx> = tuple[n_fields - 1]; - let field_ty = cx - .tcx - .try_normalize_erasing_regions(cx.typing_env(), field_ty) - .unwrap_or(field_ty); - match get_type_sizedness(cx, field_ty) { + 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 = normalize_if_possible(cx, item_ty); + match get_type_sizedness(cx, item_ty) { s @ (TypeSizedness::UnsizedWithMetadata | TypeSizedness::UnsizedWithExternType | TypeSizedness::NotYetKnown) => s, @@ -238,96 +249,124 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type } } -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.clone(), - sp, - vec![ImproperCTypesLayer { - ty, - note: fluent::lint_improper_ctypes_array_reason, - help: Some(fluent::lint_improper_ctypes_array_help), - inner_ty: None, - span_note: None, - }], - ); - true - } else { - false +#[repr(u8)] +#[derive(Clone,Copy,Debug)] +enum CTypesVisitorState{ + // bitflags: + // 0001: inner type + // 0010: static + // 0100: function return + // 1000: used in declared function + StaticTy = 0b0010, + StaticInner = 0b0011, + ArgumentTyInDefinition = 0b1000, + ReturnTyInDefinition = 0b1100, + ArgumentInnerInDefinition = 0b1001, + ReturnInnerInDefinition = 0b1101, + ArgumentTyInDeclaration = 0b0000, + ReturnTyInDeclaration = 0b0100, + ArgumentInnerInDeclaration = 0b0001, + ReturnInnerInDeclaration = 0b0101, +} + +impl CTypesVisitorState{ + /// wether we are being directly used in a function or static + fn has_direct_use(self) -> bool { + ((self as u8) & 0b0001) == 0 + } + /// wether the type is used (directly or not) in a static variable + fn is_in_static(self) -> bool { + ((self as u8) & 0b0010) != 0 + } + /// wether the type is used (directly or not) in a function, in return position + fn is_in_function_return(self) -> bool { + let ret = ((self as u8) & 0b0100) != 0; + #[cfg(debug_assertions)] + if ret{ + assert!(!self.is_in_static()); } + ret } - - /// 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) + /// wether the type is used (directly or not) in a defined function + /// in other words, wether 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 { + let ret = ((self as u8) & 0b1000) != 0; + #[cfg(debug_assertions)] + if ret{ + assert!(!self.is_in_static()); + } + ret + } + /// get a new CTypesVisitorState from the current one, to visit the current type's inner types + fn to_inner_ty(self) -> Self{ + //(self as u8 | 0b0001).try_into().unwrap() + match self{ + Self::StaticTy|Self::StaticInner => Self::StaticInner, + Self::ArgumentTyInDefinition|Self::ArgumentInnerInDefinition + => Self::ArgumentInnerInDefinition, + Self::ReturnTyInDefinition|Self::ReturnInnerInDefinition + => Self::ReturnInnerInDefinition, + Self::ArgumentTyInDeclaration|Self::ArgumentInnerInDeclaration + => Self::ArgumentInnerInDeclaration, + Self::ReturnTyInDeclaration|Self::ReturnInnerInDeclaration + => Self::ReturnInnerInDeclaration, + } } +} - /// Checks if the given `VariantDef`'s field types are "ffi-safe". - fn check_variant_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, +impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { + + /// Checks wether an `extern "ABI" fn` function pointer is indeed FFI-safe to call + fn visit_fnptr( + &mut self, + // TODO this feels wrong but rustc doesn't compile withoug that :') + mode: CItemKind, ty: Ty<'tcx>, - def: ty::AdtDef<'tcx>, - variant: &ty::VariantDef, - args: GenericArgsRef<'tcx>, + sig: Sig<'tcx> ) -> FfiResult<'tcx> { use FfiResult::*; - let transparent_with_all_zst_fields = if def.repr().transparent() { - 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.. - match self.check_field_type_for_ffi(acc, field, args) { - FfiUnsafe { ty, .. } if ty.is_unit() => (), - r => return r, - } - - false - } else { - // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all - // `PhantomData`). - true - } - } else { - false + debug_assert!(!sig.abi().is_rustic_abi()); + let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); + let state = match mode { + CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration, + CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition, }; - - // 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, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false, - FfiPhantom(..) => true, - r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r, + for arg in sig.inputs() { + match self.visit_type(state, *arg) { + FfiSafe => {} + r => { + return FfiUnsafeWrapper { + ty, + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }; + } } } - 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 + let ret_ty = sig.output(); + let state = match mode { + CItemKind::Declaration => CTypesVisitorState::ReturnTyInDeclaration, + CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition, + }; + + match self.visit_type(state, ret_ty) { + r @ (FfiSafe | FfiPhantom(_)) => r, + r @ _ => FfiUnsafeWrapper { + ty: ty.clone(), + reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, + help: None, + wrapped: Box::new(r), + }, } } /// Checks if the given indirection (box,ref,pointer) is "ffi-safe" - fn check_indirection_for_ffi( - &self, - acc: &mut CTypesVisitorState<'tcx>, + fn visit_indirection( + &mut self, + state: CTypesVisitorState, ty: Ty<'tcx>, inner_ty: Ty<'tcx>, indirection_type: IndirectionType, @@ -352,12 +391,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // // For extern function declarations, the actual definition of the function is written somewhere else, // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) + // (or other possibly better tricks, see https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs) // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, // and having the full type information is necessary to compile the function. - if matches!(self.mode, CItemKind::Definition) { + if state.is_in_defined_function() { return FfiSafe; } else { - let inner_res = self.check_type_for_ffi(acc, inner_ty); + let inner_res = self.visit_type(state.to_inner_ty(), inner_ty); return match inner_res { FfiSafe => inner_res, _ => FfiUnsafeWrapper { @@ -387,7 +427,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // wether 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. - return self.check_type_for_ffi(acc, inner_ty); + return self.visit_type(state.to_inner_ty(), inner_ty); } TypeSizedness::UnsizedWithMetadata => { let help = match inner_ty.kind() { @@ -396,10 +436,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Adt(def, _) if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) && matches!( + // TODO also use that trick to separate closures from dyn, if possible tcx.get_diagnostic_name(def.did()), Some(sym::cstring_type | sym::cstr_type) ) - && !acc.base_ty.is_mutable_ptr() => + // TODO vv here vv : originally used acc.base_ty, needs more generic + && !ty.is_mutable_ptr() => { Some(fluent::lint_improper_ctypes_cstr_help) } @@ -415,11 +457,185 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } + /// 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 transparent_with_all_zst_fields = if def.repr().transparent() { + 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.. + let field_ty = get_type_from_field(self.cx, field, args); + match self.visit_type(state.to_inner_ty(), field_ty) { + FfiUnsafe { ty, .. } if ty.is_unit() => (), + r => return r, + } + + false + } else { + // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all + // `PhantomData`). + true + } + } 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 { + let field_ty = get_type_from_field(self.cx, field, args); + all_phantom &= match self.visit_type(state.to_inner_ty(), field_ty) { + FfiSafe => false, + // `()` fields are FFI-safe! + FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false, + FfiPhantom(..) => true, + r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r, + } + } + + 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 + } + } + + fn visit_struct_union( + &mut self, + state: CTypesVisitorState, + ty: Ty<'tcx>, + def: ty::AdtDef<'tcx>, + args: GenericArgsRef<'tcx>, + ) -> FfiResult<'tcx> { + debug_assert!(matches!(def.adt_kind(), AdtKind::Struct|AdtKind::Union)); + use FfiResult::*; + + 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.visit_variant_fields(state, ty, def, def.non_enum_variant(), args) + } + + fn visit_enum( + &mut self, + state: CTypesVisitorState, + 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 empty type: + // values for them must never be constructed, + // functions using them as argument or return must... err. + // TODO + 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, + ) + { + return self.visit_type(state.to_inner_ty(), 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, + }; + } + + 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.visit_variant_fields(state, ty, def, variant, args) { + // TODO no need to pick only one + FfiSafe => ControlFlow::Continue(()), + r => ControlFlow::Break(r), + } + }); + if let ControlFlow::Break(result) = ret { + return result; + } + + FfiSafe + } + /// 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>, + fn visit_type( + &mut self, + state: CTypesVisitorState, ty: Ty<'tcx>, ) -> FfiResult<'tcx> { use FfiResult::*; @@ -430,124 +646,39 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // `struct S(*mut S);`. // FIXME: A recursion limit is necessary as well, for irregular // recursive types. - if !acc.cache.insert(ty) { + if !self.cache.insert(ty) { return FfiSafe; } match *ty.kind() { ty::Adt(def, args) => { if let Some(inner_ty) = ty.boxed_ty() { - return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Box); + return self.visit_indirection(state, ty, inner_ty, IndirectionType::Box); } 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) = + // I thought CStr could not be reached here: + // - not using an indirection would cause a compile error prior to this lint + // - and using one would cause the lint to catch on the indirection before reaching its pointee + // but for some reason one can just go and write function *pointers* like that: + // `type Foo = extern "C" fn(::std::ffi::CStr);` + 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) - }, + help: Some(fluent::lint_improper_ctypes_cstr_help), // TODO look into many cases: own/CString, ref/CString, own/struct/CString, ref/struct/CString, own/struct/ref/CString, etc... }; } - self.check_variant_for_ffi(acc, ty, def, def.non_enum_variant(), args) + self.visit_struct_union(state, ty, def, 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) - { - 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, - }; - } - - 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; - } - - FfiSafe + self.visit_enum(state, ty, def, args) } } } @@ -560,7 +691,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // 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::Pat(base, ..) => self.visit_type(state, base), // FIXME: this should probably be architecture-dependant // same with some ty::Float variants. @@ -587,11 +718,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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::Tuple(tuple) => + // TODO do we move the "unit types allowed as fields" here? assuming they are also allowed behind indirections + // ...doesn't seem so. Existing logic doesn't like Boxes and refs to those. + if tuple.is_empty() && state.is_in_function_return() && state.has_direct_use() { + // C functions can return void + FfiSafe + } else { + FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_tuple_reason, + help: Some(fluent::lint_improper_ctypes_tuple_help), + } + }, ty::RawPtr(ty, _) if match ty.kind() { @@ -603,55 +742,41 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } ty::RawPtr(inner_ty, _) => { - return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::RawPtr); + return self.visit_indirection(state, ty, inner_ty, IndirectionType::RawPtr); } ty::Ref(_, inner_ty, _) => { - return self.check_indirection_for_ffi(acc, ty, inner_ty, IndirectionType::Ref); + return self.visit_indirection(state, ty, inner_ty, IndirectionType::Ref); } - // having arrays as arguments / return values themselves is not FFI safe, - // but that is checked elsewhere - // if we reach this, we can assume the array is inside a struct, behind an indirection, etc. - ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty), + ty::Array(inner_ty, _) => { + if state.has_direct_use() && !state.is_in_static() { + // C doesn't really support passing arrays by value - the only way to pass an array by value + // is through a struct. + FfiUnsafe{ + ty, + reason:fluent::lint_improper_ctypes_array_reason, + help: Some(fluent::lint_improper_ctypes_array_help), + } + } else { + self.visit_type(state.to_inner_ty(), inner_ty) + } + } ty::FnPtr(sig_tys, hdr) => { let sig = sig_tys.with(hdr); if sig.abi().is_rustic_abi() { - return FfiUnsafe { + FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_fnptr_reason, help: Some(fluent::lint_improper_ctypes_fnptr_help), - }; - } - - 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 FfiUnsafeWrapper { - ty, - reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, - help: None, - wrapped: Box::new(r), - }; - } } - } - - let ret_ty = sig.output(); - if ret_ty.is_unit() { - return FfiSafe; - } - - match self.check_type_for_ffi(acc, ret_ty) { - r @ (FfiSafe | FfiPhantom(_)) => r, - r => FfiUnsafeWrapper { - ty: ty.clone(), - reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, - help: None, - wrapped: Box::new(r), - }, + } else { + let mode = if state.is_in_defined_function() { + CItemKind::Definition + } else { + CItemKind::Declaration + }; + self.visit_fnptr(mode, ty, sig) } } @@ -666,7 +791,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // `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) => + if state.is_in_defined_function() => { FfiSafe } @@ -687,34 +812,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn emit_ffi_unsafe_type_lint( - &mut self, - ty: Ty<'tcx>, - sp: Span, - mut reasons: Vec>, - ) { - 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", - }; - for reason in reasons.iter_mut() { - reason.span_note = if let ty::Adt(def, _) = reason.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, reasons }); - } - - fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { + fn check_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { struct ProhibitOpaqueTypes; impl<'tcx> ty::TypeVisitor> for ProhibitOpaqueTypes { type Result = ControlFlow>; @@ -732,61 +830,272 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - if let Some(ty) = self - .cx - .tcx - .try_normalize_erasing_regions(self.cx.typing_env(), ty) - .unwrap_or(ty) + if let Some(ty) = ty .visit_with(&mut ProhibitOpaqueTypes) .break_value() { - self.emit_ffi_unsafe_type_lint( - ty.clone(), - sp, - vec![ImproperCTypesLayer { - ty, - note: fluent::lint_improper_ctypes_opaque, - span_note: Some(sp), - help: None, - inner_ty: None, - }], - ); - true + FfiResult::FfiUnsafe{ + ty, + reason: fluent::lint_improper_ctypes_opaque, + help: None, + } } else { - false + FfiResult::FfiSafe } } - fn check_type_for_ffi_and_report_errors( + fn check_for_type( &mut self, - sp: Span, + state: CTypesVisitorState, + ty: Ty<'tcx>, + ) -> FfiResult<'tcx> { + let ty = normalize_if_possible(self.cx, ty); + + match self.check_for_opaque_ty(ty) { + FfiResult::FfiSafe => (), + ffi_res @ _ => return ffi_res, + } + self.visit_type(state, ty) + } + + fn check_for_fnptr( + &mut self, + mode: CItemKind, + ty: Ty<'tcx>, + ) -> FfiResult<'tcx> { + let ty = normalize_if_possible(self.cx, ty); + + match self.check_for_opaque_ty(ty) { + FfiResult::FfiSafe => (), + ffi_res @ _ => return ffi_res, + } + + 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(mode, ty, sig) + } + }, + _ => bug!("expected to inspect the type of an `extern \"ABI\"` FnPtr, not whtaever this is"), + } + } +} + +/// common structure for functionality that is shared +/// between ImproperCTypesDeclarations and ImproperCTypesDefinitions +struct ImproperCTypesLint<'c, 'tcx>{ + cx: &'c LateContext<'tcx> +} + +impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ + fn check_arg_for_power_alignment( + &mut self, + ty: Ty<'tcx>, + ) -> bool { + let tcx = self.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(field_ty) { + return true; + } + } + } + return false; + } + + fn check_struct_for_power_alignment( + &mut self, + item: &'tcx hir::Item<'tcx>, + ) { + let tcx = self.cx.tcx; + let adt_def = 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() + && 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 = tcx.type_of(def_id).instantiate_identity(); + if self.check_arg_for_power_alignment(ty) { + self.cx.emit_span_lint( + USES_POWER_ALIGNMENT, + field_def.span, + UsesPowerAlignment, + ); + } + } + } + } + + /// Find any fn-ptr types with external ABIs in `ty`. + /// + /// For example, `Option` returns `extern "C" fn()` + fn check_type_for_external_abi_fnptr( + &self, + fn_mode: CItemKind, + hir_ty: &hir::Ty<'tcx>, 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; + 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 + .map(|(fn_ptr_ty, span)|{ + // TODO this will probably lead to error deduplication: fix this + let mut visitor = ImproperCTypesVisitor{cx: self.cx, cache: FxHashSet::default()}; + let ffi_res = visitor.check_for_fnptr(fn_mode, fn_ptr_ty); + (span, ffi_res) + }) + //.flatten() // TODO already planning for more + // even in function *definitions*, FnPtr:s are always function declarations. so it makes sense ...right? + .map(|(span, ffi_res)|self.process_ffi_result(span, ffi_res, fn_mode)) + .reduce(|_a:(),_b:()|()); + //.drain(); + } + + /// 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( + &self, + fn_mode: CItemKind, + 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_external_abi_fnptr(fn_mode, input_hir, *input_ty); + // for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) { + // // no CTypesVisitorState needed, it's overwritten as soon as the FnPtr is entered + // // can default to ArgumentTyInDeclaration if needed + // let res = todo!(fn_ptr_ty); + // res.iter().map(|res|self.process_ffi_result(span, res)).drain(); + // } } - let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty); + if let hir::FnRetTy::Return(ret_hir) = decl.output { + self.check_type_for_external_abi_fnptr(fn_mode, ret_hir, sig.output()); + } + } + + + /// Check that an extern "ABI" static variable is of a ffi-safe type + fn check_foreign_static( + &self, + id: hir::OwnerId, + span: Span + ) { + let ty = self.cx.tcx.type_of(id).instantiate_identity(); + let mut visitor = ImproperCTypesVisitor{cx: self.cx, cache: FxHashSet::default()}; + let ffi_res = visitor.check_for_type(CTypesVisitorState::StaticTy, ty); + self.process_ffi_result(span, ffi_res, CItemKind::Declaration); + } + + /// Check if a function's argument types and result type are "ffi-safe". + fn check_foreign_fn( + &self, + fn_mode: CItemKind, + 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); - // 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; + for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { + let mut visitor = ImproperCTypesVisitor{cx: self.cx, cache: FxHashSet::default()}; + let visit_state = match fn_mode{ + CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition, + CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration, + }; + let ffi_res = visitor.check_for_type(visit_state, *input_ty); + self.process_ffi_result(input_hir.span, ffi_res, fn_mode); } - // 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; + if let hir::FnRetTy::Return(ret_hir) = decl.output { + let mut visitor = ImproperCTypesVisitor{cx: self.cx, cache: FxHashSet::default()}; + let visit_state = match fn_mode{ + CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition, + CItemKind::Declaration => CTypesVisitorState::ReturnTyInDeclaration, + }; + let ffi_res = visitor.check_for_type(visit_state, sig.output()); + self.process_ffi_result(ret_hir.span, ffi_res, fn_mode); } + } + - let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty }; - match self.check_type_for_ffi(&mut acc, ty) { + fn process_ffi_result( + &self, + sp: Span, + res: FfiResult<'tcx>, + fn_mode: CItemKind, + ) { + match res { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { self.emit_ffi_unsafe_type_lint( @@ -799,6 +1108,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: None, inner_ty: None, }], + fn_mode, ); } FfiResult::FfiUnsafe { ty, reason, help } => { @@ -812,11 +1122,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { span_note: None, // filled later inner_ty: None, }], + fn_mode, ); } ffir @ FfiResult::FfiUnsafeWrapper { .. } => { let mut ffiresult_recursor = ControlFlow::Continue(&ffir); - let mut cimproper_layers: Vec> = vec![]; + let mut cimproper_layers: Vec> = vec![]; // this whole while block converts the arbitrarily-deep // FfiResult stack to an ImproperCTypesLayer Vec @@ -861,200 +1172,67 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } // should always have at least one type let last_ty = cimproper_layers.last().unwrap().ty.clone(); - self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers); - } - } - } - - /// 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); + self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers, fn_mode); } } } - /// 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( + fn emit_ffi_unsafe_type_lint( &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) - } - } - - 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) - } + sp: Span, + mut reasons: Vec>, + fn_mode: CItemKind, + ) { + let lint = match fn_mode { + CItemKind::Declaration => IMPROPER_CTYPES, + CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, + }; + let desc = match fn_mode { + CItemKind::Declaration => "block", + CItemKind::Definition => "fn", + }; + for reason in reasons.iter_mut() { + reason.span_note = if let ty::Adt(def, _) = reason.ty.kind() + && let Some(sp) = self.cx.tcx.hir_span_if_local(def.did()) + { + Some(sp) + } else { + None + }; } - 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() + self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons }); } + } 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()); + let lint = ImproperCTypesLint{cx}; match it.kind { hir::ForeignItemKind::Fn(sig, _, _) => { - if abi.is_rustic_abi() { - vis.check_fn(it.owner_id.def_id, sig.decl) + if abi.is_rustic_abi() { + lint.check_fn_for_external_abi_fnptr( + CItemKind::Declaration, + it.owner_id.def_id, + sig.decl + ) } else { - vis.check_foreign_fn(it.owner_id.def_id, sig.decl); + lint.check_foreign_fn(CItemKind::Declaration, 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); + lint.check_foreign_static(it.owner_id, ty.span); } hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), } } } -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() - { - 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, - ); - } - } - } - } -} - /// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in /// `extern "C" { }` blocks): /// @@ -1068,8 +1246,8 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { hir::ItemKind::Static(_, ty, ..) | hir::ItemKind::Const(_, ty, ..) | hir::ItemKind::TyAlias(_, ty, ..) => { - self.check_ty_maybe_containing_foreign_fnptr( - cx, + ImproperCTypesLint{cx}.check_type_for_external_abi_fnptr( + CItemKind::Definition, ty, cx.tcx.type_of(item.owner_id).instantiate_identity(), ); @@ -1079,7 +1257,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { // 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); + ImproperCTypesLint{cx}.check_struct_for_power_alignment(item); } // See `check_field_def`.. hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} @@ -1097,8 +1275,8 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { - self.check_ty_maybe_containing_foreign_fnptr( - cx, + ImproperCTypesLint{cx}.check_type_for_external_abi_fnptr( + CItemKind::Definition, field.ty, cx.tcx.type_of(field.def_id).instantiate_identity(), ); @@ -1121,11 +1299,11 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { _ => return, }; - let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition }; + let lint = ImproperCTypesLint{cx}; if abi.is_rustic_abi() { - vis.check_fn(id, decl); + lint.check_fn_for_external_abi_fnptr(CItemKind::Definition,id, decl); } else { - vis.check_foreign_fn(id, decl); + lint.check_foreign_fn(CItemKind::Definition, id, decl); } } } 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..ad030b22b55b8 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-call/via-registers.rs @@ -38,6 +38,7 @@ pub fn params( } #[no_mangle] +#[allow(improper_ctypes_definitions)] pub fn returns( f1: extern "C-cmse-nonsecure-call" fn() -> u32, f2: extern "C-cmse-nonsecure-call" fn() -> u64, From fecd5804214421cb73bee37b9cd20b6b100fc336 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Fri, 10 Jan 2025 00:37:54 +0100 Subject: [PATCH 07/15] lint ImproperCTypes: more cleanups and reworks [...] - removed special-case logic for a few cases (including the unit type, which is now only checked for in one place) - moved a lot of type-checking code in their dedicated visit_* methods - reworked FfiResult type to handle multiple diagnostics per type (currently imperfect due to type caching) - reworked the messages around CStr and CString --- compiler/rustc_lint/messages.ftl | 14 +- .../rustc_lint/src/types/improper_ctypes.rs | 595 +++++++++++------- tests/ui/lint/improper_ctypes/ctypes.rs | 279 ++++---- 3 files changed, 523 insertions(+), 365 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index df41bdd432bd9..c98e6e3669169 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -382,8 +382,17 @@ 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 @@ -410,6 +419,7 @@ lint_improper_ctypes_pat_reason = pattern types have no C equivalent lint_improper_ctypes_sized_ptr_to_unsafe_type = this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout +lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field 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 diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 2b3eba20cd3d4..8d3294ef92c03 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -91,22 +91,129 @@ struct ImproperCTypesVisitor<'a, 'tcx> { cache: FxHashSet>, } +#[derive(Clone,Debug)] +struct FfiUnsafeReason<'tcx> { + ty: Ty<'tcx>, + reason: DiagMessage, + help: Option, + inner: Option>>, +} + +#[derive(Clone,Debug)] enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), - FfiUnsafe { - ty: Ty<'tcx>, - reason: DiagMessage, - help: Option, - }, - FfiUnsafeWrapper { - ty: Ty<'tcx>, - reason: DiagMessage, - help: Option, - wrapped: Box>, - }, + 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![FfiUnsafeReason{ty,help, reason: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(|reason| FfiUnsafeReason{ + ty, + help: help.clone(), + reason:note.clone(), + inner:Some(Box::new(reason)) + }) + .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::FfiSafe|Self::FfiUnsafe(..) => self, + Self::FfiPhantom(ty) => { + Self::FfiUnsafe(vec![FfiUnsafeReason{ + ty, + reason: fluent::lint_improper_ctypes_only_phantomdata, + help:None, + inner:None, + }]) + }, + } + } } +impl<'tcx> std::ops::AddAssign> for FfiResult<'tcx> { + fn add_assign(&mut self, mut other: Self) { + // this function is fluffin' awful but that's because matching mutable references consumes them (?!) + // the function itself imitates the following piece of non-compiling code: + + // match (self, other) { + // (Self::FfiUnsafe(_), _) => { + // // nothing to do + // }, + // (_, Self::FfiUnsafe(_)) => { + // *self = other; + // }, + // (Self::FfiPhantom(ref ty1),Self::FfiPhantom(ty2)) => { + // println!("kick me, both FfiPhantom, self({:?}) other({:?})", ty1, ty2); + // }, + // (Self::FfiSafe,Self::FfiPhantom(_)) => { + // *self = other; + // }, + // (_, Self::FfiSafe) => { + // // nothing to do + // }, + // } + + let s_disc = std::mem::discriminant(self); + let o_disc = std::mem::discriminant(&other); + if s_disc == o_disc { + match (self, &mut other) { + (Self::FfiUnsafe(s_inner),Self::FfiUnsafe(o_inner)) => { + s_inner.append(o_inner); + }, + (Self::FfiPhantom(ty1),Self::FfiPhantom(ty2)) => { + println!("kick me, both FfiPhantom, self({:?}) other({:?})", ty1, ty2); + }, + (Self::FfiSafe,Self::FfiSafe) => {}, + _ => unreachable!(), + } + } else { + if let Self::FfiUnsafe(_) = self { + return; + } + match other { + Self::FfiUnsafe(o_inner) => { + // self is Safe or Phantom: Unsafe wins + *self = Self::FfiUnsafe(o_inner); + }, + Self::FfiSafe => { + // self is always "wins" + return; + }, + Self::FfiPhantom(o_inner) => { + // self is Safe: Phantom wins + *self = Self::FfiPhantom(o_inner); + }, + } + } + } +} +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 wether it affects references/pointers/boxes to it #[derive(Clone, Copy)] enum TypeSizedness { @@ -258,22 +365,13 @@ enum CTypesVisitorState{ // 0100: function return // 1000: used in declared function StaticTy = 0b0010, - StaticInner = 0b0011, ArgumentTyInDefinition = 0b1000, ReturnTyInDefinition = 0b1100, - ArgumentInnerInDefinition = 0b1001, - ReturnInnerInDefinition = 0b1101, ArgumentTyInDeclaration = 0b0000, ReturnTyInDeclaration = 0b0100, - ArgumentInnerInDeclaration = 0b0001, - ReturnInnerInDeclaration = 0b0101, } impl CTypesVisitorState{ - /// wether we are being directly used in a function or static - fn has_direct_use(self) -> bool { - ((self as u8) & 0b0001) == 0 - } /// wether the type is used (directly or not) in a static variable fn is_in_static(self) -> bool { ((self as u8) & 0b0010) != 0 @@ -298,21 +396,6 @@ impl CTypesVisitorState{ } ret } - /// get a new CTypesVisitorState from the current one, to visit the current type's inner types - fn to_inner_ty(self) -> Self{ - //(self as u8 | 0b0001).try_into().unwrap() - match self{ - Self::StaticTy|Self::StaticInner => Self::StaticInner, - Self::ArgumentTyInDefinition|Self::ArgumentInnerInDefinition - => Self::ArgumentInnerInDefinition, - Self::ReturnTyInDefinition|Self::ReturnInnerInDefinition - => Self::ReturnInnerInDefinition, - Self::ArgumentTyInDeclaration|Self::ArgumentInnerInDeclaration - => Self::ArgumentInnerInDeclaration, - Self::ReturnTyInDeclaration|Self::ReturnInnerInDeclaration - => Self::ReturnInnerInDeclaration, - } - } } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { @@ -332,18 +415,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration, CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition, }; + + let mut all_ffires = FfiSafe; + for arg in sig.inputs() { - match self.visit_type(state, *arg) { - FfiSafe => {} - r => { - return FfiUnsafeWrapper { - ty, - reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, - help: None, - wrapped: Box::new(r), - }; - } - } + let ffi_res = self.visit_type(state, None, *arg); + all_ffires += ffi_res.forbid_phantom().wrap_all( + ty, + fluent::lint_improper_ctypes_fnptr_indirect_reason, + None, + ); } let ret_ty = sig.output(); @@ -352,17 +433,76 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition, }; - match self.visit_type(state, ret_ty) { - r @ (FfiSafe | FfiPhantom(_)) => r, - r @ _ => FfiUnsafeWrapper { - ty: ty.clone(), - reason: fluent::lint_improper_ctypes_fnptr_indirect_reason, - help: None, - wrapped: Box::new(r), - }, + let ffi_res = self.visit_type(state, None, ret_ty); + all_ffires += ffi_res.forbid_phantom().wrap_all( + ty, + fluent::lint_improper_ctypes_fnptr_indirect_reason, + None, + ); + + all_ffires + } + + /// 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, + _ => 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!( + // TODO also use that trick to separate closures from dyn, if possible + self.cx.tcx.get_diagnostic_name(def.did()), + Some(sym::cstring_type | sym::cstr_type) + ) + ) + ); + + // TODO look into many cases: own/CString, ref/CString, own/struct/CString, ref/struct/CString, own/struct/ref/CString, etc... + // maybe mutable: own/CString, own/struct/CString + // known mutable: ref/CString, ref/CStr, ref/struct/CString, own/struct/ref/CString own/struct/ref/CStr + // should be impossible: anything/own/CStr + 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, @@ -371,8 +511,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { inner_ty: Ty<'tcx>, indirection_type: IndirectionType, ) -> FfiResult<'tcx> { - use FfiResult::*; 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().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; + } + } + } + match get_type_sizedness(self.cx, inner_ty) { TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => { // there's a nuance on what this lint should do for @@ -395,18 +562,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, // and having the full type information is necessary to compile the function. if state.is_in_defined_function() { - return FfiSafe; + return FfiResult::FfiSafe; } else { - let inner_res = self.visit_type(state.to_inner_ty(), inner_ty); - return match inner_res { - FfiSafe => inner_res, - _ => FfiUnsafeWrapper { - ty, - reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, - wrapped: Box::new(inner_res), - help: None, - }, - }; + return self.visit_type(state, Some(ty), inner_ty) + .forbid_phantom() + .wrap_all(ty, fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, None); } } TypeSizedness::NotYetKnown => { @@ -427,24 +587,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // wether 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. - return self.visit_type(state.to_inner_ty(), inner_ty); + return 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), - ty::Adt(def, _) - if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union) - && matches!( - // TODO also use that trick to separate closures from dyn, if possible - tcx.get_diagnostic_name(def.did()), - Some(sym::cstring_type | sym::cstr_type) - ) - // TODO vv here vv : originally used acc.base_ty, needs more generic - && !ty.is_mutable_ptr() => - { - Some(fluent::lint_improper_ctypes_cstr_help) - } _ => None, }; let reason = match indirection_type { @@ -452,7 +600,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { IndirectionType::Ref => fluent::lint_improper_ctypes_unsized_ref, IndirectionType::Box => fluent::lint_improper_ctypes_unsized_box, }; - FfiUnsafe { ty, reason, help } + FfiResult::new_with_reason(ty, reason, help) } } } @@ -471,12 +619,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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.. let field_ty = get_type_from_field(self.cx, field, args); - match self.visit_type(state.to_inner_ty(), field_ty) { - FfiUnsafe { ty, .. } if ty.is_unit() => (), - r => return r, - } - - false + let ffi_res = self.visit_type(state, Some(ty), field_ty); + debug_assert!(!matches!( + // 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 here + ffi_res, + FfiUnsafe(ref reasons) + if matches!( + (reasons.len(),reasons.first()), + (1,Some(FfiUnsafeReason{ty,..})) if ty.is_unit() + ) + )); + return ffi_res; } else { // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all // `PhantomData`). @@ -486,23 +640,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { false }; + let mut all_ffires = FfiSafe; // 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 { let field_ty = get_type_from_field(self.cx, field, args); - all_phantom &= match self.visit_type(state.to_inner_ty(), field_ty) { - FfiSafe => false, - // `()` fields are FFI-safe! - FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false, + all_phantom &= match self.visit_type(state, Some(ty), field_ty) { FfiPhantom(..) => true, - r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r, + r @ (FfiUnsafe { .. } | FfiSafe) => {all_ffires += r; false}, } } - if all_phantom { + if matches!(all_ffires, FfiUnsafe(..)) { + all_ffires.wrap_all(ty, fluent::lint_improper_ctypes_struct_dueto, None) + } else if all_phantom { FfiPhantom(ty) } else if transparent_with_all_zst_fields { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_struct_zst, None) } else { FfiSafe } @@ -516,50 +670,49 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { debug_assert!(matches!(def.adt_kind(), AdtKind::Struct|AdtKind::Union)); - use FfiResult::*; if !def.repr().c() && !def.repr().transparent() { - return FfiUnsafe { + return FfiResult::new_with_reason( ty, - reason: if def.is_struct() { + if def.is_struct() { fluent::lint_improper_ctypes_struct_layout_reason } else { fluent::lint_improper_ctypes_union_layout_reason }, - help: if def.is_struct() { + 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 { + return FfiResult::new_with_reason( ty, - reason: if def.is_struct() { + if def.is_struct() { fluent::lint_improper_ctypes_struct_non_exhaustive } else { fluent::lint_improper_ctypes_union_non_exhaustive }, - help: None, - }; + None, + ); } if def.non_enum_variant().fields.is_empty() { - return FfiUnsafe { + return FfiResult::new_with_reason( ty, - reason: if def.is_struct() { + if def.is_struct() { fluent::lint_improper_ctypes_struct_fieldless_reason } else { fluent::lint_improper_ctypes_union_fieldless_reason }, - help: if def.is_struct() { + if def.is_struct() { Some(fluent::lint_improper_ctypes_struct_fieldless_help) } else { Some(fluent::lint_improper_ctypes_union_fieldless_help) }, - }; + ); } self.visit_variant_fields(state, ty, def, def.non_enum_variant(), args) @@ -576,7 +729,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { use FfiResult::*; if def.variants().is_empty() { - // Empty enums are implicitely handled as the empty type: + // Empty enums are implicitely handled as the never type: // values for them must never be constructed, // functions using them as argument or return must... err. // TODO @@ -587,36 +740,36 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() { // Special-case types like `Option` and `Result` - if let Some(ty) = + if let Some(inner_ty) = repr_nullable_ptr( self.cx.tcx, self.cx.typing_env(), ty, ) { - return self.visit_type(state.to_inner_ty(), ty); + return self.visit_type(state, Some(ty), inner_ty); } - return FfiUnsafe { + return FfiResult::new_with_reason( ty, - reason: fluent::lint_improper_ctypes_enum_repr_reason, - help: Some(fluent::lint_improper_ctypes_enum_repr_help), - }; + 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 FfiUnsafe { + return FfiResult::new_with_reason( ty, - reason: fluent::lint_improper_ctypes_128bit, - help: None, - }; + fluent::lint_improper_ctypes_128bit, + None, + ); } 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 })?; + .map_break(|reason| FfiResult::new_with_reason(ty, reason, None))?; match self.visit_variant_fields(state, ty, def, variant, args) { // TODO no need to pick only one @@ -636,6 +789,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn visit_type( &mut self, state: CTypesVisitorState, + outer_ty: Option>, ty: Ty<'tcx>, ) -> FfiResult<'tcx> { use FfiResult::*; @@ -660,21 +814,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } match def.adt_kind() { AdtKind::Struct | AdtKind::Union => { - // I thought CStr could not be reached here: + // I thought CStr (not CString) could not be reached here: // - not using an indirection would cause a compile error prior to this lint // - and using one would cause the lint to catch on the indirection before reaching its pointee // but for some reason one can just go and write function *pointers* like that: // `type Foo = extern "C" fn(::std::ffi::CStr);` if let Some(sym::cstring_type|sym::cstr_type) = - tcx.get_diagnostic_name(def.did()) - { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_cstr_reason, - help: Some(fluent::lint_improper_ctypes_cstr_help), // TODO look into many cases: own/CString, ref/CString, own/struct/CString, ref/struct/CString, own/struct/ref/CString, etc... - }; + // TODO also use that trick to separate closures from dyn, if possible + tcx.get_diagnostic_name(def.did()) { + return self.visit_cstr(outer_ty, ty); } - self.visit_struct_union(state, ty, def, args) } AdtKind::Enum => { @@ -683,54 +832,69 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - ty::Char => FfiUnsafe { + ty::Char => FfiResult::new_with_reason( ty, - reason: fluent::lint_improper_ctypes_char_reason, - help: Some(fluent::lint_improper_ctypes_char_help), - }, + fluent::lint_improper_ctypes_char_reason, + 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.visit_type(state, base), + ty::Pat(base, ..) => self.visit_type(state, outer_ty, base), - // FIXME: this should probably be architecture-dependant - // same with some ty::Float variants. - ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None } - } + // types which likely have a stable representation, depending on the target architecture + ty::Int(..) | ty::Uint(..) | ty::Float(..) => self.visit_numeric(ty), // Primitive types with a stable representation. - ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, + ty::Bool | ty::Never => FfiSafe, - ty::Slice(_) => FfiUnsafe { + ty::Slice(_) => FfiResult::new_with_reason( ty, - reason: fluent::lint_improper_ctypes_slice_reason, - help: Some(fluent::lint_improper_ctypes_slice_help), - }, + fluent::lint_improper_ctypes_slice_reason, + Some(fluent::lint_improper_ctypes_slice_help), + ), - ty::Dynamic(..) => { - FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None } - } + ty::Dynamic(..) => FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_dyn, + None, + ), - ty::Str => FfiUnsafe { + ty::Str => FfiResult::new_with_reason( ty, - reason: fluent::lint_improper_ctypes_str_reason, - help: Some(fluent::lint_improper_ctypes_str_help), - }, + fluent::lint_improper_ctypes_str_reason, + Some(fluent::lint_improper_ctypes_str_help), + ), - ty::Tuple(tuple) => - // TODO do we move the "unit types allowed as fields" here? assuming they are also allowed behind indirections - // ...doesn't seem so. Existing logic doesn't like Boxes and refs to those. - if tuple.is_empty() && state.is_in_function_return() && state.has_direct_use() { - // C functions can return void + ty::Tuple(tuple) => { + let empty_and_safe = if tuple.is_empty() { + if let Some(outer_ty) = outer_ty { + match outer_ty.kind() { + // `()` fields are FFI-safe! + ty::Adt(..) => true, + ty::RawPtr(..) => true, + // most of those are not even reachable, + // but let's not worry about checking that here + _ => false, + } + } else { + // C functions can return void + state.is_in_function_return() + } + } else { + false + }; + + if empty_and_safe{ FfiSafe } else { - FfiUnsafe { + FfiResult::new_with_reason( ty, - reason: fluent::lint_improper_ctypes_tuple_reason, - help: Some(fluent::lint_improper_ctypes_tuple_help), - } - }, + fluent::lint_improper_ctypes_tuple_reason, + Some(fluent::lint_improper_ctypes_tuple_help), + ) + } + }, ty::RawPtr(ty, _) if match ty.kind() { @@ -749,27 +913,27 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } ty::Array(inner_ty, _) => { - if state.has_direct_use() && !state.is_in_static() { + if outer_ty.is_none() && !state.is_in_static() { // C doesn't really support passing arrays by value - the only way to pass an array by value // is through a struct. - FfiUnsafe{ + FfiResult::new_with_reason( ty, - reason:fluent::lint_improper_ctypes_array_reason, - help: Some(fluent::lint_improper_ctypes_array_help), - } + fluent::lint_improper_ctypes_array_reason, + Some(fluent::lint_improper_ctypes_array_help), + ) } else { - self.visit_type(state.to_inner_ty(), inner_ty) + self.visit_type(state, Some(ty), inner_ty) } } ty::FnPtr(sig_tys, hdr) => { let sig = sig_tys.with(hdr); if sig.abi().is_rustic_abi() { - FfiUnsafe { + FfiResult::new_with_reason( ty, - reason: fluent::lint_improper_ctypes_fnptr_reason, - help: Some(fluent::lint_improper_ctypes_fnptr_help), - } + fluent::lint_improper_ctypes_fnptr_reason, + Some(fluent::lint_improper_ctypes_fnptr_help), + ) } else { let mode = if state.is_in_defined_function() { CItemKind::Definition @@ -784,9 +948,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // 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 } - } + ty::Alias(ty::Opaque, ..) => FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_opaque, + 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. @@ -834,11 +1000,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { .visit_with(&mut ProhibitOpaqueTypes) .break_value() { - FfiResult::FfiUnsafe{ + FfiResult::new_with_reason( ty, - reason: fluent::lint_improper_ctypes_opaque, - help: None, - } + fluent::lint_improper_ctypes_opaque, + None, + ) } else { FfiResult::FfiSafe } @@ -855,7 +1021,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiResult::FfiSafe => (), ffi_res @ _ => return ffi_res, } - self.visit_type(state, ty) + self.visit_type(state, None, ty) } fn check_for_fnptr( @@ -1111,64 +1277,31 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ fn_mode, ); } - FfiResult::FfiUnsafe { ty, reason, help } => { - self.emit_ffi_unsafe_type_lint( - ty.clone(), - sp, - vec![ImproperCTypesLayer { - ty, - help, - note: reason, - span_note: None, // filled later - inner_ty: None, - }], - fn_mode, - ); - } - ffir @ FfiResult::FfiUnsafeWrapper { .. } => { - let mut ffiresult_recursor = ControlFlow::Continue(&ffir); + FfiResult::FfiUnsafe(reasons) => for reason in reasons { + let mut ffiresult_recursor = ControlFlow::Continue(&reason); let mut cimproper_layers: Vec> = vec![]; // this whole while block converts the arbitrarily-deep // FfiResult stack to an ImproperCTypesLayer Vec - while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor { - match ffir_rec { - FfiResult::FfiPhantom(ty) => { - 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: None, - note: fluent::lint_improper_ctypes_only_phantomdata, - span_note: None, // filled later - }); - ffiresult_recursor = ControlFlow::Break(()); - } - FfiResult::FfiUnsafe { ty, reason, help } - | FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => { - 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: reason.clone(), - span_note: None, // filled later - }); - - if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec { - ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref()); - } else { - ffiresult_recursor = ControlFlow::Break(()); - } - } - FfiResult::FfiSafe => { - bug!("malformed FfiResult stack: it should be unsafe all the way down") - } - }; + while let ControlFlow::Continue(FfiUnsafeReason{ + ty, reason, 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: reason.clone(), + span_note: None, // filled later + }); + + if let Some(inner) = inner { + ffiresult_recursor = ControlFlow::Continue(inner.as_ref()); + } else { + ffiresult_recursor = ControlFlow::Break(()); + } } // should always have at least one type let last_ty = cimproper_layers.last().unwrap().ty.clone(); diff --git a/tests/ui/lint/improper_ctypes/ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs index 22617e0ebb99b..f67827681f674 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -1,132 +1,147 @@ -#![feature(rustc_private)] -#![feature(extern_types)] - -#![allow(private_interfaces)] -#![deny(improper_ctypes)] - -use std::cell::UnsafeCell; -use std::marker::PhantomData; -use std::ffi::{c_int, c_uint}; -use std::fmt::Debug; - -unsafe extern "C" {type UnsizedOpaque;} -trait Bar { } -trait Mirror { type It: ?Sized; } -impl Mirror for T { type It = Self; } -#[repr(C)] -pub struct StructWithProjection(*mut ::It); -#[repr(C)] -pub struct StructWithProjectionAndLifetime<'a>( - &'a mut as Mirror>::It -); -pub type I32Pair = (i32, i32); -#[repr(C)] -pub struct ZeroSize; -pub type RustFn = fn(); -pub type RustBoxRet = extern "C" fn() -> Box; -pub type CVoidRet = (); -pub struct Foo; -#[repr(transparent)] -pub struct TransparentI128(i128); -#[repr(transparent)] -pub struct TransparentStr(&'static str); -#[repr(transparent)] -pub struct TransparentBoxFn(RustBoxRet); -#[repr(transparent)] -pub struct TransparentInt(u32); -#[repr(transparent)] -pub struct TransparentRef<'a>(&'a TransparentInt); -#[repr(transparent)] -pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); -#[repr(transparent)] -pub struct TransparentUnit(f32, PhantomData); -#[repr(transparent)] -pub struct TransparentCustomZst(i32, ZeroSize); -#[repr(C)] -pub struct UnsizedStructBecauseForeign { - sized: u32, - unszd: UnsizedOpaque, -} -#[repr(C)] -pub struct UnsizedStructBecauseDyn { - sized: u32, - unszd: dyn Debug, -} - -#[repr(C)] -pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); - -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_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); - pub fn opt_box_type(p: 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 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` - pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); - //~^ ERROR uses type `ZeroSizeWithPhantomData` - pub fn zero_size_phantom_toplevel() - -> ::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: RustBoxRet); - 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: TransparentBoxFn); - pub fn raw_array(arr: [u8; 8]); //~ ERROR: uses type `[u8; 8]` - - pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign); - pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn` - - pub fn no_niche_a(a: Option>); - //~^ ERROR: uses type `Option>` - pub fn no_niche_b(b: Option>); - //~^ ERROR: uses type `Option>` - - pub static static_u128_type: u128; //~ ERROR: uses type `u128` - pub static static_u128_array_type: [u128; 16]; //~ ERROR: uses type `u128` - - pub fn good3(fptr: Option); - pub fn good4(aptr: &[u8; 4 as usize]); - pub fn good5(s: StructWithProjection); - pub fn good6(s: StructWithProjectionAndLifetime); - pub fn good7(fptr: extern "C" fn() -> ()); - pub fn good8(fptr: extern "C" fn() -> !); - pub fn good9() -> (); - pub fn good10() -> CVoidRet; - pub fn good11(size: isize); - pub fn good12(size: usize); - pub fn good13(n: TransparentInt); - pub fn good14(p: TransparentRef); - pub fn good15(p: TransparentLifetime); - pub fn good16(p: TransparentUnit); - pub fn good17(p: TransparentCustomZst); - #[allow(improper_ctypes)] - pub fn good18(_: &String); - pub fn good20(arr: *const [u8; 8]); - pub static good21: [u8; 8]; - -} - -#[allow(improper_ctypes)] -extern "C" { - pub fn good19(_: &String); -} - -#[cfg(not(target_arch = "wasm32"))] -extern "C" { - pub fn good1(size: *const c_int); - pub fn good2(size: *const c_uint); -} - -fn main() { -} +<<<<<<< Conflict 1 of 1 ++++++++ Contents of side #1 +%%%%%%% Changes from base to side #2 + #![feature(rustc_private)] + #![feature(extern_types)] + + #![allow(private_interfaces)] + #![deny(improper_ctypes)] + + use std::cell::UnsafeCell; + use std::marker::PhantomData; + use std::ffi::{c_int, c_uint}; + use std::fmt::Debug; + + unsafe extern "C" {type UnsizedOpaque;} + trait Bar { } + trait Mirror { type It: ?Sized; } + impl Mirror for T { type It = Self; } + #[repr(C)] + pub struct StructWithProjection(*mut ::It); + #[repr(C)] + pub struct StructWithProjectionAndLifetime<'a>( + &'a mut as Mirror>::It + ); + pub type I32Pair = (i32, i32); + #[repr(C)] + pub struct ZeroSize; + pub type RustFn = fn(); + pub type RustBoxRet = extern "C" fn() -> Box; + pub type CVoidRet = (); + pub struct Foo; + #[repr(transparent)] + pub struct TransparentI128(i128); + #[repr(transparent)] + pub struct TransparentStr(&'static str); + #[repr(transparent)] + pub struct TransparentBoxFn(RustBoxRet); + #[repr(transparent)] + pub struct TransparentInt(u32); + #[repr(transparent)] + pub struct TransparentRef<'a>(&'a TransparentInt); + #[repr(transparent)] + pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); + #[repr(transparent)] + pub struct TransparentUnit(f32, PhantomData); + #[repr(transparent)] + pub struct TransparentCustomZst(i32, ZeroSize); + #[repr(C)] + pub struct UnsizedStructBecauseForeign { + sized: u32, + unszd: UnsizedOpaque, + } + #[repr(C)] + pub struct UnsizedStructBecauseDyn { + sized: u32, + unszd: dyn Debug, + } + + #[repr(C)] ++pub struct TwoBadTypes<'a> { ++ 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_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); + pub fn opt_box_type(p: 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 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` + pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); + //~^ ERROR uses type `ZeroSizeWithPhantomData` + pub fn zero_size_phantom_toplevel() + -> ::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: RustBoxRet); + 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: 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: Box>)); ++ //~^ ERROR: uses type `char` ++ //~^^ ERROR: uses type `&dyn Debug` ++ // (possible FIXME: the in-struct `char` field doesn't get a warning due ^^) ++ //~^^^^ ERROR: uses type `&[u8]` + + pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign); + pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn` + + pub fn no_niche_a(a: Option>); + //~^ ERROR: uses type `Option>` + pub fn no_niche_b(b: Option>); + //~^ ERROR: uses type `Option>` + + pub static static_u128_type: u128; //~ ERROR: uses type `u128` + pub static static_u128_array_type: [u128; 16]; //~ ERROR: uses type `u128` + + pub fn good3(fptr: Option); + pub fn good4(aptr: &[u8; 4 as usize]); + pub fn good5(s: StructWithProjection); + pub fn good6(s: StructWithProjectionAndLifetime); + pub fn good7(fptr: extern "C" fn() -> ()); + pub fn good8(fptr: extern "C" fn() -> !); + pub fn good9() -> (); + pub fn good10() -> CVoidRet; + pub fn good11(size: isize); + pub fn good12(size: usize); + pub fn good13(n: TransparentInt); + pub fn good14(p: TransparentRef); + pub fn good15(p: TransparentLifetime); + pub fn good16(p: TransparentUnit); + pub fn good17(p: TransparentCustomZst); + #[allow(improper_ctypes)] + pub fn good18(_: &String); + pub fn good20(arr: *const [u8; 8]); + pub static good21: [u8; 8]; + + } + + #[allow(improper_ctypes)] + extern "C" { + pub fn good19(_: &String); + } + + #[cfg(not(target_arch = "wasm32"))] + extern "C" { + pub fn good1(size: *const c_int); + pub fn good2(size: *const c_uint); + } + + fn main() { + } +>>>>>>> Conflict 1 of 1 ends From 10a9e74f3cd1c95e9e5941935033bb71b388faf6 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sat, 18 Jan 2025 18:30:21 +0100 Subject: [PATCH 08/15] lint ImproperCTypes: add considerations for which values can be sourced from non-rust code --- compiler/rustc_lint/messages.ftl | 9 +- compiler/rustc_lint/src/types.rs | 34 +- .../rustc_lint/src/types/improper_ctypes.rs | 542 ++++++++++-------- tests/ui/abi/nullable-pointer-ffi-compat.rs | 1 + tests/ui/asm/issue-97490.rs | 2 + ...extern-C-non-FFI-safe-arg-ice-52334.stderr | 8 +- tests/ui/lint/improper_ctypes/ctypes.rs | 295 +++++----- tests/ui/lint/improper_ctypes/ctypes.stderr | 105 +++- .../lint/improper_ctypes/lint-113436-1.stderr | 12 + .../lint/improper_ctypes/lint-73249-3.stderr | 6 + tests/ui/lint/improper_ctypes/lint-cstr.rs | 22 +- .../ui/lint/improper_ctypes/lint-cstr.stderr | 63 +- tests/ui/lint/improper_ctypes/lint-fn.rs | 13 +- tests/ui/lint/improper_ctypes/lint-fn.stderr | 84 ++- .../lint/improper_ctypes/lint-tykind-fuzz.rs | 119 ++-- .../improper_ctypes/lint-tykind-fuzz.stderr | 202 ++++--- .../repr-rust-is-undefined.stderr | 18 + 17 files changed, 908 insertions(+), 627 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index c98e6e3669169..dcac2ca256e8d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -413,8 +413,13 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` lint_improper_ctypes_opaque = opaque types have no C equivalent -lint_improper_ctypes_pat_help = consider using the base type instead -lint_improper_ctypes_pat_reason = pattern types have no C equivalent +lint_improper_ctypes_pat_intrange_help = consider using the base type instead +lint_improper_ctypes_pat_intrange_reason = integers constrained to a given range 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 and references 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_sized_ptr_to_unsafe_type = this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 8d4bed0f3635e..1fe155a65ae80 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -4,7 +4,7 @@ use rustc_abi::{BackendRepr, 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, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::{Span, Symbol, sym}; use tracing::debug; @@ -925,6 +925,38 @@ fn get_nullable_type_from_pat<'tcx>( } } +/// 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_none() + { + false + } else { + let (tcx, typing_env) = (cx.tcx, cx.typing_env()); + + // see the insides of super::repr_nullable_ptr() + let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok(); + match (compute_size_skeleton(ty), compute_size_skeleton(outer_ty)) { + (Some(sk1), Some(sk2)) => sk1.same_size(sk2), + _ => false, + } + } + } else { + false + } +} + declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]); impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 8d3294ef92c03..77e74de64bcfc 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -27,9 +27,8 @@ type Sig<'tcx> = Binder, FnSig>>; // a shorthand for an often used lifetime-region normalisation step #[inline] -fn normalize_if_possible<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx>{ - cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) - .unwrap_or(ty) +fn normalize_if_possible<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty) } // getting the (normalized) type out of a field (for, e.g., an enum variant or a tuple) @@ -43,8 +42,9 @@ fn get_type_from_field<'tcx>( normalize_if_possible(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. @@ -52,10 +52,10 @@ fn get_type_from_field<'tcx>( /// 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 @@ -64,15 +64,15 @@ 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 { @@ -91,7 +91,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> { cache: FxHashSet>, } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] struct FfiUnsafeReason<'tcx> { ty: Ty<'tcx>, reason: DiagMessage, @@ -99,7 +99,7 @@ struct FfiUnsafeReason<'tcx> { inner: Option>>, } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), @@ -109,7 +109,7 @@ enum FfiResult<'tcx> { 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![FfiUnsafeReason{ty,help, reason:note, inner:None}]) + Self::FfiUnsafe(vec![FfiUnsafeReason { ty, help, reason: note, inner: None }]) } /// If the FfiUnsafe variant, 'wraps' all reasons, @@ -118,16 +118,17 @@ impl<'tcx> FfiResult<'tcx> { fn wrap_all(self, ty: Ty<'tcx>, note: DiagMessage, help: Option) -> Self { match self { Self::FfiUnsafe(this) => { - let unsafeties = this.into_iter() - .map(|reason| FfiUnsafeReason{ + let unsafeties = this + .into_iter() + .map(|reason| FfiUnsafeReason { ty, help: help.clone(), - reason:note.clone(), - inner:Some(Box::new(reason)) + reason: note.clone(), + inner: Some(Box::new(reason)), }) .collect(); Self::FfiUnsafe(unsafeties) - }, + } r @ _ => r, } } @@ -135,22 +136,23 @@ impl<'tcx> FfiResult<'tcx> { /// Otherwise, keep unchanged. fn forbid_phantom(self) -> Self { match self { - Self::FfiSafe|Self::FfiUnsafe(..) => self, - Self::FfiPhantom(ty) => { - Self::FfiUnsafe(vec![FfiUnsafeReason{ - ty, - reason: fluent::lint_improper_ctypes_only_phantomdata, - help:None, - inner:None, - }]) - }, + Self::FfiSafe | Self::FfiUnsafe(..) => self, + Self::FfiPhantom(ty) => Self::FfiUnsafe(vec![FfiUnsafeReason { + ty, + reason: fluent::lint_improper_ctypes_only_phantomdata, + help: None, + inner: None, + }]), } } } impl<'tcx> std::ops::AddAssign> for FfiResult<'tcx> { fn add_assign(&mut self, mut other: Self) { - // this function is fluffin' awful but that's because matching mutable references consumes them (?!) + // 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 + + // this function is awful to look but that's because matching mutable references consumes them (?!) // the function itself imitates the following piece of non-compiling code: // match (self, other) { @@ -161,7 +163,7 @@ impl<'tcx> std::ops::AddAssign> for FfiResult<'tcx> { // *self = other; // }, // (Self::FfiPhantom(ref ty1),Self::FfiPhantom(ty2)) => { - // println!("kick me, both FfiPhantom, self({:?}) other({:?})", ty1, ty2); + // println!("whoops, both FfiPhantom: self({:?}) += other({:?})", ty1, ty2); // }, // (Self::FfiSafe,Self::FfiPhantom(_)) => { // *self = other; @@ -175,13 +177,13 @@ impl<'tcx> std::ops::AddAssign> for FfiResult<'tcx> { let o_disc = std::mem::discriminant(&other); if s_disc == o_disc { match (self, &mut other) { - (Self::FfiUnsafe(s_inner),Self::FfiUnsafe(o_inner)) => { + (Self::FfiUnsafe(s_inner), Self::FfiUnsafe(o_inner)) => { s_inner.append(o_inner); - }, - (Self::FfiPhantom(ty1),Self::FfiPhantom(ty2)) => { - println!("kick me, both FfiPhantom, self({:?}) other({:?})", ty1, ty2); - }, - (Self::FfiSafe,Self::FfiSafe) => {}, + } + (Self::FfiPhantom(ty1), Self::FfiPhantom(ty2)) => { + debug!("whoops: both FfiPhantom, self({:?}) += other({:?})", ty1, ty2); + } + (Self::FfiSafe, Self::FfiSafe) => {} _ => unreachable!(), } } else { @@ -192,15 +194,15 @@ impl<'tcx> std::ops::AddAssign> for FfiResult<'tcx> { Self::FfiUnsafe(o_inner) => { // self is Safe or Phantom: Unsafe wins *self = Self::FfiUnsafe(o_inner); - }, + } Self::FfiSafe => { // self is always "wins" return; - }, + } Self::FfiPhantom(o_inner) => { // self is Safe: Phantom wins *self = Self::FfiPhantom(o_inner); - }, + } } } } @@ -213,7 +215,6 @@ impl<'tcx> std::ops::Add> for FfiResult<'tcx> { } } - /// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it #[derive(Clone, Copy)] enum TypeSizedness { @@ -267,10 +268,8 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type return TypeSizedness::UnsizedWithMetadata; } - // FIXME: double-check: non-exhaustive structs from other crates are assumed to be ?Sized, right? - if def.non_enum_variant().field_list_has_applicable_non_exhaustive() { - return TypeSizedness::NotYetKnown; - } + // 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"); @@ -279,13 +278,12 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type let variant = def.non_enum_variant(); // only the last field may be !Sized (or ?Sized in the case of type params) - // (also since !ty.is_sized(), we have at least one field) - let last_field_i = variant.fields.last_index().unwrap(); - let last_field = &variant.fields[last_field_i]; - // let last_field = match &variant.fields.iter().last(){ // TODO performance - // Some(last_field) => last_field, - // None => bug!("Empty struct should be Sized, right?"), // TODO: nonexhaustive empty struct from another crate/module - // }; + 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 @@ -357,10 +355,9 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type } #[repr(u8)] -#[derive(Clone,Copy,Debug)] -enum CTypesVisitorState{ +#[derive(Clone, Copy, Debug)] +enum CTypesVisitorState { // bitflags: - // 0001: inner type // 0010: static // 0100: function return // 1000: used in declared function @@ -371,7 +368,7 @@ enum CTypesVisitorState{ ReturnTyInDeclaration = 0b0100, } -impl CTypesVisitorState{ +impl CTypesVisitorState { /// wether the type is used (directly or not) in a static variable fn is_in_static(self) -> bool { ((self as u8) & 0b0010) != 0 @@ -380,7 +377,7 @@ impl CTypesVisitorState{ fn is_in_function_return(self) -> bool { let ret = ((self as u8) & 0b0100) != 0; #[cfg(debug_assertions)] - if ret{ + if ret { assert!(!self.is_in_static()); } ret @@ -391,23 +388,31 @@ impl CTypesVisitorState{ fn is_in_defined_function(self) -> bool { let ret = ((self as u8) & 0b1000) != 0; #[cfg(debug_assertions)] - if ret{ + if ret { assert!(!self.is_in_static()); } ret } + + /// wether the value for that type might come from the non-rust side of a FFI boundary + fn value_may_be_unchecked(self) -> bool { + // function declarations are assumed to be rust-caller, non-rust-callee + // function definitions are assumed to be maybe-not-rust-caller, rust-callee + // FnPtrs are... well, nothing's certain about anything. (TODO need more flags in enum?) + // Same with statics. + if self.is_in_static() { + true + } else if self.is_in_defined_function() { + !self.is_in_function_return() + } else { + self.is_in_function_return() + } + } } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - /// Checks wether an `extern "ABI" fn` function pointer is indeed FFI-safe to call - fn visit_fnptr( - &mut self, - // TODO this feels wrong but rustc doesn't compile withoug that :') - mode: CItemKind, - ty: Ty<'tcx>, - sig: Sig<'tcx> - ) -> FfiResult<'tcx> { + fn visit_fnptr(&mut self, mode: CItemKind, 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); @@ -445,68 +450,52 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// 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> { + 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::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, _ => 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!( - // TODO also use that trick to separate closures from dyn, if possible - self.cx.tcx.get_diagnostic_name(def.did()), - Some(sym::cstring_type | sym::cstr_type) - ) + 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) ) - ); + )); - // TODO look into many cases: own/CString, ref/CString, own/struct/CString, ref/struct/CString, own/struct/ref/CString, etc... - // maybe mutable: own/CString, own/struct/CString - // known mutable: ref/CString, ref/CStr, ref/struct/CString, own/struct/ref/CString own/struct/ref/CStr - // should be impossible: anything/own/CStr let help = if let Some(outer_ty) = outer_ty { match outer_ty.kind() { - ty::Ref(..)|ty::RawPtr(..) => { + 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, + } + 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), - ) + 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, @@ -514,9 +503,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, '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()) { + 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. + 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 { @@ -525,7 +516,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match cstr_res { FfiResult::FfiUnsafe(ref mut reasons) => { reasons.first_mut().unwrap().ty = ty; - }, + } _ => unreachable!(), } let note = match indirection_type { @@ -540,7 +531,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - match get_type_sizedness(self.cx, inner_ty) { + // 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 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 => { // there's a nuance on what this lint should do for // function definitions (`extern "C" fn fn_name(...) {...}`) @@ -562,11 +558,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, // and having the full type information is necessary to compile the function. if state.is_in_defined_function() { - return FfiResult::FfiSafe; + FfiResult::FfiSafe } else { - return self.visit_type(state, Some(ty), inner_ty) - .forbid_phantom() - .wrap_all(ty, fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, None); + return self.visit_type(state, Some(ty), inner_ty).forbid_phantom().wrap_all( + ty, + fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, + None, + ); } } TypeSizedness::NotYetKnown => { @@ -587,7 +585,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // wether 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. - return self.visit_type(state, Some(ty), inner_ty); + self.visit_type(state, Some(ty), inner_ty) } TypeSizedness::UnsizedWithMetadata => { let help = match inner_ty.kind() { @@ -600,9 +598,36 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { IndirectionType::Ref => fluent::lint_improper_ctypes_unsized_ref, IndirectionType::Box => fluent::lint_improper_ctypes_unsized_box, }; - FfiResult::new_with_reason(ty, reason, help) + 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?) + 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". @@ -647,7 +672,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let field_ty = get_type_from_field(self.cx, field, args); all_phantom &= match self.visit_type(state, Some(ty), field_ty) { FfiPhantom(..) => true, - r @ (FfiUnsafe { .. } | FfiSafe) => {all_ffires += r; false}, + r @ (FfiUnsafe { .. } | FfiSafe) => { + all_ffires += r; + false + } } } @@ -669,7 +697,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { def: ty::AdtDef<'tcx>, args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { - debug_assert!(matches!(def.adt_kind(), AdtKind::Struct|AdtKind::Union)); + debug_assert!(matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)); if !def.repr().c() && !def.repr().transparent() { return FfiResult::new_with_reason( @@ -730,23 +758,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.variants().is_empty() { // Empty enums are implicitely handled as the never type: - // values for them must never be constructed, - // functions using them as argument or return must... err. - // TODO + // TODO think about the FFI-safety of functions that use that 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() - { + if !def.repr().c() && !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, - ) - { + if let Some(inner_ty) = repr_nullable_ptr( + self.cx.tcx, + self.cx.typing_env(), + ty, + ) { return self.visit_type(state, Some(ty), inner_ty); } @@ -767,21 +790,34 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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| FfiResult::new_with_reason(ty, reason, None))?; - - match self.visit_variant_fields(state, ty, def, variant, args) { - // TODO no need to pick only one - FfiSafe => ControlFlow::Continue(()), - r => ControlFlow::Break(r), - } + + 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; }); - if let ControlFlow::Break(result) = ret { - return result; - } - FfiSafe + 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 { + def.variants() + .iter() + .map(|variant| { + self.visit_variant_fields(state, ty, def, variant, args) + // TODO: 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?) + .forbid_phantom() + }) + .reduce(|r1, r2| r1 + r2) + .unwrap() // always at least one variant if we hit this branch + } } /// Checks if the given type is "ffi-safe" (has a stable, well-defined @@ -807,7 +843,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { match *ty.kind() { ty::Adt(def, args) => { if let Some(inner_ty) = ty.boxed_ty() { - return self.visit_indirection(state, ty, inner_ty, IndirectionType::Box); + return self.visit_indirection( + state, + outer_ty, + ty, + inner_ty, + IndirectionType::Box, + ); } if def.is_phantom_data() { return FfiPhantom(ty); @@ -819,16 +861,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // - and using one would cause the lint to catch on the indirection before reaching its pointee // but for some reason one can just go and write function *pointers* like that: // `type Foo = extern "C" fn(::std::ffi::CStr);` - if let Some(sym::cstring_type|sym::cstr_type) = - // TODO also use that trick to separate closures from dyn, if possible - tcx.get_diagnostic_name(def.did()) { + 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_union(state, ty, def, args) } - AdtKind::Enum => { - self.visit_enum(state, ty, def, args) - } + AdtKind::Enum => self.visit_enum(state, ty, def, args), } } @@ -838,9 +878,28 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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.visit_type(state, outer_ty, base), + ty::Pat(pat_ty, _) => { + if state.value_may_be_unchecked() { + // you would think that int-range pattern types that exclude 0 would have Option layout optimisation + // they don't (see tests/ui/type/pattern_types/range_patterns.stderr) + // so there's no need to allow Option. + debug_assert!(matches!( + pat_ty.kind(), + ty::Int(..) | ty::Uint(..) | ty::Float(..) + )); + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_pat_intrange_reason, + Some(fluent::lint_improper_ctypes_pat_intrange_help), + ) + } else if let ty::Int(_) | ty::Uint(_) = pat_ty.kind() { + self.visit_numeric(pat_ty) + } else { + bug!( + "this lint was written when pattern types could only be integers constrained to ranges" + ) + } + } // types which likely have a stable representation, depending on the target architecture ty::Int(..) | ty::Uint(..) | ty::Float(..) => self.visit_numeric(ty), @@ -854,11 +913,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { Some(fluent::lint_improper_ctypes_slice_help), ), - ty::Dynamic(..) => FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_dyn, - None, - ), + ty::Dynamic(..) => { + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_dyn, None) + } ty::Str => FfiResult::new_with_reason( ty, @@ -885,7 +942,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { false }; - if empty_and_safe{ + if empty_and_safe { FfiSafe } else { FfiResult::new_with_reason( @@ -894,7 +951,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { Some(fluent::lint_improper_ctypes_tuple_help), ) } - }, + } ty::RawPtr(ty, _) if match ty.kind() { @@ -906,10 +963,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } ty::RawPtr(inner_ty, _) => { - return self.visit_indirection(state, ty, inner_ty, IndirectionType::RawPtr); + return self.visit_indirection( + state, + outer_ty, + ty, + inner_ty, + IndirectionType::RawPtr, + ); } ty::Ref(_, inner_ty, _) => { - return self.visit_indirection(state, ty, inner_ty, IndirectionType::Ref); + return self.visit_indirection(state, outer_ty, ty, inner_ty, IndirectionType::Ref); } ty::Array(inner_ty, _) => { @@ -948,11 +1011,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // 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, ..) => FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_opaque, - None, - ), + ty::Alias(ty::Opaque, ..) => { + FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_opaque, 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. @@ -996,25 +1057,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - if let Some(ty) = ty - .visit_with(&mut ProhibitOpaqueTypes) - .break_value() - { - FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_opaque, - None, - ) + 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> { + fn check_for_type(&mut self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { let ty = normalize_if_possible(self.cx, ty); match self.check_for_opaque_ty(ty) { @@ -1024,11 +1074,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { self.visit_type(state, None, ty) } - fn check_for_fnptr( - &mut self, - mode: CItemKind, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { + fn check_for_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>) -> FfiResult<'tcx> { let ty = normalize_if_possible(self.cx, ty); match self.check_for_opaque_ty(ty) { @@ -1040,23 +1086,27 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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") + bug!( + "expected to inspect the type of an `extern \"ABI\"` FnPtr, not an internal-ABI one" + ) } else { self.visit_fnptr(mode, ty, sig) } - }, - _ => bug!("expected to inspect the type of an `extern \"ABI\"` FnPtr, not whtaever this is"), + } + _ => bug!( + "expected to inspect the type of an `extern \"ABI\"` FnPtr, not whtaever this is" + ), } } } /// common structure for functionality that is shared /// between ImproperCTypesDeclarations and ImproperCTypesDefinitions -struct ImproperCTypesLint<'c, 'tcx>{ - cx: &'c LateContext<'tcx> +struct ImproperCTypesLint<'c, 'tcx> { + cx: &'c LateContext<'tcx>, } -impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ +impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { fn check_arg_for_power_alignment( &mut self, ty: Ty<'tcx>, @@ -1164,22 +1214,22 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ } } - let mut visitor = FnPtrFinder {spans: Vec::new(), tys: Vec::new() }; + 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 - .map(|(fn_ptr_ty, span)|{ - // TODO this will probably lead to error deduplication: fix this - let mut visitor = ImproperCTypesVisitor{cx: self.cx, cache: FxHashSet::default()}; + .map(|(fn_ptr_ty, span)| { + // FIXME this will probably lead to error deduplication: fix this + let mut visitor = + ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() }; let ffi_res = visitor.check_for_fnptr(fn_mode, fn_ptr_ty); (span, ffi_res) }) - //.flatten() // TODO already planning for more - // even in function *definitions*, FnPtr:s are always function declarations. so it makes sense ...right? - .map(|(span, ffi_res)|self.process_ffi_result(span, ffi_res, fn_mode)) - .reduce(|_a:(),_b:()|()); + // even in function *definitions*, `FnPtr`s are always function declarations ...right? + // (TODO: we can't do that yet because one of rustc's crates can't compile if we do) + .for_each(|(span, ffi_res)| self.process_ffi_result(span, ffi_res, fn_mode)); //.drain(); } @@ -1189,19 +1239,13 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ &self, fn_mode: CItemKind, def_id: LocalDefId, - decl: &'tcx hir::FnDecl<'_> + 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_external_abi_fnptr(fn_mode, input_hir, *input_ty); - // for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) { - // // no CTypesVisitorState needed, it's overwritten as soon as the FnPtr is entered - // // can default to ArgumentTyInDeclaration if needed - // let res = todo!(fn_ptr_ty); - // res.iter().map(|res|self.process_ffi_result(span, res)).drain(); - // } } if let hir::FnRetTy::Return(ret_hir) = decl.output { @@ -1209,15 +1253,10 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ } } - /// Check that an extern "ABI" static variable is of a ffi-safe type - fn check_foreign_static( - &self, - id: hir::OwnerId, - span: Span - ) { + fn check_foreign_static(&self, id: hir::OwnerId, span: Span) { let ty = self.cx.tcx.type_of(id).instantiate_identity(); - let mut visitor = ImproperCTypesVisitor{cx: self.cx, cache: FxHashSet::default()}; + let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() }; let ffi_res = visitor.check_for_type(CTypesVisitorState::StaticTy, ty); self.process_ffi_result(span, ffi_res, CItemKind::Declaration); } @@ -1229,13 +1268,12 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ 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) { - let mut visitor = ImproperCTypesVisitor{cx: self.cx, cache: FxHashSet::default()}; - let visit_state = match fn_mode{ + let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() }; + let visit_state = match fn_mode { CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition, CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration, }; @@ -1244,8 +1282,8 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ } if let hir::FnRetTy::Return(ret_hir) = decl.output { - let mut visitor = ImproperCTypesVisitor{cx: self.cx, cache: FxHashSet::default()}; - let visit_state = match fn_mode{ + let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() }; + let visit_state = match fn_mode { CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition, CItemKind::Declaration => CTypesVisitorState::ReturnTyInDeclaration, }; @@ -1254,13 +1292,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ } } - - fn process_ffi_result( - &self, - sp: Span, - res: FfiResult<'tcx>, - fn_mode: CItemKind, - ) { + fn process_ffi_result(&self, sp: Span, res: FfiResult<'tcx>, fn_mode: CItemKind) { match res { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { @@ -1277,35 +1309,41 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ fn_mode, ); } - FfiResult::FfiUnsafe(reasons) => for reason in reasons { - let mut ffiresult_recursor = ControlFlow::Continue(&reason); - 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, reason, 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: reason.clone(), - span_note: None, // filled later - }); - - if let Some(inner) = inner { - ffiresult_recursor = ControlFlow::Continue(inner.as_ref()); - } else { - ffiresult_recursor = ControlFlow::Break(()); + FfiResult::FfiUnsafe(reasons) => { + for reason in reasons { + let mut ffiresult_recursor = ControlFlow::Continue(&reason); + 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, + reason, + 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: reason.clone(), + span_note: None, // filled later + }); + + if let Some(inner) = inner { + ffiresult_recursor = ControlFlow::Continue(inner.as_ref()); + } else { + ffiresult_recursor = ControlFlow::Break(()); + } } + // should always have at least one type + let last_ty = cimproper_layers.last().unwrap().ty.clone(); + self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers, fn_mode); } - // should always have at least one type - let last_ty = cimproper_layers.last().unwrap().ty.clone(); - self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers, fn_mode); } } } @@ -1337,14 +1375,12 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx>{ self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons }); } - } impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) { - let abi = cx.tcx.hir_get_foreign_abi(it.hir_id()); - let lint = ImproperCTypesLint{cx}; + let lint = ImproperCTypesLint { cx }; match it.kind { hir::ForeignItemKind::Fn(sig, _, _) => { @@ -1352,7 +1388,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { lint.check_fn_for_external_abi_fnptr( CItemKind::Declaration, it.owner_id.def_id, - sig.decl + sig.decl, ) } else { lint.check_foreign_fn(CItemKind::Declaration, it.owner_id.def_id, sig.decl); @@ -1379,7 +1415,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { hir::ItemKind::Static(_, ty, ..) | hir::ItemKind::Const(_, ty, ..) | hir::ItemKind::TyAlias(_, ty, ..) => { - ImproperCTypesLint{cx}.check_type_for_external_abi_fnptr( + ImproperCTypesLint { cx }.check_type_for_external_abi_fnptr( CItemKind::Definition, ty, cx.tcx.type_of(item.owner_id).instantiate_identity(), @@ -1408,7 +1444,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { - ImproperCTypesLint{cx}.check_type_for_external_abi_fnptr( + ImproperCTypesLint { cx }.check_type_for_external_abi_fnptr( CItemKind::Definition, field.ty, cx.tcx.type_of(field.def_id).instantiate_identity(), @@ -1432,9 +1468,9 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { _ => return, }; - let lint = ImproperCTypesLint{cx}; + let lint = ImproperCTypesLint { cx }; if abi.is_rustic_abi() { - lint.check_fn_for_external_abi_fnptr(CItemKind::Definition,id, decl); + lint.check_fn_for_external_abi_fnptr(CItemKind::Definition, id, decl); } else { lint.check_foreign_fn(CItemKind::Definition, id, decl); } diff --git a/tests/ui/abi/nullable-pointer-ffi-compat.rs b/tests/ui/abi/nullable-pointer-ffi-compat.rs index f94f838723a56..33d856732b2c5 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_ctypes_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/asm/issue-97490.rs b/tests/ui/asm/issue-97490.rs index 5f364a22bc437..931c378704a29 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_ctypes_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/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr index b5c718ec38147..abd52584b1e57 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 @@ -5,7 +5,9 @@ LL | type Foo = extern "C" fn(::std::ffi::CStr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: the function pointer to `extern "C" fn(CStr)` is FFI-unsafe due to `CStr` - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = 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: `#[warn(improper_ctypes_definitions)]` on by default @@ -16,7 +18,9 @@ LL | fn meh(blah: Foo); | ^^^ not FFI-safe | = note: the function pointer to `extern "C" fn(CStr)` is FFI-unsafe due to `CStr` - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = 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: `#[warn(improper_ctypes)]` on by default diff --git a/tests/ui/lint/improper_ctypes/ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs index f67827681f674..dcdebb80594f4 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -1,147 +1,148 @@ -<<<<<<< Conflict 1 of 1 -+++++++ Contents of side #1 -%%%%%%% Changes from base to side #2 - #![feature(rustc_private)] - #![feature(extern_types)] - - #![allow(private_interfaces)] - #![deny(improper_ctypes)] - - use std::cell::UnsafeCell; - use std::marker::PhantomData; - use std::ffi::{c_int, c_uint}; - use std::fmt::Debug; - - unsafe extern "C" {type UnsizedOpaque;} - trait Bar { } - trait Mirror { type It: ?Sized; } - impl Mirror for T { type It = Self; } - #[repr(C)] - pub struct StructWithProjection(*mut ::It); - #[repr(C)] - pub struct StructWithProjectionAndLifetime<'a>( - &'a mut as Mirror>::It - ); - pub type I32Pair = (i32, i32); - #[repr(C)] - pub struct ZeroSize; - pub type RustFn = fn(); - pub type RustBoxRet = extern "C" fn() -> Box; - pub type CVoidRet = (); - pub struct Foo; - #[repr(transparent)] - pub struct TransparentI128(i128); - #[repr(transparent)] - pub struct TransparentStr(&'static str); - #[repr(transparent)] - pub struct TransparentBoxFn(RustBoxRet); - #[repr(transparent)] - pub struct TransparentInt(u32); - #[repr(transparent)] - pub struct TransparentRef<'a>(&'a TransparentInt); - #[repr(transparent)] - pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); - #[repr(transparent)] - pub struct TransparentUnit(f32, PhantomData); - #[repr(transparent)] - pub struct TransparentCustomZst(i32, ZeroSize); - #[repr(C)] - pub struct UnsizedStructBecauseForeign { - sized: u32, - unszd: UnsizedOpaque, - } - #[repr(C)] - pub struct UnsizedStructBecauseDyn { - sized: u32, - unszd: dyn Debug, - } - - #[repr(C)] -+pub struct TwoBadTypes<'a> { -+ 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_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); - pub fn opt_box_type(p: 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 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` - pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); - //~^ ERROR uses type `ZeroSizeWithPhantomData` - pub fn zero_size_phantom_toplevel() - -> ::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: RustBoxRet); - 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: 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: Box>)); -+ //~^ ERROR: uses type `char` -+ //~^^ ERROR: uses type `&dyn Debug` -+ // (possible FIXME: the in-struct `char` field doesn't get a warning due ^^) -+ //~^^^^ ERROR: uses type `&[u8]` - - pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign); - pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn` - - pub fn no_niche_a(a: Option>); - //~^ ERROR: uses type `Option>` - pub fn no_niche_b(b: Option>); - //~^ ERROR: uses type `Option>` - - pub static static_u128_type: u128; //~ ERROR: uses type `u128` - pub static static_u128_array_type: [u128; 16]; //~ ERROR: uses type `u128` - - pub fn good3(fptr: Option); - pub fn good4(aptr: &[u8; 4 as usize]); - pub fn good5(s: StructWithProjection); - pub fn good6(s: StructWithProjectionAndLifetime); - pub fn good7(fptr: extern "C" fn() -> ()); - pub fn good8(fptr: extern "C" fn() -> !); - pub fn good9() -> (); - pub fn good10() -> CVoidRet; - pub fn good11(size: isize); - pub fn good12(size: usize); - pub fn good13(n: TransparentInt); - pub fn good14(p: TransparentRef); - pub fn good15(p: TransparentLifetime); - pub fn good16(p: TransparentUnit); - pub fn good17(p: TransparentCustomZst); - #[allow(improper_ctypes)] - pub fn good18(_: &String); - pub fn good20(arr: *const [u8; 8]); - pub static good21: [u8; 8]; - - } - - #[allow(improper_ctypes)] - extern "C" { - pub fn good19(_: &String); - } - - #[cfg(not(target_arch = "wasm32"))] - extern "C" { - pub fn good1(size: *const c_int); - pub fn good2(size: *const c_uint); - } - - fn main() { - } ->>>>>>> Conflict 1 of 1 ends +#![feature(rustc_private)] +#![feature(extern_types)] +#![feature(pattern_types, rustc_attrs)] +#![feature(pattern_type_macro)] + +#![allow(private_interfaces)] +#![deny(improper_ctypes)] + +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; } +#[repr(C)] +pub struct StructWithProjection(*mut ::It); +#[repr(C)] +pub struct StructWithProjectionAndLifetime<'a>( + &'a mut as Mirror>::It +); +pub type I32Pair = (i32, i32); +#[repr(C)] +pub struct ZeroSize; +pub type RustFn = fn(); +pub type RustBoxRet = extern "C" fn() -> Box; +pub type CVoidRet = (); +pub struct Foo; +#[repr(transparent)] +pub struct TransparentI128(i128); +#[repr(transparent)] +pub struct TransparentStr(&'static str); +#[repr(transparent)] +pub struct TransparentBoxFn(RustBoxRet); +#[repr(transparent)] +pub struct TransparentInt(u32); +#[repr(transparent)] +pub struct TransparentRef<'a>(&'a TransparentInt); +#[repr(transparent)] +pub struct TransparentLifetime<'a>(*const u8, PhantomData<&'a ()>); +#[repr(transparent)] +pub struct TransparentUnit(f32, PhantomData); +#[repr(transparent)] +pub struct TransparentCustomZst(i32, ZeroSize); +#[repr(C)] +pub struct UnsizedStructBecauseForeign { + sized: u32, + unszd: UnsizedOpaque, +} +#[repr(C)] +pub struct UnsizedStructBecauseDyn { + sized: u32, + unszd: dyn Debug, +} + +#[repr(C)] +pub struct TwoBadTypes<'a> { + 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_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); + pub fn opt_box_type(p: 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 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` + pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); + //~^ ERROR uses type `ZeroSizeWithPhantomData` + pub fn zero_size_phantom_toplevel() + -> ::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: RustBoxRet); + 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: 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: Box>)); + //~^ ERROR: uses type `char` + //~^^ ERROR: uses type `&dyn Debug` + // (possible FIXME: the in-struct `char` field doesn't get a warning due ^^) + //~^^^^ ERROR: uses type `&[u8]` + + pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign); + pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn` + + pub fn no_niche_a(a: Option>); + //~^ ERROR: uses type `Option>` + pub fn no_niche_b(b: Option>); + //~^ ERROR: uses type `Option>` + + pub static static_u128_type: u128; //~ ERROR: uses type `u128` + pub static static_u128_array_type: [u128; 16]; //~ ERROR: uses type `u128` + + pub fn good3(fptr: Option); + pub fn good4(aptr: &[u8; 4 as usize]); + pub fn good5(s: StructWithProjection); + pub fn good6(s: StructWithProjectionAndLifetime); + pub fn good7(fptr: extern "C" fn() -> ()); + pub fn good8(fptr: extern "C" fn() -> !); + pub fn good9() -> (); + pub fn good10() -> CVoidRet; + pub fn good11(size: isize); + pub fn good12(size: usize); + pub fn good13(n: TransparentInt); + pub fn good14(p: TransparentRef); + pub fn good15(p: TransparentLifetime); + pub fn good16(p: TransparentUnit); + pub fn good17(p: TransparentCustomZst); + #[allow(improper_ctypes)] + pub fn good18(_: &String); + pub fn good20(arr: *const [u8; 8]); + pub static good21: [u8; 8]; + +} + +#[allow(improper_ctypes)] +extern "C" { + pub fn good19(_: &String); +} + +#[cfg(not(target_arch = "wasm32"))] +extern "C" { + pub fn good1(size: *const c_int); + pub fn good2(size: *const c_uint); +} + +fn main() { +} diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index 3a91b78a7f434..cc1333dcdf770 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/ctypes.rs:60:28 + --> $DIR/ctypes.rs:69:28 | LL | pub fn ptr_type1(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -8,18 +8,18 @@ LL | pub fn ptr_type1(size: *const Foo); = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/ctypes.rs:28:1 + --> $DIR/ctypes.rs:31:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/ctypes.rs:5:9 + --> $DIR/ctypes.rs:7:9 | LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/ctypes.rs:61:28 + --> $DIR/ctypes.rs:70:28 | LL | pub fn ptr_type2(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -28,13 +28,13 @@ LL | pub fn ptr_type2(size: *const Foo); = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here - --> $DIR/ctypes.rs:28:1 + --> $DIR/ctypes.rs:31:1 | LL | pub struct Foo; | ^^^^^^^^^^^^^^ error: `extern` block uses type `((),)`, which is not FFI-safe - --> $DIR/ctypes.rs:63:25 + --> $DIR/ctypes.rs:72:25 | LL | pub fn ptr_tuple(p: *const ((),)); | ^^^^^^^^^^^^ not FFI-safe @@ -44,7 +44,7 @@ LL | pub fn ptr_tuple(p: *const ((),)); = note: tuples have unspecified layout error: `extern` block uses type `&[u32]`, which is not FFI-safe - --> $DIR/ctypes.rs:64:26 + --> $DIR/ctypes.rs:73:26 | LL | pub fn slice_type(p: &[u32]); | ^^^^^^ not FFI-safe @@ -53,7 +53,7 @@ LL | pub fn slice_type(p: &[u32]); = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/ctypes.rs:65:24 + --> $DIR/ctypes.rs:74:24 | LL | pub fn str_type(p: &str); | ^^^^ not FFI-safe @@ -62,7 +62,7 @@ LL | pub fn str_type(p: &str); = 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:68:25 + --> $DIR/ctypes.rs:77:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -71,7 +71,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/ctypes.rs:69:25 + --> $DIR/ctypes.rs:78:25 | LL | pub fn i128_type(p: i128); | ^^^^ not FFI-safe @@ -79,15 +79,24 @@ LL | pub fn i128_type(p: i128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:70:25 + --> $DIR/ctypes.rs:79: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:80:27 + | +LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead + = note: integers constrained to a given range 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:71:26 + --> $DIR/ctypes.rs:82:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -95,7 +104,7 @@ LL | pub fn trait_type(p: &dyn Bar); = 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:72:26 + --> $DIR/ctypes.rs:83:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -104,7 +113,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/ctypes.rs:73:27 + --> $DIR/ctypes.rs:84:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -113,7 +122,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/ctypes.rs:74:25 + --> $DIR/ctypes.rs:85:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -121,26 +130,26 @@ LL | pub fn zero_size(p: ZeroSize); = help: consider adding a member to this struct = note: this struct has no fields note: the type is defined here - --> $DIR/ctypes.rs:24:1 + --> $DIR/ctypes.rs:27:1 | LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/ctypes.rs:75:33 + --> $DIR/ctypes.rs:86: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:57:1 + --> $DIR/ctypes.rs:66:1 | LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/ctypes.rs:78:12 + --> $DIR/ctypes.rs:89:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -148,7 +157,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:79:23 + --> $DIR/ctypes.rs:90:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -157,7 +166,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:80:24 + --> $DIR/ctypes.rs:91:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -166,7 +175,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/ctypes.rs:82:32 + --> $DIR/ctypes.rs:93:32 | LL | pub fn transparent_i128(p: TransparentI128); | ^^^^^^^^^^^^^^^ not FFI-safe @@ -174,7 +183,7 @@ LL | pub fn transparent_i128(p: TransparentI128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/ctypes.rs:83:31 + --> $DIR/ctypes.rs:94:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -183,7 +192,7 @@ LL | pub fn transparent_str(p: TransparentStr); = 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:85:27 + --> $DIR/ctypes.rs:96:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -191,8 +200,44 @@ LL | pub fn raw_array(arr: [u8; 8]); = help: consider passing a pointer to the array = note: passing raw arrays by value is not FFI-safe +error: `extern` block uses type `char`, which is not FFI-safe + --> $DIR/ctypes.rs:97:36 + | +LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), Box>)` is FFI-unsafe due to `char` + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `extern` block uses type `&dyn Debug`, which is not FFI-safe + --> $DIR/ctypes.rs:97:36 + | +LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), Box>)` 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` block uses type `&[u8]`, which is not FFI-safe + --> $DIR/ctypes.rs:97:36 + | +LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), Box>)` is FFI-unsafe due to `Box>` + = note: this reference (`Box>`) is ABI-compatible with a C pointer, but `TwoBadTypes<'_>` itself does not have a C layout + = note: this struct/enum/union (`TwoBadTypes<'_>`) is FFI-unsafe due to a `&[u8]` field +note: the type is defined here + --> $DIR/ctypes.rs:60: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:88:47 + --> $DIR/ctypes.rs:104:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -200,7 +245,7 @@ LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); = 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:90:26 + --> $DIR/ctypes.rs:106:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -209,7 +254,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/ctypes.rs:92:26 + --> $DIR/ctypes.rs:108:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -218,7 +263,7 @@ LL | pub fn no_niche_b(b: Option>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:95:34 + --> $DIR/ctypes.rs:111:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -226,12 +271,12 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:96:40 + --> $DIR/ctypes.rs:112: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 24 previous errors +error: aborting due to 28 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-113436-1.stderr b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr index f01dc3b6e0d1e..45cdacc34318e 100644 --- a/tests/ui/lint/improper_ctypes/lint-113436-1.stderr +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr @@ -4,6 +4,12 @@ error: `extern` fn uses type `NotSafe`, which is not FFI-safe 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)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here @@ -23,6 +29,12 @@ error: `extern` fn uses type `NotSafe`, which is not FFI-safe 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)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here diff --git a/tests/ui/lint/improper_ctypes/lint-73249-3.stderr b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr index ebc9eb5eb8274..431c80eb398ce 100644 --- a/tests/ui/lint/improper_ctypes/lint-73249-3.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr @@ -4,6 +4,12 @@ error: `extern` block uses type `Qux`, which is not FFI-safe 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:15: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 diff --git a/tests/ui/lint/improper_ctypes/lint-cstr.rs b/tests/ui/lint/improper_ctypes/lint-cstr.rs index c4de5a44a9623..aa7afd12ee587 100644 --- a/tests/ui/lint/improper_ctypes/lint-cstr.rs +++ b/tests/ui/lint/improper_ctypes/lint-cstr.rs @@ -6,24 +6,24 @@ 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()` + //~| 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` instead, and use `CStr::as_ptr()` + //~| 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 no_special_help_for_mut_cstring(s: *mut CString); + fn take_cstring_ptr_mut(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 + //~| HELP consider passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer - fn no_special_help_for_mut_cstring_ref(s: &mut CString); + fn take_cstring_ref_mut(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 + //~| 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) {} @@ -31,6 +31,10 @@ extern "C" fn rust_take_cstr_ref(s: &CStr) {} //~| 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) {} +//~| 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 index a50d5db4aacc1..ca5248b8ea888 100644 --- a/tests/ui/lint/improper_ctypes/lint-cstr.stderr +++ b/tests/ui/lint/improper_ctypes/lint-cstr.stderr @@ -4,7 +4,9 @@ error: `extern` block uses type `CStr`, which is not FFI-safe LL | fn take_cstr(s: CStr); | ^^^^ not FFI-safe | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = 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 @@ -18,8 +20,9 @@ error: `extern` block uses type `&CStr`, which is not FFI-safe 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: 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 @@ -27,7 +30,9 @@ error: `extern` block uses type `CString`, which is not FFI-safe LL | fn take_cstring(s: CString); | ^^^^^^^ not FFI-safe | - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = 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 @@ -36,29 +41,26 @@ error: `extern` block uses type `CString`, which is not FFI-safe LL | fn take_cstring_ref(s: &CString); | ^^^^^^^^ not FFI-safe | - = note: this reference (`&CString`) is ABI-compatible with a C pointer, but `CString` itself does not have a C layout - = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = 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:43 + --> $DIR/lint-cstr.rs:20:32 | -LL | fn no_special_help_for_mut_cstring(s: *mut CString); - | ^^^^^^^^^^^^ not FFI-safe +LL | fn take_cstring_ptr_mut(s: *mut CString); + | ^^^^^^^^^^^^ not FFI-safe | - = note: this reference (`*mut CString`) is ABI-compatible with a C pointer, but `CString` itself does not have a C layout - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = 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:47 + --> $DIR/lint-cstr.rs:24:32 | -LL | fn no_special_help_for_mut_cstring_ref(s: &mut CString); - | ^^^^^^^^^^^^ not FFI-safe +LL | fn take_cstring_ref_mut(s: &mut CString); + | ^^^^^^^^^^^^ not FFI-safe | - = note: this reference (`&mut CString`) is ABI-compatible with a C pointer, but `CString` itself does not have a C layout - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = 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 @@ -66,8 +68,9 @@ error: `extern` fn uses type `&CStr`, which is not FFI-safe 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: 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 | @@ -80,8 +83,28 @@ error: `extern` fn uses type `CString`, which is not FFI-safe 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()` + = 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 8 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs index e16ff9573fd18..fa23b7b9c514f 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.rs +++ b/tests/ui/lint/improper_ctypes/lint-fn.rs @@ -74,8 +74,10 @@ pub extern "C" fn str_type(p: &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]>` @@ -130,11 +132,15 @@ pub extern "C" fn transparent_fn(p: TransparentBadFn) { } 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 `&mut StructWithProjectionAndLifetime<'_>` +// note: the type translation might be a little eager for +// `::It` pub extern "C" fn good7(fptr: extern "C" fn() -> ()) { } @@ -150,7 +156,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 `&TransparentInt` pub extern "C" fn good15(p: TransparentLifetime) { } diff --git a/tests/ui/lint/improper_ctypes/lint-fn.stderr b/tests/ui/lint/improper_ctypes/lint-fn.stderr index a14c6c6108ece..e024efce4990e 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.stderr +++ b/tests/ui/lint/improper_ctypes/lint-fn.stderr @@ -21,8 +21,18 @@ LL | pub extern "C" fn str_type(p: &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 `Box`, which is not FFI-safe + --> $DIR/lint-fn.rs:76: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 and references 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:80:34 + --> $DIR/lint-fn.rs:82:34 | LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } | ^^^^^^^^^ not FFI-safe @@ -31,7 +41,7 @@ LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } = 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:83:35 + --> $DIR/lint-fn.rs:85:35 | LL | pub extern "C" fn boxed_string(p: Box) { } | ^^^^^^^^ not FFI-safe @@ -40,7 +50,7 @@ LL | pub extern "C" fn boxed_string(p: Box) { } = 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:34 + --> $DIR/lint-fn.rs:88:34 | LL | pub extern "C" fn boxed_trait(p: Box) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -48,7 +58,7 @@ LL | pub extern "C" fn boxed_trait(p: Box) { } = 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:89:32 + --> $DIR/lint-fn.rs:91:32 | LL | pub extern "C" fn char_type(p: char) { } | ^^^^ not FFI-safe @@ -57,7 +67,7 @@ LL | pub extern "C" fn char_type(p: char) { } = note: the `char` type has no C equivalent error: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/lint-fn.rs:92:32 + --> $DIR/lint-fn.rs:94:32 | LL | pub extern "C" fn i128_type(p: i128) { } | ^^^^ not FFI-safe @@ -65,7 +75,7 @@ LL | pub extern "C" fn i128_type(p: i128) { } = 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:95:32 + --> $DIR/lint-fn.rs:97:32 | LL | pub extern "C" fn u128_type(p: u128) { } | ^^^^ not FFI-safe @@ -73,7 +83,7 @@ LL | pub extern "C" fn u128_type(p: u128) { } = 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:98:33 + --> $DIR/lint-fn.rs:100:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -82,7 +92,7 @@ LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } = note: tuples have unspecified layout error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-fn.rs:101:34 + --> $DIR/lint-fn.rs:103:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -91,7 +101,7 @@ LL | pub extern "C" fn tuple_type2(p: I32Pair) { } = note: tuples have unspecified layout error: `extern` fn uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-fn.rs:104:32 + --> $DIR/lint-fn.rs:106:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -105,7 +115,7 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:107:40 + --> $DIR/lint-fn.rs:109:40 | LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -118,7 +128,7 @@ LL | pub struct ZeroSizeWithPhantomData(PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:110:51 + --> $DIR/lint-fn.rs:112:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -126,7 +136,7 @@ LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-fn.rs:115:30 + --> $DIR/lint-fn.rs:117:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -135,7 +145,7 @@ LL | pub extern "C" fn fn_type(p: RustFn) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-fn.rs:118:31 + --> $DIR/lint-fn.rs:120:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -144,7 +154,7 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/lint-fn.rs:123:39 + --> $DIR/lint-fn.rs:125:39 | LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } | ^^^^^^^^^^^^^^^ not FFI-safe @@ -152,7 +162,7 @@ LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } = 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-fn.rs:126:38 + --> $DIR/lint-fn.rs:128:38 | LL | pub extern "C" fn transparent_str(p: TransparentStr) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -160,8 +170,44 @@ LL | pub extern "C" fn transparent_str(p: TransparentStr) { } = 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 `&[u8; 4]`, which is not FFI-safe + --> $DIR/lint-fn.rs:135: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 and references 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 `&mut StructWithProjectionAndLifetime<'_>`, which is not FFI-safe + --> $DIR/lint-fn.rs:140: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:18:1 + | +LL | pub struct StructWithProjectionAndLifetime<'a>( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using a raw pointer, or wrapping `&mut StructWithProjectionAndLifetime<'_>` in an `Option<_>` + = note: boxes and references 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 `&TransparentInt`, which is not FFI-safe + --> $DIR/lint-fn.rs:159:51 + | +LL | pub extern "C" fn argument_with_assumptions_14(p: TransparentRef) { } + | ^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&TransparentInt` in an `Option<_>` + = note: boxes and references 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:172:43 + --> $DIR/lint-fn.rs:179:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -169,7 +215,7 @@ LL | pub extern "C" fn unused_generic2() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-fn.rs:185:39 + --> $DIR/lint-fn.rs:192:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -178,7 +224,7 @@ LL | pub extern "C" fn used_generic4(x: Vec) { } = note: this struct has unspecified layout error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-fn.rs:188:41 + --> $DIR/lint-fn.rs:195:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe @@ -186,5 +232,5 @@ LL | pub extern "C" fn used_generic5() -> Vec { = 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 +error: aborting due to 24 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs index 27bcfcf0e9a0c..6bbb848762eba 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs @@ -47,10 +47,10 @@ impl TemplateStruct { extern "C" fn tstruct_sum( // Ref[Struct] - slf: &TemplateStruct - // Alias ...not Inherent. dangit -) -> TemplateStruct::Out { - slf.one + slf.two + slf: Option<&TemplateStruct> + // Option> ...not Inherent. dangit +) -> Option::Out>> { + Some(Box::new(slf?.one + slf?.two)) } #[repr(C)] @@ -87,7 +87,7 @@ pub trait TimesTwo: std::ops::Add + Sized + Clone //} extern "C" fn t2_box( // Box[Param] - self: Box + self: Box, // Alias ) -> as std::ops::Add>>::Output { self.clone() + self @@ -161,36 +161,35 @@ extern "C" fn all_ty_kinds<'a,const N:usize,T>( 3_usize } -extern "C" { -fn all_ty_kinds_in_ptr( +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, //~ ERROR: uses type `String` + s: *const String, // Ptr[Str] s2: *const str, //~ ERROR: uses type `*const str` // Ptr[Char] - c: *const char, //~ ERROR: uses type `char` + c: *const char, // Ptr[Slice] s3: *const [u8], //~ ERROR: uses type `*const [u8]` - // deactivated here, because this is a function *declaration* (param N unacceptable) - // s4: *const [u8;N], + // Ptr[Array] (this gets caught outside of the code we want to test) + s4: *const [u8;N], // Ptr[Tuple] - p: *const (u8,u8), //~ ERROR: uses type `(u8, u8)` - // deactivated here, because this is a function *declaration* (pattern unacceptable) - // (p2, p3):(*const u8, *const u8), + p: *const (u8,u8), + // Tuple + (p2, p3):(*const u8, *const u8), // Pat nz: *const pattern_type!(u32 is 1..), //~ ERROR: uses type `(u32) is 1..=` - // deactivated here, because this is a function *declaration* (pattern unacceptable) - //SomeStruct{b: ref p4,..}: & SomeStruct, + // Ptr[Struct] + SomeStruct{b: ref p4,..}: & SomeStruct, // Ptr[Union] u2: *const SomeUnion, // Ptr[Enum], e: *const SomeEnum, - // deactivated here, because this is a function *declaration* (impl type unacceptable) - //d: *const impl Clone, - // deactivated here, because this is a function *declaration* (type param unacceptable) - //t: *const T, + // Param + d: *const impl Clone, + // Param + t: *const T, // Ptr[Foreign] e2: *mut ExtType, // Ptr[Struct] @@ -199,46 +198,48 @@ fn all_ty_kinds_in_ptr( x: *const !, //r1: &u8, r2: *const u8, r3: Box, // Ptr[FnPtr] - f2: *const fn(u8)->u8, //~ ERROR: uses type `fn(u8) -> u8` + 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` - // deactivated here, because this is a function *declaration* (impl type unacceptable) - //a: *const impl async Fn(u8)->u8, + // Ptr[Param], + a: *const impl async Fn(u8)->u8, // Alias (this gets caught outside of the code we want to test) -) -> *const dyn std::fmt::Debug; //~ ERROR: uses type `*const dyn Debug` +) -> *const dyn std::fmt::Debug { //~ ERROR: uses type `*const dyn Debug` + todo!() } -extern "C" fn all_ty_kinds_in_ref<'a, const N:usize,T>( +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, + s: &String, //~ ERROR: uses type `String` // Ref[Str] s2: &str, //~ ERROR: uses type `&str` // Ref[Char] - c: &char, + c: &char, //~ ERROR: uses type `char` // Ref[Slice] s3: &[u8], //~ ERROR: uses type `&[u8]` - // Ref[Array] (this gets caught outside of the code we want to test) - s4: &[u8;N], + // deactivated here, because this is a function *declaration* (param N unacceptable) + // s4: &[u8;N], // Ref[Tuple] - p: &(u8, u8), - // also Tuple - (p2, p3):(&u8, &u8), //~ ERROR: uses type `(&u8, &u8)` + p: &(u8, u8), //~ ERROR: uses type `(u8, u8)` + // deactivated here, because this is a function *declaration* (patterns unacceptable) + // (p2, p3):(&u8, &u8), //~ ERROR: uses type `(&u8, &u8)` // Pat nz: &pattern_type!(u32 is 1..), - // Ref[Struct] - SomeStruct{b: ref p4,..}: &SomeStruct, + // deactivated here, because this is a function *declaration* (pattern unacceptable) + // SomeStruct{b: ref p4,..}: &SomeStruct, // Ref[Union] u2: &SomeUnion, // Ref[Enum], e: &SomeEnum, - // Ref[Param] - d: &impl Clone, - // Ref[Param] - t: &T, + // 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] @@ -247,65 +248,65 @@ extern "C" fn all_ty_kinds_in_ref<'a, const N:usize,T>( x: &!, //r1: &u8, r2: &u8, r3: Box, // Ref[FnPtr] - f2: &fn(u8)->u8, + f2: &fn(u8)->u8, //~ ERROR: uses type `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` - // Ref[Param], - a: &impl async Fn(u8)->u8, + // deactivated here, because this is a function *declaration* (impl type unacceptable) + // a: &impl async Fn(u8)->u8, // Ref[Dynamic] (this gets caught outside of the code we want to test) -) -> &'a dyn std::fmt::Debug { //~ ERROR: uses type `&dyn Debug` - i +) -> &'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: Box, i: Box, f: Box, b: Box, + u: Option>, i: Option>, f: Option>, b: Option>, // Box[Struct] - s: Box, + s: Option>, // Box[Str] s2: Box, //~ ERROR: uses type `Box` // Box[Char] - c: Box, + 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: Box<[u8;N]>, + s4: Option>, // Box[Tuple] - p: Box<(u8,u8)>, + p: Option>, // also Tuple (p2,p3):(Box, Box), //~ ERROR: uses type `(Box, Box)` // Pat - nz: Box, + nz: Option>, // Ref[Struct] SomeStruct{b: ref p4,..}: &SomeStruct, // Box[Union] - u2: Box, + u2: Option>, // Box[Enum], - e: Box, + e: Option>, // Box[Param] - d: Box, + d: Option>, // Box[Param] - t: Box, + t: Option>, // Box[Foreign] - e2: Box, + e2: Option>, // Box[Struct] e3: Box, //~ ERROR: uses type `Box` // Box[Never] - x: Box, + // (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>, + f2: Boxu8>, //~ ERROR: uses type `Box u8>` // Box[Dynamic] f3: Boxu8>, //~ ERROR: uses type `Box u8>` // Box[Dynamic] d2: Box>, //~ ERROR: uses type `Box>` - // Box[Param], - a: Boxu8>, + // Option[Box[Param]], + a: Optionu8>>, // Box[Dynamic] (this gets caught outside of the code we want to test) ) -> Box { //~ ERROR: uses type `Box` - i + u.unwrap() } fn main() {} diff --git a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr index 411086159f615..6bd795be7e586 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -82,7 +82,7 @@ LL | nz: pattern_type!(u32 is 1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = help: consider using the base type instead - = note: pattern types have no C equivalent + = note: integers constrained to a given range 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:147:7 @@ -125,23 +125,8 @@ LL | ) -> impl std::fmt::Debug { | = note: opaque types have no C equivalent -error: `extern` block uses type `String`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:169:6 - | -LL | s: *const String, - | ^^^^^^^^^^^^^ not FFI-safe - | - = note: this reference (`*const String`) is ABI-compatible with a C pointer, but `String` itself does not have a C layout - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout -note: the lint level is defined here - --> $DIR/lint-tykind-fuzz.rs:5:9 - | -LL | #![deny(improper_ctypes,improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^ - -error: `extern` block uses type `*const str`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:171:7 +error: `extern` fn uses type `*const str`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:170:7 | LL | s2: *const str, | ^^^^^^^^^^ not FFI-safe @@ -149,18 +134,8 @@ LL | s2: *const str, = 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` block uses type `char`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:173:6 - | -LL | c: *const char, - | ^^^^^^^^^^^^ not FFI-safe - | - = note: this reference (`*const char`) is ABI-compatible with a C pointer, but `char` itself does not have a C layout - = help: consider using `u32` or `libc::wchar_t` instead - = note: the `char` type has no C equivalent - -error: `extern` block uses type `*const [u8]`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:175:7 +error: `extern` fn uses type `*const [u8]`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:174:7 | LL | s3: *const [u8], | ^^^^^^^^^^^ not FFI-safe @@ -168,70 +143,74 @@ LL | s3: *const [u8], = 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` block uses type `(u8, u8)`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:179:6 +error: `extern` fn uses type `(*const u8, *const u8)`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:180:12 | -LL | p: *const (u8,u8), - | ^^^^^^^^^^^^^^ not FFI-safe +LL | (p2, p3):(*const u8, *const u8), + | ^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = note: this reference (`*const (u8, u8)`) is ABI-compatible with a C pointer, but `(u8, u8)` itself does not have a C layout = help: consider using a struct instead = note: tuples have unspecified layout -error: `extern` block uses type `(u32) is 1..=`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:183:7 +error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:184:29 | -LL | nz: *const pattern_type!(u32 is 1..), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | SomeStruct{b: ref p4,..}: & SomeStruct, + | ^^^^^^^^^^^^ not FFI-safe | - = note: this reference (`*const (u32) is 1..=`) is ABI-compatible with a C pointer, but `(u32) is 1..=` itself does not have a C layout - = help: consider using the base type instead - = note: pattern types have no C equivalent + = help: consider using a raw pointer, or wrapping `&SomeStruct` in an `Option<_>` + = note: boxes and references 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` block uses type `*const StructWithDyn`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:197:7 +error: `extern` fn uses type `*const StructWithDyn`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:196: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` block uses type `fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:202:7 - | -LL | f2: *const fn(u8)->u8, - | ^^^^^^^^^^^^^^^^^ not FFI-safe - | - = note: this reference (`*const fn(u8) -> u8`) is ABI-compatible with a C pointer, but `fn(u8) -> u8` itself does not have a C layout - = help: consider using an `extern fn(...) -> ...` function pointer instead - = note: this function pointer has Rust-specific calling convention - -error: `extern` block uses type `*const dyn Fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:204:7 +error: `extern` fn uses type `*const dyn Fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:203: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` block uses type `*const dyn PartialOrd`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:206:7 +error: `extern` fn uses type `*const dyn PartialOrd`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:205: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` block uses type `*const dyn Debug`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:210:6 +error: `extern` fn uses type `*const dyn Debug`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:209:6 | -LL | ) -> *const dyn std::fmt::Debug; +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` fn uses type `&str`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:219:7 +error: `extern` block uses type `String`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:218:6 + | +LL | s: &String, + | ^^^^^^^ not FFI-safe + | + = note: this reference (`&String`) is ABI-compatible with a C pointer, but `String` itself does not have a C layout + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the lint level is defined here + --> $DIR/lint-tykind-fuzz.rs:5:9 + | +LL | #![deny(improper_ctypes,improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^ + +error: `extern` block uses type `&str`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:220:7 | LL | s2: &str, | ^^^^ not FFI-safe @@ -239,8 +218,18 @@ LL | s2: &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 `&[u8]`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:223:7 +error: `extern` block uses type `char`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:222:6 + | +LL | c: &char, + | ^^^^^ not FFI-safe + | + = note: this reference (`&char`) is ABI-compatible with a C pointer, but `char` itself does not have a C layout + = help: consider using `u32` or `libc::wchar_t` instead + = note: the `char` type has no C equivalent + +error: `extern` block uses type `&[u8]`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:224:7 | LL | s3: &[u8], | ^^^^^ not FFI-safe @@ -248,43 +237,54 @@ LL | s3: &[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 -error: `extern` fn uses type `(&u8, &u8)`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:229:12 +error: `extern` block uses type `(u8, u8)`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:228:6 | -LL | (p2, p3):(&u8, &u8), - | ^^^^^^^^^^ not FFI-safe +LL | p: &(u8, u8), + | ^^^^^^^^^ not FFI-safe | + = note: this reference (`&(u8, u8)`) is ABI-compatible with a C pointer, but `(u8, u8)` itself does not have a C layout = help: consider using a struct instead = note: tuples have unspecified layout -error: `extern` fn uses type `&StructWithDyn`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:245:7 +error: `extern` block uses type `&StructWithDyn`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:246: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 `&dyn Fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:252:7 +error: `extern` block uses type `fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:251:7 + | +LL | f2: &fn(u8)->u8, + | ^^^^^^^^^^^ not FFI-safe + | + = note: this reference (`&fn(u8) -> u8`) is ABI-compatible with a C pointer, but `fn(u8) -> u8` itself does not have a C layout + = help: consider using an `extern fn(...) -> ...` function pointer instead + = note: this function pointer has Rust-specific calling convention + +error: `extern` block uses type `&dyn Fn(u8) -> u8`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:253: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` fn uses type `&dyn PartialOrd`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:254:7 +error: `extern` block uses type `&dyn PartialOrd`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:255: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 `&dyn Debug`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:258:6 +error: `extern` block uses type `&dyn Debug`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:259:6 | -LL | ) -> &'a dyn std::fmt::Debug { +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 @@ -298,6 +298,15 @@ LL | s2: Box, = 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:270:6 + | +LL | c: Box, + | ^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` + = note: boxes and references 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:272:7 | @@ -316,6 +325,16 @@ LL | (p2,p3):(Box, Box), = 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:282:29 + | +LL | SomeStruct{b: ref p4,..}: &SomeStruct, + | ^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&SomeStruct` in an `Option<_>` + = note: boxes and references 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:294:7 | @@ -324,8 +343,27 @@ LL | e3: Box, | = 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:297:6 + | +LL | x: Box, + | ^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` + = note: boxes and references 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:300:7 + | +LL | f2: Boxu8>, + | ^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `Box u8>` in an `Option<_>` + = note: boxes and references 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:301:7 + --> $DIR/lint-tykind-fuzz.rs:302:7 | LL | f3: Boxu8>, | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -333,7 +371,7 @@ LL | f3: Boxu8>, = 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:303:7 + --> $DIR/lint-tykind-fuzz.rs:304:7 | LL | d2: Box>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -341,12 +379,12 @@ LL | d2: Box>, = 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:307:6 + --> $DIR/lint-tykind-fuzz.rs:308:6 | LL | ) -> Box { | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: this box for an unsized type contains metadata, which makes it incompatible with a C pointer -error: aborting due to 38 previous errors; 1 warning emitted +error: aborting due to 42 previous errors; 1 warning emitted 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..723d0aa3d54c3 100644 --- a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr +++ b/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr @@ -23,6 +23,12 @@ error: `extern` block uses type `A`, which is not FFI-safe LL | fn bar(x: B); | ^ not FFI-safe | + = note: this struct/enum/union (`B`) is FFI-unsafe due to a `A` field +note: the type is defined here + --> $DIR/repr-rust-is-undefined.rs:13:1 + | +LL | struct B { + | ^^^^^^^^ = 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 @@ -51,6 +57,12 @@ error: `extern` block uses type `A`, which is not FFI-safe LL | fn quux(x: B2); | ^^ not FFI-safe | + = note: this struct/enum/union (`B`) is FFI-unsafe due to a `A` field +note: the type is defined here + --> $DIR/repr-rust-is-undefined.rs:13:1 + | +LL | struct B { + | ^^^^^^^^ = 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 @@ -65,6 +77,12 @@ error: `extern` block uses type `A`, which is not FFI-safe LL | fn fred(x: D); | ^ not FFI-safe | + = 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)]` or `#[repr(transparent)]` attribute to this struct = note: this struct has unspecified layout note: the type is defined here From 5cb24ccda6c3179058996f8c8f08cb5c2bb781fd Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sat, 18 Jan 2025 21:17:39 +0100 Subject: [PATCH 09/15] lint ImproperCTypes: smoothing out some nits and un-tidy-ness --- compiler/rustc_lint/messages.ftl | 6 +- .../rustc_lint/src/types/improper_ctypes.rs | 168 +++++++----------- tests/ui/lint/improper_ctypes/ctypes.rs | 10 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 28 +-- .../improper_ctypes/lint-tykind-fuzz.stderr | 2 + 5 files changed, 88 insertions(+), 126 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index dcac2ca256e8d..b28f50eab8624 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -424,13 +424,13 @@ lint_improper_ctypes_ptr_validity_reason = lint_improper_ctypes_sized_ptr_to_unsafe_type = this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout -lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field 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_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 = this struct has no fields diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 77e74de64bcfc..322eb51ad1861 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -148,61 +148,25 @@ impl<'tcx> FfiResult<'tcx> { } impl<'tcx> std::ops::AddAssign> for FfiResult<'tcx> { - fn add_assign(&mut self, mut other: Self) { + 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 - // this function is awful to look but that's because matching mutable references consumes them (?!) - // the function itself imitates the following piece of non-compiling code: - - // match (self, other) { - // (Self::FfiUnsafe(_), _) => { - // // nothing to do - // }, - // (_, Self::FfiUnsafe(_)) => { - // *self = other; - // }, - // (Self::FfiPhantom(ref ty1),Self::FfiPhantom(ty2)) => { - // println!("whoops, both FfiPhantom: self({:?}) += other({:?})", ty1, ty2); - // }, - // (Self::FfiSafe,Self::FfiPhantom(_)) => { - // *self = other; - // }, - // (_, Self::FfiSafe) => { - // // nothing to do - // }, - // } - - let s_disc = std::mem::discriminant(self); - let o_disc = std::mem::discriminant(&other); - if s_disc == o_disc { - match (self, &mut other) { - (Self::FfiUnsafe(s_inner), Self::FfiUnsafe(o_inner)) => { - s_inner.append(o_inner); - } - (Self::FfiPhantom(ty1), Self::FfiPhantom(ty2)) => { - debug!("whoops: both FfiPhantom, self({:?}) += other({:?})", ty1, ty2); - } - (Self::FfiSafe, Self::FfiSafe) => {} - _ => unreachable!(), + match (self, other) { + (Self::FfiUnsafe(_), _) => { + // nothing to do } - } else { - if let Self::FfiUnsafe(_) = self { - return; + (myself, other @ Self::FfiUnsafe(_)) => { + *myself = other; } - match other { - Self::FfiUnsafe(o_inner) => { - // self is Safe or Phantom: Unsafe wins - *self = Self::FfiUnsafe(o_inner); - } - Self::FfiSafe => { - // self is always "wins" - return; - } - Self::FfiPhantom(o_inner) => { - // self is Safe: Phantom wins - *self = Self::FfiPhantom(o_inner); - } + (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 } } } @@ -215,7 +179,7 @@ impl<'tcx> std::ops::Add> for FfiResult<'tcx> { } } -/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it +/// 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) @@ -354,39 +318,51 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type } } +#[allow(non_snake_case)] +mod CTypesVisitorStateFlags { + pub(super) const NO_FLAGS: u8 = 0b0000; + /// for static variables (not used in functions) + pub(super) const STATIC: u8 = 0b0010; + /// for variables in function returns (implicitly: not for static variables) + pub(super) const FN_RETURN: u8 = 0b0100; + /// for variables in functions which are defined in rust (implicitly: not for static variables) + pub(super) const FN_DECLARED: u8 = 0b1000; +} + #[repr(u8)] #[derive(Clone, Copy, Debug)] enum CTypesVisitorState { - // bitflags: - // 0010: static - // 0100: function return - // 1000: used in declared function - StaticTy = 0b0010, - ArgumentTyInDefinition = 0b1000, - ReturnTyInDefinition = 0b1100, - ArgumentTyInDeclaration = 0b0000, - ReturnTyInDeclaration = 0b0100, + // uses bitflags from CTypesVisitorStateFlags + StaticTy = CTypesVisitorStateFlags::STATIC, + ArgumentTyInDefinition = CTypesVisitorStateFlags::FN_DECLARED, + ReturnTyInDefinition = + CTypesVisitorStateFlags::FN_DECLARED | CTypesVisitorStateFlags::FN_RETURN, + ArgumentTyInDeclaration = CTypesVisitorStateFlags::NO_FLAGS, + ReturnTyInDeclaration = CTypesVisitorStateFlags::FN_RETURN, } impl CTypesVisitorState { - /// wether the type is used (directly or not) in a static variable + /// whether the type is used (directly or not) in a static variable fn is_in_static(self) -> bool { - ((self as u8) & 0b0010) != 0 + use CTypesVisitorStateFlags::*; + ((self as u8) & STATIC) != 0 } - /// wether the type is used (directly or not) in a function, in return position + /// whether the type is used (directly or not) in a function, in return position fn is_in_function_return(self) -> bool { - let ret = ((self as u8) & 0b0100) != 0; + use CTypesVisitorStateFlags::*; + let ret = ((self as u8) & FN_RETURN) != 0; #[cfg(debug_assertions)] if ret { assert!(!self.is_in_static()); } ret } - /// wether the type is used (directly or not) in a defined function - /// in other words, wether or not we allow non-FFI-safe types behind a C pointer, + /// 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 { - let ret = ((self as u8) & 0b1000) != 0; + use CTypesVisitorStateFlags::*; + let ret = ((self as u8) & FN_DECLARED) != 0; #[cfg(debug_assertions)] if ret { assert!(!self.is_in_static()); @@ -394,11 +370,11 @@ impl CTypesVisitorState { ret } - /// wether the value for that type might come from the non-rust side of a FFI boundary + /// whether the value for that type might come from the non-rust side of a FFI boundary fn value_may_be_unchecked(self) -> bool { // function declarations are assumed to be rust-caller, non-rust-callee // function definitions are assumed to be maybe-not-rust-caller, rust-callee - // FnPtrs are... well, nothing's certain about anything. (TODO need more flags in enum?) + // FnPtrs are... well, nothing's certain about anything. (FIXME need more flags in enum?) // Same with statics. if self.is_in_static() { true @@ -411,7 +387,7 @@ impl CTypesVisitorState { } impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { - /// Checks wether an `extern "ABI" fn` function pointer is indeed FFI-safe to call + /// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call fn visit_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>, sig: Sig<'tcx>) -> FfiResult<'tcx> { use FfiResult::*; debug_assert!(!sig.abi().is_rustic_abi()); @@ -534,7 +510,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // 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 a value given by non-rust code? + // - 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 => { @@ -583,7 +559,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions. // (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't) - // wether they are FFI-safe or not does not depend on the indirections involved (&Self, &T, Box), + // 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) } @@ -603,6 +579,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; // 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, @@ -758,18 +735,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.variants().is_empty() { // Empty enums are implicitely handled as the never type: - // TODO think about the FFI-safety of functions that use that + // FIXME think about the FFI-safety of functions that use that 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(inner_ty) = repr_nullable_ptr( - self.cx.tcx, - self.cx.typing_env(), - ty, - ) { + if let Some(inner_ty) = repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty) { return self.visit_type(state, Some(ty), inner_ty); } @@ -781,11 +754,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } if let Some(IntegerType::Fixed(Integer::I128, _)) = def.repr().int { - return FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_128bit, - None, - ); + return FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_128bit, None); } let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); @@ -811,7 +780,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { .iter() .map(|variant| { self.visit_variant_fields(state, ty, def, variant, args) - // TODO: check that enums allow any (up to all) variants to be phantoms? + // 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?) .forbid_phantom() }) @@ -856,11 +825,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } match def.adt_kind() { AdtKind::Struct | AdtKind::Union => { - // I thought CStr (not CString) could not be reached here: - // - not using an indirection would cause a compile error prior to this lint + // 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 for some reason one can just go and write function *pointers* like that: - // `type Foo = extern "C" fn(::std::ffi::CStr);` + // 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()) { @@ -1107,10 +1075,7 @@ struct ImproperCTypesLint<'c, 'tcx> { } impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { - fn check_arg_for_power_alignment( - &mut self, - ty: Ty<'tcx>, - ) -> bool { + fn check_arg_for_power_alignment(&mut self, ty: Ty<'tcx>) -> bool { let tcx = self.cx.tcx; assert!(tcx.sess.target.os == "aix"); // Structs (under repr(C)) follow the power alignment rule if: @@ -1141,10 +1106,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { return false; } - fn check_struct_for_power_alignment( - &mut self, - item: &'tcx hir::Item<'tcx>, - ) { + fn check_struct_for_power_alignment(&mut self, item: &'tcx hir::Item<'tcx>) { let tcx = self.cx.tcx; let adt_def = tcx.adt_def(item.owner_id.to_def_id()); // repr(C) structs also with packed or aligned representation @@ -1228,7 +1190,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { (span, ffi_res) }) // even in function *definitions*, `FnPtr`s are always function declarations ...right? - // (TODO: we can't do that yet because one of rustc's crates can't compile if we do) + // (FIXME: we can't do that yet because one of rustc's crates can't compile if we do) .for_each(|(span, ffi_res)| self.process_ffi_result(span, ffi_res, fn_mode)); //.drain(); } @@ -1316,12 +1278,8 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { // this whole while block converts the arbitrarily-deep // FfiResult stack to an ImproperCTypesLayer Vec - while let ControlFlow::Continue(FfiUnsafeReason { - ty, - reason, - help, - inner, - }) = ffiresult_recursor + while let ControlFlow::Continue(FfiUnsafeReason { ty, reason, help, inner }) = + ffiresult_recursor { if let Some(layer) = cimproper_layers.last_mut() { layer.inner_ty = Some(ty.clone()); @@ -1384,7 +1342,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { match it.kind { hir::ForeignItemKind::Fn(sig, _, _) => { - if abi.is_rustic_abi() { + if abi.is_rustic_abi() { lint.check_fn_for_external_abi_fnptr( CItemKind::Declaration, it.owner_id.def_id, @@ -1426,7 +1384,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { // Structs are checked based on if they follow the power alignment // rule (under repr(C)). hir::ItemKind::Struct(..) => { - ImproperCTypesLint{cx}.check_struct_for_power_alignment(item); + ImproperCTypesLint { cx }.check_struct_for_power_alignment(item); } // See `check_field_def`.. hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} diff --git a/tests/ui/lint/improper_ctypes/ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs index dcdebb80594f4..e06d0721d5a28 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -94,11 +94,13 @@ extern "C" { pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `&str` 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: Box>)); - //~^ ERROR: uses type `char` - //~^^ ERROR: uses type `&dyn Debug` + pub fn multi_errors_per_arg( + f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>) + ); + //~^^ ERROR: uses type `char` + //~^^^ ERROR: uses type `&dyn Debug` // (possible FIXME: the in-struct `char` field doesn't get a warning due ^^) - //~^^^^ ERROR: uses type `&[u8]` + //~^^^^^ ERROR: uses type `&[u8]` pub fn struct_unsized_ptr_no_metadata(p: &UnsizedStructBecauseForeign); pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); //~ ERROR uses type `&UnsizedStructBecauseDyn` diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index cc1333dcdf770..a365bac891122 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -201,29 +201,29 @@ LL | pub fn raw_array(arr: [u8; 8]); = note: passing raw arrays by value is not FFI-safe error: `extern` block uses type `char`, which is not FFI-safe - --> $DIR/ctypes.rs:97:36 + --> $DIR/ctypes.rs:98:12 | -LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), Box>)` is FFI-unsafe due to `char` = help: consider using `u32` or `libc::wchar_t` instead = note: the `char` type has no C equivalent error: `extern` block uses type `&dyn Debug`, which is not FFI-safe - --> $DIR/ctypes.rs:97:36 + --> $DIR/ctypes.rs:98:12 | -LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), Box>)` 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` block uses type `&[u8]`, which is not FFI-safe - --> $DIR/ctypes.rs:97:36 + --> $DIR/ctypes.rs:98:12 | -LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | = note: the function pointer to `for<'a, 'b> extern "C" fn(char, &'a (dyn Debug + 'a), Box>)` is FFI-unsafe due to `Box>` = note: this reference (`Box>`) is ABI-compatible with a C pointer, but `TwoBadTypes<'_>` itself does not have a C layout @@ -237,7 +237,7 @@ LL | pub struct TwoBadTypes<'a> { = 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:104:47 + --> $DIR/ctypes.rs:106:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -245,7 +245,7 @@ LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); = 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:106:26 + --> $DIR/ctypes.rs:108:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -254,7 +254,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/ctypes.rs:108:26 + --> $DIR/ctypes.rs:110:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -263,7 +263,7 @@ LL | pub fn no_niche_b(b: Option>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:111:34 + --> $DIR/ctypes.rs:113:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -271,7 +271,7 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:112:40 + --> $DIR/ctypes.rs:114:40 | LL | pub static static_u128_array_type: [u128; 16]; | ^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr index 6bd795be7e586..f70b619aee1b0 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -307,6 +307,7 @@ LL | c: Box, = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` = note: boxes and references 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:272:7 | @@ -352,6 +353,7 @@ LL | x: Box, = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` = note: boxes and references 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:300:7 | From a4cded5244c088c1dd87bd87b31c56142e5dde10 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sun, 4 May 2025 16:07:41 +0200 Subject: [PATCH 10/15] lint ImproperCTypes: split the two lints into four total [...] - now the lint scans repr(C) struct/enum/union definitions - it now also scans method declarations in traits - many other changes in the underlying logic - some extra tests --- .../example/std_example.rs | 2 +- compiler/rustc_lint/messages.ftl | 27 +- compiler/rustc_lint/src/lib.rs | 14 +- compiler/rustc_lint/src/types.rs | 25 +- .../rustc_lint/src/types/improper_ctypes.rs | 1069 ++++++++++++----- compiler/rustc_llvm/src/lib.rs | 3 +- library/alloc/src/collections/btree/node.rs | 1 + library/panic_abort/src/lib.rs | 3 +- library/panic_unwind/src/gcc.rs | 2 + library/panic_unwind/src/lib.rs | 3 +- library/proc_macro/src/bridge/client.rs | 2 + .../clippy/tests/ui/inherent_to_string.rs | 2 +- .../simd_feature_flag_difference.rs | 2 +- .../crates/ide-db/src/generated/lints.rs | 18 +- tests/auxiliary/minicore.rs | 2 +- tests/ui/abi/abi-sysv64-arg-passing.rs | 3 +- tests/ui/abi/abi-sysv64-register-usage.rs | 2 +- tests/ui/abi/arm-unadjusted-intrinsic.rs | 1 + tests/ui/abi/compatibility.rs | 3 +- tests/ui/abi/extern/extern-pass-empty.rs | 3 +- tests/ui/abi/foreign/foreign-fn-with-byval.rs | 2 +- tests/ui/abi/large-byval-align.rs | 2 +- tests/ui/abi/nullable-pointer-ffi-compat.rs | 2 +- tests/ui/abi/simd-abi-checks-avx.rs | 2 +- tests/ui/abi/simd-abi-checks-empty-list.rs | 2 +- tests/ui/abi/simd-abi-checks-s390x.rs | 2 +- tests/ui/abi/simd-abi-checks-sse.rs | 2 +- ...sized-args-in-c-abi-issues-94223-115845.rs | 2 +- tests/ui/asm/issue-97490.rs | 4 +- tests/ui/asm/naked-functions-ffi.stderr | 2 +- .../asm/naked-functions-unused.aarch64.stderr | 72 +- tests/ui/asm/naked-functions-unused.rs | 9 +- .../asm/naked-functions-unused.x86_64.stderr | 73 +- tests/ui/attributes/export/exportable.rs | 2 +- .../backtrace/auxiliary/dylib-dep-helper.rs | 2 +- tests/ui/backtrace/dylib-dep.rs | 2 +- .../cmse-nonsecure-call/return-via-stack.rs | 2 +- .../cmse-nonsecure-call/via-registers.rs | 4 +- .../cmse-nonsecure-entry/params-via-stack.rs | 2 +- .../cmse-nonsecure-entry/return-via-stack.rs | 8 +- .../cmse-nonsecure-entry/via-registers.rs | 6 +- .../defaults/repr-c-issue-82792.rs | 1 + .../consts/const-extern-fn/const-extern-fn.rs | 4 +- .../consts/extra-const-ub/detect-extra-ub.rs | 1 + .../detect-extra-ub.with_flag.stderr | 4 +- .../extern-C-non-FFI-safe-arg-ice-52334.rs | 4 +- ...extern-C-non-FFI-safe-arg-ice-52334.stderr | 22 +- tests/ui/extern/extern-C-str-arg-ice-80125.rs | 4 +- .../extern/extern-C-str-arg-ice-80125.stderr | 21 +- tests/ui/extern/issue-16250.stderr | 4 +- tests/ui/hashmap/hashmap-memory.rs | 2 +- tests/ui/issues/issue-16441.rs | 2 +- tests/ui/issues/issue-26997.rs | 2 +- tests/ui/issues/issue-28600.rs | 2 +- tests/ui/issues/issue-38763.rs | 3 +- tests/ui/issues/issue-51907.rs | 6 +- tests/ui/layout/reprc-power-alignment.rs | 3 +- tests/ui/layout/reprc-power-alignment.stderr | 54 +- tests/ui/lint/clashing-extern-fn.rs | 3 + tests/ui/lint/clashing-extern-fn.stderr | 39 +- tests/ui/lint/extern-C-fnptr-lints-slices.rs | 4 +- .../lint/extern-C-fnptr-lints-slices.stderr | 6 +- .../allow-phantomdata-in-ffi.rs | 2 +- .../improper_ctypes/allow_improper_ctypes.rs | 160 +++ .../allow_improper_ctypes.stderr | 173 +++ .../auxiliary/outer_crate_types.rs | 41 + tests/ui/lint/improper_ctypes/ctypes.rs | 29 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 189 +-- .../ui/lint/improper_ctypes/lint-113436-1.rs | 8 +- .../lint/improper_ctypes/lint-113436-1.stderr | 41 +- tests/ui/lint/improper_ctypes/lint-73249-2.rs | 6 +- .../lint/improper_ctypes/lint-73249-2.stderr | 17 +- tests/ui/lint/improper_ctypes/lint-73249-3.rs | 6 +- .../lint/improper_ctypes/lint-73249-3.stderr | 23 +- tests/ui/lint/improper_ctypes/lint-73249-5.rs | 4 +- .../lint/improper_ctypes/lint-73249-5.stderr | 10 +- tests/ui/lint/improper_ctypes/lint-94223.rs | 29 +- .../ui/lint/improper_ctypes/lint-94223.stderr | 78 +- tests/ui/lint/improper_ctypes/lint-cstr.rs | 2 +- .../ui/lint/improper_ctypes/lint-cstr.stderr | 6 +- .../ui/lint/improper_ctypes/lint-enum.stderr | 60 +- tests/ui/lint/improper_ctypes/lint-fn.rs | 19 +- tests/ui/lint/improper_ctypes/lint-fn.stderr | 140 ++- .../lint-option-nonnull-unsized.rs | 2 +- .../lint-option-nonnull-unsized.stderr | 4 +- .../improper_ctypes/lint-transparent-help.rs | 21 + .../lint-transparent-help.stderr | 57 + .../lint/improper_ctypes/lint-tykind-fuzz.rs | 99 +- .../improper_ctypes/lint-tykind-fuzz.stderr | 293 +++-- .../lint/improper_ctypes/mustpass-113436.rs | 2 +- .../improper_ctypes/repr-rust-is-undefined.rs | 9 +- .../repr-rust-is-undefined.stderr | 74 +- .../improper_ctypes_definitions_ice_134060.rs | 1 + ...roper_ctypes_definitions_ice_134060.stderr | 14 +- tests/ui/lint/unused/unused-attr-duplicate.rs | 2 +- tests/ui/lint/wasm_c_abi_transition.rs | 3 +- tests/ui/lint/wasm_c_abi_transition.stderr | 18 +- tests/ui/mir/mir_cast_fn_ret.rs | 4 +- tests/ui/mir/mir_codegen_calls.rs | 2 +- .../offset-of/offset-of-slice-normalized.rs | 3 +- tests/ui/offset-of/offset-of-slice.rs | 3 +- tests/ui/repr/align-with-extern-c-fn.rs | 2 +- .../repr/repr-transparent-issue-87496.stderr | 2 +- .../extern_crate_improper.stderr | 6 +- .../improper_ctypes/same_crate_proper.rs | 6 +- tests/ui/structs-enums/align-struct.rs | 1 + .../enum-non-c-like-repr-c-and-int.rs | 2 + .../structs-enums/enum-non-c-like-repr-c.rs | 2 + .../structs-enums/enum-non-c-like-repr-int.rs | 2 + .../abstraction/const_generic_fn.rs | 1 + .../arrays/should_have_correct_length.rs | 1 + .../arrays/should_inherit_alignment.rs | 1 + .../transmutability/references/u8-to-unit.rs | 1 + .../references/unit-to-itself.rs | 1 + ...ould_accept_if_dst_has_safety_invariant.rs | 1 + ...ould_accept_if_src_has_safety_invariant.rs | 1 + ...ould_accept_if_src_has_safety_invariant.rs | 1 + .../structs/repr/should_handle_all.rs | 1 + .../unions/repr/should_handle_align.rs | 1 + .../unions/repr/should_handle_packed.rs | 1 + tests/ui/union/union-repr-c.stderr | 2 +- 121 files changed, 2366 insertions(+), 906 deletions(-) create mode 100644 tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs create mode 100644 tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr create mode 100644 tests/ui/lint/improper_ctypes/auxiliary/outer_crate_types.rs create mode 100644 tests/ui/lint/improper_ctypes/lint-transparent-help.rs create mode 100644 tests/ui/lint/improper_ctypes/lint-transparent-help.stderr 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/messages.ftl b/compiler/rustc_lint/messages.ftl index b28f50eab8624..44c78a9ff2a16 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -368,7 +368,7 @@ lint_implicit_unsafe_autorefs = implicit autoref creates a reference to the dere .method_def = method calls to `{$method_name}` require a reference .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 @@ -418,35 +418,34 @@ lint_improper_ctypes_pat_intrange_reason = integers constrained to a given range lint_improper_ctypes_ptr_validity_help = consider using a raw pointer, or wrapping `{$ty}` in an `Option<_>` lint_improper_ctypes_ptr_validity_reason = - boxes and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + 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_sized_ptr_to_unsafe_type = - this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout - 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_reason = string slices have no C equivalent +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 = 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_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_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 diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 4ff586a79a6ec..29223d55f1a67 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -194,8 +194,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, @@ -334,6 +335,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. @@ -352,6 +361,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/types.rs b/compiler/rustc_lint/src/types.rs index 1fe155a65ae80..c4cc327f35d2b 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -11,7 +11,10 @@ use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations -pub(crate) use improper_ctypes::{ImproperCTypesDeclarations, ImproperCTypesDefinitions}; +pub(crate) use improper_ctypes::{ + IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, IMPROPER_CTYPE_DEFINITIONS, IMPROPER_CTYPES, + ImproperCTypesLint, +}; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, @@ -701,6 +704,26 @@ 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>, diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 322eb51ad1861..9cb2c9386c8ac 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -1,16 +1,16 @@ +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 as hir; -use rustc_hir::AmbigArg; use rustc_hir::def::CtorKind; use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::bug; use rustc_middle::ty::{ - self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + self, Adt, AdtDef, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; use rustc_session::{declare_lint, declare_lint_pass}; @@ -25,10 +25,21 @@ use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; type Sig<'tcx> = Binder, FnSig>>; -// a shorthand for an often used lifetime-region normalisation step -#[inline] -fn normalize_if_possible<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty) +// 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) @@ -39,7 +50,7 @@ fn get_type_from_field<'tcx>( args: GenericArgsRef<'tcx>, ) -> Ty<'tcx> { let field_ty = field.ty(cx.tcx, args); - normalize_if_possible(cx, field_ty) + erase_and_maybe_normalize(cx, field_ty) } /// Check a variant of a non-exhaustive enum for improper ctypes @@ -80,36 +91,68 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { !matches!(variant.ctor_kind(), Some(CtorKind::Const)) } -#[derive(Clone, Copy)] +/// 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 { - Declaration, - Definition, -} - -struct ImproperCTypesVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - cache: FxHashSet>, + /// 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>, - reason: DiagMessage, + 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>), - FfiUnsafe(Vec>), + /// 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![FfiUnsafeReason { ty, help, reason: note, inner: None }]) + Self::FfiUnsafe(vec![FfiUnsafeExplanation { + override_cause_ty: None, + reason: Box::new(FfiUnsafeReason { ty, help, note, inner: None }), + }]) } /// If the FfiUnsafe variant, 'wraps' all reasons, @@ -120,13 +163,16 @@ impl<'tcx> FfiResult<'tcx> { Self::FfiUnsafe(this) => { let unsafeties = this .into_iter() - .map(|reason| FfiUnsafeReason { - ty, - help: help.clone(), - reason: note.clone(), - inner: Some(Box::new(reason)), + .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(); + .collect::>(); Self::FfiUnsafe(unsafeties) } r @ _ => r, @@ -136,15 +182,27 @@ impl<'tcx> FfiResult<'tcx> { /// Otherwise, keep unchanged. fn forbid_phantom(self) -> Self { match self { - Self::FfiSafe | Self::FfiUnsafe(..) => self, - Self::FfiPhantom(ty) => Self::FfiUnsafe(vec![FfiUnsafeReason { - ty, - reason: fluent::lint_improper_ctypes_only_phantomdata, - help: None, - inner: None, - }]), + Self::FfiPhantom(ty) => { + Self::new_with_reason(ty, fluent::lint_improper_ctypes_only_phantomdata, None) + } + _ => self, } } + + /// 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> { @@ -153,6 +211,9 @@ impl<'tcx> std::ops::AddAssign> for FfiResult<'tcx> { // 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 } @@ -210,7 +271,6 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type // note that sizedness is unrelated to inhabitedness if ty.is_sized(tcx, cx.typing_env()) { - //let is_inh = ty.is_privately_uninhabited(tcx, cx.typing_env()); TypeSizedness::Definite } else { // the overall type is !Sized or ?Sized @@ -266,7 +326,7 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type Some(item_ty) => *item_ty, None => bug!("Empty tuple (AKA unit type) should be Sized, right?"), }; - let item_ty = normalize_if_possible(cx, item_ty); + let item_ty = erase_and_maybe_normalize(cx, item_ty); match get_type_sizedness(cx, item_ty) { s @ (TypeSizedness::UnsizedWithMetadata | TypeSizedness::UnsizedWithExternType @@ -320,32 +380,57 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type #[allow(non_snake_case)] mod CTypesVisitorStateFlags { - pub(super) const NO_FLAGS: u8 = 0b0000; - /// for static variables (not used in functions) - pub(super) const STATIC: u8 = 0b0010; + 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 = 0b0100; + 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_DECLARED: u8 = 0b1000; + 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)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] enum CTypesVisitorState { + None = CTypesVisitorStateFlags::NO_FLAGS, // uses bitflags from CTypesVisitorStateFlags StaticTy = CTypesVisitorStateFlags::STATIC, - ArgumentTyInDefinition = CTypesVisitorStateFlags::FN_DECLARED, - ReturnTyInDefinition = - CTypesVisitorStateFlags::FN_DECLARED | CTypesVisitorStateFlags::FN_RETURN, - ArgumentTyInDeclaration = CTypesVisitorStateFlags::NO_FLAGS, - ReturnTyInDeclaration = CTypesVisitorStateFlags::FN_RETURN, + 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 (directly or not) in a static variable + /// whether the type is used in a static variable fn is_in_static(self) -> bool { use CTypesVisitorStateFlags::*; - ((self as u8) & STATIC) != 0 + 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 { @@ -353,7 +438,7 @@ impl CTypesVisitorState { let ret = ((self as u8) & FN_RETURN) != 0; #[cfg(debug_assertions)] if ret { - assert!(!self.is_in_static()); + assert!(self.is_in_function()); } ret } @@ -362,45 +447,95 @@ impl CTypesVisitorState { /// 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_DECLARED) != 0; + let ret = ((self as u8) & FN_DEFINED) != 0; #[cfg(debug_assertions)] if ret { - assert!(!self.is_in_static()); + 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 { - // function declarations are assumed to be rust-caller, non-rust-callee - // function definitions are assumed to be maybe-not-rust-caller, rust-callee - // FnPtrs are... well, nothing's certain about anything. (FIXME need more flags in enum?) - // Same with statics. - if self.is_in_static() { + 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, mode: CItemKind, ty: Ty<'tcx>, sig: Sig<'tcx>) -> FfiResult<'tcx> { + 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 state = match mode { - CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration, - CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition, - }; let mut all_ffires = FfiSafe; for arg in sig.inputs() { - let ffi_res = self.visit_type(state, None, *arg); + 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, @@ -409,18 +544,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } let ret_ty = sig.output(); - let state = match mode { - CItemKind::Declaration => CTypesVisitorState::ReturnTyInDeclaration, - CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition, - }; - let ffi_res = self.visit_type(state, None, ret_ty); + 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 } @@ -491,7 +621,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // 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().ty = ty; + reasons.first_mut().unwrap().reason.ty = ty; } _ => unreachable!(), } @@ -514,50 +644,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // this block deals with the first two. let mut ffi_res = match get_type_sizedness(self.cx, inner_ty) { TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => { - // there's a nuance on what this lint should do for - // function definitions (`extern "C" fn fn_name(...) {...}`) - // versus declarations (`unsafe extern "C" {fn fn_name(...);}`). - // This is touched upon in https://github.com/rust-lang/rust/issues/66220 - // and https://github.com/rust-lang/rust/pull/72700 - // - // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer - // (which has a stable layout) but points to FFI-unsafe type, is it safe? - // On one hand, the function's ABI will match that of a similar C-declared function API, - // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful. - // In this code, the opinion on is split between function declarations and function definitions, - // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type. - // For declarations, we see this as unsafe, but for definitions, we see this as safe. - // - // For extern function declarations, the actual definition of the function is written somewhere else, - // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side) - // (or other possibly better tricks, see https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs) - // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side, - // and having the full type information is necessary to compile the function. - if state.is_in_defined_function() { - FfiResult::FfiSafe - } else { - return self.visit_type(state, Some(ty), inner_ty).forbid_phantom().wrap_all( - ty, - fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type, - None, - ); - } + // 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 - // (FIXME note: function 'declarations' there should be treated as definitions) // - 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) - // - // FIXME: on a side note, we should separate 'true' declarations (non-rust code), - // 'fake' declarations (in traits, needed to be implemented elsewhere), and definitions. - // (for instance, definitions should worry about &self with Self:?Sized, but fake declarations shouldn't) // 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. @@ -617,47 +721,116 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { use FfiResult::*; - let transparent_with_all_zst_fields = if def.repr().transparent() { + let (transparent_with_all_zst_fields, field_list) = if def.repr().transparent() { + // determine if there is 0 or 1 non-1ZST field, and which it is. + // (note: enums are not allowed to br transparent) + 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.. - let field_ty = get_type_from_field(self.cx, field, args); - let ffi_res = self.visit_type(state, Some(ty), field_ty); - debug_assert!(!matches!( - // 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 here - ffi_res, - FfiUnsafe(ref reasons) - if matches!( - (reasons.len(),reasons.first()), - (1,Some(FfiUnsafeReason{ty,..})) if ty.is_unit() - ) - )); - return ffi_res; + // 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 + (true, variant.fields.iter().collect::>()) } } else { - false + (false, variant.fields.iter().collect::>()) }; - let mut all_ffires = FfiSafe; + let mut field_ffires = FfiSafe; // 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 { + 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); - all_phantom &= match self.visit_type(state, Some(ty), field_ty) { + 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, - r @ (FfiUnsafe { .. } | FfiSafe) => { - all_ffires += r; + FfiSafe => false, + r @ FfiUnsafe { .. } => { + fields_ok_list[field_i] = false; + field_ffires += r; false } } } - if matches!(all_ffires, FfiUnsafe(..)) { - all_ffires.wrap_all(ty, fluent::lint_improper_ctypes_struct_dueto, None) + // 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) = field_ffires { + // 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) + { + 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 { @@ -667,16 +840,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn visit_struct_union( + 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().transparent() { + 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() { @@ -687,6 +862,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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) }, ); @@ -704,8 +880,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ); } - if def.non_enum_variant().fields.is_empty() { - return FfiResult::new_with_reason( + 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 @@ -717,15 +893,31 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } else { Some(fluent::lint_improper_ctypes_union_fieldless_help) }, - ); - } + ) + } else { + self.visit_variant_fields(state, ty, def, def.non_enum_variant(), args) + }; - 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>, @@ -740,7 +932,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } // Check for a repr() attribute to specify the size of the // discriminant. - if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none() { + 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) { return self.visit_type(state, Some(ty), inner_ty); @@ -767,6 +962,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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 { @@ -776,7 +973,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { None, ) } else { - def.variants() + let ffires = def + .variants() .iter() .map(|variant| { self.visit_variant_fields(state, ty, def, variant, args) @@ -785,7 +983,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { .forbid_phantom() }) .reduce(|r1, r2| r1 + r2) - .unwrap() // always at least one variant if we hit this branch + .unwrap(); // always at least one variant if we hit this branch + + // 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) } } @@ -805,7 +1010,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // `struct S(*mut S);`. // FIXME: A recursion limit is necessary as well, for irregular // recursive types. - if !self.cache.insert(ty) { + if !self.ty_cache.insert(ty) { return FfiSafe; } @@ -834,9 +1039,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { { return self.visit_cstr(outer_ty, ty); } - self.visit_struct_union(state, ty, def, args) + self.visit_struct_or_union(state, outer_ty, ty, def, args) } - AdtKind::Enum => self.visit_enum(state, ty, def, args), + AdtKind::Enum => self.visit_enum(state, outer_ty, ty, def, args), } } @@ -893,18 +1098,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Tuple(tuple) => { let empty_and_safe = if tuple.is_empty() { - if let Some(outer_ty) = outer_ty { - match outer_ty.kind() { - // `()` fields are FFI-safe! - ty::Adt(..) => true, - ty::RawPtr(..) => true, - // most of those are not even reachable, - // but let's not worry about checking that here - _ => false, - } - } else { + match outer_ty.map(|ty| ty.kind()) { // C functions can return void - state.is_in_function_return() + 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 @@ -944,7 +1146,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } ty::Array(inner_ty, _) => { - if outer_ty.is_none() && !state.is_in_static() { + 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( @@ -953,49 +1157,92 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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); - if sig.abi().is_rustic_abi() { + 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 { - let mode = if state.is_in_defined_function() { - CItemKind::Definition - } else { - CItemKind::Declaration - }; - self.visit_fnptr(mode, ty, sig) + FfiSafe + }; + + if state.value_may_be_unchecked() + && outer_ty + .map(|outer_ty| super::is_outer_optionlike_around_ty(self.cx, outer_ty, ty)) + == Some(true) + { + 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 } } 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. + // 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` functions can have type parameters, which may or may not be FFI-safe, + // `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. - ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..) - if state.is_in_defined_function() => - { - FfiSafe + // 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::Param(..) - | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..) - | ty::Infer(..) + ty::Infer(..) | ty::Bound(..) | ty::Error(_) | ty::Closure(..) @@ -1007,7 +1254,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn check_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + fn visit_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { struct ProhibitOpaqueTypes; impl<'tcx> ty::TypeVisitor> for ProhibitOpaqueTypes { type Result = ControlFlow>; @@ -1033,22 +1280,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } fn check_for_type(&mut self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { - let ty = normalize_if_possible(self.cx, ty); - - match self.check_for_opaque_ty(ty) { - FfiResult::FfiSafe => (), - ffi_res @ _ => return ffi_res, - } + let ty = erase_and_maybe_normalize(self.cx, ty); self.visit_type(state, None, ty) } - fn check_for_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>) -> FfiResult<'tcx> { - let ty = normalize_if_possible(self.cx, ty); - - match self.check_for_opaque_ty(ty) { - FfiResult::FfiSafe => (), - ffi_res @ _ => return ffi_res, - } + 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) => { @@ -1058,26 +1295,83 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { "expected to inspect the type of an `extern \"ABI\"` FnPtr, not an internal-ABI one" ) } else { - self.visit_fnptr(mode, ty, sig) + self.visit_fnptr(CTypesVisitorState::None, None, ty, sig) } } - _ => bug!( - "expected to inspect the type of an `extern \"ABI\"` FnPtr, not whtaever this is" - ), + r @ _ => { + bug!("expected to inspect the type of an `extern \"ABI\"` FnPtr, not {:?}", r,) + } } } -} -/// common structure for functionality that is shared -/// between ImproperCTypesDeclarations and ImproperCTypesDefinitions -struct ImproperCTypesLint<'c, 'tcx> { - cx: &'c LateContext<'tcx>, -} + fn check_for_adtdef(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + use FfiResult::*; + let ty = erase_and_maybe_normalize(self.cx, ty); -impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { - fn check_arg_for_power_alignment(&mut self, ty: Ty<'tcx>) -> bool { - let tcx = self.cx.tcx; + 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 @@ -1098,7 +1392,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { // 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(field_ty) { + if self.check_arg_for_power_alignment(cx, field_ty) { return true; } } @@ -1106,41 +1400,39 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { return false; } - fn check_struct_for_power_alignment(&mut self, item: &'tcx hir::Item<'tcx>) { - let tcx = self.cx.tcx; - let adt_def = tcx.adt_def(item.owner_id.to_def_id()); + 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. - if adt_def.repr().c() - && !adt_def.repr().packed() - && adt_def.repr().align.is_none() - && tcx.sess.target.os == "aix" - && !adt_def.all_fields().next().is_none() - { + 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 = tcx.type_of(def_id).instantiate_identity(); - if self.check_arg_for_power_alignment(ty) { - self.cx.emit_span_lint( - USES_POWER_ALIGNMENT, - field_def.span, - UsesPowerAlignment, - ); + 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); } } } } +} - /// Find any fn-ptr types with external ABIs in `ty`. - /// - /// For example, `Option` returns `extern "C" fn()` - fn check_type_for_external_abi_fnptr( - &self, - fn_mode: CItemKind, +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>, ) { @@ -1181,84 +1473,111 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { visitor.visit_ty_unambig(hir_ty); let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)); - all_types - .map(|(fn_ptr_ty, span)| { - // FIXME this will probably lead to error deduplication: fix this - let mut visitor = - ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() }; - let ffi_res = visitor.check_for_fnptr(fn_mode, fn_ptr_ty); - (span, ffi_res) - }) - // even in function *definitions*, `FnPtr`s are always function declarations ...right? - // (FIXME: we can't do that yet because one of rustc's crates can't compile if we do) - .for_each(|(span, ffi_res)| self.process_ffi_result(span, ffi_res, fn_mode)); - //.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( - &self, - fn_mode: CItemKind, + fn check_fn_for_external_abi_fnptr<'tcx>( + &mut self, + cx: &LateContext<'tcx>, 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); + 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(fn_mode, input_hir, *input_ty); + 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(fn_mode, ret_hir, sig.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(&self, id: hir::OwnerId, span: Span) { - let ty = self.cx.tcx.type_of(id).instantiate_identity(); - let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() }; + 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(span, ffi_res, CItemKind::Declaration); + 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( - &self, + fn check_foreign_fn<'tcx>( + &mut self, + cx: &LateContext<'tcx>, fn_mode: CItemKind, 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); + 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 { cx: self.cx, cache: FxHashSet::default() }; + let mut visitor = ImproperCTypesVisitor::new(cx); let visit_state = match fn_mode { - CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition, - CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration, + 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(input_hir.span, ffi_res, fn_mode); + 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 { cx: self.cx, cache: FxHashSet::default() }; + let mut visitor = ImproperCTypesVisitor::new(cx); let visit_state = match fn_mode { - CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition, - CItemKind::Declaration => CTypesVisitorState::ReturnTyInDeclaration, + 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(ret_hir.span, ffi_res, fn_mode); + self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode); } } - fn process_ffi_result(&self, sp: Span, res: FfiResult<'tcx>, fn_mode: CItemKind) { + 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 { @@ -1271,14 +1590,14 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { fn_mode, ); } - FfiResult::FfiUnsafe(reasons) => { - for reason in reasons { - let mut ffiresult_recursor = ControlFlow::Continue(&reason); + 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, reason, help, inner }) = + while let ControlFlow::Continue(FfiUnsafeReason { ty, note, help, inner }) = ffiresult_recursor { if let Some(layer) = cimproper_layers.last_mut() { @@ -1288,7 +1607,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { ty: ty.clone(), inner_ty: None, help: help.clone(), - note: reason.clone(), + note: note.clone(), span_note: None, // filled later }); @@ -1298,32 +1617,41 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { ffiresult_recursor = ControlFlow::Break(()); } } - // should always have at least one type - let last_ty = cimproper_layers.last().unwrap().ty.clone(); - self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers, fn_mode); + 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( + 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::Declaration => IMPROPER_CTYPES, - CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, + 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::Declaration => "block", - CItemKind::Definition => "fn", + 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) = self.cx.tcx.hir_span_if_local(def.did()) + && let Some(sp) = cx.tcx.hir_span_if_local(def.did()) { Some(sp) } else { @@ -1331,63 +1659,77 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { }; } - self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons }); + cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons }); } } -impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { +/// 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()); - let lint = ImproperCTypesLint { cx }; match it.kind { hir::ForeignItemKind::Fn(sig, _, _) => { - if abi.is_rustic_abi() { - lint.check_fn_for_external_abi_fnptr( - CItemKind::Declaration, + // 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, - ) - } else { - lint.check_foreign_fn(CItemKind::Declaration, it.owner_id.def_id, sig.decl); + ); } } hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => { - lint.check_foreign_static(it.owner_id, ty.span); + self.check_foreign_static(cx, it.owner_id, ty.span); } hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), } } -} -/// `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, ..) => { - ImproperCTypesLint { cx }.check_type_for_external_abi_fnptr( - CItemKind::Definition, + self.check_type_for_external_abi_fnptr( + cx, ty, cx.tcx.type_of(item.owner_id).instantiate_identity(), ); } - // See `check_fn`.. + // See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks hir::ItemKind::Fn { .. } => {} - // Structs are checked based on if they follow the power alignment - // rule (under repr(C)). - hir::ItemKind::Struct(..) => { - ImproperCTypesLint { cx }.check_struct_for_power_alignment(item); + 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); + } } - // 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(..) @@ -1402,8 +1744,8 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { } fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) { - ImproperCTypesLint { cx }.check_type_for_external_abi_fnptr( - CItemKind::Definition, + self.check_type_for_external_abi_fnptr( + cx, field.ty, cx.tcx.type_of(field.def_id).instantiate_identity(), ); @@ -1426,11 +1768,60 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { _ => return, }; - let lint = ImproperCTypesLint { cx }; - if abi.is_rustic_abi() { - lint.check_fn_for_external_abi_fnptr(CItemKind::Definition, id, decl); - } else { - lint.check_foreign_fn(CItemKind::Definition, id, decl); + // 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); + } } } } @@ -1438,12 +1829,14 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { 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); /// } /// ``` /// @@ -1457,16 +1850,15 @@ declare_lint! { /// 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, + pub(crate) 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 + /// 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 /// @@ -1486,11 +1878,66 @@ declare_lint! { /// 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, + 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. @@ -1546,4 +1993,10 @@ declare_lint! { "Structs do not follow the power alignment rule under repr(C)" } -declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]); +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 d1706b6525295..676af03ce660b 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -23,7 +23,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/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/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 f9ff392126616..a89dd508e9385 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 33d856732b2c5..428e018103599 100644 --- a/tests/ui/abi/nullable-pointer-ffi-compat.rs +++ b/tests/ui/abi/nullable-pointer-ffi-compat.rs @@ -14,7 +14,7 @@ use std::mem; -#[allow(improper_ctypes_definitions)] // it's worried about invalid pointers given as values of x +#[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 7432381d15b72..54ebc89b7bca8 100644 --- a/tests/ui/abi/simd-abi-checks-avx.rs +++ b/tests/ui/abi/simd-abi-checks-avx.rs @@ -4,7 +4,7 @@ #![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 931c378704a29..72827cd2d60b1 100644 --- a/tests/ui/asm/issue-97490.rs +++ b/tests/ui/asm/issue-97490.rs @@ -2,8 +2,8 @@ //@ only-x86_64 //@ needs-asm-support -#[allow(improper_ctypes_definitions)] // it's worried about invalid pointers being given as the - // argument value +#[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 ad030b22b55b8..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,7 +38,7 @@ pub fn params( } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[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 abd52584b1e57..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,4 +1,4 @@ -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); @@ -6,23 +6,9 @@ LL | type Foo = extern "C" fn(::std::ffi::CStr); | = 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 `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer - (note that `CString::into_raw()`'s output must not be `libc::free()`'d) + 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 - | - = 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 `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: `#[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 f2ee21c316658..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,4 +1,4 @@ -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); @@ -7,17 +7,7 @@ LL | type ExternCallback = extern "C" fn(*const u8, u32, str); = 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 - | - = 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_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 @@ -25,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..d559e5b0e4a04 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 + = note: integers constrained to a given range 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 + = note: integers constrained to a given range 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 + = note: integers constrained to a given range 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 4e3832ab1b672..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 c0923dd96c8c3..fa488d14d0304 100644 --- a/tests/ui/lint/extern-C-fnptr-lints-slices.stderr +++ b/tests/ui/lint/extern-C-fnptr-lints-slices.stderr @@ -1,4 +1,4 @@ -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]); @@ -10,8 +10,8 @@ LL | pub type F = extern "C" fn(&[u8]); 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/improper_ctypes/ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs index e06d0721d5a28..20b95375689a9 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -4,7 +4,8 @@ #![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; @@ -24,14 +25,14 @@ 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 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 TransparentBoxFn(RustBoxRet); @@ -52,12 +53,15 @@ pub struct UnsizedStructBecauseForeign { } #[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], } @@ -66,10 +70,10 @@ pub struct TwoBadTypes<'a> { 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 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); @@ -77,7 +81,7 @@ extern "C" { 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 pat_type1() -> pattern_type!(u32 is 1..); //~ ERROR uses type `(u32) is 1..=` + 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)` @@ -90,17 +94,16 @@ extern "C" { 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: RustBoxRet); - pub fn transparent_i128(p: TransparentI128); //~ ERROR: uses type `i128` - pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `&str` + 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: Box>) + f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) ); //~^^ ERROR: uses type `char` - //~^^^ ERROR: uses type `&dyn Debug` - // (possible FIXME: the in-struct `char` field doesn't get a warning due ^^) - //~^^^^^ ERROR: uses type `&[u8]` + //~| 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` diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index a365bac891122..9f2132219506f 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -1,59 +1,78 @@ -error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/ctypes.rs:69:28 +error: `repr(C)` type uses type `ZeroSize`, which is not FFI-safe + --> $DIR/ctypes.rs:28:1 | -LL | pub fn ptr_type1(size: *const Foo); - | ^^^^^^^^^^ not FFI-safe +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = note: this reference (`*const Foo`) is ABI-compatible with a C pointer, but `Foo` itself does not have a C layout - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout + = help: consider adding a member to this struct + = note: `ZeroSize` has no fields note: the type is defined here - --> $DIR/ctypes.rs:31:1 + --> $DIR/ctypes.rs:28:1 | -LL | pub struct Foo; - | ^^^^^^^^^^^^^^ +LL | pub struct ZeroSize; + | ^^^^^^^^^^^^^^^^^^^ note: the lint level is defined here - --> $DIR/ctypes.rs:7:9 + --> $DIR/ctypes.rs:8:36 | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ +LL | #![deny(improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/ctypes.rs:70:28 +error: `repr(C)` type uses type `dyn Debug`, which is not FFI-safe + --> $DIR/ctypes.rs:55:1 | -LL | pub fn ptr_type2(size: *const Foo); - | ^^^^^^^^^^ not FFI-safe +LL | / pub struct UnsizedStructBecauseDyn { +LL | | +LL | | sized: u32, +LL | | unszd: dyn Debug, +LL | | } + | |_^ not FFI-safe | - = note: this reference (`*const Foo`) is ABI-compatible with a C pointer, but `Foo` itself does not have a C layout - = 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/ctypes.rs:31:1 + = 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 Foo; - | ^^^^^^^^^^^^^^ +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: `extern` block uses type `((),)`, which is not FFI-safe - --> $DIR/ctypes.rs:72:25 +error: `repr(C)` type uses type `&[u8]`, which is not FFI-safe + --> $DIR/ctypes.rs:62:1 | -LL | pub fn ptr_tuple(p: *const ((),)); - | ^^^^^^^^^^^^ not FFI-safe +LL | / pub struct TwoBadTypes<'a> { +LL | | +LL | | +LL | | non_c_type: char, +LL | | ref_with_mdata: &'a [u8], +LL | | } + | |_^ not FFI-safe | - = note: this reference (`*const ((),)`) is ABI-compatible with a C pointer, but `((),)` itself does not have a C layout - = help: consider using a struct instead - = note: tuples have unspecified layout + = 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:73:26 + --> $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:74:24 + --> $DIR/ctypes.rs:78:24 | LL | pub fn str_type(p: &str); | ^^^^ not FFI-safe @@ -62,7 +81,7 @@ LL | pub fn str_type(p: &str); = 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:77:25 + --> $DIR/ctypes.rs:81:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -71,7 +90,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/ctypes.rs:78:25 + --> $DIR/ctypes.rs:82:25 | LL | pub fn i128_type(p: i128); | ^^^^ not FFI-safe @@ -79,15 +98,15 @@ LL | pub fn i128_type(p: i128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:79:25 + --> $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:80:27 +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 @@ -96,7 +115,7 @@ LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); = note: integers constrained to a given range 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:82:26 + --> $DIR/ctypes.rs:86:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -104,7 +123,7 @@ LL | pub fn trait_type(p: &dyn Bar); = 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:83:26 + --> $DIR/ctypes.rs:87:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -113,7 +132,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/ctypes.rs:84:27 + --> $DIR/ctypes.rs:88:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -122,34 +141,34 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/ctypes.rs:85:25 + --> $DIR/ctypes.rs:89: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: `ZeroSize` has no fields note: the type is defined here - --> $DIR/ctypes.rs:27:1 + --> $DIR/ctypes.rs:28:1 | LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/ctypes.rs:86:33 + --> $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:66:1 + --> $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:89:12 + --> $DIR/ctypes.rs:93:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -157,7 +176,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:90:23 + --> $DIR/ctypes.rs:94:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -166,7 +185,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:91:24 + --> $DIR/ctypes.rs:95:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -174,25 +193,37 @@ LL | pub fn fn_type2(p: fn()); = help: consider using an `extern fn(...) -> ...` function pointer instead = note: this function pointer has Rust-specific calling convention -error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/ctypes.rs:93:32 +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 `&str`, which is not FFI-safe - --> $DIR/ctypes.rs:94:31 +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:96:27 + --> $DIR/ctypes.rs:100:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -200,36 +231,40 @@ LL | pub fn raw_array(arr: [u8; 8]); = help: consider passing a pointer to the array = note: passing raw arrays by value is not FFI-safe -error: `extern` block uses type `char`, which is not FFI-safe - --> $DIR/ctypes.rs:98:12 +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: Box>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +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), Box>)` is FFI-unsafe due to `char` + = 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` block uses type `&dyn Debug`, which is not FFI-safe - --> $DIR/ctypes.rs:98:12 +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: Box>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +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), Box>)` is FFI-unsafe due to `&dyn Debug` + = 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` block uses type `&[u8]`, which is not FFI-safe - --> $DIR/ctypes.rs:98:12 +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: Box>) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe +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), Box>)` is FFI-unsafe due to `Box>` - = note: this reference (`Box>`) is ABI-compatible with a C pointer, but `TwoBadTypes<'_>` itself does not have a C layout + = 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:60:1 + --> $DIR/ctypes.rs:62:1 | LL | pub struct TwoBadTypes<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -237,7 +272,7 @@ LL | pub struct TwoBadTypes<'a> { = 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:106:47 + --> $DIR/ctypes.rs:109:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -245,7 +280,7 @@ LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); = 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:108:26 + --> $DIR/ctypes.rs:111:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -254,7 +289,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/ctypes.rs:110:26 + --> $DIR/ctypes.rs:113:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -263,7 +298,7 @@ LL | pub fn no_niche_b(b: Option>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:113:34 + --> $DIR/ctypes.rs:116:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -271,12 +306,12 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:114:40 + --> $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 28 previous errors +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 index 1ca59c6868d6d..11dae42fa538b 100644 --- a/tests/ui/lint/improper_ctypes/lint-113436-1.rs +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.rs @@ -1,4 +1,4 @@ -#![deny(improper_ctypes_definitions)] +#![deny(improper_c_fn_definitions, improper_ctype_definitions)] #[repr(C)] pub struct Foo { @@ -13,15 +13,15 @@ extern "C" fn foo(x: Foo) -> Foo { struct NotSafe(u32); #[repr(C)] -pub struct Bar { +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 `NotSafe`, which is not FFI-safe - //~^^ ERROR `extern` fn uses type `NotSafe`, which is not FFI-safe + //~^ ERROR `extern` fn uses type `Bar`, which is not FFI-safe + //~^^ ERROR `extern` fn uses type `Bar`, which is not FFI-safe todo!() } diff --git a/tests/ui/lint/improper_ctypes/lint-113436-1.stderr b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr index 45cdacc34318e..255063e42204d 100644 --- a/tests/ui/lint/improper_ctypes/lint-113436-1.stderr +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr @@ -1,4 +1,27 @@ -error: `extern` fn uses type `NotSafe`, which is not FFI-safe +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 { @@ -10,8 +33,8 @@ note: the type is defined here | LL | pub struct Bar { | ^^^^^^^^^^^^^^ - = 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 `NotSafe` + = note: `NotSafe` has unspecified layout note: the type is defined here --> $DIR/lint-113436-1.rs:13:1 | @@ -20,10 +43,10 @@ LL | struct NotSafe(u32); note: the lint level is defined here --> $DIR/lint-113436-1.rs:1:9 | -LL | #![deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(improper_c_fn_definitions, improper_ctype_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `extern` fn uses type `NotSafe`, which is not FFI-safe +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 { @@ -35,13 +58,13 @@ note: the type is defined here | LL | pub struct Bar { | ^^^^^^^^^^^^^^ - = 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 `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 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-73249-2.rs b/tests/ui/lint/improper_ctypes/lint-73249-2.rs index 31af0e3d381ef..5f1f9c7386037 100644 --- a/tests/ui/lint/improper_ctypes/lint-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 index 418cf43764e12..9f52eefc686e5 100644 --- a/tests/ui/lint/improper_ctypes/lint-73249-2.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-2.stderr @@ -1,15 +1,22 @@ -error: `extern` block uses type `Qux`, which is not FFI-safe - --> $DIR/lint-73249-2.rs:27:21 +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 reference (`&Qux`) is ABI-compatible with a C pointer, but `Qux` itself does not have a C layout - = note: opaque types have no C equivalent + = 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)] +LL | #![deny(improper_ctypes, improper_ctype_definitions)] | ^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/improper_ctypes/lint-73249-3.rs b/tests/ui/lint/improper_ctypes/lint-73249-3.rs index 8bdf536bf77e6..e3e7659ba16c9 100644 --- a/tests/ui/lint/improper_ctypes/lint-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 index 431c80eb398ce..bd449c1fa6e77 100644 --- a/tests/ui/lint/improper_ctypes/lint-73249-3.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr @@ -1,4 +1,19 @@ -error: `extern` block uses type `Qux`, which is not FFI-safe +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; @@ -6,7 +21,7 @@ LL | pub fn lint_me() -> A; | = 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:15:1 + --> $DIR/lint-73249-3.rs:16:1 | LL | pub struct A { | ^^^^^^^^^^^^ @@ -14,8 +29,8 @@ LL | pub struct A { note: the lint level is defined here --> $DIR/lint-73249-3.rs:2:9 | -LL | #![deny(improper_ctypes)] +LL | #![deny(improper_ctypes, improper_ctype_definitions)] | ^^^^^^^^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-73249-5.rs b/tests/ui/lint/improper_ctypes/lint-73249-5.rs index cc6da59950d7a..8272256b5a83a 100644 --- a/tests/ui/lint/improper_ctypes/lint-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 index 484927f57fead..9172039afa104 100644 --- a/tests/ui/lint/improper_ctypes/lint-73249-5.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-5.stderr @@ -1,14 +1,20 @@ -error: `extern` block uses type `Qux`, which is not FFI-safe +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)] +LL | #![deny(improper_ctypes, improper_ctype_definitions)] | ^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/improper_ctypes/lint-94223.rs b/tests/ui/lint/improper_ctypes/lint-94223.rs index ac24f61b0ac7a..d6a03f19d543d 100644 --- a/tests/ui/lint/improper_ctypes/lint-94223.rs +++ b/tests/ui/lint/improper_ctypes/lint-94223.rs @@ -1,49 +1,50 @@ #![crate_type = "lib"] -#![deny(improper_ctypes_definitions)] +#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_c_callbacks)] pub fn bad(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 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 +//~^ 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` fn uses type `[u8]`, which is not FFI-safe +//~^ ERROR `extern` callback 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 + //~^ ERROR `extern` callback 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 + //~^ ERROR `extern` callback 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 +//~^ 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` fn uses type `Option<&::FooType>`, which is not FFI-safe +//~^ ERROR `extern` callback uses type `Option<&::FooType>`, which is not FFI-safe pub struct FfiUnsafe; -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_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 +//~^ ERROR `extern` callback 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 +//~^ 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` fn uses type `FfiUnsafe`, which is not FFI-safe +//~^ 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 index ce657e80dd25a..cbde25517ec82 100644 --- a/tests/ui/lint/improper_ctypes/lint-94223.stderr +++ b/tests/ui/lint/improper_ctypes/lint-94223.stderr @@ -1,5 +1,5 @@ -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-94223.rs:4:15 +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 @@ -8,13 +8,13 @@ LL | pub fn bad(f: extern "C" fn([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:2:9 + --> $DIR/lint-94223.rs:3:9 | -LL | #![deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(improper_c_callbacks)] + | ^^^^^^^^^^^^^^^^^^^^ -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-94223.rs:7:28 +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 @@ -23,8 +23,8 @@ LL | pub fn bad_twice(f: Result) {} = help: consider using a raw pointer to the slice's first element (and a length) instead = note: slices have no C equivalent -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-94223.rs:7:49 +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 @@ -33,8 +33,8 @@ LL | pub fn bad_twice(f: Result) {} = help: consider using a raw pointer to the slice's first element (and a length) instead = note: slices have no C equivalent -error: `extern` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-94223.rs:11:18 +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 @@ -43,8 +43,8 @@ LL | struct BadStruct(extern "C" fn([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` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-94223.rs:15:7 +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 @@ -53,8 +53,8 @@ LL | A(extern "C" fn([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` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-94223.rs:20:7 +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 @@ -63,8 +63,8 @@ LL | A(extern "C" fn([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` fn uses type `[u8]`, which is not FFI-safe - --> $DIR/lint-94223.rs:24:12 +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 @@ -73,8 +73,8 @@ LL | type Foo = extern "C" fn([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` fn uses type `Option<&::FooType>`, which is not FFI-safe - --> $DIR/lint-94223.rs:31:20 +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 @@ -83,62 +83,62 @@ LL | pub type Foo2 = extern "C" fn(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` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-94223.rs:41:17 +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)]` 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 `FfiUnsafe` + = note: `FfiUnsafe` has unspecified layout note: the type is defined here - --> $DIR/lint-94223.rs:34:1 + --> $DIR/lint-94223.rs:35:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ -error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-94223.rs:44:30 +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)]` 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 `FfiUnsafe` + = note: `FfiUnsafe` has unspecified layout note: the type is defined here - --> $DIR/lint-94223.rs:34:1 + --> $DIR/lint-94223.rs:35:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ -error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-94223.rs:44:56 +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)]` 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 `FfiUnsafe` + = note: `FfiUnsafe` has unspecified layout note: the type is defined here - --> $DIR/lint-94223.rs:34:1 + --> $DIR/lint-94223.rs:35:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ -error: `extern` fn uses type `FfiUnsafe`, which is not FFI-safe - --> $DIR/lint-94223.rs:48:22 +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)]` 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 `FfiUnsafe` + = note: `FfiUnsafe` has unspecified layout note: the type is defined here - --> $DIR/lint-94223.rs:34:1 + --> $DIR/lint-94223.rs:35:1 | LL | pub struct FfiUnsafe; | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/improper_ctypes/lint-cstr.rs b/tests/ui/lint/improper_ctypes/lint-cstr.rs index aa7afd12ee587..a0b818a553e44 100644 --- a/tests/ui/lint/improper_ctypes/lint-cstr.rs +++ b/tests/ui/lint/improper_ctypes/lint-cstr.rs @@ -1,5 +1,5 @@ #![crate_type = "lib"] -#![deny(improper_ctypes, improper_ctypes_definitions)] +#![deny(improper_ctypes, improper_c_fn_definitions)] use std::ffi::{CStr, CString}; diff --git a/tests/ui/lint/improper_ctypes/lint-cstr.stderr b/tests/ui/lint/improper_ctypes/lint-cstr.stderr index ca5248b8ea888..281c94875c8c5 100644 --- a/tests/ui/lint/improper_ctypes/lint-cstr.stderr +++ b/tests/ui/lint/improper_ctypes/lint-cstr.stderr @@ -11,7 +11,7 @@ LL | fn take_cstr(s: CStr); note: the lint level is defined here --> $DIR/lint-cstr.rs:2:9 | -LL | #![deny(improper_ctypes, improper_ctypes_definitions)] +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `&CStr`, which is not FFI-safe @@ -74,8 +74,8 @@ LL | extern "C" fn rust_take_cstr_ref(s: &CStr) {} note: the lint level is defined here --> $DIR/lint-cstr.rs:2:26 | -LL | #![deny(improper_ctypes, improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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 diff --git a/tests/ui/lint/improper_ctypes/lint-enum.stderr b/tests/ui/lint/improper_ctypes/lint-enum.stderr index 0a834d9943f39..6cc1793a0b0ab 100644 --- a/tests/ui/lint/improper_ctypes/lint-enum.stderr +++ b/tests/ui/lint/improper_ctypes/lint-enum.stderr @@ -1,5 +1,5 @@ error: `extern` block uses type `U`, which is not FFI-safe - --> $DIR/lint-enum.rs:84:14 + --> $DIR/lint-enum.rs:83:14 | LL | fn uf(x: U); | ^ not FFI-safe @@ -18,7 +18,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/lint-enum.rs:85:14 + --> $DIR/lint-enum.rs:84:14 | LL | fn bf(x: B); | ^ not FFI-safe @@ -32,7 +32,7 @@ LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-enum.rs:86:14 + --> $DIR/lint-enum.rs:85:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -44,8 +44,9 @@ note: the type is defined here | LL | enum T { | ^^^^^^ + error: `extern` block uses type `U128`, which is not FFI-safe - --> $DIR/lint-enum.rs:90:21 + --> $DIR/lint-enum.rs:89:21 | LL | fn repr_u128(x: U128); | ^^^^ not FFI-safe @@ -58,7 +59,7 @@ LL | enum U128 { | ^^^^^^^^^ error: `extern` block uses type `I128`, which is not FFI-safe - --> $DIR/lint-enum.rs:91:21 + --> $DIR/lint-enum.rs:90:21 | LL | fn repr_i128(x: I128); | ^^^^ not FFI-safe @@ -71,7 +72,7 @@ LL | enum I128 { | ^^^^^^^^^ error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-enum.rs:100:31 + --> $DIR/lint-enum.rs:99:31 | LL | fn option_nonzero_u128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -79,7 +80,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-enum.rs:107:31 + --> $DIR/lint-enum.rs:106:31 | LL | fn option_nonzero_i128(x: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -87,7 +88,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-enum.rs:112:36 + --> $DIR/lint-enum.rs:111:36 | LL | fn option_transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -96,7 +97,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-enum.rs:114:28 + --> $DIR/lint-enum.rs:113:28 | LL | fn option_repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -105,15 +106,16 @@ 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-enum.rs:115:21 + --> $DIR/lint-enum.rs:114:21 | LL | fn option_u8(x: 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-enum.rs:125:33 + --> $DIR/lint-enum.rs:124:33 | LL | fn result_nonzero_u128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -121,7 +123,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-enum.rs:132:33 + --> $DIR/lint-enum.rs:131:33 | LL | fn result_nonzero_i128_t(x: Result, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -129,7 +131,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-enum.rs:137:38 + --> $DIR/lint-enum.rs:136:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -138,7 +140,7 @@ LL | fn result_transparent_union_t(x: Result>, ()>`, which is not FFI-safe - --> $DIR/lint-enum.rs:139:30 + --> $DIR/lint-enum.rs:138:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -147,7 +149,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-enum.rs:143:51 + --> $DIR/lint-enum.rs:142:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -156,7 +158,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-enum.rs:145:53 + --> $DIR/lint-enum.rs:144:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -165,7 +167,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-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 @@ -174,7 +176,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-enum.rs:150:49 + --> $DIR/lint-enum.rs:149:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -183,7 +185,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-enum.rs:152:30 + --> $DIR/lint-enum.rs:151:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -192,7 +194,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-enum.rs:163:33 + --> $DIR/lint-enum.rs:162:33 | LL | fn result_nonzero_u128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -200,7 +202,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-enum.rs:170:33 + --> $DIR/lint-enum.rs:169:33 | LL | fn result_nonzero_i128_e(x: Result<(), num::NonZero>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -208,7 +210,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-enum.rs:175:38 + --> $DIR/lint-enum.rs:174:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -217,7 +219,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-enum.rs:177:30 + --> $DIR/lint-enum.rs:176:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -226,7 +228,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-enum.rs:181:51 + --> $DIR/lint-enum.rs:180:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -235,7 +237,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-enum.rs:183:53 + --> $DIR/lint-enum.rs:182:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -244,7 +246,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-enum.rs:185:51 + --> $DIR/lint-enum.rs:184:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -253,7 +255,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-enum.rs:188:49 + --> $DIR/lint-enum.rs:187:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -262,7 +264,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result>>`, which is not FFI-safe - --> $DIR/lint-enum.rs:190:30 + --> $DIR/lint-enum.rs:189:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -271,7 +273,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-enum.rs:192:27 + --> $DIR/lint-enum.rs:191:27 | LL | fn result_unit_t_e(x: Result<(), ()>); | ^^^^^^^^^^^^^^ not FFI-safe diff --git a/tests/ui/lint/improper_ctypes/lint-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs index fa23b7b9c514f..479a61a8036d4 100644 --- a/tests/ui/lint/improper_ctypes/lint-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 = (); @@ -121,14 +122,16 @@ 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) { } +// ^ FIXME it doesn't see the error... but at least it reports it elsewhere? pub extern "C" fn good3(fptr: Option) { } @@ -138,7 +141,7 @@ pub extern "C" fn argument_with_assumptions_4(aptr: &[u8; 4 as usize]) { } pub extern "C" fn good5(s: StructWithProjection) { } pub extern "C" fn argument_with_assumptions_6(s: StructWithProjectionAndLifetime) { } -//~^ ERROR: uses type `&mut StructWithProjectionAndLifetime<'_>` +//~^ ERROR: uses type `StructWithProjectionAndLifetime<'_>` // note: the type translation might be a little eager for // `::It` @@ -157,7 +160,7 @@ pub extern "C" fn good12(size: usize) { } pub extern "C" fn good13(n: TransparentInt) { } pub extern "C" fn argument_with_assumptions_14(p: TransparentRef) { } -//~^ ERROR: uses type `&TransparentInt` +//~^ ERROR: uses type `TransparentRef<'_>` pub extern "C" fn good15(p: TransparentLifetime) { } @@ -165,7 +168,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 index e024efce4990e..6b43d0b64e303 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.stderr +++ b/tests/ui/lint/improper_ctypes/lint-fn.stderr @@ -1,5 +1,39 @@ +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:70:33 + --> $DIR/lint-fn.rs:71:33 | LL | pub extern "C" fn slice_type(p: &[u32]) { } | ^^^^^^ not FFI-safe @@ -7,13 +41,13 @@ LL | pub extern "C" fn slice_type(p: &[u32]) { } = 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:9 + --> $DIR/lint-fn.rs:2:26 | -LL | #![deny(improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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:73:31 + --> $DIR/lint-fn.rs:74:31 | LL | pub extern "C" fn str_type(p: &str) { } | ^^^^ not FFI-safe @@ -22,17 +56,17 @@ LL | pub extern "C" fn str_type(p: &str) { } = 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:76:31 + --> $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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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:82:34 + --> $DIR/lint-fn.rs:83:34 | LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } | ^^^^^^^^^ not FFI-safe @@ -41,7 +75,7 @@ LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } = 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:85:35 + --> $DIR/lint-fn.rs:86:35 | LL | pub extern "C" fn boxed_string(p: Box) { } | ^^^^^^^^ not FFI-safe @@ -50,7 +84,7 @@ LL | pub extern "C" fn boxed_string(p: Box) { } = 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:88:34 + --> $DIR/lint-fn.rs:89:34 | LL | pub extern "C" fn boxed_trait(p: Box) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -58,7 +92,7 @@ LL | pub extern "C" fn boxed_trait(p: Box) { } = 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:91:32 + --> $DIR/lint-fn.rs:92:32 | LL | pub extern "C" fn char_type(p: char) { } | ^^^^ not FFI-safe @@ -67,7 +101,7 @@ LL | pub extern "C" fn char_type(p: char) { } = note: the `char` type has no C equivalent error: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/lint-fn.rs:94:32 + --> $DIR/lint-fn.rs:95:32 | LL | pub extern "C" fn i128_type(p: i128) { } | ^^^^ not FFI-safe @@ -75,7 +109,7 @@ LL | pub extern "C" fn i128_type(p: i128) { } = 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:97:32 + --> $DIR/lint-fn.rs:98:32 | LL | pub extern "C" fn u128_type(p: u128) { } | ^^^^ not FFI-safe @@ -83,7 +117,7 @@ LL | pub extern "C" fn u128_type(p: u128) { } = 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:100:33 + --> $DIR/lint-fn.rs:101:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -92,7 +126,7 @@ LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } = note: tuples have unspecified layout error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-fn.rs:103:34 + --> $DIR/lint-fn.rs:104:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -101,34 +135,34 @@ LL | pub extern "C" fn tuple_type2(p: I32Pair) { } = note: tuples have unspecified layout error: `extern` fn uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-fn.rs:106:32 + --> $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: this struct has no fields + = note: `ZeroSize` has no fields note: the type is defined here - --> $DIR/lint-fn.rs:25:1 + --> $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:109:40 + --> $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:60:1 + --> $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:112:51 + --> $DIR/lint-fn.rs:113:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -136,7 +170,7 @@ LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-fn.rs:117:30 + --> $DIR/lint-fn.rs:118:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -145,7 +179,7 @@ LL | pub extern "C" fn fn_type(p: RustFn) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-fn.rs:120:31 + --> $DIR/lint-fn.rs:121:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -153,61 +187,79 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = 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-fn.rs:125:39 +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 `&str`, which is not FFI-safe - --> $DIR/lint-fn.rs:128:38 +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 `&[u8; 4]`, which is not FFI-safe - --> $DIR/lint-fn.rs:135:53 + --> $DIR/lint-fn.rs:138: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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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 `&mut StructWithProjectionAndLifetime<'_>`, which is not FFI-safe - --> $DIR/lint-fn.rs:140:50 +error: `extern` fn uses type `StructWithProjectionAndLifetime<'_>`, which is not FFI-safe + --> $DIR/lint-fn.rs:143: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:18:1 + --> $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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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 `&TransparentInt`, which is not FFI-safe - --> $DIR/lint-fn.rs:159:51 +error: `extern` fn uses type `TransparentRef<'_>`, which is not FFI-safe + --> $DIR/lint-fn.rs:162: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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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:179:43 + --> $DIR/lint-fn.rs:182:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -215,22 +267,22 @@ LL | pub extern "C" fn unused_generic2() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-fn.rs:192:39 + --> $DIR/lint-fn.rs:195: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 + = 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:195:41 + --> $DIR/lint-fn.rs:198: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 + = 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 24 previous errors +error: aborting due to 26 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.rs b/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.rs index ca08eb23a57eb..57addd449ee3a 100644 --- a/tests/ui/lint/improper_ctypes/lint-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/improper_ctypes/lint-option-nonnull-unsized.stderr b/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.stderr index b17fb6bd6145d..57a557b5d68e5 100644 --- a/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.stderr +++ b/tests/ui/lint/improper_ctypes/lint-option-nonnull-unsized.stderr @@ -9,8 +9,8 @@ LL | extern "C" fn foo() -> Option<&'static T> { note: the lint level is defined here --> $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-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 index 6bbb848762eba..56a1a02e576e7 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs @@ -2,7 +2,8 @@ //@ edition:2018 #![allow(dead_code,unused_variables)] -#![deny(improper_ctypes,improper_ctypes_definitions)] +#![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)] @@ -31,7 +32,7 @@ struct SomeStruct{ impl SomeStruct{ extern "C" fn klol( // Ref[Struct] - &self + &self //~ ERROR: `extern` fn uses type `&SomeStruct` ){} } @@ -87,14 +88,14 @@ pub trait TimesTwo: std::ops::Add + Sized + Clone //} extern "C" fn t2_box( // Box[Param] - self: Box, + 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 + &self //~ ERROR: `extern` fn uses type `&Self` // Alias ) -> <&Self as std::ops::Add<&Self>>::Output { self + self @@ -105,6 +106,7 @@ 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. @@ -130,7 +132,7 @@ extern "C" fn all_ty_kinds<'a,const N:usize,T>( // also Tuple (p2, p3):(u8, u8), //~ ERROR: uses type `(u8, u8)` // Pat - nz: pattern_type!(u32 is 1..), //~ ERROR: uses type `(u32) is 1..=` + nz: pattern_type!(u32 is 1..), //~ ERROR: uses type `(u32) is 1..` // Struct SomeStruct{b:p4,..}: SomeStruct, // Union @@ -156,7 +158,7 @@ extern "C" fn all_ty_kinds<'a,const N:usize,T>( 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 (this gets caught outside of the code we want to test) + // Alias ) -> impl std::fmt::Debug { //~ ERROR: uses type `impl Debug` 3_usize } @@ -177,11 +179,11 @@ extern "C" fn all_ty_kinds_in_ptr( // Ptr[Tuple] p: *const (u8,u8), // Tuple - (p2, p3):(*const u8, *const u8), + (p2, p3):(*const u8, *const u8), //~ ERROR: uses type `(*const u8, *const u8)` // Pat - nz: *const pattern_type!(u32 is 1..), //~ ERROR: uses type `(u32) is 1..=` + nz: *const pattern_type!(u32 is 1..), // Ptr[Struct] - SomeStruct{b: ref p4,..}: & SomeStruct, + SomeStruct{b: ref p4,..}: & SomeStruct, //~ ERROR: uses type `&SomeStruct` // Ptr[Union] u2: *const SomeUnion, // Ptr[Enum], @@ -205,7 +207,7 @@ extern "C" fn all_ty_kinds_in_ptr( d2: *const dyn std::cmp::PartialOrd, //~ ERROR: uses type `*const dyn PartialOrd` // Ptr[Param], a: *const impl async Fn(u8)->u8, - // Alias (this gets caught outside of the code we want to test) + // Alias ) -> *const dyn std::fmt::Debug { //~ ERROR: uses type `*const dyn Debug` todo!() } @@ -215,19 +217,19 @@ 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, //~ ERROR: uses type `String` + s: &String, // Ref[Str] s2: &str, //~ ERROR: uses type `&str` // Ref[Char] - c: &char, //~ ERROR: uses type `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), //~ ERROR: uses type `(u8, u8)` + p: &(u8, u8), // deactivated here, because this is a function *declaration* (patterns unacceptable) - // (p2, p3):(&u8, &u8), //~ ERROR: uses type `(&u8, &u8)` + // (p2, p3):(&u8, &u8), // Pat nz: &pattern_type!(u32 is 1..), // deactivated here, because this is a function *declaration* (pattern unacceptable) @@ -248,14 +250,14 @@ fn all_ty_kinds_in_ref<'a>( x: &!, //r1: &u8, r2: &u8, r3: Box, // Ref[FnPtr] - f2: &fn(u8)->u8, //~ ERROR: uses type `fn(u8) -> u8` + 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] (this gets caught outside of the code we want to test) + // Ref[Dynamic] ) -> &'a dyn std::fmt::Debug; //~ ERROR: uses type `&dyn Debug` } @@ -279,7 +281,7 @@ extern "C" fn all_ty_kinds_in_box( // Pat nz: Option>, // Ref[Struct] - SomeStruct{b: ref p4,..}: &SomeStruct, + SomeStruct{b: ref p4,..}: &SomeStruct, //~ ERROR: uses type `&SomeStruct` // Box[Union] u2: Option>, // Box[Enum], @@ -304,9 +306,70 @@ extern "C" fn all_ty_kinds_in_box( d2: Box>, //~ ERROR: uses type `Box>` // Option[Box[Param]], a: Optionu8>>, - // Box[Dynamic] (this gets caught outside of the code we want to test) + // 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 index f70b619aee1b0..46b193200e1f1 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -1,5 +1,5 @@ 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:10:12 + --> $DIR/lint-tykind-fuzz.rs:11:12 | LL | #![feature(inherent_associated_types)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,22 +7,65 @@ 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:119:5 + --> $DIR/lint-tykind-fuzz.rs:121:5 | LL | s:String, | ^^^^^^ not FFI-safe | - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout -note: the lint level is defined here - --> $DIR/lint-tykind-fuzz.rs:5:25 - | -LL | #![deny(improper_ctypes,improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = 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:121:6 + --> $DIR/lint-tykind-fuzz.rs:123:6 | LL | s2:&str, | ^^^^ not FFI-safe @@ -31,7 +74,7 @@ LL | s2:&str, = 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:123:6 + --> $DIR/lint-tykind-fuzz.rs:125:6 | LL | c: char, | ^^^^ not FFI-safe @@ -40,7 +83,7 @@ LL | c: char, = note: the `char` type has no C equivalent error: `extern` fn uses type `&[u8]`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:125:6 + --> $DIR/lint-tykind-fuzz.rs:127:6 | LL | s3:&[u8], | ^^^^^ not FFI-safe @@ -49,7 +92,7 @@ LL | s3:&[u8], = 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:127:6 + --> $DIR/lint-tykind-fuzz.rs:129:6 | LL | s4:[u8;N], | ^^^^^^ not FFI-safe @@ -58,7 +101,7 @@ LL | s4:[u8;N], = 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:129:5 + --> $DIR/lint-tykind-fuzz.rs:131:5 | LL | p:(u8, u8), | ^^^^^^^^ not FFI-safe @@ -67,7 +110,7 @@ LL | p:(u8, u8), = note: tuples have unspecified layout error: `extern` fn uses type `(u8, u8)`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:131:12 + --> $DIR/lint-tykind-fuzz.rs:133:12 | LL | (p2, p3):(u8, u8), | ^^^^^^^^ not FFI-safe @@ -75,8 +118,8 @@ LL | (p2, p3):(u8, u8), = 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:133:7 +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 @@ -85,7 +128,7 @@ LL | nz: pattern_type!(u32 is 1..), = note: integers constrained to a given range 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:147:7 + --> $DIR/lint-tykind-fuzz.rs:149:7 | LL | e3: &StructWithDyn, | ^^^^^^^^^^^^^^ not FFI-safe @@ -93,7 +136,7 @@ LL | e3: &StructWithDyn, = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer error: `extern` fn uses type `fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:152:7 + --> $DIR/lint-tykind-fuzz.rs:154:7 | LL | f2: fn(u8)->u8, | ^^^^^^^^^^ not FFI-safe @@ -102,7 +145,7 @@ LL | f2: fn(u8)->u8, = 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:154:7 + --> $DIR/lint-tykind-fuzz.rs:156:7 | LL | f3: &'a dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -110,7 +153,7 @@ LL | f3: &'a dyn Fn(u8)->u8, = 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:156:7 + --> $DIR/lint-tykind-fuzz.rs:158:7 | LL | d2: &dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -118,7 +161,7 @@ LL | d2: &dyn std::cmp::PartialOrd, = 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:160:6 + --> $DIR/lint-tykind-fuzz.rs:162:6 | LL | ) -> impl std::fmt::Debug { | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -126,7 +169,7 @@ LL | ) -> impl std::fmt::Debug { = note: opaque types have no C equivalent error: `extern` fn uses type `*const str`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:170:7 + --> $DIR/lint-tykind-fuzz.rs:172:7 | LL | s2: *const str, | ^^^^^^^^^^ not FFI-safe @@ -135,7 +178,7 @@ LL | s2: *const str, = 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:174:7 + --> $DIR/lint-tykind-fuzz.rs:176:7 | LL | s3: *const [u8], | ^^^^^^^^^^^ not FFI-safe @@ -144,7 +187,7 @@ LL | s3: *const [u8], = 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:180:12 + --> $DIR/lint-tykind-fuzz.rs:182:12 | LL | (p2, p3):(*const u8, *const u8), | ^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -153,17 +196,17 @@ LL | (p2, p3):(*const u8, *const u8), = note: tuples have unspecified layout error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:184:29 + --> $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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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:196:7 + --> $DIR/lint-tykind-fuzz.rs:198:7 | LL | e3: *const StructWithDyn, | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -171,7 +214,7 @@ LL | e3: *const StructWithDyn, = 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:203:7 + --> $DIR/lint-tykind-fuzz.rs:205:7 | LL | f3: *const dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -179,7 +222,7 @@ LL | f3: *const dyn Fn(u8)->u8, = 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:205:7 + --> $DIR/lint-tykind-fuzz.rs:207:7 | LL | d2: *const dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -187,49 +230,29 @@ LL | d2: *const dyn std::cmp::PartialOrd, = 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:209:6 + --> $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 `String`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:218:6 - | -LL | s: &String, - | ^^^^^^^ not FFI-safe - | - = note: this reference (`&String`) is ABI-compatible with a C pointer, but `String` itself does not have a C layout - = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct - = note: this struct has unspecified layout -note: the lint level is defined here - --> $DIR/lint-tykind-fuzz.rs:5:9 - | -LL | #![deny(improper_ctypes,improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^ - error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:220:7 + --> $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 - -error: `extern` block uses type `char`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:222:6 - | -LL | c: &char, - | ^^^^^ not FFI-safe +note: the lint level is defined here + --> $DIR/lint-tykind-fuzz.rs:5:9 | - = note: this reference (`&char`) is ABI-compatible with a C pointer, but `char` itself does not have a C layout - = help: consider using `u32` or `libc::wchar_t` instead - = note: the `char` type has no C equivalent +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:224:7 + --> $DIR/lint-tykind-fuzz.rs:226:7 | LL | s3: &[u8], | ^^^^^ not FFI-safe @@ -237,36 +260,16 @@ LL | s3: &[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 -error: `extern` block uses type `(u8, u8)`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:228:6 - | -LL | p: &(u8, u8), - | ^^^^^^^^^ not FFI-safe - | - = note: this reference (`&(u8, u8)`) is ABI-compatible with a C pointer, but `(u8, u8)` itself does not have a C layout - = help: consider using a struct instead - = note: tuples have unspecified layout - error: `extern` block uses type `&StructWithDyn`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:246:7 + --> $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 `fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:251:7 - | -LL | f2: &fn(u8)->u8, - | ^^^^^^^^^^^ not FFI-safe - | - = note: this reference (`&fn(u8) -> u8`) is ABI-compatible with a C pointer, but `fn(u8) -> u8` itself does not have a C layout - = help: consider using an `extern fn(...) -> ...` function pointer instead - = note: this function pointer has Rust-specific calling convention - error: `extern` block uses type `&dyn Fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:253:7 + --> $DIR/lint-tykind-fuzz.rs:255:7 | LL | f3: &dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^ not FFI-safe @@ -274,7 +277,7 @@ LL | f3: &dyn Fn(u8)->u8, = 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:255:7 + --> $DIR/lint-tykind-fuzz.rs:257:7 | LL | d2: &dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -282,7 +285,7 @@ LL | d2: &dyn std::cmp::PartialOrd, = 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:259:6 + --> $DIR/lint-tykind-fuzz.rs:261:6 | LL | ) -> &'a dyn std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -290,7 +293,7 @@ LL | ) -> &'a dyn std::fmt::Debug; = 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:268:7 + --> $DIR/lint-tykind-fuzz.rs:270:7 | LL | s2: Box, | ^^^^^^^^ not FFI-safe @@ -299,17 +302,17 @@ LL | s2: Box, = 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:270:6 + --> $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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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:272:7 + --> $DIR/lint-tykind-fuzz.rs:274:7 | LL | s3: Box<[u8]>, | ^^^^^^^^^ not FFI-safe @@ -318,7 +321,7 @@ LL | s3: Box<[u8]>, = 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:278:11 + --> $DIR/lint-tykind-fuzz.rs:280:11 | LL | (p2,p3):(Box, Box), | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -327,17 +330,17 @@ LL | (p2,p3):(Box, Box), = note: tuples have unspecified layout error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:282:29 + --> $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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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:294:7 + --> $DIR/lint-tykind-fuzz.rs:296:7 | LL | e3: Box, | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -345,27 +348,27 @@ LL | e3: Box, = 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:297:6 + --> $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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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:300:7 + --> $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 and references are assumed to be valid (non-null, non-dangling, aligned) pointers, + = 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 + --> $DIR/lint-tykind-fuzz.rs:304:7 | LL | f3: Boxu8>, | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -373,7 +376,7 @@ LL | f3: Boxu8>, = 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:304:7 + --> $DIR/lint-tykind-fuzz.rs:306:7 | LL | d2: Box>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -381,12 +384,108 @@ LL | d2: Box>, = 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:308:6 + --> $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: aborting due to 42 previous errors; 1 warning emitted +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 51 previous errors; 1 warning emitted diff --git a/tests/ui/lint/improper_ctypes/mustpass-113436.rs b/tests/ui/lint/improper_ctypes/mustpass-113436.rs index d5acdc45f92e5..b36013f4482cd 100644 --- a/tests/ui/lint/improper_ctypes/mustpass-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/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 723d0aa3d54c3..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,65 +36,53 @@ 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 | - = note: this struct/enum/union (`B`) is FFI-unsafe due to a `A` field + = 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:13:1 | LL | struct B { | ^^^^^^^^ - = 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/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: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 | - = note: this struct/enum/union (`B`) is FFI-unsafe due to a `A` field + = 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:13:1 | LL | struct B { | ^^^^^^^^ - = 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/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: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 @@ -83,13 +93,13 @@ note: the type is defined here | LL | struct D { | ^^^^^^^^ - = 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: 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/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/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 From d28660dcc9fe86aa991c751e0b8123fd1bc93798 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Tue, 6 May 2025 22:42:23 +0200 Subject: [PATCH 11/15] lint ImproperCTypes: deal with uninhabited types --- compiler/rustc_lint/messages.ftl | 5 + .../rustc_lint/src/types/improper_ctypes.rs | 191 +++++++++++++++--- tests/ui/lint/improper_ctypes/lint-enum.rs | 2 +- .../ui/lint/improper_ctypes/lint-enum.stderr | 25 ++- .../lint/improper_ctypes/lint-tykind-fuzz.rs | 2 +- .../improper_ctypes/lint-tykind-fuzz.stderr | 10 +- .../lint/improper_ctypes/lint_uninhabited.rs | 75 +++++++ .../improper_ctypes/lint_uninhabited.stderr | 159 +++++++++++++++ tests/ui/structs-enums/foreign-struct.rs | 15 +- 9 files changed, 438 insertions(+), 46 deletions(-) create mode 100644 tests/ui/lint/improper_ctypes/lint_uninhabited.rs create mode 100644 tests/ui/lint/improper_ctypes/lint_uninhabited.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 44c78a9ff2a16..7f5309fdafc56 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -441,6 +441,11 @@ 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_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 diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 9cb2c9386c8ac..c9e23535453a4 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -189,6 +189,49 @@ impl<'tcx> FfiResult<'tcx> { } } + /// 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. @@ -554,6 +597,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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> { @@ -721,23 +803,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { use FfiResult::*; - let (transparent_with_all_zst_fields, field_list) = if def.repr().transparent() { - // determine if there is 0 or 1 non-1ZST field, and which it is. - // (note: enums are not allowed to br transparent) - 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]) + 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 { - // ..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::>()) - }; + (false, variant.fields.iter().collect::>()) + }; - let mut field_ffires = FfiSafe; // 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()]; @@ -763,7 +867,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe => false, r @ FfiUnsafe { .. } => { fields_ok_list[field_i] = false; - field_ffires += r; + ffires_accumulator += r; false } } @@ -773,7 +877,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // (if this combination is somehow possible) // otherwide, having all fields be phantoms // takes priority over transparent_with_all_zst_fields - if let FfiUnsafe(explanations) = field_ffires { + 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()) @@ -812,14 +916,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let help = if non_1zst_count == 1 && last_non_1zst.map(|field_i| fields_ok_list[field_i]) == Some(true) { - match def.adt_kind() { - AdtKind::Struct => { - Some(fluent::lint_improper_ctypes_struct_consider_transparent) - } - AdtKind::Union => { - Some(fluent::lint_improper_ctypes_union_consider_transparent) + 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)"), } - AdtKind::Enum => bug!("cannot suggest an enum to be repr(transparent)"), } } else { None @@ -927,8 +1036,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.variants().is_empty() { // Empty enums are implicitely handled as the never type: - // FIXME think about the FFI-safety of functions that use that - return FfiSafe; + return self.visit_uninhabited(state, outer_ty, ty); } // Check for a repr() attribute to specify the size of the // discriminant. @@ -973,18 +1081,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { None, ) } else { - let ffires = def + // 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() - .map(|variant| { - self.visit_variant_fields(state, ty, def, variant, args) - // 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?) - .forbid_phantom() + .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``) @@ -1078,7 +1203,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Int(..) | ty::Uint(..) | ty::Float(..) => self.visit_numeric(ty), // Primitive types with a stable representation. - ty::Bool | ty::Never => FfiSafe, + ty::Bool => FfiSafe, ty::Slice(_) => FfiResult::new_with_reason( ty, @@ -1197,6 +1322,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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 diff --git a/tests/ui/lint/improper_ctypes/lint-enum.rs b/tests/ui/lint/improper_ctypes/lint-enum.rs index 3b57433fca153..1e56b829b9242 100644 --- a/tests/ui/lint/improper_ctypes/lint-enum.rs +++ b/tests/ui/lint/improper_ctypes/lint-enum.rs @@ -79,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/improper_ctypes/lint-enum.stderr b/tests/ui/lint/improper_ctypes/lint-enum.stderr index 6cc1793a0b0ab..410ce4eaef1f4 100644 --- a/tests/ui/lint/improper_ctypes/lint-enum.stderr +++ b/tests/ui/lint/improper_ctypes/lint-enum.stderr @@ -1,3 +1,21 @@ +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-enum.rs:83:14 | @@ -11,11 +29,6 @@ note: the type is defined here | LL | enum U { | ^^^^^^ -note: the lint level is defined here - --> $DIR/lint-enum.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe --> $DIR/lint-enum.rs:84:14 @@ -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/improper_ctypes/lint-tykind-fuzz.rs b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs index 56a1a02e576e7..6e4f10d8704c0 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs @@ -148,7 +148,7 @@ extern "C" fn all_ty_kinds<'a,const N:usize,T>( // Ref[Struct] e3: &StructWithDyn, //~ ERROR: uses type `&StructWithDyn` // Never - x:!, + x:!, //~ ERROR: uses type `!` //r1: &u8, r2: *const u8, r3: Box, // FnPtr f2: fn(u8)->u8, //~ ERROR: uses type `fn(u8) -> u8` diff --git a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr index 46b193200e1f1..f9d4fca1d79be 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -135,6 +135,14 @@ LL | e3: &StructWithDyn, | = 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 | @@ -487,5 +495,5 @@ LL | | } | = note: this reference to an unsized type contains metadata, which makes it incompatible with a C pointer -error: aborting due to 51 previous errors; 1 warning emitted +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/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); } } From a64e55b4109daf066789bfdd159acfecda604659 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Tue, 6 May 2025 23:22:26 +0200 Subject: [PATCH 12/15] lint ImproperCTypes: redo handling of pattern types [...] - also fix a couple of thorny typos in/around types.rs::is_outer_optionlike_around_ty() and subsequently fix library and tests --- compiler/rustc_lint/messages.ftl | 6 +- compiler/rustc_lint/src/foreign_modules.rs | 4 +- compiler/rustc_lint/src/types.rs | 213 +++++++++++++++++- .../rustc_lint/src/types/improper_ctypes.rs | 106 ++++++--- library/proc_macro/src/bridge/buffer.rs | 14 +- library/proc_macro/src/bridge/closure.rs | 6 +- tests/ui/lint/clashing-extern-fn.stderr | 12 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 4 +- tests/ui/lint/improper_ctypes/lint-fn.rs | 5 +- tests/ui/lint/improper_ctypes/lint-fn.stderr | 30 ++- .../improper_ctypes/lint-pattern-types.rs | 92 ++++++++ .../improper_ctypes/lint-pattern-types.stderr | 152 +++++++++++++ .../improper_ctypes/lint-tykind-fuzz.stderr | 4 +- 13 files changed, 570 insertions(+), 78 deletions(-) create mode 100644 tests/ui/lint/improper_ctypes/lint-pattern-types.rs create mode 100644 tests/ui/lint/improper_ctypes/lint-pattern-types.stderr diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 7f5309fdafc56..5f46bfdb63d10 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -413,8 +413,10 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` lint_improper_ctypes_opaque = opaque types have no C equivalent -lint_improper_ctypes_pat_intrange_help = consider using the base type instead -lint_improper_ctypes_pat_intrange_reason = integers constrained to a given range cannot have their value be provided by non-rust code +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 = diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 7e892dd7c413d..49b02a0d503a3 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -366,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) { + 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) { + 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/types.rs b/compiler/rustc_lint/src/types.rs index c4cc327f35d2b..e716e23e4e635 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,10 +1,10 @@ use std::iter; -use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange}; +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, AdtKind, Ty, TyCtxt, 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::{Span, Symbol, sym}; use tracing::debug; @@ -858,13 +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>, + checked_conversion_is_from: bool, ) -> Option> { debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty); match ty.kind() { @@ -889,6 +890,20 @@ pub(crate) fn repr_nullable_ptr<'tcx>( _ => return None, }; + 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; } @@ -925,11 +940,191 @@ pub(crate) fn repr_nullable_ptr<'tcx>( } None } - ty::Pat(base, pat) => 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) + } + } _ => None, } } +/// 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), + }; + + 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() + } + }; + 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() + } + }; + (is_signed, usable_size, start, end) + } + + 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()) + }; + + if (start.to_bits(scalar_size), end.to_bits(scalar_size)) == (scalar_min, scalar_max) { + return None; + } + + // 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?"), + } + } + 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 + }; + 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) + } + } else { + // 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 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; + } + + if only_had_overlaps { None } else { Some(disallowed_value_count) } + } + } +} + fn get_nullable_type_from_pat<'tcx>( tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, @@ -959,19 +1154,19 @@ fn is_outer_optionlike_around_ty<'tcx>( // 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) + if (!matches!(def.adt_kind(), AdtKind::Enum)) || def.repr().c() || def.repr().transparent() - || def.repr().int.is_none() + || def.repr().int.is_some() { false } else { let (tcx, typing_env) = (cx.tcx, cx.typing_env()); // see the insides of super::repr_nullable_ptr() - let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env).ok(); + let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, typing_env); match (compute_size_skeleton(ty), compute_size_skeleton(outer_ty)) { - (Some(sk1), Some(sk2)) => sk1.same_size(sk2), + (Ok(sk1), Ok(sk2)) => sk1.same_size(sk2), _ => false, } } diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index c9e23535453a4..f2211d00ce1b2 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -645,6 +645,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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"), } } @@ -1045,7 +1051,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { && 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) { + 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); } @@ -1170,37 +1181,60 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - ty::Char => FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_char_reason, - Some(fluent::lint_improper_ctypes_char_help), - ), - - ty::Pat(pat_ty, _) => { - if state.value_may_be_unchecked() { - // you would think that int-range pattern types that exclude 0 would have Option layout optimisation - // they don't (see tests/ui/type/pattern_types/range_patterns.stderr) - // so there's no need to allow Option. - debug_assert!(matches!( - pat_ty.kind(), - ty::Int(..) | ty::Uint(..) | ty::Float(..) - )); - FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_pat_intrange_reason, - Some(fluent::lint_improper_ctypes_pat_intrange_help), - ) - } else if let ty::Int(_) | ty::Uint(_) = pat_ty.kind() { - self.visit_numeric(pat_ty) - } else { + 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::Int(..) | ty::Uint(..) | ty::Float(..) => self.visit_numeric(ty), + ty::Char | ty::Int(..) | ty::Uint(..) | ty::Float(..) => self.visit_numeric(ty), // Primitive types with a stable representation. ty::Bool => FfiSafe, @@ -1304,17 +1338,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe }; - if state.value_may_be_unchecked() - && outer_ty - .map(|outer_ty| super::is_outer_optionlike_around_ty(self.cx, outer_ty, ty)) - == Some(true) - { - inherent_safety - + FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_ptr_validity_reason, - Some(fluent::lint_improper_ctypes_ptr_validity_help), - ) + 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 } 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/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/tests/ui/lint/clashing-extern-fn.stderr b/tests/ui/lint/clashing-extern-fn.stderr index d559e5b0e4a04..14a9cd7be42a1 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -23,8 +23,8 @@ warning: `extern` block uses type `(usize) is 1..`, which is not FFI-safe LL | fn pt_non_zero_usize() -> pattern_type!(usize is 1..); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using the base type instead - = note: integers constrained to a given range cannot have their value be provided by non-rust code + = 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:503:54 @@ -41,8 +41,8 @@ warning: `extern` block uses type `(usize) is 1..`, which is not FFI-safe LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using the base type instead - = note: integers constrained to a given range cannot have their value be provided by non-rust code + = 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 @@ -56,8 +56,8 @@ note: the type is defined here | LL | struct NonZeroUsize(pattern_type!(usize is 1..)); | ^^^^^^^^^^^^^^^^^^^ - = help: consider using the base type instead - = note: integers constrained to a given range cannot have their value be provided by non-rust code + = 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 diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index 9f2132219506f..ef5219cb92bb5 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -111,8 +111,8 @@ error: `extern` block uses type `(u32) is 1..`, which is not FFI-safe LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using the base type instead - = note: integers constrained to a given range cannot have their value be provided by non-rust code + = 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 diff --git a/tests/ui/lint/improper_ctypes/lint-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs index 479a61a8036d4..ecf21618e12ef 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.rs +++ b/tests/ui/lint/improper_ctypes/lint-fn.rs @@ -40,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); @@ -131,7 +131,8 @@ pub extern "C" fn transparent_str(p: TransparentStr) { } //~^ ERROR: uses type `TransparentStr` pub extern "C" fn transparent_fn(p: TransparentBadFn) { } -// ^ FIXME it doesn't see the error... but at least it reports it elsewhere? +//~^ 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) { } diff --git a/tests/ui/lint/improper_ctypes/lint-fn.stderr b/tests/ui/lint/improper_ctypes/lint-fn.stderr index 6b43d0b64e303..06086de892cec 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.stderr +++ b/tests/ui/lint/improper_ctypes/lint-fn.stderr @@ -216,8 +216,24 @@ 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:138:53 + --> $DIR/lint-fn.rs:139:53 | LL | pub extern "C" fn argument_with_assumptions_4(aptr: &[u8; 4 as usize]) { } | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -227,7 +243,7 @@ LL | pub extern "C" fn argument_with_assumptions_4(aptr: &[u8; 4 as usize]) { } 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:143:50 + --> $DIR/lint-fn.rs:144:50 | LL | pub extern "C" fn argument_with_assumptions_6(s: StructWithProjectionAndLifetime) { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -243,7 +259,7 @@ LL | pub struct StructWithProjectionAndLifetime<'a>( 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:162:51 + --> $DIR/lint-fn.rs:163:51 | LL | pub extern "C" fn argument_with_assumptions_14(p: TransparentRef) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -259,7 +275,7 @@ LL | pub struct TransparentRef<'a>(&'a TransparentInt); 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:182:43 + --> $DIR/lint-fn.rs:183:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -267,7 +283,7 @@ LL | pub extern "C" fn unused_generic2() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-fn.rs:195:39 + --> $DIR/lint-fn.rs:196:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -276,7 +292,7 @@ LL | pub extern "C" fn used_generic4(x: Vec) { } = note: `Vec` has unspecified layout error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-fn.rs:198:41 + --> $DIR/lint-fn.rs:199:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe @@ -284,5 +300,5 @@ LL | pub extern "C" fn used_generic5() -> Vec { = 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 26 previous errors +error: aborting due to 27 previous errors 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-tykind-fuzz.stderr b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr index f9d4fca1d79be..02bb8b9f76671 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -124,8 +124,8 @@ error: `extern` fn uses type `(u32) is 1..`, which is not FFI-safe LL | nz: pattern_type!(u32 is 1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe | - = help: consider using the base type instead - = note: integers constrained to a given range cannot have their value be provided by non-rust code + = 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 From 955cf3c52d1476cc86ea21f75176aefdef346557 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Tue, 6 May 2025 23:22:26 +0200 Subject: [PATCH 13/15] lint ImproperCTypes: change what elements are being checked [...] - do not check ADT definitions themselves, it turns out `repr(C)` is not a strong enough signal to determine if something is designed for FFIs - however, start checking static variables with `#[no_mangle]` or `#[export_name=_]`, even if that's not a perfect signal due to the lack of specified ABI - some changes to the LLVM codegen so it can be seen as FFI-safe --- compiler/rustc_codegen_llvm/src/back/write.rs | 3 +- .../rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 7 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 527 ++++++++++-------- compiler/rustc_lint/src/lib.rs | 1 + compiler/rustc_lint/src/types.rs | 4 +- .../rustc_lint/src/types/improper_ctypes.rs | 74 ++- tests/ui/layout/reprc-power-alignment.rs | 2 +- .../ui/lint/improper_ctypes/lint-113436-1.rs | 2 +- ...-allocations-dont-inherit-codegen-attrs.rs | 2 + 9 files changed, 384 insertions(+), 238 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 20721c7460878..4b491a4a45358 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -462,10 +462,11 @@ fn report_inline_asm( cgcx.diag_emitter.inline_asm_error(span, msg, level, source); } -unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void) { +unsafe extern "C" fn diagnostic_handler(info: Option<&DiagnosticInfo>, user: *mut c_void) { if user.is_null() { return; } + let info = info.unwrap(); let (cgcx, dcx) = unsafe { *(user as *const (&CodegenContext, DiagCtxtHandle<'_>)) }; diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 2ad39fc853819..dd57725eca913 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -6,7 +6,9 @@ use libc::{c_char, c_uint}; use super::MetadataKindId; use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; use crate::llvm::Bool; +use crate::wrap_returns_in_options; +wrap_returns_in_options! { #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { // Enzyme @@ -15,7 +17,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustGetLastInstruction<'a>(BB: &BasicBlock) -> Option<&'a Value>; pub(crate) fn LLVMRustDIGetInstMetadata(I: &Value) -> Option<&Metadata>; pub(crate) fn LLVMRustEraseInstFromParent(V: &Value); - pub(crate) fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; + |wrap pub(crate) fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; pub(crate) fn LLVMRustVerifyFunction(V: &Value, action: LLVMRustVerifierFailureAction) -> Bool; pub(crate) fn LLVMRustHasAttributeAtIndex(V: &Value, i: c_uint, Kind: AttributeKind) -> bool; pub(crate) fn LLVMRustGetArrayNumElements(Ty: &Type) -> u64; @@ -39,10 +41,11 @@ unsafe extern "C" { pub(crate) fn LLVMDumpModule(M: &Module); pub(crate) fn LLVMDumpValue(V: &Value); pub(crate) fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; - pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type; + |wrap pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type; pub(crate) fn LLVMGetParams(Fnc: &Value, parms: *mut &Value); pub(crate) fn LLVMGetNamedFunction(M: &Module, Name: *const c_char) -> Option<&Value>; } +} #[repr(C)] #[derive(Copy, Clone, PartialEq)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 67a66e6ec795f..fa6596a797c21 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -30,6 +30,83 @@ use super::debuginfo::{ }; use crate::llvm; +/// wrap an `extern "ABI"` block that only imports functions +/// use it, and for all functions that return references or boxes, +/// preface them/their docs with `|wrap` for automated non-null-assumption checks on those returns +#[macro_export] +macro_rules! wrap_returns_in_options { + ( + $( + $(#[$em:meta])* + unsafe extern $abi:literal {$( + $( + $(#[$nwm:meta])* $nwvs:vis + $(safe fn $snwfn_name:ident)? + $(unsafe fn $unwfn_name:ident)? + $(fn $nwfn_name:ident)? + $(<$($nwlt:lifetime),*>)? ( + $($nwarg:ident : $nwargty:ty),*$(,)? + ) $(-> $nwout:ty)? + )? + $( |wrap + $(#[$m:meta])* $vs:vis + $(safe fn $sfn_name:ident)? + $(unsafe fn $ufn_name:ident)? + $(fn $fn_name:ident)? + $(<$($lt:lifetime),*>)? ( + $($arg:ident : $argty:ty),*$(,)? + ) -> $out:ty + )? + );*} + )* + ) => { + $($(#[$em])* + unsafe extern $abi {$( + $( + $(#[$nwm])* $nwvs + $(safe fn $snwfn_name)? + $(unsafe fn $unwfn_name)? + $(fn $nwfn_name)? + $(<$($nwlt),*>)? ( + $($nwarg: $nwargty),* + ) $(-> $nwout)?; + )? + )*})* + + mod macro_internal_functions{ + use super::*; + $($(#[$em])* + unsafe extern $abi {$( + $( + $(#[$m])* pub(super) + $(fn $fn_name)? + $(fn $ufn_name)? + $(fn $sfn_name)? + $(<$($lt),*>)? ( + $($arg: $argty),* + ) -> Option<$out>; + )? + )*})* + } + $($($( + #[inline] + $(#[$m])* $vs + $(fn $sfn_name)? + $(unsafe fn $ufn_name)? + $(unsafe fn $fn_name)? + $(<$($lt),*>)? ( + $($arg: $argty),* + ) -> $out { + unsafe{macro_internal_functions:: + $($fn_name)? + $($ufn_name)? + $($sfn_name)? + ($($arg),*)}.unwrap() + } + )?)*)* + }; +} + /// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, /// which has a different ABI from Rust or C++ `bool`. pub(crate) type Bool = c_int; @@ -780,7 +857,7 @@ unsafe extern "C" { pub(crate) type DiagnosticHandler; } -pub(crate) type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void); +pub(crate) type DiagnosticHandlerTy = unsafe extern "C" fn(Option<&DiagnosticInfo>, *mut c_void); pub(crate) mod debuginfo { use std::ptr; @@ -994,6 +1071,7 @@ impl From for MetadataKindId { } } +wrap_returns_in_options! { unsafe extern "C" { // Create and destroy contexts. pub(crate) fn LLVMContextDispose(C: &'static mut Context); @@ -1004,11 +1082,11 @@ unsafe extern "C" { ) -> MetadataKindId; // Create modules. - pub(crate) fn LLVMModuleCreateWithNameInContext( + |wrap pub(crate) fn LLVMModuleCreateWithNameInContext( ModuleID: *const c_char, C: &Context, ) -> &Module; - pub(crate) fn LLVMCloneModule(M: &Module) -> &Module; + |wrap pub(crate) fn LLVMCloneModule(M: &Module) -> &Module; /// Data layout. See Module::getDataLayout. pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; @@ -1035,23 +1113,23 @@ unsafe extern "C" { ) -> &'ll Value; // Operations on integer types - pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMInt8TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMInt16TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMInt32TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMInt64TypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; + |wrap pub(crate) fn LLVMInt1TypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMInt8TypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMInt16TypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMInt32TypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMInt64TypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMIntTypeInContext(C: &Context, NumBits: c_uint) -> &Type; pub(crate) fn LLVMGetIntTypeWidth(IntegerTy: &Type) -> c_uint; // Operations on real types - pub(crate) fn LLVMHalfTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMFloatTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMDoubleTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMFP128TypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMHalfTypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMFloatTypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMDoubleTypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMFP128TypeInContext(C: &Context) -> &Type; // Operations on function types - pub(crate) fn LLVMFunctionType<'a>( + |wrap pub(crate) fn LLVMFunctionType<'a>( ReturnType: &'a Type, ParamTypes: *const &'a Type, ParamCount: c_uint, @@ -1061,7 +1139,7 @@ unsafe extern "C" { pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type); // Operations on struct types - pub(crate) fn LLVMStructTypeInContext<'a>( + |wrap pub(crate) fn LLVMStructTypeInContext<'a>( C: &'a Context, ElementTypes: *const &'a Type, ElementCount: c_uint, @@ -1069,38 +1147,38 @@ unsafe extern "C" { ) -> &'a Type; // Operations on array, pointer, and vector types (sequence types) - pub(crate) fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; - pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; + |wrap pub(crate) fn LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; + |wrap pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; - pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type; + |wrap pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type; pub(crate) fn LLVMGetVectorSize(VectorTy: &Type) -> c_uint; // Operations on other types - pub(crate) fn LLVMVoidTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMTokenTypeInContext(C: &Context) -> &Type; - pub(crate) fn LLVMMetadataTypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMVoidTypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMTokenTypeInContext(C: &Context) -> &Type; + |wrap pub(crate) fn LLVMMetadataTypeInContext(C: &Context) -> &Type; // Operations on all values - pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type; + |wrap pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type; pub(crate) fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char; pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value); pub(crate) safe fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: MetadataKindId, Node: &'a Value); pub(crate) fn LLVMGlobalSetMetadata<'a>(Val: &'a Value, KindID: c_uint, Metadata: &'a Metadata); - pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; + |wrap pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; // Operations on constants of any type - pub(crate) fn LLVMConstNull(Ty: &Type) -> &Value; - pub(crate) fn LLVMGetUndef(Ty: &Type) -> &Value; - pub(crate) fn LLVMGetPoison(Ty: &Type) -> &Value; + |wrap pub(crate) fn LLVMConstNull(Ty: &Type) -> &Value; + |wrap pub(crate) fn LLVMGetUndef(Ty: &Type) -> &Value; + |wrap pub(crate) fn LLVMGetPoison(Ty: &Type) -> &Value; // Operations on metadata - pub(crate) fn LLVMMDStringInContext2( + |wrap pub(crate) fn LLVMMDStringInContext2( C: &Context, Str: *const c_char, SLen: size_t, ) -> &Metadata; - pub(crate) fn LLVMMDNodeInContext2<'a>( + |wrap pub(crate) fn LLVMMDNodeInContext2<'a>( C: &'a Context, Vals: *const &'a Metadata, Count: size_t, @@ -1112,46 +1190,46 @@ unsafe extern "C" { ); // Operations on scalar constants - pub(crate) fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value; - pub(crate) fn LLVMConstIntOfArbitraryPrecision( + |wrap pub(crate) fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value; + |wrap pub(crate) fn LLVMConstIntOfArbitraryPrecision( IntTy: &Type, Wn: c_uint, Ws: *const u64, ) -> &Value; - pub(crate) fn LLVMConstReal(RealTy: &Type, N: f64) -> &Value; + |wrap pub(crate) fn LLVMConstReal(RealTy: &Type, N: f64) -> &Value; // Operations on composite constants - pub(crate) fn LLVMConstArray2<'a>( + |wrap pub(crate) fn LLVMConstArray2<'a>( ElementTy: &'a Type, ConstantVals: *const &'a Value, Length: u64, ) -> &'a Value; - pub(crate) fn LLVMArrayType2(ElementType: &Type, ElementCount: u64) -> &Type; - pub(crate) fn LLVMConstStringInContext2( + |wrap pub(crate) fn LLVMArrayType2(ElementType: &Type, ElementCount: u64) -> &Type; + |wrap pub(crate) fn LLVMConstStringInContext2( C: &Context, Str: *const c_char, Length: size_t, DontNullTerminate: Bool, ) -> &Value; - pub(crate) fn LLVMConstStructInContext<'a>( + |wrap pub(crate) fn LLVMConstStructInContext<'a>( C: &'a Context, ConstantVals: *const &'a Value, Count: c_uint, Packed: Bool, ) -> &'a Value; - pub(crate) fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value; + |wrap pub(crate) fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value; // Constant expressions - pub(crate) fn LLVMConstInBoundsGEP2<'a>( + |wrap pub(crate) fn LLVMConstInBoundsGEP2<'a>( ty: &'a Type, ConstantVal: &'a Value, ConstantIndices: *const &'a Value, NumIndices: c_uint, ) -> &'a Value; - pub(crate) fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; - pub(crate) fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; - pub(crate) fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; - pub(crate) fn LLVMConstPointerCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + |wrap pub(crate) fn LLVMConstPtrToInt<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + |wrap pub(crate) fn LLVMConstIntToPtr<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + |wrap pub(crate) fn LLVMConstBitCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; + |wrap pub(crate) fn LLVMConstPointerCast<'a>(ConstantVal: &'a Value, ToType: &'a Type) -> &'a Value; pub(crate) fn LLVMGetAggregateElement(ConstantVal: &Value, Idx: c_uint) -> Option<&Value>; pub(crate) fn LLVMGetConstOpcode(ConstantVal: &Value) -> Opcode; pub(crate) fn LLVMIsAConstantExpr(Val: &Value) -> Option<&Value>; @@ -1166,11 +1244,11 @@ unsafe extern "C" { pub(crate) fn LLVMGetAlignment(Global: &Value) -> c_uint; pub(crate) fn LLVMSetAlignment(Global: &Value, Bytes: c_uint); pub(crate) fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass); - pub(crate) fn LLVMGlobalGetValueType(Global: &Value) -> &Type; + |wrap pub(crate) fn LLVMGlobalGetValueType(Global: &Value) -> &Type; // Operations on global variables pub(crate) fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; - pub(crate) fn LLVMAddGlobal<'a>(M: &'a Module, Ty: &'a Type, Name: *const c_char) -> &'a Value; + |wrap pub(crate) fn LLVMAddGlobal<'a>(M: &'a Module, Ty: &'a Type, Name: *const c_char) -> &'a Value; pub(crate) fn LLVMGetNamedGlobal(M: &Module, Name: *const c_char) -> Option<&Value>; pub(crate) fn LLVMGetFirstGlobal(M: &Module) -> Option<&Value>; pub(crate) fn LLVMGetNextGlobal(GlobalVar: &Value) -> Option<&Value>; @@ -1184,7 +1262,7 @@ unsafe extern "C" { pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); // Operations on attributes - pub(crate) fn LLVMCreateStringAttribute( + |wrap pub(crate) fn LLVMCreateStringAttribute( C: &Context, Name: *const c_char, NameLen: c_uint, @@ -1198,11 +1276,11 @@ unsafe extern "C" { // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; pub(crate) safe fn LLVMCountParams(Fn: &Value) -> c_uint; - pub(crate) fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value; + |wrap pub(crate) fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value; // Operations on basic blocks - pub(crate) fn LLVMGetBasicBlockParent(BB: &BasicBlock) -> &Value; - pub(crate) fn LLVMAppendBasicBlockInContext<'a>( + |wrap pub(crate) fn LLVMGetBasicBlockParent(BB: &BasicBlock) -> &Value; + |wrap pub(crate) fn LLVMAppendBasicBlockInContext<'a>( C: &'a Context, Fn: &'a Value, Name: *const c_char, @@ -1210,7 +1288,7 @@ unsafe extern "C" { // Operations on instructions pub(crate) fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>; - pub(crate) fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock; + |wrap pub(crate) fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock; pub(crate) fn LLVMGetOperand(Val: &Value, Index: c_uint) -> Option<&Value>; // Operations on call sites @@ -1228,9 +1306,9 @@ unsafe extern "C" { ); // Instruction builders - pub(crate) fn LLVMCreateBuilderInContext(C: &Context) -> &mut Builder<'_>; + |wrap pub(crate) fn LLVMCreateBuilderInContext(C: &Context) -> &mut Builder<'_>; pub(crate) fn LLVMPositionBuilderAtEnd<'a>(Builder: &Builder<'a>, Block: &'a BasicBlock); - pub(crate) fn LLVMGetInsertBlock<'a>(Builder: &Builder<'a>) -> &'a BasicBlock; + |wrap pub(crate) fn LLVMGetInsertBlock<'a>(Builder_: &Builder<'a>) -> &'a BasicBlock; pub(crate) fn LLVMDisposeBuilder<'a>(Builder: &'a mut Builder<'a>); // Metadata @@ -1238,30 +1316,30 @@ unsafe extern "C" { pub(crate) fn LLVMGetCurrentDebugLocation2<'a>(Builder: &Builder<'a>) -> Option<&'a Metadata>; // Terminators - pub(crate) safe fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value; - pub(crate) fn LLVMBuildRet<'a>(B: &Builder<'a>, V: &'a Value) -> &'a Value; - pub(crate) fn LLVMBuildBr<'a>(B: &Builder<'a>, Dest: &'a BasicBlock) -> &'a Value; - pub(crate) fn LLVMBuildCondBr<'a>( + |wrap pub(crate) safe fn LLVMBuildRetVoid<'a>(B: &Builder<'a>) -> &'a Value; + |wrap pub(crate) fn LLVMBuildRet<'a>(B: &Builder<'a>, V: &'a Value) -> &'a Value; + |wrap pub(crate) fn LLVMBuildBr<'a>(B: &Builder<'a>, Dest: &'a BasicBlock) -> &'a Value; + |wrap pub(crate) fn LLVMBuildCondBr<'a>( B: &Builder<'a>, If: &'a Value, Then: &'a BasicBlock, Else: &'a BasicBlock, ) -> &'a Value; - pub(crate) fn LLVMBuildSwitch<'a>( + |wrap pub(crate) fn LLVMBuildSwitch<'a>( B: &Builder<'a>, V: &'a Value, Else: &'a BasicBlock, NumCases: c_uint, ) -> &'a Value; - pub(crate) fn LLVMBuildLandingPad<'a>( + |wrap pub(crate) fn LLVMBuildLandingPad<'a>( B: &Builder<'a>, Ty: &'a Type, PersFn: Option<&'a Value>, NumClauses: c_uint, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildResume<'a>(B: &Builder<'a>, Exn: &'a Value) -> &'a Value; - pub(crate) fn LLVMBuildUnreachable<'a>(B: &Builder<'a>) -> &'a Value; + |wrap pub(crate) fn LLVMBuildResume<'a>(B: &Builder<'a>, Exn: &'a Value) -> &'a Value; + |wrap pub(crate) fn LLVMBuildUnreachable<'a>(B: &Builder<'a>) -> &'a Value; pub(crate) fn LLVMBuildCleanupPad<'a>( B: &Builder<'a>, @@ -1307,170 +1385,170 @@ unsafe extern "C" { pub(crate) fn LLVMSetCleanup(LandingPad: &Value, Val: Bool); // Arithmetic - pub(crate) fn LLVMBuildAdd<'a>( + |wrap pub(crate) fn LLVMBuildAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFAdd<'a>( + |wrap pub(crate) fn LLVMBuildFAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildSub<'a>( + |wrap pub(crate) fn LLVMBuildSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFSub<'a>( + |wrap pub(crate) fn LLVMBuildFSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildMul<'a>( + |wrap pub(crate) fn LLVMBuildMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFMul<'a>( + |wrap pub(crate) fn LLVMBuildFMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildUDiv<'a>( + |wrap pub(crate) fn LLVMBuildUDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildExactUDiv<'a>( + |wrap pub(crate) fn LLVMBuildExactUDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildSDiv<'a>( + |wrap pub(crate) fn LLVMBuildSDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildExactSDiv<'a>( + |wrap pub(crate) fn LLVMBuildExactSDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFDiv<'a>( + |wrap pub(crate) fn LLVMBuildFDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildURem<'a>( + |wrap pub(crate) fn LLVMBuildURem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildSRem<'a>( + |wrap pub(crate) fn LLVMBuildSRem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFRem<'a>( + |wrap pub(crate) fn LLVMBuildFRem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildShl<'a>( + |wrap pub(crate) fn LLVMBuildShl<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildLShr<'a>( + |wrap pub(crate) fn LLVMBuildLShr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildAShr<'a>( + |wrap pub(crate) fn LLVMBuildAShr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildNSWAdd<'a>( + |wrap pub(crate) fn LLVMBuildNSWAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildNUWAdd<'a>( + |wrap pub(crate) fn LLVMBuildNUWAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildNSWSub<'a>( + |wrap pub(crate) fn LLVMBuildNSWSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildNUWSub<'a>( + |wrap pub(crate) fn LLVMBuildNUWSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildNSWMul<'a>( + |wrap pub(crate) fn LLVMBuildNSWMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildNUWMul<'a>( + |wrap pub(crate) fn LLVMBuildNUWMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildAnd<'a>( + |wrap pub(crate) fn LLVMBuildAnd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildOr<'a>( + |wrap pub(crate) fn LLVMBuildOr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildXor<'a>( + |wrap pub(crate) fn LLVMBuildXor<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) + |wrap pub(crate) fn LLVMBuildNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; - pub(crate) fn LLVMBuildFNeg<'a>( + |wrap pub(crate) fn LLVMBuildFNeg<'a>( B: &Builder<'a>, V: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildNot<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) + |wrap pub(crate) fn LLVMBuildNot<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; // Extra flags on arithmetic @@ -1479,27 +1557,27 @@ unsafe extern "C" { pub(crate) fn LLVMSetNSW(ArithInst: &Value, HasNSW: Bool); // Memory - pub(crate) fn LLVMBuildAlloca<'a>( + |wrap pub(crate) fn LLVMBuildAlloca<'a>( B: &Builder<'a>, Ty: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildArrayAlloca<'a>( + |wrap pub(crate) fn LLVMBuildArrayAlloca<'a>( B: &Builder<'a>, Ty: &'a Type, Val: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildLoad2<'a>( + |wrap pub(crate) fn LLVMBuildLoad2<'a>( B: &Builder<'a>, Ty: &'a Type, PointerVal: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildStore<'a>(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value; + |wrap pub(crate) fn LLVMBuildStore<'a>(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value; - pub(crate) fn LLVMBuildGEPWithNoWrapFlags<'a>( + |wrap pub(crate) fn LLVMBuildGEPWithNoWrapFlags<'a>( B: &Builder<'a>, Ty: &'a Type, Pointer: &'a Value, @@ -1510,85 +1588,85 @@ unsafe extern "C" { ) -> &'a Value; // Casts - pub(crate) fn LLVMBuildTrunc<'a>( + |wrap pub(crate) fn LLVMBuildTrunc<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildZExt<'a>( + |wrap pub(crate) fn LLVMBuildZExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildSExt<'a>( + |wrap pub(crate) fn LLVMBuildSExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFPToUI<'a>( + |wrap pub(crate) fn LLVMBuildFPToUI<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFPToSI<'a>( + |wrap pub(crate) fn LLVMBuildFPToSI<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildUIToFP<'a>( + |wrap pub(crate) fn LLVMBuildUIToFP<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildSIToFP<'a>( + |wrap pub(crate) fn LLVMBuildSIToFP<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFPTrunc<'a>( + |wrap pub(crate) fn LLVMBuildFPTrunc<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFPExt<'a>( + |wrap pub(crate) fn LLVMBuildFPExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildPtrToInt<'a>( + |wrap pub(crate) fn LLVMBuildPtrToInt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildIntToPtr<'a>( + |wrap pub(crate) fn LLVMBuildIntToPtr<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildBitCast<'a>( + |wrap pub(crate) fn LLVMBuildBitCast<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildPointerCast<'a>( + |wrap pub(crate) fn LLVMBuildPointerCast<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildIntCast2<'a>( + |wrap pub(crate) fn LLVMBuildIntCast2<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, @@ -1597,14 +1675,14 @@ unsafe extern "C" { ) -> &'a Value; // Comparisons - pub(crate) fn LLVMBuildICmp<'a>( + |wrap pub(crate) fn LLVMBuildICmp<'a>( B: &Builder<'a>, Op: c_uint, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildFCmp<'a>( + |wrap pub(crate) fn LLVMBuildFCmp<'a>( B: &Builder<'a>, Op: c_uint, LHS: &'a Value, @@ -1613,48 +1691,48 @@ unsafe extern "C" { ) -> &'a Value; // Miscellaneous instructions - pub(crate) fn LLVMBuildPhi<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) + |wrap pub(crate) fn LLVMBuildPhi<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; - pub(crate) fn LLVMBuildSelect<'a>( + |wrap pub(crate) fn LLVMBuildSelect<'a>( B: &Builder<'a>, If: &'a Value, Then: &'a Value, Else: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildVAArg<'a>( + |wrap pub(crate) fn LLVMBuildVAArg<'a>( B: &Builder<'a>, list: &'a Value, Ty: &'a Type, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildExtractElement<'a>( + |wrap pub(crate) fn LLVMBuildExtractElement<'a>( B: &Builder<'a>, VecVal: &'a Value, Index: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildInsertElement<'a>( + |wrap pub(crate) fn LLVMBuildInsertElement<'a>( B: &Builder<'a>, VecVal: &'a Value, EltVal: &'a Value, Index: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildShuffleVector<'a>( + |wrap pub(crate) fn LLVMBuildShuffleVector<'a>( B: &Builder<'a>, V1: &'a Value, V2: &'a Value, Mask: &'a Value, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildExtractValue<'a>( + |wrap pub(crate) fn LLVMBuildExtractValue<'a>( B: &Builder<'a>, AggVal: &'a Value, Index: c_uint, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildInsertValue<'a>( + |wrap pub(crate) fn LLVMBuildInsertValue<'a>( B: &Builder<'a>, AggVal: &'a Value, EltVal: &'a Value, @@ -1663,7 +1741,7 @@ unsafe extern "C" { ) -> &'a Value; // Atomic Operations - pub(crate) fn LLVMBuildAtomicCmpXchg<'a>( + |wrap pub(crate) fn LLVMBuildAtomicCmpXchg<'a>( B: &Builder<'a>, LHS: &'a Value, CMP: &'a Value, @@ -1675,7 +1753,7 @@ unsafe extern "C" { pub(crate) fn LLVMSetWeak(CmpXchgInst: &Value, IsWeak: Bool); - pub(crate) fn LLVMBuildAtomicRMW<'a>( + |wrap pub(crate) fn LLVMBuildAtomicRMW<'a>( B: &Builder<'a>, Op: AtomicRmwBinOp, LHS: &'a Value, @@ -1684,7 +1762,7 @@ unsafe extern "C" { SingleThreaded: Bool, ) -> &'a Value; - pub(crate) fn LLVMBuildFence<'a>( + |wrap pub(crate) fn LLVMBuildFence<'a>( B: &Builder<'a>, Order: AtomicOrdering, SingleThreaded: Bool, @@ -1694,7 +1772,7 @@ unsafe extern "C" { /// Writes a module to the specified path. Returns 0 on success. pub(crate) fn LLVMWriteBitcodeToFile(M: &Module, Path: *const c_char) -> c_int; - /// Creates a legacy pass manager -- only used for final codegen. + |wrap /// Creates a legacy pass manager -- only used for final codegen. pub(crate) fn LLVMCreatePassManager<'a>() -> &'a mut PassManager<'a>; pub(crate) fn LLVMAddAnalysisPasses<'a>(T: &'a TargetMachine, PM: &PassManager<'a>); @@ -1705,7 +1783,7 @@ unsafe extern "C" { pub(crate) fn LLVMIsMultithreaded() -> Bool; - pub(crate) fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; + |wrap pub(crate) fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; pub(crate) fn LLVMStructSetBody<'a>( StructTy: &'a Type, @@ -1714,13 +1792,13 @@ unsafe extern "C" { Packed: Bool, ); - pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; + |wrap pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; pub(crate) fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); pub(crate) fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; - pub(crate) fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; + |wrap pub(crate) fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; pub(crate) fn LLVMSetComdat(V: &Value, C: &Comdat); pub(crate) fn LLVMCreateOperandBundle( @@ -1731,7 +1809,7 @@ unsafe extern "C" { ) -> *mut OperandBundle<'_>; pub(crate) fn LLVMDisposeOperandBundle(Bundle: ptr::NonNull>); - pub(crate) fn LLVMBuildCallWithOperandBundles<'a>( + |wrap pub(crate) fn LLVMBuildCallWithOperandBundles<'a>( B: &Builder<'a>, Ty: &'a Type, Fn: &'a Value, @@ -1741,7 +1819,7 @@ unsafe extern "C" { NumBundles: c_uint, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildInvokeWithOperandBundles<'a>( + |wrap pub(crate) fn LLVMBuildInvokeWithOperandBundles<'a>( B: &Builder<'a>, Ty: &'a Type, Fn: &'a Value, @@ -1753,7 +1831,7 @@ unsafe extern "C" { NumBundles: c_uint, Name: *const c_char, ) -> &'a Value; - pub(crate) fn LLVMBuildCallBr<'a>( + |wrap pub(crate) fn LLVMBuildCallBr<'a>( B: &Builder<'a>, Ty: &'a Type, Fn: &'a Value, @@ -1780,30 +1858,30 @@ unsafe extern "C" { pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>); - pub(crate) fn LLVMDIBuilderCreateNameSpace<'ll>( - Builder: &DIBuilder<'ll>, + |wrap pub(crate) fn LLVMDIBuilderCreateNameSpace<'ll>( + Builder_: &DIBuilder<'ll>, ParentScope: Option<&'ll Metadata>, Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, ExportSymbols: llvm::Bool, ) -> &'ll Metadata; - pub(crate) fn LLVMDIBuilderCreateLexicalBlock<'ll>( - Builder: &DIBuilder<'ll>, + |wrap pub(crate) fn LLVMDIBuilderCreateLexicalBlock<'ll>( + Builder_: &DIBuilder<'ll>, Scope: &'ll Metadata, File: &'ll Metadata, Line: c_uint, Column: c_uint, ) -> &'ll Metadata; - pub(crate) fn LLVMDIBuilderCreateLexicalBlockFile<'ll>( - Builder: &DIBuilder<'ll>, + |wrap pub(crate) fn LLVMDIBuilderCreateLexicalBlockFile<'ll>( + Builder_: &DIBuilder<'ll>, Scope: &'ll Metadata, File: &'ll Metadata, Discriminator: c_uint, // (optional "DWARF path discriminator"; default is 0) ) -> &'ll Metadata; - pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( + |wrap pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( Ctx: &'ll Context, Line: c_uint, Column: c_uint, @@ -1818,7 +1896,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustDisableSystemDialogsOnCrash(); // Create and destroy contexts. - pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; + |wrap pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; /// See llvm::LLVMTypeKind::getTypeID. pub(crate) fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind; @@ -1844,13 +1922,13 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool); // Operations on global variables - pub(crate) fn LLVMRustGetOrInsertGlobal<'a>( + |wrap pub(crate) fn LLVMRustGetOrInsertGlobal<'a>( M: &'a Module, Name: *const c_char, NameLen: size_t, T: &'a Type, ) -> &'a Value; - pub(crate) fn LLVMRustInsertPrivateGlobal<'a>(M: &'a Module, T: &'a Type) -> &'a Value; + |wrap pub(crate) fn LLVMRustInsertPrivateGlobal<'a>(M: &'a Module, T: &'a Type) -> &'a Value; pub(crate) fn LLVMRustGetNamedValue( M: &Module, Name: *const c_char, @@ -1858,21 +1936,21 @@ unsafe extern "C" { ) -> Option<&Value>; // Operations on attributes - pub(crate) fn LLVMRustCreateAttrNoValue(C: &Context, attr: AttributeKind) -> &Attribute; - pub(crate) fn LLVMRustCreateAlignmentAttr(C: &Context, bytes: u64) -> &Attribute; - pub(crate) fn LLVMRustCreateDereferenceableAttr(C: &Context, bytes: u64) -> &Attribute; - pub(crate) fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; - pub(crate) fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub(crate) fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub(crate) fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; - pub(crate) fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; - pub(crate) fn LLVMRustCreateAllocSizeAttr(C: &Context, size_arg: u32) -> &Attribute; - pub(crate) fn LLVMRustCreateAllocKindAttr(C: &Context, size_arg: u64) -> &Attribute; - pub(crate) fn LLVMRustCreateMemoryEffectsAttr( + |wrap pub(crate) fn LLVMRustCreateAttrNoValue(C: &Context, attr: AttributeKind) -> &Attribute; + |wrap pub(crate) fn LLVMRustCreateAlignmentAttr(C: &Context, bytes: u64) -> &Attribute; + |wrap pub(crate) fn LLVMRustCreateDereferenceableAttr(C: &Context, bytes: u64) -> &Attribute; + |wrap pub(crate) fn LLVMRustCreateDereferenceableOrNullAttr(C: &Context, bytes: u64) -> &Attribute; + |wrap pub(crate) fn LLVMRustCreateByValAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + |wrap pub(crate) fn LLVMRustCreateStructRetAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + |wrap pub(crate) fn LLVMRustCreateElementTypeAttr<'a>(C: &'a Context, ty: &'a Type) -> &'a Attribute; + |wrap pub(crate) fn LLVMRustCreateUWTableAttr(C: &Context, async_: bool) -> &Attribute; + |wrap pub(crate) fn LLVMRustCreateAllocSizeAttr(C: &Context, size_arg: u32) -> &Attribute; + |wrap pub(crate) fn LLVMRustCreateAllocKindAttr(C: &Context, size_arg: u64) -> &Attribute; + |wrap pub(crate) fn LLVMRustCreateMemoryEffectsAttr( C: &Context, effects: MemoryEffects, ) -> &Attribute; - pub(crate) fn LLVMRustCreateRangeAttribute( + |wrap pub(crate) fn LLVMRustCreateRangeAttribute( C: &Context, num_bits: c_uint, lower_words: *const u64, @@ -1880,7 +1958,7 @@ unsafe extern "C" { ) -> &Attribute; // Operations on functions - pub(crate) fn LLVMRustGetOrInsertFunction<'a>( + |wrap pub(crate) fn LLVMRustGetOrInsertFunction<'a>( M: &'a Module, Name: *const c_char, NameLen: size_t, @@ -1906,7 +1984,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetAllowReassoc(Instr: &Value); // Miscellaneous instructions - pub(crate) fn LLVMRustBuildMemCpy<'a>( + |wrap pub(crate) fn LLVMRustBuildMemCpy<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -1915,7 +1993,7 @@ unsafe extern "C" { Size: &'a Value, IsVolatile: bool, ) -> &'a Value; - pub(crate) fn LLVMRustBuildMemMove<'a>( + |wrap pub(crate) fn LLVMRustBuildMemMove<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -1924,7 +2002,7 @@ unsafe extern "C" { Size: &'a Value, IsVolatile: bool, ) -> &'a Value; - pub(crate) fn LLVMRustBuildMemSet<'a>( + |wrap pub(crate) fn LLVMRustBuildMemSet<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -1933,55 +2011,55 @@ unsafe extern "C" { IsVolatile: bool, ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceFAdd<'a>( + |wrap pub(crate) fn LLVMRustBuildVectorReduceFAdd<'a>( B: &Builder<'a>, Acc: &'a Value, Src: &'a Value, ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceFMul<'a>( + |wrap pub(crate) fn LLVMRustBuildVectorReduceFMul<'a>( B: &Builder<'a>, Acc: &'a Value, Src: &'a Value, ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceAdd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceMul<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceAnd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceOr<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceXor<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceMin<'a>( + |wrap pub(crate) fn LLVMRustBuildVectorReduceAdd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + |wrap pub(crate) fn LLVMRustBuildVectorReduceMul<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + |wrap pub(crate) fn LLVMRustBuildVectorReduceAnd<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + |wrap pub(crate) fn LLVMRustBuildVectorReduceOr<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + |wrap pub(crate) fn LLVMRustBuildVectorReduceXor<'a>(B: &Builder<'a>, Src: &'a Value) -> &'a Value; + |wrap pub(crate) fn LLVMRustBuildVectorReduceMin<'a>( B: &Builder<'a>, Src: &'a Value, IsSigned: bool, ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceMax<'a>( + |wrap pub(crate) fn LLVMRustBuildVectorReduceMax<'a>( B: &Builder<'a>, Src: &'a Value, IsSigned: bool, ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceFMin<'a>( + |wrap pub(crate) fn LLVMRustBuildVectorReduceFMin<'a>( B: &Builder<'a>, Src: &'a Value, IsNaN: bool, ) -> &'a Value; - pub(crate) fn LLVMRustBuildVectorReduceFMax<'a>( + |wrap pub(crate) fn LLVMRustBuildVectorReduceFMax<'a>( B: &Builder<'a>, Src: &'a Value, IsNaN: bool, ) -> &'a Value; - pub(crate) fn LLVMRustBuildMinNum<'a>( + |wrap pub(crate) fn LLVMRustBuildMinNum<'a>( B: &Builder<'a>, LHS: &'a Value, - LHS: &'a Value, + RHS: &'a Value, ) -> &'a Value; - pub(crate) fn LLVMRustBuildMaxNum<'a>( + |wrap pub(crate) fn LLVMRustBuildMaxNum<'a>( B: &Builder<'a>, LHS: &'a Value, - LHS: &'a Value, + RHS: &'a Value, ) -> &'a Value; // Atomic Operations - pub(crate) fn LLVMRustBuildAtomicLoad<'a>( + |wrap pub(crate) fn LLVMRustBuildAtomicLoad<'a>( B: &Builder<'a>, ElementType: &'a Type, PointerVal: &'a Value, @@ -1989,7 +2067,7 @@ unsafe extern "C" { Order: AtomicOrdering, ) -> &'a Value; - pub(crate) fn LLVMRustBuildAtomicStore<'a>( + |wrap pub(crate) fn LLVMRustBuildAtomicStore<'a>( B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value, @@ -2043,7 +2121,7 @@ unsafe extern "C" { BufferOut: &RustString, ); - pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar( + |wrap pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar( F: &Value, FuncName: *const c_char, FuncNameLen: size_t, @@ -2083,8 +2161,8 @@ unsafe extern "C" { ValueLen: size_t, ); - pub(crate) fn LLVMRustDIBuilderCreateCompileUnit<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateCompileUnit<'a>( + Builder_: &DIBuilder<'a>, Lang: c_uint, File: &'a DIFile, Producer: *const c_char, @@ -2100,8 +2178,8 @@ unsafe extern "C" { DebugNameTableKind: DebugNameTableKind, ) -> &'a DIDescriptor; - pub(crate) fn LLVMRustDIBuilderCreateFile<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateFile<'a>( + Builder_: &DIBuilder<'a>, Filename: *const c_char, FilenameLen: size_t, Directory: *const c_char, @@ -2113,13 +2191,13 @@ unsafe extern "C" { SourceLen: size_t, ) -> &'a DIFile; - pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>( + Builder_: &DIBuilder<'a>, ParameterTypes: &'a DIArray, ) -> &'a DICompositeType; - pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( + Builder_: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, NameLen: size_t, @@ -2136,8 +2214,8 @@ unsafe extern "C" { Decl: Option<&'a DIDescriptor>, ) -> &'a DISubprogram; - pub(crate) fn LLVMRustDIBuilderCreateMethod<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateMethod<'a>( + Builder_: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, NameLen: size_t, @@ -2151,16 +2229,16 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; - pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>( + Builder_: &DIBuilder<'a>, Name: *const c_char, NameLen: size_t, SizeInBits: u64, Encoding: c_uint, ) -> &'a DIBasicType; - pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( + Builder_: &DIBuilder<'a>, Type: &'a DIBasicType, Name: *const c_char, NameLen: size_t, @@ -2169,8 +2247,8 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>( + Builder_: &DIBuilder<'a>, PointeeTy: &'a DIType, SizeInBits: u64, AlignInBits: u32, @@ -2179,8 +2257,8 @@ unsafe extern "C" { NameLen: size_t, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( + Builder_: &DIBuilder<'a>, Scope: Option<&'a DIDescriptor>, Name: *const c_char, NameLen: size_t, @@ -2197,8 +2275,8 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DICompositeType; - pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( + Builder_: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, NameLen: size_t, @@ -2211,8 +2289,8 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( + Builder_: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, NameLen: size_t, @@ -2226,8 +2304,8 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateStaticMemberType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateStaticMemberType<'a>( + Builder_: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, NameLen: size_t, @@ -2239,14 +2317,14 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( + Builder_: &DIBuilder<'a>, Tag: c_uint, Type: &'a DIType, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStaticVariable<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateStaticVariable<'a>( + Builder_: &DIBuilder<'a>, Context: Option<&'a DIScope>, Name: *const c_char, NameLen: size_t, @@ -2261,8 +2339,8 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIGlobalVariableExpression; - pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>( + Builder_: &DIBuilder<'a>, Tag: c_uint, Scope: &'a DIDescriptor, Name: *const c_char, @@ -2276,28 +2354,28 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>( + Builder_: &DIBuilder<'a>, Size: u64, AlignInBits: u32, Ty: &'a DIType, Subscripts: &'a DIArray, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( + Builder_: &DIBuilder<'a>, Lo: i64, Count: i64, ) -> &'a DISubrange; - pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( + Builder_: &DIBuilder<'a>, Ptr: *const Option<&'a DIDescriptor>, Count: c_uint, ) -> &'a DIArray; pub(crate) fn LLVMRustDIBuilderInsertDeclareAtEnd<'a>( - Builder: &DIBuilder<'a>, + Builder_: &DIBuilder<'a>, Val: &'a Value, VarInfo: &'a DIVariable, AddrOps: *const u64, @@ -2306,8 +2384,8 @@ unsafe extern "C" { InsertAtEnd: &'a BasicBlock, ); - pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( + Builder_: &DIBuilder<'a>, Name: *const c_char, NameLen: size_t, Value: *const u64, @@ -2315,8 +2393,8 @@ unsafe extern "C" { IsUnsigned: bool, ) -> &'a DIEnumerator; - pub(crate) fn LLVMRustDIBuilderCreateEnumerationType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateEnumerationType<'a>( + Builder_: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, NameLen: size_t, @@ -2329,8 +2407,8 @@ unsafe extern "C" { IsScoped: bool, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>( + Builder_: &DIBuilder<'a>, Scope: Option<&'a DIScope>, Name: *const c_char, NameLen: size_t, @@ -2345,8 +2423,8 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( + Builder_: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, NameLen: size_t, @@ -2361,8 +2439,8 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateTemplateTypeParameter<'a>( - Builder: &DIBuilder<'a>, + |wrap pub(crate) fn LLVMRustDIBuilderCreateTemplateTypeParameter<'a>( + Builder_: &DIBuilder<'a>, Scope: Option<&'a DIScope>, Name: *const c_char, NameLen: size_t, @@ -2370,7 +2448,7 @@ unsafe extern "C" { ) -> &'a DITemplateTypeParameter; pub(crate) fn LLVMRustDICompositeTypeReplaceArrays<'a>( - Builder: &DIBuilder<'a>, + Builder_: &DIBuilder<'a>, CompositeType: &'a DIType, Elements: Option<&'a DIArray>, Params: Option<&'a DIArray>, @@ -2488,7 +2566,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t); pub(crate) fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>; - pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; + |wrap pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; pub(crate) fn LLVMRustArchiveIteratorNext<'a>( AIR: &ArchiveIterator<'a>, ) -> Option<&'a mut ArchiveChild<'a>>; @@ -2522,7 +2600,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustWriteDiagnosticInfoToString(DI: &DiagnosticInfo, s: &RustString); pub(crate) fn LLVMRustGetDiagInfoKind(DI: &DiagnosticInfo) -> DiagnosticKind; - pub(crate) fn LLVMRustGetSMDiagnostic<'a>( + |wrap pub(crate) fn LLVMRustGetSMDiagnostic<'a>( DI: &'a DiagnosticInfo, cookie_out: &mut u64, ) -> &'a SMDiagnostic; @@ -2545,7 +2623,7 @@ unsafe extern "C" { Kind: ArchiveKind, isEC: bool, ) -> LLVMRustResult; - pub(crate) fn LLVMRustArchiveMemberNew<'a>( + |wrap pub(crate) fn LLVMRustArchiveMemberNew<'a>( Filename: *const c_char, Name: *const c_char, Child: Option<&ArchiveChild<'a>>, @@ -2559,14 +2637,14 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetModulePICLevel(M: &Module); pub(crate) fn LLVMRustSetModulePIELevel(M: &Module); pub(crate) fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); - pub(crate) fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; + |wrap pub(crate) fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; pub(crate) fn LLVMRustModuleBufferPtr(p: &ModuleBuffer) -> *const u8; pub(crate) fn LLVMRustModuleBufferLen(p: &ModuleBuffer) -> usize; pub(crate) fn LLVMRustModuleBufferFree(p: &'static mut ModuleBuffer); pub(crate) fn LLVMRustModuleCost(M: &Module) -> u64; pub(crate) fn LLVMRustModuleInstructionStats(M: &Module, Str: &RustString); - pub(crate) fn LLVMRustThinLTOBufferCreate( + |wrap pub(crate) fn LLVMRustThinLTOBufferCreate( M: &Module, is_thin: bool, emit_summary: bool, @@ -2609,7 +2687,7 @@ unsafe extern "C" { out_len: &mut usize, ) -> *const u8; - pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; + |wrap pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; pub(crate) fn LLVMRustLinkerAdd( linker: &Linker<'_>, bytecode: *const c_char, @@ -2663,3 +2741,4 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetNoSanitizeAddress(Global: &Value); pub(crate) fn LLVMRustSetNoSanitizeHWAddress(Global: &Value); } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 29223d55f1a67..d8d7a7994bef5 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -339,6 +339,7 @@ fn register_builtins(store: &mut LintStore) { "improper_c_boundaries", IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, + IMPROPER_C_VAR_DEFINITIONS, IMPROPER_CTYPE_DEFINITIONS, IMPROPER_CTYPES ); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index e716e23e4e635..bf4ad40cc141e 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -12,8 +12,8 @@ use {rustc_ast as ast, rustc_hir as hir}; 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, + IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, IMPROPER_C_VAR_DEFINITIONS, + IMPROPER_CTYPE_DEFINITIONS, IMPROPER_CTYPES, ImproperCTypesLint, }; use crate::lints::{ diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index f2211d00ce1b2..e0990b58138a5 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -99,11 +99,13 @@ 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, + /// `no_mangle`/`export_name` static variables, assumed to be used from across an FFI boundary + ExportedStatic, /// `extern "C"` function pointers -> IMPROPER_C_CALLBACKS, Callback, /// `repr(C)` structs/enums/unions -> IMPROPER_CTYPE_DEFINITIONS + #[allow(unused)] AdtDef, } @@ -443,6 +445,8 @@ enum CTypesVisitorState { None = CTypesVisitorStateFlags::NO_FLAGS, // uses bitflags from CTypesVisitorStateFlags StaticTy = CTypesVisitorStateFlags::STATIC, + ExportedStaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::FN_DEFINED, + #[allow(unused)] AdtDef = CTypesVisitorStateFlags::THEORETICAL, ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_DEFINED, ReturnTyInDefinition = CTypesVisitorStateFlags::FUNC @@ -512,7 +516,11 @@ impl CTypesVisitorState { 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 + if ((self as u8) & THEORETICAL) != 0 { + true + } else { + ((self as u8) & FN_DEFINED) != 0 && ((self as u8) & STATIC) == 0 + } } /// whether the value for that type might come from the non-rust side of a FFI boundary @@ -523,6 +531,8 @@ impl CTypesVisitorState { // so let's not make hasty judgement false } else if self.is_in_static() { + // FIXME: this is evidently untrue for non-mut static variables + // (assuming the cross-FFI code respects this) true } else if self.is_in_defined_function() { // function definitions are assumed to be maybe-not-rust-caller, rust-callee @@ -1465,6 +1475,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } + #[allow(unused)] fn check_for_adtdef(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { use FfiResult::*; let ty = erase_and_maybe_normalize(self.cx, ty); @@ -1673,16 +1684,12 @@ impl ImproperCTypesLint { 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); + let 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 @@ -1693,6 +1700,19 @@ impl ImproperCTypesLint { self.process_ffi_result(cx, span, ffi_res, CItemKind::ImportedExtern); } + /// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type + fn check_exported_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::ExportedStaticTy, ty); + self.process_ffi_result(cx, span, ffi_res, CItemKind::ExportedStatic); + } + /// Check if a function's argument types and result type are "ffi-safe". fn check_foreign_fn<'tcx>( &mut self, @@ -1801,12 +1821,14 @@ impl ImproperCTypesLint { let lint = match fn_mode { CItemKind::ImportedExtern => IMPROPER_CTYPES, CItemKind::ExportedFunction => IMPROPER_C_FN_DEFINITIONS, + CItemKind::ExportedStatic => IMPROPER_C_VAR_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::ExportedStatic => "foreign-code-reachable static", CItemKind::Callback => "`extern` callback", CItemKind::AdtDef => "`repr(C)` type", }; @@ -1878,6 +1900,13 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { ty, cx.tcx.type_of(item.owner_id).instantiate_identity(), ); + + if matches!(item.kind, hir::ItemKind::Static(..)) + && (cx.tcx.has_attr(item.owner_id, sym::no_mangle) + || cx.tcx.has_attr(item.owner_id, sym::export_name)) + { + self.check_exported_static(cx, item.owner_id, ty.span); + } } // See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks hir::ItemKind::Fn { .. } => {} @@ -2044,6 +2073,36 @@ declare_lint! { "proper use of libc types in foreign item definitions" } +declare_lint! { + /// The `improper_c_var_definitions` lint detects incorrect use of + /// [`no_mangle`] and [`export_name`] static variable definitions. + /// (in other words, static variables accessible by name by foreign code) + /// + /// [`no_mangle`]: https://doc.rust-lang.org/stable/reference/abi.html#the-no_mangle-attribute + /// [`export_name`]: https://doc.rust-lang.org/stable/reference/abi.html#the-export_name-attribute + /// + /// ### Example + /// + /// ```rust + /// # #![unsafe(no_mangle)] + /// static mut PLUGIN_ABI_MIN_VERSION: &'static str = "0.0.5"; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The compiler has several checks to verify that types used in + /// static variables exposed to foreign code 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_C_VAR_DEFINITIONS, + Warn, + "proper use of libc types in foreign-reachable static variable definitions" +} + declare_lint! { /// The `improper_c_callbacks` lint detects incorrect use of /// [`extern` function] pointers. @@ -2157,6 +2216,7 @@ declare_lint! { declare_lint_pass!(ImproperCTypesLint => [ IMPROPER_CTYPES, IMPROPER_C_FN_DEFINITIONS, + IMPROPER_C_VAR_DEFINITIONS, IMPROPER_C_CALLBACKS, IMPROPER_CTYPE_DEFINITIONS, USES_POWER_ALIGNMENT, diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs index 5456bc92d264c..5f50e88e564e8 100644 --- a/tests/ui/layout/reprc-power-alignment.rs +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -150,7 +150,7 @@ pub struct I { e: f64, } #[repr(C)] -pub struct J { //~ WARNING `repr(C)` type uses type `I` +pub struct J { a: u8, b: I, } diff --git a/tests/ui/lint/improper_ctypes/lint-113436-1.rs b/tests/ui/lint/improper_ctypes/lint-113436-1.rs index 11dae42fa538b..44cf2fcb21009 100644 --- a/tests/ui/lint/improper_ctypes/lint-113436-1.rs +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.rs @@ -13,7 +13,7 @@ extern "C" fn foo(x: Foo) -> Foo { struct NotSafe(u32); #[repr(C)] -pub struct Bar { //~ ERROR `repr(C)` type uses type `NotSafe` +pub struct Bar { a: u8, b: (), c: NotSafe, diff --git a/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs b/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs index 0b7e659c7b75a..059af87a72234 100644 --- a/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs +++ b/tests/ui/statics/nested-allocations-dont-inherit-codegen-attrs.rs @@ -1,5 +1,7 @@ //@ build-pass +#![allow(improper_c_var_definitions)] + // Make sure that the nested static allocation for `FOO` doesn't inherit `no_mangle`. #[no_mangle] pub static mut FOO: &mut [i32] = &mut [42]; From 8f4ba715f787f0a940ee4f0e36c3fcf9ccab64d4 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Tue, 6 May 2025 23:22:26 +0200 Subject: [PATCH 14/15] lint ImproperCTypes: rm improper_ctype_definitions [...] for now, let's fully remove this lint. it might be reintroduced later as a way to make the lints ignore the inside of some ADT definitions --- compiler/rustc_lint/src/lib.rs | 1 - compiler/rustc_lint/src/types.rs | 4 +- .../rustc_lint/src/types/improper_ctypes.rs | 141 +------------ library/panic_unwind/src/gcc.rs | 2 - library/proc_macro/src/bridge/client.rs | 2 - .../crates/ide-db/src/generated/lints.rs | 8 +- tests/auxiliary/minicore.rs | 2 +- tests/ui/abi/abi-sysv64-arg-passing.rs | 3 +- tests/ui/abi/arm-unadjusted-intrinsic.rs | 1 - tests/ui/abi/compatibility.rs | 2 +- tests/ui/abi/extern/extern-pass-empty.rs | 2 +- .../backtrace/auxiliary/dylib-dep-helper.rs | 2 +- .../defaults/repr-c-issue-82792.rs | 1 - .../consts/extra-const-ub/detect-extra-ub.rs | 1 - .../detect-extra-ub.with_flag.stderr | 4 +- tests/ui/issues/issue-38763.rs | 1 - tests/ui/layout/reprc-power-alignment.rs | 1 - tests/ui/layout/reprc-power-alignment.stderr | 54 ++--- .../allow-phantomdata-in-ffi.rs | 2 +- .../improper_ctypes/allow_improper_ctypes.rs | 13 +- .../allow_improper_ctypes.stderr | 63 ++---- .../auxiliary/outer_crate_types.rs | 4 +- tests/ui/lint/improper_ctypes/ctypes.rs | 9 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 115 +++-------- .../ui/lint/improper_ctypes/lint-113436-1.rs | 2 +- .../lint/improper_ctypes/lint-113436-1.stderr | 27 +-- tests/ui/lint/improper_ctypes/lint-73249-2.rs | 2 +- .../lint/improper_ctypes/lint-73249-2.stderr | 2 +- tests/ui/lint/improper_ctypes/lint-73249-3.rs | 4 +- .../lint/improper_ctypes/lint-73249-3.stderr | 19 +- tests/ui/lint/improper_ctypes/lint-73249-5.rs | 2 +- .../lint/improper_ctypes/lint-73249-5.stderr | 2 +- tests/ui/lint/improper_ctypes/lint-94223.rs | 2 +- tests/ui/lint/improper_ctypes/lint-fn.rs | 4 +- tests/ui/lint/improper_ctypes/lint-fn.stderr | 23 +-- .../improper_ctypes/lint-transparent-help.rs | 4 +- .../lint-transparent-help.stderr | 33 +-- .../lint/improper_ctypes/lint-tykind-fuzz.rs | 64 +----- .../improper_ctypes/lint-tykind-fuzz.stderr | 193 ++++-------------- .../lint/improper_ctypes/lint_uninhabited.rs | 2 +- .../improper_ctypes/lint_uninhabited.stderr | 2 +- .../lint/improper_ctypes/mustpass-113436.rs | 2 +- .../improper_ctypes/repr-rust-is-undefined.rs | 3 +- .../repr-rust-is-undefined.stderr | 36 +--- tests/ui/lint/wasm_c_abi_transition.rs | 1 - tests/ui/lint/wasm_c_abi_transition.stderr | 8 +- .../offset-of/offset-of-slice-normalized.rs | 1 - tests/ui/offset-of/offset-of-slice.rs | 1 - tests/ui/structs-enums/align-struct.rs | 1 - .../enum-non-c-like-repr-c-and-int.rs | 2 - .../structs-enums/enum-non-c-like-repr-c.rs | 2 - .../structs-enums/enum-non-c-like-repr-int.rs | 2 - .../abstraction/const_generic_fn.rs | 1 - .../arrays/should_have_correct_length.rs | 1 - .../arrays/should_inherit_alignment.rs | 1 - .../transmutability/references/u8-to-unit.rs | 1 - .../references/unit-to-itself.rs | 1 - ...ould_accept_if_dst_has_safety_invariant.rs | 1 - ...ould_accept_if_src_has_safety_invariant.rs | 1 - ...ould_accept_if_src_has_safety_invariant.rs | 1 - .../structs/repr/should_handle_all.rs | 1 - .../unions/repr/should_handle_align.rs | 1 - .../unions/repr/should_handle_packed.rs | 1 - 63 files changed, 178 insertions(+), 717 deletions(-) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index d8d7a7994bef5..1fed7058685a0 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -340,7 +340,6 @@ fn register_builtins(store: &mut LintStore) { IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, IMPROPER_C_VAR_DEFINITIONS, - IMPROPER_CTYPE_DEFINITIONS, IMPROPER_CTYPES ); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index bf4ad40cc141e..0bab87d10fdd4 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -12,8 +12,8 @@ use {rustc_ast as ast, rustc_hir as hir}; mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations pub(crate) use improper_ctypes::{ - IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, IMPROPER_C_VAR_DEFINITIONS, - IMPROPER_CTYPE_DEFINITIONS, IMPROPER_CTYPES, ImproperCTypesLint, + IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS, IMPROPER_C_VAR_DEFINITIONS, IMPROPER_CTYPES, + ImproperCTypesLint, }; use crate::lints::{ diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index e0990b58138a5..312206e75e6ad 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -104,9 +104,6 @@ enum CItemKind { ExportedStatic, /// `extern "C"` function pointers -> IMPROPER_C_CALLBACKS, Callback, - /// `repr(C)` structs/enums/unions -> IMPROPER_CTYPE_DEFINITIONS - #[allow(unused)] - AdtDef, } #[derive(Clone, Debug)] @@ -446,8 +443,6 @@ enum CTypesVisitorState { // uses bitflags from CTypesVisitorStateFlags StaticTy = CTypesVisitorStateFlags::STATIC, ExportedStaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::FN_DEFINED, - #[allow(unused)] - AdtDef = CTypesVisitorStateFlags::THEORETICAL, ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_DEFINED, ReturnTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_RETURN @@ -507,11 +502,6 @@ impl CTypesVisitorState { ((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::*; @@ -526,11 +516,7 @@ impl CTypesVisitorState { /// 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() { + if self.is_in_static() { // FIXME: this is evidently untrue for non-mut static variables // (assuming the cross-FFI code respects this) true @@ -614,9 +600,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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(..)),)) + if state.is_in_function_return() + && matches!(outer_ty.map(|ty| ty.kind()), None | Some(ty::FnPtr(..))) { FfiResult::FfiSafe } else { @@ -814,7 +799,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { &mut self, state: CTypesVisitorState, ty: Ty<'tcx>, - def: ty::AdtDef<'tcx>, + def: AdtDef<'tcx>, variant: &ty::VariantDef, args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { @@ -968,9 +953,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn visit_struct_or_union( &mut self, state: CTypesVisitorState, - outer_ty: Option>, ty: Ty<'tcx>, - def: ty::AdtDef<'tcx>, + def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { debug_assert!(matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)); @@ -1031,12 +1015,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // 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, + // 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) + ffires.with_overrides(Some(ty)) } fn visit_enum( @@ -1044,7 +1025,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { state: CTypesVisitorState, outer_ty: Option>, ty: Ty<'tcx>, - def: ty::AdtDef<'tcx>, + def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { debug_assert!(matches!(def.adt_kind(), AdtKind::Enum)); @@ -1131,12 +1112,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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, + // 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) + ffires.with_overrides(Some(ty)) } } @@ -1185,7 +1164,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { { return self.visit_cstr(outer_ty, ty); } - self.visit_struct_or_union(state, outer_ty, ty, def, args) + self.visit_struct_or_union(state, ty, def, args) } AdtKind::Enum => self.visit_enum(state, outer_ty, ty, def, args), } @@ -1475,71 +1454,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - #[allow(unused)] - 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"); @@ -1822,7 +1736,6 @@ impl ImproperCTypesLint { CItemKind::ImportedExtern => IMPROPER_CTYPES, CItemKind::ExportedFunction => IMPROPER_C_FN_DEFINITIONS, CItemKind::ExportedStatic => IMPROPER_C_VAR_DEFINITIONS, - CItemKind::AdtDef => IMPROPER_CTYPE_DEFINITIONS, CItemKind::Callback => IMPROPER_C_CALLBACKS, }; let desc = match fn_mode { @@ -1830,7 +1743,6 @@ impl ImproperCTypesLint { CItemKind::ExportedFunction => "`extern` fn", CItemKind::ExportedStatic => "foreign-code-reachable static", 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() @@ -1860,9 +1772,6 @@ impl ImproperCTypesLint { /// 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>) { @@ -2131,33 +2040,6 @@ declare_lint! { "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. @@ -2218,6 +2100,5 @@ declare_lint_pass!(ImproperCTypesLint => [ IMPROPER_C_FN_DEFINITIONS, IMPROPER_C_VAR_DEFINITIONS, IMPROPER_C_CALLBACKS, - IMPROPER_CTYPE_DEFINITIONS, USES_POWER_ALIGNMENT, ]); diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index b385e4e5e574f..5f95870069dc5 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -52,8 +52,6 @@ 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/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index b225df6e39c61..e7d547966a5d5 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -358,8 +358,6 @@ 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/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index a89dd508e9385..5c52529b8fa94 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 @@ -461,15 +461,15 @@ pub const DEFAULT_LINTS: &[Lint] = &[ deny_since: None, }, Lint { - label: "improper_ctypes", - description: r##"proper use of libc types in foreign modules"##, + label: "improper_c_var_definitions", + description: r##"proper use of libc types when making static variables foreign-code-accessible"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, }, Lint { - label: "improper_ctype_definitions", - description: r##"proper use of libc types when defining foreign-code-compatible structs"##, + label: "improper_ctypes", + description: r##"proper use of libc types in foreign modules"##, default_severity: Severity::Warning, warn_since: None, deny_since: None, diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 373c76f4ca3e6..b89434053157a 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -27,7 +27,7 @@ asm_experimental_arch, unboxed_closures )] -#![allow(unused, improper_ctype_definitions, internal_features)] +#![allow(unused, 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 cfc4c7200b3cc..c18752418a1d6 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, improper_ctype_definitions)] +#[allow(improper_ctypes)] #[cfg(target_arch = "x86_64")] mod tests { @@ -72,7 +72,6 @@ mod tests { } #[repr(C)] - #[allow(improper_ctype_definitions)] pub struct Empty; #[repr(C)] diff --git a/tests/ui/abi/arm-unadjusted-intrinsic.rs b/tests/ui/abi/arm-unadjusted-intrinsic.rs index 150c1626ab348..dcf0d9f39f673 100644 --- a/tests/ui/abi/arm-unadjusted-intrinsic.rs +++ b/tests/ui/abi/arm-unadjusted-intrinsic.rs @@ -25,7 +25,6 @@ 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 c8a9d6727f21a..5bd74a1a89673 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -63,7 +63,7 @@ #![feature(unsized_fn_params, transparent_unions)] #![no_core] #![allow(unused, internal_features)] -#![allow(improper_ctype_definitions, improper_c_fn_definitions, improper_c_callbacks)] +#![allow(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 f2cb894c3cd35..f38f76166bf27 100644 --- a/tests/ui/abi/extern/extern-pass-empty.rs +++ b/tests/ui/abi/extern/extern-pass-empty.rs @@ -1,5 +1,5 @@ //@ run-pass -#![allow(improper_ctypes, improper_ctype_definitions)] +#![allow(improper_ctypes)] // FIXME: this test is inherently not FFI-safe. // Test a foreign function that accepts empty struct. diff --git a/tests/ui/backtrace/auxiliary/dylib-dep-helper.rs b/tests/ui/backtrace/auxiliary/dylib-dep-helper.rs index 75f15574522b1..b553aaa84bc33 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_ctype_definitions, improper_c_fn_definitions)] +#![allow(improper_c_fn_definitions)] type Pos = (&'static str, u32); 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 d0e5c9d47e740..c23187598bceb 100644 --- a/tests/ui/const-generics/defaults/repr-c-issue-82792.rs +++ b/tests/ui/const-generics/defaults/repr-c-issue-82792.rs @@ -3,7 +3,6 @@ //@ run-pass #[repr(C)] -#[allow(improper_ctype_definitions)] pub struct Loaf { head: [T; N], slice: [T], 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 8bf320b344ec7..c3bd8301d5ce4 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -74,7 +74,6 @@ 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 a48fbb2f775bb..ea3b0e70b8285 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:84:16 + --> $DIR/detect-extra-ub.rs:83: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:99:16 + --> $DIR/detect-extra-ub.rs:98:16 | LL | let _val = &*slice; | ^^^^^^^ constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object diff --git a/tests/ui/issues/issue-38763.rs b/tests/ui/issues/issue-38763.rs index 3a0b177edde8d..328563945f447 100644 --- a/tests/ui/issues/issue-38763.rs +++ b/tests/ui/issues/issue-38763.rs @@ -2,7 +2,6 @@ //@ needs-threads #[repr(C)] -#[allow(improper_ctype_definitions)] pub struct Foo(i128); #[no_mangle] diff --git a/tests/ui/layout/reprc-power-alignment.rs b/tests/ui/layout/reprc-power-alignment.rs index 5f50e88e564e8..f144094d43fbc 100644 --- a/tests/ui/layout/reprc-power-alignment.rs +++ b/tests/ui/layout/reprc-power-alignment.rs @@ -10,7 +10,6 @@ extern crate minicore; use minicore::*; #[warn(uses_power_alignment)] -#[warn(improper_ctype_definitions)] #[repr(C)] pub struct Floats { diff --git a/tests/ui/layout/reprc-power-alignment.stderr b/tests/ui/layout/reprc-power-alignment.stderr index 2917705e50363..18664e4d655d3 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:19:5 + --> $DIR/reprc-power-alignment.rs:18: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:46:5 + --> $DIR/reprc-power-alignment.rs:45:5 | LL | b: f64, | ^^^^^^ @@ -19,112 +19,94 @@ 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:53:5 + --> $DIR/reprc-power-alignment.rs:52: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:59:5 + --> $DIR/reprc-power-alignment.rs:58: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:66:5 + --> $DIR/reprc-power-alignment.rs:65: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:67:5 + --> $DIR/reprc-power-alignment.rs:66: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:73:5 + --> $DIR/reprc-power-alignment.rs:72: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:78: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:80:5 + --> $DIR/reprc-power-alignment.rs:79: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:86:5 + --> $DIR/reprc-power-alignment.rs:85: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:93:5 + --> $DIR/reprc-power-alignment.rs:92: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:106:3 + --> $DIR/reprc-power-alignment.rs:105: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:111:3 + --> $DIR/reprc-power-alignment.rs:110: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:120:3 + --> $DIR/reprc-power-alignment.rs:119: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:125:3 + --> $DIR/reprc-power-alignment.rs:124: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:131:5 + --> $DIR/reprc-power-alignment.rs:130: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:133:5 + --> $DIR/reprc-power-alignment.rs:132:5 | LL | e: f64, | ^^^^^^ -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 +warning: 17 warnings emitted 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 b6c365536d208..a90159d2b5894 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, improper_ctype_definitions)] +#![forbid(improper_ctypes)] #![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 index 91efb9d8ad2c1..5bee42124e141 100644 --- a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs +++ b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs @@ -1,4 +1,4 @@ -#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_ctypes, improper_c_fn_definitions)] #![deny(improper_c_callbacks)] //@ aux-build: outer_crate_types.rs @@ -8,15 +8,17 @@ extern crate outer_crate_types as outer; // //////////////////////////////////////////////////////// // first, the same bank of types as in the extern crate +// FIXME: maybe re-introduce improper_ctype_definitions (ctype singular) +// as a way to mark ADTs as "let's ignore that they are not actually FFI-unsafe" + #[repr(C)] struct SafeStruct (i32); #[repr(C)] struct UnsafeStruct (String); -//~^ ERROR: `repr(C)` type uses type `String` #[repr(C)] -#[allow(improper_ctype_definitions)] +//#[allow(improper_ctype_definitions)] struct AllowedUnsafeStruct (String); // refs are only unsafe if the value comes from the other side of the FFI boundary @@ -28,7 +30,7 @@ struct AllowedUnsafeStruct (String); struct UnsafeFromForeignStruct<'a> (&'a u32); #[repr(C)] -#[allow(improper_ctype_definitions)] +//#[allow(improper_ctype_definitions)] struct AllowedUnsafeFromForeignStruct<'a> (&'a u32); @@ -134,7 +136,6 @@ extern "C" fn fn6ou() -> outer::AllowedUnsafeFromForeignStruct<'static> { #[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]` @@ -146,7 +147,7 @@ type FakeVTableMaker = extern "C" fn() -> FakeVTable; //~^ ERROR: `extern` callback uses type `FakeVTable` #[repr(C)] -#[allow(improper_c_callbacks, improper_ctype_definitions)] +#[allow(improper_c_callbacks)] struct FakeVTableAllowed{ make_new: extern "C" fn() -> A, combine: extern "C" fn(&[A]) -> A, diff --git a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr index 4759494b351b4..b95c1a5ed0a8e 100644 --- a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr @@ -1,19 +1,5 @@ -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 + --> $DIR/allow_improper_ctypes.rs:39:20 | LL | type UnsafeFnPtr = extern "C" fn((i32, i32))->i32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -28,7 +14,7 @@ LL | #![deny(improper_c_callbacks)] | ^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `&String`, which is not FFI-safe - --> $DIR/allow_improper_ctypes.rs:73:23 + --> $DIR/allow_improper_ctypes.rs:75:23 | LL | extern "C" fn fn1u(e: &String) -> &str {&*e} | ^^^^^^^ not FFI-safe @@ -39,11 +25,11 @@ LL | extern "C" fn fn1u(e: &String) -> &str {&*e} 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)] +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `&str`, which is not FFI-safe - --> $DIR/allow_improper_ctypes.rs:73:35 + --> $DIR/allow_improper_ctypes.rs:75:35 | LL | extern "C" fn fn1u(e: &String) -> &str {&*e} | ^^^^ not FFI-safe @@ -52,14 +38,14 @@ LL | extern "C" fn fn1u(e: &String) -> &str {&*e} = 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 + --> $DIR/allow_improper_ctypes.rs:81: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 + --> $DIR/allow_improper_ctypes.rs:18:1 | LL | struct UnsafeStruct (String); | ^^^^^^^^^^^^^^^^^^^ @@ -67,7 +53,7 @@ LL | struct UnsafeStruct (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 + --> $DIR/allow_improper_ctypes.rs:85:24 | LL | extern "C" fn fn2ou(e: outer::UnsafeStruct) {} | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -77,14 +63,14 @@ LL | extern "C" fn fn2ou(e: outer::UnsafeStruct) {} = note: `String` has unspecified layout error: `extern` fn uses type `AllowedUnsafeStruct`, which is not FFI-safe - --> $DIR/allow_improper_ctypes.rs:88:23 + --> $DIR/allow_improper_ctypes.rs:90: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 + --> $DIR/allow_improper_ctypes.rs:22:1 | LL | struct AllowedUnsafeStruct (String); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,7 +78,7 @@ LL | struct AllowedUnsafeStruct (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 + --> $DIR/allow_improper_ctypes.rs:95:24 | LL | extern "C" fn fn3ou(e: outer::AllowedUnsafeStruct) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -102,14 +88,14 @@ LL | extern "C" fn fn3ou(e: outer::AllowedUnsafeStruct) {} = note: `String` has unspecified layout error: `extern` fn uses type `UnsafeFromForeignStruct<'_>`, which is not FFI-safe - --> $DIR/allow_improper_ctypes.rs:99:23 + --> $DIR/allow_improper_ctypes.rs:101: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 + --> $DIR/allow_improper_ctypes.rs:30:1 | LL | struct UnsafeFromForeignStruct<'a> (&'a u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +104,7 @@ LL | struct UnsafeFromForeignStruct<'a> (&'a u32); 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 + --> $DIR/allow_improper_ctypes.rs:105:24 | LL | extern "C" fn fn4ou(e: outer::UnsafeFromForeignStruct) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -128,23 +114,8 @@ LL | extern "C" fn fn4ou(e: outer::UnsafeFromForeignStruct) {} = 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 + --> $DIR/allow_improper_ctypes.rs:140:14 | LL | combine: extern "C" fn(&[A]) -> A, | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -154,7 +125,7 @@ LL | combine: extern "C" fn(&[A]) -> A, = 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 + --> $DIR/allow_improper_ctypes.rs:146:24 | LL | type FakeVTableMaker = extern "C" fn() -> FakeVTable; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -162,12 +133,12 @@ LL | type FakeVTableMaker = extern "C" fn() -> FakeVTable; = 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 + --> $DIR/allow_improper_ctypes.rs:138:1 | LL | struct FakeVTable{ | ^^^^^^^^^^^^^^^^^^^^ = help: consider using a struct instead = note: tuples have unspecified layout -error: aborting due to 13 previous errors +error: aborting due to 11 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 index 104281065fd9c..37a52bd314ba7 100644 --- a/tests/ui/lint/improper_ctypes/auxiliary/outer_crate_types.rs +++ b/tests/ui/lint/improper_ctypes/auxiliary/outer_crate_types.rs @@ -8,7 +8,7 @@ pub struct SafeStruct (pub i32); pub struct UnsafeStruct (pub String); #[repr(C)] -#[allow(improper_ctype_definitions)] +//#[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 @@ -19,7 +19,7 @@ pub struct AllowedUnsafeStruct (pub String); pub struct UnsafeFromForeignStruct<'a> (pub &'a u32); #[repr(C)] -#[allow(improper_ctype_definitions)] +//#[allow(improper_ctype_definitions)] pub struct AllowedUnsafeFromForeignStruct<'a> (pub &'a u32); diff --git a/tests/ui/lint/improper_ctypes/ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs index 20b95375689a9..216ac0acfdf14 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -5,7 +5,7 @@ #![allow(private_interfaces)] #![deny(improper_ctypes, improper_c_callbacks)] -#![deny(improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_c_fn_definitions)] use std::cell::UnsafeCell; use std::marker::PhantomData; @@ -25,14 +25,14 @@ pub struct StructWithProjectionAndLifetime<'a>( ); pub type I32Pair = (i32, i32); #[repr(C)] -pub struct ZeroSize; //~ ERROR: `repr(C)` type uses type `ZeroSize` +pub struct ZeroSize; pub type RustFn = fn(); pub type RustBoxRet = extern "C" fn() -> Box; pub type CVoidRet = (); pub struct Foo; #[repr(transparent)] pub struct TransparentI128(i128); -#[repr(transparent)] // reminder: repr(transparent) struct defs are not scanned +#[repr(transparent)] pub struct TransparentStr(&'static str); #[repr(transparent)] pub struct TransparentBoxFn(RustBoxRet); @@ -53,15 +53,12 @@ pub struct UnsizedStructBecauseForeign { } #[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], } diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index ef5219cb92bb5..8aadeade6f508 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -1,64 +1,5 @@ -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 + --> $DIR/ctypes.rs:74:26 | LL | pub fn slice_type(p: &[u32]); | ^^^^^^ not FFI-safe @@ -72,7 +13,7 @@ LL | #![deny(improper_ctypes, improper_c_callbacks)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/ctypes.rs:78:24 + --> $DIR/ctypes.rs:75:24 | LL | pub fn str_type(p: &str); | ^^^^ not FFI-safe @@ -81,7 +22,7 @@ LL | pub fn str_type(p: &str); = 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 + --> $DIR/ctypes.rs:78:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -90,7 +31,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/ctypes.rs:82:25 + --> $DIR/ctypes.rs:79:25 | LL | pub fn i128_type(p: i128); | ^^^^ not FFI-safe @@ -98,7 +39,7 @@ LL | pub fn i128_type(p: i128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:83:25 + --> $DIR/ctypes.rs:80:25 | LL | pub fn u128_type(p: u128); | ^^^^ not FFI-safe @@ -106,7 +47,7 @@ LL | pub fn u128_type(p: u128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `(u32) is 1..`, which is not FFI-safe - --> $DIR/ctypes.rs:84:27 + --> $DIR/ctypes.rs:81:27 | LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -115,7 +56,7 @@ LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); = 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 + --> $DIR/ctypes.rs:83:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -123,7 +64,7 @@ LL | pub fn trait_type(p: &dyn Bar); = 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 + --> $DIR/ctypes.rs:84:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -132,7 +73,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/ctypes.rs:88:27 + --> $DIR/ctypes.rs:85:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -141,7 +82,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/ctypes.rs:89:25 + --> $DIR/ctypes.rs:86:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -155,20 +96,20 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/ctypes.rs:90:33 + --> $DIR/ctypes.rs:87: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 + --> $DIR/ctypes.rs:67:1 | LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/ctypes.rs:93:12 + --> $DIR/ctypes.rs:90:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +117,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:94:23 + --> $DIR/ctypes.rs:91:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -185,7 +126,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:95:24 + --> $DIR/ctypes.rs:92:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -194,7 +135,7 @@ LL | pub fn fn_type2(p: fn()); = 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 + --> $DIR/ctypes.rs:94:32 | LL | pub fn transparent_i128(p: TransparentI128); | ^^^^^^^^^^^^^^^ not FFI-safe @@ -208,7 +149,7 @@ 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 + --> $DIR/ctypes.rs:95:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -223,7 +164,7 @@ LL | pub struct TransparentStr(&'static str); = 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 + --> $DIR/ctypes.rs:97:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -232,7 +173,7 @@ LL | pub fn raw_array(arr: [u8; 8]); = 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 + --> $DIR/ctypes.rs:99:12 | LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -247,7 +188,7 @@ LL | #![deny(improper_ctypes, improper_c_callbacks)] | ^^^^^^^^^^^^^^^^^^^^ error: `extern` callback uses type `&dyn Debug`, which is not FFI-safe - --> $DIR/ctypes.rs:102:12 + --> $DIR/ctypes.rs:99:12 | LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -256,7 +197,7 @@ LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) = 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 + --> $DIR/ctypes.rs:99:12 | LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -264,7 +205,7 @@ LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) = 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 + --> $DIR/ctypes.rs:61:1 | LL | pub struct TwoBadTypes<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -272,7 +213,7 @@ LL | pub struct TwoBadTypes<'a> { = 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 + --> $DIR/ctypes.rs:106:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -280,7 +221,7 @@ LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); = 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 + --> $DIR/ctypes.rs:108:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -289,7 +230,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/ctypes.rs:113:26 + --> $DIR/ctypes.rs:110:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -298,7 +239,7 @@ LL | pub fn no_niche_b(b: Option>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:116:34 + --> $DIR/ctypes.rs:113:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -306,12 +247,12 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/ctypes.rs:117:40 + --> $DIR/ctypes.rs:114: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 +error: aborting due to 25 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-113436-1.rs b/tests/ui/lint/improper_ctypes/lint-113436-1.rs index 44cf2fcb21009..118ba2075e60d 100644 --- a/tests/ui/lint/improper_ctypes/lint-113436-1.rs +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.rs @@ -1,4 +1,4 @@ -#![deny(improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_c_fn_definitions)] #[repr(C)] pub struct Foo { diff --git a/tests/ui/lint/improper_ctypes/lint-113436-1.stderr b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr index 255063e42204d..764c166dda002 100644 --- a/tests/ui/lint/improper_ctypes/lint-113436-1.stderr +++ b/tests/ui/lint/improper_ctypes/lint-113436-1.stderr @@ -1,26 +1,3 @@ -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 | @@ -43,7 +20,7 @@ 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)] +LL | #![deny(improper_c_fn_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `Bar`, which is not FFI-safe @@ -66,5 +43,5 @@ note: the type is defined here LL | struct NotSafe(u32); | ^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-73249-2.rs b/tests/ui/lint/improper_ctypes/lint-73249-2.rs index 5f1f9c7386037..84e2a5ab03e0c 100644 --- a/tests/ui/lint/improper_ctypes/lint-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, improper_ctype_definitions)] +#![deny(improper_ctypes)] trait Baz {} diff --git a/tests/ui/lint/improper_ctypes/lint-73249-2.stderr b/tests/ui/lint/improper_ctypes/lint-73249-2.stderr index 9f52eefc686e5..c835d276d459c 100644 --- a/tests/ui/lint/improper_ctypes/lint-73249-2.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-2.stderr @@ -16,7 +16,7 @@ LL | struct A { note: the lint level is defined here --> $DIR/lint-73249-2.rs:2:9 | -LL | #![deny(improper_ctypes, improper_ctype_definitions)] +LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/improper_ctypes/lint-73249-3.rs b/tests/ui/lint/improper_ctypes/lint-73249-3.rs index e3e7659ba16c9..aff2a182e3f49 100644 --- a/tests/ui/lint/improper_ctypes/lint-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, improper_ctype_definitions)] +#![deny(improper_ctypes)] pub trait Baz {} @@ -13,7 +13,7 @@ fn assign() -> Qux { } #[repr(C)] -pub struct A { //~ ERROR: `repr(C)` type uses type `Qux` +pub struct A { x: Qux, } diff --git a/tests/ui/lint/improper_ctypes/lint-73249-3.stderr b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr index bd449c1fa6e77..dc6f6fb08ed33 100644 --- a/tests/ui/lint/improper_ctypes/lint-73249-3.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr @@ -1,18 +1,3 @@ -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 | @@ -29,8 +14,8 @@ LL | pub struct A { note: the lint level is defined here --> $DIR/lint-73249-3.rs:2:9 | -LL | #![deny(improper_ctypes, improper_ctype_definitions)] +LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/lint/improper_ctypes/lint-73249-5.rs b/tests/ui/lint/improper_ctypes/lint-73249-5.rs index 8272256b5a83a..8ad5be4e6301e 100644 --- a/tests/ui/lint/improper_ctypes/lint-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, improper_ctype_definitions)] +#![deny(improper_ctypes)] pub trait Baz {} diff --git a/tests/ui/lint/improper_ctypes/lint-73249-5.stderr b/tests/ui/lint/improper_ctypes/lint-73249-5.stderr index 9172039afa104..f42924f4d5b56 100644 --- a/tests/ui/lint/improper_ctypes/lint-73249-5.stderr +++ b/tests/ui/lint/improper_ctypes/lint-73249-5.stderr @@ -14,7 +14,7 @@ LL | pub struct A { note: the lint level is defined here --> $DIR/lint-73249-5.rs:2:9 | -LL | #![deny(improper_ctypes, improper_ctype_definitions)] +LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/lint/improper_ctypes/lint-94223.rs b/tests/ui/lint/improper_ctypes/lint-94223.rs index d6a03f19d543d..4ca28e5722280 100644 --- a/tests/ui/lint/improper_ctypes/lint-94223.rs +++ b/tests/ui/lint/improper_ctypes/lint-94223.rs @@ -1,5 +1,5 @@ #![crate_type = "lib"] -#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_ctypes, improper_c_fn_definitions)] #![deny(improper_c_callbacks)] pub fn bad(f: extern "C" fn([u8])) {} diff --git a/tests/ui/lint/improper_ctypes/lint-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs index ecf21618e12ef..5dbaba80214f8 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.rs +++ b/tests/ui/lint/improper_ctypes/lint-fn.rs @@ -1,5 +1,5 @@ #![allow(private_interfaces)] -#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_ctypes, improper_c_fn_definitions)] #![deny(improper_c_callbacks)] use std::default::Default; @@ -23,7 +23,7 @@ pub struct StructWithProjectionAndLifetime<'a>( pub type I32Pair = (i32, i32); #[repr(C)] -pub struct ZeroSize; //~ ERROR uses type `ZeroSize` +pub struct ZeroSize; pub type RustFn = fn(); diff --git a/tests/ui/lint/improper_ctypes/lint-fn.stderr b/tests/ui/lint/improper_ctypes/lint-fn.stderr index 06086de892cec..bfd2afdefcad7 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.stderr +++ b/tests/ui/lint/improper_ctypes/lint-fn.stderr @@ -1,22 +1,3 @@ -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 | @@ -43,7 +24,7 @@ LL | pub extern "C" fn slice_type(p: &[u32]) { } note: the lint level is defined here --> $DIR/lint-fn.rs:2:26 | -LL | #![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `&str`, which is not FFI-safe @@ -300,5 +281,5 @@ LL | pub extern "C" fn used_generic5() -> Vec { = 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 +error: aborting due to 26 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-transparent-help.rs b/tests/ui/lint/improper_ctypes/lint-transparent-help.rs index 36e6e76097b6f..d2f3a0147376c 100644 --- a/tests/ui/lint/improper_ctypes/lint-transparent-help.rs +++ b/tests/ui/lint/improper_ctypes/lint-transparent-help.rs @@ -1,4 +1,4 @@ -#![deny(improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_c_fn_definitions)] use std::marker::PhantomData; use std::collections::HashMap; use std::ffi::c_void; @@ -10,7 +10,7 @@ struct DictPhantom<'a, A,B:'a>{ } #[repr(C)] // [option 2] oops, we meant repr(transparent) -struct MyTypedRawPointer<'a,T:'a>{ //~ ERROR: uses type `DictPhantom<'_, T, T>` +struct MyTypedRawPointer<'a,T:'a>{ ptr: *const c_void, metadata: DictPhantom<'a,T,T>, } diff --git a/tests/ui/lint/improper_ctypes/lint-transparent-help.stderr b/tests/ui/lint/improper_ctypes/lint-transparent-help.stderr index 4739d53255534..de8e7bd42cfc0 100644 --- a/tests/ui/lint/improper_ctypes/lint-transparent-help.stderr +++ b/tests/ui/lint/improper_ctypes/lint-transparent-help.stderr @@ -1,32 +1,3 @@ -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 | @@ -50,8 +21,8 @@ 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)] +LL | #![deny(improper_c_fn_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs index 6e4f10d8704c0..6494451049c27 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs @@ -2,7 +2,7 @@ //@ edition:2018 #![allow(dead_code,unused_variables)] -#![deny(improper_ctypes, improper_c_fn_definitions, improper_ctype_definitions)] +#![deny(improper_ctypes, improper_c_fn_definitions)] #![deny(improper_c_callbacks)] // we want ALL the ty_kinds, including the feature-gated ones @@ -106,7 +106,6 @@ 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. @@ -311,65 +310,4 @@ extern "C" fn all_ty_kinds_in_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 index 02bb8b9f76671..f30c6ddcf189f 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -19,7 +19,7 @@ LL | &self 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)] +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `Box`, which is not FFI-safe @@ -42,21 +42,8 @@ LL | &self = 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 + --> $DIR/lint-tykind-fuzz.rs:120:5 | LL | s:String, | ^^^^^^ not FFI-safe @@ -65,7 +52,7 @@ LL | s:String, = note: `String` has unspecified layout error: `extern` fn uses type `&str`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:123:6 + --> $DIR/lint-tykind-fuzz.rs:122:6 | LL | s2:&str, | ^^^^ not FFI-safe @@ -74,7 +61,7 @@ LL | s2:&str, = 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 + --> $DIR/lint-tykind-fuzz.rs:124:6 | LL | c: char, | ^^^^ not FFI-safe @@ -83,7 +70,7 @@ LL | c: char, = 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 + --> $DIR/lint-tykind-fuzz.rs:126:6 | LL | s3:&[u8], | ^^^^^ not FFI-safe @@ -92,7 +79,7 @@ LL | s3:&[u8], = 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 + --> $DIR/lint-tykind-fuzz.rs:128:6 | LL | s4:[u8;N], | ^^^^^^ not FFI-safe @@ -101,7 +88,7 @@ LL | s4:[u8;N], = 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 + --> $DIR/lint-tykind-fuzz.rs:130:5 | LL | p:(u8, u8), | ^^^^^^^^ not FFI-safe @@ -110,7 +97,7 @@ LL | p:(u8, u8), = note: tuples have unspecified layout error: `extern` fn uses type `(u8, u8)`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:133:12 + --> $DIR/lint-tykind-fuzz.rs:132:12 | LL | (p2, p3):(u8, u8), | ^^^^^^^^ not FFI-safe @@ -119,7 +106,7 @@ LL | (p2, p3):(u8, u8), = 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 + --> $DIR/lint-tykind-fuzz.rs:134:7 | LL | nz: pattern_type!(u32 is 1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -128,7 +115,7 @@ LL | nz: pattern_type!(u32 is 1..), = 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 + --> $DIR/lint-tykind-fuzz.rs:148:7 | LL | e3: &StructWithDyn, | ^^^^^^^^^^^^^^ not FFI-safe @@ -136,7 +123,7 @@ LL | e3: &StructWithDyn, = 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 + --> $DIR/lint-tykind-fuzz.rs:150:5 | LL | x:!, | ^ not FFI-safe @@ -144,7 +131,7 @@ LL | x:!, = 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 + --> $DIR/lint-tykind-fuzz.rs:153:7 | LL | f2: fn(u8)->u8, | ^^^^^^^^^^ not FFI-safe @@ -153,7 +140,7 @@ LL | f2: fn(u8)->u8, = 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 + --> $DIR/lint-tykind-fuzz.rs:155:7 | LL | f3: &'a dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -161,7 +148,7 @@ LL | f3: &'a dyn Fn(u8)->u8, = 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 + --> $DIR/lint-tykind-fuzz.rs:157:7 | LL | d2: &dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -169,7 +156,7 @@ LL | d2: &dyn std::cmp::PartialOrd, = 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 + --> $DIR/lint-tykind-fuzz.rs:161:6 | LL | ) -> impl std::fmt::Debug { | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -177,7 +164,7 @@ LL | ) -> impl std::fmt::Debug { = 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 + --> $DIR/lint-tykind-fuzz.rs:171:7 | LL | s2: *const str, | ^^^^^^^^^^ not FFI-safe @@ -186,7 +173,7 @@ LL | s2: *const str, = 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 + --> $DIR/lint-tykind-fuzz.rs:175:7 | LL | s3: *const [u8], | ^^^^^^^^^^^ not FFI-safe @@ -195,7 +182,7 @@ LL | s3: *const [u8], = 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 + --> $DIR/lint-tykind-fuzz.rs:181:12 | LL | (p2, p3):(*const u8, *const u8), | ^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -204,7 +191,7 @@ LL | (p2, p3):(*const u8, *const u8), = note: tuples have unspecified layout error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:186:29 + --> $DIR/lint-tykind-fuzz.rs:185:29 | LL | SomeStruct{b: ref p4,..}: & SomeStruct, | ^^^^^^^^^^^^ not FFI-safe @@ -214,7 +201,7 @@ LL | SomeStruct{b: ref p4,..}: & SomeStruct, 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 + --> $DIR/lint-tykind-fuzz.rs:197:7 | LL | e3: *const StructWithDyn, | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -222,7 +209,7 @@ LL | e3: *const StructWithDyn, = 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 + --> $DIR/lint-tykind-fuzz.rs:204:7 | LL | f3: *const dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -230,7 +217,7 @@ LL | f3: *const dyn Fn(u8)->u8, = 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 + --> $DIR/lint-tykind-fuzz.rs:206:7 | LL | d2: *const dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -238,7 +225,7 @@ LL | d2: *const dyn std::cmp::PartialOrd, = 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 + --> $DIR/lint-tykind-fuzz.rs:210:6 | LL | ) -> *const dyn std::fmt::Debug { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -246,7 +233,7 @@ LL | ) -> *const dyn std::fmt::Debug { = 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 + --> $DIR/lint-tykind-fuzz.rs:221:7 | LL | s2: &str, | ^^^^ not FFI-safe @@ -256,11 +243,11 @@ LL | s2: &str, 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)] +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `&[u8]`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:226:7 + --> $DIR/lint-tykind-fuzz.rs:225:7 | LL | s3: &[u8], | ^^^^^ not FFI-safe @@ -269,7 +256,7 @@ LL | s3: &[u8], = 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 + --> $DIR/lint-tykind-fuzz.rs:247:7 | LL | e3: &StructWithDyn, | ^^^^^^^^^^^^^^ not FFI-safe @@ -277,7 +264,7 @@ LL | e3: &StructWithDyn, = 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 + --> $DIR/lint-tykind-fuzz.rs:254:7 | LL | f3: &dyn Fn(u8)->u8, | ^^^^^^^^^^^^^^^ not FFI-safe @@ -285,7 +272,7 @@ LL | f3: &dyn Fn(u8)->u8, = 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 + --> $DIR/lint-tykind-fuzz.rs:256:7 | LL | d2: &dyn std::cmp::PartialOrd, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -293,7 +280,7 @@ LL | d2: &dyn std::cmp::PartialOrd, = 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 + --> $DIR/lint-tykind-fuzz.rs:260:6 | LL | ) -> &'a dyn std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -301,7 +288,7 @@ LL | ) -> &'a dyn std::fmt::Debug; = 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 + --> $DIR/lint-tykind-fuzz.rs:269:7 | LL | s2: Box, | ^^^^^^^^ not FFI-safe @@ -310,7 +297,7 @@ LL | s2: Box, = 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 + --> $DIR/lint-tykind-fuzz.rs:271:6 | LL | c: Box, | ^^^^^^^^^ not FFI-safe @@ -320,7 +307,7 @@ LL | c: Box, 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 + --> $DIR/lint-tykind-fuzz.rs:273:7 | LL | s3: Box<[u8]>, | ^^^^^^^^^ not FFI-safe @@ -329,7 +316,7 @@ LL | s3: Box<[u8]>, = 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 + --> $DIR/lint-tykind-fuzz.rs:279:11 | LL | (p2,p3):(Box, Box), | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -338,7 +325,7 @@ LL | (p2,p3):(Box, Box), = note: tuples have unspecified layout error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:284:29 + --> $DIR/lint-tykind-fuzz.rs:283:29 | LL | SomeStruct{b: ref p4,..}: &SomeStruct, | ^^^^^^^^^^^ not FFI-safe @@ -348,7 +335,7 @@ LL | SomeStruct{b: ref p4,..}: &SomeStruct, 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 + --> $DIR/lint-tykind-fuzz.rs:295:7 | LL | e3: Box, | ^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -356,7 +343,7 @@ LL | e3: Box, = 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 + --> $DIR/lint-tykind-fuzz.rs:298:6 | LL | x: Box, | ^^^^^^ not FFI-safe @@ -366,7 +353,7 @@ LL | x: Box, 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 + --> $DIR/lint-tykind-fuzz.rs:301:7 | LL | f2: Boxu8>, | ^^^^^^^^^^^^^^^ not FFI-safe @@ -376,7 +363,7 @@ LL | f2: Boxu8>, 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 + --> $DIR/lint-tykind-fuzz.rs:303:7 | LL | f3: Boxu8>, | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -384,7 +371,7 @@ LL | f3: Boxu8>, = 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 + --> $DIR/lint-tykind-fuzz.rs:305:7 | LL | d2: Box>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -392,108 +379,12 @@ LL | d2: Box>, = 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 + --> $DIR/lint-tykind-fuzz.rs:309: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 +error: aborting due to 42 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 index a328dceeb5765..9051c1760fb32 100644 --- a/tests/ui/lint/improper_ctypes/lint_uninhabited.rs +++ b/tests/ui/lint/improper_ctypes/lint_uninhabited.rs @@ -1,7 +1,7 @@ #![feature(never_type)] #![allow(dead_code, unused_variables)] -#![deny(improper_ctypes,improper_ctype_definitions)] +#![deny(improper_ctypes)] #![deny(improper_c_fn_definitions, improper_c_callbacks)] use std::mem::transmute; diff --git a/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr b/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr index 7863b3996e846..f678b0d725aec 100644 --- a/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr +++ b/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr @@ -19,7 +19,7 @@ LL | enum Uninhabited{} note: the lint level is defined here --> $DIR/lint_uninhabited.rs:4:9 | -LL | #![deny(improper_ctypes,improper_ctype_definitions)] +LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `AlsoUninhabited`, which is not FFI-safe diff --git a/tests/ui/lint/improper_ctypes/mustpass-113436.rs b/tests/ui/lint/improper_ctypes/mustpass-113436.rs index b36013f4482cd..1c5e4b2ac0142 100644 --- a/tests/ui/lint/improper_ctypes/mustpass-113436.rs +++ b/tests/ui/lint/improper_ctypes/mustpass-113436.rs @@ -1,5 +1,5 @@ //@ check-pass -#![deny(improper_c_fn_definitions,improper_ctype_definitions)] +#![deny(improper_c_fn_definitions)] #[repr(C)] pub struct Wrap(T); 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 47fd95c15b253..5e73441750362 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, improper_ctype_definitions)] +#![deny(improper_ctypes)] #![allow(dead_code)] // issue https://github.com/rust-lang/rust/issues/14309 @@ -29,7 +29,6 @@ struct D { x: C, y: A } -//~^^^^ ERROR type `A` extern "C" { fn foo(x: A); //~ ERROR type `A`, which is not FFI-safe 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 994b27c112f1a..5c4df4f02cdce 100644 --- a/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr +++ b/tests/ui/lint/improper_ctypes/repr-rust-is-undefined.stderr @@ -1,27 +1,5 @@ -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:35:15 + --> $DIR/repr-rust-is-undefined.rs:34:15 | LL | fn foo(x: A); | ^ not FFI-safe @@ -36,11 +14,11 @@ LL | struct A { note: the lint level is defined here --> $DIR/repr-rust-is-undefined.rs:1:9 | -LL | #![deny(improper_ctypes, improper_ctype_definitions)] +LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:36:15 + --> $DIR/repr-rust-is-undefined.rs:35:15 | LL | fn bar(x: B); | ^ not FFI-safe @@ -54,7 +32,7 @@ LL | struct B { | ^^^^^^^^ error: `extern` block uses type `A`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:38:15 + --> $DIR/repr-rust-is-undefined.rs:37:15 | LL | fn qux(x: A2); | ^^ not FFI-safe @@ -68,7 +46,7 @@ LL | struct A { | ^^^^^^^^ error: `extern` block uses type `B`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:39:16 + --> $DIR/repr-rust-is-undefined.rs:38:16 | LL | fn quux(x: B2); | ^^ not FFI-safe @@ -82,7 +60,7 @@ LL | struct B { | ^^^^^^^^ error: `extern` block uses type `D`, which is not FFI-safe - --> $DIR/repr-rust-is-undefined.rs:41:16 + --> $DIR/repr-rust-is-undefined.rs:40:16 | LL | fn fred(x: D); | ^ not FFI-safe @@ -101,5 +79,5 @@ note: the type is defined here LL | struct A { | ^^^^^^^^ -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/lint/wasm_c_abi_transition.rs b/tests/ui/lint/wasm_c_abi_transition.rs index 74bd246d82f29..c97e0814e4442 100644 --- a/tests/ui/lint/wasm_c_abi_transition.rs +++ b/tests/ui/lint/wasm_c_abi_transition.rs @@ -41,7 +41,6 @@ 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_c_fn_definitions)] diff --git a/tests/ui/lint/wasm_c_abi_transition.stderr b/tests/ui/lint/wasm_c_abi_transition.stderr index 699eed10e3957..c2f44e8acafec 100644 --- a/tests/ui/lint/wasm_c_abi_transition.stderr +++ b/tests/ui/lint/wasm_c_abi_transition.stderr @@ -1,5 +1,5 @@ warning: `extern` fn uses type `v128`, which is not FFI-safe - --> $DIR/wasm_c_abi_transition.rs:56:35 + --> $DIR/wasm_c_abi_transition.rs:55:35 | LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } | ^^^^ not FFI-safe @@ -7,14 +7,14 @@ LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } = 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:54:1 + --> $DIR/wasm_c_abi_transition.rs:53:1 | LL | pub struct v128([i32; 4]); | ^^^^^^^^^^^^^^^ = 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:56:44 + --> $DIR/wasm_c_abi_transition.rs:55:44 | LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } | ^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | pub extern "C" fn my_safe_simd(x: v128) -> v128 { x } = 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:54:1 + --> $DIR/wasm_c_abi_transition.rs:53:1 | LL | pub struct v128([i32; 4]); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/offset-of/offset-of-slice-normalized.rs b/tests/ui/offset-of/offset-of-slice-normalized.rs index 4f95fa1fb119f..b7326461a6a9e 100644 --- a/tests/ui/offset-of/offset-of-slice-normalized.rs +++ b/tests/ui/offset-of/offset-of-slice-normalized.rs @@ -4,7 +4,6 @@ //@ run-pass #![feature(offset_of_slice)] -#![allow(improper_ctype_definitions)] use std::mem::offset_of; diff --git a/tests/ui/offset-of/offset-of-slice.rs b/tests/ui/offset-of/offset-of-slice.rs index a7a0956206384..806b8a50c2118 100644 --- a/tests/ui/offset-of/offset-of-slice.rs +++ b/tests/ui/offset-of/offset-of-slice.rs @@ -1,6 +1,5 @@ //@run-pass #![feature(offset_of_slice)] -#![allow(improper_ctype_definitions)] use std::mem::offset_of; diff --git a/tests/ui/structs-enums/align-struct.rs b/tests/ui/structs-enums/align-struct.rs index 0f94e1553c7a5..3d8dad6e324e3 100644 --- a/tests/ui/structs-enums/align-struct.rs +++ b/tests/ui/structs-enums/align-struct.rs @@ -33,7 +33,6 @@ 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 6bdce52e15276..142d0ee32872e 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,8 +4,6 @@ // 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 11f70abe0e11f..15c9784dbb9ad 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,8 +4,6 @@ // 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 714bd689ebc5c..64338b2aba765 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,8 +4,6 @@ // 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/transmutability/abstraction/const_generic_fn.rs b/tests/ui/transmutability/abstraction/const_generic_fn.rs index 0499afb4eb122..1ea978ce1bab9 100644 --- a/tests/ui/transmutability/abstraction/const_generic_fn.rs +++ b/tests/ui/transmutability/abstraction/const_generic_fn.rs @@ -4,7 +4,6 @@ #![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 8b89cd8fcc91a..00c0c1122ef6b 100644 --- a/tests/ui/transmutability/arrays/should_have_correct_length.rs +++ b/tests/ui/transmutability/arrays/should_have_correct_length.rs @@ -4,7 +4,6 @@ #![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 7aa5e5f23e1d1..70d2f07c449d3 100644 --- a/tests/ui/transmutability/arrays/should_inherit_alignment.rs +++ b/tests/ui/transmutability/arrays/should_inherit_alignment.rs @@ -4,7 +4,6 @@ #![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 3c89c72eb67b5..98deb6457cb07 100644 --- a/tests/ui/transmutability/references/u8-to-unit.rs +++ b/tests/ui/transmutability/references/u8-to-unit.rs @@ -18,7 +18,6 @@ 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 f0bd578f8419d..789455c03ea17 100644 --- a/tests/ui/transmutability/references/unit-to-itself.rs +++ b/tests/ui/transmutability/references/unit-to-itself.rs @@ -18,7 +18,6 @@ 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 998c38755df28..0113049f51e53 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,7 +19,6 @@ 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 46672d3faf421..46e84b48044f5 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,7 +18,6 @@ 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 cb430621435fd..aaba6febde4e8 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,7 +18,6 @@ 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 1fcf741e0a930..e5ca37e68ec6b 100644 --- a/tests/ui/transmutability/structs/repr/should_handle_all.rs +++ b/tests/ui/transmutability/structs/repr/should_handle_all.rs @@ -4,7 +4,6 @@ #![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 1d5d071742acb..0605651bd7bb1 100644 --- a/tests/ui/transmutability/unions/repr/should_handle_align.rs +++ b/tests/ui/transmutability/unions/repr/should_handle_align.rs @@ -4,7 +4,6 @@ #![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 83e6b9a8106c1..5e9851ab0c984 100644 --- a/tests/ui/transmutability/unions/repr/should_handle_packed.rs +++ b/tests/ui/transmutability/unions/repr/should_handle_packed.rs @@ -4,7 +4,6 @@ #![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}; From 347d62b96cce660073b5a1648e42543896cf0aab Mon Sep 17 00:00:00 2001 From: niacdoial Date: Wed, 21 May 2025 00:32:08 +0200 Subject: [PATCH 15/15] lint ImproperCTypes: clean up exported static variables --- .../rustc_lint/src/types/improper_ctypes.rs | 123 ++++++++---------- tests/ui/lint/improper_ctypes/ctypes.rs | 12 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 26 +++- 3 files changed, 92 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 312206e75e6ad..031df5cb805a0 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -3,6 +3,7 @@ use std::iter; use std::ops::ControlFlow; use rustc_abi::{Integer, IntegerType, VariantIdx}; +use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::def::CtorKind; @@ -422,18 +423,21 @@ fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> Type #[allow(non_snake_case)] mod CTypesVisitorStateFlags { - pub(super) const NO_FLAGS: u8 = 0b00000; + pub(super) const NO_FLAGS: u8 = 0b000000; /// for use in (externally-linked) static variables - pub(super) const STATIC: u8 = 0b00001; + pub(super) const STATIC: u8 = 0b000001; /// for use in functions in general - pub(super) const FUNC: u8 = 0b00010; + pub(super) const FUNC: u8 = 0b000010; /// 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 + pub(super) const FN_RETURN: u8 = 0b000100; + /// for variables in functions/variables which are defined in rust + pub(super) const DEFINED: u8 = 0b001000; + /// for times where we are only defining the type of something /// (struct/enum/union definitions, FnPtrs) - pub(super) const THEORETICAL: u8 = 0b10000; + pub(super) const THEORETICAL: u8 = 0b010000; + /// if we are looking at an interface where the value can be set by the non-rust side + /// (important for e.g. nonzero assumptions) + pub(super) const FOREIGN_VALUES: u8 = 0b100000; } #[repr(u8)] @@ -441,14 +445,21 @@ mod CTypesVisitorStateFlags { enum CTypesVisitorState { None = CTypesVisitorStateFlags::NO_FLAGS, // uses bitflags from CTypesVisitorStateFlags - StaticTy = CTypesVisitorStateFlags::STATIC, - ExportedStaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::FN_DEFINED, - ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_DEFINED, + StaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::FOREIGN_VALUES, + ExportedStaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::DEFINED, + ExportedStaticMutTy = CTypesVisitorStateFlags::STATIC + | CTypesVisitorStateFlags::DEFINED + | CTypesVisitorStateFlags::FOREIGN_VALUES, + ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC + | CTypesVisitorStateFlags::DEFINED + | CTypesVisitorStateFlags::FOREIGN_VALUES, ReturnTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_RETURN - | CTypesVisitorStateFlags::FN_DEFINED, + | CTypesVisitorStateFlags::DEFINED, ArgumentTyInDeclaration = CTypesVisitorStateFlags::FUNC, - ReturnTyInDeclaration = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_RETURN, + ReturnTyInDeclaration = CTypesVisitorStateFlags::FUNC + | CTypesVisitorStateFlags::FN_RETURN + | CTypesVisitorStateFlags::FOREIGN_VALUES, ArgumentTyInFnPtr = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::THEORETICAL, ReturnTyInFnPtr = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::THEORETICAL @@ -456,15 +467,6 @@ enum CTypesVisitorState { } 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::*; @@ -489,58 +491,35 @@ impl CTypesVisitorState { /// 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() + ((self as u8) & DEFINED) != 0 && self.is_in_function() } /// 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 - if ((self as u8) & THEORETICAL) != 0 { - true - } else { - ((self as u8) & FN_DEFINED) != 0 && ((self as u8) & STATIC) == 0 - } + // rust-defined functions, as well as FnPtrs + ((self as u8) & THEORETICAL) != 0 || self.is_in_defined_function() } /// 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.is_in_static() { - // FIXME: this is evidently untrue for non-mut static variables - // (assuming the cross-FFI code respects this) - 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() - } + // function definitions are assumed to be maybe-not-rust-caller, rust-callee + // function declarations are assumed to be rust-caller, non-rust-callee + // 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? + use CTypesVisitorStateFlags::*; + ((self as u8) & FOREIGN_VALUES) != 0 } } @@ -1620,10 +1599,16 @@ impl ImproperCTypesLint { cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span, + is_mut: bool, ) { let ty = cx.tcx.type_of(id).instantiate_identity(); let mut visitor = ImproperCTypesVisitor::new(cx); - let ffi_res = visitor.check_for_type(CTypesVisitorState::ExportedStaticTy, ty); + let state = if is_mut { + CTypesVisitorState::ExportedStaticMutTy + } else { + CTypesVisitorState::ExportedStaticTy + }; + let ffi_res = visitor.check_for_type(state, ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::ExportedStatic); } @@ -1810,11 +1795,15 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { cx.tcx.type_of(item.owner_id).instantiate_identity(), ); - if matches!(item.kind, hir::ItemKind::Static(..)) + if let hir::ItemKind::Static(_, _, is_mut, _) = item.kind && (cx.tcx.has_attr(item.owner_id, sym::no_mangle) || cx.tcx.has_attr(item.owner_id, sym::export_name)) { - self.check_exported_static(cx, item.owner_id, ty.span); + let is_mut = match is_mut { + Mutability::Not => false, + Mutability::Mut => true, + }; + self.check_exported_static(cx, item.owner_id, ty.span, is_mut); } } // See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks diff --git a/tests/ui/lint/improper_ctypes/ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs index 216ac0acfdf14..5ecae7ed3a536 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -5,7 +5,7 @@ #![allow(private_interfaces)] #![deny(improper_ctypes, improper_c_callbacks)] -#![deny(improper_c_fn_definitions)] +#![deny(improper_c_fn_definitions, improper_c_var_definitions)] use std::cell::UnsafeCell; use std::marker::PhantomData; @@ -140,6 +140,16 @@ extern "C" { pub fn good19(_: &String); } +static DEFAULT_U32: u32 = 42; +#[no_mangle] +static EXPORTED_STATIC: &u32 = &DEFAULT_U32; +#[no_mangle] +static EXPORTED_STATIC_BAD: &'static str = "is this reaching you, plugin?"; +//~^ ERROR: uses type `&str` +#[export_name="EXPORTED_STATIC_MUT_BUT_RENAMED"] +static mut EXPORTED_STATIC_MUT: &u32 = &DEFAULT_U32; +//~^ ERROR: uses type `&u32` + #[cfg(not(target_arch = "wasm32"))] extern "C" { pub fn good1(size: *const c_int); diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index 8aadeade6f508..7eb3eb09f522f 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -254,5 +254,29 @@ LL | pub static static_u128_array_type: [u128; 16]; | = note: 128-bit integers don't currently have a known stable ABI -error: aborting due to 25 previous errors +error: foreign-code-reachable static uses type `&str`, which is not FFI-safe + --> $DIR/ctypes.rs:147:29 + | +LL | static EXPORTED_STATIC_BAD: &'static str = "is this reaching you, plugin?"; + | ^^^^^^^^^^^^ 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/ctypes.rs:8:36 + | +LL | #![deny(improper_c_fn_definitions, improper_c_var_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: foreign-code-reachable static uses type `&u32`, which is not FFI-safe + --> $DIR/ctypes.rs:150:33 + | +LL | static mut EXPORTED_STATIC_MUT: &u32 = &DEFAULT_U32; + | ^^^^ not FFI-safe + | + = 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: aborting due to 27 previous errors