blob: 7cf7a80f7d7031b615dbed327a9e826c75b5a58f [file] [edit]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14
// <optional>
// template <class T> void swap(optional<T>& x, optional<T>& y)
// noexcept(noexcept(x.swap(y)));
#include <cassert>
#include <memory>
#include <optional>
#include <type_traits>
#include "test_macros.h"
#include "archetypes.h"
using std::optional;
class X
{
int i_;
public:
static unsigned dtor_called;
X(int i) : i_(i) {}
X(X&& x) = default;
X& operator=(X&&) = default;
~X() {++dtor_called;}
friend bool operator==(const X& x, const X& y) {return x.i_ == y.i_;}
};
unsigned X::dtor_called = 0;
class Y
{
int i_;
public:
static unsigned dtor_called;
Y(int i) : i_(i) {}
Y(Y&&) = default;
~Y() {++dtor_called;}
friend constexpr bool operator==(const Y& x, const Y& y) {return x.i_ == y.i_;}
friend void swap(Y& x, Y& y) {std::swap(x.i_, y.i_);}
};
unsigned Y::dtor_called = 0;
class Z
{
int i_;
public:
Z(int i) : i_(i) {}
Z(Z&&) { TEST_THROW(7);}
friend constexpr bool operator==(const Z& x, const Z& y) {return x.i_ == y.i_;}
friend void swap(Z&, Z&) { TEST_THROW(6);}
};
struct NonSwappable {
NonSwappable(NonSwappable const&) = delete;
};
void swap(NonSwappable&, NonSwappable&) = delete;
void test_swap_sfinae() {
using std::optional;
{
using T = TestTypes::TestType;
static_assert(std::is_swappable_v<optional<T>>, "");
}
{
using T = TestTypes::MoveOnly;
static_assert(std::is_swappable_v<optional<T>>, "");
}
{
using T = TestTypes::Copyable;
static_assert(std::is_swappable_v<optional<T>>, "");
}
{
using T = TestTypes::NoCtors;
static_assert(!std::is_swappable_v<optional<T>>, "");
}
{
using T = NonSwappable;
static_assert(!std::is_swappable_v<optional<T>>, "");
}
{
// Even though CopyOnly has deleted move operations, those operations
// cause optional<CopyOnly> to have implicitly deleted move operations
// that decay into copies.
using T = TestTypes::CopyOnly;
using Opt = optional<T>;
T::reset();
Opt L(101), R(42);
T::reset_constructors();
std::swap(L, R);
assert(L->value == 42);
assert(R->value == 101);
assert(T::copy_constructed == 1);
assert(T::constructed == T::copy_constructed);
assert(T::assigned == 2);
assert(T::assigned == T::copy_assigned);
}
}
#if TEST_STD_VER >= 26
template <typename T>
constexpr bool test_swap_ref() {
{
optional<T&> opt1;
optional<T&> opt2;
static_assert(noexcept(swap(opt1, opt2)) == true);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
}
{
T one{1};
optional<T&> opt1(one);
optional<T&> opt2;
static_assert(noexcept(swap(opt1, opt2)) == true);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(std::addressof(*opt1) == std::addressof(one));
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
assert(std::addressof(*opt2) == std::addressof(one));
}
{
T two{2};
optional<T&> opt1;
optional<T&> opt2(two);
static_assert(noexcept(swap(opt1, opt2)) == true);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(std::addressof(*opt2) == std::addressof(two));
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(std::addressof(*opt1) == std::addressof(two));
assert(static_cast<bool>(opt2) == false);
}
{
T one{1};
T two{2};
optional<T&> opt1(one);
optional<T&> opt2(two);
static_assert(noexcept(swap(opt1, opt2)) == true);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(std::addressof(*opt1) == std::addressof(one));
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
assert(std::addressof(*opt2) == std::addressof(two));
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(std::addressof(*opt1) == std::addressof(two));
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
assert(std::addressof(*opt2) == std::addressof(one));
}
return true;
}
struct ADLSwap {
static inline bool called_adl_swap{false};
friend void swap(ADLSwap&, ADLSwap&) noexcept { called_adl_swap = true; }
friend void swap(ADLSwap*&, ADLSwap*&) noexcept { called_adl_swap = true; }
};
struct DelADLSwap {
static inline bool called_adl_swap{false};
friend void swap(DelADLSwap&, DelADLSwap&) noexcept = delete;
friend void swap(DelADLSwap*&, DelADLSwap*&) noexcept = delete;
};
// LWG4439: Verify that an ADL-found swap is not selected when calling optional<T&>::swap()
void test_ref_swap_adl() {
ADLSwap t1{};
ADLSwap t2{};
std::optional<ADLSwap&> o{t1};
std::optional<ADLSwap&> o2{t2};
o.swap(o2);
assert(!ADLSwap::called_adl_swap);
DelADLSwap td1{};
DelADLSwap td2{};
std::optional<DelADLSwap&> od1{td1};
std::optional<DelADLSwap&> od2{td2};
// Ensure that no ADL ever occurs, the following line will be invalid if it does
od1.swap(od2);
}
#endif
int main(int, char**)
{
test_swap_sfinae();
#if TEST_STD_VER >= 26
static_assert(test_swap_ref<int>());
static_assert(test_swap_ref<double>());
test_swap_ref<int>();
test_swap_ref<double>();
test_ref_swap_adl();
#endif
{
optional<int> opt1;
optional<int> opt2;
static_assert(noexcept(swap(opt1, opt2)) == true, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
}
{
optional<int> opt1(1);
optional<int> opt2;
static_assert(noexcept(swap(opt1, opt2)) == true, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
{
optional<int> opt1;
optional<int> opt2(2);
static_assert(noexcept(swap(opt1, opt2)) == true, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == false);
}
{
optional<int> opt1(1);
optional<int> opt2(2);
static_assert(noexcept(swap(opt1, opt2)) == true, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
{
optional<X> opt1;
optional<X> opt2;
static_assert(noexcept(swap(opt1, opt2)) == true, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
assert(X::dtor_called == 0);
}
{
optional<X> opt1(1);
optional<X> opt2;
static_assert(noexcept(swap(opt1, opt2)) == true, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == false);
X::dtor_called = 0;
swap(opt1, opt2);
assert(X::dtor_called == 1);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
{
optional<X> opt1;
optional<X> opt2(2);
static_assert(noexcept(swap(opt1, opt2)) == true, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
X::dtor_called = 0;
swap(opt1, opt2);
assert(X::dtor_called == 1);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == false);
}
{
optional<X> opt1(1);
optional<X> opt2(2);
static_assert(noexcept(swap(opt1, opt2)) == true, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
X::dtor_called = 0;
swap(opt1, opt2);
assert(X::dtor_called == 1); // from inside std::swap
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
{
optional<Y> opt1;
optional<Y> opt2;
static_assert(noexcept(swap(opt1, opt2)) == false, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
assert(Y::dtor_called == 0);
}
{
optional<Y> opt1(1);
optional<Y> opt2;
static_assert(noexcept(swap(opt1, opt2)) == false, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == false);
Y::dtor_called = 0;
swap(opt1, opt2);
assert(Y::dtor_called == 1);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
{
optional<Y> opt1;
optional<Y> opt2(2);
static_assert(noexcept(swap(opt1, opt2)) == false, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
Y::dtor_called = 0;
swap(opt1, opt2);
assert(Y::dtor_called == 1);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == false);
}
{
optional<Y> opt1(1);
optional<Y> opt2(2);
static_assert(noexcept(swap(opt1, opt2)) == false, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
Y::dtor_called = 0;
swap(opt1, opt2);
assert(Y::dtor_called == 0);
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 2);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 1);
}
{
optional<Z> opt1;
optional<Z> opt2;
static_assert(noexcept(swap(opt1, opt2)) == false, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
swap(opt1, opt2);
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == false);
}
#ifndef TEST_HAS_NO_EXCEPTIONS
{
optional<Z> opt1;
opt1.emplace(1);
optional<Z> opt2;
static_assert(noexcept(swap(opt1, opt2)) == false, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == false);
try
{
swap(opt1, opt2);
assert(false);
}
catch (int i)
{
assert(i == 7);
}
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == false);
}
{
optional<Z> opt1;
optional<Z> opt2;
opt2.emplace(2);
static_assert(noexcept(swap(opt1, opt2)) == false, "");
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
try
{
swap(opt1, opt2);
assert(false);
}
catch (int i)
{
assert(i == 7);
}
assert(static_cast<bool>(opt1) == false);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
}
{
optional<Z> opt1;
opt1.emplace(1);
optional<Z> opt2;
opt2.emplace(2);
static_assert(noexcept(swap(opt1, opt2)) == false, "");
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
try
{
swap(opt1, opt2);
assert(false);
}
catch (int i)
{
assert(i == 6);
}
assert(static_cast<bool>(opt1) == true);
assert(*opt1 == 1);
assert(static_cast<bool>(opt2) == true);
assert(*opt2 == 2);
}
#endif // TEST_HAS_NO_EXCEPTIONS
return 0;
}