/** * This file contains tests for the -Wunique_object_duplication warning. * See the warning's documentation for more information. */ #ifdef WINDOWS_TEST #define HIDDEN // dllimport also suffices for visibility, but those can't have definitions #define VISIBLE __declspec(dllexport) #else #define HIDDEN __attribute__((visibility("hidden"))) #define VISIBLE __attribute__((visibility("default"))) #endif // Helper functions constexpr int init_constexpr(int x) { return x; }; extern double init_dynamic(int); /****************************************************************************** * Case one: Static local variables in an externally-visible function ******************************************************************************/ namespace StaticLocalTest { inline void has_static_locals_external() { // Mutable static int disallowedStatic1 = 0; // hidden-warning {{'disallowedStatic1' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'disallowedStatic1' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} // Initialization might run more than once static const double disallowedStatic2 = disallowedStatic1++; // hidden-warning {{initialization of 'disallowedStatic2' may run twice when built into a shared library: it has external linkage and hidden visibility}} // windows-warning@-1 {{initialization of 'disallowedStatic2' may run twice when built into a shared library: it has external linkage and no import/export annotation}} // OK, because immutable and compile-time-initialized static constexpr int allowedStatic1 = 0; static const float allowedStatic2 = 1; static constexpr int allowedStatic3 = init_constexpr(2); static const int allowedStatic4 = init_constexpr(3); } // Don't warn for non-inline functions, since they can't (legally) appear // in more than one TU in the first place. void has_static_locals_non_inline() { // Mutable static int allowedStatic1 = 0; // Initialization might run more than once static const double allowedStatic2 = allowedStatic1++; } // Everything in this function is OK because the function is TU-local static void has_static_locals_internal() { static int allowedStatic1 = 0; static double allowedStatic2 = init_dynamic(2); static char allowedStatic3 = []() { return allowedStatic1++; }(); static constexpr int allowedStatic4 = 0; } namespace { // Everything in this function is OK because the function is also TU-local void has_static_locals_anon() { static int allowedStatic1 = 0; static double allowedStatic2 = init_dynamic(2); static char allowedStatic3 = []() { return allowedStatic1++; }(); static constexpr int allowedStatic4 = init_constexpr(3); } } // Anonymous namespace HIDDEN inline void static_local_always_hidden() { static int disallowedStatic1 = 3; // hidden-warning {{'disallowedStatic1' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // expected-warning@-1 {{'disallowedStatic1' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-2 {{'disallowedStatic1' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} { static int disallowedStatic2 = 3; // hidden-warning {{'disallowedStatic2' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // expected-warning@-1 {{'disallowedStatic2' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-2 {{'disallowedStatic2' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} } auto lmb = []() { static int disallowedStatic3 = 3; // hidden-warning {{'disallowedStatic3' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // expected-warning@-1 {{'disallowedStatic3' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-2 {{'disallowedStatic3' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} }; } // Always visible VISIBLE void static_local_never_hidden() { static int allowedStatic1 = 3; { static int allowedStatic2 = 3; } auto lmb = []() { static int allowedStatic3 = 3; }; } // Don't warn on this because it's not in a function const int setByLambda = ([]() { static int x = 3; return x++; })(); inline void has_extern_local() { extern int allowedAddressExtern; // Not a definition } inline void has_regular_local() { int allowedAddressLocal = 0; } inline void has_thread_local() { // thread_local variables are static by default thread_local int disallowedThreadLocal = 0; // hidden-warning {{'disallowedThreadLocal' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'disallowedThreadLocal' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} } // Functions themselves are always immutable, so referencing them is okay inline auto& allowedFunctionReference = has_static_locals_external; } // namespace StaticLocalTest /****************************************************************************** * Case two: Globals with external linkage ******************************************************************************/ namespace GlobalTest { // Mutable inline float disallowedGlobal1 = 3.14; // hidden-warning {{'disallowedGlobal1' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'disallowedGlobal1' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} // Initialization might run more than once inline const double disallowedGlobal5 = disallowedGlobal1++; // hidden-warning {{initialization of 'disallowedGlobal5' may run twice when built into a shared library: it has external linkage and hidden visibility}} // windows-warning@-1 {{initialization of 'disallowedGlobal5' may run twice when built into a shared library: it has external linkage and no import/export annotation}} // OK because internal linkage, so duplication is intended static float allowedGlobal1 = 3.14; const double allowedGlobal2 = init_dynamic(2); static const char allowedGlobal3 = []() { return disallowedGlobal1++; }(); static inline double allowedGlobal4 = init_dynamic(2); // OK, because immutable and compile-time-initialized constexpr int allowedGlobal5 = 0; const float allowedGlobal6 = 1; constexpr int allowedGlobal7 = init_constexpr(2); const int allowedGlobal8 = init_constexpr(3); // We don't warn on this because non-inline variables can't (legally) appear // in more than one TU. float allowedGlobal9 = 3.14; // Pointers need to be double-const-qualified inline float& nonConstReference = disallowedGlobal1; // hidden-warning {{'nonConstReference' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'nonConstReference' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} const inline int& constReference = allowedGlobal5; inline int* nonConstPointerToNonConst = nullptr; // hidden-warning {{'nonConstPointerToNonConst' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'nonConstPointerToNonConst' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} inline int const* nonConstPointerToConst = nullptr; // hidden-warning {{'nonConstPointerToConst' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'nonConstPointerToConst' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} inline int* const constPointerToNonConst = nullptr; // hidden-warning {{'constPointerToNonConst' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'constPointerToNonConst' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} inline int const* const constPointerToConst = nullptr; // Don't warn on new because it tends to generate false positives inline int const* const constPointerToConstNew = new int(7); inline int const * const * const * const nestedConstPointer = nullptr; inline int const * const ** const * const nestedNonConstPointer = nullptr; // hidden-warning {{'nestedNonConstPointer' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'nestedNonConstPointer' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} struct Test { static inline float disallowedStaticMember1; // hidden-warning {{'disallowedStaticMember1' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'disallowedStaticMember1' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} // Defined below, in the header file static float disallowedStaticMember2; // Defined in the cpp file, so won't get duplicated static float allowedStaticMember1; // Always visible VISIBLE static inline float allowedStaticMember2 = 0.0; }; inline float Test::disallowedStaticMember2 = 2.3; // hidden-warning {{'disallowedStaticMember2' may be duplicated when built into a shared library: it is mutable, with external linkage and hidden visibility}} // windows-warning@-1 {{'disallowedStaticMember2' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} // This is always visible, so nothing inside it will get duplicated struct VISIBLE NeverHidden { static inline float allowedStaticMember3; static float allowedStaticMember4; }; inline float NeverHidden::allowedStaticMember4 = 3.4; } // namespace GlobalTest /****************************************************************************** * Case three: Inside templates ******************************************************************************/ namespace TemplateTest { // We never warn inside templates because it's usually infeasible to actually // fix the warning. template int allowedTemplate1 = 0; template int allowedTemplate1; template inline int allowedTemplate2 = 0; template int allowedTemplate2; } // namespace TemplateTest /****************************************************************************** * Case four: Nested classes ******************************************************************************/ namespace NestedClassTest { // Unlike visibility, declexport annotations do not propagate down to nested // classes. Hence on windows, we only avoid duplication of class members if // their immediate containing class is annotated. On posix, we get avoid // duplication if any containing class is annotated. class VISIBLE Outer { // Visible because their class is exported inline static int allowedOuterMember = 0; int* allowedOuterFunction() { static int allowed = 0; return &allowed; } // No annotation, and visibility is only propagated on posix. class HiddenOnWindows { inline static int disallowedInnerMember = 0; // windows-warning {{'disallowedInnerMember' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} int* disallowedInnerFunction() { static int disallowed = 0; // windows-warning {{'disallowed' may be duplicated when built into a shared library: it is mutable, with external linkage and no import/export annotation}} return &disallowed; } }; class VISIBLE AlwaysVisible { inline static int allowedInnerMember = 0; int* allowedInnerFunction() { static int allowed = 0; return &allowed; } }; }; }