[llvm] [DebugInfo][CodeView] Resolve forward references to types without unique name (PR #203781)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 14 11:49:05 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-platform-windows

Author: Nerixyz (Nerixyz)

<details>
<summary>Changes</summary>

In the following code:
```
// header.h
typedef struct lua_State lua_State;
lua_State *getState();

// source.c
#include "header.h"
struct lua_State { int field; };

lua_State *getState() {
    static lua_State state = {.field=42}; // make sure the type is emitted
    return &state;
}

// main.cpp
extern "C" {
    #include "header.h"
}
int main() { return getState() != 0; }
```

We'll get two forward references for `lua_State` when compiling with clang-cl. One with a unique name (from C++) and one without (from C). There's one complete definition for the type (from `source.c`). Since this is from C, it doesn't have a unique name. Before, we could only resolve the forward reference from C, not from C++.

With this PR, we can resolve both references. I also tested that the VS debugger resolves the reference correctly.

I'm not sure if this is a clang bug, because when compiling with MSVC, the C type also has a unique name. Clang omits the unique name, because C doesn't have ODR like C++.

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


3 Files Affected:

- (modified) llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp (+7-1) 
- (added) llvm/test/tools/llvm-pdbutil/forward-cxx-to-c-odr.test (+116) 
- (added) llvm/test/tools/llvm-pdbutil/forward-cxx-to-c.test (+89) 


``````````diff
diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
index 24d2f9e3360ea..ad996c7fec80e 100644
--- a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
@@ -209,8 +209,14 @@ TpiStream::findFullDeclForForwardRef(TypeIndex ForwardRefTI) const {
       continue;
     }
 
-    if (!FullTR.hasUniqueName())
+    if (!FullTR.hasUniqueName()) {
+      // `ForwardTR` does have a unique name, but the type we found doesn't.
+      // Remember this type, but look for better candidates in the bucket first.
+      if (ForwardTR.getName() == FullTR.getName())
+        ForwardRefTI = TI;
       continue;
+    }
+
     if (ForwardTR.getUniqueName() == FullTR.getUniqueName())
       return TI;
   }
diff --git a/llvm/test/tools/llvm-pdbutil/forward-cxx-to-c-odr.test b/llvm/test/tools/llvm-pdbutil/forward-cxx-to-c-odr.test
new file mode 100644
index 0000000000000..9a13c80cca6bc
--- /dev/null
+++ b/llvm/test/tools/llvm-pdbutil/forward-cxx-to-c-odr.test
@@ -0,0 +1,116 @@
+# Test that we prefer types with unique names when resolving forward references
+# from records with unique names.
+
+# RUN: llvm-pdbutil yaml2pdb %s --pdb=%t.pdb
+# RUN: llvm-pdbutil dump --types %t.pdb | FileCheck %s
+
+# The forward reference from 0x1002 -> 0x100A is important here.
+# In contrast to `forward-cxx-to-c.test`, we should resolve the reference to
+# 0x100A here, because both have a unique name (even if 0x1008 is lower).
+# For 0x1004, we resolve 0x1008, because of the missing unique name.
+
+# CHECK:       0x1002 | LF_STRUCTURE [size = 48] `lua_State`
+# CHECK-NEXT:           unique name: `.?AUlua_State@@`
+# CHECK-NEXT:           vtable: <no type>, base list: <no type>, field list: <no type>
+# CHECK-NEXT:           options: forward ref (-> 0x100A) | has unique name, sizeof 0
+# CHECK:       0x1004 | LF_STRUCTURE [size = 32] `lua_State`
+# CHECK-NEXT:           vtable: <no type>, base list: <no type>, field list: <no type>
+# CHECK-NEXT:           options: forward ref (-> 0x1008), sizeof 0
+# CHECK:       0x1007 | LF_FIELDLIST [size = 20]
+# CHECK-NEXT:           - LF_MEMBER [name = `field`, Type = 0x0074 (int), offset = 0, attrs = public]
+# CHECK-NEXT:  0x1008 | LF_STRUCTURE [size = 32] `lua_State`
+# CHECK-NEXT:           vtable: <no type>, base list: <no type>, field list: 0x1007
+# CHECK-NEXT:           options: , sizeof 4
+# CHECK-NEXT:  0x1009 | LF_FIELDLIST [size = 32]
+# CHECK-NEXT:           - LF_MEMBER [name = `differentField`, Type = 0x0074 (int), offset = 0, attrs = public]
+# CHECK-NEXT:  0x100A | LF_STRUCTURE [size = 48] `lua_State`
+# CHECK-NEXT:           unique name: `.?AUlua_State@@`
+# CHECK-NEXT:           vtable: <no type>, base list: <no type>, field list: 0x1007
+# CHECK-NEXT:           options: has unique name, sizeof 4
+
+---
+TpiStream:
+  Version:         VC80
+  Records:
+    - Kind:            LF_ARGLIST
+      ArgList:
+        ArgIndices:      [  ]
+    - Kind:            LF_PROCEDURE
+      Procedure:
+        ReturnType:      116
+        CallConv:        NearC
+        Options:         [ None ]
+        ParameterCount:  0
+        ArgumentList:    4096
+    - Kind:            LF_STRUCTURE
+      Class:
+        MemberCount:     0
+        Options:         [ None, ForwardReference, HasUniqueName ]
+        FieldList:       0
+        Name:            lua_State
+        UniqueName:      '.?AUlua_State@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            0
+    - Kind:            LF_POINTER
+      Pointer:
+        ReferentType:    4098
+        Attrs:           65548
+    - Kind:            LF_STRUCTURE
+      Class:
+        MemberCount:     0
+        Options:         [ None, ForwardReference ]
+        FieldList:       0
+        Name:            lua_State
+        UniqueName:      ''
+        DerivationList:  0
+        VTableShape:     0
+        Size:            0
+    - Kind:            LF_POINTER
+      Pointer:
+        ReferentType:    4100
+        Attrs:           65548
+    - Kind:            LF_PROCEDURE
+      Procedure:
+        ReturnType:      4101
+        CallConv:        NearC
+        Options:         [ None ]
+        ParameterCount:  0
+        ArgumentList:    4096
+    - Kind:            LF_FIELDLIST
+      FieldList:
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           3
+            Type:            116
+            FieldOffset:     0
+            Name:            field
+    - Kind:            LF_STRUCTURE
+      Class:
+        MemberCount:     1
+        Options:         [ None ]
+        FieldList:       4103
+        Name:            lua_State
+        UniqueName:      ''
+        DerivationList:  0
+        VTableShape:     0
+        Size:            4
+    - Kind:            LF_FIELDLIST
+      FieldList:
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           3
+            Type:            116
+            FieldOffset:     0
+            Name:            differentField
+    - Kind:            LF_STRUCTURE
+      Class:
+        MemberCount:     1
+        Options:         [ None, HasUniqueName ]
+        FieldList:       4103
+        Name:            lua_State
+        UniqueName:      '.?AUlua_State@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            4
+...
diff --git a/llvm/test/tools/llvm-pdbutil/forward-cxx-to-c.test b/llvm/test/tools/llvm-pdbutil/forward-cxx-to-c.test
new file mode 100644
index 0000000000000..7a2810cc79ed4
--- /dev/null
+++ b/llvm/test/tools/llvm-pdbutil/forward-cxx-to-c.test
@@ -0,0 +1,89 @@
+# Test that we can resolve forward references from types with a unique name (from C++)
+# to types without a unique name (from C).
+
+# RUN: llvm-pdbutil yaml2pdb %s --pdb=%t.pdb
+# RUN: llvm-pdbutil dump --types %t.pdb | FileCheck %s
+
+# The forward reference from 0x1002 -> 0x1008 is important here.
+
+# CHECK:       0x1002 | LF_STRUCTURE [size = 48] `lua_State`
+# CHECK-NEXT:           unique name: `.?AUlua_State@@`
+# CHECK-NEXT:           vtable: <no type>, base list: <no type>, field list: <no type>
+# CHECK-NEXT:           options: forward ref (-> 0x1008) | has unique name, sizeof 0
+# CHECK:       0x1004 | LF_STRUCTURE [size = 32] `lua_State`
+# CHECK-NEXT:           vtable: <no type>, base list: <no type>, field list: <no type>
+# CHECK-NEXT:           options: forward ref (-> 0x1008), sizeof 0
+# CHECK:       0x1007 | LF_FIELDLIST [size = 20]
+# CHECK-NEXT:           - LF_MEMBER [name = `field`, Type = 0x0074 (int), offset = 0, attrs = public]
+# CHECK-NEXT:  0x1008 | LF_STRUCTURE [size = 32] `lua_State`
+# CHECK-NEXT:           vtable: <no type>, base list: <no type>, field list: 0x1007
+# CHECK-NEXT:           options: , sizeof 4
+
+---
+TpiStream:
+  Version:         VC80
+  Records:
+    - Kind:            LF_ARGLIST
+      ArgList:
+        ArgIndices:      [  ]
+    - Kind:            LF_PROCEDURE
+      Procedure:
+        ReturnType:      116
+        CallConv:        NearC
+        Options:         [ None ]
+        ParameterCount:  0
+        ArgumentList:    4096
+    - Kind:            LF_STRUCTURE
+      Class:
+        MemberCount:     0
+        Options:         [ None, ForwardReference, HasUniqueName ]
+        FieldList:       0
+        Name:            lua_State
+        UniqueName:      '.?AUlua_State@@'
+        DerivationList:  0
+        VTableShape:     0
+        Size:            0
+    - Kind:            LF_POINTER
+      Pointer:
+        ReferentType:    4098
+        Attrs:           65548
+    - Kind:            LF_STRUCTURE
+      Class:
+        MemberCount:     0
+        Options:         [ None, ForwardReference ]
+        FieldList:       0
+        Name:            lua_State
+        UniqueName:      ''
+        DerivationList:  0
+        VTableShape:     0
+        Size:            0
+    - Kind:            LF_POINTER
+      Pointer:
+        ReferentType:    4100
+        Attrs:           65548
+    - Kind:            LF_PROCEDURE
+      Procedure:
+        ReturnType:      4101
+        CallConv:        NearC
+        Options:         [ None ]
+        ParameterCount:  0
+        ArgumentList:    4096
+    - Kind:            LF_FIELDLIST
+      FieldList:
+        - Kind:            LF_MEMBER
+          DataMember:
+            Attrs:           3
+            Type:            116
+            FieldOffset:     0
+            Name:            field
+    - Kind:            LF_STRUCTURE
+      Class:
+        MemberCount:     1
+        Options:         [ None ]
+        FieldList:       4103
+        Name:            lua_State
+        UniqueName:      ''
+        DerivationList:  0
+        VTableShape:     0
+        Size:            4
+...

``````````

</details>


https://github.com/llvm/llvm-project/pull/203781


More information about the llvm-commits mailing list