From 29f61a108eb0e1bc37dc085998b56075d9faebe0 Mon Sep 17 00:00:00 2001 From: Quentin Weber Date: Wed, 7 Jan 2026 15:30:43 +0100 Subject: [PATCH 1/5] rafactor: extract helper functions (no logic change) --- gen/src/write.rs | 62 +++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/gen/src/write.rs b/gen/src/write.rs index ef5d0304b..c8d0938cb 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -899,6 +899,22 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { Lang::Rust => unreachable!(), } writeln!(out, " {{"); + write_default_function_body(out, efn, indirect_return); + writeln!(out, "}}"); + for arg in &efn.args { + if let Type::Fn(f) = &arg.ty { + let var = &arg.name; + write_function_pointer_trampoline(out, efn, var, f); + } + } + out.end_block(Block::ExternC); +} + +fn write_default_function_body<'a>( + out: &mut OutFile<'a>, + efn: &'a ExternFn, + indirect_return: bool, +) { write!(out, " "); write_return_type(out, &efn.ret); match efn.receiver() { @@ -968,6 +984,27 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { None => write!(out, "{}$(", efn.name.rust), Some(_) => write!(out, "(self.*{}$)(", efn.name.rust), } + write_args_punctuated(out, efn); + write!(out, ")"); + match &efn.ret { + Some(Type::RustBox(_)) => write!(out, ".into_raw()"), + Some(Type::UniquePtr(_)) => write!(out, ".release()"), + Some(Type::Str(_) | Type::SliceRef(_)) if !indirect_return => write!(out, ")"), + _ => {} + } + if indirect_return { + write!(out, ")"); + } + writeln!(out, ";"); + if efn.throws { + writeln!(out, " throw$.ptr = nullptr;"); + writeln!(out, " }},"); + writeln!(out, " ::rust::detail::Fail(throw$));"); + writeln!(out, " return throw$;"); + } +} + +fn write_args_punctuated<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { for (i, arg) in efn.args.iter().enumerate() { if i > 0 { write!(out, ", "); @@ -996,31 +1033,6 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { write!(out, "{}", arg.name.cxx); } } - write!(out, ")"); - match &efn.ret { - Some(Type::RustBox(_)) => write!(out, ".into_raw()"), - Some(Type::UniquePtr(_)) => write!(out, ".release()"), - Some(Type::Str(_) | Type::SliceRef(_)) if !indirect_return => write!(out, ")"), - _ => {} - } - if indirect_return { - write!(out, ")"); - } - writeln!(out, ";"); - if efn.throws { - writeln!(out, " throw$.ptr = nullptr;"); - writeln!(out, " }},"); - writeln!(out, " ::rust::detail::Fail(throw$));"); - writeln!(out, " return throw$;"); - } - writeln!(out, "}}"); - for arg in &efn.args { - if let Type::Fn(f) = &arg.ty { - let var = &arg.name; - write_function_pointer_trampoline(out, efn, var, f); - } - } - out.end_block(Block::ExternC); } fn write_function_pointer_trampoline(out: &mut OutFile, efn: &ExternFn, var: &Pair, f: &Signature) { From a290593fa4c8c4d7dbc46406a24c779dc9090574 Mon Sep 17 00:00:00 2001 From: Quentin Weber Date: Wed, 7 Jan 2026 15:39:02 +0100 Subject: [PATCH 2/5] add #[constructor] attribute for ffi functions --- demo/include/blobstore.h | 2 -- demo/src/blobstore.cc | 4 ---- demo/src/main.rs | 1 + gen/src/write.rs | 23 ++++++++++++++++++++++- syntax/attrs.rs | 6 ++++++ syntax/mod.rs | 2 ++ syntax/parse.rs | 3 +++ 7 files changed, 34 insertions(+), 7 deletions(-) diff --git a/demo/include/blobstore.h b/demo/include/blobstore.h index d89583aa9..002f678ba 100644 --- a/demo/include/blobstore.h +++ b/demo/include/blobstore.h @@ -20,7 +20,5 @@ class BlobstoreClient { std::shared_ptr impl; }; -std::unique_ptr new_blobstore_client(); - } // namespace blobstore } // namespace org diff --git a/demo/src/blobstore.cc b/demo/src/blobstore.cc index 7cf40dfe3..9f9136978 100644 --- a/demo/src/blobstore.cc +++ b/demo/src/blobstore.cc @@ -63,9 +63,5 @@ BlobMetadata BlobstoreClient::metadata(uint64_t blobid) const { return metadata; } -std::unique_ptr new_blobstore_client() { - return std::make_unique(); -} - } // namespace blobstore } // namespace org diff --git a/demo/src/main.rs b/demo/src/main.rs index e43f15621..049ab7649 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -19,6 +19,7 @@ mod ffi { type BlobstoreClient; + #[constructor] fn new_blobstore_client() -> UniquePtr; fn put(&self, parts: &mut MultiBuf) -> u64; fn tag(&self, blobid: u64, tag: &str); diff --git a/gen/src/write.rs b/gen/src/write.rs index c8d0938cb..93e8fdaf2 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -18,6 +18,7 @@ use crate::syntax::{ }; pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec { + println!("genn"); let mut out_file = OutFile::new(header, opt, types); let out = &mut out_file; @@ -899,7 +900,12 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { Lang::Rust => unreachable!(), } writeln!(out, " {{"); - write_default_function_body(out, efn, indirect_return); + + if efn.is_constructor { + write_constructor_function_body(out, efn); + } else { + write_default_function_body(out, efn, indirect_return); + } writeln!(out, "}}"); for arg in &efn.args { if let Type::Fn(f) = &arg.ty { @@ -910,6 +916,21 @@ fn write_cxx_function_shim<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { out.end_block(Block::ExternC); } +fn write_constructor_function_body<'a>(out: &mut OutFile<'a>, efn: &'a ExternFn) { + write!(out, " return new "); + if let Some(Type::UniquePtr(ty)) = &efn.sig.ret { + write_type_to_generic_writer(out, &ty.inner, out.types) + } else { + panic!( + "Constructor: '{}' must return a UniquePtr", + efn.name.rust + ); + } + write!(out, "("); + write_args_punctuated(out, efn); + writeln!(out, ");"); +} + fn write_default_function_body<'a>( out: &mut OutFile<'a>, efn: &'a ExternFn, diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 7a83ba73e..282928e20 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -36,6 +36,7 @@ pub(crate) struct Parser<'a> { pub cxx_name: Option<&'a mut Option>, pub rust_name: Option<&'a mut Option>, pub self_type: Option<&'a mut Option>, + pub is_constructor: Option<&'a mut bool>, pub ignore_unrecognized: bool, // Suppress clippy needless_update lint ("struct update has no effect, all @@ -192,6 +193,11 @@ pub(crate) fn parse(cx: &mut Errors, attrs: Vec, mut parser: Parser) other_attrs.lint.push(attr); continue; } + } else if attr_path.is_ident("constructor") { + if let Some(constructor) = &mut parser.is_constructor { + **constructor = true; + continue; + } } if !parser.ignore_unrecognized { cx.error(attr, "unsupported attribute"); diff --git a/syntax/mod.rs b/syntax/mod.rs index 873606046..fd37f21b4 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -150,6 +150,8 @@ pub(crate) struct ExternFn { pub attrs: OtherAttrs, #[cfg_attr(not(proc_macro), expect(dead_code))] pub visibility: Token![pub], + #[cfg_attr(proc_macro, expect(dead_code))] + pub is_constructor: bool, pub name: Pair, pub sig: Signature, pub semi_token: Token![;], diff --git a/syntax/parse.rs b/syntax/parse.rs index cdf0a8536..7a71cf309 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -547,6 +547,7 @@ fn parse_extern_fn( let mut rust_name = None; let mut self_type = None; let mut attrs = attrs.clone(); + let mut is_constructor = false; attrs.extend(attrs::parse( cx, mem::take(&mut foreign_fn.attrs), @@ -557,6 +558,7 @@ fn parse_extern_fn( cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), self_type: Some(&mut self_type), + is_constructor: Some(&mut is_constructor), ..Default::default() }, )); @@ -725,6 +727,7 @@ fn parse_extern_fn( paren_token, throws_tokens, }, + is_constructor, semi_token, trusted, })) From dabf8de741b61287475965dc64675e8d85b09841 Mon Sep 17 00:00:00 2001 From: Quentin Weber Date: Wed, 7 Jan 2026 15:41:11 +0100 Subject: [PATCH 3/5] add tests for #[constructor] attribute --- tests/ffi/lib.rs | 7 +++++++ tests/ffi/tests.cc | 2 ++ tests/ffi/tests.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 12808b591..d5684a24a 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -112,6 +112,13 @@ pub mod ffi { unsafe extern "C++" { type C; + #[constructor] + fn new_c_0_args() -> UniquePtr; + #[constructor] + fn new_c_1_args(n: usize) -> UniquePtr; + #[constructor] + fn new_c_2_args(n: usize, m: usize) -> UniquePtr; + fn c_return_primitive() -> usize; fn c_return_shared() -> Shared; fn c_return_box() -> Box; diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index dc5ce5de3..51339b3b5 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -27,7 +27,9 @@ static_assert(4 == alignof(OveralignedStruct), "expected 4 byte alignment"); static constexpr char SLICE_DATA[] = "2020"; +C::C() : n(0) {} C::C(size_t n) : n(n) {} +C::C(size_t n, size_t m) : n(n) {} size_t C::get() const { return this->n; } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index ab6a868bd..8a1d6dbc6 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -54,7 +54,9 @@ enum class Enum : uint16_t; class C { public: + C(); C(size_t n); + C(size_t n, size_t m); size_t get() const; size_t set(size_t n); size_t get2() const; From 717f2d087b185a47bae3299cb28e43385588df62 Mon Sep 17 00:00:00 2001 From: Quentin Weber Date: Wed, 7 Jan 2026 15:43:42 +0100 Subject: [PATCH 4/5] remove debug statement --- gen/src/write.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/gen/src/write.rs b/gen/src/write.rs index 93e8fdaf2..f7cb38c54 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -18,7 +18,6 @@ use crate::syntax::{ }; pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec { - println!("genn"); let mut out_file = OutFile::new(header, opt, types); let out = &mut out_file; From d5a808e520f5206d0deee730684a97e6823467c6 Mon Sep 17 00:00:00 2001 From: Quentin Weber Date: Wed, 7 Jan 2026 16:03:47 +0100 Subject: [PATCH 5/5] fix unnused variable warning --- tests/ffi/tests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 51339b3b5..8208b9159 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -29,7 +29,7 @@ static constexpr char SLICE_DATA[] = "2020"; C::C() : n(0) {} C::C(size_t n) : n(n) {} -C::C(size_t n, size_t m) : n(n) {} +C::C(size_t n, size_t m) : n(n + m) {} size_t C::get() const { return this->n; }