192 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(swap(x,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 constraint22static_assert(std::is_swappable_v<std::expected<void, int>>);23 24struct NotSwappable {25 NotSwappable& operator=(const NotSwappable&) = delete;26};27void swap(NotSwappable&, NotSwappable&) = delete;28 29// !is_swappable_v<E>30static_assert(!std::is_swappable_v<std::expected<void, NotSwappable>>);31 32struct NotMoveConstructible {33 NotMoveConstructible(NotMoveConstructible&&) = delete;34 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {}35};36 37// !is_move_constructible_v<E>38static_assert(!std::is_swappable_v<std::expected<void, NotMoveConstructible>>);39 40// Test noexcept41struct MoveMayThrow {42 MoveMayThrow(MoveMayThrow&&) noexcept(false);43 friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {}44};45 46template <class E>47concept FreeSwapNoexcept =48 requires(std::expected<void, E> x, std::expected<void, E> y) {49 { swap(x, y) } noexcept;50 };51 52static_assert(FreeSwapNoexcept<int>);53 54// !is_nothrow_move_constructible_v<E>55static_assert(!FreeSwapNoexcept<MoveMayThrow>);56 57struct SwapMayThrow {58 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}59};60 61// !is_nothrow_swappable_v<E>62static_assert(!FreeSwapNoexcept<SwapMayThrow>);63 64constexpr bool test() {65 // this->has_value() && rhs.has_value()66 {67 std::expected<void, int> x;68 std::expected<void, int> y;69 swap(x, y);70 71 assert(x.has_value());72 assert(y.has_value());73 }74 75 // !this->has_value() && !rhs.has_value()76 {77 std::expected<void, ADLSwap> x(std::unexpect, 5);78 std::expected<void, ADLSwap> y(std::unexpect, 10);79 swap(x, y);80 81 assert(!x.has_value());82 assert(x.error().i == 10);83 assert(x.error().adlSwapCalled);84 assert(!y.has_value());85 assert(y.error().i == 5);86 assert(y.error().adlSwapCalled);87 }88 89 // this->has_value() && !rhs.has_value()90 {91 Traced::state s{};92 std::expected<void, Traced> e1(std::in_place);93 std::expected<void, Traced> e2(std::unexpect, s, 10);94 95 swap(e1, e2);96 97 assert(!e1.has_value());98 assert(e1.error().data_ == 10);99 assert(e2.has_value());100 101 assert(s.moveCtorCalled);102 assert(s.dtorCalled);103 }104 105 // !this->has_value() && rhs.has_value()106 {107 Traced::state s{};108 std::expected<void, Traced> e1(std::unexpect, s, 10);109 std::expected<void, Traced> e2(std::in_place);110 111 swap(e1, e2);112 113 assert(e1.has_value());114 assert(!e2.has_value());115 assert(e2.error().data_ == 10);116 117 assert(s.moveCtorCalled);118 assert(s.dtorCalled);119 }120 121 // TailClobberer122 {123 std::expected<void, TailClobbererNonTrivialMove<1>> x(std::in_place);124 std::expected<void, TailClobbererNonTrivialMove<1>> y(std::unexpect);125 126 swap(x, y);127 128 // The next line would fail if adjusting the "has value" flag happened129 // _before_ constructing the member object inside the `swap`.130 assert(!x.has_value());131 assert(y.has_value());132 }133 134 return true;135}136 137void testException() {138#ifndef TEST_HAS_NO_EXCEPTIONS139 // !e1.has_value() && e2.has_value()140 {141 bool e1Destroyed = false;142 std::expected<void, ThrowOnMove> e1(std::unexpect, e1Destroyed);143 std::expected<void, ThrowOnMove> e2(std::in_place);144 try {145 swap(e1, e2);146 assert(false);147 } catch (Except) {148 assert(!e1.has_value());149 assert(e2.has_value());150 assert(!e1Destroyed);151 }152 }153 154 // e1.has_value() && !e2.has_value()155 {156 bool e2Destroyed = false;157 std::expected<void, ThrowOnMove> e1(std::in_place);158 std::expected<void, ThrowOnMove> e2(std::unexpect, e2Destroyed);159 try {160 swap(e1, e2);161 assert(false);162 } catch (Except) {163 assert(e1.has_value());164 assert(!e2.has_value());165 assert(!e2Destroyed);166 }167 }168 169 // TailClobberer170 {171 std::expected<void, TailClobbererNonTrivialMove<0, false, true>> x(std::in_place);172 std::expected<void, TailClobbererNonTrivialMove<0, false, true>> y(std::unexpect);173 try {174 swap(x, y);175 assert(false);176 } catch (Except) {177 // This would fail if `TailClobbererNonTrivialMove<0, false, true>`178 // clobbered the flag before throwing the exception.179 assert(x.has_value());180 assert(!y.has_value());181 }182 }183#endif // TEST_HAS_NO_EXCEPTIONS184}185 186int main(int, char**) {187 test();188 static_assert(test());189 testException();190 return 0;191}192