[utils] Mark dawn::Span satisfying ranges::view/borrowed_range

This is necessary to allow dawn::Span to be implicitly converted to
std::span.

Modifies the CompatibleRange condition to check range concepts and fix
FakedRang types used in tests to be actual ranges with begin/end.

Add begin/end (and tests) to ityp::stack_vec to make it a range and add
static_assert in other ityp:: containers to check that there are
considered contiguous_range.

Bug: 524776858
Change-Id: I8909b4c92b56b72754c83db9f39432d61cadef17
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/319875
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn/common/ityp_stack_vec.h b/src/dawn/common/ityp_stack_vec.h
index aeb0285..71fb9a7 100644
--- a/src/dawn/common/ityp_stack_vec.h
+++ b/src/dawn/common/ityp_stack_vec.h
@@ -51,8 +51,10 @@
         Base::resize(checked_cast<size_t>(size), value);
     }
 
+    using Base::begin;
     using Base::data;
     using Base::empty;
+    using Base::end;
 
     constexpr Value& operator[](Index i) {
 #if !defined(ABSL_OPTION_HARDENED) || ABSL_OPTION_HARDENED == 0 || \
diff --git a/src/dawn/tests/unittests/ITypArrayTests.cpp b/src/dawn/tests/unittests/ITypArrayTests.cpp
index e686883..a51f6d3 100644
--- a/src/dawn/tests/unittests/ITypArrayTests.cpp
+++ b/src/dawn/tests/unittests/ITypArrayTests.cpp
@@ -25,6 +25,8 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#include <ranges>
+
 #include "src/dawn/common/ityp_array.h"
 #include "src/utils/gtest.h"
 #include "src/utils/typed_integer.h"
@@ -47,6 +49,9 @@
         static_assert(kArr.at(Key(7u)) == Val(7u));
         static_assert(kArr.size() == Key(10u));
     };
+
+    // Check that ityp::array can be used as a range.
+    static_assert(std::ranges::contiguous_range<Array>);
 };
 
 // Test that values can be set at an index and retrieved from the same index.
diff --git a/src/dawn/tests/unittests/ITypStackVecTests.cpp b/src/dawn/tests/unittests/ITypStackVecTests.cpp
index c79e359..3dbed0f 100644
--- a/src/dawn/tests/unittests/ITypStackVecTests.cpp
+++ b/src/dawn/tests/unittests/ITypStackVecTests.cpp
@@ -25,6 +25,7 @@
 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#include <ranges>
 #include <utility>
 
 #include "src/dawn/common/ityp_stack_vec.h"
@@ -40,6 +41,9 @@
     using Val = TypedInteger<struct ValT, uint32_t>;
 
     using StackVec = ityp::stack_vec<Key, Val, 10>;
+
+    // Check that ityp::stack_vec can be used as a range.
+    static_assert(std::ranges::contiguous_range<StackVec>);
 };
 
 // Test creation and initialization of the stack_vec.
@@ -61,6 +65,23 @@
     }
 }
 
+// Test that the vector can be iterated in order with a range-based for loop
+TEST_F(ITypStackVecTest, RangeBasedIteration) {
+    StackVec vec(Key(10u));
+
+    // Assign in a non-const range-based for loop
+    uint32_t i = 0;
+    for (Val& val : vec) {
+        val = Val(i);
+    }
+
+    // Check values in a const range-based for loop
+    i = 0;
+    for (Val val : static_cast<const StackVec&>(vec)) {
+        ASSERT_EQ(val, vec[Key(i++)]);
+    }
+}
+
 // Name "*DeathTest" per https://google.github.io/googletest/advanced.html#death-test-naming
 using ITypStackVecDeathTest = ITypStackVecTest;
 
diff --git a/src/dawn/tests/unittests/ITypVectorTests.cpp b/src/dawn/tests/unittests/ITypVectorTests.cpp
index d84b9be..b413172 100644
--- a/src/dawn/tests/unittests/ITypVectorTests.cpp
+++ b/src/dawn/tests/unittests/ITypVectorTests.cpp
@@ -26,6 +26,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <array>
+#include <ranges>
 #include <utility>
 
 #include "src/dawn/common/ityp_vector.h"
@@ -41,6 +42,9 @@
     using Val = TypedInteger<struct ValT, uint32_t>;
 
     using Vector = ityp::vector<Key, Val>;
+
+    // Check that ityp::vector can be used as a range.
+    static_assert(std::ranges::contiguous_range<Vector>);
 };
 
 // Name "*DeathTest" per https://google.github.io/googletest/advanced.html#death-test-naming
diff --git a/src/utils/span.h b/src/utils/span.h
index e10e0d7..682d77f 100644
--- a/src/utils/span.h
+++ b/src/utils/span.h
@@ -31,6 +31,7 @@
 #include <concepts>
 #include <limits>
 #include <memory>
+#include <ranges>
 #include <span>
 
 #include "src/utils/numeric.h"
@@ -58,7 +59,7 @@
 namespace detail {
 template <typename T, HasUnsignedUnderlyingType Index, typename PtrType>
 class SpanBase;
-}
+}  // namespace detail
 
 // A direct replacement for std::span<T>
 template <typename T>
@@ -73,7 +74,16 @@
 // The equivalent of std::dynamic_extent for the given index type.
 template <typename Index>
 inline constexpr Index DynamicExtent = std::numeric_limits<Index>::max();
+}  // namespace dawn
 
+// Mark `SpanBase` as satisfying the `view` and `borrowed_range` concepts.
+template <typename T, typename Index, typename PtrType>
+inline constexpr bool std::ranges::enable_view<dawn::detail::SpanBase<T, Index, PtrType>> = true;
+template <typename T, typename Index, typename PtrType>
+inline constexpr bool
+    std::ranges::enable_borrowed_range<dawn::detail::SpanBase<T, Index, PtrType>> = true;
+
+namespace dawn {
 namespace detail {
 
 template <typename From, typename To>
@@ -84,10 +94,13 @@
                          LegalDataConversion<std::remove_reference_t<std::iter_reference_t<It>>, T>;
 
 template <typename T, typename Index, typename R>
-concept CompatibleRange = requires(R r) {
-    { r.data() } -> std::convertible_to<T*>;
-    { r.size() } -> std::same_as<Index>;
-};
+concept CompatibleRange =
+    std::ranges::contiguous_range<R> &&  //
+    (std::ranges::borrowed_range<R> || std::is_const_v<T>) &&
+    LegalDataConversion<std::remove_reference_t<std::ranges::range_reference_t<R>>, T> &&
+    requires(R r) {
+        { r.size() } -> std::same_as<Index>;
+    };
 
 // DIFF: only dynamic_extent is supported at the moment because Dawn might not need spans with
 // static extents.
@@ -147,7 +160,7 @@
         }
     }
 
-    // Constructor from a "range-like" that provides `T* data()` and `Index size()`. Note that this
+    // Constructor from a range that provides `T* data()` and `Index size()`. Note that this
     // is quite different from Chromium's base::span constructor from ranges because adapting that
     // constructor to support TypedInteger for Index would be extremely challenging. Will
     // DAWN_CHECK() if the size doesn't fit in a size_t.
diff --git a/src/utils/span_nocompile.nc b/src/utils/span_nocompile.nc
index 7e254e3..34f3160 100644
--- a/src/utils/span_nocompile.nc
+++ b/src/utils/span_nocompile.nc
@@ -42,6 +42,8 @@
 struct FakeRange {
     size_t size() const { return kSpanData.size(); }
     const int* data() const { return kSpanData.data(); }
+    auto begin() { return kSpanData.begin(); }
+    auto end() { return kSpanData.end(); }
 };
 
 struct FakeTypedRange {
@@ -49,6 +51,8 @@
         return Index{uint32_t{kSpanData.size()}};
     }
     const int* data() const { return kSpanData.data(); }
+    auto begin() { return kSpanData.begin(); }
+    auto end() { return kSpanData.end(); }
 };
 
 void TestConstPointerToNonConstSpan() {
diff --git a/src/utils/span_tests.cc b/src/utils/span_tests.cc
index 2e83894..3e234fc 100644
--- a/src/utils/span_tests.cc
+++ b/src/utils/span_tests.cc
@@ -44,6 +44,8 @@
 struct FakeRange {
     size_t size() const { return kSpanData.size(); }
     const int* data() const { return kSpanData.data(); }
+    auto begin() { return kSpanData.begin(); }
+    auto end() { return kSpanData.end(); }
 };
 
 struct FakeTypedRange {
@@ -51,6 +53,8 @@
         return Index{uint32_t{kSpanData.size()}};
     }
     const int* data() const { return kSpanData.data(); }
+    auto begin() { return kSpanData.begin(); }
+    auto end() { return kSpanData.end(); }
 };
 
 struct FakeTyped64Range {
@@ -58,6 +62,8 @@
         return Index64{uint64_t{kSpanData.size()}};
     }
     const int* data() const { return kSpanData.data(); }
+    auto begin() { return kSpanData.begin(); }
+    auto end() { return kSpanData.end(); }
 };
 
 TEST(SpanTest, Constructor_Default) {