brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.1 KiB · d2eaf5c Raw
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