@@ -77,6 +77,50 @@ fn parse_method(item: FnItem, options: &BindgenOptions) -> Option<ExternMethod>
7777
7878 let method_name = sig. ident . to_string ( ) ;
7979
80+ let is_x86_windows = std:: env:: var ( "CARGO_CFG_TARGET_ARCH" ) . is_ok_and ( |v| v == "x86" )
81+ && std:: env:: var ( "CARGO_CFG_TARGET_OS" ) . is_ok_and ( |v| v == "windows" ) ;
82+ let call_conv = if let Some ( abi) = sig. abi . map ( |abi| abi. name ) . flatten ( ) {
83+ let abi_str = & abi. value ( ) ;
84+ if abi_str. contains ( "system" ) {
85+ // For i686-pc-windows-* (32-bit binaries) the default calling convention is stdcall, unlike everywhere else.
86+ // See https://doc.rust-lang.org/reference/items/external-blocks.html#abi for a list of possible ABIs and what they translate to.
87+ if is_x86_windows { "StdCall" }
88+ else { "Cdecl" }
89+ }
90+ else if abi_str. contains ( "stdcall" ) { "StdCall" }
91+ else if abi_str. contains ( "thiscall" ) {
92+ if !is_x86_windows {
93+ eprintln ! ( "ThisCall is only allowed on 32-bit MSVC as it's the 32-bit member function calling convention. Consider using \" system\" instead." ) ;
94+ panic ! ( "Cannot emit `thiscall` code in Rust for a non-x86 target." ) ;
95+ }
96+ else { "ThisCall" }
97+ }
98+ else if abi_str. contains ( "win64" ) && ( std:: env:: var ( "CARGO_CFG_TARGET_OS" ) . is_ok_and ( |v| v != "windows" ) || std:: env:: var ( "CARGO_CFG_TARGET_ARCH" ) . is_ok_and ( |v| v != "x86_64" ) ) {
99+ eprintln ! ( "win64 is an AMD64-only calling convention. Consider using \" system\" instead." ) ;
100+ panic ! ( "Cannot emit `win64` code in Rust for a non-AMD64-windows target." ) ;
101+ }
102+ else if abi_str. contains ( "sysv64" ) && ( std:: env:: var ( "CARGO_CFG_TARGET_OS" ) . is_ok_and ( |v| v == "windows" ) || std:: env:: var ( "CARGO_CFG_TARGET_ARCH" ) . is_ok_and ( |v| v != "x86_64" ) ) {
103+ eprintln ! ( "sysv64 is the calling convention for AMD64 non-windows, consider using \" system\" instead." ) ;
104+ panic ! ( "Cannot emit `sysv64` on non-AMD64 and/or windows target." ) ;
105+ }
106+ else if abi_str. contains ( "aapcs" ) {
107+ eprintln ! ( "ARM is an extremely complex landscape and not all possible combinations can be checked. Emitting at the user's risk." ) ;
108+ "WinApi"
109+ }
110+ else if abi_str. contains ( "C" ) || abi_str. contains ( "cdecl" ) || abi_str. contains ( "win64" ) || abi_str. contains ( "sysv64" ) || abi_str. contains ( "aapcs" ) { "Cdecl" }
111+ else {
112+ // This is only ever hit for the `Rust`, `fastcall`, and `efiapi` calling conventions, none of which are supported by C# (as of .NET 9)
113+ // https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callconvfastcall?view=net-9.0
114+ // https://learn.microsoft.com/en-us/dotnet/standard/native-interop/calling-conventions#platform-default-calling-convention
115+ // https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.callingconvention?view=net-9.0
116+ eprintln ! ( "C# support for calling conventions is limited. Please stick to any of the supported options for interop:" ) ;
117+ eprintln ! ( "`cdecl` for `Cdecl`, `stdcall` for `StdCall`, `thiscall` for `ThisCall`, `C` for the compiler's default (most likely `Cdecl`), `system` for automatic selection, or your platform-specific target." ) ;
118+ panic ! ( "Unsupported calling convention requested! .NET interop does not support {abi_str}, please consider using \" system\" for the extern ABI." ) ;
119+ }
120+ } else {
121+ "Cdecl"
122+ } . to_string ( ) ;
123+
80124 let mut parameters: Vec < Parameter > = Vec :: new ( ) ;
81125 let mut return_type: Option < RustType > = None ;
82126
@@ -143,6 +187,7 @@ fn parse_method(item: FnItem, options: &BindgenOptions) -> Option<ExternMethod>
143187 parameters,
144188 return_type,
145189 doc_comment : gather_docs ( & attrs) ,
190+ call_conv,
146191 } ) ;
147192 }
148193
0 commit comments