blob: 923e909805a6347c2f3095b02a6abfeb28b5f479 [file] [log] [blame]
// { dg-options "-std=gnu++20 -fno-lifetime-dse -O0" }
// { dg-do run { target c++20 } }
// { dg-require-effective-target hosted }
// C++20 20.11.3.7 shared_ptr Creation [util.smartptr.shared.create]
#include <memory>
#ifndef __cpp_lib_smart_ptr_for_overwrite
# error "Feature-test macro for make_shared_for_overwrite missing in <memory>"
#elif __cpp_lib_smart_ptr_for_overwrite < 202002L
# error "Feature-test macro for make_shared_for_overwrite has wrong value in <memory>"
#endif
#include <cstring>
#include <testsuite_hooks.h>
int counter = 0;
template<typename T>
struct Alloc : std::allocator<T>
{
Alloc() = default;
template<typename U>
Alloc(const Alloc<U>&) { }
T* allocate(std::size_t n)
{
++counter;
void* p = std::allocator<T>::allocate(n);
// need -fno-lifetime-dse to check for these values later.
std::memset(p, 0xff, n * sizeof(T));
return (T*)p;
}
void construct(auto*, auto&&...)
{
// The objects must be default-initialized, not using this function.
VERIFY( ! "allocator_traits::construct" );
}
void destroy(auto*)
{
// The objects must be destroyed by ~T(), not using this function.
VERIFY( ! "allocator_traits::destroy" );
}
};
void
test01()
{
Alloc<int> a;
const int expected = 0xffffffff;
std::shared_ptr<int> p1 = std::allocate_shared_for_overwrite<int>(a);
VERIFY( counter == 1 );
VERIFY( *p1 == expected );
std::shared_ptr<int[44]> p2 = std::allocate_shared_for_overwrite<int[44]>(a);
VERIFY( counter == 2 );
VERIFY( p2[0] == expected );
p2.reset();
std::shared_ptr<int[]> p3 = std::allocate_shared_for_overwrite<int[]>(a, 88);
VERIFY( counter == 3 );
VERIFY( p3[0] == expected );
VERIFY( p3[87] == expected );
std::shared_ptr<int[3][4]> p4 = std::allocate_shared_for_overwrite<int[3][4]>(a);
VERIFY( counter == 4 );
VERIFY( p4[0][0] == expected );
VERIFY( p4[2][3] == expected );
std::shared_ptr<int[][5]> p5 = std::allocate_shared_for_overwrite<int[][5]>(a, 6);
VERIFY( counter == 5 );
VERIFY( p5[0][0] == expected );
VERIFY( p5[5][4] == expected );
struct BigBoi { int x[100]; };
std::shared_ptr<BigBoi> p6 = std::allocate_shared_for_overwrite<BigBoi>(a);
VERIFY( counter == 6 );
VERIFY( p6->x[0] == expected );
std::shared_ptr<BigBoi[22]> p7 = std::allocate_shared_for_overwrite<BigBoi[22]>(a);
VERIFY( counter == 7 );
VERIFY( p7[0].x[0] == expected );
VERIFY( p7[21].x[99] == expected );
std::shared_ptr<BigBoi[]> p8 = std::allocate_shared_for_overwrite<BigBoi[]>(a, 11);
VERIFY( counter == 8 );
VERIFY( p8[0].x[0] == expected );
VERIFY( p8[10].x[10] == expected );
}
void
test02()
{
// These aren't created by the custom allocator, so we can't check that the
// memory was left uninitialized. Just dereference them.
std::shared_ptr<int> p1 = std::make_shared_for_overwrite<int>();
(void) *p1;
std::shared_ptr<int[44]> p2 = std::make_shared_for_overwrite<int[44]>();
(void) p2[0];
std::shared_ptr<int[]> p3 = std::make_shared_for_overwrite<int[]>(88);
(void) p3[0];
(void) p3[87];
std::shared_ptr<int[3][4]> p4 = std::make_shared_for_overwrite<int[3][4]>();
(void) p4[0][0];
(void) p4[2][3];
std::shared_ptr<int[][5]> p5 = std::make_shared_for_overwrite<int[][5]>(6);
(void) p5[0][0];
(void) p5[5][4];
struct BigBoi { int x[100]; };
std::shared_ptr<BigBoi> p6 = std::make_shared_for_overwrite<BigBoi>();
(void) p6->x[0];
std::shared_ptr<BigBoi[22]> p7 = std::make_shared_for_overwrite<BigBoi[22]>();
(void) p7[0].x[0];
(void) p7[21].x[99];
std::shared_ptr<BigBoi[]> p8 = std::make_shared_for_overwrite<BigBoi[]>(11);
(void) p8[0].x[0];
(void) p8[10].x[10];
}
void
test03()
{
// Type with non-trivial initialization should still be default-initialized.
struct NonTriv
{
int init = 0xbb;
int uninit;
};
std::shared_ptr<NonTriv> a = std::make_shared_for_overwrite<NonTriv>();
VERIFY( a->init == 0xbb );
std::shared_ptr<NonTriv[]> b = std::make_shared_for_overwrite<NonTriv[2]>();
VERIFY( b[1].init == 0xbb );
std::shared_ptr<NonTriv[]> c = std::make_shared_for_overwrite<NonTriv[]>(2);
VERIFY( c[1].init == 0xbb );
}
int
main()
{
test01();
test02();
test03();
}