173 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(expected&& rhs) noexcept(see below);12//13// Constraints:14// - is_move_constructible_v<T> is true and15// - is_move_constructible_v<E> is true.16//17// Effects: If rhs.has_value() is true, direct-non-list-initializes val with std::move(*rhs).18// Otherwise, direct-non-list-initializes unex with std::move(rhs.error()).19//20// Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true.21//22// Throws: Any exception thrown by the initialization of val or unex.23//24// Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>.25//26// This constructor is trivial if27// - is_trivially_move_constructible_v<T> is true and28// - is_trivially_move_constructible_v<E> is true.29 30#include <cassert>31#include <expected>32#include <type_traits>33#include <utility>34 35#include "test_macros.h"36#include "../../types.h"37 38struct NonMovable {39 NonMovable(NonMovable&&) = delete;40};41 42struct MovableNonTrivial {43 int i;44 constexpr MovableNonTrivial(int ii) : i(ii) {}45 constexpr MovableNonTrivial(MovableNonTrivial&& o) : i(o.i) { o.i = 0; }46 friend constexpr bool operator==(const MovableNonTrivial&, const MovableNonTrivial&) = default;47};48 49struct MoveMayThrow {50 MoveMayThrow(MoveMayThrow&&) {}51};52 53// Test Constraints:54// - is_move_constructible_v<T> is true and55// - is_move_constructible_v<E> is true.56static_assert(std::is_move_constructible_v<std::expected<int, int>>);57static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, int>>);58static_assert(std::is_move_constructible_v<std::expected<int, MovableNonTrivial>>);59static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>);60static_assert(!std::is_move_constructible_v<std::expected<NonMovable, int>>);61static_assert(!std::is_move_constructible_v<std::expected<int, NonMovable>>);62static_assert(!std::is_move_constructible_v<std::expected<NonMovable, NonMovable>>);63 64// Test: This constructor is trivial if65// - is_trivially_move_constructible_v<T> is true and66// - is_trivially_move_constructible_v<E> is true.67static_assert(std::is_trivially_move_constructible_v<std::expected<int, int>>);68static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, int>>);69static_assert(!std::is_trivially_move_constructible_v<std::expected<int, MovableNonTrivial>>);70static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>);71 72// Test: The exception specification is equivalent to73// is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>.74static_assert(std::is_nothrow_move_constructible_v<std::expected<int, int>>);75static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, int>>);76static_assert(!std::is_nothrow_move_constructible_v<std::expected<int, MoveMayThrow>>);77static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, MoveMayThrow>>);78 79constexpr bool test() {80 // move the value non-trivial81 {82 std::expected<MovableNonTrivial, int> e1(5);83 auto e2 = std::move(e1);84 assert(e2.has_value());85 assert(e2.value().i == 5);86 assert(e1.has_value());87 assert(e1.value().i == 0);88 }89 90 // move the error non-trivial91 {92 std::expected<int, MovableNonTrivial> e1(std::unexpect, 5);93 auto e2 = std::move(e1);94 assert(!e2.has_value());95 assert(e2.error().i == 5);96 assert(!e1.has_value());97 assert(e1.error().i == 0);98 }99 100 // move the value trivial101 {102 std::expected<int, int> e1(5);103 auto e2 = std::move(e1);104 assert(e2.has_value());105 assert(e2.value() == 5);106 assert(e1.has_value());107 }108 109 // move the error trivial110 {111 std::expected<int, int> e1(std::unexpect, 5);112 auto e2 = std::move(e1);113 assert(!e2.has_value());114 assert(e2.error() == 5);115 assert(!e1.has_value());116 }117 118 // move TailClobbererNonTrivialMove as value119 {120 std::expected<TailClobbererNonTrivialMove<0>, bool> e1;121 auto e2 = std::move(e1);122 assert(e2.has_value());123 assert(e1.has_value());124 }125 126 // move TailClobbererNonTrivialMove as error127 {128 std::expected<bool, TailClobbererNonTrivialMove<1>> e1(std::unexpect);129 auto e2 = std::move(e1);130 assert(!e2.has_value());131 assert(!e1.has_value());132 }133 134 return true;135}136 137void testException() {138#ifndef TEST_HAS_NO_EXCEPTIONS139 struct Throwing {140 Throwing() = default;141 Throwing(Throwing&&) { throw Except{}; }142 };143 144 // throw on moving value145 {146 std::expected<Throwing, int> e1;147 try {148 [[maybe_unused]] auto e2 = std::move(e1);149 assert(false);150 } catch (Except) {151 }152 }153 154 // throw on moving error155 {156 std::expected<int, Throwing> e1(std::unexpect);157 try {158 [[maybe_unused]] auto e2 = std::move(e1);159 assert(false);160 } catch (Except) {161 }162 }163 164#endif // TEST_HAS_NO_EXCEPTIONS165}166 167int main(int, char**) {168 test();169 static_assert(test());170 testException();171 return 0;172}173