Type aliases bound to fully-instantiated template
PiperOrigin-RevId: 449002160
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 9713fae..49c30cb 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -28,6 +28,7 @@
#include "common/check.h"
#include "common/status_macros.h"
#include "lifetime_annotations/type_lifetimes.h"
+#include "rs_bindings_from_cc/ast_util.h"
#include "rs_bindings_from_cc/bazel_types.h"
#include "rs_bindings_from_cc/ir.h"
#include "clang/AST/ASTContext.h"
@@ -41,6 +42,7 @@
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
+#include "clang/Sema/Sema.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
@@ -52,6 +54,17 @@
constexpr absl::string_view kTypeStatusPayloadUrl =
"type.googleapis.com/devtools.rust.cc_interop.rs_binding_from_cc.type";
+// Checks if the return value from `GetDeclItem` indicates that the import was
+// successful.
+absl::Status CheckImportStatus(const std::optional<IR::Item>& item) {
+ if (!item.has_value()) {
+ return absl::InvalidArgumentError("The import has been skipped");
+ }
+ if (auto* unsupported = std::get_if<UnsupportedItem>(&*item)) {
+ return absl::InvalidArgumentError(unsupported->message);
+ }
+ return absl::OkStatus();
+}
}
// A mapping of C++ standard types to their equivalent Rust types.
@@ -322,6 +335,27 @@
return ordered_item_ids;
}
+std::vector<ItemId> Importer::GetOrderedItemIdsOfTemplateInstantiations()
+ const {
+ std::vector<SourceLocationComparator::OrderedItemId> items;
+ items.reserve(class_template_instantiations_for_current_target_.size());
+ for (const auto* decl : class_template_instantiations_for_current_target_) {
+ items.push_back(
+ {decl->getSourceRange(), GetDeclOrder(decl), GenerateItemId(decl)});
+ }
+
+ clang::SourceManager& sm = ctx_.getSourceManager();
+ auto compare_locations = SourceLocationComparator(sm);
+ llvm::sort(items, compare_locations);
+
+ std::vector<ItemId> ordered_item_ids;
+ ordered_item_ids.reserve(items.size());
+ for (const auto& ordered_item : items) {
+ ordered_item_ids.push_back(std::get<2>(ordered_item));
+ }
+ return ordered_item_ids;
+}
+
void Importer::ImportFreeComments() {
clang::SourceManager& sm = ctx_.getSourceManager();
for (const auto& header : invocation_.entry_headers_) {
@@ -369,6 +403,11 @@
}
invocation_.ir_.top_level_item_ids =
GetItemIdsInSourceOrder(translation_unit_decl);
+
+ // TODO(b/222001243): Consider placing the generated template instantiations
+ // into a separate namespace (maybe `crubit::instantiated_templates` ?).
+ llvm::copy(GetOrderedItemIdsOfTemplateInstantiations(),
+ std::back_inserter(invocation_.ir_.top_level_item_ids));
}
void Importer::ImportDeclsFromDeclContext(
@@ -408,6 +447,12 @@
}
BazelLabel Importer::GetOwningTarget(const clang::Decl* decl) const {
+ // Template instantiations need to be generated in the target that triggered
+ // the instantiation (not in the target where the template is defined).
+ if (IsFullClassTemplateSpecializationOrChild(decl)) {
+ return invocation_.target_;
+ }
+
clang::SourceManager& source_manager = ctx_.getSourceManager();
auto source_location = decl->getLocation();
@@ -504,6 +549,53 @@
.column = sm.getSpellingColumnNumber(loc)};
}
+absl::StatusOr<MappedType> Importer::ConvertTemplateSpecializationType(
+ const clang::TemplateSpecializationType* type) {
+ // Qualifiers are handled separately in TypeMapper::ConvertQualType().
+ std::string type_string = clang::QualType(type, 0).getAsString();
+
+ auto* specialization_decl =
+ clang::dyn_cast_or_null<clang::ClassTemplateSpecializationDecl>(
+ type->getAsCXXRecordDecl());
+ if (!specialization_decl) {
+ return absl::InvalidArgumentError(absl::Substitute(
+ "Template specialization '$0' without an associated record decl "
+ "is not supported.",
+ type_string));
+ }
+
+ if (IsFromCurrentTarget(specialization_decl) &&
+ !specialization_decl->isExplicitSpecialization()) {
+ // Store implicit `specialization_decl`s so that they will get included in
+ // IR::top_level_item_ids.
+ class_template_instantiations_for_current_target_.insert(
+ specialization_decl);
+ }
+
+ // `Sema::isCompleteType` will try to instantiate the class template as a
+ // side-effect and we rely on this here. `decl->getDefinition()` can
+ // return nullptr before the call to sema and return its definition
+ // afterwards.
+ if (!sema_.isCompleteType(specialization_decl->getLocation(),
+ ctx_.getRecordType(specialization_decl))) {
+ return absl::InvalidArgumentError(absl::Substitute(
+ "'$0' template specialization is incomplete", type_string));
+ }
+
+ // TODO(lukasza): Limit specialization depth? (e.g. using
+ // `isSpecializationDepthGreaterThan` from earlier prototypes).
+
+ absl::Status import_status =
+ CheckImportStatus(GetDeclItem(specialization_decl));
+ if (!import_status.ok()) {
+ return absl::InvalidArgumentError(absl::Substitute(
+ "Failed to create bindings for template specialization type $0: $1",
+ type_string, import_status.message()));
+ }
+
+ return type_mapper_.ConvertTypeDecl(specialization_decl);
+}
+
absl::StatusOr<MappedType> TypeMapper::ConvertTypeDecl(
const clang::TypeDecl* decl) const {
if (!known_type_decls_.contains(decl)) {
@@ -623,6 +715,9 @@
} else if (const auto* typedef_type =
type->getAsAdjusted<clang::TypedefType>()) {
return ConvertTypeDecl(typedef_type->getDecl());
+ } else if (const auto* subst_type =
+ type->getAs<clang::SubstTemplateTypeParmType>()) {
+ return ConvertQualType(subst_type->getReplacementType(), lifetimes);
}
return absl::UnimplementedError(absl::StrCat(
@@ -654,6 +749,26 @@
}
std::string Importer::GetMangledName(const clang::NamedDecl* named_decl) const {
+ if (auto specialization_decl =
+ clang::dyn_cast<clang::ClassTemplateSpecializationDecl>(named_decl)) {
+ // Itanium mangler produces valid Rust identifiers, use it to generate a
+ // name for this instantiation.
+ llvm::SmallString<128> storage;
+ llvm::raw_svector_ostream buffer(storage);
+ mangler_->mangleTypeName(ctx_.getRecordType(specialization_decl), buffer);
+
+ // The Itanium mangler does not provide a way to get the mangled
+ // representation of a type. Instead, we call mangleTypeName() that
+ // returns the name of the RTTI typeinfo symbol, and remove the _ZTS
+ // prefix. Then we prepend __CcTemplateInst to reduce chances of conflict
+ // with regular C and C++ structs.
+ constexpr llvm::StringRef kZtsPrefix = "_ZTS";
+ constexpr llvm::StringRef kCcTemplatePrefix = "__CcTemplateInst";
+ CRUBIT_CHECK(buffer.str().take_front(4) == kZtsPrefix);
+ return llvm::formatv("{0}{1}", kCcTemplatePrefix,
+ buffer.str().drop_front(kZtsPrefix.size()));
+ }
+
clang::GlobalDecl decl;
// There are only three named decl types that don't work with the GlobalDecl