215 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 G>12// constexpr expected& operator=(unexpected<G>&& e);13//14// Let GF be G15// Constraints:16// - is_constructible_v<E, GF> is true; and17// - is_assignable_v<E&, GF> is true; and18// - is_nothrow_constructible_v<E, GF> || is_nothrow_move_constructible_v<T> ||19// is_nothrow_move_constructible_v<E> is true.20//21// Effects:22// - If has_value() is true, equivalent to:23// reinit-expected(unex, val, std::forward<GF>(e.error()));24// has_val = false;25// - Otherwise, equivalent to: unex = std::forward<GF>(e.error());26// Returns: *this.27 28#include <cassert>29#include <concepts>30#include <expected>31#include <type_traits>32#include <utility>33 34#include "../../types.h"35#include "test_macros.h"36 37struct NotMoveConstructible {38 NotMoveConstructible(NotMoveConstructible&&) = delete;39 NotMoveConstructible& operator=(NotMoveConstructible&&) = default;40};41 42struct NotMoveAssignable {43 NotMoveAssignable(NotMoveAssignable&&) = default;44 NotMoveAssignable& operator=(NotMoveAssignable&&) = delete;45};46 47struct MoveMayThrow {48 MoveMayThrow(MoveMayThrow const&) = default;49 MoveMayThrow& operator=(const MoveMayThrow&) = default;50 MoveMayThrow(MoveMayThrow&&) noexcept(false) {}51 MoveMayThrow& operator=(MoveMayThrow&&) noexcept(false) { return *this; }52};53 54// Test constraints55static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>&&>);56 57// !is_constructible_v<E, GF>58static_assert(59 !std::is_assignable_v<std::expected<int, NotMoveConstructible>&, std::unexpected<NotMoveConstructible>&&>);60 61// !is_assignable_v<E&, GF>62static_assert(!std::is_assignable_v<std::expected<int, NotMoveAssignable>&, std::unexpected<NotMoveAssignable>&&>);63 64template <bool moveNoexcept, bool convertNoexcept>65struct MaybeNoexcept {66 explicit MaybeNoexcept(int) noexcept(convertNoexcept);67 MaybeNoexcept(MaybeNoexcept&&) noexcept(moveNoexcept);68 MaybeNoexcept& operator=(MaybeNoexcept&&) = default;69 MaybeNoexcept& operator=(int);70};71 72// !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> &&73// is_nothrow_move_constructible_v<E>74static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<true, false>>&,75 std::unexpected<int>&&>);76 77// is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> &&78// !is_nothrow_move_constructible_v<E>79static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, true>>&,80 std::unexpected<int>&&>);81 82// !is_nothrow_constructible_v<E, GF> && is_nothrow_move_constructible_v<T> &&83// !is_nothrow_move_constructible_v<E>84static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<true, true>, MaybeNoexcept<false, false>>&,85 std::unexpected<int>&&>);86 87// !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<T> &&88// !is_nothrow_move_constructible_v<E>89static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&,90 std::unexpected<int>&&>);91 92constexpr bool test() {93 // - If has_value() is true, equivalent to:94 // reinit-expected(unex, val, std::forward<GF>(e.error()));95 // is_nothrow_constructible_v<E, GF>96 //97 // In this case, it should call the branch98 // destroy_at(addressof(oldval));99 // construct_at(addressof(newval), std::forward<Args>(args)...);100 {101 BothNoexcept::state oldState{};102 std::expected<BothNoexcept, BothNoexcept> e(std::in_place, oldState, 5);103 std::unexpected<int> un(10);104 decltype(auto) x = (e = std::move(un));105 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, BothNoexcept>&>);106 assert(&x == &e);107 108 assert(!oldState.moveCtorCalled);109 assert(oldState.dtorCalled);110 assert(e.error().movedFromInt);111 }112 113 // - If has_value() is true, equivalent to:114 // reinit-expected(unex, val, std::forward<GF>(e.error()));115 // !is_nothrow_constructible_v<E, GF> && is_nothrow_move_constructible_v<E>116 //117 // In this case, it should call the branch118 // T tmp(std::forward<Args>(args)...);119 // destroy_at(addressof(oldval));120 // construct_at(addressof(newval), std::move(tmp));121 {122 BothNoexcept::state oldState{};123 std::expected<BothNoexcept, MoveNoexceptConvThrow> e(std::in_place, oldState, 5);124 std::unexpected<int> un(10);125 decltype(auto) x = (e = std::move(un));126 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, MoveNoexceptConvThrow>&>);127 assert(&x == &e);128 129 assert(!oldState.moveCtorCalled);130 assert(oldState.dtorCalled);131 assert(!e.error().movedFromInt);132 assert(e.error().movedFromTmp);133 }134 135 // - If has_value() is true, equivalent to:136 // reinit-expected(unex, val, std::forward<GF>(e.error()));137 // !is_nothrow_constructible_v<E, GF> && !is_nothrow_move_constructible_v<E>138 // is_nothrow_move_constructible_v<T>139 //140 // In this case, it should call the branch141 // U tmp(std::move(oldval));142 // destroy_at(addressof(oldval));143 // try {144 // construct_at(addressof(newval), std::forward<Args>(args)...);145 // } catch (...) {146 // construct_at(addressof(oldval), std::move(tmp));147 // throw;148 // }149 {150 BothNoexcept::state oldState{};151 std::expected<BothNoexcept, BothMayThrow> e(std::in_place, oldState, 5);152 std::unexpected<int> un(10);153 decltype(auto) x = (e = std::move(un));154 static_assert(std::same_as<decltype(x), std::expected<BothNoexcept, BothMayThrow>&>);155 assert(&x == &e);156 157 assert(oldState.moveCtorCalled);158 assert(oldState.dtorCalled);159 assert(e.error().movedFromInt);160 }161 162 // Otherwise, equivalent to: unex = std::forward<GF>(e.error());163 {164 Traced::state oldState{};165 Traced::state newState{};166 std::expected<int, Traced> e1(std::unexpect, oldState, 5);167 std::unexpected<Traced> e(std::in_place, newState, 10);168 decltype(auto) x = (e1 = std::move(e));169 static_assert(std::same_as<decltype(x), std::expected<int, Traced >&>);170 assert(&x == &e1);171 172 assert(!e1.has_value());173 assert(e1.error().data_ == 10);174 assert(oldState.moveAssignCalled);175 }176 177 // CheckForInvalidWrites178 {179 {180 CheckForInvalidWrites<true, true> e;181 std::unexpected<int> un(std::in_place, 42);182 e = std::move(un);183 assert(e.check());184 }185 {186 CheckForInvalidWrites<false, true> e;187 std::unexpected<bool> un(std::in_place, true);188 e = std::move(un);189 assert(e.check());190 }191 }192 193 return true;194}195 196void testException() {197#ifndef TEST_HAS_NO_EXCEPTIONS198 std::expected<void, ThrowOnMoveConstruct> e1(std::in_place);199 std::unexpected<ThrowOnMoveConstruct> un(std::in_place);200 try {201 e1 = std::move(un);202 assert(false);203 } catch (Except) {204 assert(e1.has_value());205 }206#endif // TEST_HAS_NO_EXCEPTIONS207}208 209int main(int, char**) {210 test();211 static_assert(test());212 testException();213 return 0;214}215