Repository: dtolnay/cxx (language: rust)

Issue:
Regression in 1.0.40 handling of extern C++ return reference types
With 1.0.40, this code:

```rust
    unsafe extern "C++" {
        include!("rpmostree-clientlib.h");
        fn client_require_root() -> Result<()>;
        type ClientConnection;
        fn new_client_connection() -> Result<UniquePtr<ClientConnection>>;
        fn get_connection<'a>(self: Pin<&'a mut ClientConnection>) -> Pin<&'a mut GDBusConnection>;
        fn transaction_connect_progress_sync(&self, address: &str) -> Result<()>;
    }
```

no longer compiles:

```
error[E0308]: mismatched types
   --> rust/src/lib.rs:26:1
    |
26  | #[cxx::bridge(namespace = "rpmostreecxx")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Pin`, found `&mut _`
...
381 |         fn get_connection<'a>(self: Pin<&'a mut ClientConnection>) -> Pin<&'a mut GDBusConnection>;
    |                                                                       ---------------------------- expected `Pin<&'a mut cxxrsutil::FFIGDBusConnection>` because of return type
    |
    = note:         expected struct `Pin<&'a mut cxxrsutil::FFIGDBusConnection>`
            found mutable reference `&mut _`
    = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

```

(Associated C++ code is here: https://github.com/coreos/rpm-ostree/blob/bef1fc792655e6a22cc8676949a6a5792686ee2b/src/app/rpmostree-clientlib.h#L37-L56)

It's not clear from the error message what exactly is wrong. It said it found a `&mut _` but the underlined type matches exactly the expected type (`GDBusConnection` is the same type as `cxxrsutil::FFIGDBusConnection`).


Discussion / hints:

Fix patch:
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 73e2edf3c..12e4fe08a 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -619,7 +619,11 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
                     },
                     inner if types.is_considered_improper_ctype(inner) => {
                         let mutability = ty.mutability;
-                        quote!(&#mutability *#call.cast())
+                        let deref_mut = quote!(&#mutability *#call.cast());
+                        match ty.pinned {
+                            false => deref_mut,
+                            true => quote!(::std::pin::Pin::new_unchecked(#deref_mut)),
+                        }
                     }
                     _ => call,
                 },


Test patch:
diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs
index 089a5c5ff..21a86206d 100644
--- a/tests/ffi/module.rs
+++ b/tests/ffi/module.rs
@@ -52,6 +52,7 @@ pub mod ffi2 {
         fn c_return_trivial_ns_ptr() -> UniquePtr<G>;
         fn c_return_trivial_ns() -> G;
         fn c_return_opaque_ptr() -> UniquePtr<E>;
+        fn c_return_opaque_mut_pin(e: Pin<&mut E>) -> Pin<&mut E>;
         fn c_return_ns_opaque_ptr() -> UniquePtr<F>;
         fn c_return_ns_unique_ptr() -> UniquePtr<H>;
         fn c_take_ref_ns_c(h: &H);
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 9259f38bf..ba12ed45b 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -713,6 +713,8 @@ std::unique_ptr<E> c_return_opaque_ptr() {
   return e;
 }
 
+E &c_return_opaque_mut_pin(E &e) { return e; }
+
 std::unique_ptr<::F::F> c_return_ns_opaque_ptr() {
   auto f = std::unique_ptr<::F::F>(new ::F::F());
   f->f = 40;
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index a5d8bc034..74acbf4fd 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -201,6 +201,7 @@ D c_return_trivial();
 std::unique_ptr<::G::G> c_return_trivial_ns_ptr();
 ::G::G c_return_trivial_ns();
 std::unique_ptr<E> c_return_opaque_ptr();
+E &c_return_opaque_mut_pin(E &e);
 std::unique_ptr<::F::F> c_return_ns_opaque_ptr();
 
 rust::String cOverloadedFunction(int32_t x);