brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.4 KiB · 795fa77 Raw
389 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//  template<class U = T>12//   constexpr expected& operator=(U&& v);13//14// Constraints:15// - is_same_v<expected, remove_cvref_t<U>> is false; and16// - remove_cvref_t<U> is not a specialization of unexpected; and17// - is_constructible_v<T, U> is true; and18// - is_assignable_v<T&, U> is true; and19// - is_nothrow_constructible_v<T, U> || is_nothrow_move_constructible_v<T> ||20//   is_nothrow_move_constructible_v<E> is true.21//22// Effects:23// - If has_value() is true, equivalent to: val = std::forward<U>(v);24// - Otherwise, equivalent to:25//   reinit-expected(val, unex, std::forward<U>(v));26//   has_val = true;27// - Returns: *this.28 29#include <cassert>30#include <concepts>31#include <expected>32#include <type_traits>33#include <utility>34 35#include "../../types.h"36#include "test_macros.h"37 38struct NotCopyConstructible {39  NotCopyConstructible(const NotCopyConstructible&)            = delete;40  NotCopyConstructible& operator=(const NotCopyConstructible&) = default;41};42 43struct NotCopyAssignable {44  NotCopyAssignable(const NotCopyAssignable&)            = default;45  NotCopyAssignable& operator=(const NotCopyAssignable&) = delete;46};47 48// Test constraints49static_assert(std::is_assignable_v<std::expected<int, int>&, int>);50 51// is_same_v<expected, remove_cvref_t<U>>52// it is true because it covered by the copy assignment53static_assert(std::is_assignable_v<std::expected<int, int>&, std::expected<int, int>>);54 55// remove_cvref_t<U> is a specialization of unexpected56// it is true because it covered the unexpected overload57static_assert(std::is_assignable_v<std::expected<int, int>&, std::unexpected<int>>);58 59// !is_constructible_v<T, U>60struct NoCtorFromInt {61  NoCtorFromInt(int) = delete;62  NoCtorFromInt& operator=(int);63};64static_assert(!std::is_assignable_v<std::expected<NoCtorFromInt, int>&, int>);65 66// !is_assignable_v<T&, U>67struct NoAssignFromInt {68  explicit NoAssignFromInt(int);69  NoAssignFromInt& operator=(int) = delete;70};71static_assert(!std::is_assignable_v<std::expected<NoAssignFromInt, int>&, int>);72 73template <bool moveNoexcept, bool convertNoexcept>74struct MaybeNoexcept {75  explicit MaybeNoexcept(int) noexcept(convertNoexcept);76  MaybeNoexcept(MaybeNoexcept&&) noexcept(moveNoexcept);77  MaybeNoexcept& operator=(MaybeNoexcept&&) = default;78  MaybeNoexcept& operator=(int);79};80 81// !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&82// is_nothrow_move_constructible_v<E>83static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, int>&, int>);84 85// is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&86// !is_nothrow_move_constructible_v<E>87static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<false, true>, MaybeNoexcept<false, false>>&, int>);88 89// !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> &&90// !is_nothrow_move_constructible_v<E>91static_assert(std::is_assignable_v<std::expected<MaybeNoexcept<true, false>, MaybeNoexcept<false, false>>&, int>);92 93// !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&94// !is_nothrow_move_constructible_v<E>95static_assert(!std::is_assignable_v<std::expected<MaybeNoexcept<false, false>, MaybeNoexcept<false, false>>&, int>);96 97constexpr bool test() {98  // If has_value() is true, equivalent to: val = std::forward<U>(v);99  // Copy100  {101    Traced::state oldState{};102    Traced::state newState{};103    std::expected<Traced, int> e1(std::in_place, oldState, 5);104    Traced u(newState, 10);105    decltype(auto) x = (e1 = u);106    static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);107    assert(&x == &e1);108 109    assert(e1.has_value());110    assert(e1.value().data_ == 10);111    assert(oldState.copyAssignCalled);112  }113 114  // If has_value() is true, equivalent to: val = std::forward<U>(v);115  // Move116  {117    Traced::state oldState{};118    Traced::state newState{};119    std::expected<Traced, int> e1(std::in_place, oldState, 5);120    Traced u(newState, 10);121    decltype(auto) x = (e1 = std::move(u));122    static_assert(std::same_as<decltype(x), std::expected<Traced, int>&>);123    assert(&x == &e1);124 125    assert(e1.has_value());126    assert(e1.value().data_ == 10);127    assert(oldState.moveAssignCalled);128  }129 130  // Otherwise, equivalent to:131  //   reinit-expected(val, unex, std::forward<U>(v));132  // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&133  // !is_nothrow_move_constructible_v<E>134  // copy135  //136  //  In this case, it should call the branch137  //    destroy_at(addressof(oldval));138  //    construct_at(addressof(newval), std::forward<Args>(args)...);139  {140    BothMayThrow::state oldState{};141    std::expected<MoveThrowConvNoexcept, BothMayThrow> e1(std::unexpect, oldState, 5);142    const int i      = 10;143    decltype(auto) x = (e1 = i);144    static_assert(std::same_as<decltype(x), std::expected<MoveThrowConvNoexcept, BothMayThrow>&>);145    assert(&x == &e1);146 147    assert(e1.has_value());148    assert(e1.value().data_ == 10);149 150    assert(!oldState.copyCtorCalled);151    assert(!oldState.moveCtorCalled);152    assert(oldState.dtorCalled);153    assert(e1.value().copiedFromInt);154  }155 156  // Otherwise, equivalent to:157  //   reinit-expected(val, unex, std::forward<U>(v));158  // is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&159  // !is_nothrow_move_constructible_v<E>160  // move161  //162  //  In this case, it should call the branch163  //    destroy_at(addressof(oldval));164  //    construct_at(addressof(newval), std::forward<Args>(args)...);165  {166    BothMayThrow::state oldState{};167    std::expected<MoveThrowConvNoexcept, BothMayThrow> e1(std::unexpect, oldState, 5);168    decltype(auto) x = (e1 = 10);169    static_assert(std::same_as<decltype(x), std::expected<MoveThrowConvNoexcept, BothMayThrow>&>);170    assert(&x == &e1);171 172    assert(e1.has_value());173    assert(e1.value().data_ == 10);174 175    assert(!oldState.copyCtorCalled);176    assert(!oldState.moveCtorCalled);177    assert(oldState.dtorCalled);178    assert(e1.value().movedFromInt);179  }180 181  // Otherwise, equivalent to:182  //   reinit-expected(val, unex, std::forward<U>(v));183  // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> &&184  // !is_nothrow_move_constructible_v<E>185  // copy186  //187  //  In this case, it should call the branch188  //  T tmp(std::forward<Args>(args)...);189  //  destroy_at(addressof(oldval));190  //  construct_at(addressof(newval), std::move(tmp));191  {192    BothMayThrow::state oldState{};193    std::expected<MoveNoexceptConvThrow, BothMayThrow> e1(std::unexpect, oldState, 5);194    const int i      = 10;195    decltype(auto) x = (e1 = i);196    static_assert(std::same_as<decltype(x), std::expected<MoveNoexceptConvThrow, BothMayThrow>&>);197    assert(&x == &e1);198 199    assert(e1.has_value());200    assert(e1.value().data_ == 10);201 202    assert(!oldState.copyCtorCalled);203    assert(!oldState.moveCtorCalled);204    assert(oldState.dtorCalled);205    assert(!e1.value().copiedFromInt);206    assert(e1.value().movedFromTmp);207  }208 209  // Otherwise, equivalent to:210  //   reinit-expected(val, unex, std::forward<U>(v));211  // !is_nothrow_constructible_v<T, U> && is_nothrow_move_constructible_v<T> &&212  // !is_nothrow_move_constructible_v<E>213  // move214  //215  //  In this case, it should call the branch216  //  T tmp(std::forward<Args>(args)...);217  //  destroy_at(addressof(oldval));218  //  construct_at(addressof(newval), std::move(tmp));219  {220    BothMayThrow::state oldState{};221    std::expected<MoveNoexceptConvThrow, BothMayThrow> e1(std::unexpect, oldState, 5);222    decltype(auto) x = (e1 = 10);223    static_assert(std::same_as<decltype(x), std::expected<MoveNoexceptConvThrow, BothMayThrow>&>);224    assert(&x == &e1);225 226    assert(e1.has_value());227    assert(e1.value().data_ == 10);228 229    assert(!oldState.copyCtorCalled);230    assert(!oldState.moveCtorCalled);231    assert(oldState.dtorCalled);232    assert(!e1.value().copiedFromInt);233    assert(e1.value().movedFromTmp);234  }235 236  // Otherwise, equivalent to:237  //   reinit-expected(val, unex, std::forward<U>(v));238  // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&239  // is_nothrow_move_constructible_v<E>240  // copy241  //242  //  In this case, it should call the branch243  //  U tmp(std::move(oldval));244  //  destroy_at(addressof(oldval));245  //  try {246  //    construct_at(addressof(newval), std::forward<Args>(args)...);247  //  } catch (...) {248  //    construct_at(addressof(oldval), std::move(tmp));249  //    throw;250  //  }251  {252    TracedNoexcept::state oldState{};253    std::expected<BothMayThrow, TracedNoexcept> e1(std::unexpect, oldState, 5);254    const int i      = 10;255    decltype(auto) x = (e1 = i);256    static_assert(std::same_as<decltype(x), std::expected<BothMayThrow, TracedNoexcept>&>);257    assert(&x == &e1);258 259    assert(e1.has_value());260    assert(e1.value().data_ == 10);261 262    assert(!oldState.copyCtorCalled);263    assert(oldState.moveCtorCalled);264    assert(oldState.dtorCalled);265    assert(e1.value().copiedFromInt);266  }267 268  // Otherwise, equivalent to:269  //   reinit-expected(val, unex, std::forward<U>(v));270  // !is_nothrow_constructible_v<T, U> && !is_nothrow_move_constructible_v<T> &&271  // is_nothrow_move_constructible_v<E>272  // move273  //274  //  In this case, it should call the branch275  //  U tmp(std::move(oldval));276  //  destroy_at(addressof(oldval));277  //  try {278  //    construct_at(addressof(newval), std::forward<Args>(args)...);279  //  } catch (...) {280  //    construct_at(addressof(oldval), std::move(tmp));281  //    throw;282  //  }283  {284    TracedNoexcept::state oldState{};285    std::expected<BothMayThrow, TracedNoexcept> e1(std::unexpect, oldState, 5);286    decltype(auto) x = (e1 = 10);287    static_assert(std::same_as<decltype(x), std::expected<BothMayThrow, TracedNoexcept>&>);288    assert(&x == &e1);289 290    assert(e1.has_value());291    assert(e1.value().data_ == 10);292 293    assert(!oldState.copyCtorCalled);294    assert(oldState.moveCtorCalled);295    assert(oldState.dtorCalled);296    assert(e1.value().movedFromInt);297  }298 299  // Test default template argument.300  // Without it, the template parameter cannot be deduced from an initializer list301  {302    struct Bar {303      int i;304      int j;305      constexpr Bar(int ii, int jj) : i(ii), j(jj) {}306    };307 308    std::expected<Bar, int> e({5, 6});309    e = {7, 8};310    assert(e.value().i == 7);311    assert(e.value().j == 8);312  }313 314  // CheckForInvalidWrites315  {316    {317      CheckForInvalidWrites<true> e1(std::unexpect);318      e1 = 42;319      assert(e1.check());320    }321    {322      CheckForInvalidWrites<false> e1(std::unexpect);323      e1 = true;324      assert(e1.check());325    }326  }327 328  // Check move constructor selection329  {330    struct MoveOnlyMulti {331      bool used_move1 = false;332      bool used_move2 = false;333 334      constexpr MoveOnlyMulti()                                = default;335      constexpr MoveOnlyMulti(const MoveOnlyMulti&)            = delete;336      constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&) = delete;337      constexpr MoveOnlyMulti& operator=(MoveOnlyMulti&&) {338        used_move1 = true;339        return *this;340      }341      constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&&) {342        used_move2 = true;343        return *this;344      };345      constexpr MoveOnlyMulti(MoveOnlyMulti&&) : used_move1(true) {}346      constexpr MoveOnlyMulti(const MoveOnlyMulti&&) : used_move2(true) {}347    };348 349    {350      MoveOnlyMulti t{};351      std::expected<MoveOnlyMulti, int> e1(std::unexpect);352      static_assert(std::is_same_v<decltype(std::move(t)), MoveOnlyMulti&&>);353      e1 = {std::move(t)};354      assert(e1.value().used_move1);355    }356    {357      const MoveOnlyMulti t{};358      std::expected<MoveOnlyMulti, int> e1(std::unexpect);359      static_assert(std::is_same_v<decltype(std::move(t)), const MoveOnlyMulti&&>);360      // _Up = remove_cv_t<const MoveOnlyMulti&&> --> should use MoveOnlyMulti(MoveOnlyMulti&&)361      e1 = {std::move(t)};362      assert(e1.value().used_move1);363    }364  }365 366  return true;367}368 369void testException() {370#ifndef TEST_HAS_NO_EXCEPTIONS371  std::expected<ThrowOnConvert, int> e1(std::unexpect, 5);372  try {373    e1 = 10;374    assert(false);375  } catch (Except) {376    assert(!e1.has_value());377    assert(e1.error() == 5);378  }379 380#endif // TEST_HAS_NO_EXCEPTIONS381}382 383int main(int, char**) {384  test();385  static_assert(test());386  testException();387  return 0;388}389