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 7150b2e
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 26 deletions.
43 changes: 33 additions & 10 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,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();
Expand Down Expand Up @@ -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();
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 Expand Up @@ -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) {
Expand Down Expand Up @@ -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<Ident>,
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, ", ");
Expand Down Expand Up @@ -1003,19 +1025,20 @@ fn write_rust_function_shim_impl(
out: &mut OutFile,
local_name: &str,
sig: &Signature,
self_type: &Option<Ident>,
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;
}
if sig.receiver.is_none() {
// 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;
Expand Down
84 changes: 71 additions & 13 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 All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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<Ident>,
types: &Types,
link_name: &Symbol,
local_name: Ident,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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<Ident>,
types: &Types,
local_name: &Ident,
invoke: &Ident,
) -> TokenStream {
Expand Down Expand Up @@ -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,)*));
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
Loading

0 comments on commit 7150b2e

Please sign in to comment.