if (Importer.IsStructurallyEquivalent(D->getType(),
FoundField->getType())) {
Importer.MapImported(D, FoundField);
+ // In case of a FieldDecl of a ClassTemplateSpecializationDecl, the
+ // initializer of a FieldDecl might not had been instantiated in the
+ // "To" context. However, the "From" context might instantiated that,
+ // thus we have to merge that.
+ if (Expr *FromInitializer = D->getInClassInitializer()) {
+ // We don't have yet the initializer set.
+ if (FoundField->hasInClassInitializer() &&
+ !FoundField->getInClassInitializer()) {
+ Expr *ToInitializer = Importer.Import(FromInitializer);
+ if (!ToInitializer)
+ // We can't return a nullptr here,
+ // since we already mapped D as imported.
+ return FoundField;
+ FoundField->setInClassInitializer(ToInitializer);
+ }
+ }
return FoundField;
}
// Try to find an existing specialization with these template arguments.
void *InsertPos = nullptr;
- ClassTemplateSpecializationDecl *D2
- = ClassTemplate->findSpecialization(TemplateArgs, InsertPos);
- if (D2) {
- // We already have a class template specialization with these template
- // arguments.
+ ClassTemplateSpecializationDecl *D2 = nullptr;
+ ClassTemplatePartialSpecializationDecl *PartialSpec =
+ dyn_cast<ClassTemplatePartialSpecializationDecl>(D);
+ if (PartialSpec)
+ D2 = ClassTemplate->findPartialSpecialization(TemplateArgs, InsertPos);
+ else
+ D2 = ClassTemplate->findSpecialization(TemplateArgs, InsertPos);
+ ClassTemplateSpecializationDecl * const PrevDecl = D2;
+ RecordDecl *FoundDef = D2 ? D2->getDefinition() : nullptr;
+ if (FoundDef) {
+ if (!D->isCompleteDefinition()) {
+ // The "From" translation unit only had a forward declaration; call it
+ // the same declaration.
+ // TODO Handle the redecl chain properly!
+ return Importer.MapImported(D, FoundDef);
+ }
- // FIXME: Check for specialization vs. instantiation errors.
+ if (IsStructuralMatch(D, FoundDef)) {
- if (RecordDecl *FoundDef = D2->getDefinition()) {
- if (!D->isCompleteDefinition() || IsStructuralMatch(D, FoundDef)) {
- // The record types structurally match, or the "from" translation
- // unit only had a forward declaration anyway; call it the same
- // function.
- return Importer.MapImported(D, FoundDef);
- }
+ Importer.MapImported(D, FoundDef);
+
+ // Import those those default field initializers which have been
+ // instantiated in the "From" context, but not in the "To" context.
+ for (auto *FromField : D->fields())
+ Importer.Import(FromField);
+
+ // Import those methods which have been instantiated in the
+ // "From" context, but not in the "To" context.
+ for (CXXMethodDecl *FromM : D->methods())
+ Importer.Import(FromM);
+
+ // TODO Import instantiated default arguments.
+ // TODO Import instantiated exception specifications.
+ //
+ // Generally, ASTCommon.h/DeclUpdateKind enum gives a very good hint what
+ // else could be fused during an AST merge.
+
+ return FoundDef;
}
- } else {
- // Create a new specialization.
- if (auto *PartialSpec =
- dyn_cast<ClassTemplatePartialSpecializationDecl>(D)) {
- // Import TemplateArgumentListInfo
+ } else { // We either couldn't find any previous specialization in the "To"
+ // context, or we found one but without definition. Let's create a
+ // new specialization and register that at the class template.
+ if (PartialSpec) {
+ // Import TemplateArgumentListInfo.
TemplateArgumentListInfo ToTAInfo;
const auto &ASTTemplateArgs = *PartialSpec->getTemplateArgsAsWritten();
if (ImportTemplateArgumentListInfo(ASTTemplateArgs, ToTAInfo))
D2, D, Importer.getToContext(), D->getTagKind(), DC, StartLoc,
IdLoc, ToTPList, ClassTemplate,
llvm::makeArrayRef(TemplateArgs.data(), TemplateArgs.size()),
- ToTAInfo, CanonInjType, nullptr))
+ ToTAInfo, CanonInjType,
+ cast_or_null<ClassTemplatePartialSpecializationDecl>(PrevDecl)))
return D2;
- } else {
+ // Update InsertPos, because preceding import calls may have invalidated
+ // it by adding new specializations.
+ if (!ClassTemplate->findPartialSpecialization(TemplateArgs, InsertPos))
+ // Add this partial specialization to the class template.
+ ClassTemplate->AddPartialSpecialization(
+ cast<ClassTemplatePartialSpecializationDecl>(D2), InsertPos);
+
+ } else { // Not a partial specialization.
if (GetImportedOrCreateDecl(
D2, D, Importer.getToContext(), D->getTagKind(), DC, StartLoc,
- IdLoc, ClassTemplate, TemplateArgs, /*PrevDecl=*/nullptr))
+ IdLoc, ClassTemplate, TemplateArgs, PrevDecl))
return D2;
+
+ // Update InsertPos, because preceding import calls may have invalidated
+ // it by adding new specializations.
+ if (!ClassTemplate->findSpecialization(TemplateArgs, InsertPos))
+ // Add this specialization to the class template.
+ ClassTemplate->AddSpecialization(D2, InsertPos);
}
D2->setSpecializationKind(D->getSpecializationKind());
- // Add this specialization to the class template.
- ClassTemplate->AddSpecialization(D2, InsertPos);
-
// Import the qualifier, if any.
D2->setQualifierInfo(Importer.Import(D->getQualifierLoc()));
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTu, classTemplateDecl(hasName("declToImport")));
auto *ToD = Import(FromD, Lang_CXX);
-
+
auto Pattern = classTemplateDecl(
has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
-
+
auto *Class =
FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
}
+TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ int a{0}; // FieldDecl with InitListExpr
+ X(char) : a(3) {} // (1)
+ X(int) {} // (2)
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ // ClassTemplateSpec with ctor (1): FieldDecl without InitlistExpr
+ X<char> xc('c');
+ }
+ )", Lang_CXX11);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ // FieldDecl without InitlistExpr:
+ auto *ToField = *ToSpec->field_begin();
+ ASSERT_TRUE(ToField);
+ ASSERT_FALSE(ToField->getInClassInitializer());
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ // ClassTemplateSpec with ctor (2): FieldDecl WITH InitlistExpr
+ X<char> xc(1);
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ // FieldDecl with InitlistExpr:
+ auto *FromField = *FromSpec->field_begin();
+ ASSERT_TRUE(FromField);
+ ASSERT_TRUE(FromField->getInClassInitializer());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ // After the import, the FieldDecl has to be merged, thus it should have the
+ // InitListExpr.
+ EXPECT_TRUE(ToField->getInClassInitializer());
+}
+
+TEST_P(ASTImporterTestBase, MergeFunctionOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ void f() {}
+ void g() {}
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ X<char> x;
+ x.f();
+ }
+ )", Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ X<char> x;
+ x.g();
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ auto FunPattern = functionDecl(hasName("g"),
+ hasParent(classTemplateSpecializationDecl()));
+ auto *FromFun =
+ FirstDeclMatcher<FunctionDecl>().match(FromTU, FunPattern);
+ auto *ToFun =
+ FirstDeclMatcher<FunctionDecl>().match(ToTU, FunPattern);
+ ASSERT_TRUE(FromFun->hasBody());
+ ASSERT_FALSE(ToFun->hasBody());
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_TRUE(ToFun->hasBody());
+}
+
+TEST_P(ASTImporterTestBase,
+ ODRViolationOfClassTemplateSpecializationsShouldBeReported) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {};
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ template <>
+ struct X<char> {
+ int a;
+ };
+ void foo() {
+ X<char> x;
+ }
+ )",
+ Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ template <>
+ struct X<char> {
+ int b;
+ };
+ void foo() {
+ X<char> x;
+ }
+ )",
+ Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+
+ // We expect one (ODR) warning during the import.
+ EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+
+ // The second specialization is different from the first, thus it violates
+ // ODR, consequently we expect to keep the first specialization only, which is
+ // already in the "To" context.
+ EXPECT_TRUE(ImportedSpec);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl()));
+}
+
+TEST_P(ASTImporterTestBase, MergeCtorOfClassTemplateSpecialization) {
+ std::string ClassTemplate =
+ R"(
+ template <typename T>
+ struct X {
+ X(char) {}
+ X(int) {}
+ };
+ )";
+ Decl *ToTU = getToTuDecl(ClassTemplate +
+ R"(
+ void foo() {
+ X<char> x('c');
+ }
+ )", Lang_CXX11);
+ Decl *FromTU = getTuDecl(ClassTemplate +
+ R"(
+ void bar() {
+ X<char> x(1);
+ }
+ )", Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl(hasName("X")));
+ // Match the void(int) ctor.
+ auto CtorPattern =
+ cxxConstructorDecl(hasParameter(0, varDecl(hasType(asString("int")))),
+ hasParent(classTemplateSpecializationDecl()));
+ auto *FromCtor =
+ FirstDeclMatcher<CXXConstructorDecl>().match(FromTU, CtorPattern);
+ auto *ToCtor =
+ FirstDeclMatcher<CXXConstructorDecl>().match(ToTU, CtorPattern);
+ ASSERT_TRUE(FromCtor->hasBody());
+ ASSERT_FALSE(ToCtor->hasBody());
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ ASSERT_TRUE(ImportedSpec);
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(hasName("X")));
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_TRUE(ToCtor->hasBody());
+}
+
+TEST_P(ASTImporterTestBase,
+ ClassTemplatePartialSpecializationsShouldNotBeDuplicated) {
+ auto Code =
+ R"(
+ // primary template
+ template<class T1, class T2, int I>
+ class A {};
+
+ // partial specialization
+ template<class T, int I>
+ class A<T, T*, I> {};
+ )";
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+ auto *FromSpec =
+ FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
+ FromTU, classTemplatePartialSpecializationDecl());
+ auto *ToSpec =
+ FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl()));
+}
+
+TEST_P(ASTImporterTestBase, ClassTemplateSpecializationsShouldNotBeDuplicated) {
+ auto Code =
+ R"(
+ // primary template
+ template<class T1, class T2, int I>
+ class A {};
+
+ // full specialization
+ template<>
+ class A<int, int, 1> {};
+ )";
+ Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
+ Decl *FromTU = getTuDecl(Code, Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl());
+ auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_EQ(ImportedSpec, ToSpec);
+ EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl()));
+}
+
+TEST_P(ASTImporterTestBase, ClassTemplateFullAndPartialSpecsShouldNotBeMixed) {
+ std::string PrimaryTemplate =
+ R"(
+ template<class T1, class T2, int I>
+ class A {};
+ )";
+ auto PartialSpec =
+ R"(
+ template<class T, int I>
+ class A<T, T*, I> {};
+ )";
+ auto FullSpec =
+ R"(
+ template<>
+ class A<int, int, 1> {};
+ )";
+ Decl *ToTU = getToTuDecl(PrimaryTemplate + FullSpec, Lang_CXX11);
+ Decl *FromTU = getTuDecl(PrimaryTemplate + PartialSpec, Lang_CXX11);
+ auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, classTemplateSpecializationDecl());
+
+ auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
+ EXPECT_TRUE(ImportedSpec);
+ // Check the number of partial specializations.
+ EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
+ ToTU, classTemplatePartialSpecializationDecl()));
+ // Check the number of full specializations.
+ EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
+ ToTU, classTemplateSpecializationDecl(
+ unless(classTemplatePartialSpecializationDecl()))));
+}
+
struct DeclContextTest : ASTImporterTestBase {};
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {