Skip to content

Commit 9670e09

Browse files
authored
Enable unique-object-duplication warning for windows (#143537)
Followup to #125526. This expands the logic of the unique-object-duplication warning so that it also works for windows code. For the most part, the logic is unchanged, merely substituting "has no import/export annotation" in place of "has hidden visibility". However, there are some small inconsistencies between the two; namely, visibility is propagated through nested classes, while import/export annotations aren't. This PR: 1. Updates the logic for the warning to account for the differences between posix and windows 2. Changes the warning message and documentation appropriately 3. Updates the tests to cover windows, and adds new test cases for the places where behavior differs. This PR was tested by building chromium (cross compiling linux->windows) with the changes in place. After accounting for the differences in semantics, no new warnings were discovered.
1 parent 82911f1 commit 9670e09

File tree

5 files changed

+114
-54
lines changed

5 files changed

+114
-54
lines changed

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,9 @@ def UniqueObjectDuplication : DiagGroup<"unique-object-duplication"> {
806806
Warns when objects which are supposed to be globally unique might get duplicated
807807
when built into a shared library.
808808

809-
If an object with hidden visibility is built into a shared library, each instance
809+
This can occur to objects which are hidden from the dynamic linker, due to
810+
having hidden visibility (on posix) or lacking a dllimport/dllexport attribute
811+
(on windows). If such an object is built into a shared library, each instance
810812
of the library will get its own copy. This can cause very subtle bugs if there was
811813
only supposed to be one copy of the object in question: singletons aren't single,
812814
changes to one object won't affect the others, the object's initializer will run
@@ -815,7 +817,7 @@ once per copy, etc.
815817
Specifically, this warning fires when it detects an object which:
816818
1. Is defined as ``inline`` in a header file (so it might get compiled into multiple libaries), and
817819
2. Has external linkage (otherwise it's supposed to be duplicated), and
818-
3. Has hidden visibility.
820+
3. Has hidden visibility (posix) or lacks a dllimport/dllexport attribute (windows).
819821

820822
As well as one of the following:
821823
1. The object is mutable, or
@@ -825,13 +827,15 @@ The warning can be resolved by removing one of the conditions above. In rough
825827
order of preference, this may be done by:
826828
1. Marking the object ``const`` (if possible)
827829
2. Moving the object's definition to a source file
828-
3. Giving the object non-hidden visibility, e.g. using ``__attribute((visibility("default")))``.
830+
3. Making the object visible using ``__attribute((visibility("default")))``,
831+
``__declspec(dllimport)``, or ``__declspec(dllexport)``.
832+
833+
When annotating an object with ``__declspec(dllimport)`` or ``__declspec(dllexport)``,
834+
take care to ensure that the object is only exported from one dll, and is imported
835+
everywhere else.
829836

830837
Note that for (2), all levels of a pointer variable must be constant;
831838
``const int*`` will trigger the warning because the pointer itself is mutable.
832-
833-
This warning is not yet implemented for Windows, since Windows uses
834-
import/export rules instead of visibility.
835839
}];
836840
}
837841

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6267,14 +6267,19 @@ def warn_static_local_in_extern_inline : Warning<
62676267
def note_convert_inline_to_static : Note<
62686268
"use 'static' to give inline function %0 internal linkage">;
62696269

6270-
def warn_possible_object_duplication_mutable : Warning<
6271-
"%0 may be duplicated when built into a shared library: "
6272-
"it is mutable, has hidden visibility, and external linkage">,
6273-
InGroup<UniqueObjectDuplication>, DefaultIgnore;
6274-
def warn_possible_object_duplication_init : Warning<
6275-
"initialization of %0 may run twice when built into a shared library: "
6276-
"it has hidden visibility and external linkage">,
6277-
InGroup<UniqueObjectDuplication>, DefaultIgnore;
6270+
def warn_possible_object_duplication_mutable
6271+
: Warning<"%0 may be duplicated when built into a shared library: "
6272+
"it is mutable, with external linkage and "
6273+
"%select{hidden visibility|no import/export annotation}1">,
6274+
InGroup<UniqueObjectDuplication>,
6275+
DefaultIgnore;
6276+
def warn_possible_object_duplication_init
6277+
: Warning<"initialization of %0 may run twice when built into a shared "
6278+
"library: "
6279+
"it has external linkage and "
6280+
"%select{hidden visibility|no import/export annotation}1">,
6281+
InGroup<UniqueObjectDuplication>,
6282+
DefaultIgnore;
62786283

62796284
def ext_redefinition_of_typedef : ExtWarn<
62806285
"redefinition of typedef %0 is a C11 feature">,

clang/lib/Sema/SemaDecl.cpp

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13518,8 +13518,28 @@ bool Sema::GloballyUniqueObjectMightBeAccidentallyDuplicated(
1351813518

1351913519
// If the object isn't hidden, the dynamic linker will prevent duplication.
1352013520
clang::LinkageInfo Lnk = Target->getLinkageAndVisibility();
13521-
if (Lnk.getVisibility() != HiddenVisibility)
13521+
13522+
// The target is "hidden" (from the dynamic linker) if:
13523+
// 1. On posix, it has hidden visibility, or
13524+
// 2. On windows, it has no import/export annotation
13525+
if (Context.getTargetInfo().shouldDLLImportComdatSymbols()) {
13526+
if (Target->hasAttr<DLLExportAttr>() || Target->hasAttr<DLLImportAttr>())
13527+
return false;
13528+
13529+
// If the variable isn't directly annotated, check to see if it's a member
13530+
// of an annotated class.
13531+
const VarDecl *VD = dyn_cast<VarDecl>(Target);
13532+
13533+
if (VD && VD->isStaticDataMember()) {
13534+
const CXXRecordDecl *Ctx = dyn_cast<CXXRecordDecl>(VD->getDeclContext());
13535+
if (Ctx &&
13536+
(Ctx->hasAttr<DLLExportAttr>() || Ctx->hasAttr<DLLImportAttr>()))
13537+
return false;
13538+
}
13539+
} else if (Lnk.getVisibility() != HiddenVisibility) {
13540+
// Posix case
1352213541
return false;
13542+
}
1352313543

1352413544
// If the obj doesn't have external linkage, it's supposed to be duplicated.
1352513545
if (!isExternalFormalLinkage(Lnk.getLinkage()))
@@ -13550,19 +13570,16 @@ void Sema::DiagnoseUniqueObjectDuplication(const VarDecl *VD) {
1355013570
// duplicated when built into a shared library, which causes problems if it's
1355113571
// mutable (since the copies won't be in sync) or its initialization has side
1355213572
// effects (since it will run once per copy instead of once globally).
13553-
// FIXME: Windows uses dllexport/dllimport instead of visibility, and we don't
13554-
// handle that yet. Disable the warning on Windows for now.
1355513573

1355613574
// Don't diagnose if we're inside a template, because it's not practical to
1355713575
// fix the warning in most cases.
13558-
if (!Context.getTargetInfo().shouldDLLImportComdatSymbols() &&
13559-
!VD->isTemplated() &&
13576+
if (!VD->isTemplated() &&
1356013577
GloballyUniqueObjectMightBeAccidentallyDuplicated(VD)) {
1356113578

1356213579
QualType Type = VD->getType();
1356313580
if (looksMutable(Type, VD->getASTContext())) {
1356413581
Diag(VD->getLocation(), diag::warn_possible_object_duplication_mutable)
13565-
<< VD;
13582+
<< VD << Context.getTargetInfo().shouldDLLImportComdatSymbols();
1356613583
}
1356713584

1356813585
// To keep false positives low, only warn if we're certain that the
@@ -13575,7 +13592,7 @@ void Sema::DiagnoseUniqueObjectDuplication(const VarDecl *VD) {
1357513592
/*IncludePossibleEffects=*/false) &&
1357613593
!isa<CXXNewExpr>(Init->IgnoreParenImpCasts())) {
1357713594
Diag(Init->getExprLoc(), diag::warn_possible_object_duplication_init)
13578-
<< VD;
13595+
<< VD << Context.getTargetInfo().shouldDLLImportComdatSymbols();
1357913596
}
1358013597
}
1358113598
}

clang/test/SemaCXX/unique_object_duplication.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify=hidden -Wunique-object-duplication -fvisibility=hidden -Wno-unused-value %s
2-
// RUN: %clang_cc1 -fsyntax-only -verify -Wunique-object-duplication -Wno-unused-value %s
3-
// The check is currently disabled on windows in MSVC-like environments. The test should fail because we're not getting the expected warnings.
4-
// XFAIL: target={{.*}}-windows-msvc, {{.*}}-ps{{(4|5)(-.+)?}}
1+
// RUN: %clang_cc1 -fsyntax-only -Wunique-object-duplication -Wno-unused-value \
2+
// RUN: -verify -triple=x86_64-pc-linux-gnu %s
3+
// RUN: %clang_cc1 -fsyntax-only -Wunique-object-duplication -Wno-unused-value \
4+
// RUN: -verify=hidden -triple=x86_64-pc-linux-gnu -fvisibility=hidden %s
5+
// RUN: %clang_cc1 -fsyntax-only -Wunique-object-duplication -Wno-unused-value \
6+
// RUN: -verify=windows -triple=x86_64-windows-msvc -DWINDOWS_TEST -fdeclspec %s
57

68
#include "unique_object_duplication.h"
79

0 commit comments

Comments
 (0)