Translate C++ constructors with a single T argument into `impl From<T>`.
PiperOrigin-RevId: 419711182
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index 8466b29..56fafe2 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -99,6 +99,13 @@
pub fn is_void(&self) -> bool {
self.name.as_deref() == Some("void")
}
+
+ pub fn is_const_ref_to(&self, record: &Record) -> bool {
+ self.name.as_deref() == Some("&")
+ && self.type_args.first().map_or(false, |type_arg| {
+ type_arg.is_const && type_arg.name.is_none() && type_arg.decl_id == Some(record.id)
+ })
+ }
}
pub trait TypeWithDeclId {
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index c87a1f8..780d65e 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -565,6 +565,23 @@
}
#[test]
+fn test_copy_constructor_properties() {
+ let ir = ir_from_cc(
+ "struct SomeStruct {
+ SomeStruct() = delete;
+ SomeStruct(const SomeStruct&);
+ SomeStruct(const SomeStruct&&) = delete;
+ ~SomeStruct() = delete;
+ };",
+ )
+ .unwrap();
+ let func = ir.functions().next().unwrap();
+ let rec = ir.records().next().unwrap();
+ assert_eq!(2, func.params.len());
+ assert!(func.params[1].type_.cc_type.is_const_ref_to(rec));
+}
+
+#[test]
fn test_unsupported_items_are_emitted() -> Result<()> {
// We will have to rewrite this test to use something else that is unsupported
// once we start importing structs from namespaces.
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 91f8dfa..0bb047c 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -245,30 +245,57 @@
}
UnqualifiedIdentifier::Constructor => {
+ // TODO(lukasza): Also allow mapping constructors to inherent static methods
+ // (e.g. if named via a bindings-generator-recognized C++
+ // attribute).
let record = record.ok_or_else(|| anyhow!("Constructors must be member functions."))?;
if !record.is_trivial_abi {
return empty_result;
}
- let type_name = make_ident(&record.identifier.identifier);
- match func.params.len() {
+ let (trait_name, method_name) = match func.params.len() {
0 => bail!("Constructor should have at least 1 parameter (__this)"),
- 1 => quote! {
- #doc_comment
- impl Default for #type_name {
- #[inline(always)]
- fn default() -> Self {
- let mut tmp = std::mem::MaybeUninit::<Self>::uninit();
- unsafe {
- crate::detail::#thunk_ident(tmp.as_mut_ptr());
- tmp.assume_init()
- }
+ 1 => (quote! { Default }, quote! { default }),
+ 2 => {
+ let param_type = &func.params[1].type_;
+ if param_type.cc_type.is_const_ref_to(record) {
+ // TODO(b/200066396): Map copy constructor to `impl Clone`.
+ // TODO(lukasza): Do something smart with move constructor.
+ return empty_result;
+ } else {
+ let quoted_param_type = ¶m_types[1];
+ (quote! { From< #quoted_param_type > }, quote! { from })
+ }
+ }
+ _ => {
+ // TODO(b/200066396): Map other constructors to something.
+ return empty_result;
+ }
+ };
+ // Skip the first parameter in the public function definition. C++ constructors
+ // (and the thunk) take `__this` as the first parameter, but Rust
+ // translation returns a `Self` instead (in Clone, Default, and From
+ // traits, as well as in static methods).
+ let (param_idents, param_types) = (
+ // TODO(lukasza): We should also trim `generic_params` so that
+ // 1) the generated Rust code is easier to read and 2) to avoid
+ // running into unused lifetime parameters warning (see also
+ // https://github.com/rust-lang/rust/issues/41960).
+ param_idents.iter().skip(1).collect_vec(),
+ param_types.iter().skip(1).collect_vec(),
+ );
+
+ let struct_name = make_ident(&record.identifier.identifier);
+ quote! {
+ #doc_comment
+ impl #trait_name for #struct_name {
+ #[inline(always)]
+ fn #method_name #generic_params( #( #param_idents: #param_types ),* ) -> Self {
+ let mut tmp = std::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ crate::detail::#thunk_ident(tmp.as_mut_ptr() #( , #param_idents )* );
+ tmp.assume_init()
}
}
- },
- _ => {
- // TODO(b/208946210): Map some of these constructors to the From trait.
- // TODO(b/200066396): Map other constructors (to the Clone trait?).
- return empty_result;
}
}
}
diff --git a/rs_bindings_from_cc/test/golden/doc_comment.h b/rs_bindings_from_cc/test/golden/doc_comment.h
index 5172931..11b655c 100644
--- a/rs_bindings_from_cc/test/golden/doc_comment.h
+++ b/rs_bindings_from_cc/test/golden/doc_comment.h
@@ -9,13 +9,18 @@
///
/// * with three slashes
struct DocCommentSlashes final {
- /// The default constructor
+ /// The default constructor which will get translated into
+ /// `impl Default for DocCommentSlashes`.
DocCommentSlashes();
- /// A static method
+ /// A conversion constructor which will get translated into
+ /// `impl From<int> for DocCommentSlashes`.
+ explicit DocCommentSlashes(int);
+
+ /// A static method.
static int static_method();
- /// A field
+ /// A field.
int i;
};
diff --git a/rs_bindings_from_cc/test/golden/doc_comment_rs_api.rs b/rs_bindings_from_cc/test/golden/doc_comment_rs_api.rs
index f22ca57..25c7f67 100644
--- a/rs_bindings_from_cc/test/golden/doc_comment_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/doc_comment_rs_api.rs
@@ -13,7 +13,7 @@
#[derive(Clone, Copy)]
#[repr(C)]
pub struct DocCommentSlashes {
- /// A field
+ /// A field.
pub i: i32,
}
@@ -21,7 +21,8 @@
// Error while generating bindings for item 'DocCommentSlashes::DocCommentSlashes':
// Nested classes are not supported yet
-/// The default constructor
+/// The default constructor which will get translated into
+/// `impl Default for DocCommentSlashes`.
impl Default for DocCommentSlashes {
#[inline(always)]
fn default() -> Self {
@@ -33,8 +34,21 @@
}
}
+/// A conversion constructor which will get translated into
+/// `impl From<int> for DocCommentSlashes`.
+impl From<i32> for DocCommentSlashes {
+ #[inline(always)]
+ fn from(__param_0: i32) -> Self {
+ let mut tmp = std::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ crate::detail::__rust_thunk___ZN17DocCommentSlashesC1Ei(tmp.as_mut_ptr(), __param_0);
+ tmp.assume_init()
+ }
+ }
+}
+
impl DocCommentSlashes {
- /// A static method
+ /// A static method.
#[inline(always)]
pub fn static_method() -> i32 {
unsafe { crate::detail::__rust_thunk___ZN17DocCommentSlashes13static_methodEv() }
@@ -59,7 +73,7 @@
pub i: i32,
}
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=21
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=26
// Error while generating bindings for item 'DocCommentBang::DocCommentBang':
// Nested classes are not supported yet
@@ -74,11 +88,11 @@
}
}
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=21
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=26
// Error while generating bindings for item 'DocCommentBang::DocCommentBang':
// Parameter type 'struct DocCommentBang &&' is not supported
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=21
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=26
// Error while generating bindings for item 'DocCommentBang::operator=':
// Parameter type 'struct DocCommentBang &&' is not supported
@@ -92,7 +106,7 @@
pub i: i32,
}
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=29
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=34
// Error while generating bindings for item 'MultilineCommentTwoStars::MultilineCommentTwoStars':
// Nested classes are not supported yet
@@ -107,11 +121,11 @@
}
}
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=29
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=34
// Error while generating bindings for item 'MultilineCommentTwoStars::MultilineCommentTwoStars':
// Parameter type 'struct MultilineCommentTwoStars &&' is not supported
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=29
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=34
// Error while generating bindings for item 'MultilineCommentTwoStars::operator=':
// Parameter type 'struct MultilineCommentTwoStars &&' is not supported
@@ -125,7 +139,7 @@
pub i: i32,
}
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=37
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=42
// Error while generating bindings for item 'LineComment::LineComment':
// Nested classes are not supported yet
@@ -140,11 +154,11 @@
}
}
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=37
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=42
// Error while generating bindings for item 'LineComment::LineComment':
// Parameter type 'struct LineComment &&' is not supported
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=37
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=42
// Error while generating bindings for item 'LineComment::operator=':
// Parameter type 'struct LineComment &&' is not supported
@@ -158,7 +172,7 @@
pub i: i32,
}
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=45
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=50
// Error while generating bindings for item 'MultilineOneStar::MultilineOneStar':
// Nested classes are not supported yet
@@ -173,11 +187,11 @@
}
}
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=45
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=50
// Error while generating bindings for item 'MultilineOneStar::MultilineOneStar':
// Parameter type 'struct MultilineOneStar &&' is not supported
-// rs_bindings_from_cc/test/golden/doc_comment.h;l=45
+// rs_bindings_from_cc/test/golden/doc_comment.h;l=50
// Error while generating bindings for item 'MultilineOneStar::operator=':
// Parameter type 'struct MultilineOneStar &&' is not supported
@@ -194,6 +208,11 @@
extern "C" {
#[link_name = "_ZN17DocCommentSlashesC1Ev"]
pub(crate) fn __rust_thunk___ZN17DocCommentSlashesC1Ev(__this: *mut DocCommentSlashes);
+ #[link_name = "_ZN17DocCommentSlashesC1Ei"]
+ pub(crate) fn __rust_thunk___ZN17DocCommentSlashesC1Ei(
+ __this: *mut DocCommentSlashes,
+ __param_0: i32,
+ );
#[link_name = "_ZN17DocCommentSlashes13static_methodEv"]
pub(crate) fn __rust_thunk___ZN17DocCommentSlashes13static_methodEv() -> i32;
pub(crate) fn __rust_thunk___ZN14DocCommentBangC1Ev(__this: *mut DocCommentBang);
diff --git a/rs_bindings_from_cc/test/golden/elided_lifetimes_rs_api.rs b/rs_bindings_from_cc/test/golden/elided_lifetimes_rs_api.rs
index a2446fe..b961447 100644
--- a/rs_bindings_from_cc/test/golden/elided_lifetimes_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/elided_lifetimes_rs_api.rs
@@ -32,7 +32,7 @@
impl Default for S {
#[inline(always)]
- fn default() -> Self {
+ fn default<'a>() -> Self {
let mut tmp = std::mem::MaybeUninit::<Self>::uninit();
unsafe {
crate::detail::__rust_thunk___ZN1SC1Ev(tmp.as_mut_ptr());
diff --git a/rs_bindings_from_cc/test/struct/constructors/constructors.cc b/rs_bindings_from_cc/test/struct/constructors/constructors.cc
index 37f5b09..2f6e730 100644
--- a/rs_bindings_from_cc/test/struct/constructors/constructors.cc
+++ b/rs_bindings_from_cc/test/struct/constructors/constructors.cc
@@ -4,9 +4,12 @@
#include "rs_bindings_from_cc/test/struct/constructors/constructors.h"
-StructWithUserProvidedConstructor::StructWithUserProvidedConstructor()
+StructWithUserProvidedConstructors::StructWithUserProvidedConstructors()
: int_field(42) {}
+StructWithUserProvidedConstructors::StructWithUserProvidedConstructors(int i)
+ : int_field(i) {}
+
StructWithPrivateConstructor::StructWithPrivateConstructor() : int_field(42) {}
NonTrivialStructWithConstructors::NonTrivialStructWithConstructors()
diff --git a/rs_bindings_from_cc/test/struct/constructors/constructors.h b/rs_bindings_from_cc/test/struct/constructors/constructors.h
index ec26972..74ab11d 100644
--- a/rs_bindings_from_cc/test/struct/constructors/constructors.h
+++ b/rs_bindings_from_cc/test/struct/constructors/constructors.h
@@ -5,14 +5,20 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_CONSTRUCTORS_CONSTRUCTORS_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_CONSTRUCTORS_CONSTRUCTORS_H_
-struct StructWithUserProvidedConstructor final {
- StructWithUserProvidedConstructor();
- // TODO(lukasza): Add a copy constructor (to be mapped to Clone?).
- // TODO(b/208946210): Add a "conversion" constructor (to be mapped to From).
+struct StructWithUserProvidedConstructors final {
+ // `impl Default for StructWithUserProvidedConstructors { ... }`.
+ StructWithUserProvidedConstructors();
+
+ // TODO(lukasza): Add a copy constructor (to be mapped to Clone?)
+
+ // `impl From<int> for StructWithUserProvidedConstructors { ... }`.
+ explicit StructWithUserProvidedConstructors(int);
int int_field;
};
+// TODO(lukasza): StructWithInlinedConstructors (to force thunk generation).
+
struct StructWithDeletedConstructor final {
StructWithDeletedConstructor() = delete;
diff --git a/rs_bindings_from_cc/test/struct/constructors/test.rs b/rs_bindings_from_cc/test/struct/constructors/test.rs
index b4b981c..bcc572a 100644
--- a/rs_bindings_from_cc/test/struct/constructors/test.rs
+++ b/rs_bindings_from_cc/test/struct/constructors/test.rs
@@ -11,10 +11,14 @@
#[test]
fn test_user_provided_constructors() {
- assert_impl_all!(StructWithUserProvidedConstructor: Default);
+ assert_impl_all!(StructWithUserProvidedConstructors: From<i32>);
+ assert_impl_all!(StructWithUserProvidedConstructors: Default);
- let s: StructWithUserProvidedConstructor = Default::default();
+ let s: StructWithUserProvidedConstructors = Default::default();
assert_eq!(42, s.int_field);
+
+ let i: StructWithUserProvidedConstructors = 123.into();
+ assert_eq!(123, i.int_field);
}
#[test]