martong created this revision.
martong added reviewers: teemperor, balazske.
Herald added subscribers: cfe-commits, gamesh411, Szelethus, dkrupp, rnkovacs.
Herald added a reviewer: a.sidorin.
Herald added a reviewer: shafik.
Herald added a project: clang.
martong requested review of this revision.

CXXDeductionGuideDecl with a local typedef has its own copy of the
TypedefDecl with the CXXDeductionGuideDecl as the DeclContext of that
TypedefDecl.

  template <typename T> struct A {
    typedef T U;
    A(U, T);
  };
  A a{(int)0, (int)0};

Related discussion on cfe-dev:
http://lists.llvm.org/pipermail/cfe-dev/2020-November/067252.html

Without this fix, when we import the CXXDeductionGuideDecl (via
VisitFunctionDecl) then before creating the Decl we must import the
FunctionType. However, the first parameter's type is the afore mentioned
local typedef. So, we then start importing the TypedefDecl whose
DeclContext is the CXXDeductionGuideDecl itself. The infinite loop is
formed.

  #0 
clang::ASTNodeImporter::VisitCXXDeductionGuideDecl(clang::CXXDeductionGuideDecl*)
 clang/lib/AST/ASTImporter.cpp:3543:0
  #1 clang::declvisitor::Base<std::add_pointer, clang::ASTNodeImporter, 
llvm::Expected<clang::Decl*> >::Visit(clang::Decl*) 
/home/egbomrt/WORK/llvm5/build/debug/tools/clang/include/clang/AST/DeclNodes.inc:405:0
  #2 clang::ASTImporter::ImportImpl(clang::Decl*) 
clang/lib/AST/ASTImporter.cpp:8038:0
  #3 clang::ASTImporter::Import(clang::Decl*) 
clang/lib/AST/ASTImporter.cpp:8200:0
  #4 clang::ASTImporter::ImportContext(clang::DeclContext*) 
clang/lib/AST/ASTImporter.cpp:8297:0
  #5 clang::ASTNodeImporter::ImportDeclContext(clang::Decl*, 
clang::DeclContext*&, clang::DeclContext*&) clang/lib/AST/ASTImporter.cpp:1852:0
  #6 clang::ASTNodeImporter::ImportDeclParts(clang::NamedDecl*, 
clang::DeclContext*&, clang::DeclContext*&, clang::DeclarationName&, 
clang::NamedDecl*&, clang::SourceLocation&) clang/lib/AST/ASTImporter.cpp:1628:0
  #7 clang::ASTNodeImporter::VisitTypedefNameDecl(clang::TypedefNameDecl*, 
bool) clang/lib/AST/ASTImporter.cpp:2419:0
  #8 clang::ASTNodeImporter::VisitTypedefDecl(clang::TypedefDecl*) 
clang/lib/AST/ASTImporter.cpp:2500:0
  #9 clang::declvisitor::Base<std::add_pointer, clang::ASTNodeImporter, 
llvm::Expected<clang::Decl*> >::Visit(clang::Decl*) 
/home/egbomrt/WORK/llvm5/build/debug/tools/clang/include/clang/AST/DeclNodes.inc:315:0
  #10 clang::ASTImporter::ImportImpl(clang::Decl*) 
clang/lib/AST/ASTImporter.cpp:8038:0
  #11 clang::ASTImporter::Import(clang::Decl*) 
clang/lib/AST/ASTImporter.cpp:8200:0
  #12 llvm::Expected<clang::TypedefNameDecl*> 
clang::ASTNodeImporter::import<clang::TypedefNameDecl>(clang::TypedefNameDecl*) 
clang/lib/AST/ASTImporter.cpp:165:0
  #13 clang::ASTNodeImporter::VisitTypedefType(clang::TypedefType const*) 
clang/lib/AST/ASTImporter.cpp:1304:0
  #14 clang::TypeVisitor<clang::ASTNodeImporter, 
llvm::Expected<clang::QualType> >::Visit(clang::Type const*) 
/home/egbomrt/WORK/llvm5/build/debug/tools/clang/include/clang/AST/TypeNodes.inc:74:0
  #15 clang::ASTImporter::Import(clang::QualType) 
clang/lib/AST/ASTImporter.cpp:8071:0
  #16 llvm::Expected<clang::QualType> 
clang::ASTNodeImporter::import<clang::QualType>(clang::QualType const&) 
clang/lib/AST/ASTImporter.cpp:179:0
  #17 clang::ASTNodeImporter::VisitFunctionProtoType(clang::FunctionProtoType 
const*) clang/lib/AST/ASTImporter.cpp:1244:0
  #18 clang::TypeVisitor<clang::ASTNodeImporter, 
llvm::Expected<clang::QualType> >::Visit(clang::Type const*) 
/home/egbomrt/WORK/llvm5/build/debug/tools/clang/include/clang/AST/TypeNodes.inc:47:0
  #19 clang::ASTImporter::Import(clang::QualType) 
clang/lib/AST/ASTImporter.cpp:8071:0
  #20 llvm::Expected<clang::QualType> 
clang::ASTNodeImporter::import<clang::QualType>(clang::QualType const&) 
clang/lib/AST/ASTImporter.cpp:179:0
  #21 clang::QualType 
clang::ASTNodeImporter::importChecked<clang::QualType>(llvm::Error&, 
clang::QualType const&) clang/lib/AST/ASTImporter.cpp:198:0
  #22 clang::ASTNodeImporter::VisitFunctionDecl(clang::FunctionDecl*) 
clang/lib/AST/ASTImporter.cpp:3313:0
  #23 
clang::ASTNodeImporter::VisitCXXDeductionGuideDecl(clang::CXXDeductionGuideDecl*)
 clang/lib/AST/ASTImporter.cpp:3543:0

The fix is to first create the TypedefDecl and only then start to import
the DeclContext.
Basically, we could do this during the import of all other Decls (not
just for typedefs). But it seems, there is only one another AST
construct that has a similar cycle: a struct defined as a function
parameter:

  int struct_in_proto(struct data_t{int a;int b;} *d);

In that case, however, we had decided to return simply with an error
back then because that seemed to be a very rare construct.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D92209

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
@@ -5924,6 +5924,22 @@
   EXPECT_TRUE(ToD->isExplicit());
 }
 
+TEST_P(ImportFunctions, CTADWithLocalTypedef) {
+  Decl *TU = getTuDecl(
+      R"(
+      template <typename T> struct A {
+        typedef T U;
+        A(U, T);
+      };
+      A a{(int)0, (int)0};
+      )",
+      Lang_CXX17, "input.cc");
+  auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
+      TU, cxxDeductionGuideDecl());
+  auto *ToD = Import(FromD, Lang_CXX17);
+  ASSERT_TRUE(ToD);
+}
+
 INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
                         DefaultTestValuesForRunOptions, );
 
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -385,6 +385,8 @@
     ExpectedType VisitObjCObjectPointerType(const ObjCObjectPointerType *T);
 
     // Importing declarations
+    Error ImportDeclParts(NamedDecl *D, DeclarationName &Name, NamedDecl *&ToD,
+                          SourceLocation &Loc);
     Error ImportDeclParts(
         NamedDecl *D, DeclContext *&DC, DeclContext *&LexicalDC,
         DeclarationName &Name, NamedDecl *&ToD, SourceLocation &Loc);
@@ -1644,6 +1646,25 @@
   return Error::success();
 }
 
+Error ASTNodeImporter::ImportDeclParts(NamedDecl *D, DeclarationName &Name,
+                                       NamedDecl *&ToD, SourceLocation &Loc) {
+
+  // Import the name of this declaration.
+  if (Error Err = importInto(Name, D->getDeclName()))
+    return Err;
+
+  // Import the location of this declaration.
+  if (Error Err = importInto(Loc, D->getLocation()))
+    return Err;
+
+  ToD = cast_or_null<NamedDecl>(Importer.GetAlreadyImportedOrNull(D));
+  if (ToD)
+    if (Error Err = ASTNodeImporter(*this).ImportDefinitionIfNeeded(D, ToD))
+      return Err;
+
+  return Error::success();
+}
+
 Error ASTNodeImporter::ImportDefinitionIfNeeded(Decl *FromD, Decl *ToD) {
   if (!FromD)
     return Error::success();
@@ -2412,22 +2433,29 @@
 ExpectedDecl
 ASTNodeImporter::VisitTypedefNameDecl(TypedefNameDecl *D, bool IsAlias) {
   // Import the major distinguishing characteristics of this typedef.
-  DeclContext *DC, *LexicalDC;
   DeclarationName Name;
   SourceLocation Loc;
   NamedDecl *ToD;
-  if (Error Err = ImportDeclParts(D, DC, LexicalDC, Name, ToD, Loc))
+  // Do not import the DeclContext, we will import it once the TypedefNameDecl
+  // is created.
+  if (Error Err = ImportDeclParts(D, Name, ToD, Loc))
     return std::move(Err);
   if (ToD)
     return ToD;
 
+  DeclContext *DC = cast_or_null<DeclContext>(
+      Importer.GetAlreadyImportedOrNull(cast<Decl>(D->getDeclContext())));
+  DeclContext *LexicalDC =
+      cast_or_null<DeclContext>(Importer.GetAlreadyImportedOrNull(
+          cast<Decl>(D->getLexicalDeclContext())));
+
   // If this typedef is not in block scope, determine whether we've
   // seen a typedef with the same name (that we can merge with) or any
   // other entity by that name (which name lookup could conflict with).
   // Note: Repeated typedefs are not valid in C99:
   // 'typedef int T; typedef int T;' is invalid
   // We do not care about this now.
-  if (!DC->isFunctionOrMethod()) {
+  if (DC && !DC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
     unsigned IDNS = Decl::IDNS_Ordinary;
     auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
@@ -2484,8 +2512,16 @@
       Name.getAsIdentifierInfo(), ToTypeSourceInfo))
     return ToTypedef;
 
-  ToTypedef->setAccess(D->getAccess());
+  // Import the DeclContext and set it to the Typedef.
+  if (!DC || !LexicalDC)
+    if ((Err = ImportDeclContext(D, DC, LexicalDC)))
+      return std::move(Err);
+  ToTypedef->setDeclContext(DC);
   ToTypedef->setLexicalDeclContext(LexicalDC);
+  // Add to the lookupTable because we could not do that in MapImported.
+  Importer.AddToLookupTable(ToTypedef);
+
+  ToTypedef->setAccess(D->getAccess());
 
   // Templated declarations should not appear in DeclContext.
   TypeAliasDecl *FromAlias = IsAlias ? cast<TypeAliasDecl>(D) : nullptr;
@@ -9189,7 +9225,11 @@
   // This mapping should be maintained only in this function. Therefore do not
   // check for additional consistency.
   ImportedFromDecls[To] = From;
-  AddToLookupTable(To);
+  // In the case of TypedefNameDecl we create the Decl first and only then we
+  // import and set its DeclContext. So, the DC is still not set when we reach
+  // here from GetImportedOrCreateDecl.
+  if (To->getDeclContext())
+    AddToLookupTable(To);
   return To;
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to