Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcudacxx/include/cuda/__complex/traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ _CCCL_BEGIN_NAMESPACE_CUDA

template <class _Tp>
_CCCL_CONCEPT __is_complex_compatible_tuple_like = _CCCL_REQUIRES_EXPR(
(_Tp))(requires(::cuda::std::tuple_size<_Tp>::value == 2),
(_Tp))(requires(::cuda::std::tuple_size_v<_Tp> == 2),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was intentional, tuple_size_v didn't get the job done

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we cannot do that. tuple_size is one of the places where we need to keep it like that

requires(::cuda::std::is_same_v<::cuda::std::remove_cvref_t<::cuda::std::tuple_element_t<0, _Tp>>,
::cuda::std::remove_cvref_t<::cuda::std::tuple_element_t<1, _Tp>>>));

Expand Down
2 changes: 1 addition & 1 deletion libcudacxx/include/cuda/__functional/address_stability.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct proclaims_copyable_arguments<__callable_permitting_copied_arguments<F>> :
template <typename F>
[[nodiscard]] _CCCL_API constexpr auto proclaim_copyable_arguments(F&& f)
{
if constexpr (proclaims_copyable_arguments<F>::value)
if constexpr (proclaims_copyable_arguments_v<F>)
{ // If F is already marked then we do not need to wrap it
return f;
}
Expand Down
2 changes: 1 addition & 1 deletion libcudacxx/include/cuda/std/__algorithm/equal_range.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ template <class _ForwardIterator, class _Tp, class _Compare>
[[nodiscard]] _CCCL_API constexpr pair<_ForwardIterator, _ForwardIterator>
equal_range(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp)
{
static_assert(__is_callable<_Compare, decltype(*__first), const _Tp&>::value, "The comparator has to be callable");
static_assert(__is_callable_v<_Compare, decltype(*__first), const _Tp&>, "The comparator has to be callable");
static_assert(is_copy_constructible_v<_ForwardIterator>, "Iterator has to be copy constructible");
return ::cuda::std::__equal_range<_ClassicAlgPolicy>(
::cuda::std::move(__first),
Expand Down
3 changes: 1 addition & 2 deletions libcudacxx/include/cuda/std/__algorithm/includes.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,7 @@ template <class _InputIterator1, class _InputIterator2, class _Compare>
[[nodiscard]] _CCCL_API constexpr bool includes(
_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _InputIterator2 __last2, _Compare __comp)
{
static_assert(__is_callable<_Compare, decltype(*__first1), decltype(*__first2)>::value,
"Comparator has to be callable");
static_assert(__is_callable_v<_Compare, decltype(*__first1), decltype(*__first2)>, "Comparator has to be callable");

return ::cuda::std::__includes(
::cuda::std::move(__first1),
Expand Down
2 changes: 1 addition & 1 deletion libcudacxx/include/cuda/std/__algorithm/lower_bound.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ template <class _ForwardIterator, class _Tp, class _Compare>
[[nodiscard]] _CCCL_API constexpr _ForwardIterator
lower_bound(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value, _Compare __comp)
{
static_assert(__is_callable<_Compare, decltype(*__first), const _Tp&>::value, "The comparator has to be callable");
static_assert(__is_callable_v<_Compare, decltype(*__first), const _Tp&>, "The comparator has to be callable");
auto __proj = ::cuda::std::identity();
return ::cuda::std::__lower_bound<_ClassicAlgPolicy>(__first, __last, __value, __comp, __proj);
}
Expand Down
3 changes: 1 addition & 2 deletions libcudacxx/include/cuda/std/__algorithm/min_element.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ template <class _ForwardIterator, class _Compare>
min_element(_ForwardIterator __first, _ForwardIterator __last, _Compare __comp)
{
static_assert(__has_forward_traversal<_ForwardIterator>, "std::min_element requires a ForwardIterator");
static_assert(__is_callable<_Compare, decltype(*__first), decltype(*__first)>::value,
"The comparator has to be callable");
static_assert(__is_callable_v<_Compare, decltype(*__first), decltype(*__first)>, "The comparator has to be callable");

return ::cuda::std::__min_element<__comp_ref_type<_Compare>>(
::cuda::std::move(__first), ::cuda::std::move(__last), __comp);
Expand Down
2 changes: 1 addition & 1 deletion libcudacxx/include/cuda/std/__algorithm/minmax.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ template <class _Tp>
template <class _Tp, class _Compare>
[[nodiscard]] _CCCL_API constexpr pair<_Tp, _Tp> minmax(initializer_list<_Tp> __t, _Compare __comp)
{
static_assert(__is_callable<_Compare, _Tp, _Tp>::value, "The comparator has to be callable");
static_assert(__is_callable_v<_Compare, _Tp, _Tp>, "The comparator has to be callable");
identity __proj{};
auto __ret = ::cuda::std::__minmax_element_impl(__t.begin(), __t.end(), __comp, __proj);
return pair<_Tp, _Tp>(*__ret.first, *__ret.second);
Expand Down
3 changes: 1 addition & 2 deletions libcudacxx/include/cuda/std/__algorithm/minmax_element.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ template <class _ForwardIterator, class _Compare>
minmax_element(_ForwardIterator __first, _ForwardIterator __last, _Compare __comp)
{
static_assert(__has_forward_traversal<_ForwardIterator>, "::cuda::std::minmax_element requires a ForwardIterator");
static_assert(__is_callable<_Compare, decltype(*__first), decltype(*__first)>::value,
"The comparator has to be callable");
static_assert(__is_callable_v<_Compare, decltype(*__first), decltype(*__first)>, "The comparator has to be callable");
auto __proj = identity();
return ::cuda::std::__minmax_element_impl(__first, __last, __comp, __proj);
}
Expand Down
9 changes: 3 additions & 6 deletions libcudacxx/include/cuda/std/__algorithm/stable_sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,7 @@ _CCCL_API void __stable_sort_move(
}

template <class _Tp>
struct __stable_sort_switch
{
static const unsigned value = 128 * is_trivially_copy_assignable_v<_Tp>;
};
inline constexpr unsigned __stable_sort_switch_v = 128 * is_trivially_copy_assignable_v<_Tp>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be just a function?


template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
_CCCL_API void __stable_sort(
Expand All @@ -258,7 +255,7 @@ _CCCL_API void __stable_sort(
}
return;
}
if (__len <= static_cast<difference_type>(__stable_sort_switch<value_type>::value))
if (__len <= static_cast<difference_type>(__stable_sort_switch_v<value_type>))
{
::cuda::std::__insertion_sort<_AlgPolicy, _Compare>(__first, __last, __comp);
return;
Expand Down Expand Up @@ -292,7 +289,7 @@ _CCCL_API void __stable_sort_impl(_RandomAccessIterator __first, _RandomAccessIt
difference_type __len = __last - __first;
pair<value_type*, ptrdiff_t> __buf(0, 0);
unique_ptr<value_type, __return_temporary_buffer> __h;
if (__len > static_cast<difference_type>(__stable_sort_switch<value_type>::value))
if (__len > static_cast<difference_type>(__stable_sort_switch_v<value_type>))
{
__buf = ::cuda::std::get_temporary_buffer<value_type>(__len);
__h.reset(__buf.first);
Expand Down
2 changes: 1 addition & 1 deletion libcudacxx/include/cuda/std/__simd/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ template <typename _Range, typename = void>
inline constexpr bool __has_tuple_size_v = false;

template <typename _Range>
inline constexpr bool __has_tuple_size_v<_Range, void_t<decltype(tuple_size<remove_cvref_t<_Range>>::value)>> = true;
inline constexpr bool __has_tuple_size_v<_Range, void_t<decltype(tuple_size_v<remove_cvref_t<_Range>>)>> = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

for f in \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
do
  echo "===== $f ====="
  wc -l "$f"
  sed -n '1,125p' "$f"
done

echo "===== tuple_size and tuple_size_v definitions/usages ====="
rg -n --glob '*.{h,hpp,cpp,cc,cxx,cu,cuh}' \
  'tuple_size_v|tuple_size<|__has_tuple_size_v|__is_complex_compatible_tuple_like|__pair_like' \
  libcudacxx | head -250

echo "===== current diff summary ====="
git diff --stat -- \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

echo "===== current diff for the three files ====="
git diff -- \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

Repository: NVIDIA/cccl

Length of output: 38810


🏁 Script executed:

#!/bin/bash
set -eu

echo "===== tuple_size implementation ====="
cat -n libcudacxx/include/cuda/std/__tuple_dir/tuple_size.h | sed -n '1,115p'

echo "===== constraint macro definitions ====="
rg -n -A18 -B8 \
  '_CCCL_REQUIRES_EXPR|define _CCCL_REQUIRES_EXPR|_CCCL_CONCEPT' \
  libcudacxx/include/cuda/std/__concepts/concept_macros.h \
  libcudacxx/include/cuda/std/__concepts 2>/dev/null | head -220

echo "===== related tuple-like and complex tests ====="
rg -n -A12 -B8 \
  '__pair_like|__is_complex_compatible_tuple_like|incomplete|tuple_size_v' \
  libcudacxx/test/libcudacxx/cuda/complex \
  libcudacxx/test/libcudacxx/std/utilities/tuple/tuple.tuple/tuple.helper \
  libcudacxx/test/libcudacxx/std/ranges 2>/dev/null | head -300

echo "===== recent history for the affected files ====="
git log --oneline -12 -- \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

echo "===== available C++ compilers ====="
command -v g++ || true
command -v clang++ || true
command -v nvcc || true

Repository: NVIDIA/cccl

Length of output: 50367


🏁 Script executed:

#!/bin/bash
set -eu

echo "===== available C++ compilers ====="
command -v g++ || true
command -v clang++ || true
command -v nvcc || true

echo "===== focused concept macro sections ====="
sed -n '35,72p;201,225p;229,290p' libcudacxx/include/cuda/std/__concepts/concept_macros.h

echo "===== focused history ====="
git log --oneline -12 -- libcudacxx/include/cuda/std/__simd/utility.h
git log --oneline -12 -- libcudacxx/include/cuda/__complex/traits.h
git log --oneline -12 -- libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

echo "===== commits that introduced the tuple-size spellings ====="
git log --all --oneline -S'tuple_size_v<remove_cvref_t<_Range>>' -- libcudacxx/include/cuda/std/__simd/utility.h || true
git log --all --oneline -S'tuple_size_v<_Tp>' -- libcudacxx/include/cuda/__complex/traits.h || true
git log --all --oneline -S'tuple_size_v<remove_cvref_t<_Tp>>' -- libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h || true

echo "===== standalone compiler probe ====="
cat >/tmp/tuple-size-probe.cpp <<'CPP'
`#include` <cstddef>
`#include` <type_traits>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <class T, class = void>
inline constexpr bool has_member_probe = false;

template <class T>
inline constexpr bool has_member_probe<T, std::void_t<decltype(tuple_size<T>::value)>> = true;

template <class T, class = void>
inline constexpr bool has_variable_probe = false;

template <class T>
inline constexpr bool has_variable_probe<T, std::void_t<decltype(tuple_size_v<T>)>> = true;

template <class T>
concept variable_constraint = requires { requires(tuple_size_v<T> == 2); };

template <class T>
concept member_constraint = requires { requires(tuple_size<T>::value == 2); };

struct Incomplete;

static_assert(!has_member_probe<Incomplete>);
static_assert(!has_variable_probe<Incomplete>);
static_assert(!member_constraint<Incomplete>);
static_assert(!variable_constraint<Incomplete>);

int main() {}
CPP

if command -v g++ >/dev/null 2>&1; then
  g++ -std=c++20 -fsyntax-only /tmp/tuple-size-probe.cpp
  echo "g++ probe: success"
fi
if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/tuple-size-probe.cpp
  echo "clang++ probe: success"
fi

Repository: NVIDIA/cccl

Length of output: 8332


🏁 Script executed:

#!/bin/bash
set -u

echo "===== conversion commit ====="
git show --no-ext-diff --format=fuller --stat 8bd263c5e
git show --no-ext-diff --format= --unified=18 8bd263c5e -- \
  libcudacxx/include/cuda/std/__simd/utility.h \
  libcudacxx/include/cuda/__complex/traits.h \
  libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

echo "===== standalone compiler probe with clang++ ====="
cat >/tmp/tuple-size-probe.cpp <<'CPP'
`#include` <cstddef>
`#include` <type_traits>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <class T, class = void>
inline constexpr bool has_member_probe = false;

template <class T>
inline constexpr bool has_member_probe<T, std::void_t<decltype(tuple_size<T>::value)>> = true;

template <class T, class = void>
inline constexpr bool has_variable_probe = false;

template <class T>
inline constexpr bool has_variable_probe<T, std::void_t<decltype(tuple_size_v<T>)>> = true;

template <class T>
concept variable_constraint = requires { requires(tuple_size_v<T> == 2); };

template <class T>
concept member_constraint = requires { requires(tuple_size<T>::value == 2); };

struct Incomplete;

static_assert(!has_member_probe<Incomplete>);
static_assert(!has_variable_probe<Incomplete>);
static_assert(!member_constraint<Incomplete>);
static_assert(!variable_constraint<Incomplete>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ --version | head -1
  clang++ -std=c++20 -fsyntax-only /tmp/tuple-size-probe.cpp
  echo "clang++ probe: success"
else
  echo "clang++ unavailable"
fi

Repository: NVIDIA/cccl

Length of output: 8714


🏁 Script executed:

#!/bin/bash
set -u

echo "===== _CCCL_REQUIRES_EXPR definition ====="
rg -n -A12 -B8 '_CCCL_REQUIRES_EXPR' libcudacxx/include/cuda/std

echo "===== exact pair-like behavior in a standalone probe ====="
cat >/tmp/pair-like-probe.cpp <<'CPP'
`#include` <cstddef>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <class T>
inline constexpr bool tuple_like_ext = false;

template <class T>
concept pair_like_variable = requires {
  requires(tuple_like_ext<T>);
  requires(tuple_size_v<T> == 2);
};

template <class T>
concept pair_like_member = requires {
  requires(tuple_like_ext<T>);
  requires(tuple_size<T>::value == 2);
};

struct Unsupported;

static_assert(!pair_like_variable<Unsupported>);
static_assert(!pair_like_member<Unsupported>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/pair-like-probe.cpp
  rc=$?
  echo "clang++ pair-like probe exit: $rc"
fi

echo "===== exact complex-style behavior in a standalone probe ====="
cat >/tmp/complex-like-probe.cpp <<'CPP'
`#include` <cstddef>
`#include` <type_traits>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

template <class T>
using tuple_element_t = typename T::missing;

template <class T>
concept complex_like_variable = requires {
  requires(tuple_size_v<T> == 2);
  requires(std::is_same_v<tuple_element_t<T>, tuple_element_t<T>>);
};

template <class T>
concept complex_like_member = requires {
  requires(tuple_size<T>::value == 2);
  requires(std::is_same_v<tuple_element_t<T>, tuple_element_t<T>>);
};

struct Unsupported;

static_assert(!complex_like_variable<Unsupported>);
static_assert(!complex_like_member<Unsupported>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/complex-like-probe.cpp
  rc=$?
  echo "clang++ complex-like probe exit: $rc"
fi

Repository: NVIDIA/cccl

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -u

echo "===== _CCCL_REQUIRES_EXPR implementation ====="
sed -n '296,322p' libcudacxx/include/cuda/std/__concepts/concept_macros.h

echo "===== pair-like probe with a recognized but incomplete tuple type ====="
cat >/tmp/pair-like-probe.cpp <<'CPP'
`#include` <cstddef>

template <class T>
struct tuple_size;

template <class T>
inline constexpr std::size_t tuple_size_v = tuple_size<T>::value;

struct RecognizedTuple;
template <>
inline constexpr bool tuple_like_ext<RecognizedTuple> = true;

template <class T>
concept pair_like_variable = requires {
  requires(tuple_like_ext<T>);
  requires(tuple_size_v<T> == 2);
};

template <class T>
concept pair_like_member = requires {
  requires(tuple_like_ext<T>);
  requires(tuple_size<T>::value == 2);
};

static_assert(!pair_like_member<RecognizedTuple>);
static_assert(!pair_like_variable<RecognizedTuple>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/pair-like-probe.cpp >/tmp/pair-like-probe.out 2>&1
  rc=$?
  echo "clang++ pair-like probe exit: $rc"
  sed -n '1,25p' /tmp/pair-like-probe.out
fi

echo "===== direct current-header probes ====="
cat >/tmp/current-header-probe.cpp <<'CPP'
`#include` <cuda/std/__simd/utility.h>
`#include` <cuda/std/__tuple_dir/tuple_like.h>
`#include` <cuda/__complex/traits.h>

struct UnsupportedTuple;

static_assert(!::cuda::std::simd::__has_tuple_size_v<UnsupportedTuple>);
static_assert(!::cuda::std::__pair_like<UnsupportedTuple>);
static_assert(!::cuda::__is_complex_compatible_tuple_like<UnsupportedTuple>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only -Ilibcudacxx/include /tmp/current-header-probe.cpp \
    >/tmp/current-header-probe.out 2>&1
  rc=$?
  echo "clang++ current-header probe exit: $rc"
  sed -n '1,45p' /tmp/current-header-probe.out
fi

Repository: NVIDIA/cccl

Length of output: 7470


🏁 Script executed:

#!/bin/bash
set -u

echo "===== direct __pair_like probe with an incomplete recognized tuple ====="
cat >/tmp/pair-like-current-probe.cpp <<'CPP'
`#include` <cuda/std/__tuple_dir/tuple_like.h>

static_assert(!::cuda::std::__pair_like<::cuda::std::tuple<int, int>>);

int main() {}
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only -Ilibcudacxx/include \
    /tmp/pair-like-current-probe.cpp >/tmp/pair-like-current-probe.out 2>&1
  rc=$?
  echo "clang++ __pair_like probe exit: $rc"
  sed -n '1,40p' /tmp/pair-like-current-probe.out
fi

echo "===== parent implementation of all three probes ====="
git show 8bd263c5e^:libcudacxx/include/cuda/std/__simd/utility.h | sed -n '78,86p'
git show 8bd263c5e^:libcudacxx/include/cuda/__complex/traits.h | sed -n '37,44p'
git show 8bd263c5e^:libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h | sed -n '93,99p'

Repository: NVIDIA/cccl

Length of output: 4870


critical: Restore member-based tuple-size probes in all three constraint paths. tuple_size_v<T> can hard-error when tuple_size<T> is incomplete instead of producing a failed constraint.

  • libcudacxx/include/cuda/std/__simd/utility.h:83: use decltype(tuple_size<remove_cvref_t<_Range>>::value).
  • libcudacxx/include/cuda/__complex/traits.h:41: use ::cuda::std::tuple_size<_Tp>::value.
  • libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h:97: use tuple_size<remove_cvref_t<_Tp>>::value.
📍 Affects 3 files
  • libcudacxx/include/cuda/std/__simd/utility.h#L83-L83 (this comment)
  • libcudacxx/include/cuda/__complex/traits.h#L41-L41
  • libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h#L97-L97

Source: Path instructions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be reverted


template <typename _Range, typename = void>
inline constexpr bool __has_static_extent_v = false;
Expand Down
2 changes: 1 addition & 1 deletion libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ _CCCL_CONCEPT __tuple_like = __tuple_like_ext<remove_cvref_t<_Tp>>;
// Not on line 74 because of __COUNTER__ missing in NVRTC
template <class _Tp>
_CCCL_CONCEPT __pair_like = _CCCL_REQUIRES_EXPR((_Tp)) //
(requires(__tuple_like<_Tp>), requires(tuple_size<remove_cvref_t<_Tp>>::value == 2));
(requires(__tuple_like<_Tp>), requires(tuple_size_v<remove_cvref_t<_Tp>> == 2));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- tuple_like.h ---'
sed -n '70,115p' libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
printf '%s\n' '--- concept macros ---'
sed -n '1,240p' libcudacxx/include/cuda/std/__concepts/concept_macros.h
printf '%s\n' '--- related tuple_size_v usage ---'
rg -n -C 3 'tuple_size_v|__pair_like|_CCCL_REQUIRES_EXPR' libcudacxx/include/cuda/std/__tuple_dir libcudacxx/include/cuda/std/__concepts

Repository: NVIDIA/cccl

Length of output: 31007


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '365,395p' libcudacxx/include/cuda/std/__concepts/concept_macros.h
sed -n '1,90p' libcudacxx/include/cuda/std/__tuple_dir/tuple_size.h
sed -n '1,78p' libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h
git diff -- libcudacxx/include/cuda/std/__tuple_dir/tuple_like.h

Repository: NVIDIA/cccl

Length of output: 7520


🏁 Script executed:

#!/bin/bash
set -eu
sed -n '210,295p' libcudacxx/include/cuda/std/__concepts/concept_macros.h
command -v g++ || true
command -v clang++ || true
python3 - <<'PY'
from pathlib import Path
p = Path("libcudacxx/include/cuda/std/__concepts/concept_macros.h")
text = p.read_text()
for name in ("_CCCL_CONCEPT_REQUIREMENT_CASE__CCCL_SWITCH_REQUIRES",
             "_CCCL_CONCEPT_REQUIREMENT_0",
             "_CCCL_CONCEPT_REQUIREMENT_1"):
    print(name, text.find(name))
PY
cat >/tmp/tuple_size_probe.cpp <<'CPP'
`#include` <type_traits>

template<class> struct tuple_size;
template<class T> inline constexpr int tuple_size_v = tuple_size<T>::value;

template<class T>
concept member_form = requires {
  requires (std::is_same_v<T, int>);
  requires (tuple_size<T>::value == 2);
};

template<class T>
concept variable_form = requires {
  requires (std::is_same_v<T, int>);
  requires (tuple_size_v<T> == 2);
};

static_assert(!member_form<int>);
static_assert(!variable_form<int>);
CPP
if command -v g++ >/dev/null 2>&1; then
  g++ -std=c++20 -fsyntax-only /tmp/tuple_size_probe.cpp
fi
if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++20 -fsyntax-only /tmp/tuple_size_probe.cpp
fi

Repository: NVIDIA/cccl

Length of output: 5471


🏁 Script executed:

#!/bin/bash
set -u
cat >/tmp/cccl_fallback_probe.cpp <<'CPP'
template<bool>
struct select {};
template<>
struct select<true> {
  template<class T>
  using type = T;
};
template<bool B, class T = void>
using enable_if_t = typename select<B>::template type<T>;

template<class> struct tuple_size;
template<class T>
inline constexpr int tuple_size_v = tuple_size<T>::value;

template<bool B, enable_if_t<B, int> = 0>
inline constexpr int requires_value = 0;

template<class T>
inline constexpr bool tuple_like = false;

template<class T>
struct member_form {
  template<class U>
  static auto check()
    -> enable_if_t<
         (decltype(requires_value<tuple_like<U>>{}),
          decltype(requires_value<(tuple_size<U>::value == 2)>{}),
          true),
         void>;
};

template<class T>
struct variable_form {
  template<class U>
  static auto check()
    -> enable_if_t<
         (decltype(requires_value<tuple_like<U>>{}),
          decltype(requires_value<(tuple_size_v<U> == 2)>{}),
          true),
         void>;
};

template<class T, class = void>
struct has_member : false {};
template<class T>
struct has_member<T, decltype(member_form<T>::template check<T>())> : true {};

template<class T, class = void>
struct has_variable : false {};
template<class T>
struct has_variable<T, decltype(variable_form<T>::template check<T>())> : true {};

static_assert(!has_member<int>::value);
static_assert(!has_variable<int>::value);
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++17 -fsyntax-only /tmp/cccl_fallback_probe.cpp
  printf '%s\n' 'clang probe: passed'
else
  printf '%s\n' 'clang++ unavailable'
fi
if command -v g++ >/dev/null 2>&1; then
  if g++ -std=c++17 -fsyntax-only /tmp/cccl_fallback_probe.cpp; then
    printf '%s\n' 'g++ probe: passed'
  else
    printf '%s\n' 'g++ probe: unavailable or failed'
  fi
fi

Repository: NVIDIA/cccl

Length of output: 2675


🏁 Script executed:

#!/bin/bash
set -eu
cat >/tmp/cccl_fallback_probe.cpp <<'CPP'
template<bool>
struct select {};
template<>
struct select<true> {
  template<class T>
  using type = T;
};
template<bool B, class T = void>
using enable_if_t = typename select<B>::template type<T>;

template<class> struct tuple_size;
template<class T>
inline constexpr int tuple_size_v = tuple_size<T>::value;

template<bool B, enable_if_t<B, int> = 0>
inline constexpr int requires_value = 0;

template<class T>
inline constexpr bool tuple_like = false;

template<class T>
struct member_form {
  template<class U>
  static auto check()
    -> decltype(
         requires_value<tuple_like<U>>,
         requires_value<(tuple_size<U>::value == 2)>,
         void());
};

template<class T>
struct variable_form {
  template<class U>
  static auto check()
    -> decltype(
         requires_value<tuple_like<U>>,
         requires_value<(tuple_size_v<U> == 2)>,
         void());
};

template<class T, class = void>
struct has_member { static constexpr bool value = false; };
template<class T>
struct has_member<T, decltype(member_form<T>::template check<T>())> {
  static constexpr bool value = true;
};

template<class T, class = void>
struct has_variable { static constexpr bool value = false; };
template<class T>
struct has_variable<T, decltype(variable_form<T>::template check<T>())> {
  static constexpr bool value = true;
};

static_assert(!has_member<int>::value);
static_assert(!has_variable<int>::value);
CPP

if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++17 -fsyntax-only /tmp/cccl_fallback_probe.cpp
  printf '%s\n' 'clang probe: passed'
else
  printf '%s\n' 'clang++ unavailable'
fi
if command -v g++ >/dev/null 2>&1; then
  g++ -std=c++17 -fsyntax-only /tmp/cccl_fallback_probe.cpp
  printf '%s\n' 'g++ probe: passed'
else
  printf '%s\n' 'g++ unavailable'
fi

Repository: NVIDIA/cccl

Length of output: 271


🌐 Web query:

C++ tuple_size_v requires expression hard error variable template substitution NVCC NVRTC

💡 Result:

The issue where std::tuple_size_v causes a compilation error (a hard error) instead of a SFINAE substitution failure arises because the evaluation of the variable template's initializer—std::tuple_size::value—often occurs outside the immediate context of a template substitution [1][2][3]. Under C++ template instantiation rules, SFINAE (Substitution Failure Is Not An Error) only applies to errors occurring within the immediate context of a function signature or template parameter list [2]. When you use std::tuple_size_v, the compiler must instantiate the variable template specialization to determine its value. If the type T is not tuple-like, this instantiation triggers an error in the initializer (std::tuple_size::value). Because this instantiation is considered a side effect of the template substitution rather than an error in the immediate context, the compiler treats it as a hard error [1][2]. In contrast, using std::tuple_size::value directly is often SFINAE-friendly because the failure to find a::value member in the primary template—which is typically undefined—occurs in a context where the compiler can gracefully treat it as a substitution failure [3][4]. For NVCC and NVRTC users, this behavior is a standard C++ language compliance issue rather than a specific toolchain bug [5][3]. When working with CUDA libraries (such as libcu++), encountering this error usually means: 1. The type T passed to tuple_size_v does not meet the requirements of a "tuple-like" type (it lacks an appropriate std::tuple_size specialization) [4]. 2. You are attempting to use it in a SFINAE-constrained context (like a requires clause or std::enable_if) where you expect failure to be silent, but the compiler is forcing an instantiation [2][6][3]. To resolve this, you can: - Use std::tuple_size::value instead of the variable template std::tuple_size_v [5][3]. - If you must use a variable template, wrap it in a custom trait that inherits from std::integral_constant to ensure the failure occurs in a way that respects SFINAE, or define a primary template specialization for your custom types to ensure std::tuple_size::value is always valid [6][4].

Citations:


important: Preserve the member-based check for non-concepts toolchains. _CCCL_REQUIRES_EXPR places the requirement in a substitution context, but tuple_size_v<remove_cvref_t<_Tp>> can instantiate its initializer outside the immediate context and produce a hard error for non-tuple types. Use tuple_size<remove_cvref_t<_Tp>>::value until NVCC and NVRTC compatibility is established.

Source: Path instructions


_CCCL_END_NAMESPACE_CUDA_STD

Expand Down
2 changes: 1 addition & 1 deletion libcudacxx/include/cuda/std/__type_traits/add_pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ template <class _Tp>
using add_pointer_t _CCCL_NODEBUG_ALIAS = _CCCL_BUILTIN_ADD_POINTER(_Tp);

#else // ^^^ _CCCL_BUILTIN_ADD_POINTER ^^^ / vvv !_CCCL_BUILTIN_ADD_POINTER vvv
template <class _Tp, bool = __cccl_is_referenceable<_Tp>::value || is_void<_Tp>::value>
template <class _Tp, bool = __cccl_is_referenceable<_Tp>::value || is_void_v<_Tp>>
struct __add_pointer_impl
{
using type _CCCL_NODEBUG_ALIAS = remove_reference_t<_Tp>*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ is_copy_assignable : public is_assignable<add_lvalue_reference_t<_Tp>, add_lvalu

template <class _Tp>
inline constexpr bool is_copy_assignable_v =
is_assignable<add_lvalue_reference_t<_Tp>, add_lvalue_reference_t<add_const_t<_Tp>>>::value;
is_assignable_v<add_lvalue_reference_t<_Tp>, add_lvalue_reference_t<add_const_t<_Tp>>>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of funny, because its a pessimization in this context.

The point here is that there is no builtin available, so is_assignable_v will be defined through is_assignable<...>::value, so here we avoid ODR using an additional inline variable


#endif // No builtin

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ is_copy_constructible : public is_constructible<_Tp, add_lvalue_reference_t<add_
{};

template <class _Tp>
inline constexpr bool is_copy_constructible_v = is_constructible<_Tp, add_lvalue_reference_t<add_const_t<_Tp>>>::value;
inline constexpr bool is_copy_constructible_v = is_constructible_v<_Tp, add_lvalue_reference_t<add_const_t<_Tp>>>;

#endif // No builtin

Expand Down
4 changes: 2 additions & 2 deletions libcudacxx/include/cuda/std/__type_traits/is_destructible.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ template <class _Tp, bool>
struct __destructible_false;

template <class _Tp>
struct __destructible_false<_Tp, false> : public __destructible_imp<_Tp, is_reference<_Tp>::value>
struct __destructible_false<_Tp, false> : public __destructible_imp<_Tp, is_reference_v<_Tp>>
{};

template <class _Tp>
struct __destructible_false<_Tp, true> : public false_type
{};

template <class _Tp>
struct is_destructible : public __destructible_false<_Tp, is_function<_Tp>::value>
struct is_destructible : public __destructible_false<_Tp, is_function_v<_Tp>>
{};

template <class _Tp>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct _CCCL_TYPE_VISIBILITY_DEFAULT is_move_constructible : public is_construct
{};

template <class _Tp>
inline constexpr bool is_move_constructible_v = is_constructible<_Tp, add_rvalue_reference_t<_Tp>>::value;
inline constexpr bool is_move_constructible_v = is_constructible_v<_Tp, add_rvalue_reference_t<_Tp>>;

#endif // No builtin

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ inline constexpr bool is_nothrow_destructible_v = _CCCL_BUILTIN_IS_NOTHROW_DESTR

#else // ^^^ _CCCL_BUILTIN_IS_NOTHROW_DESTRUCTIBLE ^^^ / vvv !_CCCL_BUILTIN_IS_NOTHROW_DESTRUCTIBLE vvv

template <class _Tp, bool = is_destructible<_Tp>::value>
template <class _Tp, bool = is_destructible_v<_Tp>>
struct __cccl_is_nothrow_destructible : false_type
{};

Expand Down
Loading