389 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// template<class U = T>12// constexpr expected& operator=(U&& v);13//14// Constraints:15// - is_same_v<expected, remove_cvref_t<U>> is false; and16// - remove_cvref_t<U> is not a specialization of unexpected; and17// - is_constructible_v<T, U> is true; and18// - is_assignable_v<T&, U> is true; and19// - is_nothrow_constructible_v<T, U> || is_nothrow_move_constructible_v<T> ||20// is_nothrow_move_constructible_v<E> is true.21//22// Effects:23// - If has_value() is true, equivalent to: val = std::forward<U>(v);24// - Otherwise, equivalent to:25// reinit-expected(val, unex, std::forward<U>(v));26// has_val = true;27// - Returns: *this.28 29#include <cassert>30#include <concepts>31#include <expected>32#include <type_traits>33#include <utility>34 35#include "../../types.h"36#include "test_macros.h"37 38struct NotCopyConstructible {39 NotCopyConstructible(const NotCopyConstructible&) = delete;40 NotCopyConstructible& operator=(const NotCopyConstructible&) = default;41};42 43struct NotCopyAssignable {44 NotCopyAssignable(const NotCopyAssignable&) = default;45 NotCopyAssignable& operator=(const NotCopyAssignable&) = delete;46};47 48// Test constraints49static_assert(std::is_assignable_v<std::expected<int, int>&, int>);50 51// is_same_v<expected, remove_cvref_t<U>>52// it is true because it covered by the copy assignment53static_assert(std::is_assignable_v<std::expected<int, int>&, std::expected<int, int>>);54 55// remove_cvref_t<U> is a specialization of unexpected56// it is true because it covered the unexpected overload57static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>>);58 59// !is_constructible_v<T, U>60struct NoCtorFromInt {61 NoCtorFromInt(int) = delete;62 NoCtorFromInt& operator=(int);63};64static_assert(!std::is_assignable_v<std::expected<NoCtorFromInt, int>&, int>);65 66// !is_assignable_v<T&, U>67struct NoAssignFromInt {68 explicit NoAssignFromInt(int);69 NoAssignFromInt& operator=(int) = delete;70};71static_assert(!std::is_assignable_v<std::expected<NoAssignFromInt, int>&, int>);72 73template <bool moveNoexcept, bool convertNoexcept>74struct MaybeNoexcept {75 explicit MaybeNoexcept(int) noexcept(convertNoexcept);76 MaybeNoexcept(MaybeNoexcept&&) noexcept(moveNoexcept);77 MaybeNoexcept& operator=(MaybeNoexcept&&) = default;78 MaybeNoexcept& operator=(int);79};80 81// !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&82// is_nothrow_move_constructible_v<E>83static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, int>&, int>);84 85// is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&86// !is_nothrow_move_constructible_v<E>87static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, true>, MaybeNoexcept<false, false>>&, int>);88 89// !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> &&90// !is_nothrow_move_constructible_v<E>91static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<true, false>, MaybeNoexcept<false, false>>&, int>);92 93// !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&94// !is_nothrow_move_constructible_v<E>95static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&, int>);96 97constexpr bool test() {98 // If has_value() is true, equivalent to: val = std::forward<U>(v);99 // Copy100 {101 Traced::state oldState{};102 Traced::state newState{};103 std::expected<Traced, int> e1(std::in_place, oldState, 5);104 Traced u(newState, 10);105 decltype(auto) x = (e1 = u);106 static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);107 assert(&x == &e1);108 109 assert(e1.has_value());110 assert(e1.value().data_ == 10);111 assert(oldState.copyAssignCalled);112 }113 114 // If has_value() is true, equivalent to: val = std::forward<U>(v);115 // Move116 {117 Traced::state oldState{};118 Traced::state newState{};119 std::expected<Traced, int> e1(std::in_place, oldState, 5);120 Traced u(newState, 10);121 decltype(auto) x = (e1 = std::move(u));122 static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);123 assert(&x == &e1);124 125 assert(e1.has_value());126 assert(e1.value().data_ == 10);127 assert(oldState.moveAssignCalled);128 }129 130 // Otherwise, equivalent to:131 // reinit-expected(val, unex, std::forward<U>(v));132 // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&133 // !is_nothrow_move_constructible_v<E>134 // copy135 //136 // In this case, it should call the branch137 // destroy_at(addressof(oldval));138 // construct_at(addressof(newval), std::forward<Args>(args)...);139 {140 BothMayThrow::state oldState{};141 std::expected<MoveThrowConvNoexcept, BothMayThrow> e1(std::unexpect, oldState, 5);142 const int i = 10;143 decltype(auto) x = (e1 = i);144 static_assert(std::same_as<decltype(x), std::expected<MoveThrowConvNoexcept, BothMayThrow>&>);145 assert(&x == &e1);146 147 assert(e1.has_value());148 assert(e1.value().data_ == 10);149 150 assert(!oldState.copyCtorCalled);151 assert(!oldState.moveCtorCalled);152 assert(oldState.dtorCalled);153 assert(e1.value().copiedFromInt);154 }155 156 // Otherwise, equivalent to:157 // reinit-expected(val, unex, std::forward<U>(v));158 // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&159 // !is_nothrow_move_constructible_v<E>160 // move161 //162 // In this case, it should call the branch163 // destroy_at(addressof(oldval));164 // construct_at(addressof(newval), std::forward<Args>(args)...);165 {166 BothMayThrow::state oldState{};167 std::expected<MoveThrowConvNoexcept, BothMayThrow> e1(std::unexpect, oldState, 5);168 decltype(auto) x = (e1 = 10);169 static_assert(std::same_as<decltype(x), std::expected<MoveThrowConvNoexcept, BothMayThrow>&>);170 assert(&x == &e1);171 172 assert(e1.has_value());173 assert(e1.value().data_ == 10);174 175 assert(!oldState.copyCtorCalled);176 assert(!oldState.moveCtorCalled);177 assert(oldState.dtorCalled);178 assert(e1.value().movedFromInt);179 }180 181 // Otherwise, equivalent to:182 // reinit-expected(val, unex, std::forward<U>(v));183 // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> &&184 // !is_nothrow_move_constructible_v<E>185 // copy186 //187 // In this case, it should call the branch188 // T tmp(std::forward<Args>(args)...);189 // destroy_at(addressof(oldval));190 // construct_at(addressof(newval), std::move(tmp));191 {192 BothMayThrow::state oldState{};193 std::expected<MoveNoexceptConvThrow, BothMayThrow> e1(std::unexpect, oldState, 5);194 const int i = 10;195 decltype(auto) x = (e1 = i);196 static_assert(std::same_as<decltype(x), std::expected<MoveNoexceptConvThrow, BothMayThrow>&>);197 assert(&x == &e1);198 199 assert(e1.has_value());200 assert(e1.value().data_ == 10);201 202 assert(!oldState.copyCtorCalled);203 assert(!oldState.moveCtorCalled);204 assert(oldState.dtorCalled);205 assert(!e1.value().copiedFromInt);206 assert(e1.value().movedFromTmp);207 }208 209 // Otherwise, equivalent to:210 // reinit-expected(val, unex, std::forward<U>(v));211 // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> &&212 // !is_nothrow_move_constructible_v<E>213 // move214 //215 // In this case, it should call the branch216 // T tmp(std::forward<Args>(args)...);217 // destroy_at(addressof(oldval));218 // construct_at(addressof(newval), std::move(tmp));219 {220 BothMayThrow::state oldState{};221 std::expected<MoveNoexceptConvThrow, BothMayThrow> e1(std::unexpect, oldState, 5);222 decltype(auto) x = (e1 = 10);223 static_assert(std::same_as<decltype(x), std::expected<MoveNoexceptConvThrow, BothMayThrow>&>);224 assert(&x == &e1);225 226 assert(e1.has_value());227 assert(e1.value().data_ == 10);228 229 assert(!oldState.copyCtorCalled);230 assert(!oldState.moveCtorCalled);231 assert(oldState.dtorCalled);232 assert(!e1.value().copiedFromInt);233 assert(e1.value().movedFromTmp);234 }235 236 // Otherwise, equivalent to:237 // reinit-expected(val, unex, std::forward<U>(v));238 // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&239 // is_nothrow_move_constructible_v<E>240 // copy241 //242 // In this case, it should call the branch243 // U tmp(std::move(oldval));244 // destroy_at(addressof(oldval));245 // try {246 // construct_at(addressof(newval), std::forward<Args>(args)...);247 // } catch (...) {248 // construct_at(addressof(oldval), std::move(tmp));249 // throw;250 // }251 {252 TracedNoexcept::state oldState{};253 std::expected<BothMayThrow, TracedNoexcept> e1(std::unexpect, oldState, 5);254 const int i = 10;255 decltype(auto) x = (e1 = i);256 static_assert(std::same_as<decltype(x), std::expected<BothMayThrow, TracedNoexcept>&>);257 assert(&x == &e1);258 259 assert(e1.has_value());260 assert(e1.value().data_ == 10);261 262 assert(!oldState.copyCtorCalled);263 assert(oldState.moveCtorCalled);264 assert(oldState.dtorCalled);265 assert(e1.value().copiedFromInt);266 }267 268 // Otherwise, equivalent to:269 // reinit-expected(val, unex, std::forward<U>(v));270 // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&271 // is_nothrow_move_constructible_v<E>272 // move273 //274 // In this case, it should call the branch275 // U tmp(std::move(oldval));276 // destroy_at(addressof(oldval));277 // try {278 // construct_at(addressof(newval), std::forward<Args>(args)...);279 // } catch (...) {280 // construct_at(addressof(oldval), std::move(tmp));281 // throw;282 // }283 {284 TracedNoexcept::state oldState{};285 std::expected<BothMayThrow, TracedNoexcept> e1(std::unexpect, oldState, 5);286 decltype(auto) x = (e1 = 10);287 static_assert(std::same_as<decltype(x), std::expected<BothMayThrow, TracedNoexcept>&>);288 assert(&x == &e1);289 290 assert(e1.has_value());291 assert(e1.value().data_ == 10);292 293 assert(!oldState.copyCtorCalled);294 assert(oldState.moveCtorCalled);295 assert(oldState.dtorCalled);296 assert(e1.value().movedFromInt);297 }298 299 // Test default template argument.300 // Without it, the template parameter cannot be deduced from an initializer list301 {302 struct Bar {303 int i;304 int j;305 constexpr Bar(int ii, int jj) : i(ii), j(jj) {}306 };307 308 std::expected<Bar, int> e({5, 6});309 e = {7, 8};310 assert(e.value().i == 7);311 assert(e.value().j == 8);312 }313 314 // CheckForInvalidWrites315 {316 {317 CheckForInvalidWrites<true> e1(std::unexpect);318 e1 = 42;319 assert(e1.check());320 }321 {322 CheckForInvalidWrites<false> e1(std::unexpect);323 e1 = true;324 assert(e1.check());325 }326 }327 328 // Check move constructor selection329 {330 struct MoveOnlyMulti {331 bool used_move1 = false;332 bool used_move2 = false;333 334 constexpr MoveOnlyMulti() = default;335 constexpr MoveOnlyMulti(const MoveOnlyMulti&) = delete;336 constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&) = delete;337 constexpr MoveOnlyMulti& operator=(MoveOnlyMulti&&) {338 used_move1 = true;339 return *this;340 }341 constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&&) {342 used_move2 = true;343 return *this;344 };345 constexpr MoveOnlyMulti(MoveOnlyMulti&&) : used_move1(true) {}346 constexpr MoveOnlyMulti(const MoveOnlyMulti&&) : used_move2(true) {}347 };348 349 {350 MoveOnlyMulti t{};351 std::expected<MoveOnlyMulti, int> e1(std::unexpect);352 static_assert(std::is_same_v<decltype(std::move(t)), MoveOnlyMulti&&>);353 e1 = {std::move(t)};354 assert(e1.value().used_move1);355 }356 {357 const MoveOnlyMulti t{};358 std::expected<MoveOnlyMulti, int> e1(std::unexpect);359 static_assert(std::is_same_v<decltype(std::move(t)), const MoveOnlyMulti&&>);360 // _Up = remove_cv_t<const MoveOnlyMulti&&> --> should use MoveOnlyMulti(MoveOnlyMulti&&)361 e1 = {std::move(t)};362 assert(e1.value().used_move1);363 }364 }365 366 return true;367}368 369void testException() {370#ifndef TEST_HAS_NO_EXCEPTIONS371 std::expected<ThrowOnConvert, int> e1(std::unexpect, 5);372 try {373 e1 = 10;374 assert(false);375 } catch (Except) {376 assert(!e1.has_value());377 assert(e1.error() == 5);378 }379 380#endif // TEST_HAS_NO_EXCEPTIONS381}382 383int main(int, char**) {384 test();385 static_assert(test());386 testException();387 return 0;388}389