https://github.com/DKLoehr updated https://github.com/llvm/llvm-project/pull/125902
>From d95344cf393bcf0a8580e81f4848f5f72c67a652 Mon Sep 17 00:00:00 2001 From: Devon Loehr <dlo...@google.com> Date: Tue, 4 Feb 2025 16:47:01 +0000 Subject: [PATCH 1/5] Move into separate function, call in CheckCompleteVariableDeclaration --- clang/include/clang/Sema/Sema.h | 1 + clang/lib/Sema/SemaDecl.cpp | 94 +++++++++++++++++---------------- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1870d1271c556..4aa5d82a7b535 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3669,6 +3669,7 @@ class Sema final : public SemaBase { /// cause problems if the variable is mutable, its initialization is /// effectful, or its address is taken. bool GloballyUniqueObjectMightBeAccidentallyDuplicated(const VarDecl *Dcl); + void DiagnoseDangerousUniqueObjectDuplication(const VarDecl *Dcl); /// AddInitializerToDecl - Adds the initializer Init to the /// declaration dcl. If DirectInit is true, this is C++ direct diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 74e0fcec2d911..eb8fbf424d264 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13436,6 +13436,53 @@ bool Sema::GloballyUniqueObjectMightBeAccidentallyDuplicated( return true; } +void Sema::DiagnoseDangerousUniqueObjectDuplication(const VarDecl* VD) { + // If this object has external linkage and hidden visibility, it might be + // duplicated when built into a shared library, which causes problems if it's + // mutable (since the copies won't be in sync) or its initialization has side + // effects (since it will run once per copy instead of once globally) + // FIXME: Windows uses dllexport/dllimport instead of visibility, and we don't + // handle that yet. Disable the warning on Windows for now. + // FIXME: Checking templates can cause false positives if the template in + // question is never instantiated (e.g. only specialized templates are used). + if (!Context.getTargetInfo().shouldDLLImportComdatSymbols() && + !VD->isTemplated() && + GloballyUniqueObjectMightBeAccidentallyDuplicated(VD)) { + // Check mutability. For pointers, ensure that both the pointer and the + // pointee are (recursively) const. + QualType Type = VD->getType().getNonReferenceType(); + if (!Type.isConstant(VD->getASTContext())) { + Diag(VD->getLocation(), diag::warn_possible_object_duplication_mutable) + << VD; + } else { + while (Type->isPointerType()) { + Type = Type->getPointeeType(); + if (Type->isFunctionType()) + break; + if (!Type.isConstant(VD->getASTContext())) { + Diag(VD->getLocation(), + diag::warn_possible_object_duplication_mutable) + << VD; + break; + } + } + } + + // To keep false positives low, only warn if we're certain that the + // initializer has side effects. Don't warn on operator new, since a mutable + // pointer will trigger the previous warning, and an immutable pointer + // getting duplicated just results in a little extra memory usage. + const Expr *Init = VD->getAnyInitializer(); + if (Init && + Init->HasSideEffects(VD->getASTContext(), + /*IncludePossibleEffects=*/false) && + !isa<CXXNewExpr>(Init->IgnoreParenImpCasts())) { + Diag(Init->getExprLoc(), diag::warn_possible_object_duplication_init) + << VD; + } + } +} + void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { // If there is no declaration, there was an error parsing it. Just ignore // the initializer. @@ -14655,6 +14702,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { return; } + DiagnoseDangerousUniqueObjectDuplication(var); + // Require the destructor. if (!type->isDependentType()) if (const RecordType *recordType = baseType->getAs<RecordType>()) @@ -14842,51 +14891,6 @@ void Sema::FinalizeDeclaration(Decl *ThisDecl) { if (DC->getRedeclContext()->isFileContext() && VD->isExternallyVisible()) AddPushedVisibilityAttribute(VD); - // If this object has external linkage and hidden visibility, it might be - // duplicated when built into a shared library, which causes problems if it's - // mutable (since the copies won't be in sync) or its initialization has side - // effects (since it will run once per copy instead of once globally) - // FIXME: Windows uses dllexport/dllimport instead of visibility, and we don't - // handle that yet. Disable the warning on Windows for now. - // FIXME: Checking templates can cause false positives if the template in - // question is never instantiated (e.g. only specialized templates are used). - if (!Context.getTargetInfo().shouldDLLImportComdatSymbols() && - !VD->isTemplated() && - GloballyUniqueObjectMightBeAccidentallyDuplicated(VD)) { - // Check mutability. For pointers, ensure that both the pointer and the - // pointee are (recursively) const. - QualType Type = VD->getType().getNonReferenceType(); - if (!Type.isConstant(VD->getASTContext())) { - Diag(VD->getLocation(), diag::warn_possible_object_duplication_mutable) - << VD; - } else { - while (Type->isPointerType()) { - Type = Type->getPointeeType(); - if (Type->isFunctionType()) - break; - if (!Type.isConstant(VD->getASTContext())) { - Diag(VD->getLocation(), - diag::warn_possible_object_duplication_mutable) - << VD; - break; - } - } - } - - // To keep false positives low, only warn if we're certain that the - // initializer has side effects. Don't warn on operator new, since a mutable - // pointer will trigger the previous warning, and an immutable pointer - // getting duplicated just results in a little extra memory usage. - const Expr *Init = VD->getAnyInitializer(); - if (Init && - Init->HasSideEffects(VD->getASTContext(), - /*IncludePossibleEffects=*/false) && - !isa<CXXNewExpr>(Init->IgnoreParenImpCasts())) { - Diag(Init->getExprLoc(), diag::warn_possible_object_duplication_init) - << VD; - } - } - // FIXME: Warn on unused var template partial specializations. if (VD->isFileVarDecl() && !isa<VarTemplatePartialSpecializationDecl>(VD)) MarkUnusedFileScopedDecl(VD); >From a7a2fbea6446dc17f4763e629524ae9e92bf8736 Mon Sep 17 00:00:00 2001 From: Devon Loehr <dlo...@google.com> Date: Tue, 4 Feb 2025 17:24:20 +0000 Subject: [PATCH 2/5] Enable for templates --- clang/lib/Sema/SemaDecl.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index eb8fbf424d264..a3936937e6196 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13399,8 +13399,11 @@ bool Sema::GloballyUniqueObjectMightBeAccidentallyDuplicated( // about the properties of the function containing it. const ValueDecl *Target = Dcl; // VarDecls and FunctionDecls have different functions for checking - // inline-ness, so we have to do it manually. + // inline-ness, and whether they were originally templated, so we have to + // call the appropriate functions manually. bool TargetIsInline = Dcl->isInline(); + bool TargetWasTemplated = + Dcl->getTemplateSpecializationKind() != TSK_Undeclared; // Update the Target and TargetIsInline property if necessary if (Dcl->isStaticLocal()) { @@ -13416,12 +13419,13 @@ bool Sema::GloballyUniqueObjectMightBeAccidentallyDuplicated( Target = FunDcl; // IsInlined() checks for the C++ inline property TargetIsInline = FunDcl->isInlined(); + TargetWasTemplated = + FunDcl->getTemplateSpecializationKind() != TSK_Undeclared; } - // Non-inline variables can only legally appear in one TU - // FIXME: This also applies to templated variables, but that can rarely lead - // to false positives so templates are disabled for now. - if (!TargetIsInline) + // Non-inline functions/variables can only legally appear in one TU, + // unless they were part of a template. + if (!TargetIsInline && !TargetWasTemplated) return false; // If the object isn't hidden, the dynamic linker will prevent duplication. @@ -13436,18 +13440,20 @@ bool Sema::GloballyUniqueObjectMightBeAccidentallyDuplicated( return true; } -void Sema::DiagnoseDangerousUniqueObjectDuplication(const VarDecl* VD) { +void Sema::DiagnoseDangerousUniqueObjectDuplication(const VarDecl *VD) { // If this object has external linkage and hidden visibility, it might be // duplicated when built into a shared library, which causes problems if it's // mutable (since the copies won't be in sync) or its initialization has side - // effects (since it will run once per copy instead of once globally) + // effects (since it will run once per copy instead of once globally). // FIXME: Windows uses dllexport/dllimport instead of visibility, and we don't // handle that yet. Disable the warning on Windows for now. - // FIXME: Checking templates can cause false positives if the template in - // question is never instantiated (e.g. only specialized templates are used). + + // Don't diagnose if we're inside a template; + // we'll diagnose during instantiation instead. if (!Context.getTargetInfo().shouldDLLImportComdatSymbols() && !VD->isTemplated() && GloballyUniqueObjectMightBeAccidentallyDuplicated(VD)) { + // Check mutability. For pointers, ensure that both the pointer and the // pointee are (recursively) const. QualType Type = VD->getType().getNonReferenceType(); >From 074f5e52597fa997cc015235483580e3084b84e1 Mon Sep 17 00:00:00 2001 From: Devon Loehr <dlo...@google.com> Date: Wed, 5 Feb 2025 17:48:34 +0000 Subject: [PATCH 3/5] Add tests for templates --- .../test/SemaCXX/unique_object_duplication.h | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/clang/test/SemaCXX/unique_object_duplication.h b/clang/test/SemaCXX/unique_object_duplication.h index 5b2002c31be7c..6ac5828d64484 100644 --- a/clang/test/SemaCXX/unique_object_duplication.h +++ b/clang/test/SemaCXX/unique_object_duplication.h @@ -154,4 +154,89 @@ namespace GlobalTest { }; inline float Test::disallowedStaticMember2 = 2.3; // hidden-warning {{'disallowedStaticMember2' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} -} // namespace GlobalTest \ No newline at end of file +} // namespace GlobalTest + +/****************************************************************************** + * Case three: Inside templates + ******************************************************************************/ + +namespace TemplateTest { + +template <typename T> +int disallowedTemplate1 = 0; // hidden-warning {{'disallowedTemplate1<int>' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} + +template int disallowedTemplate1<int>; // hidden-note {{in instantiation of}} + + +// Should work for implicit instantiation as well +template <typename T> +int disallowedTemplate2 = 0; // hidden-warning {{'disallowedTemplate2<int>' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} + +int implicit_instantiate() { + return disallowedTemplate2<int>; // hidden-note {{in instantiation of}} +} + + +// Ensure we only get warnings for templates that are actually instantiated +template <typename T> +int maybeAllowedTemplate = 0; // Not instantiated, so no warning here + +template <typename T> +int maybeAllowedTemplate<T*> = 1; // hidden-warning {{'maybeAllowedTemplate<int *>' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} + +template <> +int maybeAllowedTemplate<bool> = 2; // hidden-warning {{'maybeAllowedTemplate<bool>' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} + +template int maybeAllowedTemplate<int*>; // hidden-note {{in instantiation of}} + + + +// Should work the same for static class members +template <class T> +struct S { + static int staticMember; +}; + +template <class T> +int S<T>::staticMember = 0; // Never instantiated + +// T* specialization +template <class T> +struct S<T*> { + static int staticMember; +}; + +template <class T> +int S<T*>::staticMember = 1; // hidden-warning {{'staticMember' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} + +template class S<int*>; // hidden-note {{in instantiation of}} + +// T& specialization, implicitly instantiated +template <class T> +struct S<T&> { + static int staticMember; +}; + +template <class T> +int S<T&>::staticMember = 2; // hidden-warning {{'staticMember' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} + +int implicit_instantiate2() { + return S<bool&>::staticMember; // hidden-note {{in instantiation of}} +} + + +// Should work for static locals as well +template <class T> +int* wrapper() { + static int staticLocal; // hidden-warning {{'staticLocal' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} + return &staticLocal; +} + +template <> +int* wrapper<int*>() { + static int staticLocal; // hidden-warning {{'staticLocal' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} + return &staticLocal; +} + +auto dummy = wrapper<bool>(); // hidden-note {{in instantiation of}} +} // namespace TemplateTest \ No newline at end of file >From 6081efa1a76278082d65c12b3fe290244d4cbc0e Mon Sep 17 00:00:00 2001 From: Devon Loehr <dlo...@google.com> Date: Tue, 11 Feb 2025 15:36:46 +0000 Subject: [PATCH 4/5] Change class->typename in test templates --- clang/test/SemaCXX/unique_object_duplication.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/test/SemaCXX/unique_object_duplication.h b/clang/test/SemaCXX/unique_object_duplication.h index 6ac5828d64484..a59a8f91da8b8 100644 --- a/clang/test/SemaCXX/unique_object_duplication.h +++ b/clang/test/SemaCXX/unique_object_duplication.h @@ -192,32 +192,32 @@ template int maybeAllowedTemplate<int*>; // hidden-note {{in instantiation of}} // Should work the same for static class members -template <class T> +template <typename T> struct S { static int staticMember; }; -template <class T> +template <typename T> int S<T>::staticMember = 0; // Never instantiated // T* specialization -template <class T> +template <typename T> struct S<T*> { static int staticMember; }; -template <class T> +template <typename T> int S<T*>::staticMember = 1; // hidden-warning {{'staticMember' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} template class S<int*>; // hidden-note {{in instantiation of}} // T& specialization, implicitly instantiated -template <class T> +template <typename T> struct S<T&> { static int staticMember; }; -template <class T> +template <typename T> int S<T&>::staticMember = 2; // hidden-warning {{'staticMember' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} int implicit_instantiate2() { @@ -226,7 +226,7 @@ int implicit_instantiate2() { // Should work for static locals as well -template <class T> +template <typename T> int* wrapper() { static int staticLocal; // hidden-warning {{'staticLocal' may be duplicated when built into a shared library: it is mutable, has hidden visibility, and external linkage}} return &staticLocal; >From 12345719ccd6952486a12a7899495c6018d471ac Mon Sep 17 00:00:00 2001 From: Devon Loehr <dlo...@google.com> Date: Tue, 11 Feb 2025 16:24:25 +0000 Subject: [PATCH 5/5] Address more feedback --- clang/include/clang/Sema/Sema.h | 2 +- clang/lib/Sema/SemaDecl.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 4aa5d82a7b535..a501b901862b6 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3669,7 +3669,7 @@ class Sema final : public SemaBase { /// cause problems if the variable is mutable, its initialization is /// effectful, or its address is taken. bool GloballyUniqueObjectMightBeAccidentallyDuplicated(const VarDecl *Dcl); - void DiagnoseDangerousUniqueObjectDuplication(const VarDecl *Dcl); + void DiagnoseUniqueObjectDuplication(const VarDecl *Dcl); /// AddInitializerToDecl - Adds the initializer Init to the /// declaration dcl. If DirectInit is true, this is C++ direct diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a3936937e6196..4ed80327291ff 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -13440,7 +13440,7 @@ bool Sema::GloballyUniqueObjectMightBeAccidentallyDuplicated( return true; } -void Sema::DiagnoseDangerousUniqueObjectDuplication(const VarDecl *VD) { +void Sema::DiagnoseUniqueObjectDuplication(const VarDecl *VD) { // If this object has external linkage and hidden visibility, it might be // duplicated when built into a shared library, which causes problems if it's // mutable (since the copies won't be in sync) or its initialization has side @@ -14708,7 +14708,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { return; } - DiagnoseDangerousUniqueObjectDuplication(var); + DiagnoseUniqueObjectDuplication(var); // Require the destructor. if (!type->isDependentType()) _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits