brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.4 KiB · 368a85b Raw
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