[clang] [Clang] disallow selectany on non-global-variable declarations (PR #189641)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 29 08:36:46 PDT 2026
https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/189641
>From 0bf941754a1c9c7717215fd961007f0c1c147cb7 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Tue, 31 Mar 2026 15:00:28 +0300
Subject: [PATCH 1/2] [Clang] disallow selectany on non-global-variable
declarations
---
clang/docs/ReleaseNotes.rst | 2 ++
clang/include/clang/Basic/Attr.td | 1 +
.../pragma-attribute-supported-attributes-list.test | 1 +
clang/test/Sema/attr-selectany.c | 2 +-
clang/test/SemaCXX/attr-selectany.cpp | 13 ++++++++++++-
5 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bbbd3a34e01d6..b963a8cb5e324 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -252,6 +252,8 @@ Attribute Changes in Clang
sound because any writer must hold all capabilities, so holding any one
prevents concurrent writes.
+- Clang now disallows use of the ``selectany`` attribute on non-global-variable declarations. (#GH189141)
+
Improvements to Clang's diagnostics
-----------------------------------
- ``-Wunused-but-set-variable`` now diagnoses file-scope variables with
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8d716bd05b6ab..01fa34188da84 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4477,6 +4477,7 @@ def DLLImportStaticLocal : InheritableAttr, TargetSpecificAttr<TargetHasDLLImpor
def SelectAny : InheritableAttr {
let Spellings = [Declspec<"selectany">, GCC<"selectany">];
+ let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
let Documentation = [SelectAnyDocs];
let SimpleHandler = 1;
}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 03b9a77ec1814..2c91b57477b44 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -201,6 +201,7 @@
// CHECK-NEXT: SYCLSpecialClass (SubjectMatchRule_record)
// CHECK-NEXT: ScopedLockable (SubjectMatchRule_record)
// CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property)
+// CHECK-NEXT: SelectAny (SubjectMatchRule_variable_is_global)
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: StackProtectorIgnore (SubjectMatchRule_variable_is_local)
diff --git a/clang/test/Sema/attr-selectany.c b/clang/test/Sema/attr-selectany.c
index 1078695c26abc..dcb7376382c1a 100644
--- a/clang/test/Sema/attr-selectany.c
+++ b/clang/test/Sema/attr-selectany.c
@@ -8,4 +8,4 @@ extern __declspec(selectany) const int x1 = 1; // no warning, const means we nee
// Should we really warn on this?
extern __declspec(selectany) int x2 = 1; // expected-warning {{'extern' variable has an initializer}}
-__declspec(selectany) void foo(void) { } // expected-error{{'selectany' can only be applied to data items with external linkage}}
+__declspec(selectany) void foo(void) { } // expected-error{{'selectany' attribute only applies to global variables}}
diff --git a/clang/test/SemaCXX/attr-selectany.cpp b/clang/test/SemaCXX/attr-selectany.cpp
index 4afcb8130a14c..b6e9172028296 100644
--- a/clang/test/SemaCXX/attr-selectany.cpp
+++ b/clang/test/SemaCXX/attr-selectany.cpp
@@ -4,7 +4,7 @@
// MSVC produces similar diagnostics.
-__declspec(selectany) void foo() { } // expected-error{{'selectany' can only be applied to data items with external linkage}}
+__declspec(selectany) void foo() { } // expected-error{{'selectany' attribute only applies to global variables}}
__declspec(selectany) int x1 = 1;
@@ -53,3 +53,14 @@ extern const SomeStruct some_struct;
// Without selectany, this should stay an error.
const SomeStruct some_struct2; // expected-error {{default initialization of an object of const type 'const SomeStruct' without a user-provided default constructor}}
+
+struct __declspec(selectany) S1 {}; // expected-error {{'selectany' attribute only applies to global variables}}
+__declspec(selectany) struct S1 s1;
+
+void t() {
+ __declspec(selectany) int x; // expected-error {{'selectany' attribute only applies to global variables}}
+ __declspec(selectany) extern int y;
+}
+
+struct S2 {};
+struct __declspec(selectany) S2 s2; // expected-error {{'selectany' attribute only applies to global variables}}
>From d8f5dc358fd9ec74aee8eebbc1159338d84fc564 Mon Sep 17 00:00:00 2001
From: Oleksandr Tarasiuk <oleksandr.tarasiuk at outlook.com>
Date: Wed, 29 Apr 2026 18:35:47 +0300
Subject: [PATCH 2/2] align selectany attr validation with msvc behavior
---
clang/include/clang/Basic/Attr.td | 3 +-
clang/lib/Sema/SemaDecl.cpp | 30 +++++++++++--------
...a-attribute-supported-attributes-list.test | 2 +-
clang/test/Sema/attr-selectany.c | 6 +++-
clang/test/SemaCXX/attr-selectany.cpp | 22 +++++++++-----
clang/test/SemaCXX/declspec-selectany.cpp | 2 +-
6 files changed, 40 insertions(+), 25 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 01fa34188da84..978d171257b96 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4477,7 +4477,8 @@ def DLLImportStaticLocal : InheritableAttr, TargetSpecificAttr<TargetHasDLLImpor
def SelectAny : InheritableAttr {
let Spellings = [Declspec<"selectany">, GCC<"selectany">];
- let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
+ let Subjects = SubjectList<[NonParmVar, Function], ErrorDiag,
+ "data items with external linkage">;
let Documentation = [SelectAnyDocs];
let SimpleHandler = 1;
}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a479ff68c35c5..70ebd59eeeace 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3138,15 +3138,17 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
--E;
continue;
}
- } else if (isa<SelectAnyAttr>(NewAttribute) &&
- cast<VarDecl>(New)->isInline() &&
- !cast<VarDecl>(New)->isInlineSpecified()) {
+ } else if (isa<SelectAnyAttr>(NewAttribute)) {
// Don't warn about applying selectany to implicitly inline variables.
// Older compilers and language modes would require the use of selectany
// to make such variables inline, and it would have no effect if we
// honored it.
- ++I;
- continue;
+ if (auto *VD = dyn_cast<VarDecl>(New)) {
+ if (VD->isInline() && !VD->isInlineSpecified()) {
+ ++I;
+ continue;
+ }
+ }
} else if (isa<OMPDeclareVariantAttr>(NewAttribute)) {
// We allow to add OMP[Begin]DeclareVariantAttr to be added to
// declarations after definitions.
@@ -7143,15 +7145,17 @@ static void checkAliasAttr(Sema &S, NamedDecl &ND) {
}
static void checkSelectAnyAttr(Sema &S, NamedDecl &ND) {
- // 'selectany' only applies to externally visible variable declarations.
- // It does not apply to functions.
- if (SelectAnyAttr *Attr = ND.getAttr<SelectAnyAttr>()) {
- if (isa<FunctionDecl>(ND) || !ND.isExternallyVisible()) {
- S.Diag(Attr->getLocation(),
- diag::err_attribute_selectany_non_extern_data);
- ND.dropAttr<SelectAnyAttr>();
- }
+ SelectAnyAttr *Attr = ND.getAttr<SelectAnyAttr>();
+ if (!Attr)
+ return;
+
+ if (auto *VD = dyn_cast<VarDecl>(&ND)) {
+ if (!VD->isStaticDataMember() && VD->isExternallyVisible())
+ return;
}
+
+ S.Diag(Attr->getLocation(), diag::err_attribute_selectany_non_extern_data);
+ ND.dropAttr<SelectAnyAttr>();
}
static void checkHybridPatchableAttr(Sema &S, NamedDecl &ND) {
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 2c91b57477b44..ae9d5d648c260 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -201,7 +201,7 @@
// CHECK-NEXT: SYCLSpecialClass (SubjectMatchRule_record)
// CHECK-NEXT: ScopedLockable (SubjectMatchRule_record)
// CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property)
-// CHECK-NEXT: SelectAny (SubjectMatchRule_variable_is_global)
+// CHECK-NEXT: SelectAny (SubjectMatchRule_variable_not_is_parameter, SubjectMatchRule_function)
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: SpeculativeLoadHardening (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: StackProtectorIgnore (SubjectMatchRule_variable_is_local)
diff --git a/clang/test/Sema/attr-selectany.c b/clang/test/Sema/attr-selectany.c
index dcb7376382c1a..827b44af3e936 100644
--- a/clang/test/Sema/attr-selectany.c
+++ b/clang/test/Sema/attr-selectany.c
@@ -8,4 +8,8 @@ extern __declspec(selectany) const int x1 = 1; // no warning, const means we nee
// Should we really warn on this?
extern __declspec(selectany) int x2 = 1; // expected-warning {{'extern' variable has an initializer}}
-__declspec(selectany) void foo(void) { } // expected-error{{'selectany' attribute only applies to global variables}}
+__declspec(selectany) void x3(void) { } // expected-error {{'selectany' can only be applied to data items with external linkage}}
+
+void t() {
+ __declspec(selectany) extern int i;
+}
diff --git a/clang/test/SemaCXX/attr-selectany.cpp b/clang/test/SemaCXX/attr-selectany.cpp
index b6e9172028296..9c4130b9837ee 100644
--- a/clang/test/SemaCXX/attr-selectany.cpp
+++ b/clang/test/SemaCXX/attr-selectany.cpp
@@ -1,10 +1,10 @@
-// RUN: %clang_cc1 -triple x86_64-win32 -fms-compatibility -fms-extensions -fsyntax-only -verify -std=c++11 %s
-// RUN: %clang_cc1 -triple x86_64-unknown-linux -fms-compatibility -fms-extensions -fsyntax-only -verify -std=c++11 %s
-// RUN: %clang_cc1 -triple x86_64-win32-macho -fms-compatibility -fms-extensions -fsyntax-only -verify -std=c++11 %s
+// RUN: %clang_cc1 -triple x86_64-win32 -fms-compatibility -fms-extensions -fsyntax-only -verify=expected -std=c++11 %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux -fms-compatibility -fms-extensions -fsyntax-only -verify=expected -std=c++11 %s
+// RUN: %clang_cc1 -triple x86_64-win32-macho -fms-compatibility -fms-extensions -fsyntax-only -verify=expected,win23-macho -std=c++11 %s
// MSVC produces similar diagnostics.
-__declspec(selectany) void foo() { } // expected-error{{'selectany' attribute only applies to global variables}}
+__declspec(selectany) void foo() { } // expected-error{{'selectany' can only be applied to data items with external linkage}}
__declspec(selectany) int x1 = 1;
@@ -54,13 +54,19 @@ extern const SomeStruct some_struct;
// Without selectany, this should stay an error.
const SomeStruct some_struct2; // expected-error {{default initialization of an object of const type 'const SomeStruct' without a user-provided default constructor}}
-struct __declspec(selectany) S1 {}; // expected-error {{'selectany' attribute only applies to global variables}}
+struct __declspec(selectany) S1 {}; // expected-error {{'selectany' attribute only applies to data items with external linkage}}
__declspec(selectany) struct S1 s1;
void t() {
- __declspec(selectany) int x; // expected-error {{'selectany' attribute only applies to global variables}}
- __declspec(selectany) extern int y;
+ __declspec(selectany) int a; // expected-error {{'selectany' can only be applied to data items with external linkage}}
+ __declspec(selectany) extern int b;
+ __declspec(selectany) static int c; // expected-error {{'selectany' can only be applied to data items with external linkage}}
+ __declspec(selectany) thread_local int d; // expected-error {{'selectany' can only be applied to data items with external linkage}} win23-macho-error {{thread-local storage is not supported for the current target}}
}
struct S2 {};
-struct __declspec(selectany) S2 s2; // expected-error {{'selectany' attribute only applies to global variables}}
+struct __declspec(selectany) S2 s2; // expected-error {{'selectany' attribute only applies to data items with external linkage}}
+
+struct S3 {
+ __declspec(selectany) static int a; // expected-error {{'selectany' can only be applied to data items with external linkage}}
+};
diff --git a/clang/test/SemaCXX/declspec-selectany.cpp b/clang/test/SemaCXX/declspec-selectany.cpp
index 7e64a2924c99a..e53e1ff2beef9 100644
--- a/clang/test/SemaCXX/declspec-selectany.cpp
+++ b/clang/test/SemaCXX/declspec-selectany.cpp
@@ -15,4 +15,4 @@ const int __declspec(selectany) test3 = 42; // Standard usage.
struct Test4 {
static constexpr int sdm = 0;
};
-__declspec(selectany) constexpr int Test4::sdm; // no warning
+__declspec(selectany) constexpr int Test4::sdm; // expected-error {{'selectany' can only be applied to data items with external linkage}}
More information about the cfe-commits
mailing list