1919
2020// include these after __execution_fwd.hpp
2121#include " __concepts.hpp"
22+ #include " __scope.hpp"
2223
2324#include < new> // IWYU pragma: keep for ::new
2425#include < exception>
@@ -34,6 +35,13 @@ namespace stdexec {
3435 }
3536 };
3637
38+ inline auto __mk_has_value_guard (bool & __has_value) noexcept {
39+ __has_value = true ;
40+ return __scope_guard{[&]() noexcept {
41+ __has_value = false ;
42+ }};
43+ }
44+
3745 inline constexpr struct __nullopt_t {
3846 } __nullopt{};
3947
@@ -43,10 +51,10 @@ namespace stdexec {
4351 static_assert (destructible<_Tp>);
4452
4553 union {
46- _Tp __value ;
54+ _Tp __value_ ;
4755 };
4856
49- bool __has_value = false ;
57+ bool __has_value_ = false ;
5058
5159 __optional () noexcept {
5260 }
@@ -58,88 +66,102 @@ namespace stdexec {
5866
5967 template <__not_decays_to<__optional> _Up>
6068 requires constructible_from<_Tp, _Up>
61- __optional (_Up&& __v)
62- : __value(static_cast <_Up&&>(__v))
63- , __has_value(true ) {
69+ __optional (_Up&& __v) noexcept (__nothrow_constructible_from<_Tp, _Up>) {
70+ emplace (static_cast <_Up&&>(__v));
6471 }
6572
6673 template <class ... _Us>
6774 requires constructible_from<_Tp, _Us...>
68- __optional (std::in_place_t , _Us&&... __us)
69- : __value( static_cast < _Us&&>(__us) ...)
70- , __has_value( true ) {
75+ __optional (std::in_place_t , _Us&&... __us) noexcept (
76+ __nothrow_constructible_from<_Tp, _Us...>) {
77+ emplace ( static_cast <_Us&&>(__us)...);
7178 }
7279
7380 ~__optional () {
74- if (__has_value ) {
75- std::destroy_at (std::addressof (__value ));
81+ if (__has_value_ ) {
82+ std::destroy_at (std::addressof (__value_ ));
7683 }
7784 }
7885
86+ // The following emplace function must take great care to avoid use-after-free bugs.
87+ // If the object being constructed calls `start` on a newly created operation state
88+ // (as does the object returned from `submit`), and if `start` completes inline, it
89+ // could cause the destruction of the outer operation state that owns *this. The
90+ // function below uses the following pattern to avoid this:
91+ // 1. Set __has_value_ to true.
92+ // 2. Create a scope guard that will reset __has_value_ to false if the constructor
93+ // throws.
94+ // 3. Construct the new object in the storage, which may cause the invalidation of
95+ // *this. The emplace function must not access any members of *this after this point.
96+ // 4. Dismiss the scope guard, which will leave __has_value_ set to true.
97+ // 5. Return a reference to the new object -- which may be invalid! Calling code
98+ // must be aware of the danger.
7999 template <class ... _Us>
80100 requires constructible_from<_Tp, _Us...>
81101 auto emplace (_Us&&... __us) noexcept (__nothrow_constructible_from<_Tp, _Us...>) -> _Tp& {
82- reset (); // sets __has_value to false in case the next line throws
83- ::new (&__value) _Tp{static_cast <_Us&&>(__us)...};
84- __has_value = true ;
85- return __value;
102+ reset ();
103+ auto __sg = __mk_has_value_guard (__has_value_);
104+ auto * __p = ::new (static_cast <void *>(std::addressof (__value_)))
105+ _Tp{static_cast <_Us&&>(__us)...};
106+ __sg.__dismiss ();
107+ return *std::launder (__p);
86108 }
87109
88110 auto value () & -> _Tp& {
89- if (!__has_value ) {
111+ if (!__has_value_ ) {
90112 throw __bad_optional_access ();
91113 }
92- return __value ;
114+ return __value_ ;
93115 }
94116
95117 auto value () const & -> const _Tp& {
96- if (!__has_value ) {
118+ if (!__has_value_ ) {
97119 throw __bad_optional_access ();
98120 }
99- return __value ;
121+ return __value_ ;
100122 }
101123
102124 auto value () && -> _Tp&& {
103- if (!__has_value ) {
125+ if (!__has_value_ ) {
104126 throw __bad_optional_access ();
105127 }
106- return static_cast <_Tp&&>(__value );
128+ return static_cast <_Tp&&>(__value_ );
107129 }
108130
109131 auto operator *() & noexcept -> _Tp& {
110- STDEXEC_ASSERT (__has_value );
111- return __value ;
132+ STDEXEC_ASSERT (__has_value_ );
133+ return __value_ ;
112134 }
113135
114136 auto operator *() const & noexcept -> const _Tp& {
115- STDEXEC_ASSERT (__has_value );
116- return __value ;
137+ STDEXEC_ASSERT (__has_value_ );
138+ return __value_ ;
117139 }
118140
119141 auto operator *() && noexcept -> _Tp&& {
120- STDEXEC_ASSERT (__has_value );
121- return static_cast <_Tp&&>(__value );
142+ STDEXEC_ASSERT (__has_value_ );
143+ return static_cast <_Tp&&>(__value_ );
122144 }
123145
124146 auto operator ->() & noexcept -> _Tp* {
125- STDEXEC_ASSERT (__has_value );
126- return &__value ;
147+ STDEXEC_ASSERT (__has_value_ );
148+ return &__value_ ;
127149 }
128150
129151 auto operator ->() const & noexcept -> const _Tp* {
130- STDEXEC_ASSERT (__has_value );
131- return &__value ;
152+ STDEXEC_ASSERT (__has_value_ );
153+ return &__value_ ;
132154 }
133155
134156 [[nodiscard]]
135157 auto has_value () const noexcept -> bool {
136- return __has_value ;
158+ return __has_value_ ;
137159 }
138160
139161 void reset () noexcept {
140- if (__has_value ) {
141- std::destroy_at (std::addressof (__value ));
142- __has_value = false ;
162+ if (__has_value_ ) {
163+ std::destroy_at (std::addressof (__value_ ));
164+ __has_value_ = false ;
143165 }
144166 }
145167 };
0 commit comments