Type aliases bound to fully-instantiated template

PiperOrigin-RevId: 449002160
diff --git a/rs_bindings_from_cc/src_code_gen.rs b/rs_bindings_from_cc/src_code_gen.rs
index 4be69ee..9fac534 100644
--- a/rs_bindings_from_cc/src_code_gen.rs
+++ b/rs_bindings_from_cc/src_code_gen.rs
@@ -173,6 +173,12 @@
     if func.is_inline {
         return false;
     }
+    // ## Member functions (or descendants) of class templates
+    //
+    // A thunk is required to force/guarantee template instantiation.
+    if func.is_member_or_descendant_of_class_template {
+        return false;
+    }
     // ## Virtual functions
     //
     // When calling virtual `A::Method()`, it's not necessarily the case that we'll
@@ -2593,6 +2599,133 @@
     }
 
     #[test]
+    fn test_template_in_dependency_and_alias_in_current_target() -> Result<()> {
+        // See also the test with the same name in `ir_from_cc_test.rs`.
+        let ir = {
+            let dependency_src = r#" #pragma clang lifetime_elision
+                    template <typename T>
+                    struct MyTemplate {
+                        T GetValue() { return field; }
+                        T field;
+                    }; "#;
+            let current_target_src = r#" #pragma clang lifetime_elision
+                    using MyAliasOfTemplate = MyTemplate<int>; "#;
+            ir_from_cc_dependency(current_target_src, dependency_src)?
+        };
+
+        let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(&ir)?;
+        assert_rs_matches!(
+            rs_api,
+            quote! {
+                #[repr(C)]
+                pub struct __CcTemplateInst10MyTemplateIiE {
+                    pub field: i32,
+                }
+            }
+        );
+        assert_rs_matches!(
+            rs_api,
+            quote! {
+                impl __CcTemplateInst10MyTemplateIiE {
+                    #[inline(always)]
+                    pub fn GetValue<'a>(self: ... Pin<&'a mut Self>) -> i32 { unsafe {
+                        crate::detail::__rust_thunk___ZN10MyTemplateIiE8GetValueEv___test_testing_target(
+                            self)
+                    }}
+                }
+            }
+        );
+        assert_rs_matches!(
+            rs_api,
+            quote! {
+                pub type MyAliasOfTemplate = crate::__CcTemplateInst10MyTemplateIiE;
+            }
+        );
+        assert_rs_matches!(
+            rs_api,
+            quote! {
+                mod detail {
+                    ...
+                    extern "C" {
+                        ...
+                        pub(crate) fn
+                        __rust_thunk___ZN10MyTemplateIiE8GetValueEv___test_testing_target<'a>(
+                            __this: ... Pin<&'a mut crate::__CcTemplateInst10MyTemplateIiE>
+                        ) -> i32;
+                        ...
+                    }
+                }
+            }
+        );
+        assert_cc_matches!(
+            rs_api_impl,
+            quote! {
+                extern "C" int __rust_thunk___ZN10MyTemplateIiE8GetValueEv___test_testing_target(
+                        class MyTemplate<int>* __this) {
+                    return __this->GetValue();
+                }
+            }
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_template_with_out_of_line_definition() -> Result<()> {
+        // See also an end-to-end test in the `test/templates/out_of_line_definition`
+        // directory.
+        let ir = ir_from_cc(
+            r#" #pragma clang lifetime_elision
+                template <typename T>
+                class MyTemplate final {
+                 public:
+                  static MyTemplate Create(T value);
+                  const T& value() const;
+
+                 private:
+                  T value_;
+                };
+
+                using MyTypeAlias = MyTemplate<int>; "#,
+        )?;
+
+        let BindingsTokens { rs_api_impl, .. } = generate_bindings_tokens(&ir)?;
+
+        // Even though the member functions above are *not* defined inline (e.g.
+        // IR::Func::is_inline is false), they still need to have thunks generated for
+        // them (to force/guarantee that the class template and its members get
+        // instantiated).  This is also covered in the following end-to-end
+        // tests:
+        // - test/templates/out_of_line_definition/ - without a thunk, the template
+        //   won't be instantiated and Rust bindings won't be able to call the member
+        //   function (there will be no instantiation of the member function in the C++
+        //   object files)
+        // - test/templates/definition_in_cc/ - the instantiation happens in the .cc
+        //   file and therefore the thunk is not *required* (but it doesn't hurt to have
+        //   the thunk)
+        assert_cc_matches!(
+            rs_api_impl,
+            quote! {
+                extern "C" class MyTemplate<int>
+                __rust_thunk___ZN10MyTemplateIiE6CreateEi___test_testing_target(int value) {
+                    return MyTemplate<int>::Create(std::forward<decltype(value)>(value));
+                }
+            }
+        );
+        assert_cc_matches!(
+            rs_api_impl,
+            quote! {
+                extern "C" int const&
+                __rust_thunk___ZNK10MyTemplateIiE5valueEv___test_testing_target(
+                        const class MyTemplate<int>*__this) {
+                    return __this->value();
+                }
+            }
+        );
+        Ok(())
+    }
+
+    #[test]
     fn test_simple_struct() -> Result<()> {
         let ir = ir_from_cc(&tokens_to_string(quote! {
             struct SomeStruct final {