[ArrayRef] Provide constructors from any type with a data() member (#145735)

The assumption here is that if data() returns a pointer and size()
exists it's something representing contiguous storage.

Modeled after the behavior of C++20 std::span and enables two-way
conversion between std::span (or really any span-like type) and
ArrayRef. Add a unit test that verifies this if std::span is around.

This also means we can get rid of specific conversions for std::vector
and SmallVector.
This commit is contained in:
Benjamin Kramer
2025-06-25 19:33:34 +02:00
committed by GitHub
parent df79c40c98
commit 0556a2aa18
2 changed files with 31 additions and 39 deletions

View File

@@ -84,18 +84,19 @@ namespace llvm {
assert(begin <= end);
}
/// Construct an ArrayRef from a SmallVector. This is templated in order to
/// avoid instantiating SmallVectorTemplateCommon<T> whenever we
/// copy-construct an ArrayRef.
template<typename U>
/*implicit*/ ArrayRef(const SmallVectorTemplateCommon<T, U> &Vec)
: Data(Vec.data()), Length(Vec.size()) {
}
/// Construct an ArrayRef from a std::vector.
template<typename A>
/*implicit*/ ArrayRef(const std::vector<T, A> &Vec)
: Data(Vec.data()), Length(Vec.size()) {}
/// Construct an ArrayRef from a type that has a data() method that returns
/// a pointer convertible to const T *.
template <
typename C,
typename = std::enable_if_t<
std::conjunction_v<
std::is_convertible<
decltype(std::declval<const C &>().data()) *,
const T *const *>,
std::is_integral<decltype(std::declval<const C &>().size())>>,
void>>
/*implicit*/ constexpr ArrayRef(const C &V)
: Data(V.data()), Length(V.size()) {}
/// Construct an ArrayRef from a std::array
template <size_t N>
@@ -123,32 +124,6 @@ namespace llvm {
#pragma GCC diagnostic pop
#endif
/// Construct an ArrayRef<const T*> from ArrayRef<T*>. This uses SFINAE to
/// ensure that only ArrayRefs of pointers can be converted.
template <typename U>
ArrayRef(const ArrayRef<U *> &A,
std::enable_if_t<std::is_convertible<U *const *, T const *>::value>
* = nullptr)
: Data(A.data()), Length(A.size()) {}
/// Construct an ArrayRef<const T*> from a SmallVector<T*>. This is
/// templated in order to avoid instantiating SmallVectorTemplateCommon<T>
/// whenever we copy-construct an ArrayRef.
template <typename U, typename DummyT>
/*implicit*/ ArrayRef(
const SmallVectorTemplateCommon<U *, DummyT> &Vec,
std::enable_if_t<std::is_convertible<U *const *, T const *>::value> * =
nullptr)
: Data(Vec.data()), Length(Vec.size()) {}
/// Construct an ArrayRef<const T*> from std::vector<T*>. This uses SFINAE
/// to ensure that only vectors of pointers can be converted.
template <typename U, typename A>
ArrayRef(const std::vector<U *, A> &Vec,
std::enable_if_t<std::is_convertible<U *const *, T const *>::value>
* = nullptr)
: Data(Vec.data()), Length(Vec.size()) {}
/// 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.

View File

@@ -8,10 +8,16 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
#include "gtest/gtest.h"
#include <limits>
#include <vector>
#if __has_include(<version>)
#include <version>
#endif
#ifdef __cpp_lib_span
#include <span>
#endif
using namespace llvm;
// Check that the ArrayRef-of-pointer converting constructor only allows adding
@@ -406,4 +412,15 @@ TEST(ArrayRefTest, MutableArrayRefDeductionGuides) {
}
}
#ifdef __cpp_lib_span
static_assert(std::is_constructible_v<ArrayRef<int>, std::span<const int>>,
"should be able to construct ArrayRef from const std::span");
static_assert(std::is_constructible_v<std::span<const int>, ArrayRef<int>>,
"should be able to construct const std::span from ArrayRef");
static_assert(std::is_constructible_v<ArrayRef<int>, std::span<int>>,
"should be able to construct ArrayRef from mutable std::span");
static_assert(!std::is_constructible_v<std::span<int>, ArrayRef<int>>,
"cannot construct mutable std::span from ArrayRef");
#endif
} // end anonymous namespace