1 /* 2 * Copyright (c) 2021-2024 NVIDIA Corporation 3 * 4 * Licensed under the Apache License Version 2.0 with LLVM Exceptions 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * https://llvm.org/LICENSE.txt 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #include "__execution_fwd.hpp" 19 20 // include these after __execution_fwd.hpp 21 #include "__concepts.hpp" 22 #include "__scope.hpp" 23 24 #include <new> // IWYU pragma: keep for ::new 25 #include <exception> 26 #include <memory> 27 #include <utility> 28 29 namespace stdexec { 30 namespace __opt { 31 struct __bad_optional_access : std::exception { 32 [[nodiscard]] whatstdexec::__opt::__bad_optional_access33 auto what() const noexcept -> const char* override { 34 return "stdexec::__optional: bad access"; 35 } 36 }; 37 __mk_has_value_guard(bool & __has_value)38 inline auto __mk_has_value_guard(bool& __has_value) noexcept { 39 __has_value = true; 40 return __scope_guard{[&]() noexcept { __has_value = false; }}; 41 } 42 43 inline constexpr struct __nullopt_t { 44 } __nullopt{}; 45 46 // A simplified version of std::optional for better compile times 47 template <class _Tp> 48 struct __optional { 49 static_assert(destructible<_Tp>); 50 51 union { 52 _Tp __value_; 53 }; 54 55 bool __has_value_ = false; 56 __optionalstdexec::__opt::__optional57 __optional() noexcept { 58 } 59 __optionalstdexec::__opt::__optional60 __optional(__nullopt_t) noexcept { 61 } 62 63 __optional(__optional&&) = delete; // immovable for simplicity's sake 64 65 template <__not_decays_to<__optional> _Up> 66 requires constructible_from<_Tp, _Up> __optionalstdexec::__opt::__optional67 __optional(_Up&& __v) noexcept(__nothrow_constructible_from<_Tp, _Up>) { 68 emplace(static_cast<_Up&&>(__v)); 69 } 70 71 template <class... _Us> 72 requires constructible_from<_Tp, _Us...> __optionalstdexec::__opt::__optional73 __optional(std::in_place_t, _Us&&... __us) 74 noexcept(__nothrow_constructible_from<_Tp, _Us...>) { 75 emplace(static_cast<_Us&&>(__us)...); 76 } 77 ~__optionalstdexec::__opt::__optional78 ~__optional() { 79 if (__has_value_) { 80 std::destroy_at(std::addressof(__value_)); 81 } 82 } 83 84 // The following emplace function must take great care to avoid use-after-free bugs. 85 // If the object being constructed calls `start` on a newly created operation state 86 // (as does the object returned from `submit`), and if `start` completes inline, it 87 // could cause the destruction of the outer operation state that owns *this. The 88 // function below uses the following pattern to avoid this: 89 // 1. Set __has_value_ to true. 90 // 2. Create a scope guard that will reset __has_value_ to false if the constructor 91 // throws. 92 // 3. Construct the new object in the storage, which may cause the invalidation of 93 // *this. The emplace function must not access any members of *this after this point. 94 // 4. Dismiss the scope guard, which will leave __has_value_ set to true. 95 // 5. Return a reference to the new object -- which may be invalid! Calling code 96 // must be aware of the danger. 97 template <class... _Us> 98 requires constructible_from<_Tp, _Us...> emplacestdexec::__opt::__optional99 auto emplace(_Us&&... __us) noexcept(__nothrow_constructible_from<_Tp, _Us...>) -> _Tp& { 100 reset(); 101 auto __sg = __mk_has_value_guard(__has_value_); 102 auto* __p = ::new (static_cast<void*>(std::addressof(__value_))) 103 _Tp{static_cast<_Us&&>(__us)...}; 104 __sg.__dismiss(); 105 return *std::launder(__p); 106 } 107 108 template <class _Fn, class... _Args> 109 requires same_as<_Tp, __call_result_t<_Fn, _Args...>> __emplace_fromstdexec::__opt::__optional110 auto __emplace_from(_Fn&& __f, _Args&&... __args) noexcept(__nothrow_callable<_Fn, _Args...>) 111 -> _Tp& { 112 reset(); 113 auto __sg = __mk_has_value_guard(__has_value_); 114 auto* __p = ::new (static_cast<void*>(std::addressof(__value_))) 115 _Tp(static_cast<_Fn&&>(__f)(static_cast<_Args&&>(__args)...)); 116 __sg.__dismiss(); 117 return *std::launder(__p); 118 } 119 valuestdexec::__opt::__optional120 auto value() & -> _Tp& { 121 if (!__has_value_) { 122 STDEXEC_THROW(__bad_optional_access()); 123 } 124 return __value_; 125 } 126 valuestdexec::__opt::__optional127 auto value() const & -> const _Tp& { 128 if (!__has_value_) { 129 STDEXEC_THROW(__bad_optional_access()); 130 } 131 return __value_; 132 } 133 valuestdexec::__opt::__optional134 auto value() && -> _Tp&& { 135 if (!__has_value_) { 136 STDEXEC_THROW(__bad_optional_access()); 137 } 138 return static_cast<_Tp&&>(__value_); 139 } 140 operator *stdexec::__opt::__optional141 auto operator*() & noexcept -> _Tp& { 142 STDEXEC_ASSERT(__has_value_); 143 return __value_; 144 } 145 operator *stdexec::__opt::__optional146 auto operator*() const & noexcept -> const _Tp& { 147 STDEXEC_ASSERT(__has_value_); 148 return __value_; 149 } 150 operator *stdexec::__opt::__optional151 auto operator*() && noexcept -> _Tp&& { 152 STDEXEC_ASSERT(__has_value_); 153 return static_cast<_Tp&&>(__value_); 154 } 155 operator ->stdexec::__opt::__optional156 auto operator->() & noexcept -> _Tp* { 157 STDEXEC_ASSERT(__has_value_); 158 return &__value_; 159 } 160 operator ->stdexec::__opt::__optional161 auto operator->() const & noexcept -> const _Tp* { 162 STDEXEC_ASSERT(__has_value_); 163 return &__value_; 164 } 165 166 [[nodiscard]] has_valuestdexec::__opt::__optional167 auto has_value() const noexcept -> bool { 168 return __has_value_; 169 } 170 resetstdexec::__opt::__optional171 void reset() noexcept { 172 if (__has_value_) { 173 std::destroy_at(std::addressof(__value_)); 174 __has_value_ = false; 175 } 176 } 177 }; 178 } // namespace __opt 179 180 using __opt::__optional; 181 using __opt::__bad_optional_access; 182 using __opt::__nullopt; 183 } // namespace stdexec 184