-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[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
[clang][DebugInfo] Don't mark structured bindings as artificial #100355
Conversation
…tions as implicit
@llvm/pr-subscribers-debuginfo @llvm/pr-subscribers-clang Author: Michael Buch (Michael137) ChangesThis patch is motivated by the debug-info issue in #48909. Clang is currently emitting the This patch avoids marking the variables introduced as part of a decomposition as artificial, resolving the debug-info confusion. Affects on AST matchers/traversal
Main alternatives considered
Though this felt like we'd be over-relying on the structure of the AST (but maybe that's stable enough?). Alternatively we could've also added a bit to Fixes #48909 Full diff: https://github.com/llvm/llvm-project/pull/100355.diff 4 Files Affected:
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) {
|
Thanks for the patch! I would actually prefer option 2 here.
Note that I cannot comment on what make sense for debugs info, but I don't think modifying properties of the AST is reasonable, and your suggested option 2 is fine. The structure of the AST is not stable but whoever change is would have to fix the code anyway as it's all the same project so it's a non-issue (as long as you add sufficient test coverage) |
CC @zygoloid for design opinions. This holding variable is really weird in that it's both not spelled in source (the user doesn't give it a name) but is spelled in source (only exists because of a source construct). I think that makes it hard to know whether it should or should not be marked as implicit. |
I agree relying on the AST is fine w/ sufficient testing. |
Thanks all for the input! Happy to go with option 2 instead. I agree that changing the AST here just for |
Updated the PR and description with the alternative approach |
It looks like this is working for all other kinds of structured binding because |
IIUC the suggestion is to unify the tuple and non-tuple cases in We currently emit debug-info for structured bindings in 2 steps. The AFAICT, at some point in the codegen process we need to bail out early in the case of structured bindings and we have two options for doing so:
Despite having this weird 2-step emission for structured bindings, option 2 seems a bit more natural to me (also I'm not sure option 1 would change anything). |
ping |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this patch makes structured bindings not artificial, so they aren't hidden in debuggers by default. That sounds right to me.
This patch is motivated by the debug-info issue in #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
usesisImplicit
to decide which variables to mark artificial (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, but that is a confusing notion for debug-info, since these bindings are spelled out in source. The documentation forisImplicit
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
isImplicit
inCGDebugInfo
when determining whether to addDW_AT_artificial
. Instead use some other property of the AST that would tell us whether a node was explicitly spelled out in source or notSourceRange
orSourceLocation
to tell us this, but didn't find a good way to, e.g., correctly identify that the implicitthis
parameter wasn't spelled out in source (as opposed to an unnamed parameter in a function declaration)VarDeclBitFields
that indicates that aVarDecl
is a holding var, but the use-case didn't feel like good enough justification for thisVarDecl
introduced as part of a tuple-like decomposition as implicit.Fixes #48909