Skip to content

Conversation

@RivenSkaye
Copy link
Contributor

@RivenSkaye RivenSkaye commented Nov 4, 2025

Rust allows specifying different ABI calling conventions on extern functions. The default is "C" which translates to the compiler's default (almost always cdecl), but valid values like thiscall and fastcall exist, as well as some platform-specific options and the special "system" option. It's special in that its allowed everywhere, and almost always translates to "C" with the exception of 32-bit Windows binaries, where it defaults to stdcall.

Considering the still rather large portion of Windows code and programs stuck to using 32-bit binaries there's sometimes reasons to prefer the system's default over the compiler's. For example when calling into system libraries or using ancient 32-bit DLLs.
MS Learn on native interop and calling conventions explicitly outlines the differences on Win32

Stdcall ("standard call") is the default calling convention on Windows x86 and it is used by most Win32 APIs. Cdecl is the default calling convention on Linux x86. Windows ports of open-source libraries that originated on Unix often use the Cdecl calling convention even on Windows x86. It's necessary to explicitly specify the Cdecl calling convention in P/Invoke declarations for interop with these libraries.

For non-x86 architectures, both Stdcall and Cdecl calling conventions are treated as the canonical platform default calling convention.

Defaulting to Stdcall would not be an option however, as this would break any Rust code not using extern "system" for the ABI specification (e.g. code using C explicitly on 32-bit). Using the special-cased WinApi also wouldn't work for the same reason.

This PR also adds support for explicit use of stdcall, thiscall¹, win64², cdecl, sysv64³, and aapcs⁴ extern ABIs. It also causes build failures on Rust, fastcall, and efiapi.

It does not add any tests, as it'd require adding workflow runs for every allowed combination of targets and ABIs, as well as some tests to verify (implicit) disallows abort correctly.
If tests are to be added, some of them will need to be limited to specific targets to prevent compiler errors (e.g. using thiscall on AMD64 is disallowed by rustc).

It also does not consider vectorcall. The previously linked page (and further links from there) do not mention vectorcall, and they do mention fastcall (which vectorcall is based on) is not supported in .NET.

¹: thiscall is only allowed on x86 by the compiler, as it's the 32-bit MSVC member function convention.
²: only allowed on x86_64 Windows
³: not allowed on windows
⁴: A warning is emitted and the special value WinApi is used to go with the system default as ARM is a rather special beast to deal with

@neuecc
Copy link
Member

neuecc commented Nov 10, 2025

Thank you for the thorough review and justification!
It looks like it won't affect existing behavior, and it's well-considered, so I think there are no issues.
I'll merge and release it.

@neuecc neuecc merged commit e8e1d85 into Cysharp:main Nov 10, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants