• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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