Skip to content

Commit e75b58c

Browse files
authored
[Clang][Sema] Do not add implicit 'const' when matching constexpr function template explicit specializations after C++14 (#92449)
Clang incorrectly accepts the following when using C++14 or later: ``` struct A { template<typename T> void f() const; template<> constexpr void f<int>(); }; ``` Non-static member functions declared `constexpr` are only implicitly `const` in C++11. This patch makes clang reject the explicit specialization of `f` in language modes after C++11.
1 parent a91d5c0 commit e75b58c

File tree

4 files changed

+92
-8
lines changed

4 files changed

+92
-8
lines changed

clang/docs/ReleaseNotes.rst

+2
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,8 @@ Bug Fixes to C++ Support
747747
- Clang no longer transforms dependent qualified names into implicit class member access expressions
748748
until it can be determined whether the name is that of a non-static member.
749749
- Clang now correctly diagnoses when the current instantiation is used as an incomplete base class.
750+
- Clang no longer treats ``constexpr`` class scope function template specializations of non-static members
751+
as implicitly ``const`` in language modes after C++11.
750752

751753
Bug Fixes to AST Handling
752754
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaTemplate.cpp

+10-5
Original file line numberDiff line numberDiff line change
@@ -10255,15 +10255,20 @@ bool Sema::CheckFunctionTemplateSpecialization(
1025510255
Ovl->getDeclContext()->getRedeclContext()))
1025610256
continue;
1025710257

10258+
QualType FT = FD->getType();
10259+
// C++11 [dcl.constexpr]p8:
10260+
// A constexpr specifier for a non-static member function that is not
10261+
// a constructor declares that member function to be const.
10262+
//
1025810263
// When matching a constexpr member function template specialization
1025910264
// against the primary template, we don't yet know whether the
1026010265
// specialization has an implicit 'const' (because we don't know whether
1026110266
// it will be a static member function until we know which template it
10262-
// specializes), so adjust it now assuming it specializes this template.
10263-
QualType FT = FD->getType();
10264-
if (FD->isConstexpr()) {
10265-
CXXMethodDecl *OldMD =
10266-
dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl());
10267+
// specializes). This rule was removed in C++14.
10268+
if (auto *NewMD = dyn_cast<CXXMethodDecl>(FD);
10269+
!getLangOpts().CPlusPlus14 && NewMD && NewMD->isConstexpr() &&
10270+
!isa<CXXConstructorDecl, CXXDestructorDecl>(NewMD)) {
10271+
auto *OldMD = dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl());
1026710272
if (OldMD && OldMD->isConst()) {
1026810273
const FunctionProtoType *FPT = FT->castAs<FunctionProtoType>();
1026910274
FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();

clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ struct S {
8989
template<typename T> constexpr T f(); // expected-warning 0-1{{C++14}} expected-note 0-1{{candidate}}
9090
template <typename T>
9191
T g() const; // expected-note-re {{candidate template ignored: could not match 'T (){{( __attribute__\(\(thiscall\)\))?}} const' against 'char (){{( __attribute__\(\(thiscall\)\))?}}'}}
92+
#if __cplusplus >= 201402L
93+
// expected-note@-2 {{candidate template ignored: could not match 'T () const' against 'int ()'}}
94+
#endif
9295
};
9396

9497
// explicit specialization can differ in constepxr
@@ -100,13 +103,17 @@ template <> notlit S::f() const { return notlit(); }
100103
#if __cplusplus >= 201402L
101104
// expected-error@-2 {{no function template matches}}
102105
#endif
103-
template <> constexpr int S::g() { return 0; } // expected-note {{previous}}
106+
template <> constexpr int S::g() { return 0; }
104107
#if __cplusplus < 201402L
105108
// expected-warning@-2 {{C++14}}
109+
// expected-note@-3 {{previous}}
106110
#else
107-
// expected-error@-4 {{does not match any declaration in 'S'}}
111+
// expected-error@-5 {{no function template matches function template specialization 'g'}}
112+
#endif
113+
template <> int S::g() const;
114+
#if __cplusplus < 201402L
115+
// expected-error@-2 {{non-constexpr declaration of 'g<int>' follows constexpr declaration}}
108116
#endif
109-
template <> int S::g() const; // expected-error {{non-constexpr declaration of 'g<int>' follows constexpr declaration}}
110117
// specializations can drop the 'constexpr' but not the implied 'const'.
111118
template <> char S::g() { return 0; } // expected-error {{no function template matches}}
112119
template <> double S::g() const { return 0; } // ok
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify=expected,cxx11 %s
2+
// RUN: %clang_cc1 -fsyntax-only -std=c++14 -verify=expected,since-cxx14 %s
3+
4+
struct A {
5+
template<typename T>
6+
void f0();
7+
8+
template<>
9+
constexpr void f0<short>(); // cxx11-error {{conflicting types for 'f0'}}
10+
// cxx11-note@-1 {{previous declaration is here}}
11+
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
12+
13+
template<typename T>
14+
void f1() const; // since-cxx14-note 2{{candidate template ignored: could not match 'void () const' against 'void ()'}}
15+
16+
template<>
17+
constexpr void f1<short>(); // since-cxx14-error {{no function template matches function template specialization 'f1'}}
18+
// cxx11-warning@-1 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
19+
};
20+
21+
template<>
22+
constexpr void A::f0<long>(); // cxx11-error {{conflicting types for 'f0'}}
23+
// cxx11-note@-1 {{previous declaration is here}}
24+
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
25+
26+
template<>
27+
constexpr void A::f1<long>(); // since-cxx14-error {{no function template matches function template specialization 'f1'}}
28+
// cxx11-warning@-1 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
29+
30+
// FIXME: It's unclear whether [temp.expl.spec]p12 is intended to apply to
31+
// members of a class template explicitly specialized for an implicitly
32+
// instantiated specialization of that template.
33+
template<typename T>
34+
struct B {
35+
void g0(); // since-cxx14-note {{previous declaration is here}}
36+
// cxx11-note@-1 {{member declaration does not match because it is not const qualified}}
37+
38+
void g1() const; // since-cxx14-note {{member declaration does not match because it is const qualified}}
39+
// cxx11-note@-1 {{previous declaration is here}}
40+
41+
template<typename U>
42+
void h0(); // since-cxx14-note {{previous declaration is here}}
43+
44+
template<typename U>
45+
void h1() const; // cxx11-note {{previous declaration is here}}
46+
};
47+
48+
template<>
49+
constexpr void B<short>::g0(); // since-cxx14-error {{constexpr declaration of 'g0' follows non-constexpr declaration}}
50+
// cxx11-error@-1 {{out-of-line declaration of 'g0' does not match any declaration in 'B<short>'}}
51+
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
52+
53+
template<>
54+
constexpr void B<short>::g1(); // since-cxx14-error {{out-of-line declaration of 'g1' does not match any declaration in 'B<short>'}}
55+
// cxx11-error@-1 {{constexpr declaration of 'g1' follows non-constexpr declaration}}
56+
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
57+
58+
template<>
59+
template<typename U>
60+
constexpr void B<long>::h0(); // since-cxx14-error {{constexpr declaration of 'h0' follows non-constexpr declaration}}
61+
// cxx11-error@-1 {{out-of-line declaration of 'h0' does not match any declaration in 'B<long>'}}
62+
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
63+
64+
template<>
65+
template<typename U>
66+
constexpr void B<long>::h1(); // since-cxx14-error {{out-of-line declaration of 'h1' does not match any declaration in 'B<long>'}}
67+
// cxx11-error@-1 {{constexpr declaration of 'h1' follows non-constexpr declaration}}
68+
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
69+
70+

0 commit comments

Comments
 (0)