Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions compiler/rustc_abi/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
size,
max_repr_align: None,
unadjusted_abi_align: element.align.abi,
repr_c: false,
Copy link
Copy Markdown
Contributor Author

@Jules-Bertholet Jules-Bertholet May 3, 2026

Choose a reason for hiding this comment

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

This could be:

Suggested change
repr_c: false,
repr_c: element.repr_c,

Or even:

Suggested change
repr_c: false,
repr_c: true,

But I don't want this PR to get bogged down in controversy, so let's leave it alone for now (unless there is lang team consensus).

If we do change this, the documentation in library/core/src/primitive_docs.rs will need to change as well.

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's unclear to me what the consequence of either of the tree possible options here is.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  • false: all ZST arrays have trivial ABI for all element types
  • element.repr_c: ZST arrays are passed like a repr(C) ZST if the element type is repr(C)
  • true: ZST arrays are passed like a repr(C) ZST for all element types

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This codepath affects all arrays, not just ZST, right? I guess your point is that for non-ZST repr_c (currently) doesn't make a difference?

ZST arrays are passed like a repr(C) ZST if the element type is repr(C)

That's what I expected based on the description.

... now I can't find it any more, did you change the doc comment for this field? Propagating this through arrays made sense to me at first sight but I did not think deeply about it.

Also, is it correct to say "passed like a repr(C) ZST"? For all I know, C might have different zero-sized types that are passed differently.

Copy link
Copy Markdown
Contributor Author

@Jules-Bertholet Jules-Bertholet May 3, 2026

Choose a reason for hiding this comment

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

did you change the doc comment for this field?

Yes.

Also, is it correct to say "passed like a repr(C) ZST"

"Passed like a repr(C) ZST with the same alignment as the element type".

For all I know, C might have different zero-sized types that are passed differently.

Standard C doesn't have zero-sized types at all. But yes, widespread compiler extensions do make this happen: https://godbolt.org/z/1xWqqj3qv

Currently, Rust can't match the flexible-array-member ABI. This PR leaves that unchanged.

Copy link
Copy Markdown
Contributor Author

@Jules-Bertholet Jules-Bertholet May 4, 2026

Choose a reason for hiding this comment

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

Ah, it looks like flexible array members having a different ABI is just a quirk of Clang, not replicated by GCC: https://godbolt.org/z/eWKazGxxW

Filed an issue with LLVM: llvm/llvm-project#195572

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This makes me question again whether we should support any kind of ZST in our C interop, given that those types do not exist in standard C, and C compilers cannot even agree among themselves...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These types are used in the Linux kernel, including in the UAPI, so we don't have much of a choice. That being said, all practical usage is behind a pointer.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That being said, all practical usage is behind a pointer.

Then we could maybe get away with only giving them a defined layout, not a defined ABI.

(That does not avoid the win64 problem though.)

randomization_seed: element.randomization_seed.wrapping_add(Hash64::new(count)),
})
}
Expand Down Expand Up @@ -499,6 +500,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
return Err(LayoutCalculatorError::EmptyUnion);
};

let repr_c =
repr.c() || (repr.transparent() && only_variant.iter().any(|field| field.repr_c));

let combined_seed = only_variant
.iter()
.map(|v| v.randomization_seed)
Expand All @@ -514,6 +518,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
size: size.align_to(align),
max_repr_align,
unadjusted_abi_align,
repr_c,
randomization_seed: combined_seed,
})
}
Expand Down Expand Up @@ -786,6 +791,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
align: AbiAlign::new(align),
max_repr_align,
unadjusted_abi_align,
repr_c: repr.c(),
randomization_seed: combined_seed,
};

Expand Down Expand Up @@ -1133,6 +1139,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
size,
max_repr_align,
unadjusted_abi_align,
repr_c: repr.c(),
randomization_seed: combined_seed,
};

Expand Down Expand Up @@ -1482,6 +1489,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
unadjusted_abi_align
};

let repr_c = repr.c() || (repr.transparent() && fields.iter().any(|field| field.repr_c));

let seed = field_seed.wrapping_add(repr.field_shuffle_seed);

Ok(LayoutData {
Expand All @@ -1494,6 +1503,7 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
size,
max_repr_align,
unadjusted_abi_align,
repr_c,
randomization_seed: seed,
})
}
Expand Down Expand Up @@ -1594,6 +1604,7 @@ where
align: AbiAlign::new(align),
max_repr_align: None,
unadjusted_abi_align: elt.align.abi,
repr_c: false,
randomization_seed: elt.randomization_seed.wrapping_add(Hash64::new(count)),
})
}
1 change: 1 addition & 0 deletions compiler/rustc_abi/src/layout/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ pub(super) fn layout<
align,
max_repr_align: None,
unadjusted_abi_align: align.abi,
repr_c: false,
randomization_seed: Default::default(),
})
}
5 changes: 5 additions & 0 deletions compiler/rustc_abi/src/layout/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align,
repr_c: false,
randomization_seed: Hash64::new(0),
}
}
Expand All @@ -42,6 +43,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align,
repr_c: false,
randomization_seed: Hash64::ZERO,
}
}
Expand Down Expand Up @@ -84,6 +86,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
align,
max_repr_align: None,
unadjusted_abi_align: align.abi,
repr_c: false,
randomization_seed: Hash64::new(randomization_seed),
}
}
Expand Down Expand Up @@ -117,6 +120,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
size,
max_repr_align: None,
unadjusted_abi_align: align,
repr_c: false,
randomization_seed: Hash64::new(combined_seed),
}
}
Expand All @@ -143,6 +147,7 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
size: Size::ZERO,
max_repr_align: None,
unadjusted_abi_align: dl.i8_align,
repr_c: false,
randomization_seed: Hash64::ZERO,
}
}
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2142,6 +2142,11 @@ pub struct LayoutData<FieldIdx: Idx, VariantIdx: Idx> {
/// in some cases.
pub unadjusted_abi_align: Align,

/// Whether this type is `repr(C)`, or a`repr(transparent)` wrapper around such.
/// Some C ABIs pass `repr(C)` ZSTs by pointer, but `repr(Rust)` ZSTs should always
/// be ignored.
pub repr_c: bool,

/// The randomization seed based on this type's own repr and its fields.
///
/// Since randomization is toggled on a per-crate basis even crates that do not have randomization
Expand Down Expand Up @@ -2170,6 +2175,12 @@ impl<FieldIdx: Idx, VariantIdx: Idx> LayoutData<FieldIdx, VariantIdx> {
pub fn is_uninhabited(&self) -> bool {
self.uninhabited
}

/// Returns `true` if this is a `repr(C)` type,
/// or an array of such, or a `repr(transparent)` wrapper around such
pub fn is_repr_c(&self) -> bool {
self.repr_c
}
}

impl<FieldIdx: Idx, VariantIdx: Idx> fmt::Debug for LayoutData<FieldIdx, VariantIdx>
Expand All @@ -2191,6 +2202,7 @@ where
variants,
max_repr_align,
unadjusted_abi_align,
repr_c,
randomization_seed,
} = self;
f.debug_struct("Layout")
Expand All @@ -2203,6 +2215,7 @@ where
.field("variants", variants)
.field("max_repr_align", max_repr_align)
.field("unadjusted_abi_align", unadjusted_abi_align)
.field("repr_c", repr_c)
.field("randomization_seed", randomization_seed)
.finish()
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_target/src/callconv/powerpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ where
// powerpc-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
if cx.target_spec().os == Os::Linux
&& matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc)
&& arg.layout.is_zst()
&& arg.layout.is_repr_c()
{
arg.make_indirect_from_ignore();
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_target/src/callconv/s390x.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ where
// s390x-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
if cx.target_spec().os == Os::Linux
&& matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc)
&& arg.layout.is_zst()
&& arg.layout.is_repr_c()
{
arg.make_indirect_from_ignore();
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_target/src/callconv/sparc64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ where
continue;
}
if arg.is_ignore() {
if passes_zsts && arg.layout.is_zst() {
if passes_zsts && arg.layout.is_repr_c() {
arg.make_indirect_from_ignore();
double_word_count += 1;
}
Expand Down
26 changes: 15 additions & 11 deletions compiler/rustc_target/src/callconv/x86_win64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,25 @@ where
}
};

if !fn_abi.ret.is_ignore() {
// Windows ABIs do not talk about ZST since such types do not exist in MSVC.
// However, clang and gcc allow ZST is their windows-gnu targets, and pass them by pointer indierction.
// We follow that for `repr(C)` ZSTs (and `repr(transparent)` wrappers around them),
// but `repr(Rust)` ones are always ignored (ensuring that `()` matches C `void`).

if fn_abi.ret.is_ignore() {
if fn_abi.ret.layout.is_repr_c() {
fn_abi.ret.make_indirect_from_ignore();
}
} else {
fixup(&mut fn_abi.ret, true);
}

for arg in fn_abi.args.iter_mut() {
if arg.is_ignore() && arg.layout.is_zst() {
// Windows ABIs do not talk about ZST since such types do not exist in MSVC.
// In that sense we can do whatever we want here, and maybe we should throw an error
// (but of course that would be a massive breaking change now).
// We try to match clang and gcc (which allow ZST is their windows-gnu targets), so we
// pass ZST via pointer indirection.
arg.make_indirect_from_ignore();
if arg.is_ignore() {
if arg.layout.is_repr_c() {
arg.make_indirect_from_ignore();
}

continue;
}
if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
Expand All @@ -68,7 +75,4 @@ where
}
fixup(arg, false);
}
// FIXME: We should likely also do something about ZST return types, similar to above.
// However, that's non-trivial due to `()`.
// See <https://github.com/rust-lang/unsafe-code-guidelines/issues/552>.
}
11 changes: 8 additions & 3 deletions library/core/src/primitive_docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1842,9 +1842,14 @@ mod prim_ref {}
/// call will be valid ABI-wise. The callee receives the result of transmuting the function pointer
/// from `fn()` to `fn(i32)`; that transmutation is itself a well-defined operation, it's just
/// almost certainly UB to later call that function pointer.)
/// - Any two types with size 0 and alignment 1 are ABI-compatible.
/// - A `repr(transparent)` type `T` is ABI-compatible with its unique non-trivial field, i.e., the
/// unique field that doesn't have size 0 and alignment 1 (if there is such a field).
/// - Any two types fulfilling all the following conditions are ABI-compatible;
/// such types are said to have "trivial ABI":
/// - Size 0
/// - Alignment 1
/// - Not `repr(C)`
/// - Not a `repr(transparent)` wrapper around a type that fails to satisfy these conditions
/// - A `repr(transparent)` type is ABI-compatible with its unique field that does not have trivial ABI
/// (as defined above). If there is no such field, the type has trivial ABI.
/// - `i32` is ABI-compatible with `NonZero<i32>`, and similar for all other integer types.
/// - If `T` is guaranteed to be subject to the [null pointer
/// optimization](option/index.html#representation), and `E` is an enum satisfying the following
Expand Down
124 changes: 110 additions & 14 deletions tests/codegen-llvm/abi-win64-zst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,129 @@
extern crate minicore;
use minicore::*;

// Make sure the argument is always passed when explicitly requesting a Windows ABI.
#[repr(C)]
struct CMaybeZst;

#[repr(transparent)]
struct CMaybeZst2((), CMaybeZst, ());

// Make sure the argument is always passed when explicitly requesting a Windows ABI,
// and it is `repr(C)` - but not if it is `repr(Rust)`.
// Our goal here is to match clang: <https://clang.godbolt.org/z/Wr4jMWq3P>.

// CHECK: define win64cc void @pass_zst_win64(ptr {{[^,]*}})
// CHECK: define win64cc void @pass_rust_zst_win64()
#[no_mangle]
extern "win64" fn pass_rust_zst_win64(_: ()) {}
Copy link
Copy Markdown
Member

@RalfJung RalfJung May 3, 2026

Choose a reason for hiding this comment

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

This will be interesting... I am not sure how to make sense of this ABI on non-MSVC targets. Given that the type layout itself is "wrong", how could one possibly actually call such a function...?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You could call such a function from C with a signature of void pass_rust_zst_win64(). However, the Rust signature trips the improper_ctypes lint, so that's not something we are committing to.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Currently, you could not. We don't allow any arguments to be omitted at the moment.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also note that I am talking about the "win64" ABI in general, not just this particular function -- I should have been more clear about that.

A "win64" function call on a windows-gnu or Linux target that involves a zero-field struct (or struct whose only field is a 0-element array) might just produce nonsense, right? We'd need repr(win64) as well.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reading through prior discussion in rust-lang/unsafe-code-guidelines#552, I still agree with what I wrote there:

I think my stance here is what the win64 ABI simply does not support 1-ZST structs. We shouldn't take what gcc/clang do for that ABI as gospel since they do not "own" the ABI, MSVC/Microsoft does.

We should emit FFI warnings (and, eventually, errors) for code that uses an ABI outside of what the vendor has (explicitly or implicitly) defined. We should not take what GCC/clang do as gospel for Windows targets. For this concrete question that means we should disallow passing ZST.

And it seems like windows-gnu targets use the same ABI so this applies to them as well.

Is that too radical or is it something we could get away with?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

But GCC and clang don't...

Copy link
Copy Markdown
Contributor Author

@Jules-Bertholet Jules-Bertholet May 5, 2026

Choose a reason for hiding this comment

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

…with consistent layout between them. So it's not a problem. As long as two different compilers don't give a different ABI to identical type declarations, we are good

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

On a windows target, MSVC might start giving a different answer eventually. Seems bad for us to draw ourselves into that corner.

Copy link
Copy Markdown
Contributor Author

@Jules-Bertholet Jules-Bertholet May 5, 2026

Choose a reason for hiding this comment

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

That's possible, true. But given the dubious practical utility of such code, I don't think it's likely. And it's really GCC and Clang that drew themselves into that corner; repr(C) is meant to match the target's C compiler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it's really GCC and Clang that drew themselves into that corner

It would be us needlessly following them into that corner.


// CHECK: define win64cc void @pass_c_maybezst_win64(ptr {{[^,]*}})
#[no_mangle]
extern "win64" fn pass_c_maybezst_win64(_: CMaybeZst) {}

// CHECK: define win64cc void @pass_c_maybezst_2_win64(ptr {{[^,]*}})
#[no_mangle]
extern "win64" fn pass_c_maybezst_2_win64(_: CMaybeZst2) {}

// CHECK: define x86_vectorcallcc void @pass_rust_zst_vectorcall()
#[no_mangle]
extern "vectorcall" fn pass_rust_zst_vectorcall(_: ()) {}

// CHECK: define x86_vectorcallcc void @pass_c_maybezst_vectorcall(ptr {{[^,]*}})
#[no_mangle]
extern "win64" fn pass_zst_win64(_: ()) {}
extern "vectorcall" fn pass_c_maybezst_vectorcall(_: CMaybeZst) {}

// CHECK: define x86_vectorcallcc void @pass_zst_vectorcall(ptr {{[^,]*}})
// CHECK: define x86_vectorcallcc void @pass_c_maybezst_2_vectorcall(ptr {{[^,]*}})
#[no_mangle]
extern "vectorcall" fn pass_zst_vectorcall(_: ()) {}
extern "vectorcall" fn pass_c_maybezst_2_vectorcall(_: CMaybeZst2) {}

// windows-gnu: define void @pass_zst_fastcall(ptr {{[^,]*}})
// windows-msvc: define void @pass_zst_fastcall(ptr {{[^,]*}})
// windows-gnu: define void @pass_rust_zst_fastcall()
// windows-msvc: define void @pass_rust_zst_fastcall()
#[no_mangle]
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
extern "fastcall" fn pass_zst_fastcall(_: ()) {}
extern "fastcall" fn pass_rust_zst_fastcall(_: ()) {}

// windows-gnu: define void @pass_c_maybezst_fastcall(ptr {{[^,]*}})
// windows-msvc: define void @pass_c_maybezst_fastcall(ptr {{[^,]*}})
#[no_mangle]
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
extern "fastcall" fn pass_c_maybezst_fastcall(_: CMaybeZst) {}

// windows-gnu: define void @pass_c_maybezst_2_fastcall(ptr {{[^,]*}})
// windows-msvc: define void @pass_c_maybezst_2_fastcall(ptr {{[^,]*}})
#[no_mangle]
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
extern "fastcall" fn pass_c_maybezst_2_fastcall(_: CMaybeZst2) {}

// The sysv64 ABI ignores ZST.

// CHECK: define x86_64_sysvcc void @pass_zst_sysv64()
// CHECK: define x86_64_sysvcc void @pass_rust_zst_sysv64()
#[no_mangle]
extern "sysv64" fn pass_rust_zst_sysv64(_: ()) {}

// CHECK: define x86_64_sysvcc void @pass_c_maybezst_sysv64()
#[no_mangle]
extern "sysv64" fn pass_c_maybezst_sysv64(_: CMaybeZst) {}

// CHECK: define x86_64_sysvcc void @pass_c_maybezst_2_sysv64()
#[no_mangle]
extern "sysv64" fn pass_zst_sysv64(_: ()) {}
extern "sysv64" fn pass_c_maybezst_2_sysv64(_: CMaybeZst2) {}

// For `extern "C"` functions, ZST are ignored on Linux put passed on Windows.

// linux: define void @pass_zst_c()
// windows-msvc: define void @pass_zst_c(ptr {{[^,]*}})
// windows-gnu: define void @pass_zst_c(ptr {{[^,]*}})
// linux: define void @pass_rust_zst_c()
// windows-msvc: define void @pass_rust_zst_c()
// windows-gnu: define void @pass_rust_zst_c()
#[no_mangle]
extern "C" fn pass_rust_zst_c(_: ()) {}

// linux: define void @pass_c_maybezst_c()
// windows-msvc: define void @pass_c_maybezst_c(ptr {{[^,]*}})
// windows-gnu: define void @pass_c_maybezst_c(ptr {{[^,]*}})
#[no_mangle]
extern "C" fn pass_c_maybezst_c(_: CMaybeZst) {}

// linux: define void @pass_c_maybezst_2_c()
// windows-msvc: define void @pass_c_maybezst_2_c(ptr {{[^,]*}})
// windows-gnu: define void @pass_c_maybezst_2_c(ptr {{[^,]*}})
#[no_mangle]
extern "C" fn pass_c_maybezst_2_c(_: CMaybeZst2) {}

// Now check `repr(C)` return types.
// Again, we seek to match clang: <https://clang.godbolt.org/z/hKv74ThnE>

// CHECK: define win64cc void @ret_c_maybezst_win64(ptr {{[^,]*}})
#[no_mangle]
extern "win64" fn ret_c_maybezst_win64() -> CMaybeZst {
CMaybeZst
}

// CHECK: define x86_vectorcallcc void @ret_c_maybezst_vectorcall(ptr {{[^,]*}})
#[no_mangle]
extern "vectorcall" fn ret_c_maybezst_vectorcall() -> CMaybeZst {
CMaybeZst
}

// windows-gnu: define void @ret_c_maybezst_fastcall(ptr {{[^,]*}})
// windows-msvc: define void @ret_c_maybezst_fastcall(ptr {{[^,]*}})
#[no_mangle]
#[cfg(windows)] // "fastcall" is not valid on 64bit Linux
extern "fastcall" fn ret_c_maybezst_fastcall() -> CMaybeZst {
CMaybeZst
}

// The sysv64 ABI ignores ZST.

// CHECK: define x86_64_sysvcc void @ret_c_maybezst_sysv64()
#[no_mangle]
extern "sysv64" fn ret_c_maybezst_sysv64() -> CMaybeZst {
CMaybeZst
}

// For `extern "C"` functions, ZST are ignored on Linux put reted on Windows.

// linux: define void @ret_c_maybezst_c()
// windows-msvc: define void @ret_c_maybezst_c(ptr {{[^,]*}})
// windows-gnu: define void @ret_c_maybezst_c(ptr {{[^,]*}})
#[no_mangle]
extern "C" fn pass_zst_c(_: ()) {}
extern "C" fn ret_c_maybezst_c() -> CMaybeZst {
CMaybeZst
}
Loading
Loading