[llvm] [IR] Verify DISubrange/DISubrangeType bounds are ConstantInt (PR #217570)

Sewon Ahn via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 12 02:11:24 PDT 2026


https://github.com/lrycro updated https://github.com/llvm/llvm-project/pull/217570

>From b41e6666efb119fa771c029e8978971a162dfd6e Mon Sep 17 00:00:00 2001
From: lrycro <thscw973 at naver.com>
Date: Thu, 20 Aug 2026 19:04:50 +0900
Subject: [PATCH 1/3] [IR] Fix unsafe cast<ConstantInt> on
 DISubrange/DISubrangeType bound operands

DISubrange/DISubrangeType/DIGenericSubrange bound operands (LowerBound,
UpperBound, Stride, Bias, CountNode) are declared as plain Metadata*, and
when wrapped in ConstantAsMetadata the wrapped Constant is not guaranteed
to be a ConstantInt. Six call sites in llvm/lib/IR/LLVMContextImpl.h and
llvm/lib/IR/DebugInfoMetadata.cpp unconditionally did
cast<ConstantInt>(MD->getValue()) once the operand was confirmed to be
ConstantAsMetadata:

- MDNodeKeyImpl<DISubrange>::isKeyOf()'s BoundsEqual lambda
- MDNodeKeyImpl<DISubrange>::getHashValue() (CountNode)
- MDNodeKeyImpl<DIGenericSubrange>::getHashValue() (CountNode)
- MDNodeKeyImpl<DISubrangeType>::isKeyOf()'s BoundsEqual lambda
- MDNodeKeyImpl<DISubrangeType>::getHashValue()'s HashBound lambda
- DISubrangeType::convertRawToBound(), reachable from the public
  getLowerBound()/getUpperBound()/getStride()/getBias() accessors

cast<X> asserts on a type mismatch in assertions-enabled builds and is
undefined behavior otherwise. The getHashValue()/convertRawToBound() call
sites are reachable just from constructing or querying a single such
node -- no comparison against another node is needed to trigger them.

Switched all six to dyn_cast<ConstantInt> with a null check, falling
back to the existing "not equal via this path" / "hash the raw operand"
behavior already used elsewhere in the same functions for non-matching
operand kinds.

Added regression tests to llvm/unittests/IR/MetadataTest.cpp for both
DISubrange and DISubrangeType with a bound operand wrapping a
non-ConstantInt Constant (UndefValue), plus a positive-path test
confirming distinct ConstantInt instances of different bit width but
equal signed value still compare equal (unchanged intended behavior).
---
 llvm/lib/IR/DebugInfoMetadata.cpp  |  7 ++-
 llvm/lib/IR/LLVMContextImpl.h      | 26 ++++++-----
 llvm/unittests/IR/MetadataTest.cpp | 70 ++++++++++++++++++++++++++++++
 3 files changed, 89 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index c25b8c6796bbc..7f20bb620166d 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -841,8 +841,11 @@ DISubrangeType::convertRawToBound(Metadata *IN) const {
   assert(isa<ConstantAsMetadata>(IN) || isa<DIVariable>(IN) ||
          isa<DIExpression>(IN) || isa<DIDerivedType>(IN));
 
-  if (auto *MD = dyn_cast<ConstantAsMetadata>(IN))
-    return BoundType(cast<ConstantInt>(MD->getValue()));
+  if (auto *MD = dyn_cast<ConstantAsMetadata>(IN)) {
+    if (auto *CV = dyn_cast<ConstantInt>(MD->getValue()))
+      return BoundType(CV);
+    return BoundType();
+  }
 
   if (auto *MD = dyn_cast<DIVariable>(IN))
     return BoundType(MD);
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 49a8a803732a5..fabeda231b684 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -360,9 +360,9 @@ template <> struct MDNodeKeyImpl<DISubrange> {
       ConstantAsMetadata *MD1 = dyn_cast_or_null<ConstantAsMetadata>(Node1);
       ConstantAsMetadata *MD2 = dyn_cast_or_null<ConstantAsMetadata>(Node2);
       if (MD1 && MD2) {
-        ConstantInt *CV1 = cast<ConstantInt>(MD1->getValue());
-        ConstantInt *CV2 = cast<ConstantInt>(MD2->getValue());
-        if (CV1->getSExtValue() == CV2->getSExtValue())
+        ConstantInt *CV1 = dyn_cast<ConstantInt>(MD1->getValue());
+        ConstantInt *CV2 = dyn_cast<ConstantInt>(MD2->getValue());
+        if (CV1 && CV2 && CV1->getSExtValue() == CV2->getSExtValue())
           return true;
       }
       return false;
@@ -377,8 +377,9 @@ template <> struct MDNodeKeyImpl<DISubrange> {
   unsigned getHashValue() const {
     if (CountNode)
       if (auto *MD = dyn_cast<ConstantAsMetadata>(CountNode))
-        return hash_combine(cast<ConstantInt>(MD->getValue())->getSExtValue(),
-                            LowerBound, UpperBound, Stride);
+        if (auto *CV = dyn_cast<ConstantInt>(MD->getValue()))
+          return hash_combine(CV->getSExtValue(), LowerBound, UpperBound,
+                              Stride);
     return hash_combine(CountNode, LowerBound, UpperBound, Stride);
   }
 };
@@ -407,8 +408,9 @@ template <> struct MDNodeKeyImpl<DIGenericSubrange> {
   unsigned getHashValue() const {
     auto *MD = dyn_cast_or_null<ConstantAsMetadata>(CountNode);
     if (CountNode && MD)
-      return hash_combine(cast<ConstantInt>(MD->getValue())->getSExtValue(),
-                          LowerBound, UpperBound, Stride);
+      if (auto *CV = dyn_cast<ConstantInt>(MD->getValue()))
+        return hash_combine(CV->getSExtValue(), LowerBound, UpperBound,
+                            Stride);
     return hash_combine(CountNode, LowerBound, UpperBound, Stride);
   }
 };
@@ -676,9 +678,9 @@ template <> struct MDNodeKeyImpl<DISubrangeType> {
       ConstantAsMetadata *MD1 = dyn_cast_or_null<ConstantAsMetadata>(Node1);
       ConstantAsMetadata *MD2 = dyn_cast_or_null<ConstantAsMetadata>(Node2);
       if (MD1 && MD2) {
-        ConstantInt *CV1 = cast<ConstantInt>(MD1->getValue());
-        ConstantInt *CV2 = cast<ConstantInt>(MD2->getValue());
-        if (CV1->getSExtValue() == CV2->getSExtValue())
+        ConstantInt *CV1 = dyn_cast<ConstantInt>(MD1->getValue());
+        ConstantInt *CV2 = dyn_cast<ConstantInt>(MD2->getValue());
+        if (CV1 && CV2 && CV1->getSExtValue() == CV2->getSExtValue())
           return true;
       }
       return false;
@@ -699,8 +701,8 @@ template <> struct MDNodeKeyImpl<DISubrangeType> {
     unsigned val = 0;
     auto HashBound = [&](Metadata *Node) -> void {
       ConstantAsMetadata *MD = dyn_cast_or_null<ConstantAsMetadata>(Node);
-      if (MD) {
-        ConstantInt *CV = cast<ConstantInt>(MD->getValue());
+      ConstantInt *CV = MD ? dyn_cast<ConstantInt>(MD->getValue()) : nullptr;
+      if (CV) {
         val = hash_combine(val, CV->getSExtValue());
       } else {
         val = hash_combine(val, Node);
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 5f6feb5bd87bb..32b28d33f928d 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -1997,6 +1997,20 @@ TEST_F(DISubrangeTest, fortranAllocatableExpr) {
   EXPECT_NE(N, DISubrange::get(Context, nullptr, LVother, UE, SE));
 }
 
+// Regression test: MDNodeKeyImpl<DISubrange>::isKeyOf() (like its
+// DISubrangeType counterpart) must not crash when a bound operand is
+// ConstantAsMetadata wrapping a non-ConstantInt Constant.
+TEST_F(DISubrangeTest, boundsCompareNonConstantIntSafely) {
+  auto *LowerUndef =
+      ConstantAsMetadata::get(UndefValue::get(Type::getInt64Ty(Context)));
+  auto *LowerInt = ConstantAsMetadata::get(
+      ConstantInt::getSigned(Type::getInt64Ty(Context), -7));
+
+  auto *N1 = DISubrange::get(Context, nullptr, LowerUndef, nullptr, nullptr);
+  auto *N2 = DISubrange::get(Context, nullptr, LowerInt, nullptr, nullptr);
+  EXPECT_NE(N1, N2);
+}
+
 typedef MetadataTest DISubrangeTypeTest;
 
 TEST_F(DISubrangeTypeTest, get) {
@@ -2030,6 +2044,62 @@ TEST_F(DISubrangeTypeTest, get) {
   EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));
 }
 
+// MDNodeKeyImpl<DISubrangeType>::isKeyOf() compares bound operands that are
+// ConstantAsMetadata by comparing the wrapped ConstantInt's *value* rather
+// than requiring pointer identity. Use two different integer widths (i32
+// and i64) holding the same signed value: ConstantInt::get uniques per
+// (type, value), so these are genuinely distinct ConstantInt/ConstantAsMetadata
+// instances -- unlike same-width same-value constants, which LLVM would
+// already unique to an identical pointer, making the value-comparison branch
+// unreachable in that case.
+TEST_F(DISubrangeTypeTest, boundsEqualDistinctConstantAsMetadata) {
+  auto *Base =
+      DIBasicType::get(Context, dwarf::DW_TAG_base_type, "test_integer", 32, 0,
+                       dwarf::DW_ATE_signed, 100, DINode::FlagZero);
+  DILocalScope *Scope = getSubprogram();
+  DIFile *File = getFile();
+
+  auto *Lower1 =
+      ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, -7, true)));
+  auto *Lower2 =
+      ConstantAsMetadata::get(ConstantInt::get(Context, APInt(64, -7, true)));
+  ASSERT_NE(Lower1, Lower2);
+  auto *Upper = ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, 23, true)));
+
+  auto *N1 = DISubrangeType::get(Context, StringRef(), File, 101, Scope, 32, 0,
+                                 DINode::FlagZero, Base, Lower1, Upper,
+                                 nullptr, nullptr);
+  auto *N2 = DISubrangeType::get(Context, StringRef(), File, 101, Scope, 32, 0,
+                                 DINode::FlagZero, Base, Lower2, Upper,
+                                 nullptr, nullptr);
+  EXPECT_EQ(N1, N2);
+}
+
+// Regression test: a bound operand may be ConstantAsMetadata wrapping a
+// Constant that is not a ConstantInt (e.g. UndefValue). isKeyOf() must not
+// crash comparing such nodes -- it should safely treat them as unequal
+// instead of unconditionally casting to ConstantInt.
+TEST_F(DISubrangeTypeTest, boundsCompareNonConstantIntSafely) {
+  auto *Base =
+      DIBasicType::get(Context, dwarf::DW_TAG_base_type, "test_integer", 32, 0,
+                       dwarf::DW_ATE_signed, 100, DINode::FlagZero);
+  DILocalScope *Scope = getSubprogram();
+  DIFile *File = getFile();
+
+  auto *Lower1 = ConstantAsMetadata::get(UndefValue::get(Type::getInt32Ty(Context)));
+  auto *Lower2 =
+      ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, -7, true)));
+  auto *Upper = ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, 23, true)));
+
+  auto *N1 = DISubrangeType::get(Context, StringRef(), File, 101, Scope, 32, 0,
+                                 DINode::FlagZero, Base, Lower1, Upper,
+                                 nullptr, nullptr);
+  auto *N2 = DISubrangeType::get(Context, StringRef(), File, 102, Scope, 32, 0,
+                                 DINode::FlagZero, Base, Lower2, Upper,
+                                 nullptr, nullptr);
+  EXPECT_NE(N1, N2);
+}
+
 typedef MetadataTest DIGenericSubrangeTest;
 
 TEST_F(DIGenericSubrangeTest, fortranAssumedRankInt) {

>From 9c493289e5149e7ec35ef13ca30717b21714a319 Mon Sep 17 00:00:00 2001
From: lrycro <thscw973 at naver.com>
Date: Thu, 20 Aug 2026 22:48:02 +0900
Subject: [PATCH 2/3] [IR] Verify DISubrange/DISubrangeType bounds are
 ConstantInt, harden remaining call sites

Per review feedback: add a Verifier check instead of only patching call
sites. Verifier::visitDISubrangeType/visitDISubrange already required
LowerBound/UpperBound/Stride/Bias/CountNode to be ConstantAsMetadata (or
DIVariable/DIExpression/DIDerivedType), matching the error message
("must be signed constant"), but never checked the wrapped Constant was
actually a ConstantInt. Added isConstantIntMetadata() and use it in both
visit functions, so malformed IR is now rejected with a clear diagnostic
instead of asserting deep in unrelated code.

Also found and fixed the same unchecked cast<ConstantInt> in two more
places that can still be reached with programmatically-constructed
(non-verifier-checked) IR:
- DISubrange::getCount/getLowerBound/getUpperBound/getStride
  (DebugInfoMetadata.cpp) -- each had its own copy of the same pattern,
  separate from DISubrangeType::convertRawToBound fixed previously.
- MDFieldPrinter::printMetadataOrInt (AsmWriter.cpp) -- this is what the
  Verifier itself calls to render the offending node when a CheckDI
  fails, so without this fix the new Verifier check would crash while
  trying to report the very error it just caught.

Added VerifierTest.DISubrangeNonConstantIntBound(Type) confirming
verifyModule() now reports "LowerBound must be signed constant..."
instead of crashing.
---
 llvm/lib/IR/AsmWriter.cpp          | 16 +++++++-----
 llvm/lib/IR/DebugInfoMetadata.cpp  | 12 ++++++---
 llvm/lib/IR/Verifier.cpp           | 26 +++++++++++-------
 llvm/unittests/IR/VerifierTest.cpp | 42 ++++++++++++++++++++++++++++++
 4 files changed, 76 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index 8e57bff1d36c9..91fb87143bfcb 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -2048,13 +2048,15 @@ void MDFieldPrinter::printMetadataOrInt(StringRef Name, const Metadata *MD,
     return;
 
   if (auto *CI = dyn_cast<ConstantAsMetadata>(MD)) {
-    auto *CV = cast<ConstantInt>(CI->getValue());
-    if (IsUnsigned)
-      printInt(Name, CV->getZExtValue(), ShouldSkipZero);
-    else
-      printInt(Name, CV->getSExtValue(), ShouldSkipZero);
-  } else
-    printMetadata(Name, MD);
+    if (auto *CV = dyn_cast<ConstantInt>(CI->getValue())) {
+      if (IsUnsigned)
+        printInt(Name, CV->getZExtValue(), ShouldSkipZero);
+      else
+        printInt(Name, CV->getSExtValue(), ShouldSkipZero);
+      return;
+    }
+  }
+  printMetadata(Name, MD);
 }
 
 template <class IntTy>
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 7f20bb620166d..8ffe0e14f61a5 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -657,7 +657,8 @@ DISubrange::BoundType DISubrange::getCount() const {
          "Count must be signed constant or DIVariable or DIExpression");
 
   if (auto *MD = dyn_cast<ConstantAsMetadata>(CB))
-    return BoundType(cast<ConstantInt>(MD->getValue()));
+    if (auto *CV = dyn_cast<ConstantInt>(MD->getValue()))
+      return BoundType(CV);
 
   if (auto *MD = dyn_cast<DIVariable>(CB))
     return BoundType(MD);
@@ -678,7 +679,8 @@ DISubrange::BoundType DISubrange::getLowerBound() const {
          "LowerBound must be signed constant or DIVariable or DIExpression");
 
   if (auto *MD = dyn_cast<ConstantAsMetadata>(LB))
-    return BoundType(cast<ConstantInt>(MD->getValue()));
+    if (auto *CV = dyn_cast<ConstantInt>(MD->getValue()))
+      return BoundType(CV);
 
   if (auto *MD = dyn_cast<DIVariable>(LB))
     return BoundType(MD);
@@ -699,7 +701,8 @@ DISubrange::BoundType DISubrange::getUpperBound() const {
          "UpperBound must be signed constant or DIVariable or DIExpression");
 
   if (auto *MD = dyn_cast<ConstantAsMetadata>(UB))
-    return BoundType(cast<ConstantInt>(MD->getValue()));
+    if (auto *CV = dyn_cast<ConstantInt>(MD->getValue()))
+      return BoundType(CV);
 
   if (auto *MD = dyn_cast<DIVariable>(UB))
     return BoundType(MD);
@@ -720,7 +723,8 @@ DISubrange::BoundType DISubrange::getStride() const {
          "Stride must be signed constant or DIVariable or DIExpression");
 
   if (auto *MD = dyn_cast<ConstantAsMetadata>(ST))
-    return BoundType(cast<ConstantInt>(MD->getValue()));
+    if (auto *CV = dyn_cast<ConstantInt>(MD->getValue()))
+      return BoundType(CV);
 
   if (auto *MD = dyn_cast<DIVariable>(ST))
     return BoundType(MD);
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index f1b377798a4d9..78a05cdbf47fa 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1176,6 +1176,14 @@ void Verifier::visitMetadataAsValue(const MetadataAsValue &MDV, Function *F) {
 
 static bool isType(const Metadata *MD) { return !MD || isa<DIType>(MD); }
 static bool isScope(const Metadata *MD) { return !MD || isa<DIScope>(MD); }
+// Subrange bound fields accept a signed constant as ConstantAsMetadata, but
+// ConstantAsMetadata can wrap any Constant -- this additionally requires the
+// wrapped constant to actually be the ConstantInt the field's error message
+// (and its consumers) assume.
+static bool isConstantIntMetadata(const Metadata *MD) {
+  auto *CAM = dyn_cast_or_null<ConstantAsMetadata>(MD);
+  return CAM && isa<ConstantInt>(CAM->getValue());
+}
 static bool isDINode(const Metadata *MD) { return !MD || isa<DINode>(MD); }
 static bool isMDTuple(const Metadata *MD) { return !MD || isa<MDTuple>(MD); }
 
@@ -1211,30 +1219,30 @@ void Verifier::visitDISubrangeType(const DISubrangeType &N) {
   auto *BaseType = N.getRawBaseType();
   CheckDI(!BaseType || isType(BaseType), "BaseType must be a type");
   auto *LBound = N.getRawLowerBound();
-  CheckDI(!LBound || isa<ConstantAsMetadata>(LBound) ||
+  CheckDI(!LBound || isConstantIntMetadata(LBound) ||
               isa<DIVariable>(LBound) || isa<DIExpression>(LBound) ||
               isa<DIDerivedType>(LBound),
           "LowerBound must be signed constant or DIVariable or DIExpression or "
           "DIDerivedType",
           &N);
   auto *UBound = N.getRawUpperBound();
-  CheckDI(!UBound || isa<ConstantAsMetadata>(UBound) ||
+  CheckDI(!UBound || isConstantIntMetadata(UBound) ||
               isa<DIVariable>(UBound) || isa<DIExpression>(UBound) ||
               isa<DIDerivedType>(UBound),
           "UpperBound must be signed constant or DIVariable or DIExpression or "
           "DIDerivedType",
           &N);
   auto *Stride = N.getRawStride();
-  CheckDI(!Stride || isa<ConstantAsMetadata>(Stride) ||
+  CheckDI(!Stride || isConstantIntMetadata(Stride) ||
               isa<DIVariable>(Stride) || isa<DIExpression>(Stride),
           "Stride must be signed constant or DIVariable or DIExpression", &N);
   auto *Bias = N.getRawBias();
-  CheckDI(!Bias || isa<ConstantAsMetadata>(Bias) || isa<DIVariable>(Bias) ||
+  CheckDI(!Bias || isConstantIntMetadata(Bias) || isa<DIVariable>(Bias) ||
               isa<DIExpression>(Bias),
           "Bias must be signed constant or DIVariable or DIExpression", &N);
   // Subrange types currently only support constant size.
   auto *Size = N.getRawSizeInBits();
-  CheckDI(!Size || isa<ConstantAsMetadata>(Size),
+  CheckDI(!Size || isConstantIntMetadata(Size),
           "SizeInBits must be a constant");
 }
 
@@ -1243,7 +1251,7 @@ void Verifier::visitDISubrange(const DISubrange &N) {
   CheckDI(!N.getRawCountNode() || !N.getRawUpperBound(),
           "Subrange can have any one of count or upperBound", &N);
   auto *CBound = N.getRawCountNode();
-  CheckDI(!CBound || isa<ConstantAsMetadata>(CBound) ||
+  CheckDI(!CBound || isConstantIntMetadata(CBound) ||
               isa<DIVariable>(CBound) || isa<DIExpression>(CBound),
           "Count must be signed constant or DIVariable or DIExpression", &N);
   auto Count = N.getCount();
@@ -1251,17 +1259,17 @@ void Verifier::visitDISubrange(const DISubrange &N) {
               cast<ConstantInt *>(Count)->getSExtValue() >= -1,
           "invalid subrange count", &N);
   auto *LBound = N.getRawLowerBound();
-  CheckDI(!LBound || isa<ConstantAsMetadata>(LBound) ||
+  CheckDI(!LBound || isConstantIntMetadata(LBound) ||
               isa<DIVariable>(LBound) || isa<DIExpression>(LBound),
           "LowerBound must be signed constant or DIVariable or DIExpression",
           &N);
   auto *UBound = N.getRawUpperBound();
-  CheckDI(!UBound || isa<ConstantAsMetadata>(UBound) ||
+  CheckDI(!UBound || isConstantIntMetadata(UBound) ||
               isa<DIVariable>(UBound) || isa<DIExpression>(UBound),
           "UpperBound must be signed constant or DIVariable or DIExpression",
           &N);
   auto *Stride = N.getRawStride();
-  CheckDI(!Stride || isa<ConstantAsMetadata>(Stride) ||
+  CheckDI(!Stride || isConstantIntMetadata(Stride) ||
               isa<DIVariable>(Stride) || isa<DIExpression>(Stride),
           "Stride must be signed constant or DIVariable or DIExpression", &N);
 }
diff --git a/llvm/unittests/IR/VerifierTest.cpp b/llvm/unittests/IR/VerifierTest.cpp
index 893cab5b413cd..70924c7edb040 100644
--- a/llvm/unittests/IR/VerifierTest.cpp
+++ b/llvm/unittests/IR/VerifierTest.cpp
@@ -292,6 +292,48 @@ TEST(VerifierTest, DetectInvalidDebugInfo) {
   }
 }
 
+// DISubrange/DISubrangeType bound operands (LowerBound/UpperBound/Stride/
+// Bias/CountNode) accept a signed constant wrapped in ConstantAsMetadata,
+// but ConstantAsMetadata can wrap any Constant, not just a ConstantInt. The
+// textual IR parser only ever produces a ConstantInt for these fields, so
+// this is only reachable via programmatic IR construction.
+TEST(VerifierTest, DISubrangeNonConstantIntBound) {
+  LLVMContext C;
+  Module M("M", C);
+  auto *NonIntBound =
+      ConstantAsMetadata::get(UndefValue::get(Type::getInt32Ty(C)));
+  auto *N = DISubrange::get(C, nullptr, NonIntBound, nullptr, nullptr);
+  M.getOrInsertNamedMetadata("test")->addOperand(N);
+
+  std::string Error;
+  raw_string_ostream ErrorOS(Error);
+  EXPECT_TRUE(verifyModule(M, &ErrorOS));
+  EXPECT_TRUE(StringRef(Error).contains(
+      "LowerBound must be signed constant or DIVariable or DIExpression"))
+      << Error;
+}
+
+TEST(VerifierTest, DISubrangeTypeNonConstantIntBound) {
+  LLVMContext C;
+  Module M("M", C);
+  auto *Base =
+      DIBasicType::get(C, dwarf::DW_TAG_base_type, "int", 32, 0,
+                       dwarf::DW_ATE_signed, DINode::FlagZero);
+  auto *NonIntBound =
+      ConstantAsMetadata::get(UndefValue::get(Type::getInt32Ty(C)));
+  auto *N = DISubrangeType::get(C, StringRef(), nullptr, 0, nullptr, 32, 0,
+                                DINode::FlagZero, Base, NonIntBound, nullptr,
+                                nullptr, nullptr);
+  M.getOrInsertNamedMetadata("test")->addOperand(N);
+
+  std::string Error;
+  raw_string_ostream ErrorOS(Error);
+  EXPECT_TRUE(verifyModule(M, &ErrorOS));
+  EXPECT_TRUE(StringRef(Error).contains(
+      "LowerBound must be signed constant or DIVariable or DIExpression"))
+      << Error;
+}
+
 TEST(VerifierTest, MDNodeWrongContext) {
   LLVMContext C1, C2;
   auto *Node = MDNode::get(C1, {});

>From acc9b40b13dea684af423dbe5a68c726a1150fd7 Mon Sep 17 00:00:00 2001
From: lrycro <thscw973 at naver.com>
Date: Sat, 12 Sep 2026 18:01:41 +0900
Subject: [PATCH 3/3] Apply clang-format; replace UndefValue with PoisonValue
 in new tests

No behavioral change.
---
 llvm/lib/IR/LLVMContextImpl.h      |  3 +--
 llvm/lib/IR/Verifier.cpp           | 30 +++++++++++++--------------
 llvm/unittests/IR/MetadataTest.cpp | 33 ++++++++++++++++--------------
 llvm/unittests/IR/VerifierTest.cpp |  9 ++++----
 4 files changed, 37 insertions(+), 38 deletions(-)

diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index fabeda231b684..f44a95af0c426 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -409,8 +409,7 @@ template <> struct MDNodeKeyImpl<DIGenericSubrange> {
     auto *MD = dyn_cast_or_null<ConstantAsMetadata>(CountNode);
     if (CountNode && MD)
       if (auto *CV = dyn_cast<ConstantInt>(MD->getValue()))
-        return hash_combine(CV->getSExtValue(), LowerBound, UpperBound,
-                            Stride);
+        return hash_combine(CV->getSExtValue(), LowerBound, UpperBound, Stride);
     return hash_combine(CountNode, LowerBound, UpperBound, Stride);
   }
 };
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 78a05cdbf47fa..a8f4a2c07e53c 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1219,22 +1219,20 @@ void Verifier::visitDISubrangeType(const DISubrangeType &N) {
   auto *BaseType = N.getRawBaseType();
   CheckDI(!BaseType || isType(BaseType), "BaseType must be a type");
   auto *LBound = N.getRawLowerBound();
-  CheckDI(!LBound || isConstantIntMetadata(LBound) ||
-              isa<DIVariable>(LBound) || isa<DIExpression>(LBound) ||
-              isa<DIDerivedType>(LBound),
+  CheckDI(!LBound || isConstantIntMetadata(LBound) || isa<DIVariable>(LBound) ||
+              isa<DIExpression>(LBound) || isa<DIDerivedType>(LBound),
           "LowerBound must be signed constant or DIVariable or DIExpression or "
           "DIDerivedType",
           &N);
   auto *UBound = N.getRawUpperBound();
-  CheckDI(!UBound || isConstantIntMetadata(UBound) ||
-              isa<DIVariable>(UBound) || isa<DIExpression>(UBound) ||
-              isa<DIDerivedType>(UBound),
+  CheckDI(!UBound || isConstantIntMetadata(UBound) || isa<DIVariable>(UBound) ||
+              isa<DIExpression>(UBound) || isa<DIDerivedType>(UBound),
           "UpperBound must be signed constant or DIVariable or DIExpression or "
           "DIDerivedType",
           &N);
   auto *Stride = N.getRawStride();
-  CheckDI(!Stride || isConstantIntMetadata(Stride) ||
-              isa<DIVariable>(Stride) || isa<DIExpression>(Stride),
+  CheckDI(!Stride || isConstantIntMetadata(Stride) || isa<DIVariable>(Stride) ||
+              isa<DIExpression>(Stride),
           "Stride must be signed constant or DIVariable or DIExpression", &N);
   auto *Bias = N.getRawBias();
   CheckDI(!Bias || isConstantIntMetadata(Bias) || isa<DIVariable>(Bias) ||
@@ -1251,26 +1249,26 @@ void Verifier::visitDISubrange(const DISubrange &N) {
   CheckDI(!N.getRawCountNode() || !N.getRawUpperBound(),
           "Subrange can have any one of count or upperBound", &N);
   auto *CBound = N.getRawCountNode();
-  CheckDI(!CBound || isConstantIntMetadata(CBound) ||
-              isa<DIVariable>(CBound) || isa<DIExpression>(CBound),
+  CheckDI(!CBound || isConstantIntMetadata(CBound) || isa<DIVariable>(CBound) ||
+              isa<DIExpression>(CBound),
           "Count must be signed constant or DIVariable or DIExpression", &N);
   auto Count = N.getCount();
   CheckDI(!Count || !isa<ConstantInt *>(Count) ||
               cast<ConstantInt *>(Count)->getSExtValue() >= -1,
           "invalid subrange count", &N);
   auto *LBound = N.getRawLowerBound();
-  CheckDI(!LBound || isConstantIntMetadata(LBound) ||
-              isa<DIVariable>(LBound) || isa<DIExpression>(LBound),
+  CheckDI(!LBound || isConstantIntMetadata(LBound) || isa<DIVariable>(LBound) ||
+              isa<DIExpression>(LBound),
           "LowerBound must be signed constant or DIVariable or DIExpression",
           &N);
   auto *UBound = N.getRawUpperBound();
-  CheckDI(!UBound || isConstantIntMetadata(UBound) ||
-              isa<DIVariable>(UBound) || isa<DIExpression>(UBound),
+  CheckDI(!UBound || isConstantIntMetadata(UBound) || isa<DIVariable>(UBound) ||
+              isa<DIExpression>(UBound),
           "UpperBound must be signed constant or DIVariable or DIExpression",
           &N);
   auto *Stride = N.getRawStride();
-  CheckDI(!Stride || isConstantIntMetadata(Stride) ||
-              isa<DIVariable>(Stride) || isa<DIExpression>(Stride),
+  CheckDI(!Stride || isConstantIntMetadata(Stride) || isa<DIVariable>(Stride) ||
+              isa<DIExpression>(Stride),
           "Stride must be signed constant or DIVariable or DIExpression", &N);
 }
 
diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp
index 32b28d33f928d..f45f742db41e8 100644
--- a/llvm/unittests/IR/MetadataTest.cpp
+++ b/llvm/unittests/IR/MetadataTest.cpp
@@ -2001,12 +2001,12 @@ TEST_F(DISubrangeTest, fortranAllocatableExpr) {
 // DISubrangeType counterpart) must not crash when a bound operand is
 // ConstantAsMetadata wrapping a non-ConstantInt Constant.
 TEST_F(DISubrangeTest, boundsCompareNonConstantIntSafely) {
-  auto *LowerUndef =
-      ConstantAsMetadata::get(UndefValue::get(Type::getInt64Ty(Context)));
+  auto *LowerPoison =
+      ConstantAsMetadata::get(PoisonValue::get(Type::getInt64Ty(Context)));
   auto *LowerInt = ConstantAsMetadata::get(
       ConstantInt::getSigned(Type::getInt64Ty(Context), -7));
 
-  auto *N1 = DISubrange::get(Context, nullptr, LowerUndef, nullptr, nullptr);
+  auto *N1 = DISubrange::get(Context, nullptr, LowerPoison, nullptr, nullptr);
   auto *N2 = DISubrange::get(Context, nullptr, LowerInt, nullptr, nullptr);
   EXPECT_NE(N1, N2);
 }
@@ -2064,19 +2064,20 @@ TEST_F(DISubrangeTypeTest, boundsEqualDistinctConstantAsMetadata) {
   auto *Lower2 =
       ConstantAsMetadata::get(ConstantInt::get(Context, APInt(64, -7, true)));
   ASSERT_NE(Lower1, Lower2);
-  auto *Upper = ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, 23, true)));
+  auto *Upper =
+      ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, 23, true)));
 
   auto *N1 = DISubrangeType::get(Context, StringRef(), File, 101, Scope, 32, 0,
-                                 DINode::FlagZero, Base, Lower1, Upper,
-                                 nullptr, nullptr);
+                                 DINode::FlagZero, Base, Lower1, Upper, nullptr,
+                                 nullptr);
   auto *N2 = DISubrangeType::get(Context, StringRef(), File, 101, Scope, 32, 0,
-                                 DINode::FlagZero, Base, Lower2, Upper,
-                                 nullptr, nullptr);
+                                 DINode::FlagZero, Base, Lower2, Upper, nullptr,
+                                 nullptr);
   EXPECT_EQ(N1, N2);
 }
 
 // Regression test: a bound operand may be ConstantAsMetadata wrapping a
-// Constant that is not a ConstantInt (e.g. UndefValue). isKeyOf() must not
+// Constant that is not a ConstantInt (e.g. PoisonValue). isKeyOf() must not
 // crash comparing such nodes -- it should safely treat them as unequal
 // instead of unconditionally casting to ConstantInt.
 TEST_F(DISubrangeTypeTest, boundsCompareNonConstantIntSafely) {
@@ -2086,17 +2087,19 @@ TEST_F(DISubrangeTypeTest, boundsCompareNonConstantIntSafely) {
   DILocalScope *Scope = getSubprogram();
   DIFile *File = getFile();
 
-  auto *Lower1 = ConstantAsMetadata::get(UndefValue::get(Type::getInt32Ty(Context)));
+  auto *Lower1 =
+      ConstantAsMetadata::get(PoisonValue::get(Type::getInt32Ty(Context)));
   auto *Lower2 =
       ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, -7, true)));
-  auto *Upper = ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, 23, true)));
+  auto *Upper =
+      ConstantAsMetadata::get(ConstantInt::get(Context, APInt(32, 23, true)));
 
   auto *N1 = DISubrangeType::get(Context, StringRef(), File, 101, Scope, 32, 0,
-                                 DINode::FlagZero, Base, Lower1, Upper,
-                                 nullptr, nullptr);
+                                 DINode::FlagZero, Base, Lower1, Upper, nullptr,
+                                 nullptr);
   auto *N2 = DISubrangeType::get(Context, StringRef(), File, 102, Scope, 32, 0,
-                                 DINode::FlagZero, Base, Lower2, Upper,
-                                 nullptr, nullptr);
+                                 DINode::FlagZero, Base, Lower2, Upper, nullptr,
+                                 nullptr);
   EXPECT_NE(N1, N2);
 }
 
diff --git a/llvm/unittests/IR/VerifierTest.cpp b/llvm/unittests/IR/VerifierTest.cpp
index 70924c7edb040..1de31440ea8c1 100644
--- a/llvm/unittests/IR/VerifierTest.cpp
+++ b/llvm/unittests/IR/VerifierTest.cpp
@@ -301,7 +301,7 @@ TEST(VerifierTest, DISubrangeNonConstantIntBound) {
   LLVMContext C;
   Module M("M", C);
   auto *NonIntBound =
-      ConstantAsMetadata::get(UndefValue::get(Type::getInt32Ty(C)));
+      ConstantAsMetadata::get(PoisonValue::get(Type::getInt32Ty(C)));
   auto *N = DISubrange::get(C, nullptr, NonIntBound, nullptr, nullptr);
   M.getOrInsertNamedMetadata("test")->addOperand(N);
 
@@ -316,11 +316,10 @@ TEST(VerifierTest, DISubrangeNonConstantIntBound) {
 TEST(VerifierTest, DISubrangeTypeNonConstantIntBound) {
   LLVMContext C;
   Module M("M", C);
-  auto *Base =
-      DIBasicType::get(C, dwarf::DW_TAG_base_type, "int", 32, 0,
-                       dwarf::DW_ATE_signed, DINode::FlagZero);
+  auto *Base = DIBasicType::get(C, dwarf::DW_TAG_base_type, "int", 32, 0,
+                                dwarf::DW_ATE_signed, DINode::FlagZero);
   auto *NonIntBound =
-      ConstantAsMetadata::get(UndefValue::get(Type::getInt32Ty(C)));
+      ConstantAsMetadata::get(PoisonValue::get(Type::getInt32Ty(C)));
   auto *N = DISubrangeType::get(C, StringRef(), nullptr, 0, nullptr, 32, 0,
                                 DINode::FlagZero, Base, NonIntBound, nullptr,
                                 nullptr, nullptr);



More information about the llvm-commits mailing list