1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_toolchain/no_destructor.h"
16
17 #include "pw_assert/check.h"
18 #include "pw_polyfill/language_feature_macros.h"
19 #include "pw_polyfill/standard.h"
20 #include "pw_unit_test/framework.h"
21
22 namespace pw {
23 namespace {
24
25 class HasADestructor {
26 public:
HasADestructor(bool & destructor_called_flag)27 HasADestructor(bool& destructor_called_flag)
28 : destructor_called_(destructor_called_flag) {
29 destructor_called_ = false;
30 }
31
~HasADestructor()32 ~HasADestructor() { destructor_called_ = true; }
33
34 private:
35 bool& destructor_called_;
36 };
37
38 class CrashInDestructor {
39 public:
MyAddress() const40 const CrashInDestructor* MyAddress() const { return this; }
41
42 int some_value = 0;
43
44 private:
45 // Due to a bug in GCC (at least 12.2), classes with private destructors must
46 // friend NoDestructor to use it, even though NoDestructor never calls the
47 // destructor. RuntimeInitGlobal may be used as a workaround.
48 #if defined(__GNUC__) && !defined(__clang__)
49 friend class pw::NoDestructor<CrashInDestructor>;
50 #endif // defined(__GNUC__) && !defined(__clang__)
51
~CrashInDestructor()52 ~CrashInDestructor() { PW_CRASH("This destructor should never execute!"); }
53 };
54
55 class TrivialDestructor {
56 public:
TrivialDestructor(int initial_value)57 TrivialDestructor(int initial_value) : value(initial_value) {}
58
59 int value;
60 };
61
62 class ConstexprConstructible {
63 public:
ConstexprConstructible()64 constexpr ConstexprConstructible() : crash(true) {}
65
~ConstexprConstructible()66 PW_CONSTEXPR_CPP20 ~ConstexprConstructible() { PW_CHECK(!crash); }
67
68 bool crash;
69 };
70
TEST(NoDestructor,ShouldNotCallDestructor)71 TEST(NoDestructor, ShouldNotCallDestructor) {
72 bool destructor_called = false;
73
74 {
75 HasADestructor should_be_destroyed(destructor_called);
76 }
77
78 EXPECT_TRUE(destructor_called);
79
80 {
81 NoDestructor<HasADestructor> should_not_be_destroyed(destructor_called);
82 }
83
84 EXPECT_FALSE(destructor_called);
85 }
86
TEST(NoDestructor,MemberAccess)87 TEST(NoDestructor, MemberAccess) {
88 NoDestructor<CrashInDestructor> no_destructor;
89
90 no_destructor->some_value = 123;
91 EXPECT_EQ(123, (*no_destructor).some_value);
92 EXPECT_EQ(no_destructor.operator->(), no_destructor->MyAddress());
93 }
94
TEST(NoDestructor,TrivialDestructor)95 TEST(NoDestructor, TrivialDestructor) {
96 NoDestructor<TrivialDestructor> no_destructor(555);
97
98 EXPECT_EQ(no_destructor->value, 555);
99 no_destructor->value = 123;
100 EXPECT_EQ(no_destructor->value, 123);
101 }
102
TEST(NoDestructor,TrivialType)103 TEST(NoDestructor, TrivialType) {
104 NoDestructor<int> no_destructor;
105
106 EXPECT_EQ(*no_destructor, 0);
107 *no_destructor = 123;
108 EXPECT_EQ(*no_destructor, 123);
109 }
110
TEST(NoDestructor,FunctionStatic)111 TEST(NoDestructor, FunctionStatic) {
112 static NoDestructor<CrashInDestructor> function_static_no_destructor;
113 }
114
TEST(NoDestructor,Constinit)115 TEST(NoDestructor, Constinit) {
116 PW_CONSTINIT static NoDestructor<ConstexprConstructible> should_crash;
117 EXPECT_TRUE(should_crash->crash);
118 }
119
120 NoDestructor<CrashInDestructor> global_no_destructor;
121 PW_CONSTINIT NoDestructor<ConstexprConstructible> global_constinit;
122
TEST(NoDestructor,Globals)123 TEST(NoDestructor, Globals) {
124 EXPECT_EQ(global_no_destructor->some_value, 0);
125 EXPECT_TRUE(global_constinit->crash);
126 }
127
128 static_assert(std::is_trivially_destructible<pw::NoDestructor<int>>::value,
129 "Wrapper should be trivially destructible");
130
131 constexpr NoDestructor<int> kConstexprTrivialNoDestructor(1138);
132 static_assert(*kConstexprTrivialNoDestructor == 1138);
133
134 #if PW_CXX_STANDARD_IS_SUPPORTED(20)
135
136 constexpr NoDestructor<ConstexprConstructible> kConstexprNoDestructor;
137 static_assert(kConstexprNoDestructor->crash);
138
139 #endif // PW_CXX_STANDARD_IS_SUPPORTED(20)
140
141 } // namespace
142 } // namespace pw
143