285 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// friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y)));12 13#include <cassert>14#include <expected>15#include <type_traits>16#include <utility>17 18#include "../../types.h"19#include "test_macros.h"20 21// Test Constraints:22struct NotSwappable {23 NotSwappable operator=(const NotSwappable&) = delete;24};25void swap(NotSwappable&, NotSwappable&) = delete;26 27static_assert(std::is_swappable_v<std::expected<int, int>>);28 29// !is_swappable_v<T>30static_assert(!std::is_swappable_v<std::expected<NotSwappable, int>>);31 32// !is_swappable_v<E>33static_assert(!std::is_swappable_v<std::expected<int, NotSwappable>>);34 35struct NotMoveConstructible {36 NotMoveConstructible(NotMoveConstructible&&) = delete;37 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {}38};39 40// !is_move_constructible_v<T>41static_assert(!std::is_swappable_v<std::expected<NotMoveConstructible, int>>);42 43// !is_move_constructible_v<E>44static_assert(!std::is_swappable_v<std::expected<int, NotMoveConstructible>>);45 46struct MoveMayThrow {47 MoveMayThrow(MoveMayThrow&&) noexcept(false);48 friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {}49};50 51// !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>52static_assert(std::is_swappable_v<std::expected<MoveMayThrow, int>>);53 54// is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>55static_assert(std::is_swappable_v<std::expected<int, MoveMayThrow>>);56 57// !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>58static_assert(!std::is_swappable_v<std::expected<MoveMayThrow, MoveMayThrow>>);59 60// Test noexcept61static_assert(std::is_nothrow_swappable_v<std::expected<int, int>>);62 63// !is_nothrow_move_constructible_v<T>64static_assert(!std::is_nothrow_swappable_v<std::expected<MoveMayThrow, int>>);65 66// !is_nothrow_move_constructible_v<E>67static_assert(!std::is_nothrow_swappable_v<std::expected<int, MoveMayThrow>>);68 69struct SwapMayThrow {70 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}71};72 73// !is_nothrow_swappable_v<T>74static_assert(!std::is_nothrow_swappable_v<std::expected<SwapMayThrow, int>>);75 76// !is_nothrow_swappable_v<E>77static_assert(!std::is_nothrow_swappable_v<std::expected<int, SwapMayThrow>>);78 79constexpr bool test() {80 // this->has_value() && rhs.has_value()81 {82 std::expected<ADLSwap, int> x(std::in_place, 5);83 std::expected<ADLSwap, int> y(std::in_place, 10);84 swap(x, y);85 86 assert(x.has_value());87 assert(x->i == 10);88 assert(x->adlSwapCalled);89 assert(y.has_value());90 assert(y->i == 5);91 assert(y->adlSwapCalled);92 }93 94 // !this->has_value() && !rhs.has_value()95 {96 std::expected<int, ADLSwap> x(std::unexpect, 5);97 std::expected<int, ADLSwap> y(std::unexpect, 10);98 swap(x, y);99 100 assert(!x.has_value());101 assert(x.error().i == 10);102 assert(x.error().adlSwapCalled);103 assert(!y.has_value());104 assert(y.error().i == 5);105 assert(y.error().adlSwapCalled);106 }107 108 // this->has_value() && !rhs.has_value()109 // && is_nothrow_move_constructible_v<E>110 {111 std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::in_place, 5);112 std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::unexpect, 10);113 114 swap(e1, e2);115 116 assert(!e1.has_value());117 assert(e1.error().i == 10);118 assert(e2.has_value());119 assert(e2->i == 5);120 121 assert(e1.error().numberOfMoves == 2);122 assert(!e1.error().swapCalled);123 assert(e2->numberOfMoves == 1);124 assert(!e2->swapCalled);125 }126 127 // this->has_value() && !rhs.has_value()128 // && !is_nothrow_move_constructible_v<E>129 {130 std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::in_place, 5);131 std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::unexpect, 10);132 133 swap(e1, e2);134 135 assert(!e1.has_value());136 assert(e1.error().i == 10);137 assert(e2.has_value());138 assert(e2->i == 5);139 140 assert(e1.error().numberOfMoves == 1);141 assert(!e1.error().swapCalled);142 assert(e2->numberOfMoves == 2);143 assert(!e2->swapCalled);144 }145 146 // !this->has_value() && rhs.has_value()147 // && is_nothrow_move_constructible_v<E>148 {149 std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::unexpect, 10);150 std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::in_place, 5);151 152 swap(e1, e2);153 154 assert(e1.has_value());155 assert(e1->i == 5);156 assert(!e2.has_value());157 assert(e2.error().i == 10);158 159 assert(e1->numberOfMoves == 1);160 assert(!e1->swapCalled);161 assert(e2.error().numberOfMoves == 2);162 assert(!e2.error().swapCalled);163 }164 165 // !this->has_value() && rhs.has_value()166 // && !is_nothrow_move_constructible_v<E>167 {168 std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::unexpect, 10);169 std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::in_place, 5);170 171 swap(e1, e2);172 173 assert(e1.has_value());174 assert(e1->i == 5);175 assert(!e2.has_value());176 assert(e2.error().i == 10);177 178 assert(e1->numberOfMoves == 2);179 assert(!e1->swapCalled);180 assert(e2.error().numberOfMoves == 1);181 assert(!e2.error().swapCalled);182 }183 184 // TailClobberer185 {186 // is_nothrow_move_constructible_v<E>187 {188 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> x(std::in_place);189 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> y(std::unexpect);190 191 swap(x, y);192 193 // Both of these would fail if adjusting the "has value" flags happened194 // _before_ constructing the member objects inside the `swap`.195 assert(!x.has_value());196 assert(y.has_value());197 }198 199 // !is_nothrow_move_constructible_v<E>200 {201 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> x(std::in_place);202 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> y(std::unexpect);203 204 swap(x, y);205 206 // Both of these would fail if adjusting the "has value" flags happened207 // _before_ constructing the member objects inside the `swap`.208 assert(!x.has_value());209 assert(y.has_value());210 }211 }212 213 return true;214}215 216void testException() {217#ifndef TEST_HAS_NO_EXCEPTIONS218 // !e1.has_value() && e2.has_value()219 {220 std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5);221 std::expected<ThrowOnMoveConstruct, int> e2(std::in_place);222 try {223 swap(e1, e2);224 assert(false);225 } catch (Except) {226 assert(!e1.has_value());227 assert(e1.error() == 5);228 }229 }230 231 // e1.has_value() && !e2.has_value()232 {233 std::expected<int, ThrowOnMoveConstruct> e1(5);234 std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect);235 try {236 swap(e1, e2);237 assert(false);238 } catch (Except) {239 assert(e1.has_value());240 assert(*e1 == 5);241 }242 }243 244 // TailClobberer245 {246 // is_nothrow_move_constructible_v<E>247 {248 std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> x(std::in_place);249 std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> y(std::unexpect);250 try {251 swap(x, y);252 assert(false);253 } catch (Except) {254 assert(x.has_value());255 // This would fail if `TailClobbererNonTrivialMove<1>` clobbered the256 // flag when rolling back the swap.257 assert(!y.has_value());258 }259 }260 261 // !is_nothrow_move_constructible_v<E>262 {263 std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> x(std::in_place);264 std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> y(std::unexpect);265 try {266 swap(x, y);267 assert(false);268 } catch (Except) {269 // This would fail if `TailClobbererNonTrivialMove<0>` clobbered the270 // flag when rolling back the swap.271 assert(x.has_value());272 assert(!y.has_value());273 }274 }275 }276#endif // TEST_HAS_NO_EXCEPTIONS277}278 279int main(int, char**) {280 test();281 static_assert(test());282 testException();283 return 0;284}285