brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.6 KiB · 2f6af70 Raw
313 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=(const expected& rhs);12//13// Effects:14// - If this->has_value() && rhs.has_value() is true, equivalent to val = *rhs.15// - Otherwise, if this->has_value() is true, equivalent to:16//  reinit-expected(unex, val, rhs.error())17// - Otherwise, if rhs.has_value() is true, equivalent to:18//  reinit-expected(val, unex, *rhs)19// - Otherwise, equivalent to unex = rhs.error().20//21// - Then, if no exception was thrown, equivalent to: has_val = rhs.has_value(); return *this;22//23// Returns: *this.24//25// Remarks: This operator is defined as deleted unless:26// - is_copy_assignable_v<T> is true and27// - is_copy_constructible_v<T> is true and28// - is_copy_assignable_v<E> is true and29// - is_copy_constructible_v<E> is true and30// - is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true.31 32#include <cassert>33#include <concepts>34#include <expected>35#include <type_traits>36#include <utility>37 38#include "../../types.h"39#include "test_macros.h"40 41struct NotCopyConstructible {42  NotCopyConstructible(const NotCopyConstructible&)            = delete;43  NotCopyConstructible& operator=(const NotCopyConstructible&) = default;44};45 46struct NotCopyAssignable {47  NotCopyAssignable(const NotCopyAssignable&)            = default;48  NotCopyAssignable& operator=(const NotCopyAssignable&) = delete;49};50 51struct MoveMayThrow {52  MoveMayThrow(MoveMayThrow const&)            = default;53  MoveMayThrow& operator=(const MoveMayThrow&) = default;54  MoveMayThrow(MoveMayThrow&&) noexcept(false) {}55  MoveMayThrow& operator=(MoveMayThrow&&) noexcept(false) { return *this; }56};57 58// Test constraints59static_assert(std::is_copy_assignable_v<std::expected<int, int>>);60 61// !is_copy_assignable_v<T>62static_assert(!std::is_copy_assignable_v<std::expected<NotCopyAssignable, int>>);63 64// !is_copy_constructible_v<T>65static_assert(!std::is_copy_assignable_v<std::expected<NotCopyConstructible, int>>);66 67// !is_copy_assignable_v<E>68static_assert(!std::is_copy_assignable_v<std::expected<int, NotCopyAssignable>>);69 70// !is_copy_constructible_v<E>71static_assert(!std::is_copy_assignable_v<std::expected<int, NotCopyConstructible>>);72 73// !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>74static_assert(std::is_copy_assignable_v<std::expected<MoveMayThrow, int>>);75 76// is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>77static_assert(std::is_copy_assignable_v<std::expected<int, MoveMayThrow>>);78 79// !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>80static_assert(!std::is_copy_assignable_v<std::expected<MoveMayThrow, MoveMayThrow>>);81 82constexpr bool test() {83  // If this->has_value() && rhs.has_value() is true, equivalent to val = *rhs.84  {85    Traced::state oldState{};86    Traced::state newState{};87    std::expected<Traced, int> e1(std::in_place, oldState, 5);88    const std::expected<Traced, int> e2(std::in_place, newState, 10);89    decltype(auto) x = (e1 = e2);90    static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);91    assert(&x == &e1);92 93    assert(e1.has_value());94    assert(e1.value().data_ == 10);95    assert(oldState.copyAssignCalled);96  }97 98  // - Otherwise, if this->has_value() is true, equivalent to:99  // reinit-expected(unex, val, rhs.error())100  //  E move is not noexcept101  //  In this case, it should call the branch102  //103  //  U tmp(std::move(oldval));104  //  destroy_at(addressof(oldval));105  //  try {106  //    construct_at(addressof(newval), std::forward<Args>(args)...);107  //  } catch (...) {108  //    construct_at(addressof(oldval), std::move(tmp));109  //    throw;110  //  }111  //112  {113    TracedNoexcept::state oldState{};114    Traced::state newState{};115    std::expected<TracedNoexcept, Traced> e1(std::in_place, oldState, 5);116    const std::expected<TracedNoexcept, Traced> e2(std::unexpect, newState, 10);117 118    decltype(auto) x = (e1 = e2);119    static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);120    assert(&x == &e1);121 122    assert(!e1.has_value());123    assert(e1.error().data_ == 10);124 125    assert(!oldState.copyAssignCalled);126    assert(oldState.moveCtorCalled);127    assert(oldState.dtorCalled);128    assert(!oldState.copyCtorCalled);129    assert(newState.copyCtorCalled);130    assert(!newState.moveCtorCalled);131    assert(!newState.dtorCalled);132  }133 134  // - Otherwise, if this->has_value() is true, equivalent to:135  // reinit-expected(unex, val, rhs.error())136  //  E move is noexcept137  //  In this case, it should call the branch138  //139  //  destroy_at(addressof(oldval));140  //  construct_at(addressof(newval), std::forward<Args>(args)...);141  //142  {143    Traced::state oldState{};144    TracedNoexcept::state newState{};145    std::expected<Traced, TracedNoexcept> e1(std::in_place, oldState, 5);146    const std::expected<Traced, TracedNoexcept> e2(std::unexpect, newState, 10);147 148    decltype(auto) x = (e1 = e2);149    static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);150    assert(&x == &e1);151 152    assert(!e1.has_value());153    assert(e1.error().data_ == 10);154 155    assert(!oldState.copyAssignCalled);156    assert(!oldState.moveCtorCalled);157    assert(oldState.dtorCalled);158    assert(!oldState.copyCtorCalled);159    assert(newState.copyCtorCalled);160    assert(!newState.moveCtorCalled);161    assert(!newState.dtorCalled);162  }163 164  // - Otherwise, if rhs.has_value() is true, equivalent to:165  // reinit-expected(val, unex, *rhs)166  //  T move is not noexcept167  //  In this case, it should call the branch168  //169  //  U tmp(std::move(oldval));170  //  destroy_at(addressof(oldval));171  //  try {172  //    construct_at(addressof(newval), std::forward<Args>(args)...);173  //  } catch (...) {174  //    construct_at(addressof(oldval), std::move(tmp));175  //    throw;176  //  }177  //178  {179    TracedNoexcept::state oldState{};180    Traced::state newState{};181    std::expected<Traced, TracedNoexcept> e1(std::unexpect, oldState, 5);182    const std::expected<Traced, TracedNoexcept> e2(std::in_place, newState, 10);183 184    decltype(auto) x = (e1 = e2);185    static_assert(std::same_as<decltype(x), std::expected<Traced, TracedNoexcept>&>);186    assert(&x == &e1);187 188    assert(e1.has_value());189    assert(e1.value().data_ == 10);190 191    assert(!oldState.copyAssignCalled);192    assert(oldState.moveCtorCalled);193    assert(oldState.dtorCalled);194    assert(!oldState.copyCtorCalled);195    assert(newState.copyCtorCalled);196    assert(!newState.moveCtorCalled);197    assert(!newState.dtorCalled);198  }199 200  // - Otherwise, if rhs.has_value() is true, equivalent to:201  // reinit-expected(val, unex, *rhs)202  //  T move is noexcept203  //  In this case, it should call the branch204  //205  //  destroy_at(addressof(oldval));206  //  construct_at(addressof(newval), std::forward<Args>(args)...);207  //208  {209    Traced::state oldState{};210    TracedNoexcept::state newState{};211    std::expected<TracedNoexcept, Traced> e1(std::unexpect, oldState, 5);212    const std::expected<TracedNoexcept, Traced> e2(std::in_place, newState, 10);213 214    decltype(auto) x = (e1 = e2);215    static_assert(std::same_as<decltype(x), std::expected<TracedNoexcept, Traced>&>);216    assert(&x == &e1);217 218    assert(e1.has_value());219    assert(e1.value().data_ == 10);220 221    assert(!oldState.copyAssignCalled);222    assert(!oldState.moveCtorCalled);223    assert(oldState.dtorCalled);224    assert(!oldState.copyCtorCalled);225    assert(newState.copyCtorCalled);226    assert(!newState.moveCtorCalled);227    assert(!newState.dtorCalled);228  }229 230  // Otherwise, equivalent to unex = rhs.error().231  {232    Traced::state oldState{};233    Traced::state newState{};234    std::expected<int, Traced> e1(std::unexpect, oldState, 5);235    const std::expected<int, Traced> e2(std::unexpect, newState, 10);236    decltype(auto) x = (e1 = e2);237    static_assert(std::same_as<decltype(x), std::expected<int, Traced>&>);238    assert(&x == &e1);239 240    assert(!e1.has_value());241    assert(e1.error().data_ == 10);242    assert(oldState.copyAssignCalled);243  }244 245  // CheckForInvalidWrites246  {247    {248      CheckForInvalidWrites<true> e1(std::unexpect);249      CheckForInvalidWrites<true> e2;250 251      e1 = e2;252 253      assert(e1.check());254      assert(e2.check());255    }256    {257      CheckForInvalidWrites<false> e1(std::unexpect);258      CheckForInvalidWrites<false> e2;259 260      e1 = e2;261 262      assert(e1.check());263      assert(e2.check());264    }265  }266 267  return true;268}269 270void testException() {271#ifndef TEST_HAS_NO_EXCEPTIONS272  struct ThrowOnCopyMoveMayThrow {273    ThrowOnCopyMoveMayThrow() = default;274    ThrowOnCopyMoveMayThrow(const ThrowOnCopyMoveMayThrow&) { throw Except{}; };275    ThrowOnCopyMoveMayThrow& operator=(const ThrowOnCopyMoveMayThrow&) = default;276    ThrowOnCopyMoveMayThrow(ThrowOnCopyMoveMayThrow&&) noexcept(false) {}277  };278 279  // assign value throw on copy280  {281    std::expected<ThrowOnCopyMoveMayThrow, int> e1(std::unexpect, 5);282    const std::expected<ThrowOnCopyMoveMayThrow, int> e2(std::in_place);283    try {284      e1 = e2;285      assert(false);286    } catch (Except) {287      assert(!e1.has_value());288      assert(e1.error() == 5);289    }290  }291 292  // assign error throw on copy293  {294    std::expected<int, ThrowOnCopyMoveMayThrow> e1(5);295    const std::expected<int, ThrowOnCopyMoveMayThrow> e2(std::unexpect);296    try {297      e1 = e2;298      assert(false);299    } catch (Except) {300      assert(e1.has_value());301      assert(e1.value() == 5);302    }303  }304#endif // TEST_HAS_NO_EXCEPTIONS305}306 307int main(int, char**) {308  test();309  static_assert(test());310  testException();311  return 0;312}313