Some time ago, I introduced shortcut features like dylib-has-no-shared_mutex to encode whether the deployment target supported shared_mutex (say). This made the test suite annotations cleaner. However, the problem with building Lit features on top of other Lit features is that it's easier for them to become stale, especially when they are generated programmatically. Furthermore, it makes the bar for defining configurations from scratch higher, since more features have to be defined. Instead, I think it's better to put the XFAILs in the tests directly, which allows cleaning them up with a simple grep.
292 lines
8.9 KiB
C++
292 lines
8.9 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
|
|
|
|
// Throwing bad_any_cast is supported starting in macosx10.13
|
|
// XFAIL: with_system_cxx_lib=macosx10.12 && !no-exceptions
|
|
// XFAIL: with_system_cxx_lib=macosx10.11 && !no-exceptions
|
|
// XFAIL: with_system_cxx_lib=macosx10.10 && !no-exceptions
|
|
// XFAIL: with_system_cxx_lib=macosx10.9 && !no-exceptions
|
|
|
|
// <any>
|
|
|
|
// template <class T, class ...Args> T& emplace(Args&&...);
|
|
// template <class T, class U, class ...Args>
|
|
// T& emplace(initializer_list<U>, Args&&...);
|
|
|
|
#include <any>
|
|
#include <cassert>
|
|
|
|
#include "any_helpers.h"
|
|
#include "count_new.h"
|
|
#include "test_macros.h"
|
|
|
|
using std::any;
|
|
using std::any_cast;
|
|
|
|
struct Tracked {
|
|
static int count;
|
|
Tracked() {++count;}
|
|
~Tracked() { --count; }
|
|
};
|
|
int Tracked::count = 0;
|
|
|
|
template <class Type>
|
|
void test_emplace_type() {
|
|
// constructing from a small type should perform no allocations.
|
|
DisableAllocationGuard g(isSmallType<Type>()); ((void)g);
|
|
assert(Type::count == 0);
|
|
Type::reset();
|
|
{
|
|
any a(std::in_place_type<Tracked>);
|
|
assert(Tracked::count == 1);
|
|
|
|
auto &v = a.emplace<Type>();
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(&v == std::any_cast<Type>(&a));
|
|
|
|
assert(Tracked::count == 0);
|
|
assert(Type::count == 1);
|
|
assert(Type::copied == 0);
|
|
assert(Type::moved == 0);
|
|
assertContains<Type>(a, 0);
|
|
}
|
|
assert(Type::count == 0);
|
|
Type::reset();
|
|
{
|
|
any a(std::in_place_type<Tracked>);
|
|
assert(Tracked::count == 1);
|
|
|
|
auto &v = a.emplace<Type>(101);
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(&v == std::any_cast<Type>(&a));
|
|
|
|
assert(Tracked::count == 0);
|
|
assert(Type::count == 1);
|
|
assert(Type::copied == 0);
|
|
assert(Type::moved == 0);
|
|
assertContains<Type>(a, 101);
|
|
}
|
|
assert(Type::count == 0);
|
|
Type::reset();
|
|
{
|
|
any a(std::in_place_type<Tracked>);
|
|
assert(Tracked::count == 1);
|
|
|
|
auto &v = a.emplace<Type>(-1, 42, -1);
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(&v == std::any_cast<Type>(&a));
|
|
|
|
assert(Tracked::count == 0);
|
|
assert(Type::count == 1);
|
|
assert(Type::copied == 0);
|
|
assert(Type::moved == 0);
|
|
assertContains<Type>(a, 42);
|
|
}
|
|
assert(Type::count == 0);
|
|
Type::reset();
|
|
}
|
|
|
|
template <class Type>
|
|
void test_emplace_type_tracked() {
|
|
// constructing from a small type should perform no allocations.
|
|
DisableAllocationGuard g(isSmallType<Type>()); ((void)g);
|
|
{
|
|
any a(std::in_place_type<Tracked>);
|
|
assert(Tracked::count == 1);
|
|
auto &v = a.emplace<Type>();
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(&v == std::any_cast<Type>(&a));
|
|
|
|
assert(Tracked::count == 0);
|
|
assertArgsMatch<Type>(a);
|
|
}
|
|
{
|
|
any a(std::in_place_type<Tracked>);
|
|
assert(Tracked::count == 1);
|
|
auto &v = a.emplace<Type>(-1, 42, -1);
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(&v == std::any_cast<Type>(&a));
|
|
|
|
assert(Tracked::count == 0);
|
|
assertArgsMatch<Type, int, int, int>(a);
|
|
}
|
|
// initializer_list constructor tests
|
|
{
|
|
any a(std::in_place_type<Tracked>);
|
|
assert(Tracked::count == 1);
|
|
auto &v = a.emplace<Type>({-1, 42, -1});
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(&v == std::any_cast<Type>(&a));
|
|
|
|
assert(Tracked::count == 0);
|
|
assertArgsMatch<Type, std::initializer_list<int>>(a);
|
|
}
|
|
{
|
|
int x = 42;
|
|
any a(std::in_place_type<Tracked>);
|
|
assert(Tracked::count == 1);
|
|
auto &v = a.emplace<Type>({-1, 42, -1}, x);
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(&v == std::any_cast<Type>(&a));
|
|
|
|
assert(Tracked::count == 0);
|
|
assertArgsMatch<Type, std::initializer_list<int>, int&>(a);
|
|
}
|
|
}
|
|
|
|
#ifndef TEST_HAS_NO_EXCEPTIONS
|
|
|
|
struct SmallThrows {
|
|
SmallThrows(int) { throw 42; }
|
|
SmallThrows(std::initializer_list<int>, int) { throw 42; }
|
|
};
|
|
static_assert(IsSmallObject<SmallThrows>::value, "");
|
|
|
|
struct LargeThrows {
|
|
LargeThrows(int) { throw 42; }
|
|
LargeThrows(std::initializer_list<int>, int) { throw 42; }
|
|
int data[sizeof(std::any)];
|
|
};
|
|
static_assert(!IsSmallObject<LargeThrows>::value, "");
|
|
|
|
template <class Type>
|
|
void test_emplace_throws()
|
|
{
|
|
// any stores small type
|
|
{
|
|
std::any a(small{42});
|
|
assert(small::count == 1);
|
|
try {
|
|
auto &v = a.emplace<Type>(101);
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(false);
|
|
} catch (int const&) {
|
|
}
|
|
assert(small::count == 0);
|
|
}
|
|
{
|
|
std::any a(small{42});
|
|
assert(small::count == 1);
|
|
try {
|
|
auto &v = a.emplace<Type>({1, 2, 3}, 101);
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(false);
|
|
} catch (int const&) {
|
|
}
|
|
assert(small::count == 0);
|
|
}
|
|
// any stores large type
|
|
{
|
|
std::any a(large{42});
|
|
assert(large::count == 1);
|
|
try {
|
|
auto &v = a.emplace<Type>(101);
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(false);
|
|
} catch (int const&) {
|
|
}
|
|
assert(large::count == 0);
|
|
}
|
|
{
|
|
std::any a(large{42});
|
|
assert(large::count == 1);
|
|
try {
|
|
auto &v = a.emplace<Type>({1, 2, 3}, 101);
|
|
static_assert( std::is_same_v<Type&, decltype(v)>, "" );
|
|
assert(false);
|
|
} catch (int const&) {
|
|
}
|
|
assert(large::count == 0);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
template <class T, class ...Args>
|
|
constexpr auto has_emplace(int)
|
|
-> decltype(std::any{}.emplace<T>(std::declval<Args>()...), true) { return true; }
|
|
|
|
template <class ...Args>
|
|
constexpr bool has_emplace(long) { return false; }
|
|
|
|
template <class ...Args>
|
|
constexpr bool has_emplace() { return has_emplace<Args...>(0); }
|
|
|
|
|
|
template <class T, class IT, class ...Args>
|
|
constexpr auto has_emplace_init_list(int)
|
|
-> decltype(std::any{}.emplace<T>(
|
|
{std::declval<IT>(), std::declval<IT>(), std::declval<IT>()},
|
|
std::declval<Args>()...), true) { return true; }
|
|
|
|
template <class ...Args>
|
|
constexpr bool has_emplace_init_list(long) { return false; }
|
|
|
|
template <class ...Args>
|
|
constexpr bool has_emplace_init_list() { return has_emplace_init_list<Args...>(0); }
|
|
|
|
|
|
void test_emplace_sfinae_constraints() {
|
|
{
|
|
static_assert(has_emplace<int>(), "");
|
|
static_assert(has_emplace<int, int>(), "");
|
|
static_assert(!has_emplace<int, int, int>(), "not constructible");
|
|
static_assert(!has_emplace_init_list<int, int>(), "not constructible from il");
|
|
}
|
|
{
|
|
static_assert(has_emplace<small>(), "");
|
|
static_assert(has_emplace<large>(), "");
|
|
static_assert(!has_emplace<small, void*>(), "");
|
|
static_assert(!has_emplace<large, void*>(), "");
|
|
|
|
static_assert(has_emplace_init_list<small, int>(), "");
|
|
static_assert(has_emplace_init_list<large, int>(), "");
|
|
static_assert(!has_emplace_init_list<small, void*>(), "");
|
|
static_assert(!has_emplace_init_list<large, void*>(), "");
|
|
}
|
|
{
|
|
// Test that the emplace SFINAE's away when the
|
|
// argument is non-copyable
|
|
struct NoCopy {
|
|
NoCopy() = default;
|
|
NoCopy(NoCopy const&) = delete;
|
|
NoCopy(int) {}
|
|
NoCopy(std::initializer_list<int>, int, int) {}
|
|
};
|
|
static_assert(!has_emplace<NoCopy>(), "");
|
|
static_assert(!has_emplace<NoCopy, int>(), "");
|
|
static_assert(!has_emplace_init_list<NoCopy, int, int, int>(), "");
|
|
static_assert(!has_emplace<NoCopy&>(), "");
|
|
static_assert(!has_emplace<NoCopy&, int>(), "");
|
|
static_assert(!has_emplace_init_list<NoCopy&, int, int, int>(), "");
|
|
static_assert(!has_emplace<NoCopy&&>(), "");
|
|
static_assert(!has_emplace<NoCopy&&, int>(), "");
|
|
static_assert(!has_emplace_init_list<NoCopy&&, int, int, int>(), "");
|
|
|
|
}
|
|
}
|
|
|
|
int main(int, char**) {
|
|
test_emplace_type<small>();
|
|
test_emplace_type<large>();
|
|
test_emplace_type<small_throws_on_copy>();
|
|
test_emplace_type<large_throws_on_copy>();
|
|
test_emplace_type<throws_on_move>();
|
|
test_emplace_type_tracked<small_tracked_t>();
|
|
test_emplace_type_tracked<large_tracked_t>();
|
|
test_emplace_sfinae_constraints();
|
|
#ifndef TEST_HAS_NO_EXCEPTIONS
|
|
test_emplace_throws<SmallThrows>();
|
|
test_emplace_throws<LargeThrows>();
|
|
#endif
|
|
|
|
return 0;
|
|
}
|