From 9b95ce6a76d32a1c152bd3719d6cf46dd872b630 Mon Sep 17 00:00:00 2001
From: Liran Ringel <liranringel@gmail.com>
Date: Mon, 9 Oct 2017 02:38:29 +0200
Subject: [PATCH] Add support for the thiscall ABI

---
 src/codegen/mod.rs                            | 22 +++++++--
 src/features.rs                               |  3 ++
 src/ir/function.rs                            |  4 ++
 .../expectations/tests/win32-thiscall_1_0.rs  | 29 +++++++++++
 .../tests/win32-thiscall_nightly.rs           | 48 +++++++++++++++++++
 tests/headers/win32-thiscall_1_0.hpp          |  7 +++
 tests/headers/win32-thiscall_nightly.hpp      |  7 +++
 7 files changed, 117 insertions(+), 3 deletions(-)
 create mode 100644 tests/expectations/tests/win32-thiscall_1_0.rs
 create mode 100644 tests/expectations/tests/win32-thiscall_nightly.rs
 create mode 100644 tests/headers/win32-thiscall_1_0.hpp
 create mode 100644 tests/headers/win32-thiscall_nightly.hpp

diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index ea9fec4158..ca982b6bf6 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -1945,6 +1945,10 @@ impl MethodCodegen for Method {
             _ => panic!("How in the world?"),
         };
 
+        if let (Abi::ThisCall, false) = (signature.abi(), ctx.options().rust_features().thiscall_abi()) {
+            return;
+        }
+
         // Do not generate variadic methods, since rust does not allow
         // implementing them, and we don't do a good job at it anyway.
         if signature.is_variadic() {
@@ -3058,9 +3062,17 @@ impl TryToRustTy for FunctionSig {
         let arguments = utils::fnsig_arguments(ctx, &self);
         let abi = self.abi();
 
-        Ok(quote! {
-            unsafe extern #abi fn ( #( #arguments ),* ) #ret
-        })
+        match abi {
+            Abi::ThisCall if !ctx.options().rust_features().thiscall_abi() => {
+                warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target");
+                Ok(quote::Tokens::new())
+            }
+            _ => {
+                Ok(quote! {
+                    unsafe extern #abi fn ( #( #arguments ),* ) #ret
+                })
+            }
+        }
     }
 }
 
@@ -3132,6 +3144,10 @@ impl CodeGenerator for Function {
         }
 
         let abi = match signature.abi() {
+            Abi::ThisCall if !ctx.options().rust_features().thiscall_abi() => {
+                warn!("Skipping function with thiscall ABI that isn't supported by the configured Rust target");
+                return;
+            }
             Abi::Unknown(unknown_abi) => {
                 panic!(
                     "Invalid or unknown abi {:?} for function {:?} ({:?})",
diff --git a/src/features.rs b/src/features.rs
index 29e60ab735..b89185fd9b 100644
--- a/src/features.rs
+++ b/src/features.rs
@@ -140,6 +140,8 @@ rust_feature_def!(
     => untagged_union;
     /// Constant function ([RFC 911](https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md))
     => const_fn;
+    /// `thiscall` calling convention ([Tracking issue](https://github.com/rust-lang/rust/issues/42202))
+    => thiscall_abi;
 );
 
 impl From<RustTarget> for RustFeatures {
@@ -152,6 +154,7 @@ impl From<RustTarget> for RustFeatures {
 
         if rust_target >= RustTarget::Nightly {
             features.const_fn = true;
+            features.thiscall_abi = true;
         }
 
         features
diff --git a/src/ir/function.rs b/src/ir/function.rs
index b637f9f12b..2eab663835 100644
--- a/src/ir/function.rs
+++ b/src/ir/function.rs
@@ -142,6 +142,8 @@ pub enum Abi {
     Stdcall,
     /// The "fastcall" ABI.
     Fastcall,
+    /// The "thiscall" ABI.
+    ThisCall,
     /// The "aapcs" ABI.
     Aapcs,
     /// The "win64" ABI.
@@ -166,6 +168,7 @@ impl quote::ToTokens for Abi {
             Abi::C => quote! { "C" },
             Abi::Stdcall => quote! { "stdcall" },
             Abi::Fastcall => quote! { "fastcall" },
+            Abi::ThisCall => quote! { "thiscall" },
             Abi::Aapcs => quote! { "aapcs" },
             Abi::Win64 => quote! { "win64" },
             Abi::Unknown(cc) => panic!(
@@ -200,6 +203,7 @@ fn get_abi(cc: CXCallingConv) -> Abi {
         CXCallingConv_C => Abi::C,
         CXCallingConv_X86StdCall => Abi::Stdcall,
         CXCallingConv_X86FastCall => Abi::Fastcall,
+        CXCallingConv_X86ThisCall => Abi::ThisCall,
         CXCallingConv_AAPCS => Abi::Aapcs,
         CXCallingConv_X86_64Win64 => Abi::Win64,
         other => Abi::Unknown(other),
diff --git a/tests/expectations/tests/win32-thiscall_1_0.rs b/tests/expectations/tests/win32-thiscall_1_0.rs
new file mode 100644
index 0000000000..f84a70055b
--- /dev/null
+++ b/tests/expectations/tests/win32-thiscall_1_0.rs
@@ -0,0 +1,29 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+
+
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct Foo {
+    pub _address: u8,
+}
+#[test]
+fn bindgen_test_layout_Foo() {
+    assert_eq!(
+        ::std::mem::size_of::<Foo>(),
+        1usize,
+        concat!("Size of: ", stringify!(Foo))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<Foo>(),
+        1usize,
+        concat!("Alignment of ", stringify!(Foo))
+    );
+}
+impl Clone for Foo {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
diff --git a/tests/expectations/tests/win32-thiscall_nightly.rs b/tests/expectations/tests/win32-thiscall_nightly.rs
new file mode 100644
index 0000000000..0fd1c00ddc
--- /dev/null
+++ b/tests/expectations/tests/win32-thiscall_nightly.rs
@@ -0,0 +1,48 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+#![cfg(feature = "nightly")]
+#![feature(abi_thiscall)]
+
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct Foo {
+    pub _address: u8,
+}
+#[test]
+fn bindgen_test_layout_Foo() {
+    assert_eq!(
+        ::std::mem::size_of::<Foo>(),
+        1usize,
+        concat!("Size of: ", stringify!(Foo))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<Foo>(),
+        1usize,
+        concat!("Alignment of ", stringify!(Foo))
+    );
+}
+extern "thiscall" {
+    #[link_name = "\u{1}?test@Foo@@QAEXXZ"]
+    pub fn Foo_test(this: *mut Foo);
+}
+extern "thiscall" {
+    #[link_name = "\u{1}?test2@Foo@@QAEHH@Z"]
+    pub fn Foo_test2(this: *mut Foo, var: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
+}
+impl Clone for Foo {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+impl Foo {
+    #[inline]
+    pub unsafe fn test(&mut self) {
+        Foo_test(self)
+    }
+    #[inline]
+    pub unsafe fn test2(&mut self, var: ::std::os::raw::c_int) -> ::std::os::raw::c_int {
+        Foo_test2(self, var)
+    }
+}
diff --git a/tests/headers/win32-thiscall_1_0.hpp b/tests/headers/win32-thiscall_1_0.hpp
new file mode 100644
index 0000000000..5907c76eaf
--- /dev/null
+++ b/tests/headers/win32-thiscall_1_0.hpp
@@ -0,0 +1,7 @@
+// bindgen-flags: --rust-target 1.0 -- --target=i686-pc-windows-msvc
+
+class Foo {
+  public:
+    void test();
+    int test2(int var);
+};
diff --git a/tests/headers/win32-thiscall_nightly.hpp b/tests/headers/win32-thiscall_nightly.hpp
new file mode 100644
index 0000000000..2c9f2f1748
--- /dev/null
+++ b/tests/headers/win32-thiscall_nightly.hpp
@@ -0,0 +1,7 @@
+// bindgen-flags: --rust-target nightly --raw-line '#![cfg(feature = "nightly")]' --raw-line '#![feature(abi_thiscall)]' -- --target=i686-pc-windows-msvc
+
+class Foo {
+  public:
+    void test();
+    int test2(int var);
+};