Repository: dtolnay/cxx (rust)

Issue:
Support elided lifetime in member function receiver
```cpp
// include/example.h

#pragma once
#include <memory>

struct Object {
  void f() const {}
};
```

```rust
// src/main.rs

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("repro/include/example.h");

        type Object<'a>;

        fn f(self: &Object);
    }
}

fn main() {}
```

The above should work (with an elided lifetime on `Object` but currently doesn't.

```console
error[E0726]: implicit elided lifetime not allowed here
 --> src/main.rs:8:21
  |
8 |         fn f(self: &Object);
  |                     ^^^^^^- help: indicate the anonymous lifetime: `<'_>`
```

Writing `fn f(self: &Object<'_>)` instead makes it work but shouldn't be necessary.

Lifetime elision is already support in non-member functions, e.g. `fn f(obj: &Object)`.

Pull request description:
Implement lifetime elision for member function receivers
Fixes #657.

Patch:
diff --git a/macro/src/expand.rs b/macro/src/expand.rs
index 476012f3..1b09cf09 100644
--- a/macro/src/expand.rs
+++ b/macro/src/expand.rs
@@ -6,13 +6,13 @@ use crate::syntax::instantiate::ImplKey;
 use crate::syntax::report::Errors;
 use crate::syntax::symbol::Symbol;
 use crate::syntax::{
-    self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, Impl, Pair, Signature, Struct,
-    Trait, Type, TypeAlias, Types,
+    self, check, mangle, Api, Doc, Enum, ExternFn, ExternType, Impl, Lifetimes, Pair, Signature,
+    Struct, Trait, Type, TypeAlias, Types,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote, quote_spanned, ToTokens};
 use std::mem;
-use syn::{parse_quote, Result, Token};
+use syn::{parse_quote, punctuated, Lifetime, Result, Token};
 
 pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
     let ref mut errors = Errors::new();
@@ -617,16 +617,44 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
         #trampolines
         #dispatch
     });
-    let function_shim = quote! {
-        #doc
-        #attrs
-        #visibility #unsafety #fn_token #ident #generics #arg_list #ret #fn_body
-    };
     match &efn.receiver {
-        None => function_shim,
+        None => {
+            quote! {
+                #doc
+                #attrs
+                #visibility #unsafety #fn_token #ident #generics #arg_list #ret #fn_body
+            }
+        }
         Some(receiver) => {
-            let receiver_type = &receiver.ty;
-            quote!(impl #receiver_type { #function_shim })
+            let elided_generics;
+            let receiver_ident = &receiver.ty.rust;
+            let resolve = types.resolve(&receiver.ty);
+            let receiver_generics = if receiver.ty.generics.lt_token.is_some() {
+                &receiver.ty.generics
+            } else {
+                elided_generics = Lifetimes {
+                    lt_token: resolve.generics.lt_token,
+                    lifetimes: resolve
+                        .generics
+                        .lifetimes
+                        .pairs()
+                        .map(|pair| {
+                            let lifetime = Lifetime::new("'_", pair.value().apostrophe);
+                            let punct = pair.punct().map(|&&comma| comma);
+                            punctuated::Pair::new(lifetime, punct)
+                        })
+                        .collect(),
+                    gt_token: resolve.generics.gt_token,
+                };
+                &elided_generics
+            };
+            quote! {
+                impl #generics #receiver_ident #receiver_generics {
+                    #doc
+                    #attrs
+                    #visibility #unsafety #fn_token #ident #arg_list #ret #fn_body
+                }
+            }
         }
     }
 }


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 5ac461c8..10e16a6b 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -222,6 +222,8 @@ pub mod ffi {
 
         #[rust_name = "c_return_borrow_elided"]
         fn c_return_borrow(s: &CxxString) -> UniquePtr<Borrow>;
+
+        fn const_member(self: &Borrow);
     }
 
     #[repr(u32)]
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 57c48f5a..d80785d4 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -209,6 +209,8 @@ Enum c_return_enum(uint16_t n) {
 
 Borrow::Borrow(const std::string &s) : s(s) {}
 
+void Borrow::const_member() const {}
+
 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 44922862..cd265d49 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -79,6 +79,7 @@ enum COwnedEnum {
 
 struct Borrow {
   Borrow(const std::string &s);
+  void const_member() const;
   const std::string &s;
 };