brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.1 KiB · 1fe7c41 Raw
324 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 expected& operator=(expected&& rhs) noexcept(see below);12//13// Constraints:14// - is_move_constructible_v<T> is true and15// - is_move_assignable_v<T> is true and16// - is_move_constructible_v<E> is true and17// - is_move_assignable_v<E> is true and18// - is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true.19//20// Effects:21// - If this->has_value() && rhs.has_value() is true, equivalent to val = std::move(*rhs).22// - Otherwise, if this->has_value() is true, equivalent to:23//   reinit-expected(unex, val, std::move(rhs.error()))24// - Otherwise, if rhs.has_value() is true, equivalent to:25//   reinit-expected(val, unex, std::move(*rhs))26// - Otherwise, equivalent to unex = std::move(rhs.error()).27// - Then, if no exception was thrown, equivalent to: has_val = rhs.has_value(); return *this;28//29// Returns: *this.30//31// Remarks: The exception specification is equivalent to:32// is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T> &&33// is_nothrow_move_assignable_v<E> && is_nothrow_move_constructible_v<E>34 35#include <cassert>36#include <concepts>37#include <expected>38#include <type_traits>39#include <utility>40 41#include "../../types.h"42#include "test_macros.h"43 44struct NotMoveConstructible {45  NotMoveConstructible(NotMoveConstructible&&)            = delete;46  NotMoveConstructible& operator=(NotMoveConstructible&&) = default;47};48 49struct NotMoveAssignable {50  NotMoveAssignable(NotMoveAssignable&&)            = default;51  NotMoveAssignable& operator=(NotMoveAssignable&&) = delete;52};53 54struct MoveCtorMayThrow {55  MoveCtorMayThrow(MoveCtorMayThrow&&) noexcept(false) {}56  MoveCtorMayThrow& operator=(MoveCtorMayThrow&&) noexcept = default;57};58 59// Test constraints60static_assert(std::is_move_assignable_v<std::expected<int, int>>);61 62// !is_move_assignable_v<T>63static_assert(!std::is_move_assignable_v<std::expected<NotMoveAssignable, int>>);64 65// !is_move_constructible_v<T>66static_assert(!std::is_move_assignable_v<std::expected<NotMoveConstructible, int>>);67 68// !is_move_assignable_v<E>69static_assert(!std::is_move_assignable_v<std::expected<int, NotMoveAssignable>>);70 71// !is_move_constructible_v<E>72static_assert(!std::is_move_assignable_v<std::expected<int, NotMoveConstructible>>);73 74// !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>75static_assert(std::is_move_assignable_v<std::expected<MoveCtorMayThrow, int>>);76 77// is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>78static_assert(std::is_move_assignable_v<std::expected<int, MoveCtorMayThrow>>);79 80// !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>81static_assert(!std::is_move_assignable_v<std::expected<MoveCtorMayThrow, MoveCtorMayThrow>>);82 83struct MoveAssignMayThrow {84  MoveAssignMayThrow(MoveAssignMayThrow&&) noexcept = default;85  MoveAssignMayThrow& operator=(MoveAssignMayThrow&&) noexcept(false) { return *this; }86};87 88// Test noexcept89static_assert(std::is_nothrow_move_assignable_v<std::expected<int, int>>);90 91// !is_nothrow_move_assignable_v<T>92static_assert(!std::is_nothrow_move_assignable_v<std::expected<MoveAssignMayThrow, int>>);93 94// !is_nothrow_move_constructible_v<T>95static_assert(!std::is_nothrow_move_assignable_v<std::expected<MoveCtorMayThrow, int>>);96 97// !is_nothrow_move_assignable_v<E>98static_assert(!std::is_nothrow_move_assignable_v<std::expected<int, MoveAssignMayThrow>>);99 100// !is_nothrow_move_constructible_v<E>101static_assert(!std::is_nothrow_move_assignable_v<std::expected<int, MoveCtorMayThrow>>);102 103constexpr bool test() {104  // If this->has_value() && rhs.has_value() is true, equivalent to val = std::move(*rhs).105  {106    Traced::state oldState{};107    Traced::state newState{};108    std::expected<Traced, int> e1(std::in_place, oldState, 5);109    std::expected<Traced, int> e2(std::in_place, newState, 10);110    decltype(auto) x = (e1 = std::move(e2));111    static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);112    assert(&x == &e1);113 114    assert(e1.has_value());115    assert(e1.value().data_ == 10);116    assert(oldState.moveAssignCalled);117  }118 119  // - Otherwise, if this->has_value() is true, equivalent to:120  // reinit-expected(unex, val, rhs.error())121  //  E move is not noexcept122  //  In this case, it should call the branch123  //124  //  U tmp(std::move(oldval));125  //  destroy_at(addressof(oldval));126  //  try {127  //    construct_at(addressof(newval), std::forward<Args>(args)...);128  //  } catch (...) {129  //    construct_at(addressof(oldval), std::move(tmp));130  //    throw;131  //  }132  //133  {134    TracedNoexcept::state oldState{};135    Traced::state newState{};136    std::expected<TracedNoexcept, Traced> e1(std::in_place, oldState, 5);137    std::expected<TracedNoexcept, Traced> e2(std::unexpect, newState, 10);138 139    decltype(auto) x = (e1 = std::move(e2));140    static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);141    assert(&x == &e1);142 143    assert(!e1.has_value());144    assert(e1.error().data_ == 10);145 146    assert(!oldState.moveAssignCalled);147    assert(oldState.moveCtorCalled);148    assert(oldState.dtorCalled);149    assert(!oldState.copyCtorCalled);150    assert(!newState.copyCtorCalled);151    assert(newState.moveCtorCalled);152    assert(!newState.dtorCalled);153  }154 155  // - Otherwise, if this->has_value() is true, equivalent to:156  // reinit-expected(unex, val, rhs.error())157  //  E move is noexcept158  //  In this case, it should call the branch159  //160  //  destroy_at(addressof(oldval));161  //  construct_at(addressof(newval), std::forward<Args>(args)...);162  //163  {164    Traced::state oldState{};165    TracedNoexcept::state newState{};166    std::expected<Traced, TracedNoexcept> e1(std::in_place, oldState, 5);167    std::expected<Traced, TracedNoexcept> e2(std::unexpect, newState, 10);168 169    decltype(auto) x = (e1 = std::move(e2));170    static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);171    assert(&x == &e1);172 173    assert(!e1.has_value());174    assert(e1.error().data_ == 10);175 176    assert(!oldState.moveCtorCalled);177    assert(oldState.dtorCalled);178    assert(!oldState.copyCtorCalled);179    assert(!newState.copyCtorCalled);180    assert(newState.moveCtorCalled);181    assert(!newState.dtorCalled);182  }183 184  // - Otherwise, if rhs.has_value() is true, equivalent to:185  // reinit-expected(val, unex, *rhs)186  //  T move is not noexcept187  //  In this case, it should call the branch188  //189  //  U tmp(std::move(oldval));190  //  destroy_at(addressof(oldval));191  //  try {192  //    construct_at(addressof(newval), std::forward<Args>(args)...);193  //  } catch (...) {194  //    construct_at(addressof(oldval), std::move(tmp));195  //    throw;196  //  }197  //198  {199    TracedNoexcept::state oldState{};200    Traced::state newState{};201    std::expected<Traced, TracedNoexcept> e1(std::unexpect, oldState, 5);202    std::expected<Traced, TracedNoexcept> e2(std::in_place, newState, 10);203 204    decltype(auto) x = (e1 = std::move(e2));205    static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);206    assert(&x == &e1);207 208    assert(e1.has_value());209    assert(e1.value().data_ == 10);210 211    assert(oldState.moveCtorCalled);212    assert(oldState.dtorCalled);213    assert(!oldState.copyCtorCalled);214    assert(!newState.copyCtorCalled);215    assert(newState.moveCtorCalled);216    assert(!newState.dtorCalled);217  }218 219  // - Otherwise, if rhs.has_value() is true, equivalent to:220  // reinit-expected(val, unex, *rhs)221  //  T move is noexcept222  //  In this case, it should call the branch223  //224  //  destroy_at(addressof(oldval));225  //  construct_at(addressof(newval), std::forward<Args>(args)...);226  //227  {228    Traced::state oldState{};229    TracedNoexcept::state newState{};230    std::expected<TracedNoexcept, Traced> e1(std::unexpect, oldState, 5);231    std::expected<TracedNoexcept, Traced> e2(std::in_place, newState, 10);232 233    decltype(auto) x = (e1 = std::move(e2));234    static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);235    assert(&x == &e1);236 237    assert(e1.has_value());238    assert(e1.value().data_ == 10);239 240    assert(!oldState.moveCtorCalled);241    assert(oldState.dtorCalled);242    assert(!oldState.copyCtorCalled);243    assert(!newState.copyCtorCalled);244    assert(newState.moveCtorCalled);245    assert(!newState.dtorCalled);246  }247 248  // Otherwise, equivalent to unex = rhs.error().249  {250    Traced::state oldState{};251    Traced::state newState{};252    std::expected<int, Traced> e1(std::unexpect, oldState, 5);253    std::expected<int, Traced> e2(std::unexpect, newState, 10);254    decltype(auto) x = (e1 = std::move(e2));255    static_assert(std::same_as<decltype(x), std::expected<int, Traced>&>);256    assert(&x == &e1);257 258    assert(!e1.has_value());259    assert(e1.error().data_ == 10);260    assert(oldState.moveAssignCalled);261  }262 263  // CheckForInvalidWrites264  {265    {266      CheckForInvalidWrites<true> e1(std::unexpect);267      CheckForInvalidWrites<true> e2;268 269      e1 = std::move(e2);270 271      assert(e1.check());272      assert(e2.check());273    }274    {275      CheckForInvalidWrites<false> e1(std::unexpect);276      CheckForInvalidWrites<false> e2;277 278      e1 = std::move(e2);279 280      assert(e1.check());281      assert(e2.check());282    }283  }284 285  return true;286}287 288void testException() {289#ifndef TEST_HAS_NO_EXCEPTIONS290  // assign value throw on move291  {292    std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5);293    std::expected<ThrowOnMoveConstruct, int> e2(std::in_place);294    try {295      e1 = std::move(e2);296      assert(false);297    } catch (Except) {298      assert(!e1.has_value());299      assert(e1.error() == 5);300    }301  }302 303  // assign error throw on move304  {305    std::expected<int, ThrowOnMoveConstruct> e1(5);306    std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect);307    try {308      e1 = std::move(e2);309      assert(false);310    } catch (Except) {311      assert(e1.has_value());312      assert(*e1 == 5);313    }314  }315#endif // TEST_HAS_NO_EXCEPTIONS316}317 318int main(int, char**) {319  test();320  static_assert(test());321  testException();322  return 0;323}324