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
