[llvm] [ArrayRef] Make iterator_range constructor const-agnostic (PR #205183)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 3 07:39:34 PDT 2026


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/205183

>From 8ef9b3b92ce0e256e70a9c08a89632aa4211f2b9 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Mon, 22 Jun 2026 20:21:19 +0100
Subject: [PATCH 1/3] [ArrayRef] Relax iterator_range constructor (NFC)

The enable_if guard is too restrictive, and stripping it does not lead
to worse compile-time errors on modern C++ compilers, or incorrect
results. The compiler would automatically check that the Data member is
initializable with the type T *const *.
---
 llvm/include/llvm/ADT/ArrayRef.h              |  9 +--
 llvm/lib/Transforms/Vectorize/VPlan.h         |  3 +
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp |  8 +-
 llvm/unittests/ADT/ArrayRefTest.cpp           | 77 +++++++++----------
 4 files changed, 45 insertions(+), 52 deletions(-)

diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h
index 6f12564c00c1e..e9552fe45d9b2 100644
--- a/llvm/include/llvm/ADT/ArrayRef.h
+++ b/llvm/include/llvm/ADT/ArrayRef.h
@@ -114,12 +114,9 @@ template <typename T> class LLVM_GSL_POINTER [[nodiscard]] ArrayRef {
 #pragma GCC diagnostic pop
 #endif
 
-  /// Construct an ArrayRef<T> from iterator_range<U*>. This uses SFINAE
-  /// to ensure that this is only used for iterator ranges over plain pointer
-  /// iterators.
-  template <typename U, typename = std::enable_if_t<
-                            std::is_convertible_v<U *const *, T *const *>>>
-  ArrayRef(const iterator_range<U *> &Range)
+  /// Construct an ArrayRef from an iterator_range.
+  template <typename U>
+  ArrayRef(iterator_range<U> &&Range)
       : Data(Range.begin()), Length(llvm::size(Range)) {}
 
   /// @}
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 3e7be2d67733a..0e6ff4ffdd3be 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3468,6 +3468,9 @@ class LLVM_ABI_FOR_TEST VPReplicateRecipe : public VPRecipeWithIRFlags,
   operand_range operandsWithoutMask() {
     return isPredicated() ? drop_end(operands()) : operands();
   }
+  const_operand_range operandsWithoutMask() const {
+    return isPredicated() ? drop_end(operands()) : operands();
+  }
 
   /// Returns the number of operands, excluding the mask if the recipe is
   /// predicated.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 24abcf7646066..b8d7055b22de9 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -2308,8 +2308,7 @@ InstructionCost VPWidenIntrinsicRecipe::computeCallCost(
 
 InstructionCost VPWidenIntrinsicRecipe::computeCost(ElementCount VF,
                                                     VPCostContext &Ctx) const {
-  SmallVector<const VPValue *> ArgOps(operands());
-  return computeCallCost(VectorIntrinsicID, ArgOps, *this, VF, Ctx);
+  return computeCallCost(VectorIntrinsicID, operands(), *this, VF, Ctx);
 }
 
 StringRef VPWidenIntrinsicRecipe::getIntrinsicName() const {
@@ -3804,9 +3803,8 @@ InstructionCost VPReplicateRecipe::computeCost(ElementCount VF,
     auto *CalledFn =
         cast<Function>(getOperand(getNumOperands() - 1)->getLiveInIRValue());
     Type *ResultTy = this->getScalarType();
-    SmallVector<const VPValue *> ArgOps(drop_end(operands()));
-    return computeCallCost(CalledFn, ResultTy, ArgOps, isSingleScalar(), VF,
-                           Ctx);
+    return computeCallCost(CalledFn, ResultTy, operandsWithoutMask(),
+                           isSingleScalar(), VF, Ctx);
   }
   case Instruction::Add:
   case Instruction::Sub:
diff --git a/llvm/unittests/ADT/ArrayRefTest.cpp b/llvm/unittests/ADT/ArrayRefTest.cpp
index 622616f8144f1..77fa4b912d325 100644
--- a/llvm/unittests/ADT/ArrayRefTest.cpp
+++ b/llvm/unittests/ADT/ArrayRefTest.cpp
@@ -317,47 +317,6 @@ TEST(ArrayRefTest, ArrayRefFromStdArray) {
   }
 }
 
-struct TestRandomAccessIterator {
-  using iterator_category = std::random_access_iterator_tag;
-};
-
-static_assert(!std::is_constructible_v<
-                  ArrayRef<int>, iterator_range<TestRandomAccessIterator>>,
-              "cannot construct from iterator range with non-pointer iterator");
-static_assert(!std::is_constructible_v<ArrayRef<int>, iterator_range<int>>,
-              "cannot construct from iterator range with non-pointer iterator");
-
-class TestBase {};
-
-class TestDerived : public TestBase {};
-
-static_assert(
-    !std::is_constructible_v<ArrayRef<TestDerived>, iterator_range<TestBase *>>,
-    "cannot construct ArrayRef with derived type");
-static_assert(
-    !std::is_constructible_v<ArrayRef<TestBase>, iterator_range<TestDerived *>>,
-    "cannot construct ArrayRef base type");
-static_assert(!std::is_constructible_v<ArrayRef<TestBase *>,
-                                       iterator_range<TestDerived **>>,
-              "cannot construct ArrayRef pointer of base type");
-static_assert(!std::is_constructible_v<MutableArrayRef<TestBase>,
-                                       iterator_range<TestDerived *>>,
-              "cannot construct MutableArrayRef base type");
-static_assert(!std::is_constructible_v<MutableArrayRef<TestBase *>,
-                                       iterator_range<TestDerived **>>,
-              "cannot construct MutableArrayRef pointer of base type");
-
-static_assert(
-    !std::is_constructible_v<ArrayRef<int>, iterator_range<const int *>>,
-    "cannot construct ArrayRef with non-const elements from const iterator "
-    "range");
-static_assert(
-    std::is_constructible_v<ArrayRef<char *>, iterator_range<char **>>,
-    "should be able to construct ArrayRef from iterator_range over pointers");
-static_assert(
-    !std::is_constructible_v<ArrayRef<char *>, iterator_range<char *const *>>,
-    "should be able to construct ArrayRef from iterator_range over pointers");
-
 TEST(ArrayRefTest, ArrayRefFromIteratorRange) {
   int A1[] = {42, -5, 0, 1000000, -1000000, 0};
   ArrayRef<int> A2 = make_range(&A1[0], &A1[5]);
@@ -372,8 +331,26 @@ TEST(ArrayRefTest, ArrayRefFromIteratorRange) {
     EXPECT_EQ(A1[i], A3[i]);
 }
 
+TEST(ArrayRefTest, ArrayConstRefIteratorConstRange) {
+  const int A1[] = {42, -5, 0, 1000000, -1000000, 0};
+  ArrayRef<const int> A2 = make_range(&A1[0], &A1[5]);
+
+  EXPECT_EQ(5ull, A2.size());
+  for (std::size_t i = 0; i < A2.size(); ++i)
+    EXPECT_EQ(A1[i], A2[i]);
+}
+
 TEST(ArrayRefTest, ArrayRefFromIteratorConstRange) {
   const int A1[] = {42, -5, 0, 1000000, -1000000, 0};
+  ArrayRef<int> A2 = make_range(&A1[0], &A1[5]);
+
+  EXPECT_EQ(5ull, A2.size());
+  for (std::size_t i = 0; i < A2.size(); ++i)
+    EXPECT_EQ(A1[i], A2[i]);
+}
+
+TEST(ArrayRefTest, ArrayConstRefFromIteratorRange) {
+  int A1[] = {42, -5, 0, 1000000, -1000000, 0};
   ArrayRef<const int> A2 = make_range(&A1[0], &A1[5]);
 
   EXPECT_EQ(5ull, A2.size());
@@ -381,6 +358,24 @@ TEST(ArrayRefTest, ArrayRefFromIteratorConstRange) {
     EXPECT_EQ(A1[i], A2[i]);
 }
 
+struct TestBase {
+  int M;
+  TestBase(int M) : M(M) {}
+};
+
+struct TestDerived : public TestBase {
+  TestDerived(int M) : TestBase(M) {}
+};
+
+TEST(ArrayRefTest, ArrayBaseRefFromIteratorDerivedRange) {
+  TestDerived A1[] = {42, -5, 0, 1000000, -1000000, 0};
+  ArrayRef<TestBase> A2 = make_range(&A1[0], &A1[5]);
+
+  EXPECT_EQ(5ull, A2.size());
+  for (std::size_t i = 0; i < A2.size(); ++i)
+    EXPECT_EQ(A1[i].M, A2[i].M);
+}
+
 static_assert(std::is_trivially_copyable_v<ArrayRef<int>>,
               "trivially copyable");
 

>From 0ff050c95774e89787ca15f43d471ecf04cf970e Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Mon, 22 Jun 2026 22:15:43 +0100
Subject: [PATCH 2/3] [ArrayRef] Avoid rvalue; attempt mlir build-fix

---
 llvm/include/llvm/ADT/ArrayRef.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h
index e9552fe45d9b2..2dc7e463006e1 100644
--- a/llvm/include/llvm/ADT/ArrayRef.h
+++ b/llvm/include/llvm/ADT/ArrayRef.h
@@ -116,7 +116,7 @@ template <typename T> class LLVM_GSL_POINTER [[nodiscard]] ArrayRef {
 
   /// Construct an ArrayRef from an iterator_range.
   template <typename U>
-  ArrayRef(iterator_range<U> &&Range)
+  ArrayRef(const iterator_range<U *> &Range)
       : Data(Range.begin()), Length(llvm::size(Range)) {}
 
   /// @}

>From f394b90972060ee886deeab53e6370f7dc29e302 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Wed, 24 Jun 2026 13:09:42 +0100
Subject: [PATCH 3/3] [ArrayRef] Re-instate enable_if, improving it; remove
 VPlan changes

---
 llvm/include/llvm/ADT/ArrayRef.h              |  7 +-
 llvm/lib/Transforms/Vectorize/VPlan.h         |  3 -
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp |  8 +-
 llvm/unittests/ADT/ArrayRefTest.cpp           | 76 ++++++++++++-------
 4 files changed, 59 insertions(+), 35 deletions(-)

diff --git a/llvm/include/llvm/ADT/ArrayRef.h b/llvm/include/llvm/ADT/ArrayRef.h
index 2dc7e463006e1..ed91543e45bf1 100644
--- a/llvm/include/llvm/ADT/ArrayRef.h
+++ b/llvm/include/llvm/ADT/ArrayRef.h
@@ -114,8 +114,11 @@ template <typename T> class LLVM_GSL_POINTER [[nodiscard]] ArrayRef {
 #pragma GCC diagnostic pop
 #endif
 
-  /// Construct an ArrayRef from an iterator_range.
-  template <typename U>
+  /// Construct an ArrayRef<T> from iterator_range<U*>. This uses SFINAE
+  /// to ensure that this is only used for iterator ranges over plain pointer
+  /// iterators.
+  template <typename U, typename = std::enable_if_t<std::is_convertible_v<
+                            U *const *, std::add_const_t<T> *const *>>>
   ArrayRef(const iterator_range<U *> &Range)
       : Data(Range.begin()), Length(llvm::size(Range)) {}
 
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 0e6ff4ffdd3be..3e7be2d67733a 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3468,9 +3468,6 @@ class LLVM_ABI_FOR_TEST VPReplicateRecipe : public VPRecipeWithIRFlags,
   operand_range operandsWithoutMask() {
     return isPredicated() ? drop_end(operands()) : operands();
   }
-  const_operand_range operandsWithoutMask() const {
-    return isPredicated() ? drop_end(operands()) : operands();
-  }
 
   /// Returns the number of operands, excluding the mask if the recipe is
   /// predicated.
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index b8d7055b22de9..24abcf7646066 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -2308,7 +2308,8 @@ InstructionCost VPWidenIntrinsicRecipe::computeCallCost(
 
 InstructionCost VPWidenIntrinsicRecipe::computeCost(ElementCount VF,
                                                     VPCostContext &Ctx) const {
-  return computeCallCost(VectorIntrinsicID, operands(), *this, VF, Ctx);
+  SmallVector<const VPValue *> ArgOps(operands());
+  return computeCallCost(VectorIntrinsicID, ArgOps, *this, VF, Ctx);
 }
 
 StringRef VPWidenIntrinsicRecipe::getIntrinsicName() const {
@@ -3803,8 +3804,9 @@ InstructionCost VPReplicateRecipe::computeCost(ElementCount VF,
     auto *CalledFn =
         cast<Function>(getOperand(getNumOperands() - 1)->getLiveInIRValue());
     Type *ResultTy = this->getScalarType();
-    return computeCallCost(CalledFn, ResultTy, operandsWithoutMask(),
-                           isSingleScalar(), VF, Ctx);
+    SmallVector<const VPValue *> ArgOps(drop_end(operands()));
+    return computeCallCost(CalledFn, ResultTy, ArgOps, isSingleScalar(), VF,
+                           Ctx);
   }
   case Instruction::Add:
   case Instruction::Sub:
diff --git a/llvm/unittests/ADT/ArrayRefTest.cpp b/llvm/unittests/ADT/ArrayRefTest.cpp
index 77fa4b912d325..f99168df0670c 100644
--- a/llvm/unittests/ADT/ArrayRefTest.cpp
+++ b/llvm/unittests/ADT/ArrayRefTest.cpp
@@ -317,6 +317,55 @@ TEST(ArrayRefTest, ArrayRefFromStdArray) {
   }
 }
 
+struct TestRandomAccessIterator {
+  using iterator_category = std::random_access_iterator_tag;
+};
+
+static_assert(!std::is_constructible_v<
+                  ArrayRef<int>, iterator_range<TestRandomAccessIterator>>,
+              "cannot construct from iterator range with non-pointer iterator");
+static_assert(!std::is_constructible_v<ArrayRef<int>, iterator_range<int>>,
+              "cannot construct from iterator range with non-pointer iterator");
+
+class TestBase {};
+
+class TestDerived : public TestBase {};
+
+static_assert(
+    !std::is_constructible_v<ArrayRef<TestDerived>, iterator_range<TestBase *>>,
+    "cannot construct ArrayRef with derived type");
+static_assert(
+    !std::is_constructible_v<ArrayRef<TestBase>, iterator_range<TestDerived *>>,
+    "cannot construct ArrayRef base type");
+static_assert(!std::is_constructible_v<ArrayRef<TestBase *>,
+                                       iterator_range<TestDerived **>>,
+              "cannot construct ArrayRef pointer of base type");
+static_assert(!std::is_constructible_v<MutableArrayRef<TestBase>,
+                                       iterator_range<TestDerived *>>,
+              "cannot construct MutableArrayRef base type");
+static_assert(!std::is_constructible_v<MutableArrayRef<TestBase *>,
+                                       iterator_range<TestDerived **>>,
+              "cannot construct MutableArrayRef pointer of base type");
+
+static_assert(
+    std::is_constructible_v<ArrayRef<int>, iterator_range<const int *>>,
+    "should be able to construct ArrayRef with non-const elements from const "
+    "iterator_range");
+static_assert(
+    std::is_constructible_v<ArrayRef<const int>, iterator_range<int *>>,
+    "should be able to construct ArrayRef with const elements from non-const "
+    "iterator_range");
+static_assert(
+    std::is_constructible_v<ArrayRef<const int>, iterator_range<const int *>>,
+    "should be able to construct ArrayRef with const elements from const "
+    "iterator_range");
+static_assert(
+    std::is_constructible_v<ArrayRef<char *>, iterator_range<char **>>,
+    "should be able to construct ArrayRef from iterator_range over pointers");
+static_assert(
+    std::is_constructible_v<ArrayRef<char *>, iterator_range<char *const *>>,
+    "should be able to construct ArrayRef from iterator_range over pointers");
+
 TEST(ArrayRefTest, ArrayRefFromIteratorRange) {
   int A1[] = {42, -5, 0, 1000000, -1000000, 0};
   ArrayRef<int> A2 = make_range(&A1[0], &A1[5]);
@@ -349,33 +398,6 @@ TEST(ArrayRefTest, ArrayRefFromIteratorConstRange) {
     EXPECT_EQ(A1[i], A2[i]);
 }
 
-TEST(ArrayRefTest, ArrayConstRefFromIteratorRange) {
-  int A1[] = {42, -5, 0, 1000000, -1000000, 0};
-  ArrayRef<const int> A2 = make_range(&A1[0], &A1[5]);
-
-  EXPECT_EQ(5ull, A2.size());
-  for (std::size_t i = 0; i < A2.size(); ++i)
-    EXPECT_EQ(A1[i], A2[i]);
-}
-
-struct TestBase {
-  int M;
-  TestBase(int M) : M(M) {}
-};
-
-struct TestDerived : public TestBase {
-  TestDerived(int M) : TestBase(M) {}
-};
-
-TEST(ArrayRefTest, ArrayBaseRefFromIteratorDerivedRange) {
-  TestDerived A1[] = {42, -5, 0, 1000000, -1000000, 0};
-  ArrayRef<TestBase> A2 = make_range(&A1[0], &A1[5]);
-
-  EXPECT_EQ(5ull, A2.size());
-  for (std::size_t i = 0; i < A2.size(); ++i)
-    EXPECT_EQ(A1[i].M, A2[i].M);
-}
-
 static_assert(std::is_trivially_copyable_v<ArrayRef<int>>,
               "trivially copyable");
 



More information about the llvm-commits mailing list