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; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits