Skip to content

Commit 2830cfb

Browse files
committed
fix some bugs in the stdexec::__any type-erasure utility
1 parent 7872de7 commit 2830cfb

File tree

2 files changed

+76
-43
lines changed

2 files changed

+76
-43
lines changed

include/stdexec/__detail/__any.hpp

Lines changed: 71 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ namespace STDEXEC::__any
159159
struct __any_ptr;
160160

161161
template <template <class> class _Interface>
162-
struct __any_const_ptr;
162+
struct __any_cptr;
163163

164164
template <template <class> class... _BaseInterfaces>
165165
struct __extends;
@@ -265,7 +265,7 @@ namespace STDEXEC::__any
265265
[[nodiscard]]
266266
constexpr auto operator()(_Interface<_Base> const &__arg) const noexcept
267267
{
268-
return __any_const_ptr<_Interface>(std::addressof(__arg));
268+
return __any_cptr<_Interface>(std::addressof(__arg));
269269
}
270270
};
271271

@@ -308,15 +308,39 @@ namespace STDEXEC::__any
308308
STDEXEC::__any::__interface_cast<_BaseInterface>(__arg);
309309
};
310310

311+
//////////////////////////////////////////////////////////////////////////////////////////
312+
enum class __box_kind
313+
{
314+
__abstract,
315+
__object,
316+
__proxy
317+
};
318+
319+
//////////////////////////////////////////////////////////////////////////////////////////
320+
enum class __root_kind
321+
{
322+
__value,
323+
__reference
324+
};
325+
311326
//////////////////////////////////////////////////////////////////////////////////////////
312327
// __is_small: Model is Interface<_Ty> for some concrete _Ty
313328
template <class _Model>
314329
[[nodiscard]]
315330
constexpr bool __is_small(size_t __buffer_size) noexcept
316331
{
317-
constexpr bool __nothrow_movable = !__extension_of<_Model, __imovable>
318-
|| std::is_nothrow_move_constructible_v<_Model>;
319-
return sizeof(_Model) <= __buffer_size && __nothrow_movable;
332+
if constexpr (_Model::__root_kind == __root_kind::__reference)
333+
{
334+
STDEXEC_ASSERT(sizeof(_Model) <= __buffer_size);
335+
return true;
336+
}
337+
else
338+
{
339+
// If _Model requires movability, then it must be nothrow moveable to be small.
340+
STDEXEC_CONSTEXPR_LOCAL bool __nothrow_movable = !__extension_of<_Model, __imovable>
341+
|| __nothrow_move_constructible<_Model>;
342+
return sizeof(_Model) <= __buffer_size && __nothrow_movable;
343+
}
320344
}
321345

322346
//////////////////////////////////////////////////////////////////////////////////////////
@@ -372,19 +396,6 @@ namespace STDEXEC::__any
372396
template <template <class> class _Interface, class _BaseInterfaces = __bases_of<_Interface>>
373397
using __iabstract = _Interface<__mcall1<_BaseInterfaces, __iroot>>;
374398

375-
enum class __box_kind
376-
{
377-
__abstract,
378-
__object,
379-
__proxy
380-
};
381-
382-
enum class __root_kind
383-
{
384-
__value,
385-
__reference
386-
};
387-
388399
//////////////////////////////////////////////////////////////////////////////////////////
389400
// __iroot
390401
struct __iroot
@@ -454,6 +465,14 @@ namespace STDEXEC::__any
454465
: __val_(static_cast<_Args &&>(__args)...)
455466
{}
456467

468+
template <class _Fn, class... _Args>
469+
constexpr explicit __box(__in_place_from_t, _Fn &&__fn, _Args &&...__args)
470+
noexcept(__nothrow_callable<_Fn, _Args...>)
471+
: __val_(static_cast<_Fn &&>(__fn)(static_cast<_Args &&>(__args)...))
472+
{
473+
static_assert(__same_as<__call_result_t<_Fn, _Args...>, _Value>);
474+
}
475+
457476
template <class _Self>
458477
[[nodiscard]]
459478
static constexpr auto __value_(_Self &&__self) noexcept -> auto &&
@@ -1140,10 +1159,9 @@ namespace STDEXEC::__any
11401159

11411160
STDEXEC_IF_NOT_CONSTEVAL
11421161
{
1143-
STDEXEC_ASSERT((std::is_convertible_v<_Value &, __value_ref_t>)
1162+
STDEXEC_ASSERT((__std::convertible_to<_Value &, __value_ref_t>)
11441163
&& "attempt to get a mutable reference from a const reference, or an rvalue "
1145-
"from an "
1146-
"lvalue");
1164+
"from an lvalue");
11471165
}
11481166

11491167
if (__self.__is_indirect_())
@@ -1232,6 +1250,11 @@ namespace STDEXEC::__any
12321250
}
12331251
}
12341252

1253+
// __reference_proxy_root is not movable or copyable to preserve const-correctness of
1254+
// __any_cptr. Dereferencing an __any_cptr returns an lvalue reference to a const
1255+
// __reference_proxy_model. If __reference_proxy_model were copyable or movable, then
1256+
// it would be possible to cast away const-ness simply by copying or moving the
1257+
// __reference_proxy_model into a non-const object. That would be bad.
12351258
__reference_proxy_root(__reference_proxy_root &&) = delete;
12361259
__reference_proxy_root &operator=(__reference_proxy_root &&) = delete;
12371260

@@ -1249,10 +1272,7 @@ namespace STDEXEC::__any
12491272

12501273
constexpr ~__reference_proxy_root()
12511274
{
1252-
STDEXEC_IF_CONSTEVAL
1253-
{
1254-
__reset_();
1255-
}
1275+
__reset_();
12561276
}
12571277

12581278
constexpr void swap(__reference_proxy_root &__other) noexcept
@@ -1287,8 +1307,7 @@ namespace STDEXEC::__any
12871307
//! introducing an indirection.
12881308
//! @post __is_tagged() == true
12891309
auto &__ptr = *__std::start_lifetime_as<__tagged_ptr>(__buff_);
1290-
__ptr = static_cast<__iabstract<_Interface> *>(
1291-
std::addressof(STDEXEC::__unconst(__model)));
1310+
__ptr = static_cast<__interface_type *>(std::addressof(STDEXEC::__unconst(__model)));
12921311
}
12931312
else
12941313
{
@@ -1592,7 +1611,7 @@ namespace STDEXEC::__any
15921611

15931612
template <template <class> class _Interface>
15941613
[[nodiscard]]
1595-
constexpr auto *operator()(__any_const_ptr<_Interface> const &__ptr) const
1614+
constexpr auto *operator()(__any_cptr<_Interface> const &__ptr) const
15961615
{
15971616
return (*this)(__ptr.operator->());
15981617
}
@@ -1684,6 +1703,16 @@ namespace STDEXEC::__any
16841703
(*this).template __emplace_<_Value>(static_cast<_Args &&>(__args)...);
16851704
}
16861705

1706+
template <class _Fn, class... _Args>
1707+
constexpr explicit __any(__in_place_from_t, _Fn &&__fn, _Args &&...__args)
1708+
: __any()
1709+
{
1710+
using __value_t = __decay_t<__call_result_t<_Fn, _Args...>>;
1711+
(*this).template __emplace_<__value_t>(
1712+
__emplace_from{[&]() noexcept(__nothrow_callable<_Fn, _Args...>)
1713+
{ return static_cast<_Fn &&>(__fn)(static_cast<_Args &&>(__args)...); }});
1714+
}
1715+
16871716
// Implicit derived-to-base conversion constructor
16881717
template <class _Other>
16891718
requires __extension_of<_Interface<_Other>, __imovable>
@@ -1797,7 +1826,7 @@ namespace STDEXEC::__any
17971826
constexpr __any_ptr_base(__any_ptr_base const &__other) noexcept
17981827
: __reference_()
17991828
{
1800-
(*this).__proxy_assign(std::addressof(__other.__reference_));
1829+
__reference_.__copy(__other.__reference_);
18011830
}
18021831

18031832
template <template <class> class _OtherInterface>
@@ -1811,7 +1840,7 @@ namespace STDEXEC::__any
18111840
constexpr __any_ptr_base &operator=(__any_ptr_base const &__other) noexcept
18121841
{
18131842
__reset(__reference_);
1814-
(*this).__proxy_assign(std::addressof(__other.__reference_));
1843+
__reference_.__copy(__other.__reference_);
18151844
return *this;
18161845
}
18171846

@@ -1847,9 +1876,8 @@ namespace STDEXEC::__any
18471876

18481877
template <template <class> class>
18491878
friend struct __any_ptr_base;
1850-
18511879
friend struct __any_ptr<_Interface>;
1852-
friend struct __any_const_ptr<_Interface>;
1880+
friend struct __any_cptr<_Interface>;
18531881

18541882
//! @param __other A pointer to a value proxy model implementing _Interface.
18551883
template <__extension_of<_Interface> _CvValueProxy>
@@ -1917,9 +1945,9 @@ namespace STDEXEC::__any
19171945

19181946
// Disable const-to-mutable conversions:
19191947
template <template <class> class _Other>
1920-
__any_ptr(__any_const_ptr<_Other> const &) = delete;
1948+
__any_ptr(__any_cptr<_Other> const &) = delete;
19211949
template <template <class> class _Other>
1922-
__any_ptr &operator=(__any_const_ptr<_Other> const &) = delete;
1950+
__any_ptr &operator=(__any_cptr<_Other> const &) = delete;
19231951

19241952
template <__model_of<_Interface> _Value>
19251953
constexpr __any_ptr(_Value *__value_ptr) noexcept
@@ -1974,9 +2002,9 @@ namespace STDEXEC::__any
19742002
STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __any_ptr(_Interface<_Base> *) -> __any_ptr<_Interface>;
19752003

19762004
//////////////////////////////////////////////////////////////////////////////////////////
1977-
// __any_const_ptr
2005+
// __any_cptr
19782006
template <template <class> class _Interface>
1979-
struct __any_const_ptr : __any_ptr_base<_Interface>
2007+
struct __any_cptr : __any_ptr_base<_Interface>
19802008
{
19812009
using __reference = __any_ptr_base<_Interface>::__model_type const;
19822010
using __pointer = __reference *;
@@ -1985,36 +2013,36 @@ namespace STDEXEC::__any
19852013
using __any_ptr_base<_Interface>::operator=;
19862014

19872015
template <__model_of<_Interface> _Value>
1988-
constexpr __any_const_ptr(_Value const *__value_ptr) noexcept
2016+
constexpr __any_cptr(_Value const *__value_ptr) noexcept
19892017
: __any_ptr_base<_Interface>()
19902018
{
19912019
(*this).__value_assign(__value_ptr);
19922020
}
19932021

19942022
template <__extension_of<_Interface> _Proxy>
1995-
constexpr __any_const_ptr(_Proxy const *__proxy_ptr) noexcept
2023+
constexpr __any_cptr(_Proxy const *__proxy_ptr) noexcept
19962024
: __any_ptr_base<_Interface>()
19972025
{
19982026
(*this).__proxy_assign(__proxy_ptr);
19992027
}
20002028

20012029
template <__model_of<_Interface> _Value>
2002-
constexpr __any_const_ptr &operator=(_Value const *__value_ptr) noexcept
2030+
constexpr __any_cptr &operator=(_Value const *__value_ptr) noexcept
20032031
{
20042032
__reset((*this).__reference_);
20052033
(*this).__value_assign(__value_ptr);
20062034
return *this;
20072035
}
20082036

20092037
template <__extension_of<_Interface> _Proxy>
2010-
constexpr __any_const_ptr &operator=(_Proxy const *__proxy_ptr) noexcept
2038+
constexpr __any_cptr &operator=(_Proxy const *__proxy_ptr) noexcept
20112039
{
20122040
__reset((*this).__reference_);
20132041
(*this).__proxy_assign(__proxy_ptr);
20142042
return *this;
20152043
}
20162044

2017-
friend constexpr void swap(__any_const_ptr &__lhs, __any_const_ptr &__rhs) noexcept
2045+
friend constexpr void swap(__any_cptr &__lhs, __any_cptr &__rhs) noexcept
20182046
{
20192047
__lhs.__reference_.swap(__rhs.__reference_);
20202048
}
@@ -2034,7 +2062,7 @@ namespace STDEXEC::__any
20342062

20352063
template <template <class> class _Interface, class _Base>
20362064
STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE
2037-
__any_const_ptr(_Interface<_Base> const *) -> __any_const_ptr<_Interface>;
2065+
__any_cptr(_Interface<_Base> const *) -> __any_cptr<_Interface>;
20382066

20392067
//////////////////////////////////////////////////////////////////////////////////////////
20402068
// __iequality_comparable
@@ -2053,7 +2081,7 @@ namespace STDEXEC::__any
20532081
private:
20542082
[[nodiscard]]
20552083
// NOLINTNEXTLINE(modernize-use-override)
2056-
constexpr virtual bool __equal_to(__any_const_ptr<__iequality_comparable> __other) const
2084+
constexpr virtual bool __equal_to(__any_cptr<__iequality_comparable> __other) const
20572085
{
20582086
auto const &type = STDEXEC::__any::__type(*this);
20592087

include/stdexec/__detail/__utility.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ namespace STDEXEC
4646
{
4747
} __no_init{};
4848

49+
inline constexpr struct __in_place_from_t
50+
{
51+
explicit __in_place_from_t() = default;
52+
} __in_place_from{};
53+
4954
namespace
5055
{
5156
struct __anon

0 commit comments

Comments
 (0)