[clang] [clang][DebugInfo] Don't mark structured bindings as artificial (PR #100355)

Michael Buch via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 8 16:56:13 PDT 2024


https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/100355

>From 2b1255de05856e4c79f58d3e4071384ba80a881d Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 18 Jul 2024 16:26:16 -0500
Subject: [PATCH 1/3] [clang][Sema] Don't mark VarDecls of bindings in
 tuple-like decompositions as implicit

---
 clang/lib/AST/DeclCXX.cpp                     |  1 -
 clang/lib/Sema/SemaDeclCXX.cpp                |  1 -
 .../debug-info-structured-binding.cpp         | 36 ++++++++++--
 .../ASTMatchers/ASTMatchersTraversalTest.cpp  | 57 +++++++++++++++++++
 4 files changed, 88 insertions(+), 7 deletions(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 72d68f39a97a5..2c22f42a5e0a8 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3327,7 +3327,6 @@ VarDecl *BindingDecl::getHoldingVar() const {
     return nullptr;
 
   auto *VD = cast<VarDecl>(DRE->getDecl());
-  assert(VD->isImplicit() && "holding var for binding decl not implicit");
   return VD;
 }
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index f24912cde275a..f4cc1fc4583aa 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1316,7 +1316,6 @@ static bool checkTupleLikeDecomposition(Sema &S,
         S.Context.getTrivialTypeSourceInfo(T, Loc), Src->getStorageClass());
     RefVD->setLexicalDeclContext(Src->getLexicalDeclContext());
     RefVD->setTSCSpec(Src->getTSCSpec());
-    RefVD->setImplicit();
     if (Src->isInlineSpecified())
       RefVD->setInlineSpecified();
     RefVD->getLexicalDeclContext()->addHiddenDecl(RefVD);
diff --git a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
index c88a5cdaa20e7..4a3126a36598d 100644
--- a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
@@ -5,20 +5,46 @@
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_2:[0-9]+]], !DIExpression(DW_OP_plus_uconst, 4),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_3:[0-9]+]], !DIExpression(DW_OP_deref),
 // CHECK: #dbg_declare(ptr %{{[0-9]+}}, ![[VAR_4:[0-9]+]], !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 4),
+// CHECK: #dbg_declare(ptr %z1, ![[VAR_5:[0-9]+]], !DIExpression()
+// CHECK: #dbg_declare(ptr %z2, ![[VAR_6:[0-9]+]], !DIExpression()
 // CHECK: ![[VAR_0]] = !DILocalVariable(name: "a"
-// CHECK: ![[VAR_1]] = !DILocalVariable(name: "x1"
-// CHECK: ![[VAR_2]] = !DILocalVariable(name: "y1"
-// CHECK: ![[VAR_3]] = !DILocalVariable(name: "x2"
-// CHECK: ![[VAR_4]] = !DILocalVariable(name: "y2"
+// CHECK: ![[VAR_1]] = !DILocalVariable(name: "x1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_2]] = !DILocalVariable(name: "y1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_3]] = !DILocalVariable(name: "x2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_4]] = !DILocalVariable(name: "y2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_5]] = !DILocalVariable(name: "z1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
+// CHECK: ![[VAR_6]] = !DILocalVariable(name: "z2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
 
 struct A {
   int x;
   int y;
 };
 
+struct B {
+  int w;
+  int z;
+  template<int> int get();
+  template<> int get<0>() { return w; }
+  template<> int get<1>() { return z; }
+};
+
+namespace std {
+template<typename T> struct tuple_size {
+};
+template<>
+struct tuple_size<B> {
+    static constexpr int value = 2;
+};
+
+template<int, typename T> struct tuple_element {};
+template<> struct tuple_element<0, B> { using type = int; };
+template<> struct tuple_element<1, B> { using type = int; };
+}
+
 int f() {
   A a{10, 20};
   auto [x1, y1] = a;
   auto &[x2, y2] = a;
-  return x1 + y1 + x2 + y2;
+  auto [z1, z2] = B{1, 2};
+  return x1 + y1 + x2 + y2 + z1 + z2;
 }
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index 47a71134d5027..06ac07cb12509 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -3163,6 +3163,63 @@ void foo()
         matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
                              true, {"-std=c++17"}));
   }
+
+  Code = R"cpp(
+struct Pair
+{
+    int x, y;
+};
+
+// Note: these utilities are required to force binding to tuple like structure
+namespace std
+{
+    template <typename E>
+    struct tuple_size
+    {
+    };
+
+    template <>
+    struct tuple_size<Pair>
+    {
+        static constexpr unsigned value = 2;
+    };
+
+    template <unsigned I, class T>
+    struct tuple_element
+    {
+        using type = int;
+    };
+
+};
+
+template <unsigned I>
+int &&get(Pair &&p);
+
+void decompTuple()
+{
+    Pair p{1, 2};
+    auto [a, b] = p;
+
+    a = 3;
+}
+  )cpp";
+
+  {
+    auto M = bindingDecl(hasName("a"));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++17"}));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+                             true, {"-std=c++17"}));
+  }
+  {
+    auto M = varDecl(hasName("a"));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++17"}));
+    EXPECT_TRUE(
+        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
+                             true, {"-std=c++17"}));
+  }
 }
 
 TEST(Traversal, traverseNoImplicit) {

>From 05e48902a3306eec6490c492a65fe75e34603972 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Wed, 24 Jul 2024 00:10:21 +0100
Subject: [PATCH 2/3] fixup! detect holding vars in CGDebugInfo; revert to
 setting isImplicit for bindings

---
 clang/lib/AST/DeclCXX.cpp                     |  1 +
 clang/lib/CodeGen/CGDebugInfo.cpp             | 41 +++++++++++--
 clang/lib/Sema/SemaDeclCXX.cpp                |  1 +
 .../debug-info-structured-binding.cpp         | 10 ++--
 .../ASTMatchers/ASTMatchersTraversalTest.cpp  | 57 -------------------
 5 files changed, 42 insertions(+), 68 deletions(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 2c22f42a5e0a8..72d68f39a97a5 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -3327,6 +3327,7 @@ VarDecl *BindingDecl::getHoldingVar() const {
     return nullptr;
 
   auto *VD = cast<VarDecl>(DRE->getDecl());
+  assert(VD->isImplicit() && "holding var for binding decl not implicit");
   return VD;
 }
 
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 3d8a715b692de..8b261df1ea8b8 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -21,6 +21,7 @@
 #include "TargetInfo.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclFriend.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
@@ -79,6 +80,35 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) {
   return D->hasAttr<AlignedAttr>() ? D->getMaxAlignment() : 0;
 }
 
+/// Returns true if \ref VD is a VarDecl that is a holding variable
+/// (aka a VarDecl retrieved using \ref BindingDecl::getHoldingVar).
+static bool IsDecomposedVarDecl(VarDecl const *VD) {
+  auto const *Init = VD->getInit();
+  if (!Init)
+    return false;
+
+  auto const *RefExpr =
+      llvm::dyn_cast_or_null<DeclRefExpr>(Init->IgnoreUnlessSpelledInSource());
+  if (!RefExpr)
+    return false;
+
+  return llvm::dyn_cast_or_null<DecompositionDecl>(RefExpr->getDecl());
+}
+
+/// Return true if \ref VD is a compiler-generated variable
+/// and should be treated as artificial for the purposes
+/// of debug-info generation.
+static bool IsArtificial(VarDecl const *VD) {
+  // Tuple-like bindings are marked as implicit despite
+  // being spelled out in source. Don't treat them as artificial
+  // variables.
+  if (IsDecomposedVarDecl(VD))
+    return false;
+
+  return VD->isImplicit() || (isa<Decl>(VD->getDeclContext()) &&
+                              cast<Decl>(VD->getDeclContext())->isImplicit());
+}
+
 CGDebugInfo::CGDebugInfo(CodeGenModule &CGM)
     : CGM(CGM), DebugKind(CGM.getCodeGenOpts().getDebugInfo()),
       DebugTypeExtRefs(CGM.getCodeGenOpts().DebugTypeExtRefs),
@@ -4753,11 +4783,10 @@ llvm::DILocalVariable *CGDebugInfo::EmitDeclare(const VarDecl *VD,
   if (VD->hasAttr<NoDebugAttr>())
     return nullptr;
 
-  bool Unwritten =
-      VD->isImplicit() || (isa<Decl>(VD->getDeclContext()) &&
-                           cast<Decl>(VD->getDeclContext())->isImplicit());
+  const bool VarIsArtificial = IsArtificial(VD);
+
   llvm::DIFile *Unit = nullptr;
-  if (!Unwritten)
+  if (!VarIsArtificial)
     Unit = getOrCreateFile(VD->getLocation());
   llvm::DIType *Ty;
   uint64_t XOffset = 0;
@@ -4774,13 +4803,13 @@ llvm::DILocalVariable *CGDebugInfo::EmitDeclare(const VarDecl *VD,
   // Get location information.
   unsigned Line = 0;
   unsigned Column = 0;
-  if (!Unwritten) {
+  if (!VarIsArtificial) {
     Line = getLineNumber(VD->getLocation());
     Column = getColumnNumber(VD->getLocation());
   }
   SmallVector<uint64_t, 13> Expr;
   llvm::DINode::DIFlags Flags = llvm::DINode::FlagZero;
-  if (VD->isImplicit())
+  if (VarIsArtificial)
     Flags |= llvm::DINode::FlagArtificial;
 
   auto Align = getDeclAlignIfRequired(VD, CGM.getContext());
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index f4cc1fc4583aa..f24912cde275a 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1316,6 +1316,7 @@ static bool checkTupleLikeDecomposition(Sema &S,
         S.Context.getTrivialTypeSourceInfo(T, Loc), Src->getStorageClass());
     RefVD->setLexicalDeclContext(Src->getLexicalDeclContext());
     RefVD->setTSCSpec(Src->getTSCSpec());
+    RefVD->setImplicit();
     if (Src->isInlineSpecified())
       RefVD->setInlineSpecified();
     RefVD->getLexicalDeclContext()->addHiddenDecl(RefVD);
diff --git a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
index 4a3126a36598d..5fbd54c16382c 100644
--- a/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
+++ b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
@@ -28,18 +28,18 @@ struct B {
   template<> int get<1>() { return z; }
 };
 
+// Note: the following declarations are necessary for decomposition of tuple-like
+// structured bindings
 namespace std {
 template<typename T> struct tuple_size {
 };
 template<>
 struct tuple_size<B> {
-    static constexpr int value = 2;
+    static constexpr unsigned value = 2;
 };
 
-template<int, typename T> struct tuple_element {};
-template<> struct tuple_element<0, B> { using type = int; };
-template<> struct tuple_element<1, B> { using type = int; };
-}
+template<unsigned, typename T> struct tuple_element { using type = int; };
+} // namespace std
 
 int f() {
   A a{10, 20};
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index 06ac07cb12509..47a71134d5027 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -3163,63 +3163,6 @@ void foo()
         matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
                              true, {"-std=c++17"}));
   }
-
-  Code = R"cpp(
-struct Pair
-{
-    int x, y;
-};
-
-// Note: these utilities are required to force binding to tuple like structure
-namespace std
-{
-    template <typename E>
-    struct tuple_size
-    {
-    };
-
-    template <>
-    struct tuple_size<Pair>
-    {
-        static constexpr unsigned value = 2;
-    };
-
-    template <unsigned I, class T>
-    struct tuple_element
-    {
-        using type = int;
-    };
-
-};
-
-template <unsigned I>
-int &&get(Pair &&p);
-
-void decompTuple()
-{
-    Pair p{1, 2};
-    auto [a, b] = p;
-
-    a = 3;
-}
-  )cpp";
-
-  {
-    auto M = bindingDecl(hasName("a"));
-    EXPECT_TRUE(
-        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++17"}));
-    EXPECT_TRUE(
-        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
-                             true, {"-std=c++17"}));
-  }
-  {
-    auto M = varDecl(hasName("a"));
-    EXPECT_TRUE(
-        matchesConditionally(Code, traverse(TK_AsIs, M), true, {"-std=c++17"}));
-    EXPECT_TRUE(
-        matchesConditionally(Code, traverse(TK_IgnoreUnlessSpelledInSource, M),
-                             true, {"-std=c++17"}));
-  }
 }
 
 TEST(Traversal, traverseNoImplicit) {

>From 9a830ffca04eea63c3752c24377891addd9abb64 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 25 Jul 2024 00:18:14 +0100
Subject: [PATCH 3/3] fixup! fix comment

---
 clang/lib/CodeGen/CGDebugInfo.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 8b261df1ea8b8..4c59c7b65000b 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -80,8 +80,8 @@ static uint32_t getDeclAlignIfRequired(const Decl *D, const ASTContext &Ctx) {
   return D->hasAttr<AlignedAttr>() ? D->getMaxAlignment() : 0;
 }
 
-/// Returns true if \ref VD is a VarDecl that is a holding variable
-/// (aka a VarDecl retrieved using \ref BindingDecl::getHoldingVar).
+/// Returns true if \ref VD is a a holding variable (aka a
+/// VarDecl retrieved using \ref BindingDecl::getHoldingVar).
 static bool IsDecomposedVarDecl(VarDecl const *VD) {
   auto const *Init = VD->getInit();
   if (!Init)
@@ -95,7 +95,7 @@ static bool IsDecomposedVarDecl(VarDecl const *VD) {
   return llvm::dyn_cast_or_null<DecompositionDecl>(RefExpr->getDecl());
 }
 
-/// Return true if \ref VD is a compiler-generated variable
+/// Returns true if \ref VD is a compiler-generated variable
 /// and should be treated as artificial for the purposes
 /// of debug-info generation.
 static bool IsArtificial(VarDecl const *VD) {



More information about the cfe-commits mailing list