diff --git a/libcudacxx/include/cuda/std/__cccl/builtin.h b/libcudacxx/include/cuda/std/__cccl/builtin.h index becc07d18c9..f09e82f2348 100644 --- a/libcudacxx/include/cuda/std/__cccl/builtin.h +++ b/libcudacxx/include/cuda/std/__cccl/builtin.h @@ -409,6 +409,10 @@ # define _CCCL_BUILTIN_TYPE_PACK_ELEMENT(...) __type_pack_element<__VA_ARGS__> #endif // _CCCL_HAS_BUILTIN(__type_pack_element) +#if _CCCL_HAS_BUILTIN(__is_complete_type) +# define _CCCL_BUILTIN_IS_COMPLETE_TYPE(...) __is_complete_type(__VA_ARGS__) +#endif // _CCCL_HAS_BUILTIN(__is_complete_type) + // NVCC prior to 12.2 have trouble with pack expansion into __type_pack_element in an alias template #if _CCCL_CUDACC_BELOW(12, 2) # undef _CCCL_BUILTIN_TYPE_PACK_ELEMENT diff --git a/libcudacxx/include/cuda/std/__mdspan/empty_base.h b/libcudacxx/include/cuda/std/__mdspan/empty_base.h index 25a9104260a..f6c6d2d352e 100644 --- a/libcudacxx/include/cuda/std/__mdspan/empty_base.h +++ b/libcudacxx/include/cuda/std/__mdspan/empty_base.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -127,7 +128,7 @@ struct _CCCL_DECLSPEC_EMPTY_BASES __mdspan_ebco<_Elem1> : __mdspan_ebco_impl<0, } _CCCL_EXEC_CHECK_DISABLE - _CCCL_API friend constexpr void swap(__mdspan_ebco& __x, __mdspan_ebco& __y) + _CCCL_API friend constexpr void swap(__mdspan_ebco& __x, __mdspan_ebco& __y) noexcept(is_nothrow_swappable_v<_Elem1>) { swap(__x.__get<0>(), __y.__get<0>()); } @@ -219,7 +220,8 @@ struct _CCCL_DECLSPEC_EMPTY_BASES __mdspan_ebco<_Elem1, _Elem2> } _CCCL_EXEC_CHECK_DISABLE - _CCCL_API friend constexpr void swap(__mdspan_ebco& __x, __mdspan_ebco& __y) + _CCCL_API friend constexpr void swap(__mdspan_ebco& __x, __mdspan_ebco& __y) noexcept( + is_nothrow_swappable_v<_Elem1> && is_nothrow_swappable_v<_Elem2>) { swap(__x.__get<0>(), __y.__get<0>()); swap(__x.__get<1>(), __y.__get<1>()); @@ -346,7 +348,8 @@ struct _CCCL_DECLSPEC_EMPTY_BASES __mdspan_ebco<_Elem1, _Elem2, _Elem3> } _CCCL_EXEC_CHECK_DISABLE - _CCCL_API friend constexpr void swap(__mdspan_ebco& __x, __mdspan_ebco& __y) + _CCCL_API friend constexpr void swap(__mdspan_ebco& __x, __mdspan_ebco& __y) noexcept( + is_nothrow_swappable_v<_Elem1> && is_nothrow_swappable_v<_Elem2> && is_nothrow_swappable_v<_Elem3>) { swap(__x.__get<0>(), __y.__get<0>()); swap(__x.__get<1>(), __y.__get<1>()); diff --git a/libcudacxx/include/cuda/std/__ranges/compressed_movable_box.h b/libcudacxx/include/cuda/std/__ranges/compressed_movable_box.h index e1a8dc2233c..403b57fe9f0 100644 --- a/libcudacxx/include/cuda/std/__ranges/compressed_movable_box.h +++ b/libcudacxx/include/cuda/std/__ranges/compressed_movable_box.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -667,7 +668,8 @@ struct _CCCL_DECLSPEC_EMPTY_BASES __compressed_movable_box<_Elem1> : __compresse } _CCCL_EXEC_CHECK_DISABLE - _CCCL_API friend constexpr void swap(__compressed_movable_box& __x, __compressed_movable_box& __y) + _CCCL_API friend constexpr void + swap(__compressed_movable_box& __x, __compressed_movable_box& __y) noexcept(is_nothrow_swappable_v<_Elem1>) { swap(__x.__get<0>(), __y.__get<0>()); } @@ -752,7 +754,8 @@ struct _CCCL_DECLSPEC_EMPTY_BASES __compressed_movable_box<_Elem1, _Elem2> } _CCCL_EXEC_CHECK_DISABLE - _CCCL_API friend constexpr void swap(__compressed_movable_box& __x, __compressed_movable_box& __y) + _CCCL_API friend constexpr void swap(__compressed_movable_box& __x, __compressed_movable_box& __y) noexcept( + is_nothrow_swappable_v<_Elem1> && is_nothrow_swappable_v<_Elem2>) { swap(__x.__get<0>(), __y.__get<0>()); swap(__x.__get<1>(), __y.__get<1>()); @@ -874,7 +877,8 @@ struct _CCCL_DECLSPEC_EMPTY_BASES __compressed_movable_box<_Elem1, _Elem2, _Elem } _CCCL_EXEC_CHECK_DISABLE - _CCCL_API friend constexpr void swap(__compressed_movable_box& __x, __compressed_movable_box& __y) + _CCCL_API friend constexpr void swap(__compressed_movable_box& __x, __compressed_movable_box& __y) noexcept( + is_nothrow_swappable_v<_Elem1> && is_nothrow_swappable_v<_Elem2> && is_nothrow_swappable_v<_Elem3>) { swap(__x.__get<0>(), __y.__get<0>()); swap(__x.__get<1>(), __y.__get<1>()); diff --git a/libcudacxx/include/cuda/std/__tuple_dir/tuple.h b/libcudacxx/include/cuda/std/__tuple_dir/tuple.h index ac42f5f7234..95574782078 100644 --- a/libcudacxx/include/cuda/std/__tuple_dir/tuple.h +++ b/libcudacxx/include/cuda/std/__tuple_dir/tuple.h @@ -279,12 +279,12 @@ class _CCCL_TYPE_VISIBILITY_DEFAULT tuple return *this; } - _CCCL_API void swap(tuple& __t) + _CCCL_API void swap(tuple& __t) noexcept(noexcept(__base_.swap(__t.__base_))) { __base_.swap(__t.__base_); } - _CCCL_API friend void swap(tuple& __t, tuple& __u) + _CCCL_API friend void swap(tuple& __t, tuple& __u) noexcept(noexcept(__t.swap(__u))) { __t.swap(__u); } diff --git a/libcudacxx/include/cuda/std/__tuple_dir/tuple_leaf.h b/libcudacxx/include/cuda/std/__tuple_dir/tuple_leaf.h index 0e794dc419a..6818b184671 100644 --- a/libcudacxx/include/cuda/std/__tuple_dir/tuple_leaf.h +++ b/libcudacxx/include/cuda/std/__tuple_dir/tuple_leaf.h @@ -440,7 +440,19 @@ struct _CCCL_DECLSPEC_EMPTY_BASES __tuple_impl<__tuple_indices<_Indx...>, _Tp... _CCCL_HIDE_FROM_ABI __tuple_impl& operator=(__tuple_impl&&) = default; // Using a fold exppression here breaks nvrtc - _CCCL_API inline void swap(__tuple_impl& __t) noexcept(__fold_and_v...>) + _CCCL_API inline void swap(__tuple_impl& __t) + // NVCC 12.0.X has a bug where it instantiates friend functions eagerly. This leads to errors + // because the friend swap() in tuple causes this swap() to (transitively) be instantiated + // regardless of whether it is called or not. + // + // When using tuples of incomplete types this causes errors with is_nothrow_swappable (or + // rather, any is_swappable trait) as they require the types to be complete. In this case we + // need to lazily instantiate these templates so we can short-circuit if _Tp is incomplete. +#if _CCCL_CUDA_COMPILER(NVCC, <, 12, 1) + noexcept(__fold_and_v<__is_complete_and_nothrow_swappable_v>...>) +#else // ^^^ _CCCL_CUDA_COMPILER(NVCC, <, 12, 1) ^^^ / vvv _CCCL_CUDA_COMPILER(NVCC, >=, 12, 1) vvv + noexcept(__fold_and_v...>) +#endif // _CCCL_CUDA_COMPILER(NVCC, <, 12, 1) { (__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t)), ...); } diff --git a/libcudacxx/include/cuda/std/__type_traits/is_complete.h b/libcudacxx/include/cuda/std/__type_traits/is_complete.h new file mode 100644 index 00000000000..52946782045 --- /dev/null +++ b/libcudacxx/include/cuda/std/__type_traits/is_complete.h @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// Part of libcu++, the C++ Standard Library for your entire system, +// under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +#ifndef _CUDA_STD___TYPE_TRAITS_IS_COMPLETE_H +#define _CUDA_STD___TYPE_TRAITS_IS_COMPLETE_H + +#include + +#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) +# pragma GCC system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) +# pragma clang system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) +# pragma system_header +#endif // no system header + +#include // IWYU pragma: keep + +#include + +_CCCL_BEGIN_NAMESPACE_CUDA_STD + +#ifdef _CCCL_BUILTIN_IS_COMPLETE_TYPE + +template +inline constexpr bool __is_complete_v = _CCCL_BUILTIN_IS_COMPLETE_TYPE(T); + +#else // ^^^ _CCCL_BUILTIN_IS_COMPLETE_TYPE ^^^ / vvv no builtin vvv +// Must be a SFINAE trait instead of +// +// template +// inline constexpr __is_complete_v = sizeof(T) > 0; +// +// Because older NVCC doesn't even allow you to utter the phrase sizeof(T) if T is incomplete +template +inline constexpr bool __is_complete_v = false; + +template +inline constexpr bool __is_complete_v> = (sizeof(T) > 0); +#endif // ^^^ no builtin ^^^ + +_CCCL_END_NAMESPACE_CUDA_STD + +#include + +#endif // _CUDA_STD___TYPE_TRAITS_IS_COMPLETE_H diff --git a/libcudacxx/include/cuda/std/__type_traits/is_swappable.h b/libcudacxx/include/cuda/std/__type_traits/is_swappable.h index 88ed4c1f60b..8e9a87c7a6e 100644 --- a/libcudacxx/include/cuda/std/__type_traits/is_swappable.h +++ b/libcudacxx/include/cuda/std/__type_traits/is_swappable.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -49,7 +51,7 @@ namespace __detect_hidden_friend_swap // This will intentionally create an ambiguity with std::swap if that is find-able by ADL. But it will not interfere // with hidden friend swap template -_CCCL_HOST_DEVICE void swap(_Tp&, _Tp&); +_CCCL_HOST_DEVICE void swap(_Tp&, _Tp&); // NOLINT(performance-noexcept-swap) struct __hidden_friend_swap_found {}; @@ -193,6 +195,15 @@ template struct _CCCL_TYPE_VISIBILITY_DEFAULT is_nothrow_swappable : public bool_constant> {}; +// Do not use this trait over is_nothrow_swappable unless necessary. It is only useful in rare +// cases where a type may need to work with incomplete types and has friend functions that +// greedily instantiate the nothrow_swappable functions. +template > +inline constexpr bool __is_complete_and_nothrow_swappable_v = false; + +template +inline constexpr bool __is_complete_and_nothrow_swappable_v<_Tp, true> = is_nothrow_swappable_v<_Tp>; + _CCCL_END_NAMESPACE_CUDA_STD #include diff --git a/libcudacxx/test/libcudacxx/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp b/libcudacxx/test/libcudacxx/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp index 73e45a18106..5eb40b23100 100644 --- a/libcudacxx/test/libcudacxx/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp +++ b/libcudacxx/test/libcudacxx/std/utilities/tuple/tuple.tuple/tuple.cnstr/test_lazy_sfinae.pass.cpp @@ -95,7 +95,8 @@ struct BlowsUpOnConstCopy { static_assert(!cuda::std::is_same::value, ""); } - BlowsUpOnConstCopy(BlowsUpOnConstCopy&) = default; + BlowsUpOnConstCopy(BlowsUpOnConstCopy&) = default; + BlowsUpOnConstCopy(BlowsUpOnConstCopy&&) = default; }; // Test the following constructors: diff --git a/thrust/testing/swap_ranges.cu b/thrust/testing/swap_ranges.cu index 741f84d1084..a7ab8333cc4 100644 --- a/thrust/testing/swap_ranges.cu +++ b/thrust/testing/swap_ranges.cu @@ -131,7 +131,7 @@ struct type_with_swap bool m_swapped; }; -inline _CCCL_HOST_DEVICE void swap(type_with_swap& a, type_with_swap& b) +inline _CCCL_HOST_DEVICE void swap(type_with_swap& a, type_with_swap& b) noexcept { using ::cuda::std::swap; swap(a.m_x, b.m_x); diff --git a/thrust/thrust/detail/contiguous_storage.h b/thrust/thrust/detail/contiguous_storage.h index cfc0120c164..d9aa8b37d9f 100644 --- a/thrust/thrust/detail/contiguous_storage.h +++ b/thrust/thrust/detail/contiguous_storage.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -100,8 +101,27 @@ class contiguous_storage _CCCL_HOST_DEVICE void deallocate() noexcept; +private: + static constexpr bool is_swap_noexcept() + { + if (!::cuda::std::is_nothrow_swappable_v) + { + return false; + } + if (!::cuda::std::is_nothrow_swappable_v) + { + return false; + } + if (::cuda::std::allocator_traits::propagate_on_container_swap::value) + { + return ::cuda::std::is_nothrow_swappable_v; + } + return ::cuda::std::allocator_traits::is_always_equal::value; + } + +public: _CCCL_EXEC_CHECK_DISABLE - _CCCL_HOST_DEVICE void swap(contiguous_storage& other) + _CCCL_HOST_DEVICE void swap(contiguous_storage& other) noexcept(is_swap_noexcept()) { using ::cuda::std::swap; swap(m_begin, other.m_begin); diff --git a/thrust/thrust/detail/vector_base.h b/thrust/thrust/detail/vector_base.h index e084fae6b56..a549c6365a8 100644 --- a/thrust/thrust/detail/vector_base.h +++ b/thrust/thrust/detail/vector_base.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -427,7 +428,8 @@ class vector_base /*! This method swaps the contents of this vector_base with another vector_base. * \param v The vector_base with which to swap. */ - void swap(vector_base& v) + void swap(vector_base& v) noexcept(::cuda::std::is_nothrow_swappable_v + && ::cuda::std::is_nothrow_swappable_v) { using ::cuda::std::swap; swap(m_storage, v.m_storage);