Skip to content

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

3 participants