Be more specific in our handling of explicit instantiations

In this CL:
1) we generate unsupported item for explicit class template instantiation declarations
2) we make explicit class template instantiation definitions work (the golden test for `templates.h` would not compile

PiperOrigin-RevId: 472972750
diff --git a/rs_bindings_from_cc/importers/BUILD b/rs_bindings_from_cc/importers/BUILD
index 0d6e914..15ba26c 100644
--- a/rs_bindings_from_cc/importers/BUILD
+++ b/rs_bindings_from_cc/importers/BUILD
@@ -24,6 +24,7 @@
         "//rs_bindings_from_cc:ast_convert",
         "//rs_bindings_from_cc:decl_importer",
         "@llvm-project//clang:ast",
+        "@llvm-project//clang:basic",
         "@llvm-project//clang:sema",
         "@llvm-project//llvm:Support",
     ],
diff --git a/rs_bindings_from_cc/importers/cxx_record.cc b/rs_bindings_from_cc/importers/cxx_record.cc
index ca215a7..e3a7325 100644
--- a/rs_bindings_from_cc/importers/cxx_record.cc
+++ b/rs_bindings_from_cc/importers/cxx_record.cc
@@ -7,13 +7,13 @@
 #include "absl/log/check.h"
 #include "absl/log/die_if_null.h"
 #include "absl/log/log.h"
-#include "absl/strings/match.h"
 #include "rs_bindings_from_cc/ast_convert.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/Type.h"
+#include "clang/Basic/Specifiers.h"
 #include "clang/Sema/Sema.h"
 #include "llvm/Support/ErrorHandling.h"
 
@@ -51,7 +51,7 @@
   //
   // This logic should go away once we start generating top level bindings for
   // explicit class template specializations.
-  if (specialization_decl->isExplicitSpecialization()) {
+  if (specialization_decl->isExplicitInstantiationOrSpecialization()) {
     llvm::SmallString<128> storage;
     llvm::raw_svector_ostream out(storage);
     out << specialization_decl->getName();
@@ -143,8 +143,16 @@
   if (auto* specialization_decl =
           clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(
               record_decl)) {
+    if (specialization_decl->getSpecializationKind() ==
+        clang::TSK_ExplicitInstantiationDeclaration) {
+      return ictx_.ImportUnsupportedItem(
+          record_decl,
+          "Explicit class template instantiation declarations are not handled "
+          "yet.");
+    }
+
     is_implicit_class_template_specialization_decl =
-        !specialization_decl->isExplicitSpecialization();
+        !specialization_decl->isExplicitInstantiationOrSpecialization();
     rs_name = ictx_.GetMangledName(specialization_decl);
     cc_name =
         GetClassTemplateSpecializationCcName(ictx_.ctx_, specialization_decl);
diff --git a/rs_bindings_from_cc/ir_from_cc_test.rs b/rs_bindings_from_cc/ir_from_cc_test.rs
index ecb9037..bb2df15 100644
--- a/rs_bindings_from_cc/ir_from_cc_test.rs
+++ b/rs_bindings_from_cc/ir_from_cc_test.rs
@@ -143,10 +143,22 @@
 }
 
 #[test]
-fn test_dont_import_unused_class_template_or_specialization() {
-    let ir = ir_from_cc("template <class T> struct Template{}; template<> struct Template<int>{};")
-        .unwrap();
-    assert_ir_not_matches!(ir, quote! { Record { ... "Template" ... } });
+fn test_explicit_class_template_instantiation_declaration_not_supported_yet() {
+    let ir = ir_from_cc(
+        "
+        template <class T> struct MyTemplate{};
+        extern template struct MyTemplate<int>;
+      ",
+    )
+    .unwrap();
+    assert_ir_not_matches!(ir, quote! { Record });
+    assert_ir_matches!(
+        ir,
+        quote! { UnsupportedItem {
+          name: "MyTemplate",
+          message: "Explicit class template instantiation declarations are not handled yet." ...
+        }}
+    );
 }
 
 #[test]
@@ -696,8 +708,22 @@
     let type_mapping: HashMap<_, _> = fields
         .map(|f| {
             (
-                f.type_.as_ref().unwrap().cc_type.name.as_ref().unwrap().as_str(),
-                f.type_.as_ref().unwrap().rs_type.name.as_ref().unwrap().as_str(),
+                f.type_
+                    .as_ref()
+                    .unwrap()
+                    .cc_type
+                    .name
+                    .as_ref()
+                    .unwrap()
+                    .as_str(),
+                f.type_
+                    .as_ref()
+                    .unwrap()
+                    .rs_type
+                    .name
+                    .as_ref()
+                    .unwrap()
+                    .as_str(),
             )
         })
         .collect();
@@ -869,7 +895,7 @@
 }
 
 #[test]
-fn test_typedef_of_fully_instantiated_template() -> Result<()> {
+fn test_typedef_of_full_template_specialization() -> Result<()> {
     let ir = ir_from_cc(
         r#" #pragma clang lifetime_elision
             namespace test_namespace_bindings {
@@ -887,7 +913,7 @@
                 using MyTypeAlias = MyStruct<int>;
             }"#,
     )?;
-    // Instantiation of the struct template:
+    // Instantiation of MyStruct<int> specialization:
     assert_ir_matches!(
         ir,
         quote! {
@@ -939,7 +965,7 @@
           }
         }
     );
-    // Member function of the struct template instantiation:
+    // Member function of the MyTemplate<int> specialization:
     assert_ir_matches!(
         ir,
         quote! {
@@ -996,7 +1022,7 @@
                 using MyTypeAlias = MyStruct<int>;
               }"#,
     )?;
-    // Instantiation of the struct template based on the specialization for T=int:
+    // Instantiation of the explicit MyStruct<int> specialization:
     assert_ir_matches!(
         ir,
         quote! {
@@ -1028,7 +1054,7 @@
     // assert_eq!(1, ir.top_level_item_ids().filter(|&&id| id ==
     // record_id).count());
 
-    // Instance method inside the struct template:
+    // Instance method inside the explicit MyStruct<int> specialization:
     assert_ir_matches!(
         ir,
         quote! {
@@ -1049,7 +1075,7 @@
 }
 
 #[test]
-fn test_multiple_typedefs_to_same_template() -> Result<()> {
+fn test_multiple_typedefs_to_same_specialization() -> Result<()> {
     let ir = ir_from_cc(
         r#" #pragma clang lifetime_elision
             template <typename T>
@@ -1143,6 +1169,28 @@
 }
 
 #[test]
+fn test_explicit_class_template_instantiation_definitions_are_imported() {
+    let ir = ir_from_cc(
+        "
+        namespace my_namespace {
+          template <class T> struct MyTemplate{};
+          template struct MyTemplate<int>;
+        }
+      ",
+    )
+    .unwrap();
+    assert_ir_matches!(
+        ir,
+        quote! { Record {
+          rs_name: "__CcTemplateInstN12my_namespace10MyTemplateIiEE", ...
+          cc_name: "MyTemplate<int>", ...
+          owning_target: BazelLabel("//test:testing_target"), ...
+          enclosing_namespace_id: Some(...), ...
+        }}
+    );
+}
+
+#[test]
 fn test_templates_inheritance() -> Result<()> {
     let ir = ir_from_cc(
         r#" #pragma clang lifetime_elision
@@ -2070,7 +2118,10 @@
 fn test_typedef_nested_in_record_not_supported() {
     let ir = ir_from_cc("struct S { typedef int MyTypedefDecl; };").unwrap();
     assert_strings_contain(
-        ir.unsupported_items().map(|i| i.name.as_str()).collect_vec().as_slice(),
+        ir.unsupported_items()
+            .map(|i| i.name.as_str())
+            .collect_vec()
+            .as_slice(),
         "S::MyTypedefDecl",
     );
 }
@@ -2652,7 +2703,10 @@
     // once we start importing nested structs.
     let ir = ir_from_cc("struct X { struct Y {}; };")?;
     assert_strings_contain(
-        ir.unsupported_items().map(|i| i.name.as_str()).collect_vec().as_slice(),
+        ir.unsupported_items()
+            .map(|i| i.name.as_str())
+            .collect_vec()
+            .as_slice(),
         "X::Y",
     );
     Ok(())
@@ -3491,7 +3545,7 @@
 }
 
 #[test]
-fn test_incomplete_record_has_rs_name() {
+fn test_forward_declared_specialization_has_rs_name() {
     let ir = ir_from_cc(
         r#"
         namespace test_namespace_bindings {
diff --git a/rs_bindings_from_cc/test/golden/templates.h b/rs_bindings_from_cc/test/golden/templates.h
index 816f32b..ebdac54 100644
--- a/rs_bindings_from_cc/test/golden/templates.h
+++ b/rs_bindings_from_cc/test/golden/templates.h
@@ -49,8 +49,31 @@
   T t;
 };
 
+// Explicit class template specialization with definition should not be imported
+// unless also instantiated.
+// TODO(b/245680028): `MyStruct<bool>` should not be imported.
+template <>
+struct MyStruct<bool> {};
+
+// Explicit class template specialization with definition should be imported
+// even when not instantiated if there is a type alias for it.
 template <>
 struct MyStruct<char> {};
+using MyCharStruct = MyStruct<char>;
+
+// Forward declared explicit class template specialization should be imported
+// so the forward declaration code is generated (`forward_declare!`).
+template <>
+struct MyStruct<int>;
+
+// Explicit class template instantiation definition is imported similarly to
+// how implicit typedeffed instantiations are.
+template class MyStruct<float>;
+
+// Explicit class template instantiation declaration is not handled (yet?)
+// TODO(b/245467707): Consider handling these as a build speed/ergonomic
+// optimization.
+extern template class MyStruct<double>;
 
 }  // namespace test_namespace_bindings
 
diff --git a/rs_bindings_from_cc/test/golden/templates_rs_api.rs b/rs_bindings_from_cc/test/golden/templates_rs_api.rs
index e7ada0d..8a80218 100644
--- a/rs_bindings_from_cc/test/golden/templates_rs_api.rs
+++ b/rs_bindings_from_cc/test/golden/templates_rs_api.rs
@@ -130,6 +130,42 @@
     // Error while generating bindings for item 'test_namespace_bindings::MyStruct':
     // Class templates are not supported yet
 
+    /// Explicit class template specialization with definition should not be imported
+    /// unless also instantiated.
+    /// TODO(b/245680028): `MyStruct<bool>` should not be imported.
+    #[::ctor::recursively_pinned]
+    #[repr(C)]
+    pub struct __CcTemplateInstN23test_namespace_bindings8MyStructIbEE {
+        __non_field_data: [::std::mem::MaybeUninit<u8>; 1],
+    }
+    forward_declare::unsafe_define!(
+        forward_declare::symbol!("MyStruct<bool>"),
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE
+    );
+
+    impl ::ctor::CtorNew<()> for __CcTemplateInstN23test_namespace_bindings8MyStructIbEE {
+        type CtorType = impl ::ctor::Ctor<Output = Self>;
+        #[inline(always)]
+        fn ctor_new(args: ()) -> Self::CtorType {
+            let () = args;
+            unsafe {
+                ::ctor::FnCtor::new(move|dest: ::std::pin::Pin<&mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>>|{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(::std::pin::Pin::into_inner_unchecked(dest)); })
+            }
+        }
+    }
+
+    impl<'b>::ctor::CtorNew<&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>for __CcTemplateInstN23test_namespace_bindings8MyStructIbEE{ type CtorType=impl::ctor::Ctor<Output=Self>+::ctor::Captures<'b>;#[inline(always)]fn ctor_new(args:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE)->Self::CtorType{ let __param_0=args;unsafe{ ::ctor::FnCtor::new(move|dest: ::std::pin::Pin<&mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>>|{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1ERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(::std::pin::Pin::into_inner_unchecked(dest),__param_0); }) } } }
+    impl<'b>::ctor::CtorNew<(&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE,)>for __CcTemplateInstN23test_namespace_bindings8MyStructIbEE{ type CtorType=impl::ctor::Ctor<Output=Self>+::ctor::Captures<'b>;#[inline(always)]fn ctor_new(args:(&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE,))->Self::CtorType{ let(arg,)=args;<Self as::ctor::CtorNew<&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>>::ctor_new(arg) } }
+
+    impl<'b>::ctor::CtorNew<::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>>for __CcTemplateInstN23test_namespace_bindings8MyStructIbEE{ type CtorType=impl::ctor::Ctor<Output=Self>+::ctor::Captures<'b>;#[inline(always)]fn ctor_new(args: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>)->Self::CtorType{ let __param_0=args;unsafe{ ::ctor::FnCtor::new(move|dest: ::std::pin::Pin<&mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>>|{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1EOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(::std::pin::Pin::into_inner_unchecked(dest),__param_0); }) } } }
+    impl<'b>::ctor::CtorNew<(::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>,)>for __CcTemplateInstN23test_namespace_bindings8MyStructIbEE{ type CtorType=impl::ctor::Ctor<Output=Self>+::ctor::Captures<'b>;#[inline(always)]fn ctor_new(args:(::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>,))->Self::CtorType{ let(arg,)=args;<Self as::ctor::CtorNew<::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>>>::ctor_new(arg) } }
+
+    impl<'b>::ctor::Assign<&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>for __CcTemplateInstN23test_namespace_bindings8MyStructIbEE{ #[inline(always)]fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>,__param_0:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE){ unsafe{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIbEaSERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(self,__param_0); } } }
+
+    impl<'b>::ctor::Assign<::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>>for __CcTemplateInstN23test_namespace_bindings8MyStructIbEE{ #[inline(always)]fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>,__param_0: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>){ unsafe{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIbEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(self,__param_0); } } }
+
+    /// Explicit class template specialization with definition should be imported
+    /// even when not instantiated if there is a type alias for it.
     #[::ctor::recursively_pinned]
     #[repr(C)]
     pub struct __CcTemplateInstN23test_namespace_bindings8MyStructIcEE {
@@ -160,11 +196,53 @@
     impl<'b>::ctor::Assign<&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE>for __CcTemplateInstN23test_namespace_bindings8MyStructIcEE{ #[inline(always)]fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>,__param_0:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE){ unsafe{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIcEaSERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(self,__param_0); } } }
 
     impl<'b>::ctor::Assign<::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE>>for __CcTemplateInstN23test_namespace_bindings8MyStructIcEE{ #[inline(always)]fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>,__param_0: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE>){ unsafe{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIcEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(self,__param_0); } } }
+
+    pub type MyCharStruct =
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE;
+
+    forward_declare::forward_declare!(pub __CcTemplateInstN23test_namespace_bindings8MyStructIiEE = forward_declare::symbol!("__CcTemplateInstN23test_namespace_bindings8MyStructIiEE"));
+
+    /// Explicit class template instantiation definition is imported similarly to
+    /// how implicit typedeffed instantiations are.
+    #[::ctor::recursively_pinned]
+    #[repr(C)]
+    pub struct __CcTemplateInstN23test_namespace_bindings8MyStructIfEE {
+        pub t: f32,
+    }
+    forward_declare::unsafe_define!(
+        forward_declare::symbol!("MyStruct<float>"),
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE
+    );
+
+    impl ::ctor::CtorNew<()> for __CcTemplateInstN23test_namespace_bindings8MyStructIfEE {
+        type CtorType = impl ::ctor::Ctor<Output = Self>;
+        #[inline(always)]
+        fn ctor_new(args: ()) -> Self::CtorType {
+            let () = args;
+            unsafe {
+                ::ctor::FnCtor::new(move|dest: ::std::pin::Pin<&mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>>|{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(::std::pin::Pin::into_inner_unchecked(dest)); })
+            }
+        }
+    }
+
+    impl<'b>::ctor::CtorNew<&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>for __CcTemplateInstN23test_namespace_bindings8MyStructIfEE{ type CtorType=impl::ctor::Ctor<Output=Self>+::ctor::Captures<'b>;#[inline(always)]fn ctor_new(args:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE)->Self::CtorType{ let __param_0=args;unsafe{ ::ctor::FnCtor::new(move|dest: ::std::pin::Pin<&mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>>|{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1ERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(::std::pin::Pin::into_inner_unchecked(dest),__param_0); }) } } }
+    impl<'b>::ctor::CtorNew<(&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE,)>for __CcTemplateInstN23test_namespace_bindings8MyStructIfEE{ type CtorType=impl::ctor::Ctor<Output=Self>+::ctor::Captures<'b>;#[inline(always)]fn ctor_new(args:(&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE,))->Self::CtorType{ let(arg,)=args;<Self as::ctor::CtorNew<&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>>::ctor_new(arg) } }
+
+    impl<'b>::ctor::CtorNew<::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>>for __CcTemplateInstN23test_namespace_bindings8MyStructIfEE{ type CtorType=impl::ctor::Ctor<Output=Self>+::ctor::Captures<'b>;#[inline(always)]fn ctor_new(args: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>)->Self::CtorType{ let __param_0=args;unsafe{ ::ctor::FnCtor::new(move|dest: ::std::pin::Pin<&mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>>|{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1EOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(::std::pin::Pin::into_inner_unchecked(dest),__param_0); }) } } }
+    impl<'b>::ctor::CtorNew<(::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>,)>for __CcTemplateInstN23test_namespace_bindings8MyStructIfEE{ type CtorType=impl::ctor::Ctor<Output=Self>+::ctor::Captures<'b>;#[inline(always)]fn ctor_new(args:(::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>,))->Self::CtorType{ let(arg,)=args;<Self as::ctor::CtorNew<::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>>>::ctor_new(arg) } }
+
+    impl<'b>::ctor::Assign<&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>for __CcTemplateInstN23test_namespace_bindings8MyStructIfEE{ #[inline(always)]fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>,__param_0:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE){ unsafe{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIfEaSERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(self,__param_0); } } }
+
+    impl<'b>::ctor::Assign<::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>>for __CcTemplateInstN23test_namespace_bindings8MyStructIfEE{ #[inline(always)]fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>,__param_0: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>){ unsafe{ crate::detail::__rust_thunk___ZN23test_namespace_bindings8MyStructIfEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(self,__param_0); } } }
+
+    // rs_bindings_from_cc/test/golden/templates.h;l=76
+    // Error while generating bindings for item 'test_namespace_bindings::MyStruct':
+    // Explicit class template instantiation declarations are not handled yet.
 }
 
 // namespace test_namespace_bindings
 
-// rs_bindings_from_cc/test/golden/templates.h;l=57
+// rs_bindings_from_cc/test/golden/templates.h;l=80
 // Error while generating bindings for item 'MyTopLevelTemplate':
 // Class templates are not supported yet
 
@@ -183,7 +261,7 @@
 }
 
 pub mod template_template_params {
-    // rs_bindings_from_cc/test/golden/templates.h;l=72
+    // rs_bindings_from_cc/test/golden/templates.h;l=95
     // Error while generating bindings for item 'template_template_params::Policy':
     // Class templates are not supported yet
 
@@ -218,7 +296,7 @@
 
     impl<'b>::ctor::Assign<::ctor::RvalueReference<'b,crate::template_template_params::__CcTemplateInstN24template_template_params6PolicyIiEE>>for __CcTemplateInstN24template_template_params6PolicyIiEE{ #[inline(always)]fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>,__param_0: ::ctor::RvalueReference<'b,crate::template_template_params::__CcTemplateInstN24template_template_params6PolicyIiEE>){ unsafe{ crate::detail::__rust_thunk___ZN24template_template_params6PolicyIiEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(self,__param_0); } } }
 
-    // rs_bindings_from_cc/test/golden/templates.h;l=82
+    // rs_bindings_from_cc/test/golden/templates.h;l=105
     // Error while generating bindings for item 'template_template_params::MyTemplate':
     // Class templates are not supported yet
 
@@ -228,11 +306,11 @@
 
 // namespace template_template_params
 
-pub mod forward_declared_template { // rs_bindings_from_cc/test/golden/templates.h;l=100
+pub mod forward_declared_template { // rs_bindings_from_cc/test/golden/templates.h;l=123
     // Error while generating bindings for item 'forward_declared_template::ForwardDeclaredTemplate':
     // Class templates are not supported yet
 
-    // rs_bindings_from_cc/test/golden/templates.h;l=103
+    // rs_bindings_from_cc/test/golden/templates.h;l=126
     // Error while generating bindings for item 'forward_declared_template::TypeAliasToForwardDeclaredTemplate':
     // Unsupported type 'ForwardDeclaredTemplate<int>': 'ForwardDeclaredTemplate<int>' template specialization is incomplete
 }
@@ -620,11 +698,11 @@
     }
 }
 
-// rs_bindings_from_cc/test/golden/templates.h;l=58
+// rs_bindings_from_cc/test/golden/templates.h;l=81
 // Error while generating bindings for item 'MyTopLevelTemplate<test_namespace_bindings::TemplateParam>::operator=':
 // operator= for Unpin types is not yet supported.
 
-// rs_bindings_from_cc/test/golden/templates.h;l=58
+// rs_bindings_from_cc/test/golden/templates.h;l=81
 // Error while generating bindings for item 'MyTopLevelTemplate<test_namespace_bindings::TemplateParam>::operator=':
 // operator= for Unpin types is not yet supported.
 
@@ -798,6 +876,27 @@
             __this: &'a mut ::std::mem::MaybeUninit<crate::test_namespace_bindings::TemplateParam>,
             __param_0: ::ctor::RvalueReference<'b, crate::test_namespace_bindings::TemplateParam>,
         );
+        pub(crate) fn __rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<
+            'a,
+        >(
+            __this:&'a mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>,
+        );
+        pub(crate) fn __rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1ERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<
+            'a,
+            'b,
+        >(
+            __this:&'a mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>,
+            __param_0:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE,
+        );
+        pub(crate) fn __rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1EOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<
+            'a,
+            'b,
+        >(
+            __this:&'a mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>,
+            __param_0: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>,
+        );
+        pub(crate)fn __rust_thunk___ZN23test_namespace_bindings8MyStructIbEaSERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<'a,'b>(__this: ::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>,__param_0:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE)->::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>;
+        pub(crate)fn __rust_thunk___ZN23test_namespace_bindings8MyStructIbEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<'a,'b>(__this: ::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>,__param_0: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>)->::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE>;
         pub(crate) fn __rust_thunk___ZN23test_namespace_bindings8MyStructIcEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<
             'a,
         >(
@@ -819,6 +918,27 @@
         );
         pub(crate)fn __rust_thunk___ZN23test_namespace_bindings8MyStructIcEaSERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<'a,'b>(__this: ::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE>,__param_0:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE)->::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE>;
         pub(crate)fn __rust_thunk___ZN23test_namespace_bindings8MyStructIcEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<'a,'b>(__this: ::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE>,__param_0: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE>)->::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE>;
+        pub(crate) fn __rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<
+            'a,
+        >(
+            __this:&'a mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>,
+        );
+        pub(crate) fn __rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1ERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<
+            'a,
+            'b,
+        >(
+            __this:&'a mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>,
+            __param_0:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE,
+        );
+        pub(crate) fn __rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1EOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<
+            'a,
+            'b,
+        >(
+            __this:&'a mut::std::mem::MaybeUninit<crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>,
+            __param_0: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>,
+        );
+        pub(crate)fn __rust_thunk___ZN23test_namespace_bindings8MyStructIfEaSERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<'a,'b>(__this: ::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>,__param_0:&'b crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE)->::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>;
+        pub(crate)fn __rust_thunk___ZN23test_namespace_bindings8MyStructIfEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc<'a,'b>(__this: ::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>,__param_0: ::ctor::RvalueReference<'b,crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>)->::std::pin::Pin<&'a mut crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE>;
         #[link_name = "_Z36processForwardDeclaredSpecializationP18MyTopLevelTemplateIiE"]
         pub(crate) fn __rust_thunk___Z36processForwardDeclaredSpecializationP18MyTopLevelTemplateIiE<
             'a,
@@ -1051,6 +1171,28 @@
 };
 const _: () = assert!(
     ::std::mem::size_of::<
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE,
+    >() == 1
+);
+const _: () = assert!(
+    ::std::mem::align_of::<
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE,
+    >() == 1
+);
+const _: () = {
+    static_assertions::assert_not_impl_any!(
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE:
+            Copy
+    );
+};
+const _: () = {
+    static_assertions::assert_not_impl_any!(
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIbEE:
+            Drop
+    );
+};
+const _: () = assert!(
+    ::std::mem::size_of::<
         crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIcEE,
     >() == 1
 );
@@ -1071,6 +1213,34 @@
             Drop
     );
 };
+const _: () = assert!(
+    ::std::mem::size_of::<
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE,
+    >() == 4
+);
+const _: () = assert!(
+    ::std::mem::align_of::<
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE,
+    >() == 4
+);
+const _: () = {
+    static_assertions::assert_not_impl_any!(
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE:
+            Copy
+    );
+};
+const _: () = {
+    static_assertions::assert_not_impl_any!(
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE:
+            Drop
+    );
+};
+const _: () = assert!(
+    memoffset::offset_of!(
+        crate::test_namespace_bindings::__CcTemplateInstN23test_namespace_bindings8MyStructIfEE,
+        t
+    ) == 0
+);
 
 const _: () = assert!(
     ::std::mem::size_of::<
diff --git a/rs_bindings_from_cc/test/golden/templates_rs_api_impl.cc b/rs_bindings_from_cc/test/golden/templates_rs_api_impl.cc
index fa42c48..c7cfb84 100644
--- a/rs_bindings_from_cc/test/golden/templates_rs_api_impl.cc
+++ b/rs_bindings_from_cc/test/golden/templates_rs_api_impl.cc
@@ -124,6 +124,33 @@
   crubit::construct_at(__this, std::move(*__param_0));
 }
 extern "C" void
+__rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<bool>* __this) {
+  crubit::construct_at(__this);
+}
+extern "C" void
+__rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1ERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<bool>* __this,
+    const struct test_namespace_bindings::MyStruct<bool>* __param_0) {
+  crubit::construct_at(__this, *__param_0);
+}
+extern "C" void
+__rust_thunk___ZN23test_namespace_bindings8MyStructIbEC1EOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<bool>* __this,
+    struct test_namespace_bindings::MyStruct<bool>* __param_0) {
+  crubit::construct_at(__this, std::move(*__param_0));
+}
+extern "C" struct test_namespace_bindings::MyStruct<bool>*
+__rust_thunk___ZN23test_namespace_bindings8MyStructIbEaSERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<bool>* __this,
+    const struct test_namespace_bindings::MyStruct<bool>* __param_0) {
+  return &__this->operator=(*__param_0);
+} extern "C" struct test_namespace_bindings::MyStruct<bool>*
+__rust_thunk___ZN23test_namespace_bindings8MyStructIbEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<bool>* __this,
+    struct test_namespace_bindings::MyStruct<bool>* __param_0) {
+  return &__this->operator=(std::move(*__param_0));
+} extern "C" void
 __rust_thunk___ZN23test_namespace_bindings8MyStructIcEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
     struct test_namespace_bindings::MyStruct<char>* __this) {
   crubit::construct_at(__this);
@@ -151,6 +178,33 @@
     struct test_namespace_bindings::MyStruct<char>* __param_0) {
   return &__this->operator=(std::move(*__param_0));
 } extern "C" void
+__rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<float>* __this) {
+  crubit::construct_at(__this);
+}
+extern "C" void
+__rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1ERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<float>* __this,
+    const struct test_namespace_bindings::MyStruct<float>* __param_0) {
+  crubit::construct_at(__this, *__param_0);
+}
+extern "C" void
+__rust_thunk___ZN23test_namespace_bindings8MyStructIfEC1EOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<float>* __this,
+    struct test_namespace_bindings::MyStruct<float>* __param_0) {
+  crubit::construct_at(__this, std::move(*__param_0));
+}
+extern "C" struct test_namespace_bindings::MyStruct<float>*
+__rust_thunk___ZN23test_namespace_bindings8MyStructIfEaSERKS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<float>* __this,
+    const struct test_namespace_bindings::MyStruct<float>* __param_0) {
+  return &__this->operator=(*__param_0);
+} extern "C" struct test_namespace_bindings::MyStruct<float>*
+__rust_thunk___ZN23test_namespace_bindings8MyStructIfEaSEOS1___2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
+    struct test_namespace_bindings::MyStruct<float>* __this,
+    struct test_namespace_bindings::MyStruct<float>* __param_0) {
+  return &__this->operator=(std::move(*__param_0));
+} extern "C" void
 __rust_thunk___ZN18MyTopLevelTemplateIN23test_namespace_bindings13TemplateParamEEC1Ev__2f_2fthird_5fparty_2fcrubit_2frs_5fbindings_5ffrom_5fcc_2ftest_2fgolden_3atemplates_5fcc(
     struct MyTopLevelTemplate<test_namespace_bindings::TemplateParam>* __this) {
   crubit::construct_at(__this);
@@ -291,9 +345,17 @@
         struct test_namespace_bindings::TemplateWithTwoParams<int, float>) ==
     4);
 
+static_assert(sizeof(struct test_namespace_bindings::MyStruct<bool>) == 1);
+static_assert(alignof(struct test_namespace_bindings::MyStruct<bool>) == 1);
+
 static_assert(sizeof(struct test_namespace_bindings::MyStruct<char>) == 1);
 static_assert(alignof(struct test_namespace_bindings::MyStruct<char>) == 1);
 
+static_assert(sizeof(struct test_namespace_bindings::MyStruct<float>) == 4);
+static_assert(alignof(struct test_namespace_bindings::MyStruct<float>) == 4);
+static_assert(
+    CRUBIT_OFFSET_OF(t, struct test_namespace_bindings::MyStruct<float>) == 0);
+
 static_assert(
     sizeof(struct MyTopLevelTemplate<test_namespace_bindings::TemplateParam>) ==
     1);