223 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 void swap(expected& rhs) noexcept(see below);12//13// Constraints:14// is_swappable_v<E> is true and is_move_constructible_v<E> is true.15//16// Throws: Any exception thrown by the expressions in the Effects.17//18// Remarks: The exception specification is equivalent to:19// is_nothrow_move_constructible_v<E> && is_nothrow_swappable_v<E>.20 21#include <cassert>22#include <expected>23#include <type_traits>24#include <utility>25 26#include "../../types.h"27#include "test_macros.h"28 29// Test Constraints:30template <class E>31concept HasMemberSwap = requires(std::expected<void, E> x, std::expected<void, E> y) { x.swap(y); };32 33static_assert(HasMemberSwap<int>);34 35struct NotSwappable {};36void swap(NotSwappable&, NotSwappable&) = delete;37 38// !is_swappable_v<E>39static_assert(!HasMemberSwap<NotSwappable>);40 41struct NotMoveConstructible {42 NotMoveConstructible(NotMoveConstructible&&) = delete;43 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {}44};45 46// !is_move_constructible_v<E>47static_assert(!HasMemberSwap<NotMoveConstructible>);48 49// Test noexcept50struct MoveMayThrow {51 MoveMayThrow(MoveMayThrow&&) noexcept(false);52 friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {}53};54 55template <class E>56concept MemberSwapNoexcept = //57 requires(std::expected<void, E> x, std::expected<void, E> y) {58 { x.swap(y) } noexcept;59 };60 61static_assert(MemberSwapNoexcept<int>);62 63// !is_nothrow_move_constructible_v<E>64static_assert(!MemberSwapNoexcept<MoveMayThrow>);65 66struct SwapMayThrow {67 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}68};69 70// !is_nothrow_swappable_v<E>71static_assert(!MemberSwapNoexcept<SwapMayThrow>);72 73constexpr bool test() {74 // this->has_value() && rhs.has_value()75 {76 std::expected<void, int> x;77 std::expected<void, int> y;78 x.swap(y);79 80 assert(x.has_value());81 assert(y.has_value());82 }83 84 // !this->has_value() && !rhs.has_value()85 {86 std::expected<void, ADLSwap> x(std::unexpect, 5);87 std::expected<void, ADLSwap> y(std::unexpect, 10);88 x.swap(y);89 90 assert(!x.has_value());91 assert(x.error().i == 10);92 assert(x.error().adlSwapCalled);93 assert(!y.has_value());94 assert(y.error().i == 5);95 assert(y.error().adlSwapCalled);96 }97 98 // this->has_value() && !rhs.has_value()99 {100 Traced::state s{};101 std::expected<void, Traced> e1(std::in_place);102 std::expected<void, Traced> e2(std::unexpect, s, 10);103 104 e1.swap(e2);105 106 assert(!e1.has_value());107 assert(e1.error().data_ == 10);108 assert(e2.has_value());109 110 assert(s.moveCtorCalled);111 assert(s.dtorCalled);112 }113 114 // !this->has_value() && rhs.has_value()115 {116 Traced::state s{};117 std::expected<void, Traced> e1(std::unexpect, s, 10);118 std::expected<void, Traced> e2(std::in_place);119 120 e1.swap(e2);121 122 assert(e1.has_value());123 assert(!e2.has_value());124 assert(e2.error().data_ == 10);125 126 assert(s.moveCtorCalled);127 assert(s.dtorCalled);128 }129 130 // TailClobberer131 {132 std::expected<void, TailClobbererNonTrivialMove<1>> x(std::in_place);133 std::expected<void, TailClobbererNonTrivialMove<1>> y(std::unexpect);134 135 x.swap(y);136 137 // The next line would fail if adjusting the "has value" flag happened138 // _before_ constructing the member object inside the `swap`.139 assert(!x.has_value());140 assert(y.has_value());141 }142 143 // CheckForInvalidWrites144 {145 {146 CheckForInvalidWrites<true, true> x(std::unexpect);147 CheckForInvalidWrites<true, true> y;148 149 x.swap(y);150 151 assert(x.check());152 assert(y.check());153 }154 {155 CheckForInvalidWrites<false, true> x(std::unexpect);156 CheckForInvalidWrites<false, true> y;157 158 x.swap(y);159 160 assert(x.check());161 assert(y.check());162 }163 }164 165 return true;166}167 168void testException() {169#ifndef TEST_HAS_NO_EXCEPTIONS170 // !e1.has_value() && e2.has_value()171 {172 bool e1Destroyed = false;173 std::expected<void, ThrowOnMove> e1(std::unexpect, e1Destroyed);174 std::expected<void, ThrowOnMove> e2(std::in_place);175 try {176 e1.swap(e2);177 assert(false);178 } catch (Except) {179 assert(!e1.has_value());180 assert(e2.has_value());181 assert(!e1Destroyed);182 }183 }184 185 // e1.has_value() && !e2.has_value()186 {187 bool e2Destroyed = false;188 std::expected<void, ThrowOnMove> e1(std::in_place);189 std::expected<void, ThrowOnMove> e2(std::unexpect, e2Destroyed);190 try {191 e1.swap(e2);192 assert(false);193 } catch (Except) {194 assert(e1.has_value());195 assert(!e2.has_value());196 assert(!e2Destroyed);197 }198 }199 200 // TailClobberer201 {202 std::expected<void, TailClobbererNonTrivialMove<0, false, true>> x(std::in_place);203 std::expected<void, TailClobbererNonTrivialMove<0, false, true>> y(std::unexpect);204 try {205 x.swap(y);206 assert(false);207 } catch (Except) {208 // This would fail if `TailClobbererNonTrivialMove<0, false, true>`209 // clobbered the flag before throwing the exception.210 assert(x.has_value());211 assert(!y.has_value());212 }213 }214#endif // TEST_HAS_NO_EXCEPTIONS215}216 217int main(int, char**) {218 test();219 static_assert(test());220 testException();221 return 0;222}223