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;
 };