diff --git a/libcudacxx/include/cuda/std/__tuple_dir/tuple.h b/libcudacxx/include/cuda/std/__tuple_dir/tuple.h index 2a98c36e0cd..d923d7656ed 100644 --- a/libcudacxx/include/cuda/std/__tuple_dir/tuple.h +++ b/libcudacxx/include/cuda/std/__tuple_dir/tuple.h @@ -47,6 +47,22 @@ _CCCL_BEGIN_NAMESPACE_CUDA_STD +// Determines whether each element of a tuple_of_iterator_references can actually construct the +// corresponding tuple element. The tuple_of_iterator_references converting constructor below is +// otherwise unconstrained on element convertibility, so is_constructible would report true even for +// ill-formed conversions (e.g. tuple_of_iterator_references -> tuple) and then +// hard-error when the delegating constructor body is instantiated -- which happens during a +// contiguous_iterator concept check on a thrust zip_iterator under GCC 9. +template +inline constexpr bool __tuple_of_iterator_references_constructible_v = false; + +template +inline constexpr bool __tuple_of_iterator_references_constructible_v<__tuple_types<_Tp...>, + _TupleOfIteratorReferences, + __tuple_indices<_Indices...>> = + (is_constructible_v<_Tp, decltype(::cuda::std::get<_Indices>(::cuda::std::declval<_TupleOfIteratorReferences>()))> + && ...); + template class _CCCL_TYPE_VISIBILITY_DEFAULT tuple { @@ -225,7 +241,11 @@ class _CCCL_TYPE_VISIBILITY_DEFAULT tuple // NOLINTBEGIN(modernize-type-traits) enable_if_t<__is_tuple_of_iterator_references_v<_TupleOfIteratorReferences>, int> = 0, // NOLINTEND(modernize-type-traits) - enable_if_t<(tuple_size<_TupleOfIteratorReferences>::value == sizeof...(_Tp)), int> = 0> + enable_if_t<(tuple_size<_TupleOfIteratorReferences>::value == sizeof...(_Tp)), int> = 0, + enable_if_t<__tuple_of_iterator_references_constructible_v<__tuple_types<_Tp...>, + _TupleOfIteratorReferences, + __make_tuple_indices_t>, + int> = 0> _CCCL_API constexpr tuple(_TupleOfIteratorReferences&& __t) : tuple(::cuda::std::forward<_TupleOfIteratorReferences>(__t), __make_tuple_indices_t{}) {} diff --git a/thrust/thrust/iterator/detail/tuple_of_iterator_references.h b/thrust/thrust/iterator/detail/tuple_of_iterator_references.h index 04a39281d79..0e8ea00b7c5 100644 --- a/thrust/thrust/iterator/detail/tuple_of_iterator_references.h +++ b/thrust/thrust/iterator/detail/tuple_of_iterator_references.h @@ -168,7 +168,12 @@ class tuple_of_iterator_references : public ::cuda::std::tuple template < class... Us, - ::cuda::std::enable_if_t, ::cuda::std::tuple>, int> = 0> + ::cuda::std::enable_if_t, ::cuda::std::tuple>, int> = 0, + // Structure compatibility alone is not enough: each held reference must actually convert to the + // requested element, otherwise this operator is selected for ill-formed conversions such as + // tuple_of_iterator_references -> tuple, which then hard-errors in the body + // (e.g. during a contiguous_iterator concept check on a zip_iterator under GCC 9). + ::cuda::std::enable_if_t<(::cuda::std::is_convertible_v && ...), int> = 0> _CCCL_HOST_DEVICE constexpr operator ::cuda::std::tuple() const { return __to_tuple(typename ::cuda::std::__make_tuple_indices::type{});