324 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++17, c++2010 11// constexpr expected& operator=(expected&& rhs) noexcept(see below);12//13// Constraints:14// - is_move_constructible_v<T> is true and15// - is_move_assignable_v<T> is true and16// - is_move_constructible_v<E> is true and17// - is_move_assignable_v<E> is true and18// - is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true.19//20// Effects:21// - If this->has_value() && rhs.has_value() is true, equivalent to val = std::move(*rhs).22// - Otherwise, if this->has_value() is true, equivalent to:23// reinit-expected(unex, val, std::move(rhs.error()))24// - Otherwise, if rhs.has_value() is true, equivalent to:25// reinit-expected(val, unex, std::move(*rhs))26// - Otherwise, equivalent to unex = std::move(rhs.error()).27// - Then, if no exception was thrown, equivalent to: has_val = rhs.has_value(); return *this;28//29// Returns: *this.30//31// Remarks: The exception specification is equivalent to:32// is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T> &&33// is_nothrow_move_assignable_v<E> && is_nothrow_move_constructible_v<E>34 35#include <cassert>36#include <concepts>37#include <expected>38#include <type_traits>39#include <utility>40 41#include "../../types.h"42#include "test_macros.h"43 44struct NotMoveConstructible {45 NotMoveConstructible(NotMoveConstructible&&) = delete;46 NotMoveConstructible& operator=(NotMoveConstructible&&) = default;47};48 49struct NotMoveAssignable {50 NotMoveAssignable(NotMoveAssignable&&) = default;51 NotMoveAssignable& operator=(NotMoveAssignable&&) = delete;52};53 54struct MoveCtorMayThrow {55 MoveCtorMayThrow(MoveCtorMayThrow&&) noexcept(false) {}56 MoveCtorMayThrow& operator=(MoveCtorMayThrow&&) noexcept = default;57};58 59// Test constraints60static_assert(std::is_move_assignable_v<std::expected<int, int>>);61 62// !is_move_assignable_v<T>63static_assert(!std::is_move_assignable_v<std::expected<NotMoveAssignable, int>>);64 65// !is_move_constructible_v<T>66static_assert(!std::is_move_assignable_v<std::expected<NotMoveConstructible, int>>);67 68// !is_move_assignable_v<E>69static_assert(!std::is_move_assignable_v<std::expected<int, NotMoveAssignable>>);70 71// !is_move_constructible_v<E>72static_assert(!std::is_move_assignable_v<std::expected<int, NotMoveConstructible>>);73 74// !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>75static_assert(std::is_move_assignable_v<std::expected<MoveCtorMayThrow, int>>);76 77// is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>78static_assert(std::is_move_assignable_v<std::expected<int, MoveCtorMayThrow>>);79 80// !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>81static_assert(!std::is_move_assignable_v<std::expected<MoveCtorMayThrow, MoveCtorMayThrow>>);82 83struct MoveAssignMayThrow {84 MoveAssignMayThrow(MoveAssignMayThrow&&) noexcept = default;85 MoveAssignMayThrow& operator=(MoveAssignMayThrow&&) noexcept(false) { return *this; }86};87 88// Test noexcept89static_assert(std::is_nothrow_move_assignable_v<std::expected<int, int>>);90 91// !is_nothrow_move_assignable_v<T>92static_assert(!std::is_nothrow_move_assignable_v<std::expected<MoveAssignMayThrow, int>>);93 94// !is_nothrow_move_constructible_v<T>95static_assert(!std::is_nothrow_move_assignable_v<std::expected<MoveCtorMayThrow, int>>);96 97// !is_nothrow_move_assignable_v<E>98static_assert(!std::is_nothrow_move_assignable_v<std::expected<int, MoveAssignMayThrow>>);99 100// !is_nothrow_move_constructible_v<E>101static_assert(!std::is_nothrow_move_assignable_v<std::expected<int, MoveCtorMayThrow>>);102 103constexpr bool test() {104 // If this->has_value() && rhs.has_value() is true, equivalent to val = std::move(*rhs).105 {106 Traced::state oldState{};107 Traced::state newState{};108 std::expected<Traced, int> e1(std::in_place, oldState, 5);109 std::expected<Traced, int> e2(std::in_place, newState, 10);110 decltype(auto) x = (e1 = std::move(e2));111 static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);112 assert(&x == &e1);113 114 assert(e1.has_value());115 assert(e1.value().data_ == 10);116 assert(oldState.moveAssignCalled);117 }118 119 // - Otherwise, if this->has_value() is true, equivalent to:120 // reinit-expected(unex, val, rhs.error())121 // E move is not noexcept122 // In this case, it should call the branch123 //124 // U tmp(std::move(oldval));125 // destroy_at(addressof(oldval));126 // try {127 // construct_at(addressof(newval), std::forward<Args>(args)...);128 // } catch (...) {129 // construct_at(addressof(oldval), std::move(tmp));130 // throw;131 // }132 //133 {134 TracedNoexcept::state oldState{};135 Traced::state newState{};136 std::expected<TracedNoexcept, Traced> e1(std::in_place, oldState, 5);137 std::expected<TracedNoexcept, Traced> e2(std::unexpect, newState, 10);138 139 decltype(auto) x = (e1 = std::move(e2));140 static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);141 assert(&x == &e1);142 143 assert(!e1.has_value());144 assert(e1.error().data_ == 10);145 146 assert(!oldState.moveAssignCalled);147 assert(oldState.moveCtorCalled);148 assert(oldState.dtorCalled);149 assert(!oldState.copyCtorCalled);150 assert(!newState.copyCtorCalled);151 assert(newState.moveCtorCalled);152 assert(!newState.dtorCalled);153 }154 155 // - Otherwise, if this->has_value() is true, equivalent to:156 // reinit-expected(unex, val, rhs.error())157 // E move is noexcept158 // In this case, it should call the branch159 //160 // destroy_at(addressof(oldval));161 // construct_at(addressof(newval), std::forward<Args>(args)...);162 //163 {164 Traced::state oldState{};165 TracedNoexcept::state newState{};166 std::expected<Traced, TracedNoexcept> e1(std::in_place, oldState, 5);167 std::expected<Traced, TracedNoexcept> e2(std::unexpect, newState, 10);168 169 decltype(auto) x = (e1 = std::move(e2));170 static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);171 assert(&x == &e1);172 173 assert(!e1.has_value());174 assert(e1.error().data_ == 10);175 176 assert(!oldState.moveCtorCalled);177 assert(oldState.dtorCalled);178 assert(!oldState.copyCtorCalled);179 assert(!newState.copyCtorCalled);180 assert(newState.moveCtorCalled);181 assert(!newState.dtorCalled);182 }183 184 // - Otherwise, if rhs.has_value() is true, equivalent to:185 // reinit-expected(val, unex, *rhs)186 // T move is not noexcept187 // In this case, it should call the branch188 //189 // U tmp(std::move(oldval));190 // destroy_at(addressof(oldval));191 // try {192 // construct_at(addressof(newval), std::forward<Args>(args)...);193 // } catch (...) {194 // construct_at(addressof(oldval), std::move(tmp));195 // throw;196 // }197 //198 {199 TracedNoexcept::state oldState{};200 Traced::state newState{};201 std::expected<Traced, TracedNoexcept> e1(std::unexpect, oldState, 5);202 std::expected<Traced, TracedNoexcept> e2(std::in_place, newState, 10);203 204 decltype(auto) x = (e1 = std::move(e2));205 static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);206 assert(&x == &e1);207 208 assert(e1.has_value());209 assert(e1.value().data_ == 10);210 211 assert(oldState.moveCtorCalled);212 assert(oldState.dtorCalled);213 assert(!oldState.copyCtorCalled);214 assert(!newState.copyCtorCalled);215 assert(newState.moveCtorCalled);216 assert(!newState.dtorCalled);217 }218 219 // - Otherwise, if rhs.has_value() is true, equivalent to:220 // reinit-expected(val, unex, *rhs)221 // T move is noexcept222 // In this case, it should call the branch223 //224 // destroy_at(addressof(oldval));225 // construct_at(addressof(newval), std::forward<Args>(args)...);226 //227 {228 Traced::state oldState{};229 TracedNoexcept::state newState{};230 std::expected<TracedNoexcept, Traced> e1(std::unexpect, oldState, 5);231 std::expected<TracedNoexcept, Traced> e2(std::in_place, newState, 10);232 233 decltype(auto) x = (e1 = std::move(e2));234 static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);235 assert(&x == &e1);236 237 assert(e1.has_value());238 assert(e1.value().data_ == 10);239 240 assert(!oldState.moveCtorCalled);241 assert(oldState.dtorCalled);242 assert(!oldState.copyCtorCalled);243 assert(!newState.copyCtorCalled);244 assert(newState.moveCtorCalled);245 assert(!newState.dtorCalled);246 }247 248 // Otherwise, equivalent to unex = rhs.error().249 {250 Traced::state oldState{};251 Traced::state newState{};252 std::expected<int, Traced> e1(std::unexpect, oldState, 5);253 std::expected<int, Traced> e2(std::unexpect, newState, 10);254 decltype(auto) x = (e1 = std::move(e2));255 static_assert(std::same_as<decltype(x), std::expected<int, Traced>&>);256 assert(&x == &e1);257 258 assert(!e1.has_value());259 assert(e1.error().data_ == 10);260 assert(oldState.moveAssignCalled);261 }262 263 // CheckForInvalidWrites264 {265 {266 CheckForInvalidWrites<true> e1(std::unexpect);267 CheckForInvalidWrites<true> e2;268 269 e1 = std::move(e2);270 271 assert(e1.check());272 assert(e2.check());273 }274 {275 CheckForInvalidWrites<false> e1(std::unexpect);276 CheckForInvalidWrites<false> e2;277 278 e1 = std::move(e2);279 280 assert(e1.check());281 assert(e2.check());282 }283 }284 285 return true;286}287 288void testException() {289#ifndef TEST_HAS_NO_EXCEPTIONS290 // assign value throw on move291 {292 std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5);293 std::expected<ThrowOnMoveConstruct, int> e2(std::in_place);294 try {295 e1 = std::move(e2);296 assert(false);297 } catch (Except) {298 assert(!e1.has_value());299 assert(e1.error() == 5);300 }301 }302 303 // assign error throw on move304 {305 std::expected<int, ThrowOnMoveConstruct> e1(5);306 std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect);307 try {308 e1 = std::move(e2);309 assert(false);310 } catch (Except) {311 assert(e1.has_value());312 assert(*e1 == 5);313 }314 }315#endif // TEST_HAS_NO_EXCEPTIONS316}317 318int main(int, char**) {319 test();320 static_assert(test());321 testException();322 return 0;323}324