Major change: additional requirements for rust-reference-safety/Unpin, which now includes "tail padding is safe to write to".
Before, it was assumed a type could be put in a `&mut` if the type was trivially relocatable. However, some types are trivially relocatable, but nonetheless not actually safe to use in Rust via a mutable reference. They can be written to by `memcpy`, but **Rust will `memcpy` an incorrect number of bytes**. These types can still be handled by value, but all references Rust receives from C++ must be `Pin`.
In addition to ensuring types are trivially relocatable, to get a completely idiomatic interface in Rust, those types must also be final leaf types.
This is described more completely in the newly-added doc in google3/devtools/rust/cc_interop/g3doc/unpin.
---
Thanks:
* dmitrig@ for helping organize my thoughts on this, and suggesting `final` and giving up on `[[no_unique_address]]`, instead of my thought of e.g. "`data size == stride`")
* lexer@ and the rest of the Rust room for discussing this at length with me a month or so ago and giving me a lot of intellectual food to chew on.
PiperOrigin-RevId: 417820502
diff --git a/rs_bindings_from_cc/ast_visitor.cc b/rs_bindings_from_cc/ast_visitor.cc
index 42d3954..7c2cb8b 100644
--- a/rs_bindings_from_cc/ast_visitor.cc
+++ b/rs_bindings_from_cc/ast_visitor.cc
@@ -298,12 +298,14 @@
clang::AccessSpecifier default_access = clang::AS_public;
+ bool is_final = true;
if (auto* cxx_record_decl =
clang::dyn_cast<clang::CXXRecordDecl>(record_decl)) {
sema_.ForceDeclarationOfImplicitMembers(cxx_record_decl);
if (cxx_record_decl->isClass()) {
default_access = clang::AS_private;
}
+ is_final = cxx_record_decl->isEffectivelyFinal();
}
std::optional<std::vector<Field>> fields =
ImportFields(record_decl, default_access);
@@ -326,7 +328,8 @@
.copy_constructor = GetCopyCtorSpecialMemberFunc(*record_decl),
.move_constructor = GetMoveCtorSpecialMemberFunc(*record_decl),
.destructor = GetDestructorSpecialMemberFunc(*record_decl),
- .is_trivial_abi = record_decl->canPassInRegisters()});
+ .is_trivial_abi = record_decl->canPassInRegisters(),
+ .is_final = is_final});
return true;
}
diff --git a/rs_bindings_from_cc/ir.cc b/rs_bindings_from_cc/ir.cc
index 89099a5..b18afcf 100644
--- a/rs_bindings_from_cc/ir.cc
+++ b/rs_bindings_from_cc/ir.cc
@@ -284,6 +284,7 @@
record["move_constructor"] = move_constructor.ToJson();
record["destructor"] = destructor.ToJson();
record["is_trivial_abi"] = is_trivial_abi;
+ record["is_final"] = is_final;
nlohmann::json item;
item["Record"] = std::move(record);
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h
index 101a4ea..e9760b3 100644
--- a/rs_bindings_from_cc/ir.h
+++ b/rs_bindings_from_cc/ir.h
@@ -381,6 +381,12 @@
// * https://eel.is/c++draft/class.temporary#3
// * https://clang.llvm.org/docs/AttributeReference.html#trivial-abi
bool is_trivial_abi = false;
+
+ // Whether this type is effectively `final`, and cannot be inherited from.
+ //
+ // This can happen because it was explicitly marked final, or because a core
+ // function like the destructor was marked final.
+ bool is_final = false;
};
inline std::ostream& operator<<(std::ostream& o, const Record& r) {
diff --git a/rs_bindings_from_cc/ir.rs b/rs_bindings_from_cc/ir.rs
index 6ac3bcb..553d778 100644
--- a/rs_bindings_from_cc/ir.rs
+++ b/rs_bindings_from_cc/ir.rs
@@ -279,12 +279,46 @@
pub move_constructor: SpecialMemberFunc,
pub destructor: SpecialMemberFunc,
pub is_trivial_abi: bool,
+ pub is_final: bool,
}
impl Record {
pub fn owning_crate_name(&self) -> Result<&str> {
self.owning_target.target_name()
}
+
+ /// Whether this type has Rust-like object semantics for mutating
+ /// assignment, and can be passed by mut reference as a result.
+ ///
+ /// If a type `T` is mut reference safe, it can be possed as a `&mut T`
+ /// safely. Otherwise, mutable references must use `Pin<&mut T>`.
+ ///
+ /// Conditions:
+ ///
+ /// 1. It is trivially relocatable, and thus can be passed by value and have
+ /// its memory directly mutated by Rust using memcpy-like
+ /// assignment/swap.
+ ///
+ /// 2. It cannot overlap with any other objects. In particular, it cannot be
+ /// inherited from, as inheritance allows for the tail padding to be
+ /// reused by other objects.
+ ///
+ /// (In future versions, we could also include types which are POD for
+ /// the purpose of layout, but this is less predictable to C++ users,
+ /// and ABI-specific.)
+ ///
+ /// We are assuming, for the moment, that no object is stored in a
+ /// `[[no_unique_address]]` variable. Much like packed structs and
+ /// the like, users of `[[no_unique_address]]` must be very careful
+ /// when passing mutable references to Rust.
+ ///
+ /// Described in more detail at: docs/unpin
+ ///
+ /// TODO(b/200067242): Actually force mut references to !is_unpin to be
+ /// Pin<&mut T>.
+ pub fn is_unpin(&self) -> bool {
+ self.is_trivial_abi && self.is_final
+ }
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)]
diff --git a/rs_bindings_from_cc/ir_test.cc b/rs_bindings_from_cc/ir_test.cc
index 95a9ba5..c2645e2 100644
--- a/rs_bindings_from_cc/ir_test.cc
+++ b/rs_bindings_from_cc/ir_test.cc
@@ -128,7 +128,8 @@
"definition": "Trivial",
"access": "Public"
},
- "is_trivial_abi": true
+ "is_trivial_abi": true,
+ "is_final": true
}}
]
})j");
@@ -175,7 +176,8 @@
.definition =
SpecialMemberFunc::Definition::kTrivial,
.access = kPublic},
- .is_trivial_abi = true}}};
+ .is_trivial_abi = true,
+ .is_final = true}}};
EXPECT_THAT(ir.ToJson(), EqualsJson(expected));
}
diff --git a/rs_bindings_from_cc/ir_testing.rs b/rs_bindings_from_cc/ir_testing.rs
index f296cee..657dea0 100644
--- a/rs_bindings_from_cc/ir_testing.rs
+++ b/rs_bindings_from_cc/ir_testing.rs
@@ -114,7 +114,7 @@
/// Creates a simple `Item::Record` with a given name.
pub fn ir_record(name: &str) -> Record {
- let ir = ir_from_cc("struct REPLACEME {};").unwrap();
+ let ir = ir_from_cc("struct REPLACEME final {};").unwrap();
for item in ir.take_items() {
if let Item::Record(mut record) = item {
record.identifier = ir_id(name);
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index d7c07a2..4bd8007 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -362,7 +362,7 @@
quote! {#[derive( #(#derives),* )]}
};
let unpin_impl;
- if record.is_trivial_abi {
+ if record.is_unpin() {
unpin_impl = quote! {};
} else {
// negative_impls are necessary for universal initialization due to Rust's
@@ -1017,6 +1017,15 @@
assert_eq!(generate_copy_derives(&record), &[""; 0]);
}
+ /// A type can be unsafe to pass in mut references from C++, but still
+ /// Clone+Copy when handled by value.
+ #[test]
+ fn test_copy_derives_not_is_mut_reference_safe() {
+ let mut record = ir_record("S");
+ record.is_final = false;
+ assert_eq!(generate_copy_derives(&record), &["Clone", "Copy"]);
+ }
+
#[test]
fn test_copy_derives_ctor_nonpublic() {
let mut record = ir_record("S");
@@ -1180,6 +1189,26 @@
Ok(())
}
+ /// A trivially relocatable final struct is safe to use in Rust as normal,
+ /// and is Unpin.
+ #[test]
+ fn test_no_negative_impl_unpin() -> Result<()> {
+ let ir = ir_from_cc("struct Trivial final {};")?;
+ let rs_api = generate_rs_api(&ir)?;
+ assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
+ Ok(())
+ }
+
+ /// A non-final struct, even if it's trivial, is not usable by mut
+ /// reference, and so is !Unpin.
+ #[test]
+ fn test_negative_impl_unpin_nonfinal() -> Result<()> {
+ let ir = ir_from_cc("struct Nonfinal {};")?;
+ let rs_api = generate_rs_api(&ir)?;
+ assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
+ Ok(())
+ }
+
/// At the least, a trivial type should have no drop impl if or until we add
/// empty drop impls.
#[test]
diff --git a/rs_bindings_from_cc/test/golden/comment.h b/rs_bindings_from_cc/test/golden/comment.h
index 19064a5..53df9ed 100644
--- a/rs_bindings_from_cc/test/golden/comment.h
+++ b/rs_bindings_from_cc/test/golden/comment.h
@@ -12,7 +12,7 @@
// a
/// Foo
-struct Foo {
+struct Foo final {
// Foo a
/// A field
@@ -38,13 +38,13 @@
}
/// Bar
-struct Bar {
+struct Bar final {
int i;
};
// d
-struct HasNoComments {
+struct HasNoComments final {
int i;
};
diff --git a/rs_bindings_from_cc/test/golden/doc_comment.h b/rs_bindings_from_cc/test/golden/doc_comment.h
index 1ffb1e0..5172931 100644
--- a/rs_bindings_from_cc/test/golden/doc_comment.h
+++ b/rs_bindings_from_cc/test/golden/doc_comment.h
@@ -8,7 +8,7 @@
/// Doc comment
///
/// * with three slashes
-struct DocCommentSlashes {
+struct DocCommentSlashes final {
/// The default constructor
DocCommentSlashes();
@@ -22,7 +22,7 @@
//! Doc comment
//!
//! * with slashes and bang
-struct DocCommentBang {
+struct DocCommentBang final {
//! A field
int i;
};
@@ -30,7 +30,7 @@
/** Multiline comment
* with two stars */
-struct MultilineCommentTwoStars {
+struct MultilineCommentTwoStars final {
/** A field */
int i;
};
@@ -38,7 +38,7 @@
// Line comment
//
// * with two slashes
-struct LineComment {
+struct LineComment final {
// A field
int i;
};
@@ -46,7 +46,7 @@
/* Multiline comment
* with one star */
-struct MultilineOneStar {
+struct MultilineOneStar final {
/* A field */
int i;
};
diff --git a/rs_bindings_from_cc/test/golden/elided_lifetimes.h b/rs_bindings_from_cc/test/golden/elided_lifetimes.h
index 19090ca..7e11f28 100644
--- a/rs_bindings_from_cc/test/golden/elided_lifetimes.h
+++ b/rs_bindings_from_cc/test/golden/elided_lifetimes.h
@@ -9,7 +9,7 @@
int& free_function(int& p1);
-struct S {
+struct S final {
int& method(int& p1, int& p2);
};
diff --git a/rs_bindings_from_cc/test/golden/item_order.h b/rs_bindings_from_cc/test/golden/item_order.h
index f8581ee..bd7cedc 100644
--- a/rs_bindings_from_cc/test/golden/item_order.h
+++ b/rs_bindings_from_cc/test/golden/item_order.h
@@ -5,13 +5,13 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_ITEM_ORDER_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_ITEM_ORDER_H_
-struct FirstStruct {
+struct FirstStruct final {
int field;
};
inline int first_func() { return 42; }
-struct SecondStruct {
+struct SecondStruct final {
int field;
};
diff --git a/rs_bindings_from_cc/test/golden/nontrivial_type.h b/rs_bindings_from_cc/test/golden/nontrivial_type.h
index 77cea87..945a90d 100644
--- a/rs_bindings_from_cc/test/golden/nontrivial_type.h
+++ b/rs_bindings_from_cc/test/golden/nontrivial_type.h
@@ -10,7 +10,7 @@
//
// This makes it nontrivial for calls (so not trivially relocatable), as well
// as specifically giving it a nontrivial move constructor and destructor.
-struct Nontrivial {
+struct Nontrivial final {
Nontrivial(Nontrivial&&);
~Nontrivial();
@@ -21,7 +21,7 @@
//
// This makes it nontrivial for calls (so not trivially relocatable), as well
// as specifically giving it a nontrivial move constructor and destructor.
-struct NontrivialInline {
+struct NontrivialInline final {
NontrivialInline(NontrivialInline&&) {}
~NontrivialInline() {}
@@ -33,7 +33,7 @@
// This changes how the destructor / drop impl work -- instead of calling
// the destructor for NontrivialMembers, it just calls the destructors for
// each field.
-struct NontrivialMembers {
+struct NontrivialMembers final {
Nontrivial nontrivial_member;
};
diff --git a/rs_bindings_from_cc/test/golden/private_members.h b/rs_bindings_from_cc/test/golden/private_members.h
index 0214345..2871c6b 100644
--- a/rs_bindings_from_cc/test/golden/private_members.h
+++ b/rs_bindings_from_cc/test/golden/private_members.h
@@ -5,7 +5,7 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_PRIVATE_MEMBERS_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_PRIVATE_MEMBERS_H_
-class SomeClass {
+class SomeClass final {
public:
void public_method();
static void public_static_method();
diff --git a/rs_bindings_from_cc/test/golden/static_methods.h b/rs_bindings_from_cc/test/golden/static_methods.h
index 00bfc03..c82a531 100644
--- a/rs_bindings_from_cc/test/golden/static_methods.h
+++ b/rs_bindings_from_cc/test/golden/static_methods.h
@@ -5,7 +5,7 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_STATIC_METHODS_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_STATIC_METHODS_H_
-class SomeClass {
+class SomeClass final {
public:
// Example of a factory method.
static SomeClass static_factory_method(int initial_value_of_field);
diff --git a/rs_bindings_from_cc/test/golden/trivial_type.h b/rs_bindings_from_cc/test/golden/trivial_type.h
index f6f3fa4..61c82a0 100644
--- a/rs_bindings_from_cc/test/golden/trivial_type.h
+++ b/rs_bindings_from_cc/test/golden/trivial_type.h
@@ -7,13 +7,13 @@
// Implicitly defined special member functions are trivial on a struct with
// only trivial members.
-struct Trivial {
+struct Trivial final {
int trivial_field;
};
// Defaulted special member functions are trivial on a struct with only trivial
// members.
-struct TrivialWithDefaulted {
+struct TrivialWithDefaulted final {
TrivialWithDefaulted() = default;
TrivialWithDefaulted(const TrivialWithDefaulted&) = default;
@@ -26,8 +26,18 @@
int trivial_field;
};
-void TakesByValue(Trivial trivial);
+// This struct is trivial, and therefore trivially relocatable etc., but still
+// not safe to pass by reference as it is not final.
+struct TrivialNonfinal {
+ int trivial_field;
+};
+void TakesByValue(Trivial trivial);
void TakesWithDefaultedByValue(TrivialWithDefaulted trivial);
+void TakesTrivialNonfinalByValue(TrivialNonfinal trivial);
+
+void TakesByReference(Trivial& trivial);
+void TakesWithDefaultedByReference(TrivialWithDefaulted& trivial);
+void TakesTrivialNonfinalByReference(TrivialNonfinal& trivial);
#endif // CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_TRIVIAL_TYPE_H_
diff --git a/rs_bindings_from_cc/test/golden/trivial_type_rs_api.rs b/rs_bindings_from_cc/test/golden/trivial_type_rs_api.rs
index f2f58bc..00c06cb 100644
--- a/rs_bindings_from_cc/test/golden/trivial_type_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/trivial_type_rs_api.rs
@@ -3,7 +3,7 @@
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-#![feature(const_ptr_offset_from, custom_inner_attributes)]
+#![feature(const_ptr_offset_from, custom_inner_attributes, negative_impls)]
use memoffset_unstable_const::offset_of;
@@ -69,6 +69,39 @@
// Error while generating bindings for item 'TrivialWithDefaulted::operator=':
// Parameter type 'struct TrivialWithDefaulted &&' is not supported
+/// This struct is trivial, and therefore trivially relocatable etc., but still
+/// not safe to pass by reference as it is not final.
+#[derive(Clone, Copy)]
+#[repr(C)]
+pub struct TrivialNonfinal {
+ pub trivial_field: i32,
+}
+
+impl !Unpin for TrivialNonfinal {}
+
+// rs_bindings_from_cc/test/golden/trivial_type.h;l=27
+// Error while generating bindings for item 'TrivialNonfinal::TrivialNonfinal':
+// Nested classes are not supported yet
+
+impl Default for TrivialNonfinal {
+ #[inline(always)]
+ fn default() -> Self {
+ let mut tmp = std::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ crate::detail::__rust_thunk___ZN15TrivialNonfinalC1Ev(tmp.as_mut_ptr());
+ tmp.assume_init()
+ }
+ }
+}
+
+// rs_bindings_from_cc/test/golden/trivial_type.h;l=27
+// Error while generating bindings for item 'TrivialNonfinal::TrivialNonfinal':
+// Parameter type 'struct TrivialNonfinal &&' is not supported
+
+// rs_bindings_from_cc/test/golden/trivial_type.h;l=27
+// Error while generating bindings for item 'TrivialNonfinal::operator=':
+// Parameter type 'struct TrivialNonfinal &&' is not supported
+
#[inline(always)]
pub fn TakesByValue(trivial: Trivial) {
unsafe { crate::detail::__rust_thunk___Z12TakesByValue7Trivial(trivial) }
@@ -81,6 +114,34 @@
}
}
+#[inline(always)]
+pub fn TakesTrivialNonfinalByValue(trivial: TrivialNonfinal) {
+ unsafe {
+ crate::detail::__rust_thunk___Z27TakesTrivialNonfinalByValue15TrivialNonfinal(trivial)
+ }
+}
+
+#[inline(always)]
+pub fn TakesByReference(trivial: *mut Trivial) {
+ unsafe { crate::detail::__rust_thunk___Z16TakesByReferenceR7Trivial(trivial) }
+}
+
+#[inline(always)]
+pub fn TakesWithDefaultedByReference(trivial: *mut TrivialWithDefaulted) {
+ unsafe {
+ crate::detail::__rust_thunk___Z29TakesWithDefaultedByReferenceR20TrivialWithDefaulted(
+ trivial,
+ )
+ }
+}
+
+#[inline(always)]
+pub fn TakesTrivialNonfinalByReference(trivial: *mut TrivialNonfinal) {
+ unsafe {
+ crate::detail::__rust_thunk___Z31TakesTrivialNonfinalByReferenceR15TrivialNonfinal(trivial)
+ }
+}
+
// CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_TRIVIAL_TYPE_H_
mod detail {
@@ -98,12 +159,31 @@
__this: *mut TrivialWithDefaulted,
__param_0: *const TrivialWithDefaulted,
);
+ pub(crate) fn __rust_thunk___ZN15TrivialNonfinalC1Ev(__this: *mut TrivialNonfinal);
+ pub(crate) fn __rust_thunk___ZN15TrivialNonfinalC1ERKS_(
+ __this: *mut TrivialNonfinal,
+ __param_0: *const TrivialNonfinal,
+ );
#[link_name = "_Z12TakesByValue7Trivial"]
pub(crate) fn __rust_thunk___Z12TakesByValue7Trivial(trivial: Trivial);
#[link_name = "_Z25TakesWithDefaultedByValue20TrivialWithDefaulted"]
pub(crate) fn __rust_thunk___Z25TakesWithDefaultedByValue20TrivialWithDefaulted(
trivial: TrivialWithDefaulted,
);
+ #[link_name = "_Z27TakesTrivialNonfinalByValue15TrivialNonfinal"]
+ pub(crate) fn __rust_thunk___Z27TakesTrivialNonfinalByValue15TrivialNonfinal(
+ trivial: TrivialNonfinal,
+ );
+ #[link_name = "_Z16TakesByReferenceR7Trivial"]
+ pub(crate) fn __rust_thunk___Z16TakesByReferenceR7Trivial(trivial: *mut Trivial);
+ #[link_name = "_Z29TakesWithDefaultedByReferenceR20TrivialWithDefaulted"]
+ pub(crate) fn __rust_thunk___Z29TakesWithDefaultedByReferenceR20TrivialWithDefaulted(
+ trivial: *mut TrivialWithDefaulted,
+ );
+ #[link_name = "_Z31TakesTrivialNonfinalByReferenceR15TrivialNonfinal"]
+ pub(crate) fn __rust_thunk___Z31TakesTrivialNonfinalByReferenceR15TrivialNonfinal(
+ trivial: *mut TrivialNonfinal,
+ );
}
}
@@ -116,3 +196,7 @@
const _: () = assert!(std::mem::size_of::<TrivialWithDefaulted>() == 4usize);
const _: () = assert!(std::mem::align_of::<TrivialWithDefaulted>() == 4usize);
const _: () = assert!(offset_of!(TrivialWithDefaulted, trivial_field) * 8 == 0usize);
+
+const _: () = assert!(std::mem::size_of::<TrivialNonfinal>() == 4usize);
+const _: () = assert!(std::mem::align_of::<TrivialNonfinal>() == 4usize);
+const _: () = assert!(offset_of!(TrivialNonfinal, trivial_field) * 8 == 0usize);
diff --git a/rs_bindings_from_cc/test/golden/trivial_type_rs_api_impl.cc b/rs_bindings_from_cc/test/golden/trivial_type_rs_api_impl.cc
index 33aebee..6f1bf1d 100644
--- a/rs_bindings_from_cc/test/golden/trivial_type_rs_api_impl.cc
+++ b/rs_bindings_from_cc/test/golden/trivial_type_rs_api_impl.cc
@@ -27,6 +27,14 @@
TrivialWithDefaulted* __this) {
std ::destroy_at(__this);
}
+extern "C" void __rust_thunk___ZN15TrivialNonfinalC1Ev(
+ TrivialNonfinal* __this) {
+ construct_at(__this);
+}
+extern "C" void __rust_thunk___ZN15TrivialNonfinalD1Ev(
+ TrivialNonfinal* __this) {
+ std ::destroy_at(__this);
+}
static_assert(sizeof(Trivial) == 4);
static_assert(alignof(Trivial) == 4);
@@ -35,3 +43,7 @@
static_assert(sizeof(TrivialWithDefaulted) == 4);
static_assert(alignof(TrivialWithDefaulted) == 4);
static_assert(offsetof(TrivialWithDefaulted, trivial_field) * 8 == 0);
+
+static_assert(sizeof(TrivialNonfinal) == 4);
+static_assert(alignof(TrivialNonfinal) == 4);
+static_assert(offsetof(TrivialNonfinal, trivial_field) * 8 == 0);
diff --git a/rs_bindings_from_cc/test/golden/types.h b/rs_bindings_from_cc/test/golden/types.h
index ef55482..02b09a3 100644
--- a/rs_bindings_from_cc/test/golden/types.h
+++ b/rs_bindings_from_cc/test/golden/types.h
@@ -8,10 +8,9 @@
#include <cstddef>
#include <cstdint>
-struct SomeStruct {
-};
+struct SomeStruct final {};
-struct FieldTypeTestStruct {
+struct FieldTypeTestStruct final {
bool bool_field;
char char_field;
diff --git a/rs_bindings_from_cc/test/golden/types_rs_api.rs b/rs_bindings_from_cc/test/golden/types_rs_api.rs
index 5ea7d24..51855d4 100644
--- a/rs_bindings_from_cc/test/golden/types_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/types_rs_api.rs
@@ -81,11 +81,11 @@
pub const_struct_ref_field: *const SomeStruct,
}
-// rs_bindings_from_cc/test/golden/types.h;l=10
+// rs_bindings_from_cc/test/golden/types.h;l=9
// Error while generating bindings for item 'FieldTypeTestStruct::FieldTypeTestStruct':
// Nested classes are not supported yet
-// rs_bindings_from_cc/test/golden/types.h;l=10
+// rs_bindings_from_cc/test/golden/types.h;l=9
// Error while generating bindings for item 'FieldTypeTestStruct::FieldTypeTestStruct':
// Parameter type 'struct FieldTypeTestStruct &&' is not supported
diff --git a/rs_bindings_from_cc/test/golden/unsupported.h b/rs_bindings_from_cc/test/golden/unsupported.h
index 5176da8..74b150a 100644
--- a/rs_bindings_from_cc/test/golden/unsupported.h
+++ b/rs_bindings_from_cc/test/golden/unsupported.h
@@ -5,7 +5,7 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_UNSUPPORTED_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_UNSUPPORTED_H_
-struct NontrivialCustomType {
+struct NontrivialCustomType final {
NontrivialCustomType(NontrivialCustomType&&);
int i;
@@ -18,11 +18,11 @@
namespace ns {
void FunctionInNamespace();
-struct StructInNamespace {};
+struct StructInNamespace final {};
} // namespace ns
-struct ContainingStruct {
- struct NestedStruct {};
+struct ContainingStruct final {
+ struct NestedStruct final {};
};
#endif // CRUBIT_RS_BINDINGS_FROM_CC_TEST_GOLDEN_UNSUPPORTED_H_
diff --git a/rs_bindings_from_cc/test/golden/user_of_imported_type.h b/rs_bindings_from_cc/test/golden/user_of_imported_type.h
index 1743118..9ed1634 100644
--- a/rs_bindings_from_cc/test/golden/user_of_imported_type.h
+++ b/rs_bindings_from_cc/test/golden/user_of_imported_type.h
@@ -9,7 +9,7 @@
Trivial UsesImportedType(Trivial t);
-struct UserOfImportedType {
+struct UserOfImportedType final {
Trivial* trivial;
};
diff --git a/rs_bindings_from_cc/test/struct/constructors/constructors.h b/rs_bindings_from_cc/test/struct/constructors/constructors.h
index c6cb55a..6040f61 100644
--- a/rs_bindings_from_cc/test/struct/constructors/constructors.h
+++ b/rs_bindings_from_cc/test/struct/constructors/constructors.h
@@ -5,7 +5,7 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_CONSTRUCTORS_CONSTRUCTORS_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_CONSTRUCTORS_CONSTRUCTORS_H_
-struct StructWithUserProvidedConstructor {
+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).
@@ -13,20 +13,20 @@
int int_field;
};
-struct StructWithDeletedConstructor {
+struct StructWithDeletedConstructor final {
StructWithDeletedConstructor() = delete;
int int_field;
};
-struct StructWithPrivateConstructor {
+struct StructWithPrivateConstructor final {
private:
StructWithPrivateConstructor();
int int_field;
};
-struct StructWithExplicitlyDefaultedConstructor {
+struct StructWithExplicitlyDefaultedConstructor final {
StructWithExplicitlyDefaultedConstructor() = default;
int field_with_explicit_initializer = 123;
diff --git a/rs_bindings_from_cc/test/struct/fields/fields.h b/rs_bindings_from_cc/test/struct/fields/fields.h
index 9ec072c..fbf576a 100644
--- a/rs_bindings_from_cc/test/struct/fields/fields.h
+++ b/rs_bindings_from_cc/test/struct/fields/fields.h
@@ -5,12 +5,12 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_FIELDS_FIELDS_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_FIELDS_FIELDS_H_
-struct SomeStruct {
+struct SomeStruct final {
char char_var;
int int_var;
};
-class SomeClass {
+class SomeClass final {
public:
int public_field = 0;
diff --git a/rs_bindings_from_cc/test/struct/methods/methods.h b/rs_bindings_from_cc/test/struct/methods/methods.h
index 490ed94..8c231b5 100644
--- a/rs_bindings_from_cc/test/struct/methods/methods.h
+++ b/rs_bindings_from_cc/test/struct/methods/methods.h
@@ -5,7 +5,7 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_METHODS_METHODS_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_METHODS_METHODS_H_
-class SomeClass {
+class SomeClass final {
public:
static SomeClass static_factory_method(int int_var_initial_value);
static int static_method_that_multiplies_its_args(int x, int y);
diff --git a/rs_bindings_from_cc/test/struct/multiple_targets/dependency.h b/rs_bindings_from_cc/test/struct/multiple_targets/dependency.h
index 5a7c0b4..329cf1f 100644
--- a/rs_bindings_from_cc/test/struct/multiple_targets/dependency.h
+++ b/rs_bindings_from_cc/test/struct/multiple_targets/dependency.h
@@ -5,7 +5,7 @@
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_MULTIPLE_TARGETS_DEPENDENCY_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_TEST_STRUCT_MULTIPLE_TARGETS_DEPENDENCY_H_
-struct Dependency {
+struct Dependency final {
int magic;
};