From f3a2cfc10394143bbe30a6af00be8b68c1c0d607 Mon Sep 17 00:00:00 2001
From: David Blaikie <dblaikie@gmail.com>
Date: Thu, 10 Feb 2022 14:40:25 -0800
Subject: [PATCH] DebugInfo: Don't simplify any template referencing a lambda

Lambda names aren't entirely canonical (as demonstrated by the
cross-project-test added here) at the moment (we should fix that for a
bunch of reasons) - even if the template referencing them is
non-simplified, other names referencing /that/ template can't be
simplified either because type units might cause a different template to
be picked up that would conflict with the expected name.

(other than for roundtripping precision, it'd be OK to simplify types
that reference types that reference lambdas - but best be consistent
between the roundtrip/verify mode and the actual simplified template
names mode)
---
 clang/lib/CodeGen/CGDebugInfo.cpp             | 58 +++++++++++++++----
 .../debug-info-simple-template-names.cpp      |  8 ++-
 .../simplified_template_names.cpp             |  1 +
 ...template_names_noncanonical_type_units.cpp | 40 +++++++++++++
 4 files changed, 94 insertions(+), 13 deletions(-)
 create mode 100644 cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/simplified_template_names_noncanonical_type_units.cpp

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 1a9080604a79..cca5576aac07 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -4975,6 +4975,52 @@ llvm::DIGlobalVariableExpression *CGDebugInfo::CollectAnonRecordDecls(
   return GVE;
 }
 
+static bool ReferencesAnonymousEntity(ArrayRef<TemplateArgument> Args);
+static bool ReferencesAnonymousEntity(RecordType *RT) {
+  // Unnamed classes/lambdas can't be reconstituted due to a lack of column
+  // info we produce in the DWARF, so we can't get Clang's full name back.
+  // But so long as it's not one of those, it doesn't matter if some sub-type
+  // of the record (a template parameter) can't be reconstituted - because the
+  // un-reconstitutable type itself will carry its own name.
+  const auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
+  if (!RD)
+    return false;
+  if (!RD->getIdentifier())
+    return true;
+  auto *TSpecial = dyn_cast<ClassTemplateSpecializationDecl>(RD);
+  if (!TSpecial)
+    return false;
+  return ReferencesAnonymousEntity(TSpecial->getTemplateArgs().asArray());
+}
+static bool ReferencesAnonymousEntity(ArrayRef<TemplateArgument> Args) {
+  return llvm::any_of(Args, [&](const TemplateArgument &TA) {
+    switch (TA.getKind()) {
+    case TemplateArgument::Pack:
+      return ReferencesAnonymousEntity(TA.getPackAsArray());
+    case TemplateArgument::Type: {
+      struct ReferencesAnonymous
+          : public RecursiveASTVisitor<ReferencesAnonymous> {
+        bool ReferencesAnonymous = false;
+        bool VisitRecordType(RecordType *RT) {
+          if (ReferencesAnonymousEntity(RT)) {
+            ReferencesAnonymous = true;
+            return false;
+          }
+          return true;
+        }
+      };
+      ReferencesAnonymous RT;
+      RT.TraverseType(TA.getAsType());
+      if (RT.ReferencesAnonymous)
+        return true;
+      break;
+    }
+    default:
+      break;
+    }
+    return false;
+  });
+}
 namespace {
 struct ReconstitutableType : public RecursiveASTVisitor<ReconstitutableType> {
   bool Reconstitutable = true;
@@ -5002,16 +5048,8 @@ struct ReconstitutableType : public RecursiveASTVisitor<ReconstitutableType> {
     Reconstitutable &= !isNoexceptExceptionSpec(FT->getExceptionSpecType());
     return Reconstitutable;
   }
-  bool TraverseRecordType(RecordType *RT) {
-    // Unnamed classes/lambdas can't be reconstituted due to a lack of column
-    // info we produce in the DWARF, so we can't get Clang's full name back.
-    // But so long as it's not one of those, it doesn't matter if some sub-type
-    // of the record (a template parameter) can't be reconstituted - because the
-    // un-reconstitutable type itself will carry its own name.
-    const auto *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
-    if (!RD)
-      return true;
-    if (RD->isLambda() || !RD->getIdentifier()) {
+  bool VisitRecordType(RecordType *RT) {
+    if (ReferencesAnonymousEntity(RT)) {
       Reconstitutable = false;
       return false;
     }
diff --git a/clang/test/CodeGenCXX/debug-info-simple-template-names.cpp b/clang/test/CodeGenCXX/debug-info-simple-template-names.cpp
index 73a8f49967a7..344bd93020f6 100644
--- a/clang/test/CodeGenCXX/debug-info-simple-template-names.cpp
+++ b/clang/test/CodeGenCXX/debug-info-simple-template-names.cpp
@@ -48,9 +48,11 @@ void f() {
   // since we don't emit the column number. Also lambdas and unnamed classes are
   // ambiguous with each other - there's no DWARF that designates a lambda as
   // anything other than another unnamed class/struct.
-  auto A = [] {};
-  f1<decltype(A)>();
-  // CHECK: !DISubprogram(name: "f1<(lambda at {{.*}}debug-info-simple-template-names.cpp:[[# @LINE - 2]]:12)>",
+  auto Lambda = [] {};
+  f1<decltype(Lambda)>();
+  // CHECK: !DISubprogram(name: "f1<(lambda at {{.*}}debug-info-simple-template-names.cpp:[[# @LINE - 2]]:17)>",
+  f1<t1<t1<decltype(Lambda)>>>();
+  // CHECK: !DISubprogram(name: "f1<t1<t1<(lambda at {{.*}}> > >",
   struct {
   } unnamed_struct;
   f1<decltype(unnamed_struct)>();
diff --git a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/simplified_template_names.cpp b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/simplified_template_names.cpp
index 61ac76db832c..d608039959df 100644
--- a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/simplified_template_names.cpp
+++ b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/simplified_template_names.cpp
@@ -216,6 +216,7 @@ int main() {
   f1<t3<t3<int>>>();
   f1<decltype(L)>();
   t3<decltype(L)> v1;
+  f1<t3<t3<decltype(L)>>>();
   f1<int(float)>();
   f1<void(...)>();
   f1<void(int, ...)>();
diff --git a/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/simplified_template_names_noncanonical_type_units.cpp b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/simplified_template_names_noncanonical_type_units.cpp
new file mode 100644
index 000000000000..65c6bba6746f
--- /dev/null
+++ b/cross-project-tests/debuginfo-tests/clang_llvm_roundtrip/simplified_template_names_noncanonical_type_units.cpp
@@ -0,0 +1,40 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: mkdir %t/incl
+// RUN: mv %t/header.h %t/incl/header.h
+// RUN: cd %t
+// RUN: %clang %target_itanium_abi_host_triple -g -o %t/a.out \
+// RUN:   -Xclang -gsimple-template-names=mangled \
+// RUN:   -Xclang -debug-forward-template-params \
+// RUN:   -std=c++20 -fdebug-types-section -I incl a.cpp b.cpp
+// RUN: llvm-dwarfdump --verify %t/a.out
+
+//--- header.h
+template <typename T> struct t1 {};
+inline auto f1() {
+  auto T = [] {};
+  t1<decltype(T)> v;
+  return v;
+}
+inline auto f2() {
+  struct {
+  } T;
+  t1<decltype(T)> v;
+  return v;
+}
+void a();
+//--- a.cpp
+#include "incl/header.h"
+template <typename T> void ft() {}
+void a() {
+  ft<decltype(f1())>();
+  ft<decltype(f2())>();
+}
+//--- b.cpp
+#include "header.h"
+template <typename T> void ft() {}
+int main() {
+  a();
+  ft<decltype(f1())>();
+  ft<decltype(f2())>();
+}
-- 
GitLab