Skip to content

Commit 7127d9d

Browse files
committed
Support static member functions
1 parent d47e4a3 commit 7127d9d

File tree

11 files changed

+111
-9
lines changed

11 files changed

+111
-9
lines changed

gen/src/write.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
7676
.or_insert_with(Vec::new)
7777
.push(efn);
7878
}
79+
if let Some(self_type) = &efn.self_type {
80+
methods_for_type
81+
.entry(&out.types.resolve(self_type).name.rust)
82+
.or_insert_with(Vec::new)
83+
.push(efn);
84+
}
7985
}
8086
}
8187

@@ -274,6 +280,9 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern
274280
let sig = &method.sig;
275281
let local_name = method.name.cxx.to_string();
276282
let indirect_call = false;
283+
if method.self_type.is_some() {
284+
write!(out, "static ");
285+
}
277286
write_rust_function_shim_decl(out, &local_name, sig, indirect_call);
278287
writeln!(out, ";");
279288
if !method.doc.is_empty() {
@@ -770,14 +779,21 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
770779
}
771780
}
772781
write!(out, " = ");
773-
match &efn.receiver {
774-
None => write!(out, "{}", efn.name.to_fully_qualified()),
775-
Some(receiver) => write!(
782+
match (&efn.receiver, &efn.self_type) {
783+
(None, None) => write!(out, "{}", efn.name.to_fully_qualified()),
784+
(Some(receiver), None) => write!(
776785
out,
777786
"&{}::{}",
778787
out.types.resolve(&receiver.ty).name.to_fully_qualified(),
779788
efn.name.cxx,
780789
),
790+
(None, Some(self_type)) => write!(
791+
out,
792+
"&{}::{}",
793+
out.types.resolve(self_type).name.to_fully_qualified(),
794+
efn.name.cxx,
795+
),
796+
_ => unreachable!("impossible combination of receiver and self_type"),
781797
}
782798
writeln!(out, ";");
783799
write!(out, " ");

macro/src/expand.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -741,15 +741,15 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
741741
#trampolines
742742
#dispatch
743743
});
744-
match &efn.receiver {
745-
None => {
744+
match (&efn.receiver, &efn.self_type) {
745+
(None, None) => {
746746
quote! {
747747
#doc
748748
#attrs
749749
#visibility #unsafety #fn_token #ident #generics #arg_list #ret #fn_body
750750
}
751751
}
752-
Some(receiver) => {
752+
(Some(receiver), None) => {
753753
let elided_generics;
754754
let receiver_ident = &receiver.ty.rust;
755755
let resolve = types.resolve(&receiver.ty);
@@ -781,6 +781,39 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
781781
}
782782
}
783783
}
784+
(None, Some(self_type)) => {
785+
let elided_generics;
786+
let resolve = types.resolve(self_type);
787+
let self_type_ident = &resolve.name.rust;
788+
let self_type_generics = if resolve.generics.lt_token.is_some() {
789+
&resolve.generics
790+
} else {
791+
elided_generics = Lifetimes {
792+
lt_token: resolve.generics.lt_token,
793+
lifetimes: resolve
794+
.generics
795+
.lifetimes
796+
.pairs()
797+
.map(|pair| {
798+
let lifetime = Lifetime::new("'_", pair.value().apostrophe);
799+
let punct = pair.punct().map(|&&comma| comma);
800+
punctuated::Pair::new(lifetime, punct)
801+
})
802+
.collect(),
803+
gt_token: resolve.generics.gt_token,
804+
};
805+
&elided_generics
806+
};
807+
quote_spanned! {ident.span()=>
808+
#[automatically_derived]
809+
impl #generics #self_type_ident #self_type_generics {
810+
#doc
811+
#attrs
812+
#visibility #unsafety #fn_token #ident #arg_list #ret #fn_body
813+
}
814+
}
815+
}
816+
_ => unreachable!("receiver and self_type are mutually exclusive"),
784817
}
785818
}
786819

syntax/attrs.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub(crate) struct Parser<'a> {
3535
pub namespace: Option<&'a mut Namespace>,
3636
pub cxx_name: Option<&'a mut Option<ForeignName>>,
3737
pub rust_name: Option<&'a mut Option<Ident>>,
38+
pub self_type: Option<&'a mut Option<Ident>>,
3839
pub variants_from_header: Option<&'a mut Option<Attribute>>,
3940
pub ignore_unrecognized: bool,
4041

@@ -129,6 +130,19 @@ pub(crate) fn parse(cx: &mut Errors, attrs: Vec<Attribute>, mut parser: Parser)
129130
break;
130131
}
131132
}
133+
} else if attr_path.is_ident("Self") {
134+
match parse_rust_name_attribute(&attr.meta) {
135+
Ok(attr) => {
136+
if let Some(namespace) = &mut parser.self_type {
137+
**namespace = Some(attr);
138+
continue;
139+
}
140+
}
141+
Err(err) => {
142+
cx.push(err);
143+
break;
144+
}
145+
}
132146
} else if attr_path.is_ident("cfg") {
133147
match cfg::parse_attribute(&attr) {
134148
Ok(cfg_expr) => {

syntax/check.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,19 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
498498
if efn.lang == Lang::Cxx {
499499
check_mut_return_restriction(cx, efn);
500500
}
501+
502+
if let Some(self_type) = &efn.self_type {
503+
if !cx.types.structs.contains_key(self_type)
504+
&& !cx.types.cxx.contains(self_type)
505+
&& !cx.types.rust.contains(self_type)
506+
{
507+
let msg = format!("unrecognized self type: {}", self_type);
508+
cx.error(self_type, msg);
509+
}
510+
if efn.receiver.is_some() {
511+
cx.error(efn, "self type and receiver are mutually exclusive");
512+
}
513+
}
501514
}
502515

503516
fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {

syntax/mangle.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ macro_rules! join {
8585
}
8686

8787
pub(crate) fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol {
88-
match &efn.receiver {
89-
Some(receiver) => {
88+
match (&efn.receiver, &efn.self_type) {
89+
(Some(receiver), None) => {
9090
let receiver_ident = types.resolve(&receiver.ty);
9191
join!(
9292
efn.name.namespace,
@@ -95,7 +95,17 @@ pub(crate) fn extern_fn(efn: &ExternFn, types: &Types) -> Symbol {
9595
efn.name.rust,
9696
)
9797
}
98-
None => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust),
98+
(None, Some(self_type)) => {
99+
let self_type_ident = types.resolve(self_type);
100+
join!(
101+
efn.name.namespace,
102+
CXXBRIDGE,
103+
self_type_ident.name.cxx,
104+
efn.name.rust,
105+
)
106+
}
107+
(None, None) => join!(efn.name.namespace, CXXBRIDGE, efn.name.rust),
108+
_ => unreachable!("extern function cannot have both receiver and self_type"),
99109
}
100110
}
101111

syntax/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ pub(crate) struct ExternFn {
162162
pub sig: Signature,
163163
pub semi_token: Token![;],
164164
pub trusted: bool,
165+
pub self_type: Option<Ident>,
165166
}
166167

167168
pub(crate) struct TypeAlias {

syntax/parse.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ fn parse_extern_fn(
525525
let mut namespace = namespace.clone();
526526
let mut cxx_name = None;
527527
let mut rust_name = None;
528+
let mut self_type = None;
528529
let mut attrs = attrs.clone();
529530
attrs.extend(attrs::parse(
530531
cx,
@@ -535,6 +536,7 @@ fn parse_extern_fn(
535536
namespace: Some(&mut namespace),
536537
cxx_name: Some(&mut cxx_name),
537538
rust_name: Some(&mut rust_name),
539+
self_type: Some(&mut self_type),
538540
..Default::default()
539541
},
540542
));
@@ -694,6 +696,7 @@ fn parse_extern_fn(
694696
},
695697
semi_token,
696698
trusted,
699+
self_type,
697700
}))
698701
}
699702

tests/ffi/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ pub mod ffi {
203203
fn c_method_on_shared(self: &Shared) -> usize;
204204
fn c_method_ref_on_shared(self: &Shared) -> &usize;
205205
fn c_method_mut_on_shared(self: &mut Shared) -> &mut usize;
206+
#[Self = "Shared"]
207+
fn c_static_method_on_shared() -> usize;
206208
fn c_set_array(self: &mut Array, value: i32);
207209

208210
fn c_get_use_count(weak: &WeakPtr<C>) -> usize;
@@ -218,6 +220,9 @@ pub mod ffi {
218220

219221
#[namespace = "other"]
220222
fn ns_c_take_ns_shared(shared: AShared);
223+
224+
#[Self = "C"]
225+
fn c_static_method() -> usize;
221226
}
222227

223228
extern "C++" {

tests/ffi/tests.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const size_t &Shared::c_method_ref_on_shared() const noexcept {
4444

4545
size_t &Shared::c_method_mut_on_shared() noexcept { return this->z; }
4646

47+
size_t Shared::c_static_method_on_shared() noexcept { return 2025; }
48+
4749
void Array::c_set_array(int32_t val) noexcept {
4850
this->a = {val, val, val, val};
4951
}
@@ -627,6 +629,8 @@ rust::String cOverloadedFunction(rust::Str x) {
627629
return rust::String(std::string(x));
628630
}
629631

632+
size_t C::c_static_method() { return 2026; }
633+
630634
void c_take_trivial_ptr(std::unique_ptr<D> d) {
631635
if (d->d == 30) {
632636
cxx_test_suite_set_correct();

tests/ffi/tests.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class C {
5454
rust::String cOverloadedMethod(int32_t x) const;
5555
rust::String cOverloadedMethod(rust::Str x) const;
5656

57+
static size_t c_static_method();
5758
private:
5859
size_t n;
5960
std::vector<uint8_t> v;

0 commit comments

Comments
 (0)