Skip to content

Support static member functions & c++ constructor of shared type #1430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 180 additions & 83 deletions gen/src/write.rs

Large diffs are not rendered by default.

129 changes: 105 additions & 24 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,13 @@ fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream {
};
let mut outparam = None;
if indirect_return(efn, types) {
let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true);
outparam = Some(quote!(__return: *mut #ret));
if efn.sig.constructor {
let self_type = expand_extern_type(&types.resolve(efn.self_type.as_ref().unwrap()).into(), types, true);
outparam = Some(quote!(__return: *mut #self_type));
} else {
let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true);
outparam = Some(quote!(__return: *mut #ret));
}
}
let link_name = mangle::extern_fn(efn, types);
let local_name = format_ident!("__{}", efn.name.rust);
Expand Down Expand Up @@ -523,6 +528,8 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
None => quote!(()),
};
quote!(-> ::cxx::core::result::Result<#ok, ::cxx::Exception>)
} else if efn.sig.constructor {
quote!(-> Self)
} else {
expand_return_type(&efn.ret)
};
Expand Down Expand Up @@ -626,10 +633,17 @@ fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream {
let local_name = format_ident!("__{}", efn.name.rust);
let span = efn.semi_token.span;
let call = if indirect_return {
let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true);
setup.extend(quote_spanned! {span=>
let mut __return = ::cxx::core::mem::MaybeUninit::<#ret>::uninit();
});
if efn.sig.constructor {
let self_type = expand_extern_type(&types.resolve(efn.self_type.as_ref().unwrap()).into(), types, true);
setup.extend(quote_spanned! {span=>
let mut __return = ::cxx::core::mem::MaybeUninit::<#self_type>::uninit();
});
} else {
let ret = expand_extern_type(efn.ret.as_ref().unwrap(), types, true);
setup.extend(quote_spanned! {span=>
let mut __return = ::cxx::core::mem::MaybeUninit::<#ret>::uninit();
});
}
setup.extend(if efn.throws {
quote_spanned! {span=>
#local_name(#(#vars,)* __return.as_mut_ptr()).exception()?;
Expand Down Expand Up @@ -741,15 +755,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 +795,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 +844,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 +988,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 +1028,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 +1121,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 @@ -1126,8 +1191,13 @@ fn expand_rust_function_shim_impl(
let mut outparam = None;
let indirect_return = indirect_return(sig, types);
if indirect_return {
let ret = expand_extern_type(sig.ret.as_ref().unwrap(), types, false);
outparam = Some(quote_spanned!(span=> __return: *mut #ret,));
if sig.constructor {
let self_type = expand_extern_type(&types.resolve(self_type.as_ref().unwrap()).into(), types, false);
outparam = Some(quote_spanned!(span=> __return: *mut #self_type,));
} else {
let ret = expand_extern_type(sig.ret.as_ref().unwrap(), types, false);
outparam = Some(quote_spanned!(span=> __return: *mut #ret,));
}
}
if sig.throws {
let out = match sig.ret {
Expand Down Expand Up @@ -1182,6 +1252,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 @@ -1214,6 +1286,8 @@ fn expand_rust_function_shim_super(
quote_spanned!(rangle.span=> ::cxx::core::fmt::Display>)
};
quote!(-> #result_begin #result_end)
} else if sig.constructor {
expand_return_type(&Some(types.resolve(self_type.as_ref().unwrap()).into()))
} else {
expand_return_type(&sig.ret)
};
Expand All @@ -1222,12 +1296,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 Expand Up @@ -1854,9 +1933,11 @@ fn expand_return_type(ret: &Option<Type>) -> TokenStream {
}

fn indirect_return(sig: &Signature, types: &Types) -> bool {
sig.ret
.as_ref()
.is_some_and(|ret| sig.throws || types.needs_indirect_abi(ret))
sig.constructor
|| sig
.ret
.as_ref()
.is_some_and(|ret| sig.throws || types.needs_indirect_abi(ret))
}

fn expand_extern_type(ty: &Type, types: &Types, proper: bool) -> TokenStream {
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
17 changes: 17 additions & 0 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,23 @@ 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");
}

if efn.sig.constructor && !cx.types.structs.contains_key(self_type) {
cx.error(efn, "constructor is only allowed for shared structure");
}
}
}

fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
Expand Down
3 changes: 3 additions & 0 deletions syntax/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ impl PartialEq for Signature {
throws,
paren_token: _,
throws_tokens: _,
constructor: _,
} = self;
let Signature {
asyncness: asyncness2,
Expand All @@ -331,6 +332,7 @@ impl PartialEq for Signature {
throws: throws2,
paren_token: _,
throws_tokens: _,
constructor: _,
} = other;
asyncness.is_some() == asyncness2.is_some()
&& unsafety.is_some() == unsafety2.is_some()
Expand Down Expand Up @@ -375,6 +377,7 @@ impl Hash for Signature {
throws,
paren_token: _,
throws_tokens: _,
constructor: _,
} = self;
asyncness.is_some().hash(state);
unsafety.is_some().hash(state);
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!("receiver and self_type are mutually exclusive"),
}
}

Expand Down
2 changes: 2 additions & 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 Expand Up @@ -217,6 +218,7 @@ pub(crate) struct Signature {
pub throws: bool,
pub paren_token: Paren,
pub throws_tokens: Option<(kw::Result, Token![<], Token![>])>,
pub constructor: bool,
}

pub(crate) struct Var {
Expand Down
Loading