diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index c4e931e220f69..e9698915d1940 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -6245,17 +6245,21 @@ ExpectedDecl ASTNodeImporter::VisitVarTemplateDecl(VarTemplateDecl *D) { D->getTemplatedDecl())) continue; if (IsStructuralMatch(D, FoundTemplate)) { - // The Decl in the "From" context has a definition, but in the - // "To" context we already have a definition. + // FIXME Check for ODR error if the two definitions have + // different initializers? VarTemplateDecl *FoundDef = getTemplateDefinition(FoundTemplate); - if (D->isThisDeclarationADefinition() && FoundDef) - // FIXME Check for ODR error if the two definitions have - // different initializers? - return Importer.MapImported(D, FoundDef); - if (FoundTemplate->getDeclContext()->isRecord() && - D->getDeclContext()->isRecord()) - return Importer.MapImported(D, FoundTemplate); - + if (D->getDeclContext()->isRecord()) { + assert(FoundTemplate->getDeclContext()->isRecord() && + "Member variable template imported as non-member, " + "inconsistent imported AST?"); + if (FoundDef) + return Importer.MapImported(D, FoundDef); + if (!D->isThisDeclarationADefinition()) + return Importer.MapImported(D, FoundTemplate); + } else { + if (FoundDef && D->isThisDeclarationADefinition()) + return Importer.MapImported(D, FoundDef); + } FoundByLookup = FoundTemplate; break; } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 5f4d8d040772c..10a8b3be11485 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -5050,6 +5050,59 @@ TEST_P(ImportFriendClasses, RecordVarTemplateDecl) { EXPECT_EQ(ToTUX, ToX); } +TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateDeclConflict) { + getToTuDecl( + R"( + template + constexpr int X = 1; + )", + Lang_CXX14); + + Decl *FromTU = getTuDecl( + R"( + template + constexpr int X = 2; + )", + Lang_CXX14, "input1.cc"); + auto *FromX = FirstDeclMatcher().match( + FromTU, varTemplateDecl(hasName("X"))); + auto *ToX = Import(FromX, Lang_CXX11); + // FIXME: This import should fail. + EXPECT_TRUE(ToX); +} + +TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateStaticDefinition) { + Decl *ToTU = getToTuDecl( + R"( + struct A { + template + static int X; + }; + )", + Lang_CXX14); + auto *ToX = FirstDeclMatcher().match( + ToTU, varTemplateDecl(hasName("X"))); + ASSERT_FALSE(ToX->isThisDeclarationADefinition()); + + Decl *FromTU = getTuDecl( + R"( + struct A { + template + static int X; + }; + template + int A::X = 2; + )", + Lang_CXX14, "input1.cc"); + auto *FromXDef = LastDeclMatcher().match( + FromTU, varTemplateDecl(hasName("X"))); + ASSERT_TRUE(FromXDef->isThisDeclarationADefinition()); + auto *ToXDef = Import(FromXDef, Lang_CXX14); + EXPECT_TRUE(ToXDef); + EXPECT_TRUE(ToXDef->isThisDeclarationADefinition()); + EXPECT_EQ(ToXDef->getPreviousDecl(), ToX); +} + TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateParameterDeclContext) { constexpr auto Code = R"(