1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03
10 
11 // This test makes sure that the control block implementation used for non-array
12 // types in std::make_shared and std::allocate_shared is ABI compatible with the
13 // original implementation.
14 //
15 // This test is relevant because the implementation of that control block is
16 // different starting in C++20, a change that was required to implement P0674.
17 
18 #include <cassert>
19 #include <cstddef>
20 #include <memory>
21 #include <type_traits>
22 #include <utility>
23 
24 #include <string>
25 #include <vector>
26 
27 #include "test_macros.h"
28 
29 // This is the pre-C++20 implementation of the control block used by non-array
30 // std::allocate_shared and std::make_shared. We keep it here so that we can
31 // make sure our implementation is backwards compatible with it forever.
32 //
33 // Of course, the class and its methods were renamed, but the size and layout
34 // of the class should remain the same as the original implementation.
35 template <class T, class Alloc>
36 struct OldEmplaceControlBlock
37   : std::__shared_weak_count
38 {
OldEmplaceControlBlockOldEmplaceControlBlock39   explicit OldEmplaceControlBlock(Alloc a) : data_(std::move(a), std::__value_init_tag()) { }
get_elemOldEmplaceControlBlock40   T* get_elem() noexcept { return std::addressof(data_.second()); }
get_allocOldEmplaceControlBlock41   Alloc* get_alloc() noexcept { return std::addressof(data_.first()); }
42 
43 private:
__on_zero_sharedOldEmplaceControlBlock44   virtual void __on_zero_shared() noexcept {
45     // Not implemented
46   }
47 
__on_zero_shared_weakOldEmplaceControlBlock48   virtual void __on_zero_shared_weak() noexcept {
49     // Not implemented
50   }
51 
52   std::__compressed_pair<Alloc, T> data_;
53 };
54 
55 template <class T, template <class> class Alloc>
test()56 void test() {
57   using Old = OldEmplaceControlBlock<T, Alloc<T>>;
58   using New = std::__shared_ptr_emplace<T, Alloc<T>>;
59 
60   static_assert(sizeof(New) == sizeof(Old), "");
61   static_assert(alignof(New) == alignof(Old), "");
62 
63   // Also make sure each member is at the same offset
64   Alloc<T> a;
65   Old old(a);
66   New new_(a);
67 
68   // 1. Check the stored object
69   {
70     char const* old_elem = reinterpret_cast<char const*>(old.get_elem());
71     char const* new_elem = reinterpret_cast<char const*>(new_.__get_elem());
72     std::ptrdiff_t old_offset = old_elem - reinterpret_cast<char const*>(&old);
73     std::ptrdiff_t new_offset = new_elem - reinterpret_cast<char const*>(&new_);
74     assert(new_offset == old_offset && "offset of stored element changed");
75   }
76 
77   // 2. Check the allocator
78   {
79     char const* old_alloc = reinterpret_cast<char const*>(old.get_alloc());
80     char const* new_alloc = reinterpret_cast<char const*>(new_.__get_alloc());
81     std::ptrdiff_t old_offset = old_alloc - reinterpret_cast<char const*>(&old);
82     std::ptrdiff_t new_offset = new_alloc - reinterpret_cast<char const*>(&new_);
83     assert(new_offset == old_offset && "offset of allocator changed");
84   }
85 
86   // Make sure both types have the same triviality (that has ABI impact since
87   // it determined how objects are passed). Both should be non-trivial.
88   static_assert(std::is_trivial<New>::value == std::is_trivial<Old>::value, "");
89 }
90 
91 // Object types to store in the control block
92 struct TrivialEmptyType { };
93 struct TrivialNonEmptyType { char c[11]; };
94 struct FinalEmptyType final { };
95 struct NonTrivialType {
96   char c[22];
NonTrivialTypeNonTrivialType97   NonTrivialType() : c{'x'} { }
98 };
99 
100 // Allocator types
101 template <class T>
102 struct TrivialEmptyAlloc {
103   using value_type = T;
104   TrivialEmptyAlloc() = default;
TrivialEmptyAllocTrivialEmptyAlloc105   template <class U> TrivialEmptyAlloc(TrivialEmptyAlloc<U>) { }
allocateTrivialEmptyAlloc106   T* allocate(std::size_t) { return nullptr; }
deallocateTrivialEmptyAlloc107   void deallocate(T*, std::size_t) { }
108 };
109 template <class T>
110 struct TrivialNonEmptyAlloc {
111   char storage[77];
112   using value_type = T;
113   TrivialNonEmptyAlloc() = default;
TrivialNonEmptyAllocTrivialNonEmptyAlloc114   template <class U> TrivialNonEmptyAlloc(TrivialNonEmptyAlloc<U>) { }
allocateTrivialNonEmptyAlloc115   T* allocate(std::size_t) { return nullptr; }
deallocateTrivialNonEmptyAlloc116   void deallocate(T*, std::size_t) { }
117 };
118 template <class T>
119 struct FinalEmptyAlloc final {
120   using value_type = T;
121   FinalEmptyAlloc() = default;
FinalEmptyAllocFinalEmptyAlloc122   template <class U> FinalEmptyAlloc(FinalEmptyAlloc<U>) { }
allocateFinalEmptyAlloc123   T* allocate(std::size_t) { return nullptr; }
deallocateFinalEmptyAlloc124   void deallocate(T*, std::size_t) { }
125 };
126 template <class T>
127 struct NonTrivialAlloc {
128   char storage[88];
129   using value_type = T;
NonTrivialAllocNonTrivialAlloc130   NonTrivialAlloc() { }
NonTrivialAllocNonTrivialAlloc131   template <class U> NonTrivialAlloc(NonTrivialAlloc<U>) { }
allocateNonTrivialAlloc132   T* allocate(std::size_t) { return nullptr; }
deallocateNonTrivialAlloc133   void deallocate(T*, std::size_t) { }
134 };
135 
main(int,char **)136 int main(int, char**) {
137   test<TrivialEmptyType, TrivialEmptyAlloc>();
138   test<TrivialEmptyType, TrivialNonEmptyAlloc>();
139   test<TrivialEmptyType, FinalEmptyAlloc>();
140   test<TrivialEmptyType, NonTrivialAlloc>();
141 
142   test<TrivialNonEmptyType, TrivialEmptyAlloc>();
143   test<TrivialNonEmptyType, TrivialNonEmptyAlloc>();
144   test<TrivialNonEmptyType, FinalEmptyAlloc>();
145   test<TrivialNonEmptyType, NonTrivialAlloc>();
146 
147   test<FinalEmptyType, TrivialEmptyAlloc>();
148   test<FinalEmptyType, TrivialNonEmptyAlloc>();
149   test<FinalEmptyType, FinalEmptyAlloc>();
150   test<FinalEmptyType, NonTrivialAlloc>();
151 
152   test<NonTrivialType, TrivialEmptyAlloc>();
153   test<NonTrivialType, TrivialNonEmptyAlloc>();
154   test<NonTrivialType, FinalEmptyAlloc>();
155   test<NonTrivialType, NonTrivialAlloc>();
156 
157   // Test a few real world types just to make sure we didn't mess up badly somehow
158   test<std::string, std::allocator>();
159   test<int, std::allocator>();
160   test<std::vector<int>, std::allocator>();
161 
162   return 0;
163 }
164