325 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<T> is true and15// is_swappable_v<E> is true and16// is_move_constructible_v<T> && is_move_constructible_v<E> is true, and17// is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true.18//19// Throws: Any exception thrown by the expressions in the Effects.20// Remarks: The exception specification is equivalent to:21// is_nothrow_move_constructible_v<T> && is_nothrow_swappable_v<T> &&22// is_nothrow_move_constructible_v<E> && is_nothrow_swappable_v<E>23 24#include <cassert>25#include <expected>26#include <type_traits>27#include <utility>28 29#include "../../types.h"30#include "test_macros.h"31 32// Test Constraints:33template <class T, class E>34concept HasMemberSwap = requires(std::expected<T, E> x, std::expected<T, E> y) { x.swap(y); };35 36static_assert(HasMemberSwap<int, int>);37 38struct NotSwappable {};39void swap(NotSwappable&, NotSwappable&) = delete;40 41// !is_swappable_v<T>42static_assert(!HasMemberSwap<NotSwappable, int>);43 44// !is_swappable_v<E>45static_assert(!HasMemberSwap<int, NotSwappable>);46 47struct NotMoveConstructible {48 NotMoveConstructible(NotMoveConstructible&&) = delete;49 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {}50};51 52// !is_move_constructible_v<T>53static_assert(!HasMemberSwap<NotMoveConstructible, int>);54 55// !is_move_constructible_v<E>56static_assert(!HasMemberSwap<int, NotMoveConstructible>);57 58struct MoveMayThrow {59 MoveMayThrow(MoveMayThrow&&) noexcept(false);60 friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {}61};62 63// !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>64static_assert(HasMemberSwap<MoveMayThrow, int>);65 66// is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>67static_assert(HasMemberSwap<int, MoveMayThrow>);68 69// !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>70static_assert(!HasMemberSwap<MoveMayThrow, MoveMayThrow>);71 72// Test noexcept73template <class T, class E>74concept MemberSwapNoexcept = //75 requires(std::expected<T, E> x, std::expected<T, E> y) {76 { x.swap(y) } noexcept;77 };78 79static_assert(MemberSwapNoexcept<int, int>);80 81// !is_nothrow_move_constructible_v<T>82static_assert(!MemberSwapNoexcept<MoveMayThrow, int>);83 84// !is_nothrow_move_constructible_v<E>85static_assert(!MemberSwapNoexcept<int, MoveMayThrow>);86 87struct SwapMayThrow {88 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}89};90 91// !is_nothrow_swappable_v<T>92static_assert(!MemberSwapNoexcept<SwapMayThrow, int>);93 94// !is_nothrow_swappable_v<E>95static_assert(!MemberSwapNoexcept<int, SwapMayThrow>);96 97constexpr bool test() {98 // this->has_value() && rhs.has_value()99 {100 std::expected<ADLSwap, int> x(std::in_place, 5);101 std::expected<ADLSwap, int> y(std::in_place, 10);102 x.swap(y);103 104 assert(x.has_value());105 assert(x->i == 10);106 assert(x->adlSwapCalled);107 assert(y.has_value());108 assert(y->i == 5);109 assert(y->adlSwapCalled);110 }111 112 // !this->has_value() && !rhs.has_value()113 {114 std::expected<int, ADLSwap> x(std::unexpect, 5);115 std::expected<int, ADLSwap> y(std::unexpect, 10);116 x.swap(y);117 118 assert(!x.has_value());119 assert(x.error().i == 10);120 assert(x.error().adlSwapCalled);121 assert(!y.has_value());122 assert(y.error().i == 5);123 assert(y.error().adlSwapCalled);124 }125 126 // this->has_value() && !rhs.has_value()127 // && is_nothrow_move_constructible_v<E>128 {129 std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::in_place, 5);130 std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::unexpect, 10);131 132 e1.swap(e2);133 134 assert(!e1.has_value());135 assert(e1.error().i == 10);136 assert(e2.has_value());137 assert(e2->i == 5);138 139 assert(e1.error().numberOfMoves == 2);140 assert(!e1.error().swapCalled);141 assert(e2->numberOfMoves == 1);142 assert(!e2->swapCalled);143 }144 145 // this->has_value() && !rhs.has_value()146 // && !is_nothrow_move_constructible_v<E>147 {148 std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::in_place, 5);149 std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::unexpect, 10);150 151 e1.swap(e2);152 153 assert(!e1.has_value());154 assert(e1.error().i == 10);155 assert(e2.has_value());156 assert(e2->i == 5);157 158 assert(e1.error().numberOfMoves == 1);159 assert(!e1.error().swapCalled);160 assert(e2->numberOfMoves == 2);161 assert(!e2->swapCalled);162 }163 164 // !this->has_value() && rhs.has_value()165 // && is_nothrow_move_constructible_v<E>166 {167 std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::unexpect, 10);168 std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::in_place, 5);169 170 e1.swap(e2);171 172 assert(e1.has_value());173 assert(e1->i == 5);174 assert(!e2.has_value());175 assert(e2.error().i == 10);176 177 assert(e1->numberOfMoves == 1);178 assert(!e1->swapCalled);179 assert(e2.error().numberOfMoves == 2);180 assert(!e2.error().swapCalled);181 }182 183 // !this->has_value() && rhs.has_value()184 // && !is_nothrow_move_constructible_v<E>185 {186 std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::unexpect, 10);187 std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::in_place, 5);188 189 e1.swap(e2);190 191 assert(e1.has_value());192 assert(e1->i == 5);193 assert(!e2.has_value());194 assert(e2.error().i == 10);195 196 assert(e1->numberOfMoves == 2);197 assert(!e1->swapCalled);198 assert(e2.error().numberOfMoves == 1);199 assert(!e2.error().swapCalled);200 }201 202 // TailClobberer203 {204 // is_nothrow_move_constructible_v<E>205 {206 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> x(std::in_place);207 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> y(std::unexpect);208 209 x.swap(y);210 211 // Both of these would fail if adjusting the "has value" flags happened212 // _before_ constructing the member objects inside the `swap`.213 assert(!x.has_value());214 assert(y.has_value());215 }216 217 // !is_nothrow_move_constructible_v<E>218 {219 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> x(std::in_place);220 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> y(std::unexpect);221 222 x.swap(y);223 224 // Both of these would fail if adjusting the "has value" flags happened225 // _before_ constructing the member objects inside the `swap`.226 assert(!x.has_value());227 assert(y.has_value());228 }229 }230 231 // CheckForInvalidWrites232 {233 {234 CheckForInvalidWrites<true> x(std::unexpect);235 CheckForInvalidWrites<true> y;236 237 x.swap(y);238 239 assert(x.check());240 assert(y.check());241 }242 {243 CheckForInvalidWrites<false> x(std::unexpect);244 CheckForInvalidWrites<false> y;245 246 x.swap(y);247 248 assert(x.check());249 assert(y.check());250 }251 }252 253 return true;254}255 256void testException() {257#ifndef TEST_HAS_NO_EXCEPTIONS258 // !e1.has_value() && e2.has_value()259 {260 std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5);261 std::expected<ThrowOnMoveConstruct, int> e2(std::in_place);262 try {263 e1.swap(e2);264 assert(false);265 } catch (Except) {266 assert(!e1.has_value());267 assert(e1.error() == 5);268 }269 }270 271 // e1.has_value() && !e2.has_value()272 {273 std::expected<int, ThrowOnMoveConstruct> e1(5);274 std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect);275 try {276 e1.swap(e2);277 assert(false);278 } catch (Except) {279 assert(e1.has_value());280 assert(*e1 == 5);281 }282 }283 284 // TailClobberer285 {286 // is_nothrow_move_constructible_v<E>287 {288 std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> x(std::in_place);289 std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> y(std::unexpect);290 try {291 x.swap(y);292 assert(false);293 } catch (Except) {294 assert(x.has_value());295 // This would fail if `TailClobbererNonTrivialMove<1>` clobbered the296 // flag when rolling back the swap.297 assert(!y.has_value());298 }299 }300 301 // !is_nothrow_move_constructible_v<E>302 {303 std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> x(std::in_place);304 std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> y(std::unexpect);305 try {306 x.swap(y);307 assert(false);308 } catch (Except) {309 // This would fail if `TailClobbererNonTrivialMove<0>` clobbered the310 // flag when rolling back the swap.311 assert(x.has_value());312 assert(!y.has_value());313 }314 }315 }316#endif // TEST_HAS_NO_EXCEPTIONS317}318 319int main(int, char**) {320 test();321 static_assert(test());322 testException();323 return 0;324}325