Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
071fc71
ImproperCTypes: split type visiting into subfunctions
niacdoial Aug 24, 2025
359cb79
ImproperCTypes: merge outer_ty information into VisitorState
niacdoial Sep 10, 2025
c74c323
ImproperCTypes: add recursion limit
niacdoial Sep 10, 2025
764d8fb
ImproperCTypes: add architecture for layered reasoning in lints
niacdoial Aug 24, 2025
a2dc1fb
ImproperCTypes: splitting definitions lint into two
niacdoial Aug 25, 2025
84d388e
ImproperCTypes: change cstr linting
niacdoial Aug 26, 2025
218c43c
ImproperCTypes: change handling of indirections
niacdoial Aug 26, 2025
c6eb386
ImproperCTypes: change handling of FnPtrs
niacdoial Aug 27, 2025
d4dc99b
ImproperCTypes: change what type is blamed, use nested help messages
niacdoial Aug 27, 2025
5b9fb7a
ImproperCTypes: change handling of ADTs
niacdoial Aug 28, 2025
d409c7e
ImproperCTypes: change handling of slices
niacdoial Aug 28, 2025
b74bc7b
ImproperCTypes: handle uninhabited types
niacdoial Aug 28, 2025
67ff00c
ImproperCTypes: handle the Option<pattern> case
niacdoial Aug 28, 2025
e7db8b8
ImproperCTypes: refactor handling opaque aliases
niacdoial Aug 28, 2025
e59850a
ImproperCTypes: also check in traits
niacdoial Aug 28, 2025
ea86322
ImproperCTypes: also check 'exported' static variables
niacdoial Aug 28, 2025
26a7e89
ImproperCTypes: don't consider packed reprs
niacdoial Aug 28, 2025
721f23b
ImproperCTypes: add tests
niacdoial Aug 28, 2025
1ab9411
ImproperCTypes: misc. adaptations
niacdoial Aug 28, 2025
0df23fa
ImproperCTypes: rename associated tests
niacdoial Aug 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/example/std_example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
44 changes: 28 additions & 16 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -350,21 +350,22 @@ 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

lint_improper_ctypes_array_help = consider passing a pointer to the array

lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe
lint_improper_ctypes_box = box cannot be represented as a single pointer

lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead

lint_improper_ctypes_char_reason = the `char` type has no C equivalent

lint_improper_ctypes_cstr_help =
consider passing a `*const std::ffi::c_char` instead, and use `CStr::as_ptr()`
lint_improper_ctypes_cstr_help_const =
consider passing a `*const std::ffi::c_char` instead, converting to/from `{$ty}` as needed
lint_improper_ctypes_cstr_help_mut =
consider passing a `*mut std::ffi::c_char` instead, converting to/from `{$ty}` as needed
lint_improper_ctypes_cstr_reason = `CStr`/`CString` do not have a guaranteed layout

lint_improper_ctypes_dyn = trait objects have no C equivalent
Expand All @@ -375,40 +376,51 @@ 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

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_slice_help = consider using a raw pointer to the slice's first element (and a length) instead
lint_improper_ctypes_slice_reason = slices have no C equivalent
lint_improper_ctypes_str_help = consider using `*const u8` and a length instead

lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
lint_improper_ctypes_str_reason = string slices have no C equivalent
lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct

lint_improper_ctypes_struct_fieldless_reason = this struct has no fields
lint_improper_ctypes_struct_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
lint_improper_ctypes_struct_consider_transparent = `{$ty}` has exactly one non-zero-sized field, consider making it `#[repr(transparent)]` instead
lint_improper_ctypes_struct_dueto = this struct/enum/union (`{$ty}`) is FFI-unsafe due to a `{$inner_ty}` field

lint_improper_ctypes_struct_fieldless_help = consider adding a member to this struct
lint_improper_ctypes_struct_fieldless_reason = `{$ty}` has no fields

lint_improper_ctypes_struct_layout_reason = this struct has unspecified layout
lint_improper_ctypes_struct_non_exhaustive = this struct is non-exhaustive
lint_improper_ctypes_struct_zst = this struct contains only zero-sized fields
lint_improper_ctypes_struct_layout_help = consider adding a `#[repr(C)]` (not `#[repr(C,packed)]`) or `#[repr(transparent)]` attribute to `{$ty}`
lint_improper_ctypes_struct_layout_reason = `{$ty}` has unspecified layout
lint_improper_ctypes_struct_non_exhaustive = `{$ty}` is non-exhaustive
lint_improper_ctypes_struct_zst = `{$ty}` contains only zero-sized fields

lint_improper_ctypes_tuple_help = consider using a struct instead

lint_improper_ctypes_tuple_reason = tuples have unspecified layout
lint_improper_ctypes_union_fieldless_help = consider adding a member to this union

lint_improper_ctypes_uninhabited_enum = zero-variant enums and other uninhabited types are not allowed in function arguments and static variables
lint_improper_ctypes_uninhabited_never = the never type (`!`) and other uninhabited types are not allowed in function arguments and static variables

lint_improper_ctypes_union_consider_transparent = `{$ty}` has exactly one non-zero-sized field, consider making it `#[repr(transparent)]` instead
lint_improper_ctypes_union_fieldless_help = consider adding a member to this union
lint_improper_ctypes_union_fieldless_reason = this union has no fields
lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union
lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` attribute to this union

lint_improper_ctypes_union_layout_reason = this union has unspecified layout
lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive

lint_improper_ctypes_unsized_box = this box for an unsized type contains metadata, which makes it incompatible with a C pointer
lint_improper_ctypes_unsized_ptr = this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer
lint_improper_ctypes_unsized_ref = this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
Comment on lines +420 to +422
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could probably be combined to a single message like: "pointer types are only FFI-compatible if their pointee is Sized".

Also since these are for a note field, include _note in the fluent name


lint_incomplete_include =
include macro expected single expression in source

Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,18 @@ fn register_builtins(store: &mut LintStore) {
REFINING_IMPL_TRAIT_INTERNAL
);

add_lint_group!(
"improper_c_boundaries",
IMPROPER_C_CALLBACKS,
IMPROPER_C_FN_DEFINITIONS,
IMPROPER_C_VAR_DEFINITIONS,
IMPROPER_CTYPES
);

// FIXME(ctypes, migration): when should this retrocompatibility-borne
// lint group disappear, if at all? Should it turn into a register_renamed?
add_lint_group!("improper_ctypes_definitions", IMPROPER_C_CALLBACKS, IMPROPER_C_FN_DEFINITIONS);

add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024);

add_lint_group!(
Expand Down
48 changes: 37 additions & 11 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1943,29 +1943,55 @@ pub(crate) enum UnpredictableFunctionPointerComparisonsSuggestion<'a, 'tcx> {
},
}

pub(crate) struct ImproperCTypesLayer<'a> {
pub ty: Ty<'a>,
pub inner_ty: Option<Ty<'a>>,
pub note: DiagMessage,
pub span_note: Option<Span>,
pub help: Option<DiagMessage>,
}

impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> {
fn add_to_diag<G: EmissionGuarantee>(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);
};

diag.remove_arg("ty");
if self.inner_ty.is_some() {
diag.remove_arg("inner_ty");
}
}
}

pub(crate) struct ImproperCTypes<'a> {
pub ty: Ty<'a>,
pub desc: &'a str,
pub label: Span,
pub help: Option<DiagMessage>,
pub note: DiagMessage,
pub span_note: Option<Span>,
pub reasons: Vec<ImproperCTypesLayer<'a>>,
}

// Used because of the complexity of Option<DiagMessage>, DiagMessage, and Option<Span>
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);
}
}

Expand Down
Loading