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

Reply via email to