[clang] [Clang] Allow explicit member specialization to differ from template declaration wrt constexpr (PR #145272)

Vikram Hegde via cfe-commits cfe-commits at lists.llvm.org
Sun Jun 22 23:00:35 PDT 2025


https://github.com/vikramRH created https://github.com/llvm/llvm-project/pull/145272

This attempts to fix https://github.com/llvm/llvm-project/issues/26078. However I have couple of fundamental questions with the 7.1.5 consexpr note
"[ Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. — end note ]"

1. Does it also apply to member specializations which are not function templates themselves ? (as in the example https://godbolt.org/z/c4bYY8rxb)
2. if yes, then should it also apply to special member functions ? (constructors, destructors)

>From f525e34feaf519b1951733034dd71893aa79ae53 Mon Sep 17 00:00:00 2001
From: vikhegde <vikram.hegde at amd.com>
Date: Mon, 23 Jun 2025 10:39:03 +0530
Subject: [PATCH] [Clang] Allow explicit member specialization to differ from
 template declaration wrt constexpr

---
 clang/lib/Sema/SemaDecl.cpp                     | 17 +++++++++++++++++
 .../CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp   |  6 ++++++
 .../CXX/temp/temp.spec/temp.expl.spec/p12.cpp   | 13 ++++---------
 3 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 5cffd82e3372e..2ceb277eedcf5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -12163,6 +12163,23 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
     }
   }
 
+  // C++11 [dcl.constexpr]p1: An explicit specialization of a constexpr
+  // function can differ from the template declaration with respect to
+  // the constexpr specifier.
+  if (IsMemberSpecialization) {
+    FunctionDecl *InstantiationFunction =
+        OldDecl ? OldDecl->getAsFunction() : nullptr;
+    if (InstantiationFunction &&
+        InstantiationFunction->getTemplateSpecializationKind() ==
+            TSK_ImplicitInstantiation &&
+        (NewFD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization ||
+         NewFD->getTemplateSpecializationKind() == TSK_Undeclared)) {
+      if (InstantiationFunction->getConstexprKind() !=
+          NewFD->getConstexprKind())
+        InstantiationFunction->setConstexprKind(NewFD->getConstexprKind());
+    }
+  }
+
   if (Redeclaration) {
     // NewFD and OldDecl represent declarations that need to be
     // merged.
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
index 752451f3d9749..225adb2228a03 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp
@@ -94,7 +94,13 @@ struct S {
 #endif
 };
 
+template<typename T>
+struct P {
+  constexpr T h() const { return 1;}
+};
+
 // explicit specialization can differ in constepxr
+template <> int P<int>::h() const { return 0; }
 template <> notlit ft(notlit nl) { return nl; }
 template <> char ft(char c) { return c; } // expected-note {{previous}}
 template <> constexpr char ft(char nl); // expected-error {{constexpr declaration of 'ft<char>' follows non-constexpr declaration}}
diff --git a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p12.cpp b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p12.cpp
index 9717fbf419b0a..af2b17ea2c089 100644
--- a/clang/test/CXX/temp/temp.spec/temp.expl.spec/p12.cpp
+++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p12.cpp
@@ -1,6 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify=expected,cxx11 %s
 // RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify=expected,since-cxx14 %s
-
 struct A {
   template<typename T>
   void f0();
@@ -32,11 +31,9 @@ constexpr void A::f1<long>(); // since-cxx14-error {{no function template matche
 // instantiated specialization of that template.
 template<typename T>
 struct B { // #defined-here
-  void g0(); // since-cxx14-note {{previous declaration is here}}
-             // cxx11-note at -1 {{member declaration does not match because it is not const qualified}}
+  void g0(); // cxx11-note {{member declaration does not match because it is not const qualified}}
 
   void g1() const; // since-cxx14-note {{member declaration does not match because it is const qualified}}
-                   // cxx11-note at -1 {{previous declaration is here}}
 
   template<typename U>
   void h0(); // since-cxx14-note {{previous declaration is here}}
@@ -46,15 +43,13 @@ struct B { // #defined-here
 };
 
 template<>
-constexpr void B<short>::g0(); // since-cxx14-error {{constexpr declaration of 'g0' follows non-constexpr declaration}}
-                               // cxx11-error at -1 {{out-of-line declaration of 'g0' does not match any declaration in 'B<short>'}}
-                               // cxx11-warning at -2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
+constexpr void B<short>::g0(); // cxx11-error {{out-of-line declaration of 'g0' does not match any declaration in 'B<short>'}}
+                               // cxx11-warning at -1 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
                                // expected-note@#defined-here {{defined here}}
 
 template<>
 constexpr void B<short>::g1(); // since-cxx14-error {{out-of-line declaration of 'g1' does not match any declaration in 'B<short>'}}
-                               // cxx11-error at -1 {{constexpr declaration of 'g1' follows non-constexpr declaration}}
-                               // cxx11-warning at -2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
+                               // cxx11-warning at -1 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
                                // expected-note@#defined-here {{defined here}}
 
 template<>



More information about the cfe-commits mailing list