From 7150b2e76682672da07a89aab58f0e531091bea7 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Fri, 31 Jan 2025 23:35:30 +0800 Subject: [PATCH] Support static member functions --- gen/src/write.rs | 43 +++++++++++++++++------ macro/src/expand.rs | 84 ++++++++++++++++++++++++++++++++++++++------- syntax/attrs.rs | 14 ++++++++ syntax/check.rs | 13 +++++++ syntax/mangle.rs | 16 +++++++-- syntax/mod.rs | 1 + syntax/parse.rs | 3 ++ tests/ffi/lib.rs | 18 ++++++++++ tests/ffi/tests.cc | 6 ++++ tests/ffi/tests.h | 1 + tests/test.rs | 2 ++ 11 files changed, 175 insertions(+), 26 deletions(-) diff --git a/gen/src/write.rs b/gen/src/write.rs index 77e1da0b2..d3e99646b 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -76,6 +76,12 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) { .or_insert_with(Vec::new) .push(efn); } + if let Some(self_type) = &efn.self_type { + methods_for_type + .entry(&out.types.resolve(self_type).name.rust) + .or_insert_with(Vec::new) + .push(efn); + } } } @@ -274,7 +280,10 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern let sig = &method.sig; let local_name = method.name.cxx.to_string(); let indirect_call = false; - write_rust_function_shim_decl(out, &local_name, sig, indirect_call); + if method.self_type.is_some() { + write!(out, "static "); + } + write_rust_function_shim_decl(out, &local_name, sig, &None, indirect_call); writeln!(out, ";"); if !method.doc.is_empty() { out.next_section(); @@ -366,7 +375,7 @@ fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[ let sig = &method.sig; let local_name = method.name.cxx.to_string(); let indirect_call = false; - write_rust_function_shim_decl(out, &local_name, sig, indirect_call); + write_rust_function_shim_decl(out, &local_name, sig, &method.self_type, indirect_call); writeln!(out, ";"); if !method.doc.is_empty() { out.next_section(); @@ -770,14 +779,21 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { } } write!(out, " = "); - match &efn.receiver { - None => write!(out, "{}", efn.name.to_fully_qualified()), - Some(receiver) => write!( + match (&efn.receiver, &efn.self_type) { + (None, None) => write!(out, "{}", efn.name.to_fully_qualified()), + (Some(receiver), None) => write!( out, "&{}::{}", out.types.resolve(&receiver.ty).name.to_fully_qualified(), efn.name.cxx, ), + (None, Some(self_type)) => write!( + out, + "&{}::{}", + out.types.resolve(self_type).name.to_fully_qualified(), + efn.name.cxx, + ), + _ => unreachable!("impossible combination of receiver and self_type"), } writeln!(out, ";"); write!(out, " "); @@ -878,7 +894,7 @@ fn write_function_pointer_trampoline(out: &mut OutFile, efn: &ExternFn, var: &Pa out.next_section(); let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string(); let doc = Doc::new(); - write_rust_function_shim_impl(out, &c_trampoline, f, &doc, &r_trampoline, indirect_call); + write_rust_function_shim_impl(out, &c_trampoline, f, &efn.self_type, &doc, &r_trampoline, indirect_call); } fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { @@ -963,18 +979,24 @@ fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { let doc = &efn.doc; let invoke = mangle::extern_fn(efn, out.types); let indirect_call = false; - write_rust_function_shim_impl(out, &local_name, efn, doc, &invoke, indirect_call); + write_rust_function_shim_impl(out, &local_name, efn, &efn.self_type, doc, &invoke, indirect_call); } fn write_rust_function_shim_decl( out: &mut OutFile, local_name: &str, sig: &Signature, + self_type: &Option, indirect_call: bool, ) { begin_function_definition(out); write_return_type(out, &sig.ret); - write!(out, "{}(", local_name); + if let Some(self_type) = self_type { + write!(out, "{}::{}(", out.types.resolve(self_type).name.cxx, local_name); + } else { + write!(out, "{}(", local_name); + + } for (i, arg) in sig.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -1003,11 +1025,12 @@ fn write_rust_function_shim_impl( out: &mut OutFile, local_name: &str, sig: &Signature, + self_type: &Option, doc: &Doc, invoke: &Symbol, indirect_call: bool, ) { - if out.header && sig.receiver.is_some() { + if out.header && (sig.receiver.is_some() || self_type.is_some()) { // We've already defined this inside the struct. return; } @@ -1015,7 +1038,7 @@ fn write_rust_function_shim_impl( // Member functions already documented at their declaration. write_doc(out, "", doc); } - write_rust_function_shim_decl(out, local_name, sig, indirect_call); + write_rust_function_shim_decl(out, local_name, sig, self_type, indirect_call); if out.header { writeln!(out, ";"); return; diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4fcf3e00f..29722c505 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -741,15 +741,15 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { #trampolines #dispatch }); - match &efn.receiver { - None => { + match (&efn.receiver, &efn.self_type) { + (None, None) => { quote! { #doc #attrs #visibility #unsafety #fn_token #ident #generics #arg_list #ret #fn_body } } - Some(receiver) => { + (Some(receiver), None) => { let elided_generics; let receiver_ident = &receiver.ty.rust; let resolve = types.resolve(&receiver.ty); @@ -781,6 +781,39 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { } } } + (None, Some(self_type)) => { + let elided_generics; + let resolve = types.resolve(self_type); + let self_type_ident = &resolve.name.rust; + let self_type_generics = if resolve.generics.lt_token.is_some() { + &resolve.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_spanned! {ident.span()=> + #[automatically_derived] + impl #generics #self_type_ident #self_type_generics { + #doc + #attrs + #visibility #unsafety #fn_token #ident #arg_list #ret #fn_body + } + } + } + _ => unreachable!("receiver and self_type are mutually exclusive"), } } @@ -797,6 +830,7 @@ fn expand_function_pointer_trampoline( let body_span = efn.semi_token.span; let shim = expand_rust_function_shim_impl( sig, + &efn.self_type, types, &r_trampoline, local_name, @@ -940,18 +974,33 @@ fn expand_forbid(impls: TokenStream) -> TokenStream { fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let link_name = mangle::extern_fn(efn, types); - let local_name = match &efn.receiver { - None => format_ident!("__{}", efn.name.rust), - Some(receiver) => format_ident!("__{}__{}", receiver.ty.rust, efn.name.rust), + let local_name = match (&efn.receiver, &efn.self_type) { + (None, None) => format_ident!("__{}", efn.name.rust), + (Some(receiver), None) => format_ident!("__{}__{}", receiver.ty.rust, efn.name.rust), + (None, Some(self_type)) => format_ident!( + "__{}__{}", + types.resolve(self_type).name.rust, + efn.name.rust + ), + _ => unreachable!("receiver and self_type are mutually exclusive"), }; - let prevent_unwind_label = match &efn.receiver { - None => format!("::{}", efn.name.rust), - Some(receiver) => format!("::{}::{}", receiver.ty.rust, efn.name.rust), + let prevent_unwind_label = match (&efn.receiver, &efn.self_type) { + (None, None) => format!("::{}", efn.name.rust), + (Some(receiver), None) => format!("::{}::{}", receiver.ty.rust, efn.name.rust), + (None, Some(self_type)) => { + format!( + "::{}::{}", + types.resolve(self_type).name.rust, + efn.name.rust + ) + } + _ => unreachable!("receiver and self_type are mutually exclusive"), }; let invoke = Some(&efn.name.rust); let body_span = efn.semi_token.span; expand_rust_function_shim_impl( efn, + &efn.self_type, types, &link_name, local_name, @@ -965,6 +1014,7 @@ fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { fn expand_rust_function_shim_impl( sig: &Signature, + self_type: &Option, types: &Types, link_name: &Symbol, local_name: Ident, @@ -1057,7 +1107,8 @@ fn expand_rust_function_shim_impl( }); let vars: Vec<_> = receiver_var.into_iter().chain(arg_vars).collect(); - let wrap_super = invoke.map(|invoke| expand_rust_function_shim_super(sig, &local_name, invoke)); + let wrap_super = invoke + .map(|invoke| expand_rust_function_shim_super(sig, self_type, types, &local_name, invoke)); let mut requires_closure; let mut call = match invoke { @@ -1182,6 +1233,8 @@ fn expand_rust_function_shim_impl( // accurate unsafety declaration and no problematic elided lifetimes. fn expand_rust_function_shim_super( sig: &Signature, + self_type: &Option, + types: &Types, local_name: &Ident, invoke: &Ident, ) -> TokenStream { @@ -1222,12 +1275,17 @@ fn expand_rust_function_shim_super( let vars = receiver_var.iter().chain(arg_vars); let span = invoke.span(); - let call = match &sig.receiver { - None => quote_spanned!(span=> super::#invoke), - Some(receiver) => { + let call = match (&sig.receiver, &self_type) { + (None, None) => quote_spanned!(span=> super::#invoke), + (Some(receiver), None) => { let receiver_type = &receiver.ty.rust; quote_spanned!(span=> #receiver_type::#invoke) } + (None, Some(self_type)) => { + let self_type = &types.resolve(self_type).name.rust; + quote_spanned!(span=> #self_type::#invoke) + } + _ => unreachable!("receiver and self_type are mutually exclusive"), }; let mut body = quote_spanned!(span=> #call(#(#vars,)*)); diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 894b82b83..7fd4a824b 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -35,6 +35,7 @@ pub(crate) struct Parser<'a> { pub namespace: Option<&'a mut Namespace>, pub cxx_name: Option<&'a mut Option>, pub rust_name: Option<&'a mut Option>, + pub self_type: Option<&'a mut Option>, pub variants_from_header: Option<&'a mut Option>, pub ignore_unrecognized: bool, @@ -129,6 +130,19 @@ pub(crate) fn parse(cx: &mut Errors, attrs: Vec, mut parser: Parser) break; } } + } else if attr_path.is_ident("Self") { + match parse_rust_name_attribute(&attr.meta) { + Ok(attr) => { + if let Some(namespace) = &mut parser.self_type { + **namespace = Some(attr); + continue; + } + } + Err(err) => { + cx.push(err); + break; + } + } } else if attr_path.is_ident("cfg") { match cfg::parse_attribute(&attr) { Ok(cfg_expr) => { diff --git a/syntax/check.rs b/syntax/check.rs index 39ee0b0a4..d6896d8bb 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -498,6 +498,19 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if efn.lang == Lang::Cxx { check_mut_return_restriction(cx, efn); } + + if let Some(self_type) = &efn.self_type { + if !cx.types.structs.contains_key(self_type) + && !cx.types.cxx.contains(self_type) + && !cx.types.rust.contains(self_type) + { + let msg = format!("unrecognized self type: {}", self_type); + cx.error(self_type, msg); + } + if efn.receiver.is_some() { + cx.error(efn, "self type and receiver are mutually exclusive"); + } + } } fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) { diff --git a/syntax/mangle.rs b/syntax/mangle.rs index 6f019657b..ad8682008 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -85,8 +85,8 @@ macro_rules! join { } pub(crate) fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { - match &efn.receiver { - Some(receiver) => { + match (&efn.receiver, &efn.self_type) { + (Some(receiver), None) => { let receiver_ident = types.resolve(&receiver.ty); join!( efn.name.namespace, @@ -95,7 +95,17 @@ pub(crate) fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol { efn.name.rust, ) } - None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust), + (None, Some(self_type)) => { + let self_type_ident = types.resolve(self_type); + join!( + efn.name.namespace, + CXXBRIDGE, + self_type_ident.name.cxx, + efn.name.rust, + ) + } + (None, None) => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust), + _ => unreachable!("extern function cannot have both receiver and self_type"), } } diff --git a/syntax/mod.rs b/syntax/mod.rs index eacba5541..26febef0e 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -162,6 +162,7 @@ pub(crate) struct ExternFn { pub sig: Signature, pub semi_token: Token![;], pub trusted: bool, + pub self_type: Option, } pub(crate) struct TypeAlias { diff --git a/syntax/parse.rs b/syntax/parse.rs index 875e1d38a..764214965 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -525,6 +525,7 @@ fn parse_extern_fn( let mut namespace = namespace.clone(); let mut cxx_name = None; let mut rust_name = None; + let mut self_type = None; let mut attrs = attrs.clone(); attrs.extend(attrs::parse( cx, @@ -535,6 +536,7 @@ fn parse_extern_fn( namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), + self_type: Some(&mut self_type), ..Default::default() }, )); @@ -694,6 +696,7 @@ fn parse_extern_fn( }, semi_token, trusted, + self_type, })) } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 9e060d3ee..4210a32fb 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -203,6 +203,8 @@ pub mod ffi { fn c_method_on_shared(self: &Shared) -> usize; fn c_method_ref_on_shared(self: &Shared) -> &usize; fn c_method_mut_on_shared(self: &mut Shared) -> &mut usize; + #[Self = "Shared"] + fn c_static_method_on_shared() -> usize; fn c_set_array(self: &mut Array, value: i32); fn c_get_use_count(weak: &WeakPtr) -> usize; @@ -218,6 +220,9 @@ pub mod ffi { #[namespace = "other"] fn ns_c_take_ns_shared(shared: AShared); + + #[Self = "C"] + fn c_static_method() -> usize; } extern "C++" { @@ -315,6 +320,12 @@ pub mod ffi { #[cxx_name = "rAliasedFunction"] fn r_aliased_function(x: i32) -> String; + + #[Self = "Shared"] + fn r_static_method_on_shared() -> usize; + + #[Self = "R"] + fn r_static_method() -> usize; } struct Dag0 { @@ -406,6 +417,10 @@ impl R { self.0 = n; n } + + fn r_static_method() -> usize { + 2024 + } } pub struct Reference<'a>(pub &'a String); @@ -414,6 +429,9 @@ impl ffi::Shared { fn r_method_on_shared(&self) -> String { "2020".to_owned() } + fn r_static_method_on_shared() -> usize { + 2023 + } } impl ffi::Array { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 2292914cd..a3c9cc120 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -44,6 +44,8 @@ const size_t &Shared::c_method_ref_on_shared() const noexcept { size_t &Shared::c_method_mut_on_shared() noexcept { return this->z; } +size_t Shared::c_static_method_on_shared() noexcept { return 2025; } + void Array::c_set_array(int32_t val) noexcept { this->a = {val, val, val, val}; } @@ -627,6 +629,8 @@ rust::String cOverloadedFunction(rust::Str x) { return rust::String(std::string(x)); } +size_t C::c_static_method() { return 2026; } + void c_take_trivial_ptr(std::unique_ptr d) { if (d->d == 30) { cxx_test_suite_set_correct(); @@ -786,6 +790,8 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(r_return_enum(0) == Enum::AVal); ASSERT(r_return_enum(1) == Enum::BVal); ASSERT(r_return_enum(2021) == Enum::CVal); + ASSERT(Shared::r_static_method_on_shared() == 2023); + ASSERT(R::r_static_method() == 2024); r_take_primitive(2020); r_take_shared(Shared{2020}); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index dc02e4ff8..18cf80f9e 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -54,6 +54,7 @@ class C { rust::String cOverloadedMethod(int32_t x) const; rust::String cOverloadedMethod(rust::Str x) const; + static size_t c_static_method(); private: size_t n; std::vector v; diff --git a/tests/test.rs b/tests/test.rs index 3fe4ea5f8..fc9e13db1 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -264,6 +264,8 @@ fn test_c_method_calls() { assert_eq!(2021, ffi::Shared { z: 0 }.c_method_on_shared()); assert_eq!(2022, *ffi::Shared { z: 2022 }.c_method_ref_on_shared()); assert_eq!(2023, *ffi::Shared { z: 2023 }.c_method_mut_on_shared()); + assert_eq!(2025, ffi::Shared::c_static_method_on_shared()); + assert_eq!(2026, ffi::C::c_static_method()); let val = 42; let mut array = ffi::Array {