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

// <functional>

// class function<R(ArgTypes...)>

// function(Fp);

// Ensure that __not_null works for all function types.
// See https://bugs.llvm.org/show_bug.cgi?id=23589

//------------------------------------------------------------------------------
// TESTING std::function<...>::__not_null(Callable)
//
// Concerns:
//  1) The call __not_null(Callable) is well formed and correct for each
//     possible 'Callable' type category. These categories include:
//      1a) function pointers
//      1b) member function pointer
//      1c) member data pointer
//      1d) callable class type
//      1e) lambdas
//    Categories 1a, 1b, and 1c are 'Nullable' types. Only objects of these
//    types can be null. The other categories are not tested here.
//  3) '__not_null(Callable)' is well formed when the call signature includes
//      varargs.
//  4) '__not_null(Callable)' works for Callable types with all arities less
//     than or equal to 3 in C++03.
//  5) '__not_null(Callable)' works when 'Callable' is a member function
//     pointer to a cv or ref qualified function type.
//
// Plan:
//  1 For categories 1a, 1b and 1c define a set of
//    'Callable' objects for this category. This set should include examples
//    of arity 0, 1, 2 and possible 3 including versions with varargs as the
//    last parameter.
//
//  2 For each 'Callable' object in categories 1a, 1b and 1c do the following.
//
//    1 Define a type 'std::function<Sig>' as 'F' where 'Sig' is compatible with
//      the signature of the 'Callable' object.
//
//    2 Create an object of type 'F' using a null pointer of type 'Callable'.
//      Check that 'F.target<Callable>()' is null.
//
//    3 Create an object of type 'F' that is not null. Check that
//      'F.target<Callable>()' is not null and is equal to the original
//      argument.

#include <functional>
#include <type_traits>
#include <cassert>

#include "test_macros.h"

///////////////////////////////////////////////////////////////////////////////
int foo() { return 42; }
int foo(int) { return 42; }
int foo(int, int) { return 42; }
int foo(int, int, int) { return 42; }

int foo(...) { return 42; }
int foo(int, ...) { return 42; }
int foo(int, int, ...) { return 42; }
int foo(int, int, int, ...) { return 42; }

///////////////////////////////////////////////////////////////////////////////
struct MemFun03 {
    int foo() { return 42; }
    int foo() const { return 42; }
    int foo() volatile { return 42; }
    int foo() const volatile { return 42; }

    int foo(int) { return 42; }
    int foo(int) const { return 42; }
    int foo(int) volatile { return 42; }
    int foo(int) const volatile { return 42; }

    int foo(int, int) { return 42; }
    int foo(int, int) const { return 42; }
    int foo(int, int) volatile { return 42; }
    int foo(int, int) const volatile { return 42; }

    int foo(int, int, int) { return 42; }
    int foo(int, int, int) const { return 42; }
    int foo(int, int, int) volatile { return 42; }
    int foo(int, int, int) const volatile { return 42; }

    int foo(...) { return 42; }
    int foo(...) const { return 42; }
    int foo(...) volatile { return 42; }
    int foo(...) const volatile { return 42; }

    int foo(int, ...) { return 42; }
    int foo(int, ...) const { return 42; }
    int foo(int, ...) volatile { return 42; }
    int foo(int, ...) const volatile { return 42; }

    int foo(int, int, ...) { return 42; }
    int foo(int, int, ...) const { return 42; }
    int foo(int, int, ...) volatile { return 42; }
    int foo(int, int, ...) const volatile { return 42; }

    int foo(int, int, int, ...) { return 42; }
    int foo(int, int, int, ...) const { return 42; }
    int foo(int, int, int, ...) volatile { return 42; }
    int foo(int, int, int, ...) const volatile { return 42; }
};

#if TEST_STD_VER >= 11
struct MemFun11 {
    int foo() & { return 42; }
    int foo() const & { return 42; }
    int foo() volatile & { return 42; }
    int foo() const volatile & { return 42; }

    int foo(...) & { return 42; }
    int foo(...) const & { return 42; }
    int foo(...) volatile & { return 42; }
    int foo(...) const volatile & { return 42; }

    int foo() && { return 42; }
    int foo() const && { return 42; }
    int foo() volatile && { return 42; }
    int foo() const volatile && { return 42; }

    int foo(...) && { return 42; }
    int foo(...) const && { return 42; }
    int foo(...) volatile && { return 42; }
    int foo(...) const volatile && { return 42; }
};
#endif // TEST_STD_VER >= 11

struct MemData {
    int foo;
};

// Create a non-null free function by taking the address of
// &static_cast<Tp&>(foo);
template <class Tp>
struct Creator {
    static Tp create() {
        return &foo;
    }
};

// Create a non-null member pointer.
template <class Ret, class Class>
struct Creator<Ret Class::*> {
    typedef Ret Class::*ReturnType;
    static ReturnType create() {
        return &Class::foo;
    }
};

template <class TestFn, class Fn>
void test_imp() {
    { // Check that the null value is detected
        TestFn tf = nullptr;
        std::function<Fn> f = tf;
        assert(f.template target<TestFn>() == nullptr);
    }
    { // Check that the non-null value is detected.
        TestFn tf = Creator<TestFn>::create();
        assert(tf != nullptr);
        std::function<Fn> f = tf;
        assert(f.template target<TestFn>() != nullptr);
        assert(*f.template target<TestFn>() == tf);
    }
}

void test_func() {
    test_imp<int(*)(), int()>();
    test_imp<int(*)(...), int()>();
    test_imp<int(*)(int), int(int)>();
    test_imp<int(*)(int, ...), int(int)>();
    test_imp<int(*)(int, int), int(int, int)>();
    test_imp<int(*)(int, int, ...), int(int, int)>();
    test_imp<int(*)(int, int, int), int(int, int, int)>();
    test_imp<int(*)(int, int, int, ...), int(int, int, int)>();
}

void test_mf() {
    test_imp<int(MemFun03::*)(), int(MemFun03&)>();
    test_imp<int(MemFun03::*)(...), int(MemFun03&)>();
    test_imp<int(MemFun03::*)() const, int(MemFun03&)>();
    test_imp<int(MemFun03::*)(...) const, int(MemFun03&)>();
    test_imp<int(MemFun03::*)() volatile, int(MemFun03&)>();
    test_imp<int(MemFun03::*)(...) volatile, int(MemFun03&)>();
    test_imp<int(MemFun03::*)() const volatile, int(MemFun03&)>();
    test_imp<int(MemFun03::*)(...) const volatile, int(MemFun03&)>();

    test_imp<int(MemFun03::*)(int), int(MemFun03&, int)>();
    test_imp<int(MemFun03::*)(int, ...), int(MemFun03&, int)>();
    test_imp<int(MemFun03::*)(int) const, int(MemFun03&, int)>();
    test_imp<int(MemFun03::*)(int, ...) const, int(MemFun03&, int)>();
    test_imp<int(MemFun03::*)(int) volatile, int(MemFun03&, int)>();
    test_imp<int(MemFun03::*)(int, ...) volatile, int(MemFun03&, int)>();
    test_imp<int(MemFun03::*)(int) const volatile, int(MemFun03&, int)>();
    test_imp<int(MemFun03::*)(int, ...) const volatile, int(MemFun03&, int)>();

    test_imp<int(MemFun03::*)(int, int), int(MemFun03&, int, int)>();
    test_imp<int(MemFun03::*)(int, int, ...), int(MemFun03&, int, int)>();
    test_imp<int(MemFun03::*)(int, int) const, int(MemFun03&, int, int)>();
    test_imp<int(MemFun03::*)(int, int, ...) const, int(MemFun03&, int, int)>();
    test_imp<int(MemFun03::*)(int, int) volatile, int(MemFun03&, int, int)>();
    test_imp<int(MemFun03::*)(int, int, ...) volatile, int(MemFun03&, int, int)>();
    test_imp<int(MemFun03::*)(int, int) const volatile, int(MemFun03&, int, int)>();
    test_imp<int(MemFun03::*)(int, int, ...) const volatile, int(MemFun03&, int, int)>();

#if TEST_STD_VER >= 11
    test_imp<int(MemFun11::*)() &, int(MemFun11&)>();
    test_imp<int(MemFun11::*)(...) &, int(MemFun11&)>();
    test_imp<int(MemFun11::*)() const &, int(MemFun11&)>();
    test_imp<int(MemFun11::*)(...) const &, int(MemFun11&)>();
    test_imp<int(MemFun11::*)() volatile &, int(MemFun11&)>();
    test_imp<int(MemFun11::*)(...) volatile &, int(MemFun11&)>();
    test_imp<int(MemFun11::*)() const volatile &, int(MemFun11&)>();
    test_imp<int(MemFun11::*)(...) const volatile &, int(MemFun11&)>();

    test_imp<int(MemFun11::*)() &&, int(MemFun11&&)>();
    test_imp<int(MemFun11::*)(...) &&, int(MemFun11&&)>();
    test_imp<int(MemFun11::*)() const &&, int(MemFun11&&)>();
    test_imp<int(MemFun11::*)(...) const &&, int(MemFun11&&)>();
    test_imp<int(MemFun11::*)() volatile &&, int(MemFun11&&)>();
    test_imp<int(MemFun11::*)(...) volatile &&, int(MemFun11&&)>();
    test_imp<int(MemFun11::*)() const volatile &&, int(MemFun11&&)>();
    test_imp<int(MemFun11::*)(...) const volatile &&, int(MemFun11&&)>();
#endif
}

void test_md() {
    test_imp<int MemData::*, int(MemData&)>();
}

int main(int, char**) {
    test_func();
    test_mf();
    test_md();

  return 0;
}
