286 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// 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 U> optional<T>& operator=(U&& v);
 | 
						|
 | 
						|
#include <optional>
 | 
						|
#include <type_traits>
 | 
						|
#include <cassert>
 | 
						|
#include <memory>
 | 
						|
 | 
						|
#include "test_macros.h"
 | 
						|
#include "archetypes.h"
 | 
						|
 | 
						|
using std::optional;
 | 
						|
 | 
						|
struct ThrowAssign {
 | 
						|
  static int dtor_called;
 | 
						|
  ThrowAssign() = default;
 | 
						|
  ThrowAssign(int) { TEST_THROW(42); }
 | 
						|
  ThrowAssign& operator=(int) {
 | 
						|
      TEST_THROW(42);
 | 
						|
  }
 | 
						|
  ~ThrowAssign() { ++dtor_called; }
 | 
						|
};
 | 
						|
int ThrowAssign::dtor_called = 0;
 | 
						|
 | 
						|
template <class T, class Arg = T, bool Expect = true>
 | 
						|
void assert_assignable() {
 | 
						|
    static_assert(std::is_assignable<optional<T>&, Arg>::value == Expect, "");
 | 
						|
    static_assert(!std::is_assignable<const optional<T>&, Arg>::value, "");
 | 
						|
}
 | 
						|
 | 
						|
struct MismatchType {
 | 
						|
  explicit MismatchType(int) {}
 | 
						|
  explicit MismatchType(char*) {}
 | 
						|
  explicit MismatchType(int*) = delete;
 | 
						|
  MismatchType& operator=(int) { return *this; }
 | 
						|
  MismatchType& operator=(int*) { return *this; }
 | 
						|
  MismatchType& operator=(char*) = delete;
 | 
						|
};
 | 
						|
 | 
						|
struct FromOptionalType {
 | 
						|
  using Opt = std::optional<FromOptionalType>;
 | 
						|
  FromOptionalType() = default;
 | 
						|
  FromOptionalType(FromOptionalType const&) = delete;
 | 
						|
  template <class Dummy = void>
 | 
						|
  constexpr FromOptionalType(Opt&) { Dummy::BARK; }
 | 
						|
  template <class Dummy = void>
 | 
						|
  constexpr FromOptionalType& operator=(Opt&) { Dummy::BARK; return *this; }
 | 
						|
};
 | 
						|
 | 
						|
void test_sfinae() {
 | 
						|
    using I = TestTypes::TestType;
 | 
						|
    using E = ExplicitTestTypes::TestType;
 | 
						|
    assert_assignable<int>();
 | 
						|
    assert_assignable<int, int&>();
 | 
						|
    assert_assignable<int, int const&>();
 | 
						|
    // Implicit test type
 | 
						|
    assert_assignable<I, I const&>();
 | 
						|
    assert_assignable<I, I&&>();
 | 
						|
    assert_assignable<I, int>();
 | 
						|
    assert_assignable<I, void*, false>();
 | 
						|
    // Explicit test type
 | 
						|
    assert_assignable<E, E const&>();
 | 
						|
    assert_assignable<E, E &&>();
 | 
						|
    assert_assignable<E, int>();
 | 
						|
    assert_assignable<E, void*, false>();
 | 
						|
    // Mismatch type
 | 
						|
    assert_assignable<MismatchType, int>();
 | 
						|
    assert_assignable<MismatchType, int*, false>();
 | 
						|
    assert_assignable<MismatchType, char*, false>();
 | 
						|
    // Type constructible from optional
 | 
						|
    assert_assignable<FromOptionalType, std::optional<FromOptionalType>&, false>();
 | 
						|
}
 | 
						|
 | 
						|
void test_with_test_type()
 | 
						|
{
 | 
						|
    using T = TestTypes::TestType;
 | 
						|
    T::reset();
 | 
						|
    { // to empty
 | 
						|
        optional<T> opt;
 | 
						|
        opt = 3;
 | 
						|
        assert(T::alive == 1);
 | 
						|
        assert(T::constructed == 1);
 | 
						|
        assert(T::value_constructed == 1);
 | 
						|
        assert(T::assigned == 0);
 | 
						|
        assert(T::destroyed == 0);
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(3));
 | 
						|
    }
 | 
						|
    { // to existing
 | 
						|
        optional<T> opt(42);
 | 
						|
        T::reset_constructors();
 | 
						|
        opt = 3;
 | 
						|
        assert(T::alive == 1);
 | 
						|
        assert(T::constructed == 0);
 | 
						|
        assert(T::assigned == 1);
 | 
						|
        assert(T::value_assigned == 1);
 | 
						|
        assert(T::destroyed == 0);
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(3));
 | 
						|
    }
 | 
						|
    { // test default argument
 | 
						|
        optional<T> opt;
 | 
						|
        T::reset_constructors();
 | 
						|
        opt = {1, 2};
 | 
						|
        assert(T::alive == 1);
 | 
						|
        assert(T::constructed == 2);
 | 
						|
        assert(T::value_constructed == 1);
 | 
						|
        assert(T::move_constructed == 1);
 | 
						|
        assert(T::assigned == 0);
 | 
						|
        assert(T::destroyed == 1);
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(1, 2));
 | 
						|
    }
 | 
						|
    { // test default argument
 | 
						|
        optional<T> opt(42);
 | 
						|
        T::reset_constructors();
 | 
						|
        opt = {1, 2};
 | 
						|
        assert(T::alive == 1);
 | 
						|
        assert(T::constructed == 1);
 | 
						|
        assert(T::value_constructed == 1);
 | 
						|
        assert(T::assigned == 1);
 | 
						|
        assert(T::move_assigned == 1);
 | 
						|
        assert(T::destroyed == 1);
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(1, 2));
 | 
						|
    }
 | 
						|
    { // test default argument
 | 
						|
        optional<T> opt;
 | 
						|
        T::reset_constructors();
 | 
						|
        opt = {1};
 | 
						|
        assert(T::alive == 1);
 | 
						|
        assert(T::constructed == 2);
 | 
						|
        assert(T::value_constructed == 1);
 | 
						|
        assert(T::move_constructed == 1);
 | 
						|
        assert(T::assigned == 0);
 | 
						|
        assert(T::destroyed == 1);
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(1));
 | 
						|
    }
 | 
						|
    { // test default argument
 | 
						|
        optional<T> opt(42);
 | 
						|
        T::reset_constructors();
 | 
						|
        opt = {};
 | 
						|
        assert(static_cast<bool>(opt) == false);
 | 
						|
        assert(T::alive == 0);
 | 
						|
        assert(T::constructed == 0);
 | 
						|
        assert(T::assigned == 0);
 | 
						|
        assert(T::destroyed == 1);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
template <class T, class Value = int>
 | 
						|
void test_with_type() {
 | 
						|
    { // to empty
 | 
						|
        optional<T> opt;
 | 
						|
        opt = Value(3);
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(3));
 | 
						|
    }
 | 
						|
    { // to existing
 | 
						|
        optional<T> opt(Value(42));
 | 
						|
        opt = Value(3);
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(3));
 | 
						|
    }
 | 
						|
    { // test const
 | 
						|
        optional<T> opt(Value(42));
 | 
						|
        const T t(Value(3));
 | 
						|
        opt = t;
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(3));
 | 
						|
    }
 | 
						|
    { // test default argument
 | 
						|
        optional<T> opt;
 | 
						|
        opt = {Value(1)};
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(1));
 | 
						|
    }
 | 
						|
    { // test default argument
 | 
						|
        optional<T> opt(Value(42));
 | 
						|
        opt = {};
 | 
						|
        assert(static_cast<bool>(opt) == false);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
template <class T>
 | 
						|
void test_with_type_multi() {
 | 
						|
    test_with_type<T>();
 | 
						|
    { // test default argument
 | 
						|
        optional<T> opt;
 | 
						|
        opt = {1, 2};
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(1, 2));
 | 
						|
    }
 | 
						|
    { // test default argument
 | 
						|
        optional<T> opt(42);
 | 
						|
        opt = {1, 2};
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(*opt == T(1, 2));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void test_throws()
 | 
						|
{
 | 
						|
#ifndef TEST_HAS_NO_EXCEPTIONS
 | 
						|
    using T = ThrowAssign;
 | 
						|
    {
 | 
						|
        optional<T> opt;
 | 
						|
        try {
 | 
						|
            opt = 42;
 | 
						|
            assert(false);
 | 
						|
        } catch (int) {}
 | 
						|
        assert(static_cast<bool>(opt) == false);
 | 
						|
    }
 | 
						|
    assert(T::dtor_called == 0);
 | 
						|
    {
 | 
						|
        T::dtor_called = 0;
 | 
						|
        optional<T> opt(std::in_place);
 | 
						|
        try {
 | 
						|
            opt = 42;
 | 
						|
            assert(false);
 | 
						|
        } catch (int) {}
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(T::dtor_called == 0);
 | 
						|
    }
 | 
						|
    assert(T::dtor_called == 1);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
enum MyEnum { Zero, One, Two, Three, FortyTwo = 42 };
 | 
						|
 | 
						|
using Fn = void(*)();
 | 
						|
 | 
						|
// https://llvm.org/PR38638
 | 
						|
template <class T>
 | 
						|
constexpr T pr38638(T v)
 | 
						|
{
 | 
						|
  std::optional<T> o;
 | 
						|
  o = v;
 | 
						|
  return *o + 2;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int main(int, char**)
 | 
						|
{
 | 
						|
    test_sfinae();
 | 
						|
    // Test with instrumented type
 | 
						|
    test_with_test_type();
 | 
						|
    // Test with various scalar types
 | 
						|
    test_with_type<int>();
 | 
						|
    test_with_type<MyEnum, MyEnum>();
 | 
						|
    test_with_type<int, MyEnum>();
 | 
						|
    test_with_type<Fn, Fn>();
 | 
						|
    // Test types with multi argument constructors
 | 
						|
    test_with_type_multi<ConstexprTestTypes::TestType>();
 | 
						|
    test_with_type_multi<TrivialTestTypes::TestType>();
 | 
						|
    // Test move only types
 | 
						|
    {
 | 
						|
        optional<std::unique_ptr<int>> opt;
 | 
						|
        opt = std::unique_ptr<int>(new int(3));
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(**opt == 3);
 | 
						|
    }
 | 
						|
    {
 | 
						|
        optional<std::unique_ptr<int>> opt(std::unique_ptr<int>(new int(2)));
 | 
						|
        opt = std::unique_ptr<int>(new int(3));
 | 
						|
        assert(static_cast<bool>(opt) == true);
 | 
						|
        assert(**opt == 3);
 | 
						|
    }
 | 
						|
    test_throws();
 | 
						|
 | 
						|
    static_assert(pr38638(3) == 5, "");
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 |