Skip to content

Commit 64e5e41

Browse files
committed
Support static member functions
1 parent d47e4a3 commit 64e5e41

File tree

11 files changed

+178
-26
lines changed

11 files changed

+178
-26
lines changed

gen/src/write.rs

+36-10
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,7 +280,10 @@ 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;
277-
write_rust_function_shim_decl(out, &local_name, sig, indirect_call);
283+
if method.self_type.is_some() {
284+
write!(out, "static ");
285+
}
286+
write_rust_function_shim_decl(out, &local_name, sig, &None, indirect_call);
278287
writeln!(out, ";");
279288
if !method.doc.is_empty() {
280289
out.next_section();
@@ -366,7 +375,10 @@ fn write_opaque_type<'a>(out: &mut OutFile<'a>, ety: &'a ExternType, methods: &[
366375
let sig = &method.sig;
367376
let local_name = method.name.cxx.to_string();
368377
let indirect_call = false;
369-
write_rust_function_shim_decl(out, &local_name, sig, indirect_call);
378+
if method.self_type.is_some() {
379+
write!(out, "static ");
380+
}
381+
write_rust_function_shim_decl(out, &local_name, sig, &None, indirect_call);
370382
writeln!(out, ";");
371383
if !method.doc.is_empty() {
372384
out.next_section();
@@ -770,14 +782,21 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
770782
}
771783
}
772784
write!(out, " = ");
773-
match &efn.receiver {
774-
None => write!(out, "{}", efn.name.to_fully_qualified()),
775-
Some(receiver) => write!(
785+
match (&efn.receiver, &efn.self_type) {
786+
(None, None) => write!(out, "{}", efn.name.to_fully_qualified()),
787+
(Some(receiver), None) => write!(
776788
out,
777789
"&{}::{}",
778790
out.types.resolve(&receiver.ty).name.to_fully_qualified(),
779791
efn.name.cxx,
780792
),
793+
(None, Some(self_type)) => write!(
794+
out,
795+
"&{}::{}",
796+
out.types.resolve(self_type).name.to_fully_qualified(),
797+
efn.name.cxx,
798+
),
799+
_ => unreachable!("receiver and self_type are mutually exclusive"),
781800
}
782801
writeln!(out, ";");
783802
write!(out, " ");
@@ -878,7 +897,7 @@ fn write_function_pointer_trampoline(out: &mut OutFile, efn: &ExternFn, var: &Pa
878897
out.next_section();
879898
let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string();
880899
let doc = Doc::new();
881-
write_rust_function_shim_impl(out, &c_trampoline, f, &doc, &r_trampoline, indirect_call);
900+
write_rust_function_shim_impl(out, &c_trampoline, f, &efn.self_type, &doc, &r_trampoline, indirect_call);
882901
}
883902

884903
fn write_rust_function_decl<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
@@ -963,18 +982,24 @@ fn write_rust_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) {
963982
let doc = &efn.doc;
964983
let invoke = mangle::extern_fn(efn, out.types);
965984
let indirect_call = false;
966-
write_rust_function_shim_impl(out, &local_name, efn, doc, &invoke, indirect_call);
985+
write_rust_function_shim_impl(out, &local_name, efn, &efn.self_type, doc, &invoke, indirect_call);
967986
}
968987

969988
fn write_rust_function_shim_decl(
970989
out: &mut OutFile,
971990
local_name: &str,
972991
sig: &Signature,
992+
self_type: &Option<Ident>,
973993
indirect_call: bool,
974994
) {
975995
begin_function_definition(out);
976996
write_return_type(out, &sig.ret);
977-
write!(out, "{}(", local_name);
997+
if let Some(self_type) = self_type {
998+
write!(out, "{}::{}(", out.types.resolve(self_type).name.cxx, local_name);
999+
} else {
1000+
write!(out, "{}(", local_name);
1001+
1002+
}
9781003
for (i, arg) in sig.args.iter().enumerate() {
9791004
if i > 0 {
9801005
write!(out, ", ");
@@ -1003,19 +1028,20 @@ fn write_rust_function_shim_impl(
10031028
out: &mut OutFile,
10041029
local_name: &str,
10051030
sig: &Signature,
1031+
self_type: &Option<Ident>,
10061032
doc: &Doc,
10071033
invoke: &Symbol,
10081034
indirect_call: bool,
10091035
) {
1010-
if out.header && sig.receiver.is_some() {
1036+
if out.header && (sig.receiver.is_some() || self_type.is_some()) {
10111037
// We've already defined this inside the struct.
10121038
return;
10131039
}
10141040
if sig.receiver.is_none() {
10151041
// Member functions already documented at their declaration.
10161042
write_doc(out, "", doc);
10171043
}
1018-
write_rust_function_shim_decl(out, local_name, sig, indirect_call);
1044+
write_rust_function_shim_decl(out, local_name, sig, self_type, indirect_call);
10191045
if out.header {
10201046
writeln!(out, ";");
10211047
return;

macro/src/expand.rs

+71-13
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

@@ -797,6 +830,7 @@ fn expand_function_pointer_trampoline(
797830
let body_span = efn.semi_token.span;
798831
let shim = expand_rust_function_shim_impl(
799832
sig,
833+
&efn.self_type,
800834
types,
801835
&r_trampoline,
802836
local_name,
@@ -940,18 +974,33 @@ fn expand_forbid(impls: TokenStream) -> TokenStream {
940974

941975
fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
942976
let link_name = mangle::extern_fn(efn, types);
943-
let local_name = match &efn.receiver {
944-
None => format_ident!("__{}", efn.name.rust),
945-
Some(receiver) => format_ident!("__{}__{}", receiver.ty.rust, efn.name.rust),
977+
let local_name = match (&efn.receiver, &efn.self_type) {
978+
(None, None) => format_ident!("__{}", efn.name.rust),
979+
(Some(receiver), None) => format_ident!("__{}__{}", receiver.ty.rust, efn.name.rust),
980+
(None, Some(self_type)) => format_ident!(
981+
"__{}__{}",
982+
types.resolve(self_type).name.rust,
983+
efn.name.rust
984+
),
985+
_ => unreachable!("receiver and self_type are mutually exclusive"),
946986
};
947-
let prevent_unwind_label = match &efn.receiver {
948-
None => format!("::{}", efn.name.rust),
949-
Some(receiver) => format!("::{}::{}", receiver.ty.rust, efn.name.rust),
987+
let prevent_unwind_label = match (&efn.receiver, &efn.self_type) {
988+
(None, None) => format!("::{}", efn.name.rust),
989+
(Some(receiver), None) => format!("::{}::{}", receiver.ty.rust, efn.name.rust),
990+
(None, Some(self_type)) => {
991+
format!(
992+
"::{}::{}",
993+
types.resolve(self_type).name.rust,
994+
efn.name.rust
995+
)
996+
}
997+
_ => unreachable!("receiver and self_type are mutually exclusive"),
950998
};
951999
let invoke = Some(&efn.name.rust);
9521000
let body_span = efn.semi_token.span;
9531001
expand_rust_function_shim_impl(
9541002
efn,
1003+
&efn.self_type,
9551004
types,
9561005
&link_name,
9571006
local_name,
@@ -965,6 +1014,7 @@ fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
9651014

9661015
fn expand_rust_function_shim_impl(
9671016
sig: &Signature,
1017+
self_type: &Option<Ident>,
9681018
types: &Types,
9691019
link_name: &Symbol,
9701020
local_name: Ident,
@@ -1057,7 +1107,8 @@ fn expand_rust_function_shim_impl(
10571107
});
10581108
let vars: Vec<_> = receiver_var.into_iter().chain(arg_vars).collect();
10591109

1060-
let wrap_super = invoke.map(|invoke| expand_rust_function_shim_super(sig, &local_name, invoke));
1110+
let wrap_super = invoke
1111+
.map(|invoke| expand_rust_function_shim_super(sig, self_type, types, &local_name, invoke));
10611112

10621113
let mut requires_closure;
10631114
let mut call = match invoke {
@@ -1182,6 +1233,8 @@ fn expand_rust_function_shim_impl(
11821233
// accurate unsafety declaration and no problematic elided lifetimes.
11831234
fn expand_rust_function_shim_super(
11841235
sig: &Signature,
1236+
self_type: &Option<Ident>,
1237+
types: &Types,
11851238
local_name: &Ident,
11861239
invoke: &Ident,
11871240
) -> TokenStream {
@@ -1222,12 +1275,17 @@ fn expand_rust_function_shim_super(
12221275
let vars = receiver_var.iter().chain(arg_vars);
12231276

12241277
let span = invoke.span();
1225-
let call = match &sig.receiver {
1226-
None => quote_spanned!(span=> super::#invoke),
1227-
Some(receiver) => {
1278+
let call = match (&sig.receiver, &self_type) {
1279+
(None, None) => quote_spanned!(span=> super::#invoke),
1280+
(Some(receiver), None) => {
12281281
let receiver_type = &receiver.ty.rust;
12291282
quote_spanned!(span=> #receiver_type::#invoke)
12301283
}
1284+
(None, Some(self_type)) => {
1285+
let self_type = &types.resolve(self_type).name.rust;
1286+
quote_spanned!(span=> #self_type::#invoke)
1287+
}
1288+
_ => unreachable!("receiver and self_type are mutually exclusive"),
12311289
};
12321290

12331291
let mut body = quote_spanned!(span=> #call(#(#vars,)*));

syntax/attrs.rs

+14
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

+13
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

+13-3
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!("receiver and self_type are mutually exclusive"),
99109
}
100110
}
101111

syntax/mod.rs

+1
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

+3
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

0 commit comments

Comments
 (0)