You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Warn when unique objects might be duplicated in shared libraries
When a hidden object is built into multiple shared libraries, each
instance of the library will get its own copy. If
the object was supposed to be globally unique (e.g. a global
variable or static member), this can cause very subtle bugs.
An object might be incorrectly duplicated if it:
- Is defined in a header (so it might appear in multiple TUs), and
- Has external linkage (otherwise it's supposed to be duplicated), and
- Has hidden visibility (or else the dynamic linker will handle it)
The duplication is only a problem if one of the following is true:
1. The object is mutable (the copies won't be in sync), or
2. Its initialization has side effects (it may now run more than once), or
3. The value of its address is used (which one?).
To detect this, we add a new -Wunique-object-duplication warning.
It warns on cases (1) and (2) above. To be conservative, we only
warn in case (2) if we are certain the initializer has side effects,
and we don't warn on `new` because the only side effect is some
extra memory usage.
We don't currently warn on case (3) because doing so is prone to
false positives: there are many reasons for taking the address which
aren't inherently problematic (e.g. passing to a function that expects
a pointer). We only run into problems if the code inspects the value
of the address.
The check is currently disabled for windows, which uses its own analogue
of visibility (declimport/declexport). The check is also disabled inside
templates, since it can give false positives if a template is never
instantiated.
staticint disallowedStatic1 = 0; // hidden-warning {{'disallowedStatic1' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
41
+
// Initialization might run more than once
42
+
staticconstdouble disallowedStatic2 = disallowedStatic1++; // hidden-warning {{'disallowedStatic2' has hidden visibility, and external linkage; its initialization may run more than once when built into a shared library}}
43
+
44
+
// OK, because immutable and compile-time-initialized
staticint disallowedStatic1 = 0; // hidden-warning {{'disallowedStatic1' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
64
+
// Initialization might run more than once
65
+
staticconstdouble disallowedStatic2 = disallowedStatic1++; // hidden-warning {{'disallowedStatic2' has hidden visibility, and external linkage; its initialization may run more than once when built into a shared library}}
66
+
67
+
// OK, because immutable and compile-time-initialized
staticint disallowedStatic1 = 3; // hidden-warning {{'disallowedStatic1' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
104
+
// expected-warning@-1 {{'disallowedStatic1' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
105
+
{
106
+
staticint disallowedStatic2 = 3; // hidden-warning {{'disallowedStatic2' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
107
+
// expected-warning@-1 {{'disallowedStatic2' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
108
+
}
109
+
110
+
auto lmb = []() {
111
+
staticint disallowedStatic3 = 3; // hidden-warning {{'disallowedStatic3' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
112
+
// expected-warning@-1 {{'disallowedStatic3' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
113
+
};
114
+
}
115
+
116
+
DEFAULT voidstatic_local_never_hidden() {
117
+
staticint allowedStatic1 = 3;
118
+
119
+
{
120
+
staticint allowedStatic2 = 3;
121
+
}
122
+
123
+
auto lmb = []() {
124
+
staticint allowedStatic3 = 3;
125
+
};
126
+
}
127
+
128
+
// Don't warn on this because it's not in a function
externint allowedAddressExtern; // Not a definition
133
+
}
134
+
135
+
inlinevoidhas_regular_local() {
136
+
int allowedAddressLocal = 0;
137
+
}
138
+
139
+
inlinevoidhas_thread_local() {
140
+
// thread_local variables are static by default
141
+
thread_localint disallowedThreadLocal = 0; // hidden-warning {{'disallowedThreadLocal' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
inlinefloat disallowedGlobal1 = 3.14; // hidden-warning {{'disallowedGlobal1' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
152
+
// Same as above, but explicitly marked inline
153
+
inlinefloat disallowedGlobal4 = 3.14; // hidden-warning {{'disallowedGlobal4' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
154
+
155
+
// Initialization might run more than once
156
+
inlineconstdouble disallowedGlobal5 = disallowedGlobal1++; // hidden-warning {{'disallowedGlobal5' has hidden visibility, and external linkage; its initialization may run more than once when built into a shared library}}
157
+
158
+
// OK because internal linkage, so duplication is intended
// OK, because immutable and compile-time-initialized
165
+
constexprint allowedGlobal5 = 0;
166
+
constfloat allowedGlobal6 = 1;
167
+
constexprint allowedGlobal7 = init_constexpr(2);
168
+
constint allowedGlobal8 = init_constexpr(3);
169
+
170
+
// We don't warn on this because non-inline variables can't (legally) appear
171
+
// in more than one TU.
172
+
float allowedGlobal9 = 3.14;
173
+
174
+
template<typename T>
175
+
T templatedGlobal = T(9); // hidden-warning {{'templatedGlobal' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
176
+
177
+
template<typename T>
178
+
int templatedGlobal<T*> = T(3); // hidden-warning {{'templatedGlobal<T *>' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
179
+
180
+
// Don't warn on this because each explicit specialization can't (legally) appear
181
+
// in more than one TU.
182
+
template<>
183
+
int templatedGlobal<int*> = 8;
184
+
185
+
// Pointers need to be double-const-qualified
186
+
inlinefloat& nonConstReference = disallowedGlobal1; // hidden-warning {{'nonConstReference' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
187
+
constinlineint& constReference = allowedGlobal5;
188
+
189
+
inlineint* nonConstPointerToNonConst = nullptr; // hidden-warning {{'nonConstPointerToNonConst' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
190
+
inlineintconst* nonConstPointerToConst = nullptr; // hidden-warning {{'nonConstPointerToConst' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
191
+
inlineint* const constPointerToNonConst = nullptr; // hidden-warning {{'constPointerToNonConst' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
inlineintconst * const ** const * const nestedNonConstPointer = nullptr; // hidden-warning {{'nestedNonConstPointer' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
198
+
199
+
structTest {
200
+
staticinlinefloat disallowedStaticMember1; // hidden-warning {{'disallowedStaticMember1' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
201
+
// Defined below, in the header file
202
+
staticfloat disallowedStaticMember2;
203
+
// Defined in the cpp file, so won't get duplicated
204
+
staticfloat allowedStaticMember1;
205
+
206
+
// Tests here are sparse because the AddrTest case below will define plenty
207
+
// more, which aren't problematic to define (because they're immutable), but
208
+
// may still cause problems if their address is taken.
209
+
};
210
+
211
+
inlinefloat Test::disallowedStaticMember2 = 2.3; // hidden-warning {{'disallowedStaticMember2' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
0 commit comments