Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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;
Expand All @@ -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());
Expand Down
36 changes: 31 additions & 5 deletions clang/test/CodeGenCXX/debug-info-structured-binding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Loading