This is an automated email from the ASF dual-hosted git repository.

apitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/main by this push:
     new ed19a92198 GH-40252: [C++] Make span SFINAE standards-conforming to 
enable compilation with nvcc (#40253)
ed19a92198 is described below

commit ed19a921983a3783a0aa36059328e2da0f31ae6f
Author: Vyas Ramasubramani <[email protected]>
AuthorDate: Wed Mar 13 03:17:12 2024 -0700

    GH-40252: [C++] Make span SFINAE standards-conforming to enable compilation 
with nvcc (#40253)
    
    
    
    ### Rationale for this change
    
    The current code uses an incomplete type in a SFINAE construct.
    
    Closes #40252
    
    ### What changes are included in this PR?
    
    This PR changes the way that the type is specified to avoid any UB.
    
    ### Are these changes tested?
    
    I tested locally that code that originally failed to compile with nvcc now 
compiles successfully. The same code also compiles under clang and gcc. This is 
a minimal reproducer:
    ```
    #include <arrow/api.h>
    #include <vector>
    
    int main() {
      arrow::util::span<int> x{std::vector<int>{1, 2, 3}};
    }
    ```
    
    I did not include it here because it is a compile-time rather than runtime 
issue, and because at present it only manifests on nvcc.
    
    ### Are there any user-facing changes?
    
    No.
    
    * GitHub Issue: #40252
    
    Authored-by: Vyas Ramasubramani <[email protected]>
    Signed-off-by: Antoine Pitrou <[email protected]>
---
 cpp/src/arrow/util/span.h       | 28 ++++++++++++++++++++++++++--
 cpp/src/arrow/util/span_test.cc |  1 -
 2 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/cpp/src/arrow/util/span.h b/cpp/src/arrow/util/span.h
index 4254fec75e..71cf9ed448 100644
--- a/cpp/src/arrow/util/span.h
+++ b/cpp/src/arrow/util/span.h
@@ -25,6 +25,31 @@
 
 namespace arrow::util {
 
+template <class T>
+class span;
+
+// This trait is used to check if a type R can be used to construct a span<T>.
+// Specifically, it checks if std::data(R) and std::size(R) are valid 
expressions
+// that may be passed to the span(T*, size_t) constructor. The reason this 
trait
+// is needed rather than expressing this directly in the relevant span 
constructor
+// is that this check requires instantiating span<T>, which would violate the
+// C++ standard if written directly in the constructor's enable_if clause
+// because span<T> is an incomplete type at that point. By defining this trait
+// instead, we add an extra level of indirection that lets us delay the
+// evaluation of the template until the first time the associated constructor
+// is actually called, at which point span<T> is a complete type.
+//
+// Note that most compilers do support the noncompliant construct, but nvcc
+// does not. See https://github.com/apache/arrow/issues/40252
+template <class T, class R, class Enable = void>
+struct ConstructibleFromDataAndSize : std::false_type {};
+
+template <class T, class R>
+struct ConstructibleFromDataAndSize<
+    span<T>, R,
+    std::void_t<decltype(span<T>{std::data(std::declval<R>()),
+                                 std::size(std::declval<R>())})>> : 
std::true_type {};
+
 /// std::span polyfill.
 ///
 /// Does not support static extents.
@@ -58,8 +83,7 @@ writing code which would break when it is replaced by 
std::span.)");
 
   template <
       typename R,
-      typename DisableUnlessConstructibleFromDataAndSize =
-          decltype(span<T>(std::data(std::declval<R>()), 
std::size(std::declval<R>()))),
+      std::enable_if_t<ConstructibleFromDataAndSize<span<T>, R>::value, bool> 
= true,
       typename DisableUnlessSimilarTypes = std::enable_if_t<std::is_same_v<
           
std::decay_t<std::remove_pointer_t<decltype(std::data(std::declval<R>()))>>,
           std::decay_t<T>>>>
diff --git a/cpp/src/arrow/util/span_test.cc b/cpp/src/arrow/util/span_test.cc
index fcbb49f71e..01460c2bd0 100644
--- a/cpp/src/arrow/util/span_test.cc
+++ b/cpp/src/arrow/util/span_test.cc
@@ -51,7 +51,6 @@ TEST(Span, Construction) {
 
   static_assert(std::is_constructible_v<span<const int>, decltype(arr)&>);
   static_assert(std::is_constructible_v<span<const int>, 
decltype(const_arr)&>);
-  static_assert(std::is_constructible_v<span<const int>, span<int>>);
 
   static_assert(std::is_constructible_v<span<int>, std::vector<int>&>);
   static_assert(!std::is_constructible_v<span<int>, const std::vector<int>&>);

Reply via email to