[llvm] [CodeView] Expose fallible type accessors in TpiStream (PR #188299)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 30 07:20:35 PDT 2026


https://github.com/Nerixyz updated https://github.com/llvm/llvm-project/pull/188299

>From 7f415917c6bd6683515e65f2fc881172ce6bc2c1 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 24 Mar 2026 18:34:03 +0100
Subject: [PATCH 1/4] [CodeView] Expose fallible type accessors to TpiStream

---
 .../DebugInfo/CodeView/LazyRandomTypeCollection.h  |  1 +
 llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h | 11 +++++++++++
 .../CodeView/LazyRandomTypeCollection.cpp          |  9 ++++++---
 llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp        | 14 ++++++++++++++
 4 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h b/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
index da18339b8662b..7f06e6e97b9e4 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h
@@ -72,6 +72,7 @@ class LLVM_ABI LazyRandomTypeCollection : public TypeCollection {
   std::optional<CVType> tryGetType(TypeIndex Index);
   llvm::Expected<CVType> getTypeOrError(TypeIndex Index);
   CVType getType(TypeIndex Index) override;
+
   StringRef getTypeName(TypeIndex Index) override;
   bool contains(TypeIndex Index) override;
   uint32_t size() override;
diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
index ec8ac8563b00c..4168eda0ecb7b 100644
--- a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
+++ b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
@@ -67,8 +67,19 @@ class TpiStream {
   LLVM_ABI std::vector<codeview::TypeIndex>
   findRecordsByName(StringRef Name) const;
 
+  /// Get a type by its index. \p Index must be a valid type index.
   LLVM_ABI codeview::CVType getType(codeview::TypeIndex Index);
 
+  /// The same as getType() except that the return value is defined
+  /// (empty/invalid) if \p Index doesn't exist. Use codeview::CVType::valid()
+  /// to check the return value.
+  LLVM_ABI codeview::CVType getTypeOrEmpty(codeview::TypeIndex Index);
+
+  LLVM_ABI std::optional<codeview::CVType>
+  tryGetType(codeview::TypeIndex Index);
+
+  LLVM_ABI Expected<codeview::CVType> getTypeOrError(codeview::TypeIndex Index);
+
   LLVM_ABI BinarySubstreamRef getTypeRecordsSubstream() const;
 
   LLVM_ABI Error commit();
diff --git a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
index 0b36c5085c086..9dacce90b2e1d 100644
--- a/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
+++ b/llvm/lib/DebugInfo/CodeView/LazyRandomTypeCollection.cpp
@@ -87,7 +87,11 @@ CVType LazyRandomTypeCollection::getType(TypeIndex Index) {
   assert(!Index.isSimple());
 
   auto EC = ensureTypeExists(Index);
-  error(std::move(EC));
+  if (EC) {
+    assert(false && "Invalid type index (use tryGetType or getTypeOrError)");
+    llvm::consumeError(std::move(EC));
+    return {};
+  }
   assert(contains(Index));
 
   return Records[Index.toArrayIndex()].Type;
@@ -98,9 +102,8 @@ LazyRandomTypeCollection::getTypeOrError(TypeIndex Index) {
   if (Index.isSimple())
     return llvm::createStringError("Type index too low (%d)", Index.getIndex());
 
-  if (auto EC = ensureTypeExists(Index)) {
+  if (auto EC = ensureTypeExists(Index))
     return EC;
-  }
 
   if (!contains(Index))
     return llvm::createStringError("Type index too high (%d)",
diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
index ff5ae400201c7..62db6265f2a36 100644
--- a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
@@ -222,6 +222,20 @@ codeview::CVType TpiStream::getType(codeview::TypeIndex Index) {
   return Types->getType(Index);
 }
 
+codeview::CVType TpiStream::getTypeOrEmpty(codeview::TypeIndex Index) {
+  return Types->tryGetType(Index).value_or({});
+}
+
+std::optional<codeview::CVType>
+TpiStream::tryGetType(codeview::TypeIndex Index) {
+  return Types->tryGetType(Index);
+}
+
+Expected<codeview::CVType>
+TpiStream::getTypeOrError(codeview::TypeIndex Index) {
+  return Types->getTypeOrError(Index);
+}
+
 BinarySubstreamRef TpiStream::getTypeRecordsSubstream() const {
   return TypeRecordsSubstream;
 }

>From 61d1f7038f5aa7220626b423058c2e8acdb9a0f9 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Tue, 24 Mar 2026 19:32:59 +0100
Subject: [PATCH 2/4] fix: explicit argument to .value_or

---
 llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
index 62db6265f2a36..24d2f9e3360ea 100644
--- a/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
+++ b/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp
@@ -223,7 +223,7 @@ codeview::CVType TpiStream::getType(codeview::TypeIndex Index) {
 }
 
 codeview::CVType TpiStream::getTypeOrEmpty(codeview::TypeIndex Index) {
-  return Types->tryGetType(Index).value_or({});
+  return Types->tryGetType(Index).value_or<CVType>({});
 }
 
 std::optional<codeview::CVType>

>From df6e21efd7511415f019691aea285957a3c6f2f9 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Thu, 26 Mar 2026 19:37:21 +0100
Subject: [PATCH 3/4] fix: add a test for the accessors

---
 llvm/unittests/DebugInfo/PDB/CMakeLists.txt   |   1 +
 .../unittests/DebugInfo/PDB/TpiStreamTest.cpp | 142 ++++++++++++++++++
 2 files changed, 143 insertions(+)
 create mode 100644 llvm/unittests/DebugInfo/PDB/TpiStreamTest.cpp

diff --git a/llvm/unittests/DebugInfo/PDB/CMakeLists.txt b/llvm/unittests/DebugInfo/PDB/CMakeLists.txt
index b1b9d2d98c944..5cc6f2252fa81 100644
--- a/llvm/unittests/DebugInfo/PDB/CMakeLists.txt
+++ b/llvm/unittests/DebugInfo/PDB/CMakeLists.txt
@@ -12,6 +12,7 @@ add_llvm_unittest_with_input_files(DebugInfoPDBTests
   PDBApiTest.cpp
   PDBVariantTest.cpp
   PublicsStreamTest.cpp
+  TpiStreamTest.cpp
   )
 
 target_link_libraries(DebugInfoPDBTests PRIVATE LLVMTestingSupport)
diff --git a/llvm/unittests/DebugInfo/PDB/TpiStreamTest.cpp b/llvm/unittests/DebugInfo/PDB/TpiStreamTest.cpp
new file mode 100644
index 0000000000000..e7339751915bb
--- /dev/null
+++ b/llvm/unittests/DebugInfo/PDB/TpiStreamTest.cpp
@@ -0,0 +1,142 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/PDB.h"
+#include "llvm/Support/Path.h"
+
+#include "llvm/Testing/Support/Error.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::pdb;
+using namespace llvm::codeview;
+
+extern const char *TestMainArgv0;
+
+// FIXME: This should use ObjectYAML to create a PDB from YAML.
+static std::string getPdbPath() {
+  SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0);
+  llvm::sys::path::append(InputsDir, "SimpleTest.pdb");
+  return std::string(InputsDir);
+}
+
+// Types:
+//  0x1000 | LF_ARGLIST [size = 8]
+//  0x1001 | LF_PROCEDURE [size = 16]
+static Expected<std::unique_ptr<PDBFile>>
+openSimplePdb(BumpPtrAllocator &Allocator) {
+  std::string PdbPath = getPdbPath();
+  ErrorOr<std::unique_ptr<MemoryBuffer>> ErrorOrBuffer =
+      MemoryBuffer::getFile(PdbPath, /*IsText=*/false,
+                            /*RequiresNullTerminator=*/false);
+  EXPECT_TRUE(ErrorOrBuffer);
+  std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(*ErrorOrBuffer);
+
+  auto Stream = std::make_unique<MemoryBufferByteStream>(
+      std::move(Buffer), llvm::endianness::little);
+
+  auto File = std::make_unique<PDBFile>(PdbPath, std::move(Stream), Allocator);
+  if (Error Err = File->parseFileHeaders())
+    return Err;
+  if (Error Err = File->parseStreamData())
+    return Err;
+  return File;
+}
+
+TEST(TpiStreamTest, getType) {
+  BumpPtrAllocator Allocator;
+  Expected<std::unique_ptr<PDBFile>> FileOrErr = openSimplePdb(Allocator);
+  ASSERT_TRUE(!!FileOrErr);
+  std::unique_ptr<PDBFile> File = std::move(*FileOrErr);
+
+  Expected<TpiStream &> Tpi = File->getPDBTpiStream();
+  ASSERT_TRUE(!!Tpi) << Tpi.takeError();
+
+  ASSERT_EQ(Tpi->getType(TypeIndex(0x1000)).kind(), LF_ARGLIST);
+  ASSERT_EQ(Tpi->getType(TypeIndex(0x1001)).kind(), LF_PROCEDURE);
+}
+
+TEST(TpiStreamTest, getTypeOrEmpty) {
+  BumpPtrAllocator Allocator;
+  Expected<std::unique_ptr<PDBFile>> FileOrErr = openSimplePdb(Allocator);
+  ASSERT_TRUE(!!FileOrErr);
+  std::unique_ptr<PDBFile> File = std::move(*FileOrErr);
+
+  Expected<TpiStream &> Tpi = File->getPDBTpiStream();
+  ASSERT_TRUE(!!Tpi);
+
+  ASSERT_FALSE(Tpi->getTypeOrEmpty(TypeIndex(0)).valid());
+  ASSERT_FALSE(Tpi->getTypeOrEmpty(TypeIndex(0xFFF)).valid());
+  ASSERT_FALSE(Tpi->getTypeOrEmpty(TypeIndex(0x1002)).valid());
+  ASSERT_FALSE(Tpi->getTypeOrEmpty(TypeIndex(0x1003)).valid());
+  ASSERT_FALSE(Tpi->getTypeOrEmpty(TypeIndex(0x1234567)).valid());
+
+  ASSERT_EQ(Tpi->getTypeOrEmpty(TypeIndex(0x1000)).kind(), LF_ARGLIST);
+  ASSERT_EQ(Tpi->getTypeOrEmpty(TypeIndex(0x1001)).kind(), LF_PROCEDURE);
+}
+
+TEST(TpiStreamTest, tryGetType) {
+  BumpPtrAllocator Allocator;
+  Expected<std::unique_ptr<PDBFile>> FileOrErr = openSimplePdb(Allocator);
+  ASSERT_TRUE(!!FileOrErr);
+  std::unique_ptr<PDBFile> File = std::move(*FileOrErr);
+
+  Expected<TpiStream &> Tpi = File->getPDBTpiStream();
+  ASSERT_TRUE(!!Tpi);
+
+  ASSERT_FALSE(Tpi->tryGetType(TypeIndex(0)).has_value());
+  ASSERT_FALSE(Tpi->tryGetType(TypeIndex(0xFFF)).has_value());
+  ASSERT_FALSE(Tpi->tryGetType(TypeIndex(0x1002)).has_value());
+  ASSERT_FALSE(Tpi->tryGetType(TypeIndex(0x1003)).has_value());
+  ASSERT_FALSE(Tpi->tryGetType(TypeIndex(0x1234567)).has_value());
+
+  std::optional<CVType> T0 = Tpi->tryGetType(TypeIndex(0x1000));
+  std::optional<CVType> T1 = Tpi->tryGetType(TypeIndex(0x1001));
+  ASSERT_TRUE(T0.has_value());
+  ASSERT_EQ(T0->kind(), LF_ARGLIST);
+  ASSERT_TRUE(T1.has_value());
+  ASSERT_EQ(T1->kind(), LF_PROCEDURE);
+}
+
+TEST(TpiStreamTest, getTypeOrErr) {
+  BumpPtrAllocator Allocator;
+  Expected<std::unique_ptr<PDBFile>> FileOrErr = openSimplePdb(Allocator);
+  ASSERT_TRUE(!!FileOrErr);
+  std::unique_ptr<PDBFile> File = std::move(*FileOrErr);
+
+  Expected<TpiStream &> Tpi = File->getPDBTpiStream();
+  ASSERT_TRUE(!!Tpi);
+
+  Expected<CVType> E0 = Tpi->getTypeOrError(TypeIndex(0));
+  ASSERT_FALSE(E0);
+  ASSERT_EQ(toString(E0.takeError()), "Type index too low (0)");
+  Expected<CVType> E1 = Tpi->getTypeOrError(TypeIndex(0xFFF));
+  ASSERT_FALSE(E1);
+  ASSERT_EQ(toString(E1.takeError()), "Type index too low (4095)");
+  Expected<CVType> E2 = Tpi->getTypeOrError(TypeIndex(0x1002));
+  ASSERT_FALSE(E2);
+  ASSERT_EQ(toString(E2.takeError()), "Type index too high (4098)");
+  Expected<CVType> E3 = Tpi->getTypeOrError(TypeIndex(0x1003));
+  ASSERT_FALSE(E3);
+  ASSERT_EQ(toString(E3.takeError()), "Invalid type index");
+  Expected<CVType> E4 = Tpi->getTypeOrError(TypeIndex(0x123456));
+  ASSERT_FALSE(E4);
+  ASSERT_EQ(toString(E4.takeError()), "Invalid type index");
+
+  Expected<CVType> T0 = Tpi->getTypeOrError(TypeIndex(0x1000));
+  ASSERT_TRUE(!!T0);
+  ASSERT_EQ(T0->kind(), LF_ARGLIST);
+
+  Expected<CVType> T1 = Tpi->getTypeOrError(TypeIndex(0x1001));
+  ASSERT_TRUE(!!T1);
+  ASSERT_EQ(T1->kind(), LF_PROCEDURE);
+}

>From 1e8f8952e43a86ee6b28d71148695ef2e16d4589 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Mon, 30 Mar 2026 16:20:07 +0200
Subject: [PATCH 4/4] fix: document

---
 llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
index 4168eda0ecb7b..deaa313c61329 100644
--- a/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
+++ b/llvm/include/llvm/DebugInfo/PDB/Native/TpiStream.h
@@ -68,6 +68,7 @@ class TpiStream {
   findRecordsByName(StringRef Name) const;
 
   /// Get a type by its index. \p Index must be a valid type index.
+  /// Otherwise, this method asserts in Debug mode.
   LLVM_ABI codeview::CVType getType(codeview::TypeIndex Index);
 
   /// The same as getType() except that the return value is defined
@@ -75,9 +76,11 @@ class TpiStream {
   /// to check the return value.
   LLVM_ABI codeview::CVType getTypeOrEmpty(codeview::TypeIndex Index);
 
+  /// Get the type at \p Index if it exists or \c std::nullopt otherwise.
   LLVM_ABI std::optional<codeview::CVType>
   tryGetType(codeview::TypeIndex Index);
 
+  /// Get the type at \p Index if it exists or an error about the failure.
   LLVM_ABI Expected<codeview::CVType> getTypeOrError(codeview::TypeIndex Index);
 
   LLVM_ABI BinarySubstreamRef getTypeRecordsSubstream() const;



More information about the llvm-commits mailing list