//===----------------------------------------------------------------------===//
//
// 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

// Throwing bad_any_cast is supported starting in macosx10.13
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions

// <any>

// template <class ValueType>
// ValueType const* any_cast(any const *) noexcept;
//
// template <class ValueType>
// ValueType * any_cast(any *) noexcept;

#include <any>
#include <type_traits>
#include <cassert>

#include "test_macros.h"
#include "any_helpers.h"

// Test that the operators are properly noexcept.
void test_cast_is_noexcept() {
    std::any a;
    ASSERT_NOEXCEPT(std::any_cast<int>(&a));

    const std::any& ca = a;
    ASSERT_NOEXCEPT(std::any_cast<int>(&ca));
}

// Test that the return type of any_cast is correct.
void test_cast_return_type() {
    std::any a;
    ASSERT_SAME_TYPE(decltype(std::any_cast<int>(&a)),       int*);
    ASSERT_SAME_TYPE(decltype(std::any_cast<int const>(&a)), int const*);

    const std::any& ca = a;
    ASSERT_SAME_TYPE(decltype(std::any_cast<int>(&ca)),       int const*);
    ASSERT_SAME_TYPE(decltype(std::any_cast<int const>(&ca)), int const*);
}

// Test that any_cast handles null pointers.
void test_cast_nullptr() {
    std::any *a = nullptr;
    assert(nullptr == std::any_cast<int>(a));
    assert(nullptr == std::any_cast<int const>(a));

    const std::any *ca = nullptr;
    assert(nullptr == std::any_cast<int>(ca));
    assert(nullptr == std::any_cast<int const>(ca));
}

// Test casting an empty object.
void test_cast_empty() {
    {
        std::any a;
        assert(nullptr == std::any_cast<int>(&a));
        assert(nullptr == std::any_cast<int const>(&a));

        const std::any& ca = a;
        assert(nullptr == std::any_cast<int>(&ca));
        assert(nullptr == std::any_cast<int const>(&ca));
    }
    // Create as non-empty, then make empty and run test.
    {
        std::any a(42);
        a.reset();
        assert(nullptr == std::any_cast<int>(&a));
        assert(nullptr == std::any_cast<int const>(&a));

        const std::any& ca = a;
        assert(nullptr == std::any_cast<int>(&ca));
        assert(nullptr == std::any_cast<int const>(&ca));
    }
}

template <class Type>
void test_cast() {
    assert(Type::count == 0);
    Type::reset();
    {
        std::any a = Type(42);
        const std::any& ca = a;
        assert(Type::count == 1);
        assert(Type::copied == 0);
        assert(Type::moved == 1);

        // Try a cast to a bad type.
        // NOTE: Type cannot be an int.
        assert(std::any_cast<int>(&a) == nullptr);
        assert(std::any_cast<int const>(&a) == nullptr);
        assert(std::any_cast<int const volatile>(&a) == nullptr);

        // Try a cast to the right type, but as a pointer.
        assert(std::any_cast<Type*>(&a) == nullptr);
        assert(std::any_cast<Type const*>(&a) == nullptr);

        // Check getting a unqualified type from a non-const any.
        Type* v = std::any_cast<Type>(&a);
        assert(v != nullptr);
        assert(v->value == 42);

        // change the stored value and later check for the new value.
        v->value = 999;

        // Check getting a const qualified type from a non-const any.
        Type const* cv = std::any_cast<Type const>(&a);
        assert(cv != nullptr);
        assert(cv == v);
        assert(cv->value == 999);

        // Check getting a unqualified type from a const any.
        cv = std::any_cast<Type>(&ca);
        assert(cv != nullptr);
        assert(cv == v);
        assert(cv->value == 999);

        // Check getting a const-qualified type from a const any.
        cv = std::any_cast<Type const>(&ca);
        assert(cv != nullptr);
        assert(cv == v);
        assert(cv->value == 999);

        // Check that no more objects were created, copied or moved.
        assert(Type::count == 1);
        assert(Type::copied == 0);
        assert(Type::moved == 1);
    }
    assert(Type::count == 0);
}

void test_cast_non_copyable_type()
{
    // Even though 'any' never stores non-copyable types
    // we still need to support any_cast<NoCopy>(ptr)
    struct NoCopy { NoCopy(NoCopy const&) = delete; };
    std::any a(42);
    std::any const& ca = a;
    assert(std::any_cast<NoCopy>(&a) == nullptr);
    assert(std::any_cast<NoCopy>(&ca) == nullptr);
}

void test_cast_array() {
    int arr[3];
    std::any a(arr);
    RTTI_ASSERT(a.type() == typeid(int*)); // contained value is decayed
    // We can't get an array out
    int (*p)[3] = std::any_cast<int[3]>(&a);
    assert(p == nullptr);
}

void test_fn() {}

void test_cast_function_pointer() {
    using T = void(*)();
    std::any a(test_fn);
    // An any can never store a function type, but we should at least be able
    // to ask.
    assert(std::any_cast<void()>(&a) == nullptr);
    T fn_ptr = std::any_cast<T>(a);
    assert(fn_ptr == test_fn);
}

int main(int, char**) {
    test_cast_is_noexcept();
    test_cast_return_type();
    test_cast_nullptr();
    test_cast_empty();
    test_cast<small>();
    test_cast<large>();
    test_cast_non_copyable_type();
    test_cast_array();
    test_cast_function_pointer();

  return 0;
}
