Repository: dtolnay/cxx (language: rust)

Issue:
Lifetime doesn't work in pinned member function receiver
```cpp
// include/example.h

#pragma once
#include <memory>

struct Object {
  void f() {}
};
```

```rust
// src/main.rs

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("example/include/example.h");

        type Object<'a>;

        fn f(self: Pin<&mut Object>);
    }
}

fn main() {}
```

The above code should work but currently does not.

```console
error[E0726]: implicit elided lifetime not allowed here
 --> src/main.rs:8:29
  |
8 |         fn f(self: Pin<&mut Object>);
  |                             ^^^^^^- help: indicate the anonymous lifetime: `<'_>`

error[E0308]: mismatched `self` parameter type
 --> src/main.rs:8:20
  |
8 |         fn f(self: Pin<&mut Object>);
  |                    ^^^^^^^^^^^^^^^^ lifetime mismatch
  |
  = note: expected struct `Object<'static>`
             found struct `Object<'_>`
note: the anonymous lifetime #2 defined on the method body at 8:12...
 --> src/main.rs:8:12
  |
8 |         fn f(self: Pin<&mut Object>);
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^
  = note: ...does not necessarily outlive the static lifetime
```

<br>

Various attempted fixes also do not work:

<details>
<summary><code>fn f(self: Pin<&mut Object<'_>>)</code></summary>

```console
error[E0308]: mismatched `self` parameter type
 --> src/main.rs:8:20
  |
8 |         fn f(self: Pin<&mut Object<'_>>);
  |                    ^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
  |
  = note: expected struct `Object<'_>`
             found struct `Object<'_>`
note: the anonymous lifetime #2 defined on the method body at 8:12...
 --> src/main.rs:8:12
  |
8 |         fn f(self: Pin<&mut Object<'_>>);
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime `'_` as defined on the impl at 8:36
 --> src/main.rs:8:36
  |
8 |         fn f(self: Pin<&mut Object<'_>>);
  |                                    ^^

error[E0308]: mismatched `self` parameter type
 --> src/main.rs:8:20
  |
8 |         fn f(self: Pin<&mut Object<'_>>);
  |                    ^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
  |
  = note: expected struct `Object<'_>`
             found struct `Object<'_>`
note: the lifetime `'_` as defined on the impl at 8:36...
 --> src/main.rs:8:36
  |
8 |         fn f(self: Pin<&mut Object<'_>>);
  |                                    ^^
note: ...does not necessarily outlive the anonymous lifetime #2 defined on the method body at 8:12
 --> src/main.rs:8:12
  |
8 |         fn f(self: Pin<&mut Object<'_>>);
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```
</details>

<details>
<summary><code> fn f<'a>(self: Pin<&mut Object<'a>>)</code></summary>

```console
error[E0261]: use of undeclared lifetime name `'a`
 --> src/main.rs:8:40
  |
1 | #[cxx::bridge]
  |               - help: consider introducing lifetime `'a` here: `<'a>`
...
8 |         fn f<'a>(self: Pin<&mut Object<'a>>);
  |                                        ^^ undeclared lifetime
  |
  = help: if you want to experiment with in-band lifetime bindings, add `#![feature(in_band_lifetimes)]` to the crate attributes
```
</details>

<br>

This issue might be related to #657 but might be a separate bug because in the case of Pin it won't compile even with a `<'_>` added.


Discussion / hints:

Fix patch:
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 1b09cf09b..d4ceb3d21 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -436,7 +436,7 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
     let receiver = efn.receiver.iter().map(|receiver| {
         let var = receiver.var;
         if receiver.pinned {
-            let ty = receiver.ty();
+            let ty = receiver.ty_self();
             quote!(#var: #ty)
         } else {
             let ampersand = receiver.ampersand;
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index 7664427f6..bb04e1633 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -270,12 +270,18 @@ impl ToTokens for NamedType {
 }
 
 pub struct ReceiverType<'a>(&'a Receiver);
+pub struct ReceiverTypeSelf<'a>(&'a Receiver);
 
 impl Receiver {
     // &TheType
     pub fn ty(&self) -> ReceiverType {
         ReceiverType(self)
     }
+
+    // &Self
+    pub fn ty_self(&self) -> ReceiverTypeSelf {
+        ReceiverTypeSelf(self)
+    }
 }
 
 impl ToTokens for ReceiverType<'_> {
@@ -304,3 +310,30 @@ impl ToTokens for ReceiverType<'_> {
         }
     }
 }
+
+impl ToTokens for ReceiverTypeSelf<'_> {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        let Receiver {
+            pinned: _,
+            ampersand,
+            lifetime,
+            mutable: _,
+            var: _,
+            ty,
+            shorthand: _,
+            pin_tokens,
+            mutability,
+        } = &self.0;
+        if let Some((pin, langle, _rangle)) = pin_tokens {
+            tokens.extend(quote_spanned!(pin.span=> ::std::pin::Pin));
+            langle.to_tokens(tokens);
+        }
+        ampersand.to_tokens(tokens);
+        lifetime.to_tokens(tokens);
+        mutability.to_tokens(tokens);
+        Token![Self](ty.rust.span()).to_tokens(tokens);
+        if let Some((_pin, _langle, rangle)) = pin_tokens {
+            rangle.to_tokens(tokens);
+        }
+    }
+}


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 10e16a6bf..32d739db0 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -224,6 +224,7 @@ pub mod ffi {
         fn c_return_borrow(s: &CxxString) -> UniquePtr<Borrow>;
 
         fn const_member(self: &Borrow);
+        fn nonconst_member(self: Pin<&mut Borrow>);
     }
 
     #[repr(u32)]
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index d80785d4a..8ed54568f 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -211,6 +211,8 @@ Borrow::Borrow(const std::string &s) : s(s) {}
 
 void Borrow::const_member() const {}
 
+void Borrow::nonconst_member() {}
+
 std::unique_ptr<Borrow> c_return_borrow(const std::string &s) {
   return std::unique_ptr<Borrow>(new Borrow(s));
 }
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index cd265d492..a28c16659 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -80,6 +80,7 @@ enum COwnedEnum {
 struct Borrow {
   Borrow(const std::string &s);
   void const_member() const;
+  void nonconst_member();
   const std::string &s;
 };