Skip to content

Conversation

@ykhatav
Copy link
Contributor

@ykhatav ykhatav commented Feb 18, 2025

For template classes with type parameters, the information of which member variables are of a "templated type" is lost.
The debugger cannot differentiate between "templated" types and "hardcoded" types. This is because both types have the same debug information, so there is no way that gdb can distinguish between them. This patch adds support for templates as type by representing the template type as a derived type.

@ykhatav ykhatav changed the title Template as type Add support for template as type parameter Feb 19, 2025
@ykhatav ykhatav marked this pull request as ready for review February 19, 2025 15:19
@ykhatav ykhatav requested a review from bwyma February 19, 2025 15:20
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. debuginfo llvm:ir labels Feb 19, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 19, 2025

@llvm/pr-subscribers-llvm-ir
@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-debuginfo

@llvm/pr-subscribers-clang

Author: ykhatav (ykhatav)

Changes

For template classes with type parameters, the information of which member variables are of a "templated type" is lost.
The debugger cannot differentiate between "templated" types and "hardcoded" types. This is because both types have the same debug information, so there is no way that gdb can distinguish between them. This patch adds support for templates as type by representing the template type as a derived type.


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

10 Files Affected:

  • (modified) clang/include/clang/Basic/CodeGenOptions.def (+1)
  • (modified) clang/include/clang/Driver/Options.td (+5)
  • (modified) clang/lib/CodeGen/CGDebugInfo.cpp (+16-3)
  • (modified) clang/lib/CodeGen/CGDebugInfo.h (+2)
  • (added) clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp (+52)
  • (modified) llvm/include/llvm/IR/DIBuilder.h (+6)
  • (modified) llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp (+2)
  • (modified) llvm/lib/IR/DIBuilder.cpp (+9)
  • (modified) llvm/lib/IR/Verifier.cpp (+6-2)
  • (added) llvm/test/DebugInfo/X86/template-as-type-param.ll (+119)
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index 386652d2efa9e..79c3df2e43eef 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -468,6 +468,7 @@ CODEGENOPT(CtorDtorReturnThis, 1, 0)
 /// Enables emitting Import Call sections on supported targets that can be used
 /// by the Windows kernel to enable import call optimization.
 CODEGENOPT(ImportCallOptimization, 1, 0)
+CODEGENOPT(DebugTemplateParameterAsType, 1, 0)
 
 /// FIXME: Make DebugOptions its own top-level .def file.
 #include "DebugOptions.def"
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index d8123cc39fdc9..32a88d42dde49 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9028,3 +9028,8 @@ def wasm_opt : Flag<["--"], "wasm-opt">,
   Group<m_Group>,
   HelpText<"Enable the wasm-opt optimizer (default)">,
   MarshallingInfoNegativeFlag<LangOpts<"NoWasmOpt">>;
+defm debug_template_parameter_as_type
+    : BoolFOption<"debug-template-parameter-as-type",
+                  CodeGenOpts<"DebugTemplateParameterAsType">,
+                  DefaultFalse, PosFlag<SetTrue>, NegFlag<SetFalse>,
+                  BothFlags<[], [CC1Option]>>;
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index db595796c067e..ce2d34dcd4ce3 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1003,6 +1003,13 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) {
   return DBuilder.createBasicType(BTName, Size, Encoding);
 }
 
+llvm::DIType *CGDebugInfo::CreateType(const SubstTemplateTypeParmType *Ty,
+                                      llvm::DIFile *U) {
+  llvm::DIType *debugType = getOrCreateType(Ty->getReplacementType(), U);
+  return DBuilder.createTemplateTypeParameterAsType(
+      U, Ty->getReplacedParameter()->getName(), debugType);
+}
+
 llvm::DIType *CGDebugInfo::CreateType(const BitIntType *Ty) {
 
   StringRef Name = Ty->isUnsigned() ? "unsigned _BitInt" : "_BitInt";
@@ -3611,7 +3618,8 @@ llvm::DILocation *CGDebugInfo::CreateTrapFailureMessageFor(
                                /*Scope=*/TrapSP, /*InlinedAt=*/TrapLocation);
 }
 
-static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
+static QualType UnwrapTypeForDebugInfo(QualType T, const CodeGenModule &CGM) {
+  const ASTContext &C = CGM.getContext();
   Qualifiers Quals;
   do {
     Qualifiers InnerQuals = T.getLocalQualifiers();
@@ -3664,6 +3672,8 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
       T = cast<MacroQualifiedType>(T)->getUnderlyingType();
       break;
     case Type::SubstTemplateTypeParm:
+      if (CGM.getCodeGenOpts().DebugTemplateParameterAsType)
+        return C.getQualifiedType(T.getTypePtr(), Quals);
       T = cast<SubstTemplateTypeParmType>(T)->getReplacementType();
       break;
     case Type::Auto:
@@ -3690,7 +3700,7 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
 }
 
 llvm::DIType *CGDebugInfo::getTypeOrNull(QualType Ty) {
-  assert(Ty == UnwrapTypeForDebugInfo(Ty, CGM.getContext()));
+  assert(Ty == UnwrapTypeForDebugInfo(Ty, CGM));
   auto It = TypeCache.find(Ty.getAsOpaquePtr());
   if (It != TypeCache.end()) {
     // Verify that the debug info still exists.
@@ -3729,7 +3739,7 @@ llvm::DIType *CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile *Unit) {
   });
 
   // Unwrap the type as needed for debug information.
-  Ty = UnwrapTypeForDebugInfo(Ty, CGM.getContext());
+  Ty = UnwrapTypeForDebugInfo(Ty, CGM);
 
   if (auto *T = getTypeOrNull(Ty))
     return T;
@@ -3866,6 +3876,9 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) {
   case Type::Decltype:
   case Type::PackIndexing:
   case Type::UnaryTransform:
+    if (Ty->getTypeClass() == Type::SubstTemplateTypeParm &&
+        CGM.getCodeGenOpts().DebugTemplateParameterAsType)
+      return CreateType(cast<SubstTemplateTypeParmType>(Ty), Unit);
     break;
   }
 
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 38f73eca561b7..5e28fd5bf9039 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -228,6 +228,8 @@ class CGDebugInfo {
   llvm::DIType *CreateType(const MemberPointerType *Ty, llvm::DIFile *F);
   llvm::DIType *CreateType(const AtomicType *Ty, llvm::DIFile *F);
   llvm::DIType *CreateType(const PipeType *Ty, llvm::DIFile *F);
+  llvm::DIType *CreateType(const SubstTemplateTypeParmType *Ty,
+                           llvm::DIFile *F);
   /// Get enumeration type.
   llvm::DIType *CreateEnumType(const EnumType *Ty);
   llvm::DIType *CreateTypeDefinition(const EnumType *Ty);
diff --git a/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp b/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp
new file mode 100644
index 0000000000000..3422ce8f1e2bd
--- /dev/null
+++ b/clang/test/CodeGenCXX/debug-info-temp-param-as-type.cpp
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited -fdebug-template-parameter-as-type -triple x86_64-apple-darwin %s -o - | FileCheck %s
+
+
+template <typename T>
+struct TClass {
+  TClass();
+  void foo();
+  T val_;
+  int val2_;
+};
+
+template <typename T>
+void TClass<T>::foo() {
+  T tVar = 1;
+  T* pT = &tVar;
+  tVar++;
+}
+
+template <typename T>
+T bar(T tp) {
+  return tp;
+}
+
+int main () {
+  TClass<int> a;
+  a.val2_ = 3;
+  a.foo();
+  auto A = bar(42);
+  TClass<double> b;
+  return 0;
+}
+
+// CHECK: [[INT:![0-9]+]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TClass<int>"
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val_",{{.*}}baseType: [[TPARAM:![0-9]+]]
+// CHECK: [[TPARAM]] = !DIDerivedType(tag: DW_TAG_template_type_parameter, name: "T", {{.*}}baseType: [[INT]])
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val2_",{{.*}}baseType: [[INT]]
+
+// CHECK: !DILocalVariable(name: "A",{{.*}}type: [[TPARAM]])
+
+// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "TClass<double>"
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "val_",{{.*}}baseType: [[TPARAM2:![0-9]+]]
+// CHECK: [[TPARAM2]] = !DIDerivedType(tag: DW_TAG_template_type_parameter, name: "T", {{.*}}baseType: [[DOUBLE:![0-9]+]])
+// CHECK: [[DOUBLE]] = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
+
+// CHECK: distinct !DISubprogram(name: "foo"
+// CHECK: !DILocalVariable(name: "tVar",{{.*}}type: [[TPARAM]])
+// CHECK: !DILocalVariable(name: "pT",{{.*}}type: [[TPTR:![0-9]+]]
+// CHECK: [[TPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[TPARAM]]
+
+// CHECK: distinct !DISubprogram(name: "bar<int>"
+// CHECK: !DILocalVariable(name: "tp",{{.*}}type: [[TPARAM]])
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index 6c479415b9ed2..bb9b9490fdcdd 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -550,6 +550,12 @@ namespace llvm {
                                                          StringRef Name,
                                                          DIType *Ty,
                                                          bool IsDefault);
+    /// \param Scope        Scope in which this type is defined.
+    /// \param Name         Type parameter name.
+    /// \param Ty           Parameter type.
+    DIDerivedType *createTemplateTypeParameterAsType(DIScope *Scope,
+                                                     StringRef Name,
+                                                     DIType *Ty);
 
     /// Create debugging information for template
     /// value parameter.
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index 0a8a1ad38c959..8557d833dfb0e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -520,6 +520,8 @@ void DwarfUnit::addTemplateParams(DIE &Buffer, DINodeArray TParams) {
       constructTemplateTypeParameterDIE(Buffer, TTP);
     else if (auto *TVP = dyn_cast<DITemplateValueParameter>(Element))
       constructTemplateValueParameterDIE(Buffer, TVP);
+    else if (auto *TDT = dyn_cast<DIDerivedType>(Element))
+      createTypeDIE(TDT->getScope(), Buffer, TDT);
   }
 }
 
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index 8f9462ab46d88..fc50077ba8736 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -458,6 +458,15 @@ DIBuilder::createObjCProperty(StringRef Name, DIFile *File, unsigned LineNumber,
                              SetterName, PropertyAttributes, Ty);
 }
 
+DIDerivedType *DIBuilder::createTemplateTypeParameterAsType(DIScope *Context,
+                                                            StringRef Name,
+                                                            DIType *Ty) {
+  return DIDerivedType::get(VMContext, dwarf::DW_TAG_template_type_parameter,
+                            Name, nullptr, 0, Context, Ty, 0, 0, 0,
+                            std::nullopt, std::nullopt, DINode::FlagZero,
+                            nullptr);
+}
+
 DITemplateTypeParameter *
 DIBuilder::createTemplateTypeParameter(DIScope *Context, StringRef Name,
                                        DIType *Ty, bool isDefault) {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 8432779c107de..669c3370d9fc4 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1241,6 +1241,7 @@ void Verifier::visitDIDerivedType(const DIDerivedType &N) {
               N.getTag() == dwarf::DW_TAG_inheritance ||
               N.getTag() == dwarf::DW_TAG_friend ||
               N.getTag() == dwarf::DW_TAG_set_type ||
+              N.getTag() == dwarf::DW_TAG_template_type_parameter ||
               N.getTag() == dwarf::DW_TAG_template_alias,
           "invalid tag", &N);
   if (N.getTag() == dwarf::DW_TAG_ptr_to_member_type) {
@@ -1288,8 +1289,11 @@ void Verifier::visitTemplateParams(const MDNode &N, const Metadata &RawParams) {
   auto *Params = dyn_cast<MDTuple>(&RawParams);
   CheckDI(Params, "invalid template params", &N, &RawParams);
   for (Metadata *Op : Params->operands()) {
-    CheckDI(Op && isa<DITemplateParameter>(Op), "invalid template parameter",
-            &N, Params, Op);
+    CheckDI(((Op) && (isa<DITemplateParameter>(Op))) ||
+                ((isa<DIDerivedType>(Op)) &&
+                 (dyn_cast<DIDerivedType>(Op)->getTag() ==
+                  dwarf::DW_TAG_template_type_parameter)),
+            "invalid template parameter", &N, Params, Op);
   }
 }
 
diff --git a/llvm/test/DebugInfo/X86/template-as-type-param.ll b/llvm/test/DebugInfo/X86/template-as-type-param.ll
new file mode 100644
index 0000000000000..25eaeb92fec3e
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/template-as-type-param.ll
@@ -0,0 +1,119 @@
+; RUN: llc  -mtriple=x86_64-unknown-linux-gnu %s -o %t -filetype=obj
+; RUN: llvm-dwarfdump %t | FileCheck %s
+;Source code for the IR below:
+;template <typename T>
+;struct A
+;{
+;  A () : val_ (), val2_ () { }
+;  T val_;
+;  int val2_;
+;};
+
+;int main (void)
+;{
+; A<int> a;
+;  a.val2_ = 3;
+; return 0;
+;}
+
+; CHECK: DW_TAG_structure_type
+; CHECK:      DW_AT_name	("A<int>")
+; CHECK-NEXT:  DW_AT_byte_size	(0x08)
+; CHECK-NEXT:  DW_AT_decl_file	("test.cpp")
+; CHECK-NEXT:  DW_AT_decl_line	(2)
+
+; CHECK-NOT: NULL
+
+; CHECK:[[TEMPLATE:0x[0-9a-f]*]]: DW_TAG_template_type_parameter
+; CHECK-NEXT: DW_AT_type      {{.*}} "int"
+; CHECK-NEXT: DW_AT_name	("T")
+
+; CHECK: DW_TAG_member
+; CHECK-NEXT: DW_AT_name  ("val_")
+; CHECK-NEXT: DW_AT_type ([[TEMPLATE]] "T")
+
+; CHECK: DW_TAG_member
+; CHECK-NEXT: DW_AT_name	("val2_")
+; CHECK-NEXT: DW_AT_type	{{.*}} "int"
+; ModuleID = 'test.cpp'
+source_filename = "test.cpp"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+%struct.A = type { i32, i32 }
+
+$_ZN1AIiEC2Ev = comdat any
+
+; Function Attrs: mustprogress noinline norecurse optnone uwtable
+define dso_local noundef i32 @main() #0 !dbg !22 {
+entry:
+  %retval = alloca i32, align 4
+  %a = alloca %struct.A, align 4
+  store i32 0, ptr %retval, align 4
+    #dbg_declare(ptr %a, !26, !DIExpression(), !27)
+  call void @_ZN1AIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %a), !dbg !27
+  %val2_ = getelementptr inbounds nuw %struct.A, ptr %a, i32 0, i32 1, !dbg !28
+  store i32 3, ptr %val2_, align 4, !dbg !29
+  ret i32 0, !dbg !30
+}
+
+; Function Attrs: mustprogress noinline nounwind optnone uwtable
+define linkonce_odr dso_local void @_ZN1AIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this) unnamed_addr #1 comdat align 2 !dbg !31 {
+entry:
+  %this.addr = alloca ptr, align 8
+  store ptr %this, ptr %this.addr, align 8
+    #dbg_declare(ptr %this.addr, !32, !DIExpression(), !34)
+  %this1 = load ptr, ptr %this.addr, align 8
+  %val_ = getelementptr inbounds nuw %struct.A, ptr %this1, i32 0, i32 0, !dbg !35
+  store i32 0, ptr %val_, align 4, !dbg !35
+  %val2_ = getelementptr inbounds nuw %struct.A, ptr %this1, i32 0, i32 1, !dbg !36
+  store i32 0, ptr %val2_, align 4, !dbg !36
+  ret void, !dbg !37
+}
+
+attributes #0 = { mustprogress noinline norecurse optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+attributes #1 = { mustprogress noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!14, !15, !16, !17, !18, !19, !20}
+!llvm.ident = !{!21}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "", checksumkind: CSK_MD5, checksum: "451371997e00e9e85d610a4e9d44a9b5")
+!2 = !{!3}
+!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A<int>", file: !1, line: 2, size: 64, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !4, templateParams: !12, identifier: "_ZTS1AIiE")
+!4 = !{!5, !7, !8}
+!5 = !DIDerivedType(tag: DW_TAG_member, name: "val_", scope: !3, file: !1, line: 4, baseType: !38, size: 32)
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = !DIDerivedType(tag: DW_TAG_member, name: "val2_", scope: !3, file: !1, line: 5, baseType: !6, size: 32, offset: 32)
+!8 = !DISubprogram(name: "A", scope: !3, file: !1, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
+!9 = !DISubroutineType(types: !10)
+!10 = !{null, !11}
+!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!12 = !{!38}
+!13 = !DITemplateTypeParameter(name: "T", type: !6)
+!14 = !{i32 7, !"Dwarf Version", i32 5}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 4}
+!17 = !{i32 8, !"PIC Level", i32 2}
+!18 = !{i32 7, !"PIE Level", i32 2}
+!19 = !{i32 7, !"uwtable", i32 2}
+!20 = !{i32 7, !"frame-pointer", i32 2}
+!21 = !{!"clang"}
+!22 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 7, type: !23, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !25)
+!23 = !DISubroutineType(types: !24)
+!24 = !{!6}
+!25 = !{}
+!26 = !DILocalVariable(name: "a", scope: !22, file: !1, line: 8, type: !3)
+!27 = !DILocation(line: 8, column: 8, scope: !22)
+!28 = !DILocation(line: 9, column: 3, scope: !22)
+!29 = !DILocation(line: 9, column: 9, scope: !22)
+!30 = !DILocation(line: 10, column: 1, scope: !22)
+!31 = distinct !DISubprogram(name: "A", linkageName: "_ZN1AIiEC2Ev", scope: !3, file: !1, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !8, retainedNodes: !25)
+!32 = !DILocalVariable(name: "this", arg: 1, scope: !31, type: !33, flags: DIFlagArtificial | DIFlagObjectPointer)
+!33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64)
+!34 = !DILocation(line: 0, scope: !31)
+!35 = !DILocation(line: 3, column: 8, scope: !31)
+!36 = !DILocation(line: 3, column: 17, scope: !31)
+!37 = !DILocation(line: 3, column: 28, scope: !31)
+!38 = !DIDerivedType(tag: DW_TAG_template_type_parameter,name:"T",scope:!3, baseType: !6)

@ykhatav ykhatav requested a review from dwblaikie February 19, 2025 15:22
@Michael137
Copy link
Member

Michael137 commented Feb 19, 2025

Couple high-level of questions/comments:

  1. How is GDB going to make use of this additional info?
  2. Does GCC already do this? Or is this a Clang-specific extension?
  3. Unless other reviewers think otherwise, I think it might be worth a post on the LLVM Discourse. LLDB would love to have generic structure/function template definitions in DWARF (particularly for expression evaluation), but that's a much larger feature. But if/when we eventually get to exploring generic template definitions in DWARF, it would be nice if it builds on top of/complements this work.
  4. Some more tests would be useful to understand the intentions of this feature. E.g., what does your DWARF output look like for cases like:
template <typename T>
structure Bar {
  T mem;
};

template <typename T>
structure Foo {
  Bar<T> mem;
};

Foo<Bar<int>> f;

@ykhatav
Copy link
Contributor Author

ykhatav commented Feb 19, 2025

Couple high-level of questions/comments:

  1. How is GDB going to make use of this additional info?
    Currently with this implementation, GDB does not support printing template as a type and will display it as an unknown type. So, the implementation is currently behind a switch until support is added to GDB.
    For example in the following test case:
template <typename T>
struct A
{
  A () : val_ (), val2_ () { }
  T val_;
  int val2_;
};

int
main (void)
{
  A<int> a;
  a.val2_ = 3;
  
  return 0;
}
The current output of gdb for the ptype command is in this case:
(gdb) b 15
Breakpoint 1 at 0x1177: file Repr.cpp, line 15.
(gdb) run
15        return 0;
(gdb) ptype a
type = struct A<int> [with T = int] {
	T val_;
	T val2_;
  public:
	A(void);
}

The desired behavior would be to show "int val2_" instead of "T val2_".
The emitted DWARF in this case does not allow for correct resolution. Looking at the DWARF v5 D.11 examples the clang should have referenced the DW_TAG_template_type_param as val_'s DW_AT_type instead of referencing the generic int.GDB should not have just resolved all int to T but instead only for those variables that actually reference the DW_TAG_template_type_param as their DW_AT_type.

  1. Does GCC already do this? Or is this a Clang-specific extension?
    No GCC's behavior is the same as current Clang's behavior
  1. Unless other reviewers think otherwise, I think it might be worth a post on the LLVM Discourse. LLDB would love to have generic structure/function template definitions in DWARF (particularly for expression evaluation), but that's a much larger feature. But if/when we eventually get to exploring generic template definitions in DWARF, it would be nice if it builds on top of/complements this work.
    Do you suggest creating a RFC first to get broader feedback on the design?
  2. Some more tests would be useful to understand the intentions of this feature. E.g., what does your DWARF output look like for cases like:
template <typename T>
structure Bar {
  T mem;
};

template <typename T>
structure Foo {
  Bar<T> mem;
};

Foo<Bar<int>> f;

Sure, I'll work on adding more test cases. As for the dwarf output, it looks like this:

0x0000000c: DW_TAG_compile_unit
              DW_AT_producer    ("clang version 21.0.0git (https://github.com/llvm/llvm-project.git 270fe3076a362b1e4746c86dee1a4f5b887d0f93)")
              DW_AT_language    (DW_LANG_C_plus_plus_14)
              DW_AT_name        ("test1.cpp")
              DW_AT_str_offsets_base    (0x00000008)
              DW_AT_stmt_list   (0x00000000)
              DW_AT_comp_dir    ("")
              DW_AT_low_pc      (0x0000000000001130)
              DW_AT_high_pc     (0x000000000000113f)
              DW_AT_addr_base   (0x00000008)

0x00000023:   DW_TAG_subprogram
                DW_AT_low_pc    (0x0000000000001130)
                DW_AT_high_pc   (0x000000000000113f)
                DW_AT_frame_base        (DW_OP_reg6 RBP)
                DW_AT_name      ("main")
                DW_AT_decl_file ("")
                DW_AT_decl_line (10)
                DW_AT_type      (0x0000003e "int")
                DW_AT_external  (true)

0x00000032:     DW_TAG_variable
                  DW_AT_location        (DW_OP_fbreg -8)
                  DW_AT_name    ("f")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (11)
                  DW_AT_type    (0x00000042 "Foo<Bar<int> >")

0x0000003d:     NULL

0x0000003e:   DW_TAG_base_type
                DW_AT_name      ("int")
                DW_AT_encoding  (DW_ATE_signed)
                DW_AT_byte_size (0x04)

0x00000042:   DW_TAG_structure_type
                DW_AT_calling_convention        (DW_CC_pass_by_value)
                DW_AT_name      ("Foo<Bar<int> >")
                DW_AT_byte_size (0x04)
                DW_AT_decl_file ("")
                DW_AT_decl_line (7)

0x00000048:     DW_TAG_template_type_parameter
                  DW_AT_type    (0x00000058 "Bar<int>")
                  DW_AT_name    ("T")

0x0000004e:     DW_TAG_member
                  DW_AT_name    ("mem")
                  DW_AT_type    (0x0000006e "Bar<Bar<int> >")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (8)
                  DW_AT_data_member_location    (0x00)

0x00000057:     NULL
0x00000058:   DW_TAG_structure_type
                DW_AT_calling_convention        (DW_CC_pass_by_value)
                DW_AT_name      ("Bar<int>")
                DW_AT_byte_size (0x04)
                DW_AT_decl_file ("")
                DW_AT_decl_line (2)

0x0000005e:     DW_TAG_template_type_parameter
                  DW_AT_type    (0x0000003e "int")
                  DW_AT_name    ("T")

0x00000064:     DW_TAG_member
                  DW_AT_name    ("mem")
                  DW_AT_type    (0x0000005e "Bar<int>::T")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (3)
                  DW_AT_data_member_location    (0x00)

0x0000006d:     NULL

0x0000006e:   DW_TAG_structure_type
                DW_AT_calling_convention        (DW_CC_pass_by_value)
                DW_AT_name      ("Bar<Bar<int> >")
                DW_AT_byte_size (0x04)
                DW_AT_decl_file ("")
                DW_AT_decl_line (2)

0x00000074:     DW_TAG_template_type_parameter
                  DW_AT_type    (0x00000058 "Bar<int>")
                  DW_AT_name    ("T")

0x0000007a:     DW_TAG_member
                  DW_AT_name    ("mem")
                  DW_AT_type    (0x00000074 "Bar<Bar<int> >::T")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (3)
                  DW_AT_data_member_location    (0x00)

0x00000083:     NULL

0x00000084:   NULL

@ykhatav ykhatav marked this pull request as draft February 20, 2025 15:13
@ykhatav
Copy link
Contributor Author

ykhatav commented Feb 20, 2025

I found some gaps in my implementation. Converting this PR to draft while I work on it.

@dwblaikie
Copy link
Collaborator

For template classes with type parameters, the information of which member variables are of a "templated type" is lost. The debugger cannot differentiate between "templated" types and "hardcoded" types. This is because both types have the same debug information, so there is no way that gdb can distinguish between them. This patch adds support for templates as type by representing the template type as a derived type.

What features are you hoping to build on this debug info?

Mostly I worry it won't be terribly complete, because it can't work through situations, like this:

template<typename T>
struct trait {
  using type = T;
};
template<typename T>
struct other {
  trait<T>::type v1;
  T v2;
};

In this case, v2 can be described as being of type "T" referencing the template_type_parameter, but v1 can't be - because it references trait::type, for instance.

Also, I'd worry that most debuggers/DWARF consumers aren't ready to handle type references to template_type_parameters? So best to test this with at least LLDB and GDB before we commit it.

@ykhatav
Copy link
Contributor Author

ykhatav commented Mar 7, 2025

For template classes with type parameters, the information of which member variables are of a "templated type" is lost. The debugger cannot differentiate between "templated" types and "hardcoded" types. This is because both types have the same debug information, so there is no way that gdb can distinguish between them. This patch adds support for templates as type by representing the template type as a derived type.

What features are you hoping to build on this debug info?

I would say it is more of an improvement in debug information. The current implementation of Clang generates DWARF debug information that could be improved for better compliance with DWARF v5 standards. Consider the example D.11 from DWARF v5. Currently, Clang produces the following DWARF output:

0x0000003e:   DW_TAG_base_type
                DW_AT_name      ("int")
                DW_AT_encoding  (DW_ATE_signed)
                DW_AT_byte_size (0x04)

0x00000042:   DW_TAG_structure_type
                DW_AT_calling_convention        (DW_CC_pass_by_value)
                DW_AT_name      ("wrapper<int>")
                DW_AT_byte_size (0x04)
                DW_AT_decl_file ("")
                DW_AT_decl_line (2)

0x00000048:     DW_TAG_template_type_parameter
                  DW_AT_type    (0x0000003e "int")
                  DW_AT_name    ("T")

0x0000004e:     DW_TAG_member
                  DW_AT_name    ("comp")
                  DW_AT_type    (0x0000003e "int")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (3)
                  DW_AT_data_member_location    (0x00)

Note that the type of variable "comp" directly references "int" instead of DW_TAG_template_type_parameter.

Mostly I worry it won't be terribly complete, because it can't work through situations, like this:

template<typename T>
struct trait {
  using type = T;
};
template<typename T>
struct other {
  trait<T>::type v1;
  T v2;
};

In this case, v2 can be described as being of type "T" referencing the template_type_parameter, but v1 can't be - because it references trait::type, for instance.

I believe, in this case, the debug information of "v2" can still be improved and align with DWARF v5, it could be represented using DW_TAG_template_type instead of "int":

0x00000042:   DW_TAG_structure_type
                DW_AT_calling_convention        (DW_CC_pass_by_value)
                DW_AT_name      ("other<int>")
                DW_AT_byte_size (0x08)
                DW_AT_decl_file ("")
                DW_AT_decl_line (6)

0x00000048:     DW_TAG_template_type_parameter
                  DW_AT_type    (0x0000003e "int")
                  DW_AT_name    ("T")

0x0000004e:     DW_TAG_member
                  DW_AT_name    ("v1")
                  DW_AT_type    (0x0000006d "trait<int>::type")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (7)
                  DW_AT_data_member_location    (0x00)

0x00000057:     DW_TAG_member
                  DW_AT_name    ("v2")
                  DW_AT_type    (0x00000048 "other<int>::T")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (8)
                  DW_AT_data_member_location    (0x04)

Also, I'd worry that most debuggers/DWARF consumers aren't ready to handle type references to template_type_parameters? So best to test this with at least LLDB and GDB before we commit it.

While I acknowledge that current debuggers may not fully support this DWARF representation, I suggest that this implementation be controlled by a switch until downstream tools are updated to accommodate these changes.

@dwblaikie
Copy link
Collaborator

Mostly I worry it won't be terribly complete, because it can't work through situations, like this:

template<typename T>
struct trait {
  using type = T;
};
template<typename T>
struct other {
  trait<T>::type v1;
  T v2;
};

In this case, v2 can be described as being of type "T" referencing the template_type_parameter, but v1 can't be - because it references trait::type, for instance.

I believe, in this case, the debug information of "v2" can still be improved and align with DWARF v5

Right - like I said, I get that "v2" gets better, but "v1" doesn't, right? And I imagine many/(most?) uses of type parameters in templates are more complicated - so I'm not sure how much this helps, and will feel awkwardly inconsistent for DWARF consumers/users?

Also, I'd worry that most debuggers/DWARF consumers aren't ready to handle type references to template_type_parameters? So best to test this with at least LLDB and GDB before we commit it.

While I acknowledge that current debuggers may not fully support this DWARF representation, I suggest that this implementation be controlled by a switch until downstream tools are updated to accommodate these changes.

Yeah - not sure it rises to the level of needing a flag (flags are a bit of an unfortunate maintenance burden - means more DWARF variety, harder to know everyone's doing the same thing, etc... ) - but some testing ahead of time would be good.

@ykhatav
Copy link
Contributor Author

ykhatav commented Mar 11, 2025

0x00000042: DW_TAG_structure_type
DW_AT_calling_convention (DW_CC_pass_by_value)
DW_AT_name ("other")
DW_AT_byte_size (0x08)
DW_AT_decl_file ("")
DW_AT_decl_line (6)

0x00000048: DW_TAG_template_type_parameter
DW_AT_type (0x0000003e "int")
DW_AT_name ("T")

0x0000004e: DW_TAG_member
DW_AT_name ("v1")
DW_AT_type (0x0000006d "trait::type")
DW_AT_decl_file ("")
DW_AT_decl_line (7)
DW_AT_data_member_location (0x00)

0x00000057: DW_TAG_member
DW_AT_name ("v2")
DW_AT_type (0x00000048 "other::T")
DW_AT_decl_file ("")
DW_AT_decl_line (8)
DW_AT_data_member_location (0x04)

Mostly I worry it won't be terribly complete, because it can't work through situations, like this:

template<typename T>
struct trait {
  using type = T;
};
template<typename T>
struct other {
  trait<T>::type v1;
  T v2;
};

In this case, v2 can be described as being of type "T" referencing the template_type_parameter, but v1 can't be - because it references trait::type, for instance.

I believe, in this case, the debug information of "v2" can still be improved and align with DWARF v5

Right - like I said, I get that "v2" gets better, but "v1" doesn't, right? And I imagine many/(most?) uses of type parameters in templates are more complicated - so I'm not sure how much this helps, and will feel awkwardly inconsistent for DWARF consumers/users?

I am not sure that I understand your concern completely. Consider the following DWARF output based on my implementation. How would you say "v1" should be represented ideally?
`0x00000042: DW_TAG_structure_type
DW_AT_calling_convention (DW_CC_pass_by_value)
DW_AT_name ("other")
DW_AT_byte_size (0x08)
DW_AT_decl_file ("")
DW_AT_decl_line (6)

0x00000048: DW_TAG_template_type_parameter
DW_AT_type (0x0000003e "int")
DW_AT_name ("T")

0x0000004e: DW_TAG_member
DW_AT_name ("v1")
DW_AT_type (0x0000006d "trait::type")
DW_AT_decl_file ("")
DW_AT_decl_line (7)
DW_AT_data_member_location (0x00)

0x00000057: DW_TAG_member
DW_AT_name ("v2")
DW_AT_type (0x00000048 "other::T")
DW_AT_decl_file ("")
DW_AT_decl_line (8)
DW_AT_data_member_location (0x04)`

Also, I'd worry that most debuggers/DWARF consumers aren't ready to handle type references to template_type_parameters? So best to test this with at least LLDB and GDB before we commit it.

While I acknowledge that current debuggers may not fully support this DWARF representation, I suggest that this implementation be controlled by a switch until downstream tools are updated to accommodate these changes.

Yeah - not sure it rises to the level of needing a flag (flags are a bit of an unfortunate maintenance burden - means more DWARF variety, harder to know everyone's doing the same thing, etc... ) - but some testing ahead of time would be good.

My primary reason for placing this implementation behind a flag is that it currently causes LLDB to crash and results in the following output from GDB. This is largely due to the fact that these debuggers do not yet support handling template type parameters represented as types:
$1 = { val_ = <unknown type in /llvm-project/test.exe, CU 0x0, DIE 0x40>, val2_ = 3}

@dwblaikie
Copy link
Collaborator

I am not sure that I understand your concern completely. Consider the following DWARF output based on my implementation. How would you say "v1" should be represented ideally?

0x00000042: DW_TAG_structure_type
DW_AT_calling_convention (DW_CC_pass_by_value)
DW_AT_name ("other")
DW_AT_byte_size (0x08)
DW_AT_decl_file ("")
DW_AT_decl_line (6)

0x00000048: DW_TAG_template_type_parameter
DW_AT_type (0x0000003e "int")
DW_AT_name ("T")

0x0000004e: DW_TAG_member
DW_AT_name ("v1")
DW_AT_type (0x0000006d "trait<int>::type")
DW_AT_decl_file ("")
DW_AT_decl_line (7)
DW_AT_data_member_location (0x00)

0x00000057: DW_TAG_member
DW_AT_name ("v2")
DW_AT_type (0x00000048 "other<int>::T")
DW_AT_decl_file ("")
DW_AT_decl_line (8)
DW_AT_data_member_location (0x04)

That's basically my point, sorry, that v1 must be represented as "trait::type" (because it has to be canonical) and so if many uses, like this one, of a type in a template can't reference the DW_TAG_template_type_parameter, because it's non-canonical - I'm not sure there's a lot of value in making it work for the subset of cases where it is workable.

My primary reason for placing this implementation behind a flag is that it currently causes LLDB to crash and results in the following output from GDB. This is largely due to the fact that these debuggers do not yet support handling template type parameters represented as types: $1 = { val_ = <unknown type in /llvm-project/test.exe, CU 0x0, DIE 0x40>, val2_ = 3}

Ah, thanks for testing. Yeah, that makes me similarly less inclined to pursue this direction.

I'd say unless there's some specifically valuable feature that hinges on this representation change, I'm not super inclined to put this into Clang at the moment. Other folks/reviewers might have other perspectives, though.

@ykhatav
Copy link
Contributor Author

ykhatav commented Mar 11, 2025

I am not sure that I understand your concern completely. Consider the following DWARF output based on my implementation. How would you say "v1" should be represented ideally?

0x00000042: DW_TAG_structure_type
DW_AT_calling_convention (DW_CC_pass_by_value)
DW_AT_name ("other")
DW_AT_byte_size (0x08)
DW_AT_decl_file ("")
DW_AT_decl_line (6)

0x00000048: DW_TAG_template_type_parameter
DW_AT_type (0x0000003e "int")
DW_AT_name ("T")

0x0000004e: DW_TAG_member
DW_AT_name ("v1")
DW_AT_type (0x0000006d "trait<int>::type")
DW_AT_decl_file ("")
DW_AT_decl_line (7)
DW_AT_data_member_location (0x00)

0x00000057: DW_TAG_member
DW_AT_name ("v2")
DW_AT_type (0x00000048 "other<int>::T")
DW_AT_decl_file ("")
DW_AT_decl_line (8)
DW_AT_data_member_location (0x04)

That's basically my point, sorry, that v1 must be represented as "trait::type" (because it has to be canonical) and so if many uses, like this one, of a type in a template can't reference the DW_TAG_template_type_parameter, because it's non-canonical - I'm not sure there's a lot of value in making it work for the subset of cases where it is workable.

Are you suggesting that for the implementation to be considered as complete, both v1 and v2 should have the same type information? I.e "v1" type should point to 0x48 instead of 0x6d? As per my understanding based on the DWARF output below, the type for "trait::type"(0x6d) DOES reference a DW_TAG_template_type_parameter(0x67) entry:

              DW_AT_producer    ("clang version 21.0.0git (https://github.com/llvm/llvm-project.git 270fe3076a362b1e4746c86dee1a4f5b887d0f93)")
              DW_AT_language    (DW_LANG_C_plus_plus_14)
              DW_AT_name        ("test_db.cpp")
              DW_AT_str_offsets_base    (0x00000008)
              DW_AT_stmt_list   (0x00000000)
              DW_AT_comp_dir    ("")
              DW_AT_low_pc      (0x0000000000001130)
              DW_AT_high_pc     (0x000000000000113f)
              DW_AT_addr_base   (0x00000008)
 
0x00000023:   DW_TAG_subprogram
                DW_AT_low_pc    (0x0000000000001130)
                DW_AT_high_pc   (0x000000000000113f)
                DW_AT_frame_base        (DW_OP_reg6 RBP)
                DW_AT_name      ("main")
                DW_AT_decl_file ("")
                DW_AT_decl_line (10)
                DW_AT_type      (0x0000003e "int")
                DW_AT_external  (true)
 
0x00000032:     DW_TAG_variable
                  DW_AT_location        (DW_OP_fbreg -12)
                  DW_AT_name    ("local")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (11)
                  DW_AT_type    (0x00000042 "other<int>")
 
0x0000003d:     NULL
 
0x0000003e:   DW_TAG_base_type
                DW_AT_name      ("int")
                DW_AT_encoding  (DW_ATE_signed)
                DW_AT_byte_size (0x04)
 
0x00000042:   DW_TAG_structure_type
                DW_AT_calling_convention        (DW_CC_pass_by_value)
                DW_AT_name      ("other<int>")
                DW_AT_byte_size (0x08)
                DW_AT_decl_file ("")
                DW_AT_decl_line (6)
 
0x00000048:     DW_TAG_template_type_parameter
                  DW_AT_type    (0x0000003e "int")
                  DW_AT_name    ("T")
 
0x0000004e:     DW_TAG_member
                  DW_AT_name    ("v1")
                  DW_AT_type    (0x0000006d "trait<int>::type")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (7)
                  DW_AT_data_member_location    (0x00)
 
0x00000057:     DW_TAG_member
                  DW_AT_name    ("v2")
                  DW_AT_type    (0x00000048 "other<int>::T")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (8)
                  DW_AT_data_member_location    (0x04)
 
0x00000060:     NULL
 
0x00000061:   DW_TAG_structure_type
                DW_AT_calling_convention        (DW_CC_pass_by_value)
                DW_AT_name      ("trait<int>")
                DW_AT_byte_size (0x01)
                DW_AT_decl_file ("")
                DW_AT_decl_line (2)
 
0x00000067:     DW_TAG_template_type_parameter
                  DW_AT_type    (0x0000003e "int")
                  DW_AT_name    ("T")
 
0x0000006d:     DW_TAG_typedef
                  DW_AT_type    (0x00000067 "trait<int>::T")
                  DW_AT_name    ("type")
                  DW_AT_decl_file       ("")
                  DW_AT_decl_line       (3)
 
0x00000075:     NULL
 
0x00000076:   NULL

@dwblaikie
Copy link
Collaborator

Are you suggesting that for the implementation to be considered as complete, both v1 and v2 should have the same type information? I.e "v1" type should point to 0x48 instead of 0x6d? As per my understanding based on the DWARF output below, the type for "trait::type"(0x6d) DOES reference a DW_TAG_template_type_parameter(0x67) entry:

No, I don't think it should point to 0x67 instead of 0x6d - you could imagine a trait with a resulting type that has nothing to do with T (like std::conditional_v<T, bool, char>).

My contention is that DWARF doesn't have a way to express this - and I don't really have a good idea for novel solutions/additions to DWARF etc to handle this. And without that, only changing the raw T references is of limited value.

That's why I don't think this is a great direction to go.

@ykhatav
Copy link
Contributor Author

ykhatav commented Mar 12, 2025

Are you suggesting that for the implementation to be considered as complete, both v1 and v2 should have the same type information? I.e "v1" type should point to 0x48 instead of 0x6d? As per my understanding based on the DWARF output below, the type for "trait::type"(0x6d) DOES reference a DW_TAG_template_type_parameter(0x67) entry:

No, I don't think it should point to 0x67 instead of 0x6d - you could imagine a trait with a resulting type that has nothing to do with T (like std::conditional_v<T, bool, char>).

My contention is that DWARF doesn't have a way to express this - and I don't really have a good idea for novel solutions/additions to DWARF etc to handle this. And without that, only changing the raw T references is of limited value.

That's why I don't think this is a great direction to go.

I created a sample test case containing a conditional trait type:

struct trait {
  using type = std::conditional_t<std::is_integral_v<T>, bool, double>;
};

template <typename T>
struct other {
  typename trait<T>::type v1;
  T v2;
};

int main() {
  other<int> obj1;
  other<float> obj2;
  return 0;
}

If you run the above test case with -fdebug-template-parameter-as-type, we get the attached DWARF(conditional_trait_dwarf_w_change.txt). Note that this DWARF has the added benefit of indicating whether the type came from the true or false branch. While the type of trait remains the same with and without -fdebug-template-parameter-as-type , I believe the new DWARF adds more context about types in templates and conditionals. Could you please take a look at the attached DWARF and let me know your concerns with the implementation? I have also attached the DWARF output without -fdebug-template-parameter-as-type for your reference.

conditional_trait_dwarf_orig.txt
conditional_trait_dwarf_w_change.txt

@dwblaikie
Copy link
Collaborator

Could you please take a look at the attached DWARF and let me know your concerns with the implementation?

Sure - I see the effect, but I still think it's probably not worthwhile due to the limited capacity, inconsistency (due to that limited expressiveness) between direct uses of type parameters and situations where a template parameter is passed to another template, and missing consumer support (which I realize is a chicken-and-egg problem, and not always a deal breaker if there's valuable benefits to be had once consumers do support a given feature).

But perhaps other reviewers have different takes on it.

@ykhatav ykhatav marked this pull request as ready for review March 13, 2025 14:25
@ykhatav
Copy link
Contributor Author

ykhatav commented Mar 17, 2025

@adrian-prantl @Michael137 gentle ping!

@ykhatav
Copy link
Contributor Author

ykhatav commented Apr 7, 2025

Closing due to lack of downstream support.

@ykhatav ykhatav closed this Apr 7, 2025
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:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category debuginfo llvm:ir

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants