[clang] 12992dc - [clang] Fix handling of constexpr static data member template. (#220469)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 15 20:18:56 PDT 2026
Author: Eli Friedman
Date: 2026-09-15T20:18:50-07:00
New Revision: 12992dca3ccbd2c4c3fc67528467ac73a1da292d
URL: https://github.com/llvm/llvm-project/commit/12992dca3ccbd2c4c3fc67528467ac73a1da292d
DIFF: https://github.com/llvm/llvm-project/commit/12992dca3ccbd2c4c3fc67528467ac73a1da292d.diff
LOG: [clang] Fix handling of constexpr static data member template. (#220469)
[temp.expl.spec] says "An explicit specialization of a static data
member of a template or an explicit specialization of a static data
member template is a definition if the declaration includes an
initializer." The code did not match this rule for constexpr variables.
The check in question was only supposed to match the cases from
[depr.static.constexpr].
Fixes #219796
Added:
Modified:
clang/docs/ReleaseNotes.md
clang/lib/AST/Decl.cpp
clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
clang/test/SemaTemplate/class-template-spec.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 756722bcd2d1b..db710b64a6ae5 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -698,6 +698,9 @@ features cannot lower the translation-unit ABI level;
class with an invalid non-static data member, such as one qualified with an
address space. (#GH194605)
+- Fixed an issue where an explicit specialization of a constexpr variable would
+ result in a link error. (#GH219796)
+
#### Bug Fixes to AST Handling
- Fixed a non-deterministic ordering of unused local typedefs that made
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index d1d296dd60d14..34f5d0abe5f74 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2258,8 +2258,8 @@ VarDecl::isThisDeclarationADefinition(ASTContext &C) const {
// a static data member template outside the containing class?
if (isStaticDataMember()) {
if (isOutOfLine() &&
- !(getCanonicalDecl()->isInline() &&
- getCanonicalDecl()->isConstexpr()) &&
+ !(getCanonicalDecl()->isInline() && getCanonicalDecl()->isConstexpr() &&
+ !getCanonicalDecl()->isOutOfLine()) &&
(hasInit() ||
// If the first declaration is out-of-line, this may be an
// instantiation of an out-of-line partial specialization of a variable
diff --git a/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp b/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
index 9b1a6e4647e85..95011b6edf240 100644
--- a/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
+++ b/clang/test/CodeGenCXX/cxx1z-inline-variables.cpp
@@ -101,6 +101,22 @@ constexpr int Y<int>::a;
const int &yib = Y<int>::b;
// CHECK-NOT: @_ZN1YIiE1cE
+namespace PR219796 {
+template<typename> struct A;
+template<>
+struct A<void> {
+ template<int> static int value;
+};
+
+// CHECK: _ZN8PR2197961AIvE5valueILi101EEE = weak_odr constant i32 3
+template<> constexpr int A<void>::value<101> = 3;
+
+const int& f()
+{
+ return A<void>::template value<101>;
+}
+}
+
// CHECK-LABEL: define {{.*}}global_var_init
// CHECK: call noundef i32 @_Z1fv
diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp
index e60763feb2e1f..4a2f7c3efdb89 100644
--- a/clang/test/SemaTemplate/class-template-spec.cpp
+++ b/clang/test/SemaTemplate/class-template-spec.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17orlater -std=c++17 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
// RUN: %clang_cc1 -fsyntax-only -triple x86_64-linux-gnu -verify=expected,cxx14 -std=c++14 %s
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++26 %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cxx17orlater -std=c++26 %s
template<typename T, typename U = int> struct A; // expected-note {{template is declared here}} \
// expected-note{{explicitly specialized}}
@@ -259,8 +259,9 @@ namespace VarTemplateNoMember {
template<> template<typename U> constexpr int S<long>::foo;
// In C++14, these are definitions, not declarations, so they get a
// redefinition error.
- // cxx14-error at +2{{redefinition of 'foo'}}
+ // cxx14-error at +3{{redefinition of 'foo'}}
// cxx14-note at -4{{previous definition is here}}
+ // cxx17orlater-error at +1 {{must be initialized by a constant expression}}
template<> template<typename U> constexpr int S<long>::foo;
// cxx14-error at +2{{redefinition of 'foo'}}
// cxx14-note at -2{{previous definition is here}}
@@ -273,3 +274,22 @@ namespace VarTemplateNoMember {
template<> template<typename U> constexpr int S<long>::foo;
} // namespace VarTemplateNoMember
#endif
+
+#if __cplusplus >= 201703L
+namespace ConstexprVarTemplateRedeclared {
+template<typename> struct A;
+struct SS {};
+template<>
+struct A<void> {
+ template<int> static inline SS value = {};
+};
+
+template<> inline constexpr SS A<void>::value<101> = {};
+template<> inline constexpr SS A<void>::value<101>;
+
+const SS& f()
+{
+ return A<void>::value<101>;
+}
+}
+#endif
More information about the cfe-commits
mailing list