Skip to content

Commit a91afba

Browse files
committed
Implement ranges::single_view
1 parent 5916112 commit a91afba

File tree

19 files changed

+1293
-8
lines changed

19 files changed

+1293
-8
lines changed
Lines changed: 390 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,390 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
8+
//
9+
//===----------------------------------------------------------------------===//
10+
11+
#ifndef _LIBCUDACXX___RANGES_MOVABLE_BOX_H
12+
#define _LIBCUDACXX___RANGES_MOVABLE_BOX_H
13+
14+
#include <cuda/std/detail/__config>
15+
16+
#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
17+
# pragma GCC system_header
18+
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
19+
# pragma clang system_header
20+
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
21+
# pragma system_header
22+
#endif // no system header
23+
24+
#include <cuda/std/__concepts/constructible.h>
25+
#include <cuda/std/__concepts/copyable.h>
26+
#include <cuda/std/__concepts/movable.h>
27+
#include <cuda/std/__memory/addressof.h>
28+
#include <cuda/std/__memory/construct_at.h>
29+
#include <cuda/std/__tuple_dir/sfinae_helpers.h>
30+
#include <cuda/std/__type_traits/is_constructible.h>
31+
#include <cuda/std/__type_traits/is_nothrow_constructible.h>
32+
#include <cuda/std/__type_traits/is_nothrow_copy_constructible.h>
33+
#include <cuda/std/__type_traits/is_nothrow_default_constructible.h>
34+
#include <cuda/std/__type_traits/is_nothrow_move_constructible.h>
35+
#include <cuda/std/__utility/move.h>
36+
#include <cuda/std/detail/libcxx/include/optional>
37+
38+
_LIBCUDACXX_BEGIN_NAMESPACE_RANGES
39+
40+
// __movable_box allows turning a type that is move-constructible (but maybe not move-assignable) into
41+
// a type that is both move-constructible and move-assignable. It does that by introducing an empty state
42+
// and basically doing destroy-then-copy-construct in the assignment operator. The empty state is necessary
43+
// to handle the case where the copy construction fails after destroying the object.
44+
//
45+
// In some cases, we can completely avoid the use of an empty state; we provide a specialization of
46+
// __movable_box that does this, see below for the details.
47+
48+
// until C++23, `__movable_box` was named `__copyable_box` and required the stored type to be copy-constructible, not
49+
// just move-constructible; we always use the C++23 behavior
50+
template <class _Tp>
51+
_CCCL_CONCEPT __movable_box_object = move_constructible<_Tp> && is_object_v<_Tp>;
52+
53+
// The partial specialization implements an optimization for when we know we don't need to store
54+
// an empty state to represent failure to perform an assignment. For copy-assignment, this happens:
55+
//
56+
// 1. If the type is copyable (which includes copy-assignment), we can use the type's own assignment operator
57+
// directly and avoid using _CUDA_VSTD::optional.
58+
// 2. If the type is not copyable, but it is nothrow-copy-constructible, then we can implement assignment as
59+
// destroy-and-then-construct and we know it will never fail, so we don't need an empty state.
60+
//
61+
// The exact same reasoning can be applied for move-assignment, with copyable replaced by movable and
62+
// nothrow-copy-constructible replaced by nothrow-move-constructible. This specialization is enabled
63+
// whenever we can apply any of these optimizations for both the copy assignment and the move assignment
64+
// operator.
65+
66+
template <class _Tp>
67+
_CCCL_CONCEPT __doesnt_need_empty_state =
68+
(copy_constructible<_Tp>
69+
// 1. If copy_constructible<T> is true, movable-box<T> should store only a T if either T models
70+
// copyable, or is_nothrow_move_constructible_v<T> && is_nothrow_copy_constructible_v<T> is true.
71+
? copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> && is_nothrow_copy_constructible_v<_Tp>)
72+
// 2. Otherwise, movable-box<T> should store only a T if either T models movable or
73+
// is_nothrow_move_constructible_v<T> is true.
74+
: movable<_Tp> || is_nothrow_move_constructible_v<_Tp>);
75+
76+
// When _Tp doesn't have an assignment operator, we must implement __movable_box's assignment operator
77+
// by doing destroy_at followed by construct_at. However, that implementation strategy leads to UB if the nested
78+
// _Tp is potentially overlapping, as it is doing a non-transparent replacement of the sub-object, which means that
79+
// we're not considered "nested" inside the movable-box anymore, and since we're not nested within it, [basic.life]/1.5
80+
// says that we essentially just reused the storage of the movable-box for a completely unrelated object and ended the
81+
// movable-box's lifetime.
82+
// https://github.com/llvm/llvm-project/issues/70494#issuecomment-1845646490
83+
//
84+
// Hence, when the _Tp doesn't have an assignment operator, we can't risk making it a potentially-overlapping
85+
// subobject because of the above, and we don't use [[no_unique_address]] in that case.
86+
template <class _Tp>
87+
_CCCL_CONCEPT __can_use_no_unique_address = (copy_constructible<_Tp> ? copyable<_Tp> : movable<_Tp>);
88+
89+
// base class
90+
91+
template <class _Tp, bool = default_initializable<_Tp>>
92+
struct __mb_optional_destruct_base
93+
{
94+
_CCCL_NO_UNIQUE_ADDRESS optional<_Tp> __val_;
95+
96+
_CCCL_TEMPLATE(class... _Args)
97+
_CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>)
98+
_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_optional_destruct_base(in_place_t, _Args&&... __args) noexcept(
99+
is_nothrow_constructible_v<_Tp, _Args...>)
100+
: __val_(in_place, _CUDA_VSTD::forward<_Args>(__args)...)
101+
{}
102+
};
103+
104+
template <class _Tp>
105+
struct __mb_optional_destruct_base<_Tp, true>
106+
{
107+
_CCCL_NO_UNIQUE_ADDRESS optional<_Tp> __val_;
108+
109+
_LIBCUDACXX_HIDE_FROM_ABI constexpr __mb_optional_destruct_base() noexcept(is_nothrow_default_constructible_v<_Tp>)
110+
: __val_(in_place)
111+
{}
112+
113+
_CCCL_TEMPLATE(class... _Args)
114+
_CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>)
115+
_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_optional_destruct_base(in_place_t, _Args&&... __args) noexcept(
116+
is_nothrow_constructible_v<_Tp, _Args...>)
117+
: __val_(in_place, _CUDA_VSTD::forward<_Args>(__args)...)
118+
{}
119+
};
120+
121+
template <class _Tp, bool = copy_constructible<_Tp>>
122+
struct __mb_optional_copy_assign : __mb_optional_destruct_base<_Tp>
123+
{
124+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_copy_assign, __mb_optional_destruct_base, _Tp);
125+
126+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign(const __mb_optional_copy_assign&) = default;
127+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign(__mb_optional_copy_assign&&) = default;
128+
129+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign& operator=(const __mb_optional_copy_assign&) = delete;
130+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign& operator=(__mb_optional_copy_assign&&) = default;
131+
};
132+
133+
template <class _Tp>
134+
struct __mb_optional_copy_assign<_Tp, true> : __mb_optional_destruct_base<_Tp>
135+
{
136+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_copy_assign, __mb_optional_destruct_base, _Tp);
137+
138+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign(const __mb_optional_copy_assign&) = default;
139+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign(__mb_optional_copy_assign&&) = default;
140+
141+
_LIBCUDACXX_HIDE_FROM_ABI constexpr __mb_optional_copy_assign&
142+
operator=(const __mb_optional_copy_assign& __other) noexcept(is_nothrow_copy_constructible_v<_Tp>)
143+
{
144+
if (this != _CUDA_VSTD::addressof(__other))
145+
{
146+
if (__other.__has_value())
147+
{
148+
this->__val_.emplace(*__other);
149+
}
150+
else
151+
{
152+
this->__val_.reset();
153+
}
154+
}
155+
return *this;
156+
};
157+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_copy_assign& operator=(__mb_optional_copy_assign&&) = default;
158+
};
159+
160+
template <class _Tp, bool = movable<_Tp>>
161+
struct __mb_optional_move_assign : __mb_optional_copy_assign<_Tp>
162+
{
163+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_move_assign, __mb_optional_copy_assign, _Tp);
164+
};
165+
166+
template <class _Tp>
167+
struct __mb_optional_move_assign<_Tp, false> : __mb_optional_copy_assign<_Tp>
168+
{
169+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_move_assign, __mb_optional_copy_assign, _Tp);
170+
171+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_move_assign(const __mb_optional_move_assign&) = default;
172+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_move_assign(__mb_optional_move_assign&&) = default;
173+
_CCCL_HIDE_FROM_ABI constexpr __mb_optional_move_assign& operator=(const __mb_optional_move_assign&) = default;
174+
175+
_LIBCUDACXX_HIDE_FROM_ABI constexpr __mb_optional_move_assign&
176+
operator=(__mb_optional_move_assign&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>)
177+
{
178+
if (this != _CUDA_VSTD::addressof(__other))
179+
{
180+
if (__other.__has_value())
181+
{
182+
this->__val_.emplace(_CUDA_VSTD::move(*__other));
183+
}
184+
else
185+
{
186+
this->__val_.reset();
187+
}
188+
}
189+
return *this;
190+
}
191+
};
192+
193+
template <class _Tp>
194+
struct __mb_optional_base
195+
: __mb_optional_move_assign<_Tp>
196+
, __sfinae_move_base<copy_constructible<_Tp>, true>
197+
{
198+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_optional_base, __mb_optional_move_assign, _Tp);
199+
200+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept
201+
{
202+
return *this->__val_;
203+
}
204+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept
205+
{
206+
return *this->__val_;
207+
}
208+
209+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept
210+
{
211+
return this->__val_.operator->();
212+
}
213+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept
214+
{
215+
return this->__val_.operator->();
216+
}
217+
218+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr bool __has_value() const noexcept
219+
{
220+
return this->__val_.has_value();
221+
}
222+
};
223+
224+
// Specialization without a boolean
225+
template <class _Tp, bool = __can_use_no_unique_address<_Tp>>
226+
struct __mb_holder
227+
{
228+
_Tp __val_;
229+
230+
template <class... _Args>
231+
_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_holder(in_place_t, _Args&&... __args) noexcept(
232+
is_nothrow_constructible_v<_Tp, _Args...>)
233+
: __val_(_CUDA_VSTD::forward<_Args>(__args)...)
234+
{}
235+
};
236+
237+
template <class _Tp>
238+
struct __mb_holder<_Tp, true>
239+
{
240+
_CCCL_NO_UNIQUE_ADDRESS _Tp __val_;
241+
242+
template <class... _Args>
243+
_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_holder(in_place_t, _Args&&... __args) noexcept(
244+
is_nothrow_constructible_v<_Tp, _Args...>)
245+
: __val_(_CUDA_VSTD::forward<_Args>(__args)...)
246+
{}
247+
};
248+
249+
template <class _Tp, bool = default_initializable<_Tp>>
250+
struct __mb_holder_base
251+
{
252+
_CCCL_NO_UNIQUE_ADDRESS __mb_holder<_Tp> __holder_;
253+
254+
_CCCL_TEMPLATE(class... _Args)
255+
_CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>)
256+
_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_holder_base(in_place_t, _Args&&... __args) noexcept(
257+
is_nothrow_constructible_v<_Tp, _Args...>)
258+
: __holder_(in_place, _CUDA_VSTD::forward<_Args>(__args)...)
259+
{}
260+
};
261+
262+
template <class _Tp>
263+
struct __mb_holder_base<_Tp, true>
264+
{
265+
_CCCL_NO_UNIQUE_ADDRESS __mb_holder<_Tp> __holder_;
266+
267+
_LIBCUDACXX_HIDE_FROM_ABI constexpr __mb_holder_base() noexcept(is_nothrow_default_constructible_v<_Tp>)
268+
: __holder_(in_place)
269+
{}
270+
271+
_CCCL_TEMPLATE(class... _Args)
272+
_CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>)
273+
_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __mb_holder_base(in_place_t, _Args&&... __args) noexcept(
274+
is_nothrow_constructible_v<_Tp, _Args...>)
275+
: __holder_(in_place, _CUDA_VSTD::forward<_Args>(__args)...)
276+
{}
277+
};
278+
279+
template <class _Tp, bool = copyable<_Tp>>
280+
struct __mb_copy_assign : __mb_holder_base<_Tp>
281+
{
282+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_copy_assign, __mb_holder_base, _Tp);
283+
};
284+
285+
template <class _Tp>
286+
struct __mb_copy_assign<_Tp, false> : __mb_holder_base<_Tp>
287+
{
288+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_copy_assign, __mb_holder_base, _Tp);
289+
290+
_CCCL_HIDE_FROM_ABI constexpr __mb_copy_assign(const __mb_copy_assign&) = default;
291+
_CCCL_HIDE_FROM_ABI constexpr __mb_copy_assign(__mb_copy_assign&&) = default;
292+
293+
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX20 __mb_copy_assign& operator=(const __mb_copy_assign& __other) noexcept
294+
{
295+
static_assert(is_nothrow_copy_constructible_v<_Tp>);
296+
static_assert(!__can_use_no_unique_address<_Tp>);
297+
if (this != _CUDA_VSTD::addressof(__other))
298+
{
299+
_CUDA_VSTD::__destroy_at(_CUDA_VSTD::addressof(this->__holder_.__val_));
300+
_CUDA_VSTD::__construct_at(_CUDA_VSTD::addressof(this->__holder_.__val_), __other.__holder_.__val_);
301+
}
302+
return *this;
303+
};
304+
_CCCL_HIDE_FROM_ABI constexpr __mb_copy_assign& operator=(__mb_copy_assign&&) = default;
305+
};
306+
307+
template <class _Tp, bool = movable<_Tp>>
308+
struct __mb_move_assign : __mb_copy_assign<_Tp>
309+
{
310+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_move_assign, __mb_copy_assign, _Tp);
311+
};
312+
313+
template <class _Tp>
314+
struct __mb_move_assign<_Tp, false> : __mb_copy_assign<_Tp>
315+
{
316+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_move_assign, __mb_copy_assign, _Tp);
317+
318+
_CCCL_HIDE_FROM_ABI constexpr __mb_move_assign(const __mb_move_assign&) = default;
319+
_CCCL_HIDE_FROM_ABI constexpr __mb_move_assign(__mb_move_assign&&) = default;
320+
_CCCL_HIDE_FROM_ABI constexpr __mb_move_assign& operator=(const __mb_move_assign&) = default;
321+
322+
_LIBCUDACXX_HIDE_FROM_ABI _CCCL_CONSTEXPR_CXX20 __mb_move_assign&
323+
operator=(__mb_move_assign&& __other) noexcept(is_nothrow_move_constructible_v<_Tp>)
324+
{
325+
static_assert(is_nothrow_move_constructible_v<_Tp>);
326+
static_assert(!__can_use_no_unique_address<_Tp>);
327+
if (this != _CUDA_VSTD::addressof(__other))
328+
{
329+
_CUDA_VSTD::__destroy_at(_CUDA_VSTD::addressof(this->__holder_.__val_));
330+
_CUDA_VSTD::__construct_at(
331+
_CUDA_VSTD::addressof(this->__holder_.__val_), _CUDA_VSTD::move(__other.__holder_.__val_));
332+
}
333+
return *this;
334+
}
335+
};
336+
337+
template <class _Tp>
338+
struct __mb_base : __mb_move_assign<_Tp>
339+
{
340+
_LIBCUDACXX_DELEGATE_CONSTRUCTORS(__mb_base, __mb_move_assign, _Tp);
341+
342+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp const& operator*() const noexcept
343+
{
344+
return this->__holder_.__val_;
345+
}
346+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp& operator*() noexcept
347+
{
348+
return this->__holder_.__val_;
349+
}
350+
351+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept
352+
{
353+
return _CUDA_VSTD::addressof(this->__holder_.__val_);
354+
}
355+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept
356+
{
357+
return _CUDA_VSTD::addressof(this->__holder_.__val_);
358+
}
359+
360+
_CCCL_NODISCARD _LIBCUDACXX_HIDE_FROM_ABI constexpr bool __has_value() const noexcept
361+
{
362+
return true;
363+
}
364+
};
365+
366+
template <class _Tp>
367+
using __movable_box_base = _If<__doesnt_need_empty_state<_Tp>, __mb_base<_Tp>, __mb_optional_base<_Tp>>;
368+
369+
// Primary template - uses _CUDA_VSTD::optional and introduces an empty state in case assignment fails.
370+
template <class _Tp, bool = __movable_box_object<_Tp>>
371+
struct __movable_box;
372+
373+
template <class _Tp>
374+
struct __movable_box<_Tp, true> : __movable_box_base<_Tp>
375+
{
376+
using __base = __movable_box_base<_Tp>;
377+
378+
_CCCL_TEMPLATE(class... _Args)
379+
_CCCL_REQUIRES(is_constructible_v<_Tp, _Args...>)
380+
_LIBCUDACXX_HIDE_FROM_ABI constexpr explicit __movable_box(in_place_t, _Args&&... __args) noexcept(
381+
is_nothrow_constructible_v<_Tp, _Args...>)
382+
: __base(in_place, _CUDA_VSTD::forward<_Args>(__args)...)
383+
{}
384+
385+
_CCCL_HIDE_FROM_ABI constexpr __movable_box() = default;
386+
};
387+
388+
_LIBCUDACXX_END_NAMESPACE_RANGES
389+
390+
#endif // _LIBCUDACXX___RANGES_MOVABLE_BOX_H

0 commit comments

Comments
 (0)