Fixing infinite recursion in ConvertTemplateSpecializationType.
Before this CL, a class template with a member function that
refers back to the same class ...
```cpp
template <typename T>
struct MyTemplate {
void MyMethod(const MyTemplate<T>& other) const {}
};
using MyTypeAlias = MyTemplate<int>;
```
... would cause an infinite recursion:
```txt
...
Importer::ConvertTemplateSpecializationType() <- importing MyTemplate<int>
Importer::ConvertType() at importer.cc:746:12
Importer::ConvertQualType() at importer.cc:767:7
Importer::ConvertType() at importer.cc:694:5
Importer::ConvertQualType() at importer.cc:767:7
FunctionDeclImporter::Import() at function.cc:69:29 <- importing MyMethod
DeclImporterBase<clang::FunctionDecl>::ImportDecl() ...
Importer::ImportDecl() at importer.cc:461:26
Importer::GetDeclItem() at importer.cc:452:38
Importer::GetItemIdsInSourceOrder() at importer.cc:331:9
CXXRecordDeclImporter::Import() at cxx_record.cc:137:25
DeclImporterBase<clang::CXXRecordDecl>::ImportDecl() ...
Importer::ImportDecl() at importer.cc:461:26
Importer::GetDeclItem() at importer.cc:452:38
Importer::ConvertTemplateSpecializationType() <- importing MyTemplate<int>
...
```
The problem above was introduced in https://github.com/google/crubit/commit/14732b2920053fadd06917ffd9201cd6d64704ff which started to call
ConvertTemplateSpecializationType from ConvertType (rather than from
TypedefNameDeclImporter::Import which won't be recursively reentered
in this scenario).
This CL prevents the infinite recursion by checking `known_types_` to
check if the `ClassTemplateSpecializationDecl` has been already
processed before.
Interestingly, I've wondered about the infinite recursion before, but
dismissed this problem, because I thought that it is sufficiently tested
via `this` parameter of instance methods (which already had some test
coverage). It turned out that this coverage is insufficient, because
`method_decl->getThisType()` doesn't use `TemplateSpecializationType`.
PiperOrigin-RevId: 453939417
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 48cd313..f663bbe 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -595,6 +595,9 @@
type_string));
}
+ if (HasBeenAlreadySuccessfullyImported(specialization_decl))
+ return ConvertTypeDecl(specialization_decl);
+
if (IsFromCurrentTarget(specialization_decl) &&
!specialization_decl->isExplicitSpecialization()) {
// Store implicit `specialization_decl`s so that they will get included in
diff --git a/rs_bindings_from_cc/test/templates/method_params/BUILD b/rs_bindings_from_cc/test/templates/method_params/BUILD
new file mode 100644
index 0000000..96085b3
--- /dev/null
+++ b/rs_bindings_from_cc/test/templates/method_params/BUILD
@@ -0,0 +1,16 @@
+"""End-to-end example of using type aliases to fully-instantiated templates."""
+
+load("@rules_rust//rust:defs.bzl", "rust_test")
+
+licenses(["notice"])
+
+cc_library(
+ name = "method_params",
+ hdrs = ["method_params.h"],
+)
+
+rust_test(
+ name = "main",
+ srcs = ["test.rs"],
+ cc_deps = [":method_params"],
+)
diff --git a/rs_bindings_from_cc/test/templates/method_params/method_params.h b/rs_bindings_from_cc/test/templates/method_params/method_params.h
new file mode 100644
index 0000000..5ef6af7
--- /dev/null
+++ b/rs_bindings_from_cc/test/templates/method_params/method_params.h
@@ -0,0 +1,34 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_TEMPLATES_METHOD_PARAMS_METHOD_PARAMS_H_
+#define THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_TEMPLATES_METHOD_PARAMS_METHOD_PARAMS_H_
+
+#pragma clang lifetime_elision
+
+template <typename T>
+class MyTemplate {
+ public:
+ static MyTemplate Create(T value) {
+ MyTemplate result;
+ result.value_ = value;
+ return result;
+ }
+
+ T AddOneOtherItem(const MyTemplate<T>& other) const {
+ return value_ + other.value_;
+ }
+
+ T AddThreeOtherItems(const MyTemplate<T>& a, const MyTemplate<T>& b,
+ const MyTemplate<T>& c) const {
+ return value_ + a.value_ + b.value_ + c.value_;
+ }
+
+ private:
+ T value_;
+};
+
+using MyTypeAlias = MyTemplate<int>;
+
+#endif // THIRD_PARTY_CRUBIT_RS_BINDINGS_FROM_CC_TEST_TEMPLATES_METHOD_PARAMS_METHOD_PARAMS_H_
diff --git a/rs_bindings_from_cc/test/templates/method_params/test.rs b/rs_bindings_from_cc/test/templates/method_params/test.rs
new file mode 100644
index 0000000..633719f
--- /dev/null
+++ b/rs_bindings_from_cc/test/templates/method_params/test.rs
@@ -0,0 +1,31 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn test_const_ref_to_self() {
+ // Among other things, this test provides coverage against infinite
+ // recursion around Importer::ConvertTemplateSpecializationType which
+ // in the past might have happened when a member function's parameters
+ // would refer back to the same class template specialization.
+ let s1 = method_params::MyTypeAlias::Create(1);
+ let s2 = method_params::MyTypeAlias::Create(2);
+ assert_eq!(1 + 2, s1.AddOneOtherItem(&s2));
+ }
+
+ fn test_repeating_parameter_type() {
+ // Among other things, this test provides coverage for the (not currently
+ // implemented, but still considered for the future) mangling algorithm
+ // that is required as part of the "Function template" approach from
+ // `thunks_for_class_template_member_functions.md`. In particular,
+ // repeated parameter type should be subject to https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression
+ // (which was missed in the initial prototype of this approach).
+ let s1 = method_params::MyTypeAlias::Create(1);
+ let s2 = method_params::MyTypeAlias::Create(2);
+ let s3 = method_params::MyTypeAlias::Create(3);
+ let s4 = method_params::MyTypeAlias::Create(4);
+ assert_eq!(1 + 2 + 3 + 4, s1.AddThreeOtherItems(&s2, &s3, &s4));
+ }
+}