balazske created this revision. Herald added subscribers: martong, teemperor, gamesh411, Szelethus, dkrupp. Herald added a reviewer: a.sidorin. Herald added a reviewer: shafik. balazske requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
The assertion can happen if ASTImporter imports a CXXRecordDecl in a template and then imports another redeclaration of this declaration, while the first import is in progress. The process of first import did not set the "described template" yet and the second import finds the first declaration at setting the injected types. Setting the injected type requires in the assertion that the described template is set. The exact assertion was: clang/lib/AST/ASTContext.cpp:4411: clang::QualType clang::ASTContext::getInjectedClassNameType(clang::CXXRecordDecl*, clang::QualType) const: Assertion `NeedsInjectedClassNameType(Decl)' failed. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D94067 Files: clang/lib/AST/ASTImporter.cpp clang/unittests/AST/ASTImporterTest.cpp Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -6141,6 +6141,41 @@ EXPECT_EQ(ToAttr->getAnnotation(), "A"); } +TEST_P(ASTImporterOptionSpecificTestBase, + ImportOfTemplatedDeclWhenPreviousDeclHasNoDescribedTemplateSet) { + Decl *FromTU = getTuDecl( + R"( + + namespace std { + template<typename T> + class basic_stringbuf; + } + namespace std { + class char_traits; + template<typename T = char_traits> + class basic_stringbuf; + } + namespace std { + template<typename T> + class basic_stringbuf {}; + } + + )", + Lang_CXX11); + + auto *From1 = FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, + classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit()))); + auto *To1 = cast_or_null<ClassTemplateDecl>(Import(From1, Lang_CXX11)); + EXPECT_TRUE(To1); + + auto *From2 = LastDeclMatcher<ClassTemplateDecl>().match( + FromTU, + classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit()))); + auto *To2 = cast_or_null<ClassTemplateDecl>(Import(From2, Lang_CXX11)); + EXPECT_TRUE(To2); +} + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest, DefaultTestValuesForRunOptions, ); Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -2897,6 +2897,17 @@ getCanonicalForwardRedeclChain(D2CXX); for (auto *R : Redecls) { auto *RI = cast<CXXRecordDecl>(R); + // Skip this declaration if it is currently under import and + // incomplete. Because it is under import we will reach this place in + // its own import call. At import of a "templated" CXXRecordDecl the + // described template is imported first and that import call sets the + // described template. But before this is done other imports happen so + // this case may occur. + if (!RI->getDescribedTemplate()) + continue; + // Skip the declaration if injected type is already set. + if (isa<InjectedClassNameType>(RI->getTypeForDecl())) + continue; RI->setTypeForDecl(nullptr); // Below we create a new injected type and assign that to the // canonical decl, subsequent declarations in the chain will reuse
Index: clang/unittests/AST/ASTImporterTest.cpp =================================================================== --- clang/unittests/AST/ASTImporterTest.cpp +++ clang/unittests/AST/ASTImporterTest.cpp @@ -6141,6 +6141,41 @@ EXPECT_EQ(ToAttr->getAnnotation(), "A"); } +TEST_P(ASTImporterOptionSpecificTestBase, + ImportOfTemplatedDeclWhenPreviousDeclHasNoDescribedTemplateSet) { + Decl *FromTU = getTuDecl( + R"( + + namespace std { + template<typename T> + class basic_stringbuf; + } + namespace std { + class char_traits; + template<typename T = char_traits> + class basic_stringbuf; + } + namespace std { + template<typename T> + class basic_stringbuf {}; + } + + )", + Lang_CXX11); + + auto *From1 = FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, + classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit()))); + auto *To1 = cast_or_null<ClassTemplateDecl>(Import(From1, Lang_CXX11)); + EXPECT_TRUE(To1); + + auto *From2 = LastDeclMatcher<ClassTemplateDecl>().match( + FromTU, + classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit()))); + auto *To2 = cast_or_null<ClassTemplateDecl>(Import(From2, Lang_CXX11)); + EXPECT_TRUE(To2); +} + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest, DefaultTestValuesForRunOptions, ); Index: clang/lib/AST/ASTImporter.cpp =================================================================== --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -2897,6 +2897,17 @@ getCanonicalForwardRedeclChain(D2CXX); for (auto *R : Redecls) { auto *RI = cast<CXXRecordDecl>(R); + // Skip this declaration if it is currently under import and + // incomplete. Because it is under import we will reach this place in + // its own import call. At import of a "templated" CXXRecordDecl the + // described template is imported first and that import call sets the + // described template. But before this is done other imports happen so + // this case may occur. + if (!RI->getDescribedTemplate()) + continue; + // Skip the declaration if injected type is already set. + if (isa<InjectedClassNameType>(RI->getTypeForDecl())) + continue; RI->setTypeForDecl(nullptr); // Below we create a new injected type and assign that to the // canonical decl, subsequent declarations in the chain will reuse
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits