Consistently handle explicit and implicit class template specializations.
If a template specialization is used in a type alias (or in a parameter
type, etc.) Crubit's IR includes a corresponding `Record` as a top-level
item. OTOH, if a template specialization is *not* actually used, then
there is no need to include its `Record`.
Before this CL, the above was haphazardly achieved by checking
`ClassTemplateSpecializationDecl::isExplicitSpecialization` and skipping
explicit specializations, incorrectly assumming that such
specializations are not used in a type alias, etc.
After this CL, the above is achieved by skipping processing of
`ClassTemplateSpecializationDecl` in `GetCanonicalChildren` (and
therefore only processing such decls via
`Importer::ConvertTemplateSpecializationType`).
As a side-effect, this CL also fixes a bug, where we would assume that
all `ClassTemplateSpecializationDecl` have no `enclosing_namespace_id`,
but `GetCanonicalChildren` could still include such decls as children of
a namespace. The CL adds coverage for this bug under
`test/templates/extern_definition`.
PiperOrigin-RevId: 473297905
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 4aec2ba..d0874ea 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -325,6 +325,14 @@
continue;
}
+ // `CXXRecordDeclImporter::Import` supports class template specializations
+ // but such import should only be triggered when
+ // `Importer::ConvertTemplateSpecializationType` is called (which means that
+ // the specialization is actually used in an explicit instantiation via
+ // `cc_template!` macro, in a type alias, or as a parameter type of a
+ // function, etc.).
+ if (clang::isa<clang::ClassTemplateSpecializationDecl>(decl)) continue;
+
// In general we only import (and include as children) canonical decls.
// Namespaces are exempted to ensure that we process every one of
// (potential) multiple namespace blocks with the same name.
@@ -640,11 +648,8 @@
// 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));
- }
+ (void)sema_.isCompleteType(specialization_decl->getLocation(),
+ ctx_.getRecordType(specialization_decl));
// TODO(lukasza): Limit specialization depth? (e.g. using
// `isSpecializationDepthGreaterThan` from earlier prototypes).
@@ -657,13 +662,9 @@
type_string, import_status.message()));
}
- 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);
- }
+ // Store `specialization_decl`s so that they will get included in
+ // IR::top_level_item_ids.
+ class_template_instantiations_for_current_target_.insert(specialization_decl);
return ConvertTypeDecl(specialization_decl);
}