Skip to content

Commit

Permalink
Support static member functions
Browse files Browse the repository at this point in the history
  • Loading branch information
yujincheng08 committed Jan 31, 2025
1 parent d47e4a3 commit 7127d9d
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 9 deletions.
22 changes: 19 additions & 3 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

Expand Down Expand Up @@ -274,6 +280,9 @@ 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;
if method.self_type.is_some() {
write!(out, "static ");
}
write_rust_function_shim_decl(out, &local_name, sig, indirect_call);
writeln!(out, ";");
if !method.doc.is_empty() {
Expand Down Expand Up @@ -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, " ");
Expand Down
39 changes: 36 additions & 3 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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"),
}
}

Expand Down
14 changes: 14 additions & 0 deletions syntax/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub(crate) struct Parser<'a> {
pub namespace: Option<&'a mut Namespace>,
pub cxx_name: Option<&'a mut Option<ForeignName>>,
pub rust_name: Option<&'a mut Option<Ident>>,
pub self_type: Option<&'a mut Option<Ident>>,
pub variants_from_header: Option<&'a mut Option<Attribute>>,
pub ignore_unrecognized: bool,

Expand Down Expand Up @@ -129,6 +130,19 @@ pub(crate) fn parse(cx: &mut Errors, attrs: Vec<Attribute>, 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) => {
Expand Down
13 changes: 13 additions & 0 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
16 changes: 13 additions & 3 deletions syntax/mangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"),
}
}

Expand Down
1 change: 1 addition & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ pub(crate) struct ExternFn {
pub sig: Signature,
pub semi_token: Token![;],
pub trusted: bool,
pub self_type: Option<Ident>,
}

pub(crate) struct TypeAlias {
Expand Down
3 changes: 3 additions & 0 deletions syntax/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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()
},
));
Expand Down Expand Up @@ -694,6 +696,7 @@ fn parse_extern_fn(
},
semi_token,
trusted,
self_type,
}))
}

Expand Down
5 changes: 5 additions & 0 deletions tests/ffi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<C>) -> usize;
Expand All @@ -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++" {
Expand Down
4 changes: 4 additions & 0 deletions tests/ffi/tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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};
}
Expand Down Expand Up @@ -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> d) {
if (d->d == 30) {
cxx_test_suite_set_correct();
Expand Down
1 change: 1 addition & 0 deletions tests/ffi/tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t> v;
Expand Down
2 changes: 2 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 7127d9d

Please sign in to comment.