Skip to content

[DebugInfo] Produce debuginfo for nested types when the outer type is [[clang::standalone_debug]] #146175

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

khuey
Copy link
Contributor

@khuey khuey commented Jun 27, 2025

Without this, it's impossible to get debuginfo for a nested type that is unused (or that clang mistakenly believes is unused due to e.g. deficiencies in -debug-info-kind=limited), even by marking the "unused" nested type itself with [[clang::standalone_debug]]. This makes writing pretty-printers and similar things difficult.

The [[clang::standalone_debug]] attribute is not propagated to the nested type. But after applying [[clang::standalone_debug]] to the outer type to ensure the nested type appears in the debuginfo at all, a developer can also additionally apply the attribute to the nested type if desired.

… [[clang::standalone_debug]].

Without this, it's impossible to get debuginfo for a nested type that is unused (or that
clang mistakenly believes is unused due to e.g. deficiencies in -debug-info-kind=limited),
even by marking the "unused" nested type itself with [[clang::standalone_debug]].
This makes writing pretty-printers and similar things difficult.

The [[clang::standalone_debug]] attribute is not propagated to the nested type.
But after applying [[clang::standalone_debug]] to the outer type to ensure the nested type
appears in the debuginfo at all, a developer can also additionally apply the attribute to the
nested type if desired.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. debuginfo labels Jun 27, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 27, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-debuginfo

Author: Kyle Huey (khuey)

Changes

Without this, it's impossible to get debuginfo for a nested type that is unused (or that clang mistakenly believes is unused due to e.g. deficiencies in -debug-info-kind=limited), even by marking the "unused" nested type itself with [[clang::standalone_debug]]. This makes writing pretty-printers and similar things difficult.

The [[clang::standalone_debug]] attribute is not propagated to the nested type. But after applying [[clang::standalone_debug]] to the outer type to ensure the nested type appears in the debuginfo at all, a developer can also additionally apply the attribute to the nested type if desired.


Full diff: https://github.com/llvm/llvm-project/pull/146175.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+4-3)
  • (added) clang/test/CodeGenCXX/standalone-debug-attribute-limited.cpp (+29)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 7ab0e2fdaa731..d28bbcd9d9835 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2075,12 +2075,13 @@ void CGDebugInfo::CollectRecordFields(
 
         // Bump field number for next field.
         ++fieldNo;
-      } else if (CGM.getCodeGenOpts().EmitCodeView) {
+      } else if (CGM.getCodeGenOpts().EmitCodeView ||
+                 record->hasAttr<StandaloneDebugAttr>()) {
         // Debug info for nested types is included in the member list only for
-        // CodeView.
+        // CodeView and types with the standalone_debug attribute.
         if (const auto *nestedType = dyn_cast<TypeDecl>(I)) {
           // MSVC doesn't generate nested type for anonymous struct/union.
-          if (isa<RecordDecl>(I) &&
+          if (CGM.getCodeGenOpts().EmitCodeView && isa<RecordDecl>(I) &&
               cast<RecordDecl>(I)->isAnonymousStructOrUnion())
             continue;
           if (!nestedType->isImplicit() &&
diff --git a/clang/test/CodeGenCXX/standalone-debug-attribute-limited.cpp b/clang/test/CodeGenCXX/standalone-debug-attribute-limited.cpp
new file mode 100644
index 0000000000000..43ec35db18c7d
--- /dev/null
+++ b/clang/test/CodeGenCXX/standalone-debug-attribute-limited.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -DSETATTR=0 -triple x86_64-unknown-linux-gnu -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=DEBUG
+// RUN: %clang_cc1 -DSETATTR=1 -triple x86_64-unknown-linux-gnu -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=WITHATTR
+// Use -debug-info-kind=limited because an unused type will never have a used ctor.
+
+#if SETATTR
+#define STANDALONEDEBUGATTR __attribute__((standalone_debug))
+#else
+#define STANDALONEDEBUGATTR
+#endif
+
+struct STANDALONEDEBUGATTR TypeWithNested {
+  struct Unused1 {
+  };
+  struct STANDALONEDEBUGATTR Unused2 {
+  };
+
+  int value = 0;
+};
+void f(TypeWithNested s) {}
+// DEBUG:  !DICompositeType(tag: DW_TAG_structure_type, name: "TypeWithNested"
+// DEBUG-NOT:  !DICompositeType(tag: DW_TAG_structure_type, name: "Unused1"
+// DEBUG-NOT:  !DICompositeType(tag: DW_TAG_structure_type, name: "Unused2"
+// WITHATTR:  !DICompositeType(tag: DW_TAG_structure_type, name: "TypeWithNested"
+// WITHATTR:  !DICompositeType(tag: DW_TAG_structure_type, name: "Unused1"
+// The STANDALONEDEBUGATTR isn't propagated to the nested type by default, so
+// it should still be a forward declaration.
+// WITHATTR-SAME: DIFlagFwdDecl
+// WITHATTR:  !DICompositeType(tag: DW_TAG_structure_type, name: "Unused2"
+// WITHATTR-NOT: DIFlagFwdDecl

@llvmbot
Copy link
Member

llvmbot commented Jun 27, 2025

@llvm/pr-subscribers-clang-codegen

Author: Kyle Huey (khuey)

Changes

Without this, it's impossible to get debuginfo for a nested type that is unused (or that clang mistakenly believes is unused due to e.g. deficiencies in -debug-info-kind=limited), even by marking the "unused" nested type itself with [[clang::standalone_debug]]. This makes writing pretty-printers and similar things difficult.

The [[clang::standalone_debug]] attribute is not propagated to the nested type. But after applying [[clang::standalone_debug]] to the outer type to ensure the nested type appears in the debuginfo at all, a developer can also additionally apply the attribute to the nested type if desired.


Full diff: https://github.com/llvm/llvm-project/pull/146175.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+4-3)
  • (added) clang/test/CodeGenCXX/standalone-debug-attribute-limited.cpp (+29)
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 7ab0e2fdaa731..d28bbcd9d9835 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2075,12 +2075,13 @@ void CGDebugInfo::CollectRecordFields(
 
         // Bump field number for next field.
         ++fieldNo;
-      } else if (CGM.getCodeGenOpts().EmitCodeView) {
+      } else if (CGM.getCodeGenOpts().EmitCodeView ||
+                 record->hasAttr<StandaloneDebugAttr>()) {
         // Debug info for nested types is included in the member list only for
-        // CodeView.
+        // CodeView and types with the standalone_debug attribute.
         if (const auto *nestedType = dyn_cast<TypeDecl>(I)) {
           // MSVC doesn't generate nested type for anonymous struct/union.
-          if (isa<RecordDecl>(I) &&
+          if (CGM.getCodeGenOpts().EmitCodeView && isa<RecordDecl>(I) &&
               cast<RecordDecl>(I)->isAnonymousStructOrUnion())
             continue;
           if (!nestedType->isImplicit() &&
diff --git a/clang/test/CodeGenCXX/standalone-debug-attribute-limited.cpp b/clang/test/CodeGenCXX/standalone-debug-attribute-limited.cpp
new file mode 100644
index 0000000000000..43ec35db18c7d
--- /dev/null
+++ b/clang/test/CodeGenCXX/standalone-debug-attribute-limited.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -DSETATTR=0 -triple x86_64-unknown-linux-gnu -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=DEBUG
+// RUN: %clang_cc1 -DSETATTR=1 -triple x86_64-unknown-linux-gnu -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s --check-prefix=WITHATTR
+// Use -debug-info-kind=limited because an unused type will never have a used ctor.
+
+#if SETATTR
+#define STANDALONEDEBUGATTR __attribute__((standalone_debug))
+#else
+#define STANDALONEDEBUGATTR
+#endif
+
+struct STANDALONEDEBUGATTR TypeWithNested {
+  struct Unused1 {
+  };
+  struct STANDALONEDEBUGATTR Unused2 {
+  };
+
+  int value = 0;
+};
+void f(TypeWithNested s) {}
+// DEBUG:  !DICompositeType(tag: DW_TAG_structure_type, name: "TypeWithNested"
+// DEBUG-NOT:  !DICompositeType(tag: DW_TAG_structure_type, name: "Unused1"
+// DEBUG-NOT:  !DICompositeType(tag: DW_TAG_structure_type, name: "Unused2"
+// WITHATTR:  !DICompositeType(tag: DW_TAG_structure_type, name: "TypeWithNested"
+// WITHATTR:  !DICompositeType(tag: DW_TAG_structure_type, name: "Unused1"
+// The STANDALONEDEBUGATTR isn't propagated to the nested type by default, so
+// it should still be a forward declaration.
+// WITHATTR-SAME: DIFlagFwdDecl
+// WITHATTR:  !DICompositeType(tag: DW_TAG_structure_type, name: "Unused2"
+// WITHATTR-NOT: DIFlagFwdDecl

@khuey
Copy link
Contributor Author

khuey commented Jun 27, 2025

@dwblaikie could you take a look at this?

@dwblaikie
Copy link
Collaborator

I don't think this is the right direction.

standalone_debug shouldn't affect truly unused types - I don't think nested or unnested unused types should be treated the same in this regard (because they are treated the same in every other regard, I think)

Got a particular example of the problem? Perhaps there's some other solutions we could consider

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category debuginfo
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants