From fc3f6549b95a9ffabd7ffa63a5b56d8d85502bfb Mon Sep 17 00:00:00 2001 From: niacdoial Date: Wed, 11 Dec 2024 23:54:33 +0100 Subject: [PATCH 01/19] 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 | 23 +- tests/ui/lint/lint-ctypes.rs | 35 +- tests/ui/lint/lint-ctypes.stderr | 110 ++---- 15 files changed, 482 insertions(+), 175 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 4d0c0c94a8138..d87e7ad71df20 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -375,7 +375,6 @@ lint_improper_ctypes = `extern` {$desc} uses type `{$ty}`, which is not FFI-safe 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 @@ -393,7 +392,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 @@ -401,7 +402,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 @@ -427,6 +434,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 ef63c0dee8c2e..7fc082228908d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1976,29 +1976,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 b0afc333ebe2f..9af613978ba0f 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -24,10 +24,10 @@ use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AmbiguousWidePointerComparisonsCastSuggestion, AmbiguousWidePointerComparisonsExpectSuggestion, 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}; @@ -825,7 +825,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>( @@ -862,7 +965,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); @@ -1080,12 +1183,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 @@ -1142,9 +1246,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, } } @@ -1178,16 +1282,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, }; } } @@ -1333,15 +1468,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(), @@ -1351,7 +1477,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), @@ -1369,7 +1558,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), + }; + } } } @@ -1378,7 +1574,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, @@ -1417,8 +1621,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, @@ -1428,18 +1631,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 { @@ -1468,7 +1670,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 @@ -1507,15 +1715,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 0b84098e39067..b39178c98589a 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) { } @@ -115,7 +115,7 @@ pub extern "C" fn fn_type2(p: fn()) { } pub extern "C" fn fn_contained(p: RustBadRet) { } 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 a19c04a63b566..2806e29ae3529 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 @@ -125,14 +127,14 @@ 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 `str`, which is not FFI-safe +error: `extern` fn uses type `&str`, which is not FFI-safe --> $DIR/lint-ctypes-fn.rs:117: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:169:43 @@ -161,4 +163,3 @@ LL | pub extern "C" fn used_generic5() -> Vec { = note: this struct has unspecified layout error: aborting due to 17 previous errors - diff --git a/tests/ui/lint/lint-ctypes.rs b/tests/ui/lint/lint-ctypes.rs index 47586c826ab86..bf7af490f17b0 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,13 +61,12 @@ 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 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` @@ -64,11 +76,14 @@ extern "C" { -> ::std::marker::PhantomData; //~ ERROR uses type `PhantomData` pub fn fn_type(p: RustFn); //~ ERROR uses type `fn()` pub fn fn_type2(p: fn()); //~ ERROR uses type `fn()` - pub fn fn_contained(p: RustBadRet); //~ ERROR: uses type `Box` - pub fn transparent_str(p: TransparentStr); //~ ERROR: uses type `str` - pub fn transparent_fn(p: TransparentBadFn); //~ ERROR: uses type `Box` + pub fn fn_contained(p: RustBoxRet); + pub fn transparent_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 3fb36647d4f1c..57cda0f8e0ee2 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 @@ -85,16 +70,16 @@ LL | pub fn char_type(p: char); = help: consider using `u32` or `libc::wchar_t` instead = note: the `char` type has no C equivalent -error: `extern` block uses type `dyn Bar`, which is not FFI-safe +error: `extern` block uses type `&dyn Bar`, which is not FFI-safe --> $DIR/lint-ctypes.rs:57: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:58:26 + --> $DIR/lint-ctypes.rs:70:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -103,7 +88,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:59:27 + --> $DIR/lint-ctypes.rs:71:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -112,7 +97,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:60:25 + --> $DIR/lint-ctypes.rs:72:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -120,26 +105,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:61:33 + --> $DIR/lint-ctypes.rs:73: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:64:12 + --> $DIR/lint-ctypes.rs:76:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -147,7 +132,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:65:23 + --> $DIR/lint-ctypes.rs:77:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -156,7 +141,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:66:24 + --> $DIR/lint-ctypes.rs:78:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -164,35 +149,17 @@ 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:67: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 `str`, which is not FFI-safe +error: `extern` block uses type `&str`, which is not FFI-safe --> $DIR/lint-ctypes.rs:68: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:69: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:70:27 + --> $DIR/lint-ctypes.rs:82:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -200,8 +167,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:72:26 + --> $DIR/lint-ctypes.rs:87:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -210,7 +185,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:74:26 + --> $DIR/lint-ctypes.rs:89:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -218,5 +193,4 @@ LL | pub fn no_niche_b(b: Option>); = 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 22 previous errors - +error: aborting due to 17 previous errors From a438c21ff4ab4d8e72465f15e8a78cbd93969dc7 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Mon, 23 Dec 2024 11:56:55 +0100 Subject: [PATCH 02/19] lint ImproperCTypes: make checking indirection more DRY [commit does not pass tests] --- compiler/rustc_lint/src/lints.rs | 5 + compiler/rustc_lint/src/types.rs | 193 ++++++++---------- ...roper_ctypes_definitions_ice_134060.stderr | 2 +- tests/ui/lint/lint-ctypes-fn.stderr | 1 + tests/ui/lint/lint-ctypes.stderr | 9 +- 5 files changed, 100 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 7fc082228908d..318ccef8aeef1 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1999,6 +1999,11 @@ impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> { if let Some(note) = self.span_note { diag.span_note(note, fluent::lint_note); }; + + diag.remove_arg("ty"); + if self.inner_ty.is_some() { + diag.remove_arg("inner_ty"); + } } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 9af613978ba0f..cdd7c63738251 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -849,6 +849,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 { @@ -1261,6 +1272,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( @@ -1283,48 +1365,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); @@ -1477,69 +1518,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), diff --git a/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr b/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr index f6ac9a92cd5f0..3bc0a54a74945 100644 --- a/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr +++ b/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr @@ -6,7 +6,7 @@ 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 + = note: `#[warn(improper_c_fn_definitions)]` on by default warning: 1 warning emitted diff --git a/tests/ui/lint/lint-ctypes-fn.stderr b/tests/ui/lint/lint-ctypes-fn.stderr index 2806e29ae3529..c0d60b7b6d83e 100644 --- a/tests/ui/lint/lint-ctypes-fn.stderr +++ b/tests/ui/lint/lint-ctypes-fn.stderr @@ -163,3 +163,4 @@ LL | pub extern "C" fn used_generic5() -> Vec { = note: this struct has unspecified layout error: aborting due to 17 previous errors + diff --git a/tests/ui/lint/lint-ctypes.stderr b/tests/ui/lint/lint-ctypes.stderr index 57cda0f8e0ee2..5a97cfac0f6fc 100644 --- a/tests/ui/lint/lint-ctypes.stderr +++ b/tests/ui/lint/lint-ctypes.stderr @@ -71,7 +71,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `&dyn Bar`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:57:26 + --> $DIR/lint-ctypes.rs:69:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -150,7 +150,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:68:31 + --> $DIR/lint-ctypes.rs:80:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -168,7 +168,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/lint-ctypes.rs:85:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -193,4 +193,5 @@ LL | pub fn no_niche_b(b: Option>); = 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 17 previous errors +error: aborting due to 19 previous errors + From 175b70863b4c33641cad6938f88355d8ea48f84a Mon Sep 17 00:00:00 2001 From: niacdoial Date: Mon, 23 Dec 2024 15:14:16 +0100 Subject: [PATCH 03/19] 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 cdd7c63738251..e53ec756ddbda 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -847,6 +847,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 @@ -865,17 +867,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() { @@ -888,7 +889,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"); @@ -896,7 +903,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]; @@ -907,7 +914,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) } @@ -916,7 +924,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); @@ -926,18 +934,51 @@ 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::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), } } } @@ -1317,6 +1358,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), @@ -1525,6 +1586,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 d5dbf9a584128831419c3929790130c9686ef9cb Mon Sep 17 00:00:00 2001 From: niacdoial Date: Mon, 23 Dec 2024 15:17:49 +0100 Subject: [PATCH 04/19] 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 b2195b156982df58689acf4c14659a9f278c9613 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sun, 29 Dec 2024 20:32:34 +0100 Subject: [PATCH 05/19] 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 | 1188 +---------------- .../rustc_lint/src/types/improper_ctypes.rs | 1187 +++++++++++++++- .../ctypes.rs} | 0 .../ctypes.stderr} | 48 +- .../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} | 50 +- .../lint-fn.rs} | 0 .../lint-fn.stderr} | 40 +- .../lint-option-nonnull-unsized.rs} | 0 .../lint-option-nonnull-unsized.stderr} | 4 +- .../lint-tykind-fuzz.rs} | 4 +- .../lint-tykind-fuzz.stderr} | 99 +- .../mustpass-113436.rs} | 0 .../mustpass-113900.rs} | 0 .../mustpass-134060.rs} | 0 .../mustpass-134060.stderr} | 4 +- .../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 40 files changed, 1363 insertions(+), 1373 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-5.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-3.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} (90%) 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} (89%) 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} (98%) rename tests/ui/lint/{lint-ctypes-tykind-fuzz.stderr => improper_ctypes/lint-tykind-fuzz.stderr} (81%) 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/{improper_ctypes_definitions_ice_134060.rs => improper_ctypes/mustpass-134060.rs} (100%) rename tests/ui/lint/{improper_ctypes_definitions_ice_134060.stderr => improper_ctypes/mustpass-134060.stderr} (70%) 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 759e6c927b828..8ecfe4ba6166c 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -136,7 +136,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); @@ -214,10 +213,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. @@ -236,7 +234,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); @@ -307,7 +304,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, ) }, ) @@ -315,25 +311,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); @@ -347,7 +337,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, @@ -355,7 +345,6 @@ fn structurally_same_type_impl<'tcx>( typing_env, a_sig.output(), b_sig.output(), - ckind, ) } (ty::Tuple(..), ty::Tuple(..)) => { @@ -383,14 +372,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 e53ec756ddbda..0a0df5d43b0da 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,35 +1,28 @@ use std::iter; -use std::ops::ControlFlow; -use rustc_abi::{BackendRepr, 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, AmbiguousWidePointerComparisonsCastSuggestion, AmbiguousWidePointerComparisonsExpectSuggestion, 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; @@ -690,299 +683,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::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), - } - } -} - pub(crate) fn nonnull_optimization_guaranteed<'tcx>( tcx: TyCtxt<'tcx>, def: ty::AdtDef<'tcx>, @@ -1010,7 +710,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); @@ -1033,10 +732,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, @@ -1147,7 +846,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() { @@ -1172,7 +870,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; } @@ -1231,866 +929,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), - }; - } - - 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), - - // 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::FnPtr(hir::FnPtrTy { 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().2; - 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..7965e2fefe1ce 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::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,1170 @@ 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), + }; + } + + 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), + + // 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::FnPtr(hir::FnPtrTy { 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().2; + 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 5a97cfac0f6fc..d48c38432e818 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 `&dyn Bar`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:69:26 + --> $DIR/ctypes.rs:69:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -79,7 +79,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:70:26 + --> $DIR/ctypes.rs:70:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -88,7 +88,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:71:27 + --> $DIR/ctypes.rs:71:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -97,7 +97,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:72:25 + --> $DIR/ctypes.rs:72:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -105,26 +105,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:73:33 + --> $DIR/ctypes.rs:73: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:76:12 + --> $DIR/ctypes.rs:76:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -132,7 +132,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:77:23 + --> $DIR/ctypes.rs:77:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -141,7 +141,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:78:24 + --> $DIR/ctypes.rs:78:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -150,7 +150,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:80:31 + --> $DIR/ctypes.rs:80:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +159,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:82:27 + --> $DIR/ctypes.rs:82:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -168,7 +168,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:85:47 + --> $DIR/ctypes.rs:85:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +176,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:87:26 + --> $DIR/ctypes.rs:87:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -185,7 +185,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:89:26 + --> $DIR/ctypes.rs:89:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ 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-5.stderr b/tests/ui/lint/improper_ctypes/lint-73249-3.stderr similarity index 81% rename from tests/ui/lint/lint-ctypes-73249-5.stderr rename to tests/ui/lint/improper_ctypes/lint-73249-3.stderr index c4fa955de05ed..ebc9eb5eb8274 100644 --- a/tests/ui/lint/lint-ctypes-73249-5.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-5.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-5.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-3.stderr b/tests/ui/lint/improper_ctypes/lint-73249-5.stderr similarity index 81% rename from tests/ui/lint/lint-ctypes-73249-3.stderr rename to tests/ui/lint/improper_ctypes/lint-73249-5.stderr index e1a313a290651..484927f57fead 100644 --- a/tests/ui/lint/lint-ctypes-73249-3.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-3.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-3.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 f900f998d06cb..4659e554ee6d9 100644 --- a/tests/ui/lint/lint-ctypes-enum.rs +++ b/tests/ui/lint/improper_ctypes/lint-enum.rs @@ -53,7 +53,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 90% rename from tests/ui/lint/lint-ctypes-enum.stderr rename to tests/ui/lint/improper_ctypes/lint-enum.stderr index 40d22723309fb..d3a5d6d075aac 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:82:14 + --> $DIR/lint-enum.rs:81: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:9:1 + --> $DIR/lint-enum.rs:9: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:83:14 + --> $DIR/lint-enum.rs:82: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:12:1 + --> $DIR/lint-enum.rs:12:1 | LL | enum B { | ^^^^^^ error: `extern` block uses type `T`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:84:14 + --> $DIR/lint-enum.rs:83:14 | LL | fn tf(x: T); | ^ not FFI-safe @@ -40,13 +40,13 @@ 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:16:1 + --> $DIR/lint-enum.rs:16:1 | LL | enum T { | ^^^^^^ error: `extern` block uses type `Option>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:108:36 + --> $DIR/lint-enum.rs:107:36 | LL | fn option_transparent_union(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -55,7 +55,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:110:28 + --> $DIR/lint-enum.rs:109:28 | LL | fn option_repr_rust(x: Option>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -64,7 +64,7 @@ LL | fn option_repr_rust(x: Option>>); = note: enum has no representation hint error: `extern` block uses type `Option`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:111:21 + --> $DIR/lint-enum.rs:110:21 | LL | fn option_u8(x: Option); | ^^^^^^^^^^ not FFI-safe @@ -73,7 +73,7 @@ LL | fn option_u8(x: Option); = note: enum has no representation hint error: `extern` block uses type `Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:131:38 + --> $DIR/lint-enum.rs:130:38 | LL | fn result_transparent_union_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -82,7 +82,7 @@ LL | fn result_transparent_union_t(x: Result>, ()>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:133:30 + --> $DIR/lint-enum.rs:132:30 | LL | fn result_repr_rust_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -91,7 +91,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:137:51 + --> $DIR/lint-enum.rs:136:51 | LL | fn result_1zst_exhaustive_single_variant_t(x: Result, U>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -100,7 +100,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:139:53 + --> $DIR/lint-enum.rs:138:53 | LL | fn result_1zst_exhaustive_multiple_variant_t(x: Result, B>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -109,7 +109,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:141:51 + --> $DIR/lint-enum.rs:140:51 | LL | fn result_1zst_non_exhaustive_no_variant_t(x: Result, NonExhaustive>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -118,7 +118,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:144:49 + --> $DIR/lint-enum.rs:143:49 | LL | fn result_1zst_exhaustive_single_field_t(x: Result, Field>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -127,7 +127,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:146:30 + --> $DIR/lint-enum.rs:145:30 | LL | fn result_cascading_t(x: Result>, ()>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -136,7 +136,7 @@ LL | fn result_cascading_t(x: Result>, ()>); = note: enum has no representation hint error: `extern` block uses type `Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:167:38 + --> $DIR/lint-enum.rs:166:38 | LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -145,7 +145,7 @@ LL | fn result_transparent_union_e(x: Result<(), TransparentUnion>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:169:30 + --> $DIR/lint-enum.rs:168:30 | LL | fn result_repr_rust_e(x: Result<(), Rust>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -154,7 +154,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:173:51 + --> $DIR/lint-enum.rs:172:51 | LL | fn result_1zst_exhaustive_single_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -163,7 +163,7 @@ LL | fn result_1zst_exhaustive_single_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:175:53 + --> $DIR/lint-enum.rs:174:53 | LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -172,7 +172,7 @@ LL | fn result_1zst_exhaustive_multiple_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:177:51 + --> $DIR/lint-enum.rs:176:51 | LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -181,7 +181,7 @@ LL | fn result_1zst_non_exhaustive_no_variant_e(x: Result>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:180:49 + --> $DIR/lint-enum.rs:179:49 | LL | fn result_1zst_exhaustive_single_field_e(x: Result>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -190,7 +190,7 @@ LL | fn result_1zst_exhaustive_single_field_e(x: Result>>`, which is not FFI-safe - --> $DIR/lint-ctypes-enum.rs:182:30 + --> $DIR/lint-enum.rs:181:30 | LL | fn result_cascading_e(x: Result<(), Result<(), num::NonZero>>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -199,7 +199,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:184:27 + --> $DIR/lint-enum.rs:183: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 89% rename from tests/ui/lint/lint-ctypes-fn.stderr rename to tests/ui/lint/improper_ctypes/lint-fn.stderr index c0d60b7b6d83e..5143218c6b394 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 `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:92:33 + --> $DIR/lint-fn.rs:92:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -66,7 +66,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:95:34 + --> $DIR/lint-fn.rs:95:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -75,7 +75,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:98:32 + --> $DIR/lint-fn.rs:98:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -83,26 +83,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:101:40 + --> $DIR/lint-fn.rs:101: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:104:51 + --> $DIR/lint-fn.rs:104:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -110,7 +110,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:109:30 + --> $DIR/lint-fn.rs:109:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -119,7 +119,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:112:31 + --> $DIR/lint-fn.rs:112:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -128,7 +128,7 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `&str`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:117:38 + --> $DIR/lint-fn.rs:117:38 | LL | pub extern "C" fn transparent_str(p: TransparentStr) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -137,7 +137,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:169:43 + --> $DIR/lint-fn.rs:169:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -145,7 +145,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:182:39 + --> $DIR/lint-fn.rs:182:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -154,7 +154,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:185:41 + --> $DIR/lint-fn.rs:185: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 98% rename from tests/ui/lint/lint-ctypes-tykind-fuzz.rs rename to tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs index 27bcfcf0e9a0c..eb3081bfcf695 100644 --- a/tests/ui/lint/lint-ctypes-tykind-fuzz.rs +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs @@ -130,7 +130,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..), // Struct SomeStruct{b:p4,..}: SomeStruct, // Union @@ -180,7 +180,7 @@ fn all_ty_kinds_in_ptr( // 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..=` + nz: *const pattern_type!(u32 is 1..), // deactivated here, because this is a function *declaration* (pattern unacceptable) //SomeStruct{b: ref p4,..}: & SomeStruct, // Ptr[Union] diff --git a/tests/ui/lint/lint-ctypes-tykind-fuzz.stderr b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr similarity index 81% rename from tests/ui/lint/lint-ctypes-tykind-fuzz.stderr rename to tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr index e807d76bd5264..b65570e62785f 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 @@ -75,17 +75,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-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 + --> $DIR/lint-tykind-fuzz.rs:147:7 | LL | e3: &StructWithDyn, | ^^^^^^^^^^^^^^ not FFI-safe @@ -93,7 +84,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 +93,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 +101,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 +109,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 +117,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 +126,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 +141,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 +151,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 +160,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 @@ -178,18 +169,8 @@ LL | p: *const (u8,u8), = 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 + --> $DIR/lint-tykind-fuzz.rs:197:7 | LL | e3: *const StructWithDyn, | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -197,7 +178,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 +188,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 +196,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 +204,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 +212,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 +221,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 +230,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 +239,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 +247,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 +255,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 +263,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 +271,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 +280,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 +289,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 +298,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 +306,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 +314,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,12 +322,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-ctypes-tykind-fuzz.rs:307:6 + --> $DIR/lint-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 +error: aborting due to 36 previous errors; 1 warning emitted diff --git a/tests/ui/lint/lint-ctypes-113436.rs b/tests/ui/lint/improper_ctypes/mustpass-113436.rs similarity index 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/improper_ctypes_definitions_ice_134060.rs b/tests/ui/lint/improper_ctypes/mustpass-134060.rs similarity index 100% rename from tests/ui/lint/improper_ctypes_definitions_ice_134060.rs rename to tests/ui/lint/improper_ctypes/mustpass-134060.rs diff --git a/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr b/tests/ui/lint/improper_ctypes/mustpass-134060.stderr similarity index 70% rename from tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr rename to tests/ui/lint/improper_ctypes/mustpass-134060.stderr index 3bc0a54a74945..791b2f7370983 100644 --- a/tests/ui/lint/improper_ctypes_definitions_ice_134060.stderr +++ b/tests/ui/lint/improper_ctypes/mustpass-134060.stderr @@ -1,12 +1,12 @@ warning: `extern` fn uses type `()`, which is not FFI-safe - --> $DIR/improper_ctypes_definitions_ice_134060.rs:11:34 + --> $DIR/mustpass-134060.rs:11:34 | LL | extern "C" fn foo_(&self, _: ()) -> i64 { | ^^ not FFI-safe | = help: consider using a struct instead = note: tuples have unspecified layout - = note: `#[warn(improper_c_fn_definitions)]` on by default + = note: `#[warn(improper_ctypes_definitions)]` on by default warning: 1 warning emitted 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 2dcfd51abbead7590b25ddb6365c990d75a7da9b Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sun, 5 Jan 2025 11:17:28 +0100 Subject: [PATCH 06/19] lint ImproperCTypes: properly separate the lint generation from the type visitation steps [does not pass tests] --- .../rustc_lint/src/types/improper_ctypes.rs | 1135 ++++++++++------- .../cmse-nonsecure-call/via-registers.rs | 1 + .../ui/lint/improper_ctypes/lint-cstr.stderr | 8 +- 3 files changed, 663 insertions(+), 481 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 7965e2fefe1ce..40642d3191d2a 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -10,8 +10,8 @@ use rustc_hir::def::CtorKind; use rustc_hir::intravisit::VisitorExt; use rustc_middle::bug; use rustc_middle::ty::{ - self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt, + self, Adt, AdtKind, Binder, FnSig, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, }; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; @@ -22,6 +22,27 @@ use super::repr_nullable_ptr; use crate::lints::{ImproperCTypes, ImproperCTypesLayer, UsesPowerAlignment}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; +type Sig<'tcx> = Binder<'tcx, FnSig<'tcx>>; + +// 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 +87,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 +160,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 +174,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 +192,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 +248,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 +390,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 +426,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 +435,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 +456,177 @@ 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), + }; + } + + 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,116 +637,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), - }; - } - - 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) } } } @@ -552,7 +682,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), // Primitive types with a stable representation. ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe, @@ -573,11 +703,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() { @@ -589,55 +727,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) } } @@ -652,7 +776,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 } @@ -673,34 +797,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>; @@ -718,61 +815,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, + 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, - sp: Span, ty: Ty<'tcx>, - is_static: bool, - is_return_type: bool, + ) -> 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>, ) { - if self.check_for_opaque_ty(sp, ty) { - // We've already emitted an error due to an opaque type. - return; + 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().2; + 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 = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty); + /// 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>, + ) { + 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::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind + && !abi.is_rustic_abi() + { + self.spans.push(ty.span); + } - // 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; + hir::intravisit::walk_ty(self, ty) + } } - // 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; + 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(); + // } + } + + 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); + + 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); + } + + 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( @@ -785,6 +1093,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { help: None, inner_ty: None, }], + fn_mode, ); } FfiResult::FfiUnsafe { ty, reason, help } => { @@ -798,11 +1107,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 @@ -847,196 +1157,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::FnPtr(hir::FnPtrTy { 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().2; - 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): /// @@ -1050,8 +1231,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(), ); @@ -1061,7 +1242,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(..) => {} @@ -1079,8 +1260,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(), ); @@ -1103,11 +1284,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 419d26875bcd9..8d7ed003f0216 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 "cmse-nonsecure-call" fn() -> u32, f2: extern "cmse-nonsecure-call" fn() -> u64, diff --git a/tests/ui/lint/improper_ctypes/lint-cstr.stderr b/tests/ui/lint/improper_ctypes/lint-cstr.stderr index a50d5db4aacc1..7972399eac9d5 100644 --- a/tests/ui/lint/improper_ctypes/lint-cstr.stderr +++ b/tests/ui/lint/improper_ctypes/lint-cstr.stderr @@ -47,8 +47,8 @@ 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 + = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = note: `CStr`/`CString` do not have a guaranteed layout error: `extern` block uses type `CString`, which is not FFI-safe --> $DIR/lint-cstr.rs:24:47 @@ -57,8 +57,8 @@ 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 + = help: consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = note: `CStr`/`CString` do not have a guaranteed layout error: `extern` fn uses type `&CStr`, which is not FFI-safe --> $DIR/lint-cstr.rs:29:37 From 7f97a9258bd8922d1527cdc2b9378457ab3d0592 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Fri, 10 Jan 2025 00:37:54 +0100 Subject: [PATCH 07/19] 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 | 581 +++++++++++------- tests/ui/lint/improper_ctypes/ctypes.rs | 11 + tests/ui/lint/improper_ctypes/ctypes.stderr | 78 ++- .../lint/improper_ctypes/lint-113436-1.stderr | 12 + .../lint/improper_ctypes/lint-73249-3.stderr | 6 + tests/ui/lint/improper_ctypes/lint-cstr.rs | 14 +- .../ui/lint/improper_ctypes/lint-cstr.stderr | 47 +- .../repr-rust-is-undefined.stderr | 18 + 9 files changed, 519 insertions(+), 262 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index d87e7ad71df20..9a495ad2fa880 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -380,8 +380,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 @@ -408,6 +417,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 40642d3191d2a..e70df1716da5f 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -90,22 +90,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 { @@ -257,22 +364,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 @@ -297,21 +395,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> { @@ -331,18 +414,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(); @@ -351,17 +432,74 @@ 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::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, @@ -370,8 +508,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 @@ -394,18 +559,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 => { @@ -426,24 +584,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 { @@ -451,7 +597,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) } } } @@ -470,12 +616,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`). @@ -485,23 +637,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 } @@ -515,50 +667,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) @@ -575,7 +726,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 @@ -586,28 +737,28 @@ 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), + ); } 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 @@ -627,6 +778,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn visit_type( &mut self, state: CTypesVisitorState, + outer_ty: Option>, ty: Ty<'tcx>, ) -> FfiResult<'tcx> { use FfiResult::*; @@ -651,21 +803,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 => { @@ -674,48 +821,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), + + // 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() { @@ -734,27 +902,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 @@ -769,9 +937,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. @@ -819,11 +989,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 } @@ -840,7 +1010,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( @@ -1096,64 +1266,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 bf7af490f17b0..abf0bd0741442 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -53,6 +53,12 @@ pub struct UnsizedStructBecauseDyn { 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); @@ -80,6 +86,11 @@ 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` + // (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` diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index d48c38432e818..b62c63a0a844d 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:66:28 | LL | pub fn ptr_type1(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -19,7 +19,7 @@ LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ error: `extern` block uses type `Foo`, which is not FFI-safe - --> $DIR/ctypes.rs:61:28 + --> $DIR/ctypes.rs:67:28 | LL | pub fn ptr_type2(size: *const Foo); | ^^^^^^^^^^ not FFI-safe @@ -34,7 +34,7 @@ LL | pub struct Foo; | ^^^^^^^^^^^^^^ error: `extern` block uses type `((),)`, which is not FFI-safe - --> $DIR/ctypes.rs:63:25 + --> $DIR/ctypes.rs:69: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:70: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:71: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:74: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 `&dyn Bar`, which is not FFI-safe - --> $DIR/ctypes.rs:69:26 + --> $DIR/ctypes.rs:75:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -79,7 +79,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:70:26 + --> $DIR/ctypes.rs:76:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -88,7 +88,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:71:27 + --> $DIR/ctypes.rs:77:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -97,7 +97,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:72:25 + --> $DIR/ctypes.rs:78:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -111,20 +111,20 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/ctypes.rs:73:33 + --> $DIR/ctypes.rs:79: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:63:1 | LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/ctypes.rs:76:12 + --> $DIR/ctypes.rs:82:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -132,7 +132,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:77:23 + --> $DIR/ctypes.rs:83:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -141,7 +141,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:78:24 + --> $DIR/ctypes.rs:84:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -150,7 +150,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/ctypes.rs:80:31 + --> $DIR/ctypes.rs:86:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +159,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:82:27 + --> $DIR/ctypes.rs:88:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -167,8 +167,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:89: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:89: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:89: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:57: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:85:47 + --> $DIR/ctypes.rs:96:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -176,7 +212,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:87:26 + --> $DIR/ctypes.rs:98:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -185,7 +221,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:89:26 + --> $DIR/ctypes.rs:100:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -193,5 +229,5 @@ LL | pub fn no_niche_b(b: Option>); = 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 19 previous errors +error: aborting due to 22 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..ec13d7f3f62c4 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:16:1 + | +LL | pub struct A { + | ^^^^^^^^^^^^ = note: opaque types have no C equivalent note: the lint level is defined here --> $DIR/lint-73249-3.rs:2:9 diff --git a/tests/ui/lint/improper_ctypes/lint-cstr.rs b/tests/ui/lint/improper_ctypes/lint-cstr.rs index c4de5a44a9623..c1a3f362529ca 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); //~^ 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); //~^ 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()` +//~| HELP consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, extern "C" fn rust_no_special_help_for_mut_cstring(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_no_special_help_for_mut_cstring_ref(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 7972399eac9d5..aaac128f3caf0 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,8 +41,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()` + = 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 @@ -46,8 +50,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 passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = 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 @@ -56,8 +59,7 @@ 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 passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` + = 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 @@ -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:55 + | +LL | extern "C" fn rust_no_special_help_for_mut_cstring(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:59 + | +LL | extern "C" fn rust_no_special_help_for_mut_cstring_ref(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/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 df85b22eed72c41762fe555bacd23740dcfbd017 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sat, 18 Jan 2025 18:30:21 +0100 Subject: [PATCH 08/19] 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 | 545 ++++++++++-------- 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 | 5 + tests/ui/lint/improper_ctypes/ctypes.stderr | 67 ++- tests/ui/lint/improper_ctypes/lint-cstr.rs | 14 +- .../ui/lint/improper_ctypes/lint-cstr.stderr | 24 +- tests/ui/lint/improper_ctypes/lint-fn.rs | 13 +- tests/ui/lint/improper_ctypes/lint-fn.stderr | 78 ++- .../lint/improper_ctypes/lint-tykind-fuzz.rs | 121 ++-- .../improper_ctypes/lint-tykind-fuzz.stderr | 247 +++++--- .../improper_ctypes/mustpass-134060.stderr | 14 +- 15 files changed, 715 insertions(+), 467 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 9a495ad2fa880..1a56e5b92084e 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -411,8 +411,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 0a0df5d43b0da..c3930f6149f1a 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; @@ -929,6 +929,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 e70df1716da5f..049865bc0a910 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -26,9 +26,8 @@ type Sig<'tcx> = Binder<'tcx, FnSig<'tcx>>; // 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) @@ -42,8 +41,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. @@ -51,10 +51,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 @@ -63,15 +63,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 { @@ -90,7 +90,7 @@ struct ImproperCTypesVisitor<'a, 'tcx> { cache: FxHashSet>, } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] struct FfiUnsafeReason<'tcx> { ty: Ty<'tcx>, reason: DiagMessage, @@ -98,7 +98,7 @@ struct FfiUnsafeReason<'tcx> { inner: Option>>, } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] enum FfiResult<'tcx> { FfiSafe, FfiPhantom(Ty<'tcx>), @@ -108,7 +108,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, @@ -117,16 +117,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, } } @@ -134,22 +135,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) { @@ -160,7 +162,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; @@ -174,13 +176,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 { @@ -191,15 +193,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); - }, + } } } } @@ -212,7 +214,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 { @@ -266,10 +267,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"); @@ -278,13 +277,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 @@ -356,10 +354,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 @@ -370,7 +367,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 @@ -379,7 +376,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 @@ -390,23 +387,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); @@ -444,66 +449,51 @@ 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::Uint(..) | ty::Float(..) => - FfiResult::FfiSafe, - _ => bug!("visit_numeric is to be called with numeric (int, float) types"), - } + return FfiResult::FfiSafe; + // match ty.kind() { + // 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, @@ -511,9 +501,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 { @@ -522,7 +514,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 { @@ -537,7 +529,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(...) {...}`) @@ -559,11 +556,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 => { @@ -584,7 +583,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() { @@ -597,9 +596,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". @@ -644,7 +670,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 + } } } @@ -666,7 +695,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( @@ -727,23 +756,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); } @@ -756,21 +780,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 @@ -796,7 +833,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); @@ -808,16 +851,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), } } @@ -827,9 +868,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), @@ -843,11 +903,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, @@ -874,7 +932,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { false }; - if empty_and_safe{ + if empty_and_safe { FfiSafe } else { FfiResult::new_with_reason( @@ -883,7 +941,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { Some(fluent::lint_improper_ctypes_tuple_help), ) } - }, + } ty::RawPtr(ty, _) if match ty.kind() { @@ -895,10 +953,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, _) => { @@ -937,11 +1001,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. @@ -985,25 +1047,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) { @@ -1013,11 +1064,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) { @@ -1029,23 +1076,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>, @@ -1153,22 +1204,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(); } @@ -1178,19 +1229,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 { @@ -1198,15 +1243,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); } @@ -1218,13 +1258,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, }; @@ -1233,8 +1272,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, }; @@ -1243,13 +1282,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) => { @@ -1266,35 +1299,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); } } } @@ -1326,14 +1365,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, _, _) => { @@ -1341,7 +1378,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); @@ -1368,7 +1405,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(), @@ -1397,7 +1434,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(), @@ -1421,9 +1458,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 abf0bd0741442..9a4d6df82f6d7 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -1,5 +1,7 @@ #![feature(rustc_private)] #![feature(extern_types)] +#![feature(pattern_types, rustc_attrs)] +#![feature(pattern_type_macro)] #![allow(private_interfaces)] #![deny(improper_ctypes)] @@ -8,6 +10,7 @@ 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 { } @@ -72,6 +75,8 @@ extern "C" { 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 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)` diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index b62c63a0a844d..9f4fd51e022a1 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:66: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:67: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:69: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:70: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:71: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:74:25 + --> $DIR/ctypes.rs:77:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -70,8 +70,17 @@ LL | pub fn char_type(p: char); = help: consider using `u32` or `libc::wchar_t` instead = note: the `char` type has no C equivalent +error: `extern` block uses type `(u32) is 1..`, which is not FFI-safe + --> $DIR/ctypes.rs:78: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:75:26 + --> $DIR/ctypes.rs:80:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -79,7 +88,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:76:26 + --> $DIR/ctypes.rs:81:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -88,7 +97,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:77:27 + --> $DIR/ctypes.rs:82:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -97,7 +106,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:78:25 + --> $DIR/ctypes.rs:83:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -105,26 +114,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:79:33 + --> $DIR/ctypes.rs:84: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:63: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:82:12 + --> $DIR/ctypes.rs:87:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -132,7 +141,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:83:23 + --> $DIR/ctypes.rs:88:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -141,7 +150,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:84:24 + --> $DIR/ctypes.rs:89:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -150,7 +159,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `&str`, which is not FFI-safe - --> $DIR/ctypes.rs:86:31 + --> $DIR/ctypes.rs:91:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +168,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:88:27 + --> $DIR/ctypes.rs:93:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -168,7 +177,7 @@ 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:89:36 + --> $DIR/ctypes.rs:94:36 | LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -178,7 +187,7 @@ LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Deb = note: the `char` type has no C equivalent error: `extern` block uses type `&dyn Debug`, which is not FFI-safe - --> $DIR/ctypes.rs:89:36 + --> $DIR/ctypes.rs:94:36 | LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -187,7 +196,7 @@ LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Deb = 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:89:36 + --> $DIR/ctypes.rs:94:36 | LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: Box>)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -196,7 +205,7 @@ LL | pub fn multi_errors_per_arg(f: for<'a> extern "C" fn(a:char, b:&dyn Deb = 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:57:1 + --> $DIR/ctypes.rs:60:1 | LL | pub struct TwoBadTypes<'a> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -204,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:96:47 + --> $DIR/ctypes.rs:101:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -212,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:98:26 + --> $DIR/ctypes.rs:103:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -221,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:100:26 + --> $DIR/ctypes.rs:105:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -229,5 +238,5 @@ LL | pub fn no_niche_b(b: Option>); = 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 22 previous errors +error: aborting due to 23 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-cstr.rs b/tests/ui/lint/improper_ctypes/lint-cstr.rs index c1a3f362529ca..aa7afd12ee587 100644 --- a/tests/ui/lint/improper_ctypes/lint-cstr.rs +++ b/tests/ui/lint/improper_ctypes/lint-cstr.rs @@ -6,22 +6,22 @@ use std::ffi::{CStr, CString}; extern "C" { fn take_cstr(s: CStr); //~^ ERROR `extern` block uses type `CStr`, which is not FFI-safe - //~| HELP consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, + //~| HELP consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead fn take_cstr_ref(s: &CStr); //~^ ERROR `extern` block uses type `&CStr`, which is not FFI-safe //~| HELP consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()` fn take_cstring(s: CString); //~^ ERROR `extern` block uses type `CString`, which is not FFI-safe - //~| HELP consider passing a `*const std::ffi::c_char` or `*mut std::ffi::c_char` instead, + //~| 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 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 passing a `*mut std::ffi::c_char` instead, and use `CString::into_raw()` then `CString::from_raw()` or a dedicated buffer } @@ -31,10 +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` or `*mut std::ffi::c_char` instead, -extern "C" fn rust_no_special_help_for_mut_cstring(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_no_special_help_for_mut_cstring_ref(s: &mut CString) {} +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 aaac128f3caf0..ca5248b8ea888 100644 --- a/tests/ui/lint/improper_ctypes/lint-cstr.stderr +++ b/tests/ui/lint/improper_ctypes/lint-cstr.stderr @@ -45,19 +45,19 @@ 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-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 | = 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 | = 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 @@ -89,19 +89,19 @@ LL | extern "C" fn rust_take_cstring(s: CString) {} = 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:55 + --> $DIR/lint-cstr.rs:35:44 | -LL | extern "C" fn rust_no_special_help_for_mut_cstring(s: *mut CString) {} - | ^^^^^^^^^^^^ not FFI-safe +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:59 + --> $DIR/lint-cstr.rs:38:44 | -LL | extern "C" fn rust_no_special_help_for_mut_cstring_ref(s: &mut CString) {} - | ^^^^^^^^^^^^ not FFI-safe +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 diff --git a/tests/ui/lint/improper_ctypes/lint-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs index b39178c98589a..385463847b4b3 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]>` @@ -121,11 +123,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() -> ()) { } @@ -141,7 +147,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 5143218c6b394..4efde5d62ad81 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 `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-fn.rs:92:33 + --> $DIR/lint-fn.rs:94:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -66,7 +76,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:95:34 + --> $DIR/lint-fn.rs:97:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -75,7 +85,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:98:32 + --> $DIR/lint-fn.rs:100:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -89,7 +99,7 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:101:40 + --> $DIR/lint-fn.rs:103:40 | LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -102,7 +112,7 @@ LL | pub struct ZeroSizeWithPhantomData(PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:104:51 + --> $DIR/lint-fn.rs:106:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -110,7 +120,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:109:30 + --> $DIR/lint-fn.rs:111:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -119,7 +129,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:112:31 + --> $DIR/lint-fn.rs:114:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -128,7 +138,7 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `&str`, which is not FFI-safe - --> $DIR/lint-fn.rs:117:38 + --> $DIR/lint-fn.rs:119:38 | LL | pub extern "C" fn transparent_str(p: TransparentStr) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -136,8 +146,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:126: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:131: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:150: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:169:43 + --> $DIR/lint-fn.rs:176:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -145,7 +191,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:182:39 + --> $DIR/lint-fn.rs:189:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -154,7 +200,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:185:41 + --> $DIR/lint-fn.rs:192:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe @@ -162,5 +208,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 17 previous errors +error: aborting due to 21 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 eb3081bfcf695..07a33095e9c13 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 @@ -130,7 +130,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..), + nz: pattern_type!(u32 is 1..), //~ ERROR: uses type `(u32) is 1..` // Struct SomeStruct{b:p4,..}: SomeStruct, // Union @@ -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), //~ ERROR: uses type `(*const u8, *const u8)` // Pat nz: *const pattern_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), // 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 b65570e62785f..50f472363899a 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -7,6 +7,41 @@ 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:34:7 + | +LL | &self + | ^^^^^ 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 +note: the lint level is defined here + --> $DIR/lint-tykind-fuzz.rs:5:25 + | +LL | #![deny(improper_ctypes,improper_ctypes_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `extern` fn uses type `Box`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:90:14 + | +LL | self: 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 `&Self`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:97:8 + | +LL | &self + | ^^^^^ not FFI-safe + | + = help: consider using a raw pointer, or wrapping `&Self` 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 `String`, which is not FFI-safe --> $DIR/lint-tykind-fuzz.rs:119:5 | @@ -15,11 +50,6 @@ 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-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-tykind-fuzz.rs:121:6 @@ -75,6 +105,15 @@ 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 + | +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 + error: `extern` fn uses type `&StructWithDyn`, which is not FFI-safe --> $DIR/lint-tykind-fuzz.rs:147:7 | @@ -116,23 +155,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 @@ -140,18 +164,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 @@ -159,60 +173,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 `*const StructWithDyn`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:197:7 +error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:184:29 | -LL | e3: *const StructWithDyn, - | ^^^^^^^^^^^^^^^^^^^^ not FFI-safe +LL | SomeStruct{b: ref p4,..}: & SomeStruct, + | ^^^^^^^^^^^^ not FFI-safe | - = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer + = 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 `fn(u8) -> u8`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:202:7 +error: `extern` fn uses type `*const StructWithDyn`, which is not FFI-safe + --> $DIR/lint-tykind-fuzz.rs:196:7 | -LL | f2: *const fn(u8)->u8, - | ^^^^^^^^^^^^^^^^^ not FFI-safe +LL | e3: *const StructWithDyn, + | ^^^^^^^^^^^^^^^^^^^^ 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 + = note: this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer -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 @@ -220,8 +248,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 @@ -229,43 +267,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 @@ -279,6 +328,16 @@ 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 | @@ -297,6 +356,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 | @@ -305,8 +374,28 @@ 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 @@ -314,7 +403,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 @@ -322,12 +411,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 36 previous errors; 1 warning emitted +error: aborting due to 45 previous errors; 1 warning emitted diff --git a/tests/ui/lint/improper_ctypes/mustpass-134060.stderr b/tests/ui/lint/improper_ctypes/mustpass-134060.stderr index 791b2f7370983..51dcc0cd4dccc 100644 --- a/tests/ui/lint/improper_ctypes/mustpass-134060.stderr +++ b/tests/ui/lint/improper_ctypes/mustpass-134060.stderr @@ -1,3 +1,14 @@ +warning: `extern` fn uses type `&Self`, which is not FFI-safe + --> $DIR/mustpass-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 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 + = note: `#[warn(improper_ctypes_definitions)]` on by default + warning: `extern` fn uses type `()`, which is not FFI-safe --> $DIR/mustpass-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 From efe569569fa61b2307aa6defbf4912548dff7c69 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sat, 18 Jan 2025 21:17:39 +0100 Subject: [PATCH 09/19] lint ImproperCTypes: smoothing out some nits and un-tidy-ness --- compiler/rustc_lint/messages.ftl | 6 +- .../rustc_lint/src/types/improper_ctypes.rs | 165 +++++++----------- tests/ui/lint/improper_ctypes/ctypes.rs | 10 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 24 +-- 4 files changed, 86 insertions(+), 119 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 1a56e5b92084e..b4e8257c27aa3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -422,13 +422,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 049865bc0a910..a33f4caaeaa03 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -147,61 +147,28 @@ 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(self_reasons), Self::FfiUnsafe(mut other_reasons)) => { + self_reasons.append(&mut other_reasons); } - } else { - if let Self::FfiUnsafe(_) = self { - return; + (Self::FfiUnsafe(_), _) => { + // nothing to do } - 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); - } + (myself, other @ Self::FfiUnsafe(_)) => { + *myself = other; + } + (Self::FfiPhantom(ty1), Self::FfiPhantom(ty2)) => { + debug!("whoops, both FfiPhantom: self({:?}) += other({:?})", ty1, ty2); + } + (myself @ Self::FfiSafe, other @ Self::FfiPhantom(_)) => { + *myself = other; + } + (_, Self::FfiSafe) => { + // nothing to do } } } @@ -214,7 +181,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) @@ -353,39 +320,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()); @@ -393,11 +372,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 @@ -410,7 +389,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()); @@ -532,7 +511,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 => { @@ -581,7 +560,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) } @@ -601,6 +580,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, @@ -756,18 +736,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); } @@ -801,7 +777,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() }) @@ -846,11 +822,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()) { @@ -1097,10 +1072,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: @@ -1131,10 +1103,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 @@ -1218,7 +1187,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(); } @@ -1306,12 +1275,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()); @@ -1374,7 +1339,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, @@ -1416,7 +1381,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 9a4d6df82f6d7..3524ca2af979d 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -91,11 +91,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 9f4fd51e022a1..dd43b2e268525 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -177,29 +177,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:94:36 + --> $DIR/ctypes.rs:95: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:94:36 + --> $DIR/ctypes.rs:95: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:94:36 + --> $DIR/ctypes.rs:95: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 @@ -213,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:101:47 + --> $DIR/ctypes.rs:103:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -221,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:103:26 + --> $DIR/ctypes.rs:105:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -230,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:105:26 + --> $DIR/ctypes.rs:107:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe From a37e0c5c258c042027583ee446ae3e51769c4d31 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Fri, 23 May 2025 23:12:57 +0200 Subject: [PATCH 10/19] lint ImproperCTypes: add recursion limit --- .../rustc_lint/src/types/improper_ctypes.rs | 92 +++++++++++++------ tests/crashes/130310.rs | 15 --- .../lint/improper_ctypes/mustpass-130310.rs | 18 ++++ 3 files changed, 81 insertions(+), 44 deletions(-) delete mode 100644 tests/crashes/130310.rs create mode 100644 tests/ui/lint/improper_ctypes/mustpass-130310.rs diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index a33f4caaeaa03..db7c3cf39e132 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::iter; use std::ops::ControlFlow; @@ -85,11 +86,6 @@ enum CItemKind { Definition, } -struct ImproperCTypesVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - cache: FxHashSet>, -} - #[derive(Clone, Debug)] struct FfiUnsafeReason<'tcx> { ty: Ty<'tcx>, @@ -388,9 +384,52 @@ impl CTypesVisitorState { } } +/// visitor structure responsible for checking the actual FFI-safety +/// of a given type +struct ImproperCTypesVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// to prevent problems with recursive types, add a types-in-check cache + /// and a depth counter + recursion_limiter: RefCell<(FxHashSet>, usize)>, +} + +/// structure similar to a mutex guard, allocated for each type in-check +/// to let the ImproperCTypesVisitor know the current depth of the checking process +struct ImproperCTypesVisitorDepthGuard<'a, 'tcx, 'v>(&'v ImproperCTypesVisitor<'a, 'tcx>); + +impl<'a, 'tcx, 'v> Drop for ImproperCTypesVisitorDepthGuard<'a, 'tcx, 'v> { + fn drop(&mut self) { + let mut limiter_guard = self.0.recursion_limiter.borrow_mut(); + let (_, ref mut depth) = *limiter_guard; + *depth -= 1; + } +} + impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { cx, recursion_limiter: RefCell::new((FxHashSet::default(), 0)) } + } + + /// Protect against infinite recursion, for example + /// `struct S(*mut S);`, or issue #130310. + fn can_enter_type<'v>( + &'v self, + ty: Ty<'tcx>, + ) -> Result, FfiResult<'tcx>> { + // panic unlikely: this non-recursive function is the only place that + // borrows the refcell, outside of ImproperCTypesVisitorDepthGuard::drop() + let mut limiter_guard = self.recursion_limiter.borrow_mut(); + let (ref mut cache, ref mut depth) = *limiter_guard; + if (!cache.insert(ty)) || *depth >= 1024 { + Err(FfiResult::FfiSafe) + } else { + *depth += 1; + Ok(ImproperCTypesVisitorDepthGuard(self)) + } + } + /// 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(&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); @@ -428,7 +467,7 @@ 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(&self, _ty: Ty<'tcx>) -> FfiResult<'tcx> { // FIXME: for now, this is very incomplete, and seems to assume a x86_64 target return FfiResult::FfiSafe; // match ty.kind() { @@ -439,7 +478,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } /// Return the right help for Cstring and Cstr-linked unsafety - fn visit_cstr(&mut self, outer_ty: Option>, ty: Ty<'tcx>) -> FfiResult<'tcx> { + fn visit_cstr(&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()), @@ -470,7 +509,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Checks if the given indirection (box,ref,pointer) is "ffi-safe" fn visit_indirection( - &mut self, + &self, state: CTypesVisitorState, outer_ty: Option>, ty: Ty<'tcx>, @@ -610,7 +649,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Checks if the given `VariantDef`'s field types are "ffi-safe". fn visit_variant_fields( - &mut self, + &self, state: CTypesVisitorState, ty: Ty<'tcx>, def: ty::AdtDef<'tcx>, @@ -669,7 +708,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } fn visit_struct_union( - &mut self, + &self, state: CTypesVisitorState, ty: Ty<'tcx>, def: ty::AdtDef<'tcx>, @@ -725,7 +764,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } fn visit_enum( - &mut self, + &self, state: CTypesVisitorState, ty: Ty<'tcx>, def: ty::AdtDef<'tcx>, @@ -789,23 +828,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Checks if the given type is "ffi-safe" (has a stable, well-defined /// representation which can be exported to C code). fn visit_type( - &mut self, + &self, state: CTypesVisitorState, outer_ty: Option>, ty: Ty<'tcx>, ) -> FfiResult<'tcx> { use FfiResult::*; + let _depth_guard = match self.can_enter_type(ty) { + Ok(guard) => guard, + Err(ffi_res) => return ffi_res, + }; let tcx = self.cx.tcx; - // Protect against infinite recursion, for example - // `struct S(*mut S);`. - // FIXME: A recursion limit is necessary as well, for irregular - // recursive types. - if !self.cache.insert(ty) { - return FfiSafe; - } - match *ty.kind() { ty::Adt(def, args) => { if let Some(inner_ty) = ty.boxed_ty() { @@ -1004,7 +1039,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn check_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + fn check_for_opaque_ty(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> { struct ProhibitOpaqueTypes; impl<'tcx> ty::TypeVisitor> for ProhibitOpaqueTypes { type Result = ControlFlow>; @@ -1029,7 +1064,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn check_for_type(&mut self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { + fn check_for_type(&self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { let ty = normalize_if_possible(self.cx, ty); match self.check_for_opaque_ty(ty) { @@ -1039,7 +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(&self, mode: CItemKind, ty: Ty<'tcx>) -> FfiResult<'tcx> { let ty = normalize_if_possible(self.cx, ty); match self.check_for_opaque_ty(ty) { @@ -1181,8 +1216,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { 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 visitor = ImproperCTypesVisitor::new(self.cx); let ffi_res = visitor.check_for_fnptr(fn_mode, fn_ptr_ty); (span, ffi_res) }) @@ -1215,7 +1249,7 @@ 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) { let ty = self.cx.tcx.type_of(id).instantiate_identity(); - let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() }; + let visitor = ImproperCTypesVisitor::new(self.cx); let ffi_res = visitor.check_for_type(CTypesVisitorState::StaticTy, ty); self.process_ffi_result(span, ffi_res, CItemKind::Declaration); } @@ -1231,7 +1265,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> { 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 visitor = ImproperCTypesVisitor::new(self.cx); let visit_state = match fn_mode { CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition, CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration, @@ -1241,7 +1275,7 @@ 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 visitor = ImproperCTypesVisitor::new(self.cx); let visit_state = match fn_mode { CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition, CItemKind::Declaration => CTypesVisitorState::ReturnTyInDeclaration, diff --git a/tests/crashes/130310.rs b/tests/crashes/130310.rs deleted file mode 100644 index d59dd39983c78..0000000000000 --- a/tests/crashes/130310.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: rust-lang/rust#130310 - -use std::marker::PhantomData; - -#[repr(C)] -struct A { - a: *const A>, - p: PhantomData, -} - -extern "C" { - fn f(a: *const A<()>); -} - -fn main() {} diff --git a/tests/ui/lint/improper_ctypes/mustpass-130310.rs b/tests/ui/lint/improper_ctypes/mustpass-130310.rs new file mode 100644 index 0000000000000..7da47a5a1f0e9 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/mustpass-130310.rs @@ -0,0 +1,18 @@ +//@ check-pass + +//! this test checks that irregular recursive types do not cause stack overflow in ImproperCTypes + +use std::marker::PhantomData; + +#[repr(C)] +struct A { + a: *const A>, // without a recursion limit, checking this ends up creating checks for + // infinitely deep types the likes of `A>>>>>` + p: PhantomData, +} + +extern "C" { + fn f(a: *const A<()>); +} + +fn main() {} From cde6ad5d79fd9c99a56ac44c814ca47b585604ba Mon Sep 17 00:00:00 2001 From: niacdoial Date: Sun, 4 May 2025 16:07:41 +0200 Subject: [PATCH 11/19] 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 | 12 +- compiler/rustc_lint/src/types.rs | 25 +- .../rustc_lint/src/types/improper_ctypes.rs | 1087 ++++++++++++----- compiler/rustc_llvm/src/lib.rs | 3 +- library/alloc/src/boxed.rs | 1 + library/alloc/src/collections/btree/node.rs | 1 + .../compiler-builtins/src/lib.rs | 6 +- library/coretests/tests/mem.rs | 1 + library/panic_abort/src/lib.rs | 3 +- library/panic_unwind/src/gcc.rs | 2 + library/panic_unwind/src/lib.rs | 5 +- library/proc_macro/src/bridge/client.rs | 2 + library/std/src/panicking.rs | 3 +- .../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 +- .../ui/abi/numbers-arithmetic/float-struct.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-function-shim.rs | 2 +- tests/ui/asm/naked-functions-ffi.stderr | 2 +- .../asm/naked-functions-unused.aarch64.stderr | 73 +- tests/ui/asm/naked-functions-unused.rs | 9 +- .../asm/naked-functions-unused.x86_64.stderr | 73 +- tests/ui/asm/named-asm-labels.rs | 2 +- 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 | 25 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 169 +-- .../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 | 21 +- 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 +- tests/ui/lint/improper_ctypes/lint-fn.rs | 17 +- tests/ui/lint/improper_ctypes/lint-fn.stderr | 126 +- .../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 | 93 +- .../improper_ctypes/lint-tykind-fuzz.stderr | 269 ++-- .../lint/improper_ctypes/mustpass-113436.rs | 2 +- .../lint/improper_ctypes/mustpass-134060.rs | 1 + .../improper_ctypes/mustpass-134060.stderr | 4 +- .../improper_ctypes/repr-rust-is-undefined.rs | 9 +- .../repr-rust-is-undefined.stderr | 74 +- tests/ui/lint/unused/unused-attr-duplicate.rs | 2 +- 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 +- 125 files changed, 2279 insertions(+), 866 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 5d83066cffb88..6685cd89f518a 100644 --- a/compiler/rustc_codegen_cranelift/example/std_example.rs +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -209,7 +209,7 @@ fn rust_call_abi() { struct I64X2([i64; 2]); #[cfg_attr(target_arch = "s390x", allow(dead_code))] -#[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 b4e8257c27aa3..b4dcace5d2dc3 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 @@ -416,35 +416,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 f06757b3c2379..a0796af6de76b 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -195,8 +195,7 @@ late_lint_methods!( DefaultCouldBeDerived: DefaultCouldBeDerived::default(), DerefIntoDynSupertrait: DerefIntoDynSupertrait, DropForgetUseless: DropForgetUseless, - ImproperCTypesDeclarations: ImproperCTypesDeclarations, - ImproperCTypesDefinitions: ImproperCTypesDefinitions, + ImproperCTypesLint: ImproperCTypesLint, InvalidFromUtf8: InvalidFromUtf8, VariantSizeDifferences: VariantSizeDifferences, PathStatements: PathStatements, @@ -337,6 +336,14 @@ fn register_builtins(store: &mut LintStore) { REFINING_IMPL_TRAIT_INTERNAL ); + add_lint_group!( + "improper_c_boundaries", + IMPROPER_C_CALLBACKS, + IMPROPER_C_FN_DEFINITIONS, + IMPROPER_CTYPE_DEFINITIONS, + IMPROPER_CTYPES + ); + add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024); add_lint_group!( @@ -364,6 +371,7 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("static_mut_ref", "static_mut_refs"); store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); store.register_renamed("elided_named_lifetimes", "mismatched_lifetime_syntaxes"); + 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 c3930f6149f1a..8f9e990265d24 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, @@ -705,6 +708,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 db7c3cf39e132..81e727b4bd377 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -1,17 +1,17 @@ use std::cell::RefCell; +use std::cmp::{Eq, PartialEq}; use std::iter; use std::ops::ControlFlow; use rustc_abi::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, Binder, FnSig, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, + self, Adt, AdtDef, AdtKind, Binder, FnSig, 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<'tcx, FnSig<'tcx>>; -// 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,31 +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, + /// 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, @@ -115,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, @@ -131,14 +182,26 @@ 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 } } @@ -148,8 +211,8 @@ 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(self_reasons), Self::FfiUnsafe(mut other_reasons)) => { - self_reasons.append(&mut other_reasons); + (Self::FfiUnsafe(myself), Self::FfiUnsafe(mut other_reasons)) => { + myself.append(&mut other_reasons); } (Self::FfiUnsafe(_), _) => { // nothing to do @@ -208,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 @@ -264,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 @@ -318,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 { @@ -351,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 } @@ -360,36 +447,71 @@ 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 structure responsible for checking the actual FFI-safety -/// of a given type +/// 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>, - /// to prevent problems with recursive types, add a types-in-check cache - /// and a depth counter + /// to prevent problems with recursive types, + /// add a types-in-check cache and a depth counter recursion_limiter: RefCell<(FxHashSet>, usize)>, } @@ -429,19 +551,22 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } /// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call - fn visit_fnptr(&self, mode: CItemKind, ty: Ty<'tcx>, sig: Sig<'tcx>) -> FfiResult<'tcx> { + fn visit_fnptr( + &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, @@ -450,18 +575,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 } @@ -531,7 +651,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!(), } @@ -554,50 +674,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. @@ -657,47 +751,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 { @@ -707,16 +870,18 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn visit_struct_union( + fn visit_struct_or_union( &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() { @@ -727,6 +892,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) }, ); @@ -744,8 +910,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 @@ -757,15 +923,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( &self, state: CTypesVisitorState, + outer_ty: Option>, ty: Ty<'tcx>, def: ty::AdtDef<'tcx>, args: GenericArgsRef<'tcx>, @@ -780,7 +962,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); @@ -803,6 +988,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 { @@ -812,7 +999,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) @@ -821,7 +1009,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) } } @@ -866,9 +1061,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), } } @@ -907,11 +1102,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Primitive types with a stable representation. ty::Bool | ty::Never => FfiSafe, - ty::Slice(_) => FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_slice_reason, - Some(fluent::lint_improper_ctypes_slice_help), - ), + ty::Slice(inner_ty) => { + // ty::Slice is used for !Sized arrays, since they are the pointee for actual slices + let slice_is_actually_array = match outer_ty.map(|ty| ty.kind()) { + None => state.is_in_static() || state.is_being_defined(), + // this should have been caught a layer up, in visit_indirection + Some(ty::Ref(..) | ty::RawPtr(..)) => false, + Some(ty::Adt(..)) => ty.boxed_ty().is_none(), + Some(ty::Tuple(..)) => true, + Some(ty::FnPtr(..)) => false, + // this is supposed to cause a compile error that prevents this lint + // from being reached, but oh well + Some(ty::Array(..) | ty::Slice(_)) => true, + Some(ty @ _) => bug!("unexpected ty_kind around a slice: {:?}", ty), + }; + if slice_is_actually_array { + self.visit_type(state, Some(ty), inner_ty) + } else { + FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_slice_reason, + Some(fluent::lint_improper_ctypes_slice_help), + ) + } + } ty::Dynamic(..) => { FfiResult::new_with_reason(ty, fluent::lint_improper_ctypes_dyn, None) @@ -925,18 +1139,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 @@ -976,7 +1187,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( @@ -985,49 +1198,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(..) @@ -1039,7 +1295,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - fn check_for_opaque_ty(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + fn visit_for_opaque_ty(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> { struct ProhibitOpaqueTypes; impl<'tcx> ty::TypeVisitor> for ProhibitOpaqueTypes { type Result = ControlFlow>; @@ -1065,22 +1321,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } fn check_for_type(&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(&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(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> { + let ty = erase_and_maybe_normalize(self.cx, ty); match *ty.kind() { ty::FnPtr(sig_tys, hdr) => { @@ -1090,26 +1336,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 @@ -1130,7 +1433,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; } } @@ -1138,41 +1441,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().2; 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>, ) { @@ -1213,83 +1514,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 visitor = ImproperCTypesVisitor::new(self.cx); - 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 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 visitor = ImproperCTypesVisitor::new(self.cx); + fn check_foreign_static<'tcx>(&self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { + let ty = cx.tcx.type_of(id).instantiate_identity(); + let 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 visitor = ImproperCTypesVisitor::new(self.cx); + let 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 visitor = ImproperCTypesVisitor::new(self.cx); + let 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 { @@ -1302,14 +1631,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() { @@ -1319,7 +1648,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 }); @@ -1329,32 +1658,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 { @@ -1362,63 +1700,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(..) @@ -1433,8 +1785,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(), ); @@ -1457,11 +1809,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); + } } } } @@ -1469,12 +1870,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); /// } /// ``` /// @@ -1488,16 +1891,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 /// @@ -1517,11 +1919,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. @@ -1577,4 +2034,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/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f3d..48116690a4599 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -95,6 +95,7 @@ //! //! ``` //! #[repr(C)] +//! #[allow(improper_ctype_definitions)] //! pub struct Foo; //! //! #[unsafe(no_mangle)] 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/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs index fe0ad81dd3a3d..4e9ee2e630ca9 100644 --- a/library/compiler-builtins/compiler-builtins/src/lib.rs +++ b/library/compiler-builtins/compiler-builtins/src/lib.rs @@ -18,10 +18,8 @@ #![no_std] #![allow(unused_features)] #![allow(internal_features)] -// We use `u128` in a whole bunch of places which we currently agree with the -// compiler on ABIs and such, so we should be "good enough" for now and changes -// to the `u128` ABI will be reflected here. -#![allow(improper_ctypes, improper_ctypes_definitions)] +// a lot of references without a wrapping Option<_> to deal with the nonzero assumption +#![allow(improper_c_fn_definitions)] // `mem::swap` cannot be used because it may generate references to memcpy in unoptimized code. #![allow(clippy::manual_swap)] // Support compiling on both stage0 and stage1 which may differ in supported stable features. diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index e896c61ef4881..a0cf67f4cb153 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -643,6 +643,7 @@ fn offset_of_dst() { trait Trait {} #[repr(C)] + #[allow(improper_ctype_definitions)] struct Beta { x: u8, y: u16, diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index d1706b6525295..8d690433a5235 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -23,8 +23,7 @@ use core::any::Any; use core::panic::PanicPayload; #[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] -pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) { +pub unsafe extern "Rust" 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..ea39dde5cfa6b 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -88,8 +88,9 @@ unsafe extern "C" { } #[rustc_std_internal_symbol] -#[allow(improper_ctypes_definitions)] -pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) { +pub unsafe extern "Rust" 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/library/std/src/panicking.rs b/library/std/src/panicking.rs index 224cd39855a45..949c433de2dcc 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -53,8 +53,7 @@ pub static EMPTY_PANIC: fn(&'static str) -> ! = // // One day this may look a little less ad-hoc with the compiler helping out to // hook up these functions, but it is not this day! -#[allow(improper_ctypes)] -unsafe extern "C" { +unsafe extern "Rust" { #[rustc_std_internal_symbol] fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static); } 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 f9eb44d03abb9..586279f4ccc8c 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 47dadd51ce0fb..e4ee828a3bee5 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -29,7 +29,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/numbers-arithmetic/float-struct.rs b/tests/ui/abi/numbers-arithmetic/float-struct.rs index a958dc272722f..f3f7725c4f2d2 100644 --- a/tests/ui/abi/numbers-arithmetic/float-struct.rs +++ b/tests/ui/abi/numbers-arithmetic/float-struct.rs @@ -1,5 +1,7 @@ //@ run-pass +#![allow(improper_c_fn_definitions)] // unchecked-value references + use std::fmt::Debug; use std::hint::black_box; 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-function-shim.rs b/tests/ui/asm/naked-function-shim.rs index 4694d0cd963e3..f687919125374 100644 --- a/tests/ui/asm/naked-function-shim.rs +++ b/tests/ui/asm/naked-function-shim.rs @@ -8,7 +8,7 @@ //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 - +#![allow(improper_c_fn_definitions)] // unchecked-value references #![feature(no_core, lang_items)] #![crate_type = "lib"] #![no_core] diff --git a/tests/ui/asm/naked-functions-ffi.stderr b/tests/ui/asm/naked-functions-ffi.stderr index f7893a3b8de98..3b4f28b1fece3 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: 1 warning emitted diff --git a/tests/ui/asm/naked-functions-unused.aarch64.stderr b/tests/ui/asm/naked-functions-unused.aarch64.stderr index bfb2923b0b8d6..1b7dca0ec6bc6 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,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/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/asm/named-asm-labels.rs b/tests/ui/asm/named-asm-labels.rs index e78553fd775f0..a975ecd3cd441 100644 --- a/tests/ui/asm/named-asm-labels.rs +++ b/tests/ui/asm/named-asm-labels.rs @@ -9,7 +9,7 @@ // codegen unit (except for naked fns) and so the label could be duplicated // which causes less readable LLVM errors and in the worst cases causes ICEs // or segfaults based on system dependent behavior and codegen flags. - +#![allow(improper_c_fn_definitions)] // unchecked-value references use std::arch::{asm, global_asm, naked_asm}; #[no_mangle] 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 05fdb9afef80d..4fb1734adffe9 100644 --- a/tests/ui/backtrace/dylib-dep.rs +++ b/tests/ui/backtrace/dylib-dep.rs @@ -16,7 +16,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 77347b04ede83..b5b6fb3f2430c 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 "cmse-nonsecure-call" fn() -> u128, //~ ERROR [E0798] i128: extern "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 8d7ed003f0216..10e08c9df4e4f 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 "cmse-nonsecure-call" fn(), f2: extern "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 "cmse-nonsecure-call" fn() -> u32, f2: extern "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 d4f722fa1938b..c5ecae2300a0f 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 "cmse-nonsecure-entry" fn f3(_: u32, _: u64, _: u32) {} //~ ERROR [E0 pub extern "cmse-nonsecure-entry" fn f4(_: AlignRelevant, _: u32) {} //~ ERROR [E0798] #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "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 0052a0977ed71..3d87333f4fa0c 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 "cmse-nonsecure-entry" fn f4() -> ReprCAlign16 { } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "cmse-nonsecure-entry" fn f5() -> [u8; 5] { //~^ ERROR [E0798] [0xAA; 5] } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "cmse-nonsecure-entry" fn u128() -> u128 { //~^ ERROR [E0798] 123 } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "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 "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 3437328812561..b3934b2513aa4 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 "cmse-nonsecure-entry" fn inputs2(_: u32, _: u32, _: u32, _: u32) {} #[no_mangle] pub extern "cmse-nonsecure-entry" fn inputs3(_: u64, _: u64) {} #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "cmse-nonsecure-entry" fn inputs4(_: u128) {} #[no_mangle] pub extern "cmse-nonsecure-entry" fn inputs5(_: f64, _: f32, _: f32) {} #[no_mangle] pub extern "cmse-nonsecure-entry" fn inputs6(_: ReprTransparentStruct, _: U32Compound) {} #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "cmse-nonsecure-entry" fn inputs7(_: [u32; 4]) {} #[no_mangle] @@ -59,7 +59,7 @@ pub extern "cmse-nonsecure-entry" fn outputs4() -> f64 { 0.0 } #[no_mangle] -#[allow(improper_ctypes_definitions)] +#[allow(improper_c_fn_definitions)] pub extern "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 e17bac603b415..cdd3fbfde7b5c 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -68,6 +68,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 6af72868045ac..d8bace93c244a 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::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINHABITED_VARIANT` failed here error[E0080]: constructing invalid value at [0]: encountered a partial pointer or a mix of pointers - --> $DIR/detect-extra-ub.rs:77:16 + --> $DIR/detect-extra-ub.rs:78:16 | LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `PARTIAL_POINTER` failed here @@ -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]: constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/detect-extra-ub.rs:91:16 + --> $DIR/detect-extra-ub.rs:92:16 | LL | let _val = &*slice; | ^^^^^^^ evaluation of `OVERSIZED_REF` failed here 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 3524ca2af979d..663a2ccc05ce3 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); @@ -88,16 +92,15 @@ 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_str(p: TransparentStr); //~ ERROR: uses type `&str` + 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 dd43b2e268525..36b8979370ab5 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 TwoBadTypes<'a> { +LL | | +LL | | +LL | | non_c_type: char, +LL | | ref_with_mdata: &'a [u8], +LL | | } + | |_^ not FFI-safe | -LL | pub struct Foo; - | ^^^^^^^^^^^^^^ + = 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 `(u32) is 1..`, which is not FFI-safe - --> $DIR/ctypes.rs:78:27 + --> $DIR/ctypes.rs:82:27 | LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -80,7 +99,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:80:26 + --> $DIR/ctypes.rs:84:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -88,7 +107,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:81:26 + --> $DIR/ctypes.rs:85:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -97,7 +116,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:82:27 + --> $DIR/ctypes.rs:86:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -106,34 +125,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:83:25 + --> $DIR/ctypes.rs:87: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:84:33 + --> $DIR/ctypes.rs:88: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:87:12 + --> $DIR/ctypes.rs:91:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -141,7 +160,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:88:23 + --> $DIR/ctypes.rs:92:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -150,7 +169,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:89:24 + --> $DIR/ctypes.rs:93:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -158,17 +177,23 @@ 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 `&str`, which is not FFI-safe - --> $DIR/ctypes.rs:91:31 +error: `extern` block uses type `TransparentStr`, which is not FFI-safe + --> $DIR/ctypes.rs:95: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:93:27 + --> $DIR/ctypes.rs:97:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -176,36 +201,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:95:12 +error: `extern` callback uses type `char`, which is not FFI-safe + --> $DIR/ctypes.rs:99: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:95:12 +error: `extern` callback uses type `&dyn Debug`, which is not FFI-safe + --> $DIR/ctypes.rs:99: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:95:12 +error: `extern` callback uses type `TwoBadTypes<'_>`, which is not FFI-safe + --> $DIR/ctypes.rs:99: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> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -213,7 +242,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:103:47 + --> $DIR/ctypes.rs:106:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -221,7 +250,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:105:26 + --> $DIR/ctypes.rs:108:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -230,7 +259,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:107:26 + --> $DIR/ctypes.rs:110:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -238,5 +267,5 @@ LL | pub fn no_niche_b(b: Option>); = 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 23 previous errors +error: aborting due to 24 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 ec13d7f3f62c4..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; @@ -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-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs index 385463847b4b3..1ac59209a9ea7 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 = (); @@ -115,11 +116,13 @@ 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_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) { } @@ -129,7 +132,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` @@ -148,7 +151,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) { } @@ -156,7 +159,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) { } pub extern "C" fn good_i128_type(p: i128) { } diff --git a/tests/ui/lint/improper_ctypes/lint-fn.stderr b/tests/ui/lint/improper_ctypes/lint-fn.stderr index 4efde5d62ad81..196c4bec54e81 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 `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-fn.rs:94:33 + --> $DIR/lint-fn.rs:95:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -76,7 +110,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:97:34 + --> $DIR/lint-fn.rs:98:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -85,34 +119,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:100:32 + --> $DIR/lint-fn.rs:101: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:103:40 + --> $DIR/lint-fn.rs:104: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:106:51 + --> $DIR/lint-fn.rs:107:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -120,7 +154,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:111:30 + --> $DIR/lint-fn.rs:112:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -129,7 +163,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:114:31 + --> $DIR/lint-fn.rs:115:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -137,53 +171,65 @@ 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 `&str`, which is not FFI-safe - --> $DIR/lint-fn.rs:119:38 +error: `extern` fn uses type `TransparentStr`, which is not FFI-safe + --> $DIR/lint-fn.rs:121: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:126:53 + --> $DIR/lint-fn.rs:129: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:131:50 +error: `extern` fn uses type `StructWithProjectionAndLifetime<'_>`, which is not FFI-safe + --> $DIR/lint-fn.rs:134: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:150:51 +error: `extern` fn uses type `TransparentRef<'_>`, which is not FFI-safe + --> $DIR/lint-fn.rs:153: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:176:43 + --> $DIR/lint-fn.rs:179:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -191,22 +237,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:189:39 + --> $DIR/lint-fn.rs:192: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:192:41 + --> $DIR/lint-fn.rs:195: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 21 previous errors +error: aborting due to 23 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 07a33095e9c13..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. @@ -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), //~ ERROR: uses type `(*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..), // 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,17 +217,17 @@ 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), // Pat @@ -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 50f472363899a..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)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,51 +8,64 @@ LL | #![feature(inherent_associated_types)] = note: `#[warn(incomplete_features)]` on by default error: `extern` fn uses type `&SomeStruct`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:34:7 + --> $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 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 note: the lint level is defined here - --> $DIR/lint-tykind-fuzz.rs:5:25 + --> $DIR/lint-tykind-fuzz.rs:5:26 | -LL | #![deny(improper_ctypes,improper_ctypes_definitions)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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:90:14 + --> $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 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 `&Self`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:97:8 + --> $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 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: `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 + = 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 @@ -61,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 @@ -70,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 @@ -79,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 @@ -88,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 @@ -97,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 @@ -106,7 +119,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:133:7 + --> $DIR/lint-tykind-fuzz.rs:135:7 | LL | nz: pattern_type!(u32 is 1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -115,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 @@ -123,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 @@ -132,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 @@ -140,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 @@ -148,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 @@ -156,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 @@ -165,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 @@ -174,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 @@ -183,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 @@ -201,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 @@ -209,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 @@ -217,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 @@ -267,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 @@ -304,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 @@ -312,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 @@ -320,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 @@ -329,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 @@ -348,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 @@ -357,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 @@ -375,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 @@ -403,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 @@ -411,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 45 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/mustpass-134060.rs b/tests/ui/lint/improper_ctypes/mustpass-134060.rs index b30be99673687..17ec17a0373f8 100644 --- a/tests/ui/lint/improper_ctypes/mustpass-134060.rs +++ b/tests/ui/lint/improper_ctypes/mustpass-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/mustpass-134060.stderr b/tests/ui/lint/improper_ctypes/mustpass-134060.stderr index 51dcc0cd4dccc..4a329f26c4790 100644 --- a/tests/ui/lint/improper_ctypes/mustpass-134060.stderr +++ b/tests/ui/lint/improper_ctypes/mustpass-134060.stderr @@ -5,9 +5,9 @@ LL | extern "C" fn foo_(&self, _: ()) -> i64 { | ^^^^^ not FFI-safe | = help: consider using a raw pointer, or wrapping `&Self` 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 - = note: `#[warn(improper_ctypes_definitions)]` on by default + = note: `#[warn(improper_c_fn_definitions)]` on by default warning: `extern` fn uses type `()`, which is not FFI-safe --> $DIR/mustpass-134060.rs:11:34 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/unused/unused-attr-duplicate.rs b/tests/ui/lint/unused/unused-attr-duplicate.rs index cfa6c2b4228cd..c2013cd5f9d77 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); trait Trait {} 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 7810cd200d3fe812959a642e9f3903c305b99c76 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Tue, 6 May 2025 22:42:23 +0200 Subject: [PATCH 12/19] 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 b4dcace5d2dc3..f47d5b554cb24 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -439,6 +439,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 81e727b4bd377..6ea09b822bf16 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. @@ -585,6 +628,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( + &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(&self, _ty: Ty<'tcx>) -> FfiResult<'tcx> { @@ -751,23 +833,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()]; @@ -793,7 +897,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe => false, r @ FfiUnsafe { .. } => { fields_ok_list[field_i] = false; - field_ffires += r; + ffires_accumulator += r; false } } @@ -803,7 +907,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()) @@ -842,14 +946,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 @@ -957,8 +1066,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. @@ -999,18 +1107,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``) @@ -1100,7 +1225,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(inner_ty) => { // ty::Slice is used for !Sized arrays, since they are the pointee for actual slices @@ -1238,6 +1363,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 4659e554ee6d9..d536bf66d54c5 100644 --- a/tests/ui/lint/improper_ctypes/lint-enum.rs +++ b/tests/ui/lint/improper_ctypes/lint-enum.rs @@ -77,7 +77,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 d3a5d6d075aac..8e1cebb198fd4 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:80: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:8: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:81: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:82:14 @@ -207,5 +220,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 21 previous errors +error: aborting due to 22 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 d15dbb5866c0108d3a40c0d5406afb861b74d7df Mon Sep 17 00:00:00 2001 From: niacdoial Date: Tue, 6 May 2025 23:22:26 +0200 Subject: [PATCH 13/19] 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 | 118 ++++++---- 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 | 83 +++++++ .../improper_ctypes/lint-pattern-types.stderr | 88 ++++++++ .../improper_ctypes/lint-tykind-fuzz.stderr | 4 +- 13 files changed, 502 insertions(+), 85 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 f47d5b554cb24..41bad4d689329 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -411,8 +411,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 8ecfe4ba6166c..e48389b4e2b62 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -372,14 +372,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 8f9e990265d24..ffcfd4fdddecb 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; @@ -862,13 +862,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() { @@ -893,6 +894,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; } @@ -929,11 +944,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>, @@ -963,19 +1158,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 6ea09b822bf16..7d49c74b05136 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -669,14 +669,18 @@ 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(&self, _ty: Ty<'tcx>) -> FfiResult<'tcx> { + fn visit_numeric(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> { // FIXME: for now, this is very incomplete, and seems to assume a x86_64 target - return FfiResult::FfiSafe; - // match ty.kind() { - // ty::Int(..) | ty::Uint(..) | ty::Float(..) => - // FfiResult::FfiSafe, - // _ => bug!("visit_numeric is to be called with numeric (int, float) types"), - // } + match ty.kind() { + ty::Int(..) | ty::Uint(..) | ty::Float(..) => FfiResult::FfiSafe, + + ty::Char => FfiResult::new_with_reason( + ty, + fluent::lint_improper_ctypes_char_reason, + Some(fluent::lint_improper_ctypes_char_help), + ), + _ => bug!("visit_numeric is to be called with numeric (int, float) types"), + } } /// Return the right help for Cstring and Cstr-linked unsafety @@ -1075,7 +1079,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); } @@ -1192,37 +1201,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, @@ -1345,17 +1377,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 36b8979370ab5..71fc14da705f3 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -95,8 +95,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:84:26 diff --git a/tests/ui/lint/improper_ctypes/lint-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs index 1ac59209a9ea7..d3ecab696c36f 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); @@ -122,7 +122,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 196c4bec54e81..503b7e8ba21bc 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.stderr +++ b/tests/ui/lint/improper_ctypes/lint-fn.stderr @@ -186,8 +186,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:124: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:129:53 + --> $DIR/lint-fn.rs:130:53 | LL | pub extern "C" fn argument_with_assumptions_4(aptr: &[u8; 4 as usize]) { } | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -197,7 +213,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:134:50 + --> $DIR/lint-fn.rs:135:50 | LL | pub extern "C" fn argument_with_assumptions_6(s: StructWithProjectionAndLifetime) { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -213,7 +229,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:153:51 + --> $DIR/lint-fn.rs:154:51 | LL | pub extern "C" fn argument_with_assumptions_14(p: TransparentRef) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -229,7 +245,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:179:43 + --> $DIR/lint-fn.rs:180:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -237,7 +253,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:192:39 + --> $DIR/lint-fn.rs:193:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -246,7 +262,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:195:41 + --> $DIR/lint-fn.rs:196:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe @@ -254,5 +270,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 23 previous errors +error: aborting due to 24 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..ef7c085bb3ebe --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-pattern-types.rs @@ -0,0 +1,83 @@ +#![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, + e4: Option, + f1: Option, + f2: Option, + f3: Option, + f4: Option, + 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, + g32: Option, + g33: Option, + g34: Option, + //g4: Option, + + // 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..48b0a7eb82aee --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-pattern-types.stderr @@ -0,0 +1,88 @@ +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 `(usize) is 1..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:49: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:50: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:52: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:54: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:54: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 9 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 1e52d90d68c22cc656c7974dba921c03a90982ac Mon Sep 17 00:00:00 2001 From: niacdoial Date: Tue, 6 May 2025 23:22:26 +0200 Subject: [PATCH 14/19] 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 | 521 ++++++++++-------- 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, 381 insertions(+), 235 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 85a06f457ebea..3e671ec822e56 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -466,10 +466,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 56d756e52cce1..0f7dea5c9706f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -5,7 +5,9 @@ use libc::{c_char, c_uint}; use super::MetadataKindId; use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; use crate::llvm::{Bool, Builder}; +use crate::wrap_returns_in_options; +wrap_returns_in_options! { #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { // Enzyme @@ -14,7 +16,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; @@ -46,10 +48,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, params: *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 2443194ff4832..c82e6467dff2b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -31,6 +31,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; @@ -791,7 +868,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; @@ -1005,6 +1082,7 @@ impl From for MetadataKindId { } } +wrap_returns_in_options! { unsafe extern "C" { // Create and destroy contexts. pub(crate) fn LLVMContextDispose(C: &'static mut Context); @@ -1015,11 +1093,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) safe fn LLVMCloneModule(M: &Module) -> &Module; + |wrap pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; /// Data layout. See Module::getDataLayout. pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; @@ -1032,7 +1110,7 @@ unsafe extern "C" { Len: size_t, ); - /// Create the specified uniqued inline asm string. See `InlineAsm::get()`. + |wrap /// Create the specified uniqued inline asm string. See `InlineAsm::get()`. pub(crate) fn LLVMGetInlineAsm<'ll>( Ty: &'ll Type, AsmString: *const c_uchar, // See "PTR_LEN_STR". @@ -1046,23 +1124,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, @@ -1072,7 +1150,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, @@ -1080,36 +1158,36 @@ 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; + |wrap pub(crate) fn LLVMVoidTypeInContext(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, @@ -1121,51 +1199,51 @@ 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 LLVMConstNamedStruct<'a>( + |wrap pub(crate) fn LLVMConstNamedStruct<'a>( StructTy: &'a Type, ConstantVals: *const &'a Value, Count: c_uint, ) -> &'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>; @@ -1180,11 +1258,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) safe 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>; @@ -1199,7 +1277,7 @@ unsafe extern "C" { pub(crate) safe fn LLVMRustSetTailCallKind(CallInst: &Value, Kind: TailCallKind); // Operations on attributes - pub(crate) fn LLVMCreateStringAttribute( + |wrap pub(crate) fn LLVMCreateStringAttribute( C: &Context, Name: *const c_char, NameLen: c_uint, @@ -1222,11 +1300,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, @@ -1236,7 +1314,7 @@ unsafe extern "C" { pub(crate) fn LLVMGetInstructionParent(Inst: &Value) -> &BasicBlock; pub(crate) fn LLVMGetCalledValue(CallInst: &Value) -> Option<&Value>; 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 @@ -1254,9 +1332,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 @@ -1264,30 +1342,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>, @@ -1333,170 +1411,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 @@ -1505,21 +1583,21 @@ 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 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, @@ -1530,85 +1608,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, @@ -1617,14 +1695,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, @@ -1633,48 +1711,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, @@ -1683,7 +1761,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, @@ -1695,7 +1773,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, @@ -1704,7 +1782,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, @@ -1714,7 +1792,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>); @@ -1725,7 +1803,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, @@ -1734,13 +1812,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) safe 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( @@ -1751,7 +1829,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, @@ -1761,7 +1839,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, @@ -1773,7 +1851,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, @@ -1800,30 +1878,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, @@ -1838,7 +1916,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; @@ -1864,13 +1942,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, @@ -1878,21 +1956,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, @@ -1900,7 +1978,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, @@ -1926,7 +2004,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, @@ -1935,7 +2013,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, @@ -1944,7 +2022,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, @@ -1953,55 +2031,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, RHS: &'a Value, ) -> &'a Value; - pub(crate) fn LLVMRustBuildMaxNum<'a>( + |wrap pub(crate) fn LLVMRustBuildMaxNum<'a>( B: &Builder<'a>, 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, @@ -2009,7 +2087,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, @@ -2063,7 +2141,7 @@ unsafe extern "C" { BufferOut: &RustString, ); - pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar( + |wrap pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar( F: &Value, FuncName: *const c_char, FuncNameLen: size_t, @@ -2103,8 +2181,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, @@ -2120,8 +2198,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, @@ -2133,13 +2211,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, @@ -2156,8 +2234,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, @@ -2171,16 +2249,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, @@ -2189,8 +2267,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, @@ -2199,8 +2277,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, @@ -2217,8 +2295,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, @@ -2231,8 +2309,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, @@ -2246,8 +2324,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, @@ -2259,14 +2337,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, @@ -2281,8 +2359,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, @@ -2296,28 +2374,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, @@ -2326,8 +2404,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, @@ -2335,8 +2413,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, @@ -2349,8 +2427,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, @@ -2365,8 +2443,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, @@ -2381,8 +2459,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, @@ -2390,7 +2468,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>, @@ -2509,7 +2587,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>>; @@ -2543,7 +2621,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; @@ -2566,7 +2644,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>>, @@ -2581,14 +2659,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, @@ -2624,7 +2702,7 @@ unsafe extern "C" { Identifier: *const c_char, ) -> Option<&Module>; - 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, @@ -2678,3 +2756,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 a0796af6de76b..427494efa4a65 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -340,6 +340,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 ffcfd4fdddecb..dcb9043fa389e 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 7d49c74b05136..6db7d622051ac 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 @@ -1504,6 +1514,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); @@ -1712,16 +1723,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 @@ -1732,6 +1739,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>( + &self, + cx: &LateContext<'tcx>, + id: hir::OwnerId, + span: Span, + ) { + let ty = cx.tcx.type_of(id).instantiate_identity(); + let 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, @@ -1840,12 +1860,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", }; @@ -1917,6 +1939,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 { .. } => {} @@ -2083,6 +2112,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. @@ -2196,6 +2255,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 9dedb0cc7b4bb7f5948dcb5aa6717a77af7718d9 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Tue, 6 May 2025 23:22:26 +0200 Subject: [PATCH 15/19] 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 | 150 ++------------ library/alloc/src/boxed.rs | 1 - library/coretests/tests/mem.rs | 1 - 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 | 105 +++------- .../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 +--- .../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, 171 insertions(+), 716 deletions(-) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 427494efa4a65..a0cc2b20fd4b5 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -341,7 +341,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 dcb9043fa389e..e426c6be09b16 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 6db7d622051ac..510825ffda9ed 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 @@ -645,9 +631,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 { @@ -842,7 +827,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { &self, state: CTypesVisitorState, ty: Ty<'tcx>, - def: ty::AdtDef<'tcx>, + def: AdtDef<'tcx>, variant: &ty::VariantDef, args: GenericArgsRef<'tcx>, ) -> FfiResult<'tcx> { @@ -996,9 +981,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn visit_struct_or_union( &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)); @@ -1059,12 +1043,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( @@ -1072,7 +1053,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)); @@ -1155,12 +1136,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)) } } @@ -1205,7 +1184,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), } @@ -1272,7 +1251,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Slice(inner_ty) => { // ty::Slice is used for !Sized arrays, since they are the pointee for actual slices let slice_is_actually_array = match outer_ty.map(|ty| ty.kind()) { - None => state.is_in_static() || state.is_being_defined(), + None => state.is_in_static(), // this should have been caught a layer up, in visit_indirection Some(ty::Ref(..) | ty::RawPtr(..)) => false, Some(ty::Adt(..)) => ty.boxed_ty().is_none(), @@ -1514,71 +1493,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"); @@ -1740,12 +1654,7 @@ impl ImproperCTypesLint { } /// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type - fn check_exported_static<'tcx>( - &self, - cx: &LateContext<'tcx>, - id: hir::OwnerId, - span: Span, - ) { + fn check_exported_static<'tcx>(&self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { let ty = cx.tcx.type_of(id).instantiate_identity(); let visitor = ImproperCTypesVisitor::new(cx); let ffi_res = visitor.check_for_type(CTypesVisitorState::ExportedStaticTy, ty); @@ -1861,7 +1770,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 { @@ -1869,7 +1777,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() @@ -1899,9 +1806,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>) { @@ -2170,33 +2074,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. @@ -2257,6 +2134,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/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 48116690a4599..3db37f1d16f3d 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -95,7 +95,6 @@ //! //! ``` //! #[repr(C)] -//! #[allow(improper_ctype_definitions)] //! pub struct Foo; //! //! #[unsafe(no_mangle)] diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index a0cf67f4cb153..e896c61ef4881 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -643,7 +643,6 @@ fn offset_of_dst() { trait Trait {} #[repr(C)] - #[allow(improper_ctype_definitions)] struct Beta { x: u8, y: u16, 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 586279f4ccc8c..dc616a00d83f3 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 e4ee828a3bee5..f70be21cd3bba 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -29,7 +29,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 cdd3fbfde7b5c..e17bac603b415 100644 --- a/tests/ui/consts/extra-const-ub/detect-extra-ub.rs +++ b/tests/ui/consts/extra-const-ub/detect-extra-ub.rs @@ -68,7 +68,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 d8bace93c244a..6af72868045ac 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::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINHABITED_VARIANT` failed here error[E0080]: constructing invalid value at [0]: encountered a partial pointer or a mix of pointers - --> $DIR/detect-extra-ub.rs:78:16 + --> $DIR/detect-extra-ub.rs:77:16 | LL | let _val = *(&mem as *const Align as *const [*const u8; 2]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `PARTIAL_POINTER` failed here @@ -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]: constructing invalid value: encountered invalid reference metadata: slice is bigger than largest supported object - --> $DIR/detect-extra-ub.rs:92:16 + --> $DIR/detect-extra-ub.rs:91:16 | LL | let _val = &*slice; | ^^^^^^^ evaluation of `OVERSIZED_REF` failed here 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 663a2ccc05ce3..c93eaa1034aad 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 71fc14da705f3..1d2a3b8bd4e4e 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 `(u32) is 1..`, which is not FFI-safe - --> $DIR/ctypes.rs:82:27 + --> $DIR/ctypes.rs:79:27 | LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -99,7 +40,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:84:26 + --> $DIR/ctypes.rs:81:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -107,7 +48,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:85:26 + --> $DIR/ctypes.rs:82:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -116,7 +57,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:86:27 + --> $DIR/ctypes.rs:83:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -125,7 +66,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:87:25 + --> $DIR/ctypes.rs:84:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -139,20 +80,20 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/ctypes.rs:88:33 + --> $DIR/ctypes.rs:85: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:91:12 + --> $DIR/ctypes.rs:88:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -160,7 +101,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/ctypes.rs:92:23 + --> $DIR/ctypes.rs:89:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -169,7 +110,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:93:24 + --> $DIR/ctypes.rs:90:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -178,7 +119,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `TransparentStr`, which is not FFI-safe - --> $DIR/ctypes.rs:95:31 + --> $DIR/ctypes.rs:92:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -193,7 +134,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:97:27 + --> $DIR/ctypes.rs:94:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -202,7 +143,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:99:12 + --> $DIR/ctypes.rs:96:12 | LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -217,7 +158,7 @@ LL | #![deny(improper_ctypes, improper_c_callbacks)] | ^^^^^^^^^^^^^^^^^^^^ error: `extern` callback uses type `&dyn Debug`, which is not FFI-safe - --> $DIR/ctypes.rs:99:12 + --> $DIR/ctypes.rs:96:12 | LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -226,7 +167,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:99:12 + --> $DIR/ctypes.rs:96:12 | LL | f: for<'a> extern "C" fn(a:char, b:&dyn Debug, c: TwoBadTypes<'a>) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -234,7 +175,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> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -242,7 +183,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:103:47 | LL | pub fn struct_unsized_ptr_has_metadata(p: &UnsizedStructBecauseDyn); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -250,7 +191,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:105:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -259,7 +200,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:107:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -267,5 +208,5 @@ LL | pub fn no_niche_b(b: Option>); = 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 24 previous errors +error: aborting due to 20 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 d3ecab696c36f..066ace32603f3 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 503b7e8ba21bc..d29eb46446331 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 @@ -270,5 +251,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 24 previous errors +error: aborting due to 23 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/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 c105896126fd9169858c0dbd866d9b3175855e2d Mon Sep 17 00:00:00 2001 From: niacdoial Date: Wed, 21 May 2025 00:32:08 +0200 Subject: [PATCH 16/19] lint ImproperCTypes: clean up exported static variables --- .../rustc_lint/src/types/improper_ctypes.rs | 139 ++++++++++-------- tests/ui/lint/improper_ctypes/ctypes.rs | 12 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 26 +++- 3 files changed, 113 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 510825ffda9ed..cf53b7997ccc0 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -4,6 +4,7 @@ use std::iter; use std::ops::ControlFlow; use rustc_abi::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 @@ -489,58 +500,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 } } @@ -1654,10 +1642,21 @@ impl ImproperCTypesLint { } /// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type - fn check_exported_static<'tcx>(&self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { + fn check_exported_static<'tcx>( + &self, + cx: &LateContext<'tcx>, + id: hir::OwnerId, + span: Span, + is_mut: bool, + ) { let ty = cx.tcx.type_of(id).instantiate_identity(); let 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); } @@ -1844,11 +1843,27 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { 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); + // FIXME: cx.tcx.has_attr no worky + // 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)) + if let hir::ItemKind::Static(is_mut, _, _, _) = item.kind { + let is_exported_static = cx.tcx.get_all_attrs(item.owner_id).iter().any(|x| { + matches!( + x, + hir::Attribute::Parsed( + hir::attrs::AttributeKind::NoMangle(_) + | hir::attrs::AttributeKind::ExportName { .. } + ) + ) + }); + if is_exported_static { + 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 c93eaa1034aad..9e7914fbb703d 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; @@ -138,6 +138,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 1d2a3b8bd4e4e..11fdcee4d7359 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -208,5 +208,29 @@ LL | pub fn no_niche_b(b: Option>); = 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 20 previous errors +error: foreign-code-reachable static uses type `&str`, which is not FFI-safe + --> $DIR/ctypes.rs:145: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:148: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 22 previous errors From 06aeed61cd8c4f8b9688c2405bd4be04d0e9ec84 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Thu, 22 May 2025 22:45:20 +0200 Subject: [PATCH 17/19] lint ImproperCTypes: changes to pass CI [...] - make clippy happy about the changes in rust_codegen_llvm - split a test stderr into 32bit and 64bit - fix some documentation --- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 9 +- .../rustc_lint/src/types/improper_ctypes.rs | 3 +- src/tools/clippy/tests/ui/boxed_local.rs | 4 +- src/tools/clippy/tests/ui/ptr_arg.rs | 1 + src/tools/clippy/tests/ui/ptr_arg.stderr | 4 +- src/tools/lint-docs/src/groups.rs | 1 + .../exported_symbol_wrong_type.rs | 1 + .../improper_ctypes/lint-pattern-types.rs | 6 ++ .../lint-pattern-types.size32.stderr | 88 +++++++++++++++++++ ...tderr => lint-pattern-types.size64.stderr} | 20 ++--- 10 files changed, 119 insertions(+), 18 deletions(-) create mode 100644 tests/ui/lint/improper_ctypes/lint-pattern-types.size32.stderr rename tests/ui/lint/improper_ctypes/{lint-pattern-types.stderr => lint-pattern-types.size64.stderr} (90%) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index c82e6467dff2b..ef89455059700 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1332,7 +1332,8 @@ unsafe extern "C" { ); // Instruction builders - |wrap pub(crate) fn LLVMCreateBuilderInContext(C: &Context) -> &mut Builder<'_>; + |wrap #[allow(clippy::mut_from_ref)] // FIXME? + pub(crate) fn LLVMCreateBuilderInContext(C: &Context) -> &mut Builder<'_>; pub(crate) fn LLVMPositionBuilderAtEnd<'a>(Builder: &Builder<'a>, Block: &'a BasicBlock); |wrap pub(crate) fn LLVMGetInsertBlock<'a>(Builder_: &Builder<'a>) -> &'a BasicBlock; pub(crate) fn LLVMDisposeBuilder<'a>(Builder: &'a mut Builder<'a>); @@ -2587,7 +2588,8 @@ 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>; - |wrap pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; + |wrap #[allow(clippy::mut_from_ref)] // FIXME? + pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; pub(crate) fn LLVMRustArchiveIteratorNext<'a>( AIR: &ArchiveIterator<'a>, ) -> Option<&'a mut ArchiveChild<'a>>; @@ -2702,7 +2704,8 @@ unsafe extern "C" { Identifier: *const c_char, ) -> Option<&Module>; - |wrap pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; + |wrap #[allow(clippy::mut_from_ref)] // FIXME? + pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; pub(crate) fn LLVMRustLinkerAdd( linker: &Linker<'_>, bytecode: *const c_char, diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index cf53b7997ccc0..b41cec40074a1 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -2042,7 +2042,8 @@ declare_lint! { /// ### Example /// /// ```rust - /// # #![unsafe(no_mangle)] + /// # #[unsafe(no_mangle)] + /// # #[used] /// static mut PLUGIN_ABI_MIN_VERSION: &'static str = "0.0.5"; /// ``` /// diff --git a/src/tools/clippy/tests/ui/boxed_local.rs b/src/tools/clippy/tests/ui/boxed_local.rs index 3fb685aa36c11..5f36190ab6810 100644 --- a/src/tools/clippy/tests/ui/boxed_local.rs +++ b/src/tools/clippy/tests/ui/boxed_local.rs @@ -171,11 +171,11 @@ mod issue_3739 { /// Issue #5542 /// /// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. -pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} +pub extern "C" fn do_not_warn_me(_c_pointer: Option>) -> () {} #[allow(missing_abi)] #[rustfmt::skip] // Forces rustfmt to not add ABI -pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} +pub extern fn do_not_warn_me_no_abi(_c_pointer: Option>) -> () {} // Issue #4804 - default implementation in trait mod issue4804 { diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index be14e0762ff42..9a4039fe4021a 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -317,6 +317,7 @@ mod issue_9218 { } } +#[allow(improper_c_fn_definitions)] mod issue_11181 { extern "C" fn allowed(_v: &Vec) {} diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr index 87235057349e3..7d18fe23119b8 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.stderr +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -220,13 +220,13 @@ LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str { | ^^^^^^^^^^^^^^^^ help: change this to: `&str` error: writing `&String` instead of `&str` involves a new object where a slice will do - --> tests/ui/ptr_arg.rs:347:17 + --> tests/ui/ptr_arg.rs:348:17 | LL | fn good(v1: &String, v2: &String) { | ^^^^^^^ help: change this to: `&str` error: writing `&String` instead of `&str` involves a new object where a slice will do - --> tests/ui/ptr_arg.rs:347:30 + --> tests/ui/ptr_arg.rs:348:30 | LL | fn good(v1: &String, v2: &String) { | ^^^^^^^ help: change this to: `&str` diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs index a24fbbc0ceab3..10ae9e6421b19 100644 --- a/src/tools/lint-docs/src/groups.rs +++ b/src/tools/lint-docs/src/groups.rs @@ -30,6 +30,7 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[ "unknown-or-malformed-diagnostic-attributes", "detects unknown or malformed diagnostic attributes", ), + ("improper-c-boundaries", "Lints for points where rust code interacts with non-rust code"), ]; type LintGroups = BTreeMap>; diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_wrong_type.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_wrong_type.rs index e273e354334f8..e7bad493f4b93 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_wrong_type.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_wrong_type.rs @@ -1,4 +1,5 @@ #[no_mangle] +#[allow(improper_c_var_definitions)] static FOO: () = (); fn main() { diff --git a/tests/ui/lint/improper_ctypes/lint-pattern-types.rs b/tests/ui/lint/improper_ctypes/lint-pattern-types.rs index ef7c085bb3ebe..eb39a1dfeba19 100644 --- a/tests/ui/lint/improper_ctypes/lint-pattern-types.rs +++ b/tests/ui/lint/improper_ctypes/lint-pattern-types.rs @@ -1,3 +1,9 @@ +//@ revisions: size64 size32 +//@[size64] only-64bit +//@[size32] only-32bit +// (this is needed because stderr writes out usize::MAX-1) + + #![feature(pattern_types, rustc_attrs)] #![feature(pattern_type_macro)] #![feature(pattern_type_range_trait,const_trait_impl)] diff --git a/tests/ui/lint/improper_ctypes/lint-pattern-types.size32.stderr b/tests/ui/lint/improper_ctypes/lint-pattern-types.size32.stderr new file mode 100644 index 0000000000000..86fb09411d924 --- /dev/null +++ b/tests/ui/lint/improper_ctypes/lint-pattern-types.size32.stderr @@ -0,0 +1,88 @@ +error: `extern` fn uses type `Option<(u32) is 0..>`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:27: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:10: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:28: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:30: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:31: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 `(usize) is 1..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:55: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..=4294967293`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:56: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 -2147483647..`, which is not FFI-safe + --> $DIR/lint-pattern-types.rs:58:9 + | +LL | h4: pattern_type!(isize is ISZP1..), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using the base type instead, or wrapping `(isize) is -2147483647..` 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:60: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:60: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 9 previous errors + diff --git a/tests/ui/lint/improper_ctypes/lint-pattern-types.stderr b/tests/ui/lint/improper_ctypes/lint-pattern-types.size64.stderr similarity index 90% rename from tests/ui/lint/improper_ctypes/lint-pattern-types.stderr rename to tests/ui/lint/improper_ctypes/lint-pattern-types.size64.stderr index 48b0a7eb82aee..84c9a56c7f1cf 100644 --- a/tests/ui/lint/improper_ctypes/lint-pattern-types.stderr +++ b/tests/ui/lint/improper_ctypes/lint-pattern-types.size64.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `Option<(u32) is 0..>`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:21:9 + --> $DIR/lint-pattern-types.rs:27:9 | LL | ao: Option, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -7,13 +7,13 @@ LL | ao: Option, = 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 + --> $DIR/lint-pattern-types.rs:10: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 + --> $DIR/lint-pattern-types.rs:28:8 | LL | b: pattern_type!(u32 is 1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | b: 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 `(u32) is 2..`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:24:8 + --> $DIR/lint-pattern-types.rs:30:8 | LL | c: pattern_type!(u32 is 2..), | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -31,7 +31,7 @@ LL | c: pattern_type!(u32 is 2..), = 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 + --> $DIR/lint-pattern-types.rs:31:9 | LL | co: Option, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -40,7 +40,7 @@ LL | co: Option, = 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 `(usize) is 1..`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:49:9 + --> $DIR/lint-pattern-types.rs:55:9 | LL | h1: pattern_type!(usize is 1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -49,7 +49,7 @@ LL | h1: pattern_type!(usize 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 `(usize) is 0..=18446744073709551613`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:50:9 + --> $DIR/lint-pattern-types.rs:56:9 | LL | h2: pattern_type!(usize is ..USZM1), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -58,7 +58,7 @@ LL | h2: pattern_type!(usize is ..USZM1), = 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:52:9 + --> $DIR/lint-pattern-types.rs:58:9 | LL | h4: pattern_type!(isize is ISZP1..), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -67,7 +67,7 @@ LL | h4: pattern_type!(isize is ISZP1..), = 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:54:8 + --> $DIR/lint-pattern-types.rs:60:8 | LL | h: pattern_type!(char is '\0'..), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -76,7 +76,7 @@ LL | h: pattern_type!(char is '\0'..), = 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:54:8 + --> $DIR/lint-pattern-types.rs:60:8 | LL | h: pattern_type!(char is '\0'..), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe From 02d8c95c964c528c24041663d6ed7e699d9240cf Mon Sep 17 00:00:00 2001 From: niacdoial Date: Wed, 4 Jun 2025 23:15:23 +0200 Subject: [PATCH 18/19] lint ImproperCTypes: remove the parts about checking value assumptions it would be correct to lint on those, but this is deemed too steep a change for now, especially for projects that turn all warnings into errors --- 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 | 522 ++++++++---------- compiler/rustc_lint/messages.ftl | 10 - compiler/rustc_lint/src/foreign_modules.rs | 4 +- compiler/rustc_lint/src/types.rs | 103 +--- .../rustc_lint/src/types/improper_ctypes.rs | 191 +------ compiler/rustc_llvm/src/lib.rs | 3 +- library/alloc/src/collections/btree/node.rs | 1 - .../compiler-builtins/src/lib.rs | 2 - library/proc_macro/src/bridge/buffer.rs | 14 +- library/proc_macro/src/bridge/closure.rs | 6 +- src/tools/clippy/tests/ui/boxed_local.rs | 4 +- src/tools/clippy/tests/ui/ptr_arg.rs | 1 - src/tools/clippy/tests/ui/ptr_arg.stderr | 4 +- tests/ui/abi/nullable-pointer-ffi-compat.rs | 1 - .../ui/abi/numbers-arithmetic/float-struct.rs | 2 - tests/ui/asm/issue-97490.rs | 2 - tests/ui/asm/naked-function-shim.rs | 2 +- .../asm/naked-functions-unused.aarch64.stderr | 73 +-- tests/ui/asm/naked-functions-unused.rs | 9 +- .../asm/naked-functions-unused.x86_64.stderr | 73 +-- tests/ui/asm/named-asm-labels.rs | 2 +- tests/ui/lint/clashing-extern-fn.rs | 3 - tests/ui/lint/clashing-extern-fn.stderr | 39 +- .../improper_ctypes/allow_improper_ctypes.rs | 5 +- .../allow_improper_ctypes.stderr | 55 +- tests/ui/lint/improper_ctypes/ctypes.rs | 3 +- tests/ui/lint/improper_ctypes/ctypes.stderr | 21 +- tests/ui/lint/improper_ctypes/lint-73249-2.rs | 7 +- .../lint/improper_ctypes/lint-73249-2.stderr | 23 - tests/ui/lint/improper_ctypes/lint-fn.rs | 10 +- tests/ui/lint/improper_ctypes/lint-fn.stderr | 100 +--- .../improper_ctypes/lint-pattern-types.rs | 89 --- .../lint-pattern-types.size32.stderr | 88 --- .../lint-pattern-types.size64.stderr | 88 --- .../lint/improper_ctypes/lint-tykind-fuzz.rs | 19 +- .../improper_ctypes/lint-tykind-fuzz.stderr | 107 +--- .../lint/improper_ctypes/mustpass-134060.rs | 1 - .../improper_ctypes/mustpass-134060.stderr | 14 +- 40 files changed, 360 insertions(+), 1351 deletions(-) delete mode 100644 tests/ui/lint/improper_ctypes/lint-73249-2.stderr delete mode 100644 tests/ui/lint/improper_ctypes/lint-pattern-types.rs delete mode 100644 tests/ui/lint/improper_ctypes/lint-pattern-types.size32.stderr delete mode 100644 tests/ui/lint/improper_ctypes/lint-pattern-types.size64.stderr diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 3e671ec822e56..85a06f457ebea 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -466,11 +466,10 @@ fn report_inline_asm( cgcx.diag_emitter.inline_asm_error(span, msg, level, source); } -unsafe extern "C" fn diagnostic_handler(info: Option<&DiagnosticInfo>, user: *mut c_void) { +unsafe extern "C" fn diagnostic_handler(info: &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 0f7dea5c9706f..56d756e52cce1 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -5,9 +5,7 @@ use libc::{c_char, c_uint}; use super::MetadataKindId; use super::ffi::{AttributeKind, BasicBlock, Metadata, Module, Type, Value}; use crate::llvm::{Bool, Builder}; -use crate::wrap_returns_in_options; -wrap_returns_in_options! { #[link(name = "llvm-wrapper", kind = "static")] unsafe extern "C" { // Enzyme @@ -16,7 +14,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); - |wrap pub(crate) fn LLVMRustGetTerminator<'a>(B: &BasicBlock) -> &'a Value; + 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; @@ -48,11 +46,10 @@ unsafe extern "C" { pub(crate) fn LLVMDumpModule(M: &Module); pub(crate) fn LLVMDumpValue(V: &Value); pub(crate) fn LLVMGetFunctionCallConv(F: &Value) -> c_uint; - |wrap pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type; + pub(crate) fn LLVMGetReturnType(T: &Type) -> &Type; pub(crate) fn LLVMGetParams(Fnc: &Value, params: *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 ef89455059700..ba1a32e22d4d4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -31,83 +31,6 @@ 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; @@ -868,7 +791,7 @@ unsafe extern "C" { pub(crate) type DiagnosticHandler; } -pub(crate) type DiagnosticHandlerTy = unsafe extern "C" fn(Option<&DiagnosticInfo>, *mut c_void); +pub(crate) type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void); pub(crate) mod debuginfo { use std::ptr; @@ -1082,7 +1005,6 @@ impl From for MetadataKindId { } } -wrap_returns_in_options! { unsafe extern "C" { // Create and destroy contexts. pub(crate) fn LLVMContextDispose(C: &'static mut Context); @@ -1093,11 +1015,11 @@ unsafe extern "C" { ) -> MetadataKindId; // Create modules. - |wrap pub(crate) fn LLVMModuleCreateWithNameInContext( + pub(crate) fn LLVMModuleCreateWithNameInContext( ModuleID: *const c_char, C: &Context, ) -> &Module; - |wrap pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; + pub(crate) safe fn LLVMCloneModule(M: &Module) -> &Module; /// Data layout. See Module::getDataLayout. pub(crate) fn LLVMGetDataLayoutStr(M: &Module) -> *const c_char; @@ -1110,7 +1032,7 @@ unsafe extern "C" { Len: size_t, ); - |wrap /// Create the specified uniqued inline asm string. See `InlineAsm::get()`. + /// Create the specified uniqued inline asm string. See `InlineAsm::get()`. pub(crate) fn LLVMGetInlineAsm<'ll>( Ty: &'ll Type, AsmString: *const c_uchar, // See "PTR_LEN_STR". @@ -1124,23 +1046,23 @@ unsafe extern "C" { ) -> &'ll Value; // Operations on integer types - |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 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; pub(crate) fn LLVMGetIntTypeWidth(IntegerTy: &Type) -> c_uint; // Operations on real types - |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; + 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; // Operations on function types - |wrap pub(crate) fn LLVMFunctionType<'a>( + pub(crate) fn LLVMFunctionType<'a>( ReturnType: &'a Type, ParamTypes: *const &'a Type, ParamCount: c_uint, @@ -1150,7 +1072,7 @@ unsafe extern "C" { pub(crate) fn LLVMGetParamTypes<'a>(FunctionTy: &'a Type, Dest: *mut &'a Type); // Operations on struct types - |wrap pub(crate) fn LLVMStructTypeInContext<'a>( + pub(crate) fn LLVMStructTypeInContext<'a>( C: &'a Context, ElementTypes: *const &'a Type, ElementCount: c_uint, @@ -1158,36 +1080,36 @@ unsafe extern "C" { ) -> &'a Type; // Operations on array, pointer, and vector types (sequence types) - |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 LLVMPointerTypeInContext(C: &Context, AddressSpace: c_uint) -> &Type; + pub(crate) fn LLVMVectorType(ElementType: &Type, ElementCount: c_uint) -> &Type; - |wrap pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type; + pub(crate) fn LLVMGetElementType(Ty: &Type) -> &Type; pub(crate) fn LLVMGetVectorSize(VectorTy: &Type) -> c_uint; // Operations on other types - |wrap pub(crate) fn LLVMVoidTypeInContext(C: &Context) -> &Type; + pub(crate) fn LLVMVoidTypeInContext(C: &Context) -> &Type; // Operations on all values - |wrap pub(crate) fn LLVMTypeOf(Val: &Value) -> &Type; + 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); - |wrap pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; + pub(crate) safe fn LLVMValueAsMetadata(Node: &Value) -> &Metadata; // Operations on constants of any type - |wrap pub(crate) fn LLVMConstNull(Ty: &Type) -> &Value; - |wrap pub(crate) fn LLVMGetUndef(Ty: &Type) -> &Value; - |wrap pub(crate) fn LLVMGetPoison(Ty: &Type) -> &Value; + pub(crate) fn LLVMConstNull(Ty: &Type) -> &Value; + pub(crate) fn LLVMGetUndef(Ty: &Type) -> &Value; + pub(crate) fn LLVMGetPoison(Ty: &Type) -> &Value; // Operations on metadata - |wrap pub(crate) fn LLVMMDStringInContext2( + pub(crate) fn LLVMMDStringInContext2( C: &Context, Str: *const c_char, SLen: size_t, ) -> &Metadata; - |wrap pub(crate) fn LLVMMDNodeInContext2<'a>( + pub(crate) fn LLVMMDNodeInContext2<'a>( C: &'a Context, Vals: *const &'a Metadata, Count: size_t, @@ -1199,51 +1121,51 @@ unsafe extern "C" { ); // Operations on scalar constants - |wrap pub(crate) fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value; - |wrap pub(crate) fn LLVMConstIntOfArbitraryPrecision( + pub(crate) fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value; + pub(crate) fn LLVMConstIntOfArbitraryPrecision( IntTy: &Type, Wn: c_uint, Ws: *const u64, ) -> &Value; - |wrap pub(crate) fn LLVMConstReal(RealTy: &Type, N: f64) -> &Value; + pub(crate) fn LLVMConstReal(RealTy: &Type, N: f64) -> &Value; // Operations on composite constants - |wrap pub(crate) fn LLVMConstArray2<'a>( + pub(crate) fn LLVMConstArray2<'a>( ElementTy: &'a Type, ConstantVals: *const &'a Value, Length: u64, ) -> &'a Value; - |wrap pub(crate) fn LLVMArrayType2(ElementType: &Type, ElementCount: u64) -> &Type; - |wrap pub(crate) fn LLVMConstStringInContext2( + pub(crate) fn LLVMArrayType2(ElementType: &Type, ElementCount: u64) -> &Type; + pub(crate) fn LLVMConstStringInContext2( C: &Context, Str: *const c_char, Length: size_t, DontNullTerminate: Bool, ) -> &Value; - |wrap pub(crate) fn LLVMConstStructInContext<'a>( + pub(crate) fn LLVMConstStructInContext<'a>( C: &'a Context, ConstantVals: *const &'a Value, Count: c_uint, Packed: Bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMConstNamedStruct<'a>( + pub(crate) fn LLVMConstNamedStruct<'a>( StructTy: &'a Type, ConstantVals: *const &'a Value, Count: c_uint, ) -> &'a Value; - |wrap pub(crate) fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value; + pub(crate) fn LLVMConstVector(ScalarConstantVals: *const &Value, Size: c_uint) -> &Value; // Constant expressions - |wrap pub(crate) fn LLVMConstInBoundsGEP2<'a>( + pub(crate) fn LLVMConstInBoundsGEP2<'a>( ty: &'a Type, ConstantVal: &'a Value, ConstantIndices: *const &'a Value, NumIndices: c_uint, ) -> &'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 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; 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>; @@ -1258,11 +1180,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); - |wrap pub(crate) fn LLVMGlobalGetValueType(Global: &Value) -> &Type; + pub(crate) fn LLVMGlobalGetValueType(Global: &Value) -> &Type; // Operations on global variables pub(crate) safe fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; - |wrap pub(crate) fn LLVMAddGlobal<'a>(M: &'a Module, Ty: &'a Type, Name: *const c_char) -> &'a Value; + 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>; @@ -1277,7 +1199,7 @@ unsafe extern "C" { pub(crate) safe fn LLVMRustSetTailCallKind(CallInst: &Value, Kind: TailCallKind); // Operations on attributes - |wrap pub(crate) fn LLVMCreateStringAttribute( + pub(crate) fn LLVMCreateStringAttribute( C: &Context, Name: *const c_char, NameLen: c_uint, @@ -1300,11 +1222,11 @@ unsafe extern "C" { // Operations on parameters pub(crate) fn LLVMIsAArgument(Val: &Value) -> Option<&Value>; pub(crate) safe fn LLVMCountParams(Fn: &Value) -> c_uint; - |wrap pub(crate) fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value; + pub(crate) fn LLVMGetParam(Fn: &Value, Index: c_uint) -> &Value; // Operations on basic blocks - |wrap pub(crate) fn LLVMGetBasicBlockParent(BB: &BasicBlock) -> &Value; - |wrap pub(crate) fn LLVMAppendBasicBlockInContext<'a>( + pub(crate) fn LLVMGetBasicBlockParent(BB: &BasicBlock) -> &Value; + pub(crate) fn LLVMAppendBasicBlockInContext<'a>( C: &'a Context, Fn: &'a Value, Name: *const c_char, @@ -1314,7 +1236,7 @@ unsafe extern "C" { pub(crate) fn LLVMGetInstructionParent(Inst: &Value) -> &BasicBlock; pub(crate) fn LLVMGetCalledValue(CallInst: &Value) -> Option<&Value>; pub(crate) fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>; - |wrap pub(crate) fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock; + pub(crate) fn LLVMGetFirstBasicBlock(Fn: &Value) -> &BasicBlock; pub(crate) fn LLVMGetOperand(Val: &Value, Index: c_uint) -> Option<&Value>; // Operations on call sites @@ -1332,10 +1254,9 @@ unsafe extern "C" { ); // Instruction builders - |wrap #[allow(clippy::mut_from_ref)] // FIXME? pub(crate) fn LLVMCreateBuilderInContext(C: &Context) -> &mut Builder<'_>; pub(crate) fn LLVMPositionBuilderAtEnd<'a>(Builder: &Builder<'a>, Block: &'a BasicBlock); - |wrap pub(crate) fn LLVMGetInsertBlock<'a>(Builder_: &Builder<'a>) -> &'a BasicBlock; + pub(crate) fn LLVMGetInsertBlock<'a>(Builder: &Builder<'a>) -> &'a BasicBlock; pub(crate) fn LLVMDisposeBuilder<'a>(Builder: &'a mut Builder<'a>); // Metadata @@ -1343,30 +1264,30 @@ unsafe extern "C" { pub(crate) fn LLVMGetCurrentDebugLocation2<'a>(Builder: &Builder<'a>) -> Option<&'a Metadata>; // Terminators - |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>( + 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>( B: &Builder<'a>, If: &'a Value, Then: &'a BasicBlock, Else: &'a BasicBlock, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildSwitch<'a>( + pub(crate) fn LLVMBuildSwitch<'a>( B: &Builder<'a>, V: &'a Value, Else: &'a BasicBlock, NumCases: c_uint, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildLandingPad<'a>( + pub(crate) fn LLVMBuildLandingPad<'a>( B: &Builder<'a>, Ty: &'a Type, PersFn: Option<&'a Value>, NumClauses: c_uint, Name: *const c_char, ) -> &'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 LLVMBuildResume<'a>(B: &Builder<'a>, Exn: &'a Value) -> &'a Value; + pub(crate) fn LLVMBuildUnreachable<'a>(B: &Builder<'a>) -> &'a Value; pub(crate) fn LLVMBuildCleanupPad<'a>( B: &Builder<'a>, @@ -1412,170 +1333,170 @@ unsafe extern "C" { pub(crate) fn LLVMSetCleanup(LandingPad: &Value, Val: Bool); // Arithmetic - |wrap pub(crate) fn LLVMBuildAdd<'a>( + pub(crate) fn LLVMBuildAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFAdd<'a>( + pub(crate) fn LLVMBuildFAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildSub<'a>( + pub(crate) fn LLVMBuildSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFSub<'a>( + pub(crate) fn LLVMBuildFSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildMul<'a>( + pub(crate) fn LLVMBuildMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFMul<'a>( + pub(crate) fn LLVMBuildFMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildUDiv<'a>( + pub(crate) fn LLVMBuildUDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildExactUDiv<'a>( + pub(crate) fn LLVMBuildExactUDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildSDiv<'a>( + pub(crate) fn LLVMBuildSDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildExactSDiv<'a>( + pub(crate) fn LLVMBuildExactSDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFDiv<'a>( + pub(crate) fn LLVMBuildFDiv<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildURem<'a>( + pub(crate) fn LLVMBuildURem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildSRem<'a>( + pub(crate) fn LLVMBuildSRem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFRem<'a>( + pub(crate) fn LLVMBuildFRem<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildShl<'a>( + pub(crate) fn LLVMBuildShl<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildLShr<'a>( + pub(crate) fn LLVMBuildLShr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildAShr<'a>( + pub(crate) fn LLVMBuildAShr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildNSWAdd<'a>( + pub(crate) fn LLVMBuildNSWAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildNUWAdd<'a>( + pub(crate) fn LLVMBuildNUWAdd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildNSWSub<'a>( + pub(crate) fn LLVMBuildNSWSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildNUWSub<'a>( + pub(crate) fn LLVMBuildNUWSub<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildNSWMul<'a>( + pub(crate) fn LLVMBuildNSWMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildNUWMul<'a>( + pub(crate) fn LLVMBuildNUWMul<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildAnd<'a>( + pub(crate) fn LLVMBuildAnd<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildOr<'a>( + pub(crate) fn LLVMBuildOr<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildXor<'a>( + pub(crate) fn LLVMBuildXor<'a>( B: &Builder<'a>, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) + pub(crate) fn LLVMBuildNeg<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFNeg<'a>( + pub(crate) fn LLVMBuildFNeg<'a>( B: &Builder<'a>, V: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildNot<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) + pub(crate) fn LLVMBuildNot<'a>(B: &Builder<'a>, V: &'a Value, Name: *const c_char) -> &'a Value; // Extra flags on arithmetic @@ -1584,21 +1505,21 @@ unsafe extern "C" { pub(crate) fn LLVMSetNSW(ArithInst: &Value, HasNSW: Bool); // Memory - |wrap pub(crate) fn LLVMBuildAlloca<'a>( + pub(crate) fn LLVMBuildAlloca<'a>( B: &Builder<'a>, Ty: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildLoad2<'a>( + pub(crate) fn LLVMBuildLoad2<'a>( B: &Builder<'a>, Ty: &'a Type, PointerVal: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildStore<'a>(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value; + pub(crate) fn LLVMBuildStore<'a>(B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value) -> &'a Value; - |wrap pub(crate) fn LLVMBuildGEPWithNoWrapFlags<'a>( + pub(crate) fn LLVMBuildGEPWithNoWrapFlags<'a>( B: &Builder<'a>, Ty: &'a Type, Pointer: &'a Value, @@ -1609,85 +1530,85 @@ unsafe extern "C" { ) -> &'a Value; // Casts - |wrap pub(crate) fn LLVMBuildTrunc<'a>( + pub(crate) fn LLVMBuildTrunc<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildZExt<'a>( + pub(crate) fn LLVMBuildZExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildSExt<'a>( + pub(crate) fn LLVMBuildSExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFPToUI<'a>( + pub(crate) fn LLVMBuildFPToUI<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFPToSI<'a>( + pub(crate) fn LLVMBuildFPToSI<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildUIToFP<'a>( + pub(crate) fn LLVMBuildUIToFP<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildSIToFP<'a>( + pub(crate) fn LLVMBuildSIToFP<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFPTrunc<'a>( + pub(crate) fn LLVMBuildFPTrunc<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFPExt<'a>( + pub(crate) fn LLVMBuildFPExt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildPtrToInt<'a>( + pub(crate) fn LLVMBuildPtrToInt<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildIntToPtr<'a>( + pub(crate) fn LLVMBuildIntToPtr<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildBitCast<'a>( + pub(crate) fn LLVMBuildBitCast<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildPointerCast<'a>( + pub(crate) fn LLVMBuildPointerCast<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildIntCast2<'a>( + pub(crate) fn LLVMBuildIntCast2<'a>( B: &Builder<'a>, Val: &'a Value, DestTy: &'a Type, @@ -1696,14 +1617,14 @@ unsafe extern "C" { ) -> &'a Value; // Comparisons - |wrap pub(crate) fn LLVMBuildICmp<'a>( + pub(crate) fn LLVMBuildICmp<'a>( B: &Builder<'a>, Op: c_uint, LHS: &'a Value, RHS: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFCmp<'a>( + pub(crate) fn LLVMBuildFCmp<'a>( B: &Builder<'a>, Op: c_uint, LHS: &'a Value, @@ -1712,48 +1633,48 @@ unsafe extern "C" { ) -> &'a Value; // Miscellaneous instructions - |wrap pub(crate) fn LLVMBuildPhi<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) + pub(crate) fn LLVMBuildPhi<'a>(B: &Builder<'a>, Ty: &'a Type, Name: *const c_char) -> &'a Value; - |wrap pub(crate) fn LLVMBuildSelect<'a>( + pub(crate) fn LLVMBuildSelect<'a>( B: &Builder<'a>, If: &'a Value, Then: &'a Value, Else: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildVAArg<'a>( + pub(crate) fn LLVMBuildVAArg<'a>( B: &Builder<'a>, list: &'a Value, Ty: &'a Type, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildExtractElement<'a>( + pub(crate) fn LLVMBuildExtractElement<'a>( B: &Builder<'a>, VecVal: &'a Value, Index: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildInsertElement<'a>( + pub(crate) fn LLVMBuildInsertElement<'a>( B: &Builder<'a>, VecVal: &'a Value, EltVal: &'a Value, Index: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildShuffleVector<'a>( + pub(crate) fn LLVMBuildShuffleVector<'a>( B: &Builder<'a>, V1: &'a Value, V2: &'a Value, Mask: &'a Value, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildExtractValue<'a>( + pub(crate) fn LLVMBuildExtractValue<'a>( B: &Builder<'a>, AggVal: &'a Value, Index: c_uint, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildInsertValue<'a>( + pub(crate) fn LLVMBuildInsertValue<'a>( B: &Builder<'a>, AggVal: &'a Value, EltVal: &'a Value, @@ -1762,7 +1683,7 @@ unsafe extern "C" { ) -> &'a Value; // Atomic Operations - |wrap pub(crate) fn LLVMBuildAtomicCmpXchg<'a>( + pub(crate) fn LLVMBuildAtomicCmpXchg<'a>( B: &Builder<'a>, LHS: &'a Value, CMP: &'a Value, @@ -1774,7 +1695,7 @@ unsafe extern "C" { pub(crate) fn LLVMSetWeak(CmpXchgInst: &Value, IsWeak: Bool); - |wrap pub(crate) fn LLVMBuildAtomicRMW<'a>( + pub(crate) fn LLVMBuildAtomicRMW<'a>( B: &Builder<'a>, Op: AtomicRmwBinOp, LHS: &'a Value, @@ -1783,7 +1704,7 @@ unsafe extern "C" { SingleThreaded: Bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildFence<'a>( + pub(crate) fn LLVMBuildFence<'a>( B: &Builder<'a>, Order: AtomicOrdering, SingleThreaded: Bool, @@ -1793,7 +1714,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; - |wrap /// Creates a legacy pass manager -- only used for final codegen. + /// 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>); @@ -1804,7 +1725,7 @@ unsafe extern "C" { pub(crate) fn LLVMIsMultithreaded() -> Bool; - |wrap pub(crate) fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; + pub(crate) fn LLVMStructCreateNamed(C: &Context, Name: *const c_char) -> &Type; pub(crate) fn LLVMStructSetBody<'a>( StructTy: &'a Type, @@ -1813,13 +1734,13 @@ unsafe extern "C" { Packed: Bool, ); - |wrap pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; + pub(crate) safe fn LLVMMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value; pub(crate) safe fn LLVMSetUnnamedAddress(Global: &Value, UnnamedAddr: UnnamedAddr); pub(crate) fn LLVMIsAConstantInt(value_ref: &Value) -> Option<&ConstantInt>; - |wrap pub(crate) fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; + pub(crate) fn LLVMGetOrInsertComdat(M: &Module, Name: *const c_char) -> &Comdat; pub(crate) fn LLVMSetComdat(V: &Value, C: &Comdat); pub(crate) fn LLVMCreateOperandBundle( @@ -1830,7 +1751,7 @@ unsafe extern "C" { ) -> *mut OperandBundle<'_>; pub(crate) fn LLVMDisposeOperandBundle(Bundle: ptr::NonNull>); - |wrap pub(crate) fn LLVMBuildCallWithOperandBundles<'a>( + pub(crate) fn LLVMBuildCallWithOperandBundles<'a>( B: &Builder<'a>, Ty: &'a Type, Fn: &'a Value, @@ -1840,7 +1761,7 @@ unsafe extern "C" { NumBundles: c_uint, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildInvokeWithOperandBundles<'a>( + pub(crate) fn LLVMBuildInvokeWithOperandBundles<'a>( B: &Builder<'a>, Ty: &'a Type, Fn: &'a Value, @@ -1852,7 +1773,7 @@ unsafe extern "C" { NumBundles: c_uint, Name: *const c_char, ) -> &'a Value; - |wrap pub(crate) fn LLVMBuildCallBr<'a>( + pub(crate) fn LLVMBuildCallBr<'a>( B: &Builder<'a>, Ty: &'a Type, Fn: &'a Value, @@ -1879,30 +1800,30 @@ unsafe extern "C" { pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>); - |wrap pub(crate) fn LLVMDIBuilderCreateNameSpace<'ll>( - Builder_: &DIBuilder<'ll>, + 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; - |wrap pub(crate) fn LLVMDIBuilderCreateLexicalBlock<'ll>( - Builder_: &DIBuilder<'ll>, + pub(crate) fn LLVMDIBuilderCreateLexicalBlock<'ll>( + Builder: &DIBuilder<'ll>, Scope: &'ll Metadata, File: &'ll Metadata, Line: c_uint, Column: c_uint, ) -> &'ll Metadata; - |wrap pub(crate) fn LLVMDIBuilderCreateLexicalBlockFile<'ll>( - Builder_: &DIBuilder<'ll>, + 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; - |wrap pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( + pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>( Ctx: &'ll Context, Line: c_uint, Column: c_uint, @@ -1917,7 +1838,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustDisableSystemDialogsOnCrash(); // Create and destroy contexts. - |wrap pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; + pub(crate) fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context; /// See llvm::LLVMTypeKind::getTypeID. pub(crate) fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind; @@ -1943,13 +1864,13 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetDSOLocal(Global: &Value, is_dso_local: bool); // Operations on global variables - |wrap pub(crate) fn LLVMRustGetOrInsertGlobal<'a>( + pub(crate) fn LLVMRustGetOrInsertGlobal<'a>( M: &'a Module, Name: *const c_char, NameLen: size_t, T: &'a Type, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustInsertPrivateGlobal<'a>(M: &'a Module, T: &'a Type) -> &'a Value; + pub(crate) fn LLVMRustInsertPrivateGlobal<'a>(M: &'a Module, T: &'a Type) -> &'a Value; pub(crate) fn LLVMRustGetNamedValue( M: &Module, Name: *const c_char, @@ -1957,21 +1878,21 @@ unsafe extern "C" { ) -> Option<&Value>; // Operations on attributes - |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( + 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( C: &Context, effects: MemoryEffects, ) -> &Attribute; - |wrap pub(crate) fn LLVMRustCreateRangeAttribute( + pub(crate) fn LLVMRustCreateRangeAttribute( C: &Context, num_bits: c_uint, lower_words: *const u64, @@ -1979,7 +1900,7 @@ unsafe extern "C" { ) -> &Attribute; // Operations on functions - |wrap pub(crate) fn LLVMRustGetOrInsertFunction<'a>( + pub(crate) fn LLVMRustGetOrInsertFunction<'a>( M: &'a Module, Name: *const c_char, NameLen: size_t, @@ -2005,7 +1926,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetAllowReassoc(Instr: &Value); // Miscellaneous instructions - |wrap pub(crate) fn LLVMRustBuildMemCpy<'a>( + pub(crate) fn LLVMRustBuildMemCpy<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -2014,7 +1935,7 @@ unsafe extern "C" { Size: &'a Value, IsVolatile: bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildMemMove<'a>( + pub(crate) fn LLVMRustBuildMemMove<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -2023,7 +1944,7 @@ unsafe extern "C" { Size: &'a Value, IsVolatile: bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildMemSet<'a>( + pub(crate) fn LLVMRustBuildMemSet<'a>( B: &Builder<'a>, Dst: &'a Value, DstAlign: c_uint, @@ -2032,55 +1953,55 @@ unsafe extern "C" { IsVolatile: bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildVectorReduceFAdd<'a>( + pub(crate) fn LLVMRustBuildVectorReduceFAdd<'a>( B: &Builder<'a>, Acc: &'a Value, Src: &'a Value, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildVectorReduceFMul<'a>( + pub(crate) fn LLVMRustBuildVectorReduceFMul<'a>( B: &Builder<'a>, Acc: &'a Value, Src: &'a Value, ) -> &'a Value; - |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>( + 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>( B: &Builder<'a>, Src: &'a Value, IsSigned: bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildVectorReduceMax<'a>( + pub(crate) fn LLVMRustBuildVectorReduceMax<'a>( B: &Builder<'a>, Src: &'a Value, IsSigned: bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildVectorReduceFMin<'a>( + pub(crate) fn LLVMRustBuildVectorReduceFMin<'a>( B: &Builder<'a>, Src: &'a Value, IsNaN: bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildVectorReduceFMax<'a>( + pub(crate) fn LLVMRustBuildVectorReduceFMax<'a>( B: &Builder<'a>, Src: &'a Value, IsNaN: bool, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildMinNum<'a>( + pub(crate) fn LLVMRustBuildMinNum<'a>( B: &Builder<'a>, LHS: &'a Value, - RHS: &'a Value, + LHS: &'a Value, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildMaxNum<'a>( + pub(crate) fn LLVMRustBuildMaxNum<'a>( B: &Builder<'a>, LHS: &'a Value, - RHS: &'a Value, + LHS: &'a Value, ) -> &'a Value; // Atomic Operations - |wrap pub(crate) fn LLVMRustBuildAtomicLoad<'a>( + pub(crate) fn LLVMRustBuildAtomicLoad<'a>( B: &Builder<'a>, ElementType: &'a Type, PointerVal: &'a Value, @@ -2088,7 +2009,7 @@ unsafe extern "C" { Order: AtomicOrdering, ) -> &'a Value; - |wrap pub(crate) fn LLVMRustBuildAtomicStore<'a>( + pub(crate) fn LLVMRustBuildAtomicStore<'a>( B: &Builder<'a>, Val: &'a Value, Ptr: &'a Value, @@ -2142,7 +2063,7 @@ unsafe extern "C" { BufferOut: &RustString, ); - |wrap pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar( + pub(crate) fn LLVMRustCoverageCreatePGOFuncNameVar( F: &Value, FuncName: *const c_char, FuncNameLen: size_t, @@ -2182,8 +2103,8 @@ unsafe extern "C" { ValueLen: size_t, ); - |wrap pub(crate) fn LLVMRustDIBuilderCreateCompileUnit<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateCompileUnit<'a>( + Builder: &DIBuilder<'a>, Lang: c_uint, File: &'a DIFile, Producer: *const c_char, @@ -2199,8 +2120,8 @@ unsafe extern "C" { DebugNameTableKind: DebugNameTableKind, ) -> &'a DIDescriptor; - |wrap pub(crate) fn LLVMRustDIBuilderCreateFile<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateFile<'a>( + Builder: &DIBuilder<'a>, Filename: *const c_char, FilenameLen: size_t, Directory: *const c_char, @@ -2212,13 +2133,13 @@ unsafe extern "C" { SourceLen: size_t, ) -> &'a DIFile; - |wrap pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>( + Builder: &DIBuilder<'a>, ParameterTypes: &'a DIArray, ) -> &'a DICompositeType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( + Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, NameLen: size_t, @@ -2235,8 +2156,8 @@ unsafe extern "C" { Decl: Option<&'a DIDescriptor>, ) -> &'a DISubprogram; - |wrap pub(crate) fn LLVMRustDIBuilderCreateMethod<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateMethod<'a>( + Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, NameLen: size_t, @@ -2250,16 +2171,16 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; - |wrap pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>( + Builder: &DIBuilder<'a>, Name: *const c_char, NameLen: size_t, SizeInBits: u64, Encoding: c_uint, ) -> &'a DIBasicType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( + Builder: &DIBuilder<'a>, Type: &'a DIBasicType, Name: *const c_char, NameLen: size_t, @@ -2268,8 +2189,8 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - |wrap pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>( + Builder: &DIBuilder<'a>, PointeeTy: &'a DIType, SizeInBits: u64, AlignInBits: u32, @@ -2278,8 +2199,8 @@ unsafe extern "C" { NameLen: size_t, ) -> &'a DIDerivedType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( + Builder: &DIBuilder<'a>, Scope: Option<&'a DIDescriptor>, Name: *const c_char, NameLen: size_t, @@ -2296,8 +2217,8 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DICompositeType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( + Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, NameLen: size_t, @@ -2310,8 +2231,8 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIDerivedType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( + Builder: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, NameLen: size_t, @@ -2325,8 +2246,8 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateStaticMemberType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateStaticMemberType<'a>( + Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, Name: *const c_char, NameLen: size_t, @@ -2338,14 +2259,14 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIDerivedType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( + Builder: &DIBuilder<'a>, Tag: c_uint, Type: &'a DIType, ) -> &'a DIDerivedType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateStaticVariable<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateStaticVariable<'a>( + Builder: &DIBuilder<'a>, Context: Option<&'a DIScope>, Name: *const c_char, NameLen: size_t, @@ -2360,8 +2281,8 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIGlobalVariableExpression; - |wrap pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateVariable<'a>( + Builder: &DIBuilder<'a>, Tag: c_uint, Scope: &'a DIDescriptor, Name: *const c_char, @@ -2375,28 +2296,28 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - |wrap pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>( + Builder: &DIBuilder<'a>, Size: u64, AlignInBits: u32, Ty: &'a DIType, Subscripts: &'a DIArray, ) -> &'a DIType; - |wrap pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( + Builder: &DIBuilder<'a>, Lo: i64, Count: i64, ) -> &'a DISubrange; - |wrap pub(crate) fn LLVMRustDIBuilderGetOrCreateArray<'a>( - Builder_: &DIBuilder<'a>, + 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, @@ -2405,8 +2326,8 @@ unsafe extern "C" { InsertAtEnd: &'a BasicBlock, ); - |wrap pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( + Builder: &DIBuilder<'a>, Name: *const c_char, NameLen: size_t, Value: *const u64, @@ -2414,8 +2335,8 @@ unsafe extern "C" { IsUnsigned: bool, ) -> &'a DIEnumerator; - |wrap pub(crate) fn LLVMRustDIBuilderCreateEnumerationType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateEnumerationType<'a>( + Builder: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, NameLen: size_t, @@ -2428,8 +2349,8 @@ unsafe extern "C" { IsScoped: bool, ) -> &'a DIType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>( + Builder: &DIBuilder<'a>, Scope: Option<&'a DIScope>, Name: *const c_char, NameLen: size_t, @@ -2444,8 +2365,8 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DIType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( + Builder: &DIBuilder<'a>, Scope: &'a DIScope, Name: *const c_char, NameLen: size_t, @@ -2460,8 +2381,8 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DIDerivedType; - |wrap pub(crate) fn LLVMRustDIBuilderCreateTemplateTypeParameter<'a>( - Builder_: &DIBuilder<'a>, + pub(crate) fn LLVMRustDIBuilderCreateTemplateTypeParameter<'a>( + Builder: &DIBuilder<'a>, Scope: Option<&'a DIScope>, Name: *const c_char, NameLen: size_t, @@ -2469,7 +2390,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>, @@ -2588,7 +2509,6 @@ 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>; - |wrap #[allow(clippy::mut_from_ref)] // FIXME? pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>; pub(crate) fn LLVMRustArchiveIteratorNext<'a>( AIR: &ArchiveIterator<'a>, @@ -2623,7 +2543,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustWriteDiagnosticInfoToString(DI: &DiagnosticInfo, s: &RustString); pub(crate) fn LLVMRustGetDiagInfoKind(DI: &DiagnosticInfo) -> DiagnosticKind; - |wrap pub(crate) fn LLVMRustGetSMDiagnostic<'a>( + pub(crate) fn LLVMRustGetSMDiagnostic<'a>( DI: &'a DiagnosticInfo, cookie_out: &mut u64, ) -> &'a SMDiagnostic; @@ -2646,7 +2566,7 @@ unsafe extern "C" { Kind: ArchiveKind, isEC: bool, ) -> LLVMRustResult; - |wrap pub(crate) fn LLVMRustArchiveMemberNew<'a>( + pub(crate) fn LLVMRustArchiveMemberNew<'a>( Filename: *const c_char, Name: *const c_char, Child: Option<&ArchiveChild<'a>>, @@ -2661,14 +2581,14 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetModulePICLevel(M: &Module); pub(crate) fn LLVMRustSetModulePIELevel(M: &Module); pub(crate) fn LLVMRustSetModuleCodeModel(M: &Module, Model: CodeModel); - |wrap pub(crate) fn LLVMRustModuleBufferCreate(M: &Module) -> &'static mut ModuleBuffer; + 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); - |wrap pub(crate) fn LLVMRustThinLTOBufferCreate( + pub(crate) fn LLVMRustThinLTOBufferCreate( M: &Module, is_thin: bool, emit_summary: bool, @@ -2704,7 +2624,6 @@ unsafe extern "C" { Identifier: *const c_char, ) -> Option<&Module>; - |wrap #[allow(clippy::mut_from_ref)] // FIXME? pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; pub(crate) fn LLVMRustLinkerAdd( linker: &Linker<'_>, @@ -2759,4 +2678,3 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetNoSanitizeAddress(Global: &Value); pub(crate) fn LLVMRustSetNoSanitizeHWAddress(Global: &Value); } -} diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 41bad4d689329..312ebeca37b0f 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -411,16 +411,6 @@ lint_improper_ctypes_only_phantomdata = composed only of `PhantomData` lint_improper_ctypes_opaque = opaque types have no C equivalent -lint_improper_ctypes_pat_int1_help = consider using the base type instead, or wrapping `{$ty}` in an `Option<_>` -lint_improper_ctypes_pat_int1_reason = integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code -lint_improper_ctypes_pat_int2_help = consider using the base type instead -lint_improper_ctypes_pat_int2_reason = integer-pattern types with more than one disallowed value cannot have their value be provided by non-rust code - -lint_improper_ctypes_ptr_validity_help = consider using a raw pointer, or wrapping `{$ty}` in an `Option<_>` -lint_improper_ctypes_ptr_validity_reason = - boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead lint_improper_ctypes_slice_reason = slices have no C equivalent diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index e48389b4e2b62..8ecfe4ba6166c 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -372,14 +372,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, false) { + 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, false) { + 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 e426c6be09b16..622bf96d2cec8 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -4,7 +4,7 @@ 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, Const, ScalarInt, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{self, 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; @@ -862,14 +862,13 @@ 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/from, otherwise return `None`. +/// can, return the type that `ty` can be safely converted to, otherwise return `None`. /// Currently restricted to function pointers, boxes, references, `core::num::NonZero`, /// `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() { @@ -895,17 +894,11 @@ pub(crate) fn repr_nullable_ptr<'tcx>( }; 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) - } + if pattern_has_disallowed_values(*pat) || matches!(base.kind(), ty::Char) { + return get_nullable_type_from_pat(tcx, typing_env, *base, *pat); } else { - None - }; + return None; + } } if !ty_is_known_nonnull(tcx, typing_env, field_ty) { @@ -944,22 +937,13 @@ pub(crate) fn repr_nullable_ptr<'tcx>( } None } - 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) - } - } + ty::Pat(base, pat) => 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 { +/// returns whether a pattern type actually has disallowed values +pub(crate) fn pattern_has_disallowed_values<'tcx>(pat: ty::Pattern<'tcx>) -> bool { // note the logic in this function assumes that signed ints use one's complement representation, // which I believe is a requirement for rust @@ -1022,17 +1006,7 @@ pub(crate) fn get_pat_disallowed_value_count<'tcx>(pat: ty::Pattern<'tcx>) -> Op (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) + (start.to_bits(scalar_size), end.to_bits(scalar_size)) != (scalar_min, scalar_max) } ty::PatternKind::Or(patterns) => { // first, get a simplified an sorted view of the ranges @@ -1071,8 +1045,6 @@ pub(crate) fn get_pat_disallowed_value_count<'tcx>(pat: ty::Pattern<'tcx>) -> Op // (`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)); @@ -1103,28 +1075,11 @@ pub(crate) fn get_pat_disallowed_value_count<'tcx>(pat: ty::Pattern<'tcx>) -> Op 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; + // no range overlap: there are disallowed values + return true; } } - 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) } + prev_tail != scalar_max } } } @@ -1147,38 +1102,6 @@ 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_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); - match (compute_size_skeleton(ty), compute_size_skeleton(outer_ty)) { - (Ok(sk1), Ok(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 b41cec40074a1..06060ec4e838f 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -4,7 +4,6 @@ use std::iter; use std::ops::ControlFlow; use rustc_abi::VariantIdx; -use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; use rustc_hir::def::CtorKind; @@ -435,9 +434,6 @@ mod CTypesVisitorStateFlags { /// for times where we are only defining the type of something /// (struct/enum/union definitions, FnPtrs) 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)] @@ -445,21 +441,14 @@ mod CTypesVisitorStateFlags { enum CTypesVisitorState { None = CTypesVisitorStateFlags::NO_FLAGS, // uses bitflags from CTypesVisitorStateFlags - StaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::FOREIGN_VALUES, + StaticTy = CTypesVisitorStateFlags::STATIC, ExportedStaticTy = CTypesVisitorStateFlags::STATIC | CTypesVisitorStateFlags::DEFINED, - ExportedStaticMutTy = CTypesVisitorStateFlags::STATIC - | CTypesVisitorStateFlags::DEFINED - | CTypesVisitorStateFlags::FOREIGN_VALUES, - ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC - | CTypesVisitorStateFlags::DEFINED - | CTypesVisitorStateFlags::FOREIGN_VALUES, + ArgumentTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::DEFINED, ReturnTyInDefinition = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_RETURN | CTypesVisitorStateFlags::DEFINED, ArgumentTyInDeclaration = CTypesVisitorStateFlags::FUNC, - ReturnTyInDeclaration = CTypesVisitorStateFlags::FUNC - | CTypesVisitorStateFlags::FN_RETURN - | CTypesVisitorStateFlags::FOREIGN_VALUES, + ReturnTyInDeclaration = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::FN_RETURN, ArgumentTyInFnPtr = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::THEORETICAL, ReturnTyInFnPtr = CTypesVisitorStateFlags::FUNC | CTypesVisitorStateFlags::THEORETICAL @@ -509,27 +498,6 @@ impl CTypesVisitorState { // 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 { - // 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 - } } /// visitor used to recursively traverse MIR types and evaluate FFI-safety @@ -655,6 +623,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn visit_numeric(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> { // FIXME: for now, this is very incomplete, and seems to assume a x86_64 target match ty.kind() { + // note: before rust 1.77, 128-bit ints were not FFI-safe on x86_64 + // ...they probably are still unsafe on i686 and other x86_32 architectures ty::Int(..) | ty::Uint(..) | ty::Float(..) => FfiResult::FfiSafe, ty::Char => FfiResult::new_with_reason( @@ -662,7 +632,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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"), + _ => bug!("visit_numeric is to be called with numeric (char, int, float) types"), } } @@ -700,7 +670,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn visit_indirection( &self, state: CTypesVisitorState, - outer_ty: Option>, ty: Ty<'tcx>, inner_ty: Ty<'tcx>, indirection_type: IndirectionType, @@ -741,7 +710,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // - is the pointee FFI-safe? (it might not matter, see mere lines below) // - does the pointer type contain a non-zero assumption, but has a value given by non-rust code? // this block deals with the first two. - let mut ffi_res = match get_type_sizedness(self.cx, inner_ty) { + match get_type_sizedness(self.cx, inner_ty) { TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite => { // FIXME: // for now, we consider this to be safe even in the case of a FFI-unsafe pointee @@ -779,35 +748,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; return FfiResult::new_with_reason(ty, reason, help); } - }; - - // and now the third concern (does the pointer type contain a non-zero assumption, and is the value given by non-rust code?) - // technically, pointers with non-rust-given values could also be misaligned, pointing to the wrong thing, or outright dangling, but we assume they never are - ffi_res += if state.value_may_be_unchecked() { - let has_nonnull_assumption = match indirection_type { - IndirectionType::RawPtr => false, - IndirectionType::Ref | IndirectionType::Box => true, - }; - let has_optionlike_wrapper = if let Some(outer_ty) = outer_ty { - super::is_outer_optionlike_around_ty(self.cx, outer_ty, ty) - } else { - false - }; - - if has_nonnull_assumption && !has_optionlike_wrapper { - FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_ptr_validity_reason, - Some(fluent::lint_improper_ctypes_ptr_validity_help), - ) - } else { - FfiResult::FfiSafe - } - } else { - FfiResult::FfiSafe - }; - - ffi_res + } } /// Checks if the given `VariantDef`'s field types are "ffi-safe". @@ -1058,12 +999,7 @@ 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, - state.value_may_be_unchecked(), - ) { + 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); } @@ -1074,6 +1010,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ); } + // FIXME: connect `def.repr().int` to visit_numeric + // (for now it's OK, `repr(char)` doesn't exist and visit_numeric doesn't warn on anything else) + let non_exhaustive = def.variant_list_has_applicable_non_exhaustive(); // Check the contained variants. @@ -1150,13 +1089,7 @@ 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, - outer_ty, - ty, - inner_ty, - IndirectionType::Box, - ); + return self.visit_indirection(state, ty, inner_ty, IndirectionType::Box); } if def.is_phantom_data() { return FfiPhantom(ty); @@ -1178,56 +1111,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - ty::Pat(pat_ty, pat) => { + ty::Pat(pat_ty, _) => { #[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 + self.visit_numeric(pat_ty) } // types which likely have a stable representation, depending on the target architecture @@ -1308,16 +1199,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } ty::RawPtr(inner_ty, _) => { - return self.visit_indirection( - state, - outer_ty, - ty, - inner_ty, - IndirectionType::RawPtr, - ); + return self.visit_indirection(state, ty, inner_ty, IndirectionType::RawPtr); } ty::Ref(_, inner_ty, _) => { - return self.visit_indirection(state, outer_ty, ty, inner_ty, IndirectionType::Ref); + return self.visit_indirection(state, ty, inner_ty, IndirectionType::Ref); } ty::Array(inner_ty, _) => { @@ -1344,7 +1229,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // as a result, don't go into them when scanning for the safety of something else ty::FnPtr(sig_tys, hdr) => { let sig = sig_tys.with(hdr); - let inherent_safety = if sig.abi().is_rustic_abi() { + if sig.abi().is_rustic_abi() { FfiResult::new_with_reason( ty, fluent::lint_improper_ctypes_fnptr_reason, @@ -1352,21 +1237,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ) } else { FfiSafe - }; - - if let (Some(outer_ty), true) = (outer_ty, state.value_may_be_unchecked()) { - if !super::is_outer_optionlike_around_ty(self.cx, outer_ty, ty) { - inherent_safety - + FfiResult::new_with_reason( - ty, - fluent::lint_improper_ctypes_ptr_validity_reason, - Some(fluent::lint_improper_ctypes_ptr_validity_help), - ) - } else { - inherent_safety - } - } else { - inherent_safety } } @@ -1642,21 +1512,10 @@ impl ImproperCTypesLint { } /// Check that a `#[no_mangle]`/`#[export_name = _]` static variable is of a ffi-safe type - fn check_exported_static<'tcx>( - &self, - cx: &LateContext<'tcx>, - id: hir::OwnerId, - span: Span, - is_mut: bool, - ) { + fn check_exported_static<'tcx>(&self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) { let ty = cx.tcx.type_of(id).instantiate_identity(); let visitor = ImproperCTypesVisitor::new(cx); - let state = if is_mut { - CTypesVisitorState::ExportedStaticMutTy - } else { - CTypesVisitorState::ExportedStaticTy - }; - let ffi_res = visitor.check_for_type(state, ty); + let ffi_res = visitor.check_for_type(CTypesVisitorState::ExportedStaticTy, ty); self.process_ffi_result(cx, span, ffi_res, CItemKind::ExportedStatic); } @@ -1844,10 +1703,10 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { ); // FIXME: cx.tcx.has_attr no worky - // if let hir::ItemKind::Static(is_mut, _, _, _) = item.kind + // 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)) - if let hir::ItemKind::Static(is_mut, _, _, _) = item.kind { + if matches!(item.kind, hir::ItemKind::Static(..)) { let is_exported_static = cx.tcx.get_all_attrs(item.owner_id).iter().any(|x| { matches!( x, @@ -1858,11 +1717,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint { ) }); if is_exported_static { - let is_mut = match is_mut { - Mutability::Not => false, - Mutability::Mut => true, - }; - self.check_exported_static(cx, item.owner_id, ty.span, is_mut); + self.check_exported_static(cx, item.owner_id, ty.span); } } } diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 40c680e2c4c27..ed5edeef1617d 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -64,11 +64,10 @@ impl RustStringInner { /// `rustc_codegen_llvm`. #[unsafe(no_mangle)] pub unsafe extern "C" fn LLVMRustStringWriteImpl( - buf: Option<&RustString>, + buf: &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 7bd4d6030cae2..37f784a322cad 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -47,7 +47,6 @@ 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/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs index 4e9ee2e630ca9..badc0aa68af05 100644 --- a/library/compiler-builtins/compiler-builtins/src/lib.rs +++ b/library/compiler-builtins/compiler-builtins/src/lib.rs @@ -18,8 +18,6 @@ #![no_std] #![allow(unused_features)] #![allow(internal_features)] -// a lot of references without a wrapping Option<_> to deal with the nonzero assumption -#![allow(improper_c_fn_definitions)] // `mem::swap` cannot be used because it may generate references to memcpy in unoptimized code. #![allow(clippy::manual_swap)] // Support compiling on both stage0 and stage1 which may differ in supported stable features. diff --git a/library/proc_macro/src/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs index 3bc255e4d8926..3760749d83a54 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: Option Buffer>, - drop: Option, + reserve: extern "C" fn(Buffer, usize) -> Buffer, + drop: extern "C" fn(Buffer), } 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.unwrap())(b, xs.len()); + *self = (b.reserve)(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.unwrap())(b, xs.len()); + *self = (b.reserve)(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.unwrap())(b, 1); + *self = (b.reserve)(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.unwrap())(b); + (b.drop)(b); } } @@ -150,6 +150,6 @@ impl From> for Buffer { mem::drop(to_vec(b)); } - Buffer { data, len, capacity, reserve: Some(reserve), drop: Some(drop) } + Buffer { data, len, capacity, reserve, drop } } } diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index 7909e3315ab2b..e0e688434dce5 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: Option R>, + call: unsafe extern "C" fn(*mut Env, A) -> 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: Some(call::), env: f as *mut _ as *mut Env, _marker: PhantomData } + Closure { call: 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.unwrap_unchecked())(self.env, arg) } + unsafe { (self.call)(self.env, arg) } } } diff --git a/src/tools/clippy/tests/ui/boxed_local.rs b/src/tools/clippy/tests/ui/boxed_local.rs index 5f36190ab6810..3fb685aa36c11 100644 --- a/src/tools/clippy/tests/ui/boxed_local.rs +++ b/src/tools/clippy/tests/ui/boxed_local.rs @@ -171,11 +171,11 @@ mod issue_3739 { /// Issue #5542 /// /// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. -pub extern "C" fn do_not_warn_me(_c_pointer: Option>) -> () {} +pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} #[allow(missing_abi)] #[rustfmt::skip] // Forces rustfmt to not add ABI -pub extern fn do_not_warn_me_no_abi(_c_pointer: Option>) -> () {} +pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} // Issue #4804 - default implementation in trait mod issue4804 { diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index 9a4039fe4021a..be14e0762ff42 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -317,7 +317,6 @@ mod issue_9218 { } } -#[allow(improper_c_fn_definitions)] mod issue_11181 { extern "C" fn allowed(_v: &Vec) {} diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr index 7d18fe23119b8..87235057349e3 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.stderr +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -220,13 +220,13 @@ LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str { | ^^^^^^^^^^^^^^^^ help: change this to: `&str` error: writing `&String` instead of `&str` involves a new object where a slice will do - --> tests/ui/ptr_arg.rs:348:17 + --> tests/ui/ptr_arg.rs:347:17 | LL | fn good(v1: &String, v2: &String) { | ^^^^^^^ help: change this to: `&str` error: writing `&String` instead of `&str` involves a new object where a slice will do - --> tests/ui/ptr_arg.rs:348:30 + --> tests/ui/ptr_arg.rs:347:30 | LL | fn good(v1: &String, v2: &String) { | ^^^^^^^ help: change this to: `&str` diff --git a/tests/ui/abi/nullable-pointer-ffi-compat.rs b/tests/ui/abi/nullable-pointer-ffi-compat.rs index 428e018103599..f94f838723a56 100644 --- a/tests/ui/abi/nullable-pointer-ffi-compat.rs +++ b/tests/ui/abi/nullable-pointer-ffi-compat.rs @@ -14,7 +14,6 @@ use std::mem; -#[allow(improper_c_fn_definitions)] // it's worried about invalid pointers given as values of x #[inline(never)] extern "C" fn foo(x: &isize) -> Option<&isize> { Some(x) } diff --git a/tests/ui/abi/numbers-arithmetic/float-struct.rs b/tests/ui/abi/numbers-arithmetic/float-struct.rs index f3f7725c4f2d2..a958dc272722f 100644 --- a/tests/ui/abi/numbers-arithmetic/float-struct.rs +++ b/tests/ui/abi/numbers-arithmetic/float-struct.rs @@ -1,7 +1,5 @@ //@ run-pass -#![allow(improper_c_fn_definitions)] // unchecked-value references - use std::fmt::Debug; use std::hint::black_box; diff --git a/tests/ui/asm/issue-97490.rs b/tests/ui/asm/issue-97490.rs index 72827cd2d60b1..5f364a22bc437 100644 --- a/tests/ui/asm/issue-97490.rs +++ b/tests/ui/asm/issue-97490.rs @@ -2,8 +2,6 @@ //@ only-x86_64 //@ needs-asm-support -#[allow(improper_c_fn_definitions)] // it's worried about invalid pointers being given as the - // argument value pub type Yes = extern "sysv64" fn(&'static u8) -> !; fn main() { diff --git a/tests/ui/asm/naked-function-shim.rs b/tests/ui/asm/naked-function-shim.rs index f687919125374..4694d0cd963e3 100644 --- a/tests/ui/asm/naked-function-shim.rs +++ b/tests/ui/asm/naked-function-shim.rs @@ -8,7 +8,7 @@ //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none //@ [x86_64] needs-llvm-components: x86 -#![allow(improper_c_fn_definitions)] // unchecked-value references + #![feature(no_core, lang_items)] #![crate_type = "lib"] #![no_core] diff --git a/tests/ui/asm/naked-functions-unused.aarch64.stderr b/tests/ui/asm/naked-functions-unused.aarch64.stderr index 1b7dca0ec6bc6..bfb2923b0b8d6 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:17:32 + --> $DIR/naked-functions-unused.rs:16:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` @@ -12,109 +12,58 @@ LL | #![deny(unused)] = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:17:42 + --> $DIR/naked-functions-unused.rs:16: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:28:38 + --> $DIR/naked-functions-unused.rs:27: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:28:48 + --> $DIR/naked-functions-unused.rs:27: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:36:41 + --> $DIR/naked-functions-unused.rs:35: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:36:51 + --> $DIR/naked-functions-unused.rs:35: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:47:40 + --> $DIR/naked-functions-unused.rs:45: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:47:50 + --> $DIR/naked-functions-unused.rs:45: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:55:43 + --> $DIR/naked-functions-unused.rs:53: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:55:53 + --> $DIR/naked-functions-unused.rs:53:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` -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 +error: aborting due to 10 previous errors diff --git a/tests/ui/asm/naked-functions-unused.rs b/tests/ui/asm/naked-functions-unused.rs index 91db27e5e1662..945ab1a40ad0c 100644 --- a/tests/ui/asm/naked-functions-unused.rs +++ b/tests/ui/asm/naked-functions-unused.rs @@ -8,7 +8,6 @@ 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 { @@ -34,8 +33,7 @@ pub mod normal { } pub extern "C" fn method(&self, a: usize, b: usize) -> usize { - //~^ WARN uses type `&normal::Normal` - //~| ERROR unused variable: `a` + //~^ ERROR unused variable: `a` //~| ERROR unused variable: `b` unsafe { asm!("", options(noreturn)); @@ -53,8 +51,7 @@ pub mod normal { } extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { - //~^ WARN uses type `&normal::Normal` - //~| ERROR unused variable: `a` + //~^ ERROR unused variable: `a` //~| ERROR unused variable: `b` unsafe { asm!("", options(noreturn)); @@ -81,7 +78,6 @@ pub mod naked { #[unsafe(naked)] pub extern "C" fn method(&self, a: usize, b: usize) -> usize { - //~^ WARN uses type `&Naked` naked_asm!("") } } @@ -94,7 +90,6 @@ 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 1b7dca0ec6bc6..bfb2923b0b8d6 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:17:32 + --> $DIR/naked-functions-unused.rs:16:32 | LL | pub extern "C" fn function(a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_a` @@ -12,109 +12,58 @@ LL | #![deny(unused)] = note: `#[deny(unused_variables)]` implied by `#[deny(unused)]` error: unused variable: `b` - --> $DIR/naked-functions-unused.rs:17:42 + --> $DIR/naked-functions-unused.rs:16: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:28:38 + --> $DIR/naked-functions-unused.rs:27: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:28:48 + --> $DIR/naked-functions-unused.rs:27: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:36:41 + --> $DIR/naked-functions-unused.rs:35: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:36:51 + --> $DIR/naked-functions-unused.rs:35: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:47:40 + --> $DIR/naked-functions-unused.rs:45: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:47:50 + --> $DIR/naked-functions-unused.rs:45: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:55:43 + --> $DIR/naked-functions-unused.rs:53: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:55:53 + --> $DIR/naked-functions-unused.rs:53:53 | LL | extern "C" fn trait_method(&self, a: usize, b: usize) -> usize { | ^ help: if this is intentional, prefix it with an underscore: `_b` -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 +error: aborting due to 10 previous errors diff --git a/tests/ui/asm/named-asm-labels.rs b/tests/ui/asm/named-asm-labels.rs index a975ecd3cd441..e78553fd775f0 100644 --- a/tests/ui/asm/named-asm-labels.rs +++ b/tests/ui/asm/named-asm-labels.rs @@ -9,7 +9,7 @@ // codegen unit (except for naked fns) and so the label could be duplicated // which causes less readable LLVM errors and in the worst cases causes ICEs // or segfaults based on system dependent behavior and codegen flags. -#![allow(improper_c_fn_definitions)] // unchecked-value references + use std::arch::{asm, global_asm, naked_asm}; #[no_mangle] diff --git a/tests/ui/lint/clashing-extern-fn.rs b/tests/ui/lint/clashing-extern-fn.rs index b6090d730a454..e4477c9620221 100644 --- a/tests/ui/lint/clashing-extern-fn.rs +++ b/tests/ui/lint/clashing-extern-fn.rs @@ -498,14 +498,11 @@ 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 14a9cd7be42a1..0c27547a6ed8f 100644 --- a/tests/ui/lint/clashing-extern-fn.stderr +++ b/tests/ui/lint/clashing-extern-fn.stderr @@ -17,17 +17,8 @@ LL | fn hidden_niche_unsafe_cell() -> Option $DIR/clashing-extern-fn.rs:500:39 - | -LL | fn pt_non_zero_usize() -> pattern_type!(usize is 1..); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using the base type instead, or wrapping `(usize) is 1..` in an `Option<_>` - = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code - warning: `extern` block uses type `Option<(usize) is 0..>`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:503:54 + --> $DIR/clashing-extern-fn.rs:502:54 | LL | fn pt_non_zero_usize_opt_full_range() -> Option; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -35,30 +26,6 @@ LL | fn pt_non_zero_usize_opt_full_range() -> Option $DIR/clashing-extern-fn.rs:505:37 - | -LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using the base type instead, or wrapping `(usize) is 1..` in an `Option<_>` - = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code - -warning: `extern` block uses type `NonZeroUsize`, which is not FFI-safe - --> $DIR/clashing-extern-fn.rs:507:47 - | -LL | fn pt_non_zero_usize_wrapper() -> NonZeroUsize; - | ^^^^^^^^^^^^ not FFI-safe - | - = note: this struct/enum/union (`NonZeroUsize`) is FFI-unsafe due to a `(usize) is 1..` field -note: the type is defined here - --> $DIR/clashing-extern-fn.rs:498:9 - | -LL | struct NonZeroUsize(pattern_type!(usize is 1..)); - | ^^^^^^^^^^^^^^^^^^^ - = help: consider using the base type instead, or wrapping `(usize) is 1..` in an `Option<_>` - = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code - warning: `clash` redeclared with a different signature --> $DIR/clashing-extern-fn.rs:13:13 | @@ -301,7 +268,7 @@ LL | fn hidden_niche_unsafe_cell() -> Option Option>>` warning: `pt_non_null_ptr` redeclared with a different signature - --> $DIR/clashing-extern-fn.rs:519:13 + --> $DIR/clashing-extern-fn.rs:516:13 | LL | fn pt_non_null_ptr() -> pattern_type!(usize is 1..); | ---------------------------------------------------- `pt_non_null_ptr` previously declared here @@ -312,5 +279,5 @@ LL | fn pt_non_null_ptr() -> *const (); = note: expected `unsafe extern "C" fn() -> (usize) is 1..=` found `unsafe extern "C" fn() -> *const ()` -warning: 27 warnings emitted +warning: 24 warnings emitted diff --git a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs index 5bee42124e141..4f503471d7408 100644 --- a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs +++ b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.rs @@ -74,7 +74,7 @@ static INT: u32 = 42; 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` +// | FIXME: not warning about the &String feels wrong, but it's behind a FFI-safe reference so... #[allow(improper_c_fn_definitions)] extern "C" fn fn2a(e: UnsafeStruct) {} @@ -99,11 +99,10 @@ extern "C" fn fn3ou(e: outer::AllowedUnsafeStruct) {} #[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<'_>` +// the block above might become unsafe if/once we lint on the value assumptions of types #[allow(improper_c_fn_definitions)] extern "C" fn fn5a() -> UnsafeFromForeignStruct<'static> { UnsafeFromForeignStruct(&INT)} diff --git a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr index b95c1a5ed0a8e..68a1456826c38 100644 --- a/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/allow_improper_ctypes.stderr @@ -13,21 +13,6 @@ note: the lint level is defined here LL | #![deny(improper_c_callbacks)] | ^^^^^^^^^^^^^^^^^^^^ -error: `extern` fn uses type `&String`, which is not FFI-safe - --> $DIR/allow_improper_ctypes.rs:75: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)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - error: `extern` fn uses type `&str`, which is not FFI-safe --> $DIR/allow_improper_ctypes.rs:75:35 | @@ -36,6 +21,11 @@ LL | extern "C" fn fn1u(e: &String) -> &str {&*e} | = 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/allow_improper_ctypes.rs:1:26 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `UnsafeStruct`, which is not FFI-safe --> $DIR/allow_improper_ctypes.rs:81:23 @@ -87,35 +77,8 @@ LL | extern "C" fn fn3ou(e: outer::AllowedUnsafeStruct) {} = 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: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:30: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:105: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: `extern` callback uses type `&[A]`, which is not FFI-safe - --> $DIR/allow_improper_ctypes.rs:140:14 + --> $DIR/allow_improper_ctypes.rs:139:14 | LL | combine: extern "C" fn(&[A]) -> A, | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -125,7 +88,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:146:24 + --> $DIR/allow_improper_ctypes.rs:145:24 | LL | type FakeVTableMaker = extern "C" fn() -> FakeVTable; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -133,12 +96,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:138:1 + --> $DIR/allow_improper_ctypes.rs:137:1 | LL | struct FakeVTable{ | ^^^^^^^^^^^^^^^^^^^^ = help: consider using a struct instead = note: tuples have unspecified layout -error: aborting due to 11 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/lint/improper_ctypes/ctypes.rs b/tests/ui/lint/improper_ctypes/ctypes.rs index 9e7914fbb703d..9a2ba7b9d80d4 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.rs +++ b/tests/ui/lint/improper_ctypes/ctypes.rs @@ -76,7 +76,7 @@ extern "C" { 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 pat_type1() -> pattern_type!(u32 is 1..); //~ ERROR uses type `(u32) is 1..` + pub fn pat_type1() -> pattern_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)` @@ -146,7 +146,6 @@ 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" { diff --git a/tests/ui/lint/improper_ctypes/ctypes.stderr b/tests/ui/lint/improper_ctypes/ctypes.stderr index 11fdcee4d7359..2cb0aa9afb74e 100644 --- a/tests/ui/lint/improper_ctypes/ctypes.stderr +++ b/tests/ui/lint/improper_ctypes/ctypes.stderr @@ -30,15 +30,6 @@ LL | pub fn char_type(p: char); = help: consider using `u32` or `libc::wchar_t` instead = note: the `char` type has no C equivalent -error: `extern` block uses type `(u32) is 1..`, which is not FFI-safe - --> $DIR/ctypes.rs:79:27 - | -LL | pub fn pat_type1() -> pattern_type!(u32 is 1..); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using the base type instead, or wrapping `(u32) is 1..` in an `Option<_>` - = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code - error: `extern` block uses type `&dyn Bar`, which is not FFI-safe --> $DIR/ctypes.rs:81:26 | @@ -222,15 +213,5 @@ note: the lint level is defined here 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:148: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 22 previous errors +error: aborting due to 20 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 84e2a5ab03e0c..bcce94ddac3ad 100644 --- a/tests/ui/lint/improper_ctypes/lint-73249-2.rs +++ b/tests/ui/lint/improper_ctypes/lint-73249-2.rs @@ -1,3 +1,5 @@ +//@ check-pass // possible FIXME: see below + #![feature(type_alias_impl_trait)] #![deny(improper_ctypes)] @@ -24,9 +26,8 @@ struct A { } extern "C" { - // 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<()>` + // possible FIXME: the unsafety of Qux is unseen, as it is behing a FFI-safe indirection + fn lint_me() -> 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 deleted file mode 100644 index c835d276d459c..0000000000000 --- a/tests/ui/lint/improper_ctypes/lint-73249-2.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: `extern` block uses type `A<()>`, which is not FFI-safe - --> $DIR/lint-73249-2.rs:29:21 - | -LL | fn lint_me() -> A<()>; - | ^^^^^ not FFI-safe - | - = note: this struct/enum/union (`A<()>`) is FFI-unsafe due to a `&Qux` field -note: the type is defined here - --> $DIR/lint-73249-2.rs:22:1 - | -LL | struct A { - | ^^^^^^^^^^^^^^^^ - = help: consider using a raw pointer, or wrapping `&Qux` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code -note: the lint level is defined here - --> $DIR/lint-73249-2.rs:2:9 - | -LL | #![deny(improper_ctypes)] - | ^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/improper_ctypes/lint-fn.rs b/tests/ui/lint/improper_ctypes/lint-fn.rs index 066ace32603f3..859b314c07506 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.rs +++ b/tests/ui/lint/improper_ctypes/lint-fn.rs @@ -75,7 +75,6 @@ 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! @@ -122,20 +121,16 @@ pub extern "C" fn transparent_str(p: TransparentStr) { } //~^ ERROR: uses type `TransparentStr` pub extern "C" fn transparent_fn(p: TransparentBadFn) { } -//~^ ERROR: uses type `TransparentBadFn` -// ^ FIXME it doesn't see the actual FnPtr's error... but at least it reports it elsewhere? +// ^ possible FIXME: it doesn't see the actual FnPtr's error... +// but at least it reports it elsewhere? pub extern "C" fn good3(fptr: Option) { } pub extern "C" fn 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 argument_with_assumptions_6(s: StructWithProjectionAndLifetime) { } -//~^ ERROR: uses type `StructWithProjectionAndLifetime<'_>` -// note: the type translation might be a little eager for -// `::It` pub extern "C" fn good7(fptr: extern "C" fn() -> ()) { } @@ -152,7 +147,6 @@ 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 `TransparentRef<'_>` 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 d29eb46446331..a6b975ff1fd94 100644 --- a/tests/ui/lint/improper_ctypes/lint-fn.stderr +++ b/tests/ui/lint/improper_ctypes/lint-fn.stderr @@ -36,18 +36,8 @@ 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:77:31 - | -LL | pub extern "C" fn box_type(p: Box) { } - | ^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe - --> $DIR/lint-fn.rs:83:34 + --> $DIR/lint-fn.rs:82:34 | LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } | ^^^^^^^^^ not FFI-safe @@ -56,7 +46,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:86:35 + --> $DIR/lint-fn.rs:85:35 | LL | pub extern "C" fn boxed_string(p: Box) { } | ^^^^^^^^ not FFI-safe @@ -65,7 +55,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:89:34 + --> $DIR/lint-fn.rs:88:34 | LL | pub extern "C" fn boxed_trait(p: Box) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -73,7 +63,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:92:32 + --> $DIR/lint-fn.rs:91:32 | LL | pub extern "C" fn char_type(p: char) { } | ^^^^ not FFI-safe @@ -82,7 +72,7 @@ LL | pub extern "C" fn char_type(p: char) { } = note: the `char` type has no C equivalent error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-fn.rs:95:33 + --> $DIR/lint-fn.rs:94:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -91,7 +81,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:98:34 + --> $DIR/lint-fn.rs:97:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -100,7 +90,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:101:32 + --> $DIR/lint-fn.rs:100:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -114,7 +104,7 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:104:40 + --> $DIR/lint-fn.rs:103:40 | LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -127,7 +117,7 @@ LL | pub struct ZeroSizeWithPhantomData(PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:107:51 + --> $DIR/lint-fn.rs:106:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -135,7 +125,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:112:30 + --> $DIR/lint-fn.rs:111:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -144,7 +134,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:115:31 + --> $DIR/lint-fn.rs:114:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -153,7 +143,7 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `TransparentStr`, which is not FFI-safe - --> $DIR/lint-fn.rs:121:38 + --> $DIR/lint-fn.rs:120:38 | LL | pub extern "C" fn transparent_str(p: TransparentStr) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -167,66 +157,8 @@ 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:124: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:130:53 - | -LL | pub extern "C" fn argument_with_assumptions_4(aptr: &[u8; 4 as usize]) { } - | ^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer, or wrapping `&[u8; 4]` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - -error: `extern` fn uses type `StructWithProjectionAndLifetime<'_>`, which is not FFI-safe - --> $DIR/lint-fn.rs:135:50 - | -LL | pub extern "C" fn argument_with_assumptions_6(s: StructWithProjectionAndLifetime) { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = note: this struct/enum/union (`StructWithProjectionAndLifetime<'_>`) is FFI-unsafe due to a `&mut StructWithProjectionAndLifetime<'_>` field -note: the type is defined here - --> $DIR/lint-fn.rs:19:1 - | -LL | pub struct StructWithProjectionAndLifetime<'a>( - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: consider using a raw pointer, or wrapping `&mut StructWithProjectionAndLifetime<'_>` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - -error: `extern` fn uses type `TransparentRef<'_>`, which is not FFI-safe - --> $DIR/lint-fn.rs:154:51 - | -LL | pub extern "C" fn argument_with_assumptions_14(p: TransparentRef) { } - | ^^^^^^^^^^^^^^ not FFI-safe - | - = note: this struct/enum/union (`TransparentRef<'_>`) is FFI-unsafe due to a `&TransparentInt` field -note: the type is defined here - --> $DIR/lint-fn.rs:49:1 - | -LL | pub struct TransparentRef<'a>(&'a TransparentInt); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: consider using a raw pointer, or wrapping `&TransparentInt` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-fn.rs:180:43 + --> $DIR/lint-fn.rs:174:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -234,7 +166,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:193:39 + --> $DIR/lint-fn.rs:187:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -243,7 +175,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:196:41 + --> $DIR/lint-fn.rs:190:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe @@ -251,5 +183,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 23 previous errors +error: aborting due to 18 previous errors diff --git a/tests/ui/lint/improper_ctypes/lint-pattern-types.rs b/tests/ui/lint/improper_ctypes/lint-pattern-types.rs deleted file mode 100644 index eb39a1dfeba19..0000000000000 --- a/tests/ui/lint/improper_ctypes/lint-pattern-types.rs +++ /dev/null @@ -1,89 +0,0 @@ -//@ revisions: size64 size32 -//@[size64] only-64bit -//@[size32] only-32bit -// (this is needed because stderr writes out usize::MAX-1) - - -#![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, - e4: Option, - f1: Option, - f2: Option, - f3: Option, - f4: Option, - 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, - g32: Option, - g33: Option, - g34: Option, - //g4: Option, - - // 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.size32.stderr b/tests/ui/lint/improper_ctypes/lint-pattern-types.size32.stderr deleted file mode 100644 index 86fb09411d924..0000000000000 --- a/tests/ui/lint/improper_ctypes/lint-pattern-types.size32.stderr +++ /dev/null @@ -1,88 +0,0 @@ -error: `extern` fn uses type `Option<(u32) is 0..>`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:27: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:10: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:28: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:30: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:31: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 `(usize) is 1..`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:55: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..=4294967293`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:56: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 -2147483647..`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:58:9 - | -LL | h4: pattern_type!(isize is ISZP1..), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using the base type instead, or wrapping `(isize) is -2147483647..` 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:60: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:60: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 9 previous errors - diff --git a/tests/ui/lint/improper_ctypes/lint-pattern-types.size64.stderr b/tests/ui/lint/improper_ctypes/lint-pattern-types.size64.stderr deleted file mode 100644 index 84c9a56c7f1cf..0000000000000 --- a/tests/ui/lint/improper_ctypes/lint-pattern-types.size64.stderr +++ /dev/null @@ -1,88 +0,0 @@ -error: `extern` fn uses type `Option<(u32) is 0..>`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:27: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:10: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:28: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:30: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:31: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 `(usize) is 1..`, which is not FFI-safe - --> $DIR/lint-pattern-types.rs:55: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:56: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:58: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:60: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:60: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 9 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 6494451049c27..2ce99d5c3e659 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.rs @@ -32,7 +32,7 @@ struct SomeStruct{ impl SomeStruct{ extern "C" fn klol( // Ref[Struct] - &self //~ ERROR: `extern` fn uses type `&SomeStruct` + &self ){} } @@ -88,14 +88,14 @@ pub trait TimesTwo: std::ops::Add + Sized + Clone //} extern "C" fn t2_box( // Box[Param] - self: Box, //~ ERROR: `extern` fn uses type `Box` + self: Box, // Alias ) -> as std::ops::Add>>::Output { self.clone() + self } extern "C" fn t2_ref( // Ref[Param] - &self //~ ERROR: `extern` fn uses type `&Self` + &self // Alias ) -> <&Self as std::ops::Add<&Self>>::Output { self + self @@ -131,7 +131,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..), // Struct SomeStruct{b:p4,..}: SomeStruct, // Union @@ -182,7 +182,7 @@ extern "C" fn all_ty_kinds_in_ptr( // Pat nz: *const pattern_type!(u32 is 1..), // Ptr[Struct] - SomeStruct{b: ref p4,..}: & SomeStruct, //~ ERROR: uses type `&SomeStruct` + SomeStruct{b: ref p4,..}: & SomeStruct, // Ptr[Union] u2: *const SomeUnion, // Ptr[Enum], @@ -268,7 +268,7 @@ extern "C" fn all_ty_kinds_in_box( // Box[Str] s2: Box, //~ ERROR: uses type `Box` // Box[Char] - c: Box, //~ ERROR: uses type `Box` + 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) @@ -280,7 +280,7 @@ extern "C" fn all_ty_kinds_in_box( // Pat nz: Option>, // Ref[Struct] - SomeStruct{b: ref p4,..}: &SomeStruct, //~ ERROR: uses type `&SomeStruct` + SomeStruct{b: ref p4,..}: &SomeStruct, // Box[Union] u2: Option>, // Box[Enum], @@ -294,11 +294,10 @@ extern "C" fn all_ty_kinds_in_box( // Box[Struct] e3: Box, //~ ERROR: uses type `Box` // Box[Never] - // (considered FFI-unsafe because of null pointers, not the litteral uninhabited type. smh.) - x: Box, //~ ERROR: uses type `Box` + x: Box, //r1: Box, // Box[FnPtr] - f2: Boxu8>, //~ ERROR: uses type `Box u8>` + f2: Boxu8>, // Box[Dynamic] f3: Boxu8>, //~ ERROR: uses type `Box u8>` // Box[Dynamic] diff --git a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr index f30c6ddcf189f..05b15c3009fab 100644 --- a/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr +++ b/tests/ui/lint/improper_ctypes/lint-tykind-fuzz.stderr @@ -7,41 +7,6 @@ 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)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -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: `extern` fn uses type `String`, which is not FFI-safe --> $DIR/lint-tykind-fuzz.rs:120:5 | @@ -50,6 +15,11 @@ LL | s:String, | = 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/lint-tykind-fuzz.rs:5:26 + | +LL | #![deny(improper_ctypes, improper_c_fn_definitions)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `&str`, which is not FFI-safe --> $DIR/lint-tykind-fuzz.rs:122:6 @@ -105,15 +75,6 @@ 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:134:7 - | -LL | nz: pattern_type!(u32 is 1..), - | ^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using the base type instead, or wrapping `(u32) is 1..` in an `Option<_>` - = note: integer-pattern types with one disallowed value and no `Option` wrapping cannot have their value be provided by non-rust code - error: `extern` fn uses type `&StructWithDyn`, which is not FFI-safe --> $DIR/lint-tykind-fuzz.rs:148:7 | @@ -190,16 +151,6 @@ LL | (p2, p3):(*const u8, *const u8), = 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:185:29 - | -LL | SomeStruct{b: ref p4,..}: & SomeStruct, - | ^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer, or wrapping `&SomeStruct` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - error: `extern` fn uses type `*const StructWithDyn`, which is not FFI-safe --> $DIR/lint-tykind-fuzz.rs:197:7 | @@ -296,16 +247,6 @@ 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:271:6 - | -LL | c: Box, - | ^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe --> $DIR/lint-tykind-fuzz.rs:273:7 | @@ -324,16 +265,6 @@ 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:283:29 - | -LL | SomeStruct{b: ref p4,..}: &SomeStruct, - | ^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer, or wrapping `&SomeStruct` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - error: `extern` fn uses type `Box`, which is not FFI-safe --> $DIR/lint-tykind-fuzz.rs:295:7 | @@ -342,28 +273,8 @@ 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:298:6 - | -LL | x: Box, - | ^^^^^^ not FFI-safe - | - = help: consider using a raw pointer, or wrapping `Box` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - -error: `extern` fn uses type `Box u8>`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:301:7 - | -LL | f2: Boxu8>, - | ^^^^^^^^^^^^^^^ not FFI-safe - | - = help: consider using a raw pointer, or wrapping `Box u8>` in an `Option<_>` - = note: boxes, references, and function pointers are assumed to be valid (non-null, non-dangling, aligned) pointers, - which cannot be garanteed if their values are produced by non-rust code - error: `extern` fn uses type `Box u8>`, which is not FFI-safe - --> $DIR/lint-tykind-fuzz.rs:303:7 + --> $DIR/lint-tykind-fuzz.rs:302:7 | LL | f3: Boxu8>, | ^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -371,7 +282,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:305:7 + --> $DIR/lint-tykind-fuzz.rs:304:7 | LL | d2: Box>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -379,12 +290,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:309: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 42 previous errors; 1 warning emitted +error: aborting due to 33 previous errors; 1 warning emitted diff --git a/tests/ui/lint/improper_ctypes/mustpass-134060.rs b/tests/ui/lint/improper_ctypes/mustpass-134060.rs index 17ec17a0373f8..b30be99673687 100644 --- a/tests/ui/lint/improper_ctypes/mustpass-134060.rs +++ b/tests/ui/lint/improper_ctypes/mustpass-134060.rs @@ -10,7 +10,6 @@ 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/mustpass-134060.stderr b/tests/ui/lint/improper_ctypes/mustpass-134060.stderr index 4a329f26c4790..446ab3c1ccd5f 100644 --- a/tests/ui/lint/improper_ctypes/mustpass-134060.stderr +++ b/tests/ui/lint/improper_ctypes/mustpass-134060.stderr @@ -1,14 +1,3 @@ -warning: `extern` fn uses type `&Self`, which is not FFI-safe - --> $DIR/mustpass-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/mustpass-134060.rs:11:34 | @@ -17,6 +6,7 @@ LL | extern "C" fn foo_(&self, _: ()) -> i64 { | = help: consider using a struct instead = note: tuples have unspecified layout + = note: `#[warn(improper_c_fn_definitions)]` on by default -warning: 2 warnings emitted +warning: 1 warning emitted From 0d80e12c3f24096df33c334c458eb90d0d5b1210 Mon Sep 17 00:00:00 2001 From: niacdoial Date: Thu, 5 Jun 2025 10:19:10 +0200 Subject: [PATCH 19/19] lint ImproperCTypes: misc. changes due to review - relaxed the uninhabited types allowed on function return --- compiler/rustc_lint/messages.ftl | 3 - .../rustc_lint/src/types/improper_ctypes.rs | 66 +++++-------------- .../lint/improper_ctypes/lint_uninhabited.rs | 4 +- .../improper_ctypes/lint_uninhabited.stderr | 44 +------------ 4 files changed, 21 insertions(+), 96 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 312ebeca37b0f..83f49b5d27aa8 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -432,10 +432,7 @@ 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 06060ec4e838f..9f07e957578ab 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -581,40 +581,16 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } /// Checks whether an uninhabited type (one without valid values) is safe-ish to have here - fn visit_uninhabited( - &self, - state: CTypesVisitorState, - outer_ty: Option>, - ty: Ty<'tcx>, - ) -> FfiResult<'tcx> { - if state.is_in_function_return() - && matches!(outer_ty.map(|ty| ty.kind()), None | Some(ty::FnPtr(..))) - { + fn visit_uninhabited(&self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> { + if state.is_in_function_return() { 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 - } - } + ty::Adt(..) => fluent::lint_improper_ctypes_uninhabited_enum, + ty::Never => fluent::lint_improper_ctypes_uninhabited_never, r @ _ => bug!("unexpected ty_kind in uninhabited type handling: {:?}", r), }; - FfiResult::new_with_reason(ty, desc, help) + FfiResult::new_with_reason(ty, desc, None) } } @@ -769,7 +745,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // 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, + // let's consider transparent structs to be maybe 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 @@ -780,9 +756,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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) @@ -874,19 +848,14 @@ 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) { - 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)"), + 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 @@ -980,7 +949,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { fn visit_enum( &self, state: CTypesVisitorState, - outer_ty: Option>, ty: Ty<'tcx>, def: AdtDef<'tcx>, args: GenericArgsRef<'tcx>, @@ -990,7 +958,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { if def.variants().is_empty() { // Empty enums are implicitely handled as the never type: - return self.visit_uninhabited(state, outer_ty, ty); + return self.visit_uninhabited(state, ty); } // Check for a repr() attribute to specify the size of the // discriminant. @@ -1047,9 +1015,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { 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?) @@ -1107,7 +1073,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } self.visit_struct_or_union(state, ty, def, args) } - AdtKind::Enum => self.visit_enum(state, outer_ty, ty, def, args), + AdtKind::Enum => self.visit_enum(state, ty, def, args), } } @@ -1242,7 +1208,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::Foreign(..) => FfiSafe, - ty::Never => self.visit_uninhabited(state, outer_ty, ty), + ty::Never => self.visit_uninhabited(state, 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 diff --git a/tests/ui/lint/improper_ctypes/lint_uninhabited.rs b/tests/ui/lint/improper_ctypes/lint_uninhabited.rs index 9051c1760fb32..28dfcb3788acf 100644 --- a/tests/ui/lint/improper_ctypes/lint_uninhabited.rs +++ b/tests/ui/lint/improper_ctypes/lint_uninhabited.rs @@ -32,7 +32,7 @@ struct HalfHiddenUninhabited { extern "C" { fn bad_entry(e: AlsoUninhabited); //~ ERROR: uses type `AlsoUninhabited` -fn bad_exit()->AlsoUninhabited; //~ ERROR: uses type `AlsoUninhabited` +fn bad_exit()->AlsoUninhabited; fn bad0_entry(e: Uninhabited); //~ ERROR: uses type `Uninhabited` fn bad0_exit()->Uninhabited; @@ -46,7 +46,7 @@ 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` +extern "C" fn impl_bad_exit()->AlsoUninhabited { AlsoUninhabited{ a: impl_bad0_exit(), b: 0, diff --git a/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr b/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr index f678b0d725aec..49ec8bf16dbe1 100644 --- a/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr +++ b/tests/ui/lint/improper_ctypes/lint_uninhabited.stderr @@ -4,6 +4,7 @@ error: `extern` block uses type `AlsoUninhabited`, which is not FFI-safe LL | fn bad_entry(e: AlsoUninhabited); | ^^^^^^^^^^^^^^^ not FFI-safe | + = help: `AlsoUninhabited` has exactly one non-zero-sized field, consider making it `#[repr(transparent)]` instead = 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 @@ -22,26 +23,6 @@ note: the lint level is defined here LL | #![deny(improper_ctypes)] | ^^^^^^^^^^^^^^^ -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 | @@ -69,6 +50,7 @@ error: `extern` fn uses type `AlsoUninhabited`, which is not FFI-safe LL | extern "C" fn impl_bad_entry(e: AlsoUninhabited) {} | ^^^^^^^^^^^^^^^ not FFI-safe | + = help: `AlsoUninhabited` has exactly one non-zero-sized field, consider making it `#[repr(transparent)]` instead = 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 @@ -87,26 +69,6 @@ note: the lint level is defined here 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 | @@ -155,5 +117,5 @@ 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 +error: aborting due to 7 previous errors; 1 warning emitted