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

via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 9 01:41:12 PDT 2024


Author: Michael Buch
Date: 2024-08-09T09:41:09+01:00
New Revision: 310a9f3f257f1f7a41958b792b27e69a0237218f

URL: https://github.com/llvm/llvm-project/commit/310a9f3f257f1f7a41958b792b27e69a0237218f
DIFF: https://github.com/llvm/llvm-project/commit/310a9f3f257f1f7a41958b792b27e69a0237218f.diff

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

This patch is motivated by the debug-info issue in
https://github.com/llvm/llvm-project/issues/48909. Clang is currently
emitting the `DW_AT_artificial` attribute on debug-info entries for
structured bindings whereas GCC does not. GCC's interpretation of the
DWARF spec is more user-friendly in this regard, so we would like to do
the same in Clang. [`CGDebugInfo` uses `isImplicit` to decide which
variables to mark
artificial](https://github.com/llvm/llvm-project/blob/0c4023ae3b64c54ff51947e9776aee0e963c5635/clang/lib/CodeGen/CGDebugInfo.cpp#L4783-L4784)
(e.g., `ImplicitParamDecls`, compiler-generated variables, etc.). But
for the purposes of debug-info, when we say "artificial", what we really
mean in many cases is: "not explicitly spelled out in source".
`VarDecl`s that back tuple-like bindings are [technically
compiler-generated](https://github.com/llvm/llvm-project/issues/48909#issuecomment-2238976579),
but that is a confusing notion for debug-info, since these bindings
*are* spelled out in source. The [documentation for
`isImplicit`](https://github.com/llvm/llvm-project/blob/68a0d0c76223736351fd7c452bca3ba9d80ca342/clang/include/clang/AST/DeclBase.h#L596-L600)
does to some extent imply that implicit variables aren't written in
source.

This patch adds another condition to deciding whether a `VarDecl` should
be marked artificial. Specifically, don't treat structured bindings as
artificial.

**Main alternatives considered**
1. Don't use `isImplicit` in `CGDebugInfo` when determining whether to
add `DW_AT_artificial`. Instead use some other property of the AST that
would tell us whether a node was explicitly spelled out in source or not
* Considered using `SourceRange` or `SourceLocation` to tell us this,
but didn't find a good way to, e.g., correctly identify that the
implicit `this` parameter wasn't spelled out in source (as opposed to an
unnamed parameter in a function declaration)
2. We could've also added a bit to `VarDeclBitFields` that indicates
that a `VarDecl` is a holding var, but the use-case didn't feel like
good enough justification for this
3. Don't set the `VarDecl` introduced as part of a tuple-like
decomposition as implicit.
* This may affect AST matching/traversal and this specific use-case
wasn't enough to justify such a change

Fixes https://github.com/llvm/llvm-project/issues/48909

Added: 
    

Modified: 
    clang/lib/CodeGen/CGDebugInfo.cpp
    clang/test/CodeGenCXX/debug-info-structured-binding.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 8d57c87e97e393..7ad3088f0ab756 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 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());
+}
+
+/// 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) {
+  // 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),
@@ -4766,11 +4796,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;
@@ -4787,13 +4816,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/test/CodeGenCXX/debug-info-structured-binding.cpp b/clang/test/CodeGenCXX/debug-info-structured-binding.cpp
index c88a5cdaa20e78..5fbd54c16382c0 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; }
+};
+
+// 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 unsigned value = 2;
+};
+
+template<unsigned, typename T> struct tuple_element { using type = int; };
+} // namespace std
+
 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;
 }


        


More information about the cfe-commits mailing list