Implement `span::to_fixed_extent<N>(...)` to replace `make_span<N>(...)`

`span::to_fixed_extent<N>()` is a helper that converts a dynamic-extent
span to a fixed-extent span at runtime. In theory, this isn't needed
since there are already several ways to do this conversion:
- `first<N>(...)`, `last<N>(...)`, and `subspan<0, N>(...)` all extract
  a fixed-extent span from a dynamic-extent span. However, none of these
  express the intent "this span is exactly size N" without the caller
  writing additional checks.
- `make_span<N>(...)` makes a runtime-checked fixed-extent span from a
  container with dynamic length. However:
  - In general, the `make_span<...>(...)` family of functions will
    migrate to CTAD in the future.
  - As a free function, it can be used with any type of spannable
    object even when it's not necessary.
- Using an explicit `span<T, N>` construction. However, this requires
  writing `T` again, which can sometimes be clunky.

The introduction of this helper allows the removal of
`as_byte_span<N>()` and `as_writable_byte_span<N>()`, and will
facilitate the future removal of `make_span<N>()`.

Finally, remove redundant constraints from `as_byte_span()` and
`as_writable_byte_span()` as part of cleaning up the overloads.

Bug: 341907909, 355603418
Change-Id: I6191eb9191371e32db3a618c77888d773a12fa8c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5763092
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Peter Kasting <pkasting@chromium.org>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1338855}
diff --git a/base/containers/span.h b/base/containers/span.h
index 7927bf6..51db5c3 100644
--- a/base/containers/span.h
+++ b/base/containers/span.h
@@ -16,6 +16,7 @@
 #include <iterator>
 #include <limits>
 #include <memory>
+#include <optional>
 #include <span>
 #include <type_traits>
 #include <utility>
@@ -246,6 +247,7 @@
 // - byte_span_from_cstring() function.
 // - byte_span_with_nul_from_cstring() function.
 // - split_at() method.
+// - to_fixed_extent() method.
 // - operator==() comparator function.
 // - operator<=>() comparator function.
 // - operator<<() printing function.
@@ -1038,6 +1040,15 @@
     return UNSAFE_BUFFERS({data() + offset, new_extent});
   }
 
+  // Convert a dynamic-extent span to a fixed-extent span. Returns a
+  // `span<T, Extent>` iff `size() == Extent`; otherwise, returns
+  // `std::nullopt`.
+  template <size_t Extent>
+  constexpr std::optional<span<T, Extent>> to_fixed_extent() const {
+    return size() == Extent ? std::optional(span<T, Extent>(*this))
+                            : std::nullopt;
+  }
+
   // Splits a span into two at the given `offset`, returning two spans that
   // cover the full range of the original span.
   //
@@ -1679,41 +1690,16 @@
 // to convert std::string or string-objects holding chars, or std::vector
 // or vector-like objects holding other scalar types, prior to passing them
 // into an API that requires byte spans.
-template <typename T>
-  requires requires(const T& arg) {
-    requires !std::is_array_v<std::remove_reference_t<T>>;
-    make_span(arg);
-  }
-constexpr span<const uint8_t> as_byte_span(const T& arg) {
+template <int&... ExplicitArgumentBarrier, typename Spannable>
+  requires requires(const Spannable& arg) { make_span(arg); }
+constexpr auto as_byte_span(const Spannable& arg) {
   return as_bytes(make_span(arg));
 }
 
-// This overload for arrays preserves the compile-time size N of the array in
-// the span type signature span<uint8_t, N>.
-template <typename T, size_t N>
+template <int&... ExplicitArgumentBarrier, typename T, size_t N>
 constexpr span<const uint8_t, N * sizeof(T)> as_byte_span(
     const T (&arr ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) {
-  return as_bytes(make_span<N>(arr));
-}
-
-// This overload adds a compile-time size that must be explicitly given,
-// checking that the size is correct at runtime. The template argument `N` is
-// the number of _bytes_ in the input range, not the number of elements.
-//
-// This is sugar for `base::span<const uint8_t, N>(base::as_byte_span(x))`.
-//
-// Example:
-// ```
-// std::string foo = "hello";
-// base::span<const uint8_t, 5> s = base::as_byte_span<5>(foo);
-// ```
-template <size_t N, typename T>
-  requires requires(const T& arg) {
-    requires !std::is_array_v<std::remove_reference_t<T>>;
-    make_span(arg);
-  }
-constexpr span<const uint8_t, N> as_byte_span(const T& arg) {
-  return span<const uint8_t, N>(as_byte_span(arg));
+  return as_bytes(make_span(arr));
 }
 
 // Convenience function for converting an object which is itself convertible
@@ -1721,50 +1707,27 @@
 // to convert std::string or string-objects holding chars, or std::vector
 // or vector-like objects holding other scalar types, prior to passing them
 // into an API that requires mutable byte spans.
-template <typename T>
-  requires requires(T&& arg) {
-    requires !std::is_array_v<std::remove_reference_t<T>>;
+template <int&... ExplicitArgumentBarrier, typename Spannable>
+  requires requires(Spannable&& arg) {
     make_span(arg);
     requires !std::is_const_v<typename decltype(make_span(arg))::element_type>;
   }
-constexpr span<uint8_t> as_writable_byte_span(T&& arg) {
-  return as_writable_bytes(make_span(arg));
+constexpr auto as_writable_byte_span(Spannable&& arg) {
+  return as_writable_bytes(make_span(std::forward<Spannable>(arg)));
 }
 
 // This overload for arrays preserves the compile-time size N of the array in
 // the span type signature span<uint8_t, N>.
-template <typename T, size_t N>
-  requires(!std::is_const_v<T>)
+template <int&... ExplicitArgumentBarrier, typename T, size_t N>
 constexpr span<uint8_t, N * sizeof(T)> as_writable_byte_span(
     T (&arr ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) {
-  return as_writable_bytes(make_span<N>(arr));
-}
-template <typename T, size_t N>
-  requires(!std::is_const_v<T>)
-constexpr span<uint8_t, N * sizeof(T)> as_writable_byte_span(
-    T (&&arr ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) {
-  return as_writable_bytes(make_span<N>(arr));
+  return as_writable_bytes(make_span(arr));
 }
 
-// This overload adds a compile-time size that must be explicitly given,
-// checking that the size is correct at runtime. The template argument `N` is
-// the number of _bytes_ in the input range, not the number of elements.
-//
-// This is sugar for `base::span<const uint8_t, N>(base::as_byte_span(x))`.
-//
-// Example:
-// ```
-// std::string foo = "hello";
-// base::span<const uint8_t, 5> s = base::as_writable_byte_span<5>(foo);
-// ```
-template <size_t N, typename T>
-  requires requires(T&& arg) {
-    requires !std::is_array_v<std::remove_reference_t<T>>;
-    make_span(arg);
-    requires !std::is_const_v<typename decltype(make_span(arg))::element_type>;
-  }
-constexpr span<uint8_t, N> as_writable_byte_span(T&& arg) {
-  return span<uint8_t, N>(as_writable_byte_span(arg));
+template <int&... ExplicitArgumentBarrier, typename T, size_t N>
+constexpr span<uint8_t, N * sizeof(T)> as_writable_byte_span(
+    T (&&arr ABSL_ATTRIBUTE_LIFETIME_BOUND)[N]) {
+  return as_writable_bytes(make_span(arr));
 }
 
 namespace internal {
diff --git a/base/containers/span_unittest.cc b/base/containers/span_unittest.cc
index 5ac626ef..e749156 100644
--- a/base/containers/span_unittest.cc
+++ b/base/containers/span_unittest.cc
@@ -1506,6 +1506,22 @@
   }
 }
 
+TEST(SpanTest, ToFixedExtent) {
+  {
+    const int kArray[] = {1, 2, 3};
+    const span<const int> s(kArray);
+
+    auto static_span = s.to_fixed_extent<3>();
+    ASSERT_TRUE(static_span.has_value());
+    static_assert(std::same_as<typename decltype(static_span)::value_type,
+                               span<const int, 3>>);
+    EXPECT_EQ(s.data(), static_span->data());
+    EXPECT_EQ(s.size(), static_span->size());
+
+    EXPECT_EQ(std::nullopt, s.to_fixed_extent<4>());
+  }
+}
+
 TEST(SpanTest, Size) {
   {
     span<int> span;
@@ -1754,14 +1770,6 @@
     EXPECT_EQ(byte_span.size(), kVec.size() * sizeof(int));
   }
   {
-    const std::vector<int> kVec({2, 3, 5, 7, 11, 13});
-    auto byte_span = as_byte_span<6u * sizeof(int)>(kVec);
-    static_assert(std::is_same_v<decltype(byte_span),
-                                 span<const uint8_t, 6u * sizeof(int)>>);
-    EXPECT_EQ(byte_span.data(), reinterpret_cast<const uint8_t*>(kVec.data()));
-    EXPECT_EQ(byte_span.size(), kVec.size() * sizeof(int));
-  }
-  {
     int kMutArray[] = {2, 3, 5, 7};
     auto byte_span = as_byte_span(kMutArray);
     static_assert(std::is_same_v<decltype(byte_span),
@@ -1777,15 +1785,6 @@
               reinterpret_cast<const uint8_t*>(kMutVec.data()));
     EXPECT_EQ(byte_span.size(), kMutVec.size() * sizeof(int));
   }
-  {
-    std::vector<int> kMutVec({2, 3, 5, 7});
-    auto byte_span = as_byte_span<4u * sizeof(int)>(kMutVec);
-    static_assert(std::is_same_v<decltype(byte_span),
-                                 span<const uint8_t, 4u * sizeof(int)>>);
-    EXPECT_EQ(byte_span.data(),
-              reinterpret_cast<const uint8_t*>(kMutVec.data()));
-    EXPECT_EQ(byte_span.size(), kMutVec.size() * sizeof(int));
-  }
   // Rvalue input.
   {
     [](auto byte_span) {
@@ -1798,15 +1797,6 @@
   }
 }
 
-TEST(SpanDeathTest, AsByteSpan) {
-  // Constructing a fixed-size span of the wrong size will terminate.
-  const std::vector<int> kVec({2, 3, 5, 7, 11, 13});
-  EXPECT_CHECK_DEATH({
-    auto byte_span = as_byte_span<6u>(kVec);  // 6 bytes is the wrong size.
-    base::debug::Alias(&byte_span);
-  });
-}
-
 TEST(SpanTest, AsWritableByteSpan) {
   {
     int kMutArray[] = {2, 3, 5, 7};
@@ -1823,14 +1813,6 @@
     EXPECT_EQ(byte_span.data(), reinterpret_cast<uint8_t*>(kMutVec.data()));
     EXPECT_EQ(byte_span.size(), kMutVec.size() * sizeof(int));
   }
-  {
-    std::vector<int> kMutVec({2, 3, 5, 7});
-    auto byte_span = as_writable_byte_span<4u * sizeof(int)>(kMutVec);
-    static_assert(
-        std::is_same_v<decltype(byte_span), span<uint8_t, 4u * sizeof(int)>>);
-    EXPECT_EQ(byte_span.data(), reinterpret_cast<uint8_t*>(kMutVec.data()));
-    EXPECT_EQ(byte_span.size(), kMutVec.size() * sizeof(int));
-  }
   // Rvalue input.
   {
     [](auto byte_span) {
@@ -1843,16 +1825,6 @@
   }
 }
 
-TEST(SpanDeathTest, AsWritableByteSpan) {
-  // Constructing a fixed-size span of the wrong size will terminate.
-  std::vector<int> kVec({2, 3, 5, 7, 11, 13});
-  EXPECT_CHECK_DEATH({
-    auto byte_span =
-        as_writable_byte_span<6u>(kVec);  // 6 bytes is the wrong size.
-    base::debug::Alias(&byte_span);
-  });
-}
-
 TEST(SpanTest, AsStringView) {
   {
     constexpr uint8_t kArray[] = {'h', 'e', 'l', 'l', 'o'};
diff --git a/gpu/command_buffer/service/memory_program_cache.cc b/gpu/command_buffer/service/memory_program_cache.cc
index 99424b1..e43349d 100644
--- a/gpu/command_buffer/service/memory_program_cache.cc
+++ b/gpu/command_buffer/service/memory_program_cache.cc
@@ -461,81 +461,91 @@
                                      const std::string& program) {
   std::unique_ptr<GpuProgramProto> proto(
       GpuProgramProto::default_instance().New());
-  if (proto->ParseFromString(program)) {
-    AttributeMap vertex_attribs;
-    UniformMap vertex_uniforms;
-    VaryingMap vertex_varyings;
-    OutputVariableList vertex_output_variables;
-    InterfaceBlockMap vertex_interface_blocks;
-    for (int i = 0; i < proto->vertex_shader().attribs_size(); i++) {
-      RetrieveShaderAttributeInfo(proto->vertex_shader().attribs(i),
-                                  &vertex_attribs);
-    }
-    for (int i = 0; i < proto->vertex_shader().uniforms_size(); i++) {
-      RetrieveShaderUniformInfo(proto->vertex_shader().uniforms(i),
-                                &vertex_uniforms);
-    }
-    for (int i = 0; i < proto->vertex_shader().varyings_size(); i++) {
-      RetrieveShaderVaryingInfo(proto->vertex_shader().varyings(i),
-                                &vertex_varyings);
-    }
-    for (int i = 0; i < proto->vertex_shader().output_variables_size(); i++) {
-      RetrieveShaderOutputVariableInfo(
-          proto->vertex_shader().output_variables(i), &vertex_output_variables);
-    }
-    for (int i = 0; i < proto->vertex_shader().interface_blocks_size(); i++) {
-      RetrieveShaderInterfaceBlockInfo(
-          proto->vertex_shader().interface_blocks(i), &vertex_interface_blocks);
-    }
-
-    AttributeMap fragment_attribs;
-    UniformMap fragment_uniforms;
-    VaryingMap fragment_varyings;
-    OutputVariableList fragment_output_variables;
-    InterfaceBlockMap fragment_interface_blocks;
-    for (int i = 0; i < proto->fragment_shader().attribs_size(); i++) {
-      RetrieveShaderAttributeInfo(proto->fragment_shader().attribs(i),
-                                  &fragment_attribs);
-    }
-    for (int i = 0; i < proto->fragment_shader().uniforms_size(); i++) {
-      RetrieveShaderUniformInfo(proto->fragment_shader().uniforms(i),
-                                &fragment_uniforms);
-    }
-    for (int i = 0; i < proto->fragment_shader().varyings_size(); i++) {
-      RetrieveShaderVaryingInfo(proto->fragment_shader().varyings(i),
-                                &fragment_varyings);
-    }
-    for (int i = 0; i < proto->fragment_shader().output_variables_size(); i++) {
-      RetrieveShaderOutputVariableInfo(
-          proto->fragment_shader().output_variables(i),
-          &fragment_output_variables);
-    }
-    for (int i = 0; i < proto->fragment_shader().interface_blocks_size(); i++) {
-      RetrieveShaderInterfaceBlockInfo(
-          proto->fragment_shader().interface_blocks(i),
-          &fragment_interface_blocks);
-    }
-
-    std::vector<uint8_t> binary(proto->program().length());
-    memcpy(binary.data(), proto->program().c_str(), proto->program().length());
-
-    store_.Put(proto->sha(),
-               new ProgramCacheValue(
-                   proto->format(), std::move(binary),
-                   proto->has_program_is_compressed() &&
-                       proto->program_is_compressed(),
-                   proto->program_decompressed_length(), proto->sha(),
-                   base::as_byte_span<ProgramCache::kHashLength>(
-                       proto->vertex_shader().sha()),
-                   vertex_attribs, vertex_uniforms, vertex_varyings,
-                   vertex_output_variables, vertex_interface_blocks,
-                   base::as_byte_span<ProgramCache::kHashLength>(
-                       proto->fragment_shader().sha()),
-                   fragment_attribs, fragment_uniforms, fragment_varyings,
-                   fragment_output_variables, fragment_interface_blocks, this));
-  } else {
+  if (!proto->ParseFromString(program)) {
     DVLOG(2) << "Failed to parse proto file.";
+    return;
   }
+
+  AttributeMap vertex_attribs;
+  UniformMap vertex_uniforms;
+  VaryingMap vertex_varyings;
+  OutputVariableList vertex_output_variables;
+  InterfaceBlockMap vertex_interface_blocks;
+  for (int i = 0; i < proto->vertex_shader().attribs_size(); i++) {
+    RetrieveShaderAttributeInfo(proto->vertex_shader().attribs(i),
+                                &vertex_attribs);
+  }
+  for (int i = 0; i < proto->vertex_shader().uniforms_size(); i++) {
+    RetrieveShaderUniformInfo(proto->vertex_shader().uniforms(i),
+                              &vertex_uniforms);
+  }
+  for (int i = 0; i < proto->vertex_shader().varyings_size(); i++) {
+    RetrieveShaderVaryingInfo(proto->vertex_shader().varyings(i),
+                              &vertex_varyings);
+  }
+  for (int i = 0; i < proto->vertex_shader().output_variables_size(); i++) {
+    RetrieveShaderOutputVariableInfo(proto->vertex_shader().output_variables(i),
+                                     &vertex_output_variables);
+  }
+  for (int i = 0; i < proto->vertex_shader().interface_blocks_size(); i++) {
+    RetrieveShaderInterfaceBlockInfo(proto->vertex_shader().interface_blocks(i),
+                                     &vertex_interface_blocks);
+  }
+
+  AttributeMap fragment_attribs;
+  UniformMap fragment_uniforms;
+  VaryingMap fragment_varyings;
+  OutputVariableList fragment_output_variables;
+  InterfaceBlockMap fragment_interface_blocks;
+  for (int i = 0; i < proto->fragment_shader().attribs_size(); i++) {
+    RetrieveShaderAttributeInfo(proto->fragment_shader().attribs(i),
+                                &fragment_attribs);
+  }
+  for (int i = 0; i < proto->fragment_shader().uniforms_size(); i++) {
+    RetrieveShaderUniformInfo(proto->fragment_shader().uniforms(i),
+                              &fragment_uniforms);
+  }
+  for (int i = 0; i < proto->fragment_shader().varyings_size(); i++) {
+    RetrieveShaderVaryingInfo(proto->fragment_shader().varyings(i),
+                              &fragment_varyings);
+  }
+  for (int i = 0; i < proto->fragment_shader().output_variables_size(); i++) {
+    RetrieveShaderOutputVariableInfo(
+        proto->fragment_shader().output_variables(i),
+        &fragment_output_variables);
+  }
+  for (int i = 0; i < proto->fragment_shader().interface_blocks_size(); i++) {
+    RetrieveShaderInterfaceBlockInfo(
+        proto->fragment_shader().interface_blocks(i),
+        &fragment_interface_blocks);
+  }
+
+  std::vector<uint8_t> binary(proto->program().length());
+  memcpy(binary.data(), proto->program().c_str(), proto->program().length());
+
+  auto vertex_shader_sha = base::as_byte_span(proto->vertex_shader().sha())
+                               .to_fixed_extent<kHashLength>();
+  auto fragment_shader_sha = base::as_byte_span(proto->fragment_shader().sha())
+                                 .to_fixed_extent<kHashLength>();
+  if (!vertex_shader_sha.has_value()) {
+    DVLOG(2) << "Ill-formed vertex shader hash";
+    return;
+  }
+  if (!fragment_shader_sha.has_value()) {
+    DVLOG(2) << "Ill-formed fragment shader hash";
+    return;
+  }
+  store_.Put(
+      proto->sha(),
+      new ProgramCacheValue(
+          proto->format(), std::move(binary),
+          proto->has_program_is_compressed() && proto->program_is_compressed(),
+          proto->program_decompressed_length(), proto->sha(),
+          vertex_shader_sha.value(), vertex_attribs, vertex_uniforms,
+          vertex_varyings, vertex_output_variables, vertex_interface_blocks,
+          fragment_shader_sha.value(), fragment_attribs, fragment_uniforms,
+          fragment_varyings, fragment_output_variables,
+          fragment_interface_blocks, this));
 }
 
 size_t MemoryProgramCache::Trim(size_t limit) {
diff --git a/gpu/command_buffer/service/program_cache.cc b/gpu/command_buffer/service/program_cache.cc
index 8b1f29a1..9ae7e77 100644
--- a/gpu/command_buffer/service/program_cache.cc
+++ b/gpu/command_buffer/service/program_cache.cc
@@ -49,8 +49,9 @@
 bool ProgramCache::HasSuccessfullyCompiledShader(
     const std::string& shader_signature) const {
   std::string sha(kHashLength, '\0');
-  ComputeShaderHash(shader_signature,
-                    base::as_writable_byte_span<kHashLength>(sha));
+  ComputeShaderHash(
+      shader_signature,
+      base::as_writable_byte_span(sha).to_fixed_extent<kHashLength>().value());
   return base::Contains(compiled_shaders_, sha);
 }