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@+2{{redefinition of 'foo'}}
+  // cxx14-error@+3{{redefinition of 'foo'}}
   // cxx14-note@-4{{previous definition is here}}
+  // cxx17orlater-error@+1 {{must be initialized by a constant expression}}
   template<> template<typename U> constexpr int S<long>::foo;
   // cxx14-error@+2{{redefinition of 'foo'}}
   // cxx14-note@-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


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to