Author: Krystian Stasiowski Date: 2024-05-20T14:50:58-04:00 New Revision: e75b58cfc666fc168d05580d2b7fd274830a4dd0
URL: https://github.com/llvm/llvm-project/commit/e75b58cfc666fc168d05580d2b7fd274830a4dd0 DIFF: https://github.com/llvm/llvm-project/commit/e75b58cfc666fc168d05580d2b7fd274830a4dd0.diff LOG: [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. Added: clang/test/CXX/temp/temp.spec/temp.expl.spec/p12.cpp Modified: clang/docs/ReleaseNotes.rst clang/lib/Sema/SemaTemplate.cpp clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p1.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 88616b5bee732..81e9d0423f96a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -747,6 +747,8 @@ Bug Fixes to C++ Support - Clang no longer transforms dependent qualified names into implicit class member access expressions until it can be determined whether the name is that of a non-static member. - Clang now correctly diagnoses when the current instantiation is used as an incomplete base class. +- Clang no longer treats ``constexpr`` class scope function template specializations of non-static members + as implicitly ``const`` in language modes after C++11. Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index de884260790cc..02d9b64c2b14b 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -10255,15 +10255,20 @@ bool Sema::CheckFunctionTemplateSpecialization( Ovl->getDeclContext()->getRedeclContext())) continue; + QualType FT = FD->getType(); + // C++11 [dcl.constexpr]p8: + // A constexpr specifier for a non-static member function that is not + // a constructor declares that member function to be const. + // // When matching a constexpr member function template specialization // against the primary template, we don't yet know whether the // specialization has an implicit 'const' (because we don't know whether // it will be a static member function until we know which template it - // specializes), so adjust it now assuming it specializes this template. - QualType FT = FD->getType(); - if (FD->isConstexpr()) { - CXXMethodDecl *OldMD = - dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl()); + // specializes). This rule was removed in C++14. + if (auto *NewMD = dyn_cast<CXXMethodDecl>(FD); + !getLangOpts().CPlusPlus14 && NewMD && NewMD->isConstexpr() && + !isa<CXXConstructorDecl, CXXDestructorDecl>(NewMD)) { + auto *OldMD = dyn_cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl()); if (OldMD && OldMD->isConst()) { const FunctionProtoType *FPT = FT->castAs<FunctionProtoType>(); FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo(); 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 788e93b56bb38..9e890204c78bd 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 @@ -89,6 +89,9 @@ struct S { template<typename T> constexpr T f(); // expected-warning 0-1{{C++14}} expected-note 0-1{{candidate}} template <typename T> T g() const; // expected-note-re {{candidate template ignored: could not match 'T (){{( __attribute__\(\(thiscall\)\))?}} const' against 'char (){{( __attribute__\(\(thiscall\)\))?}}'}} +#if __cplusplus >= 201402L + // expected-note@-2 {{candidate template ignored: could not match 'T () const' against 'int ()'}} +#endif }; // explicit specialization can diff er in constepxr @@ -100,13 +103,17 @@ template <> notlit S::f() const { return notlit(); } #if __cplusplus >= 201402L // expected-error@-2 {{no function template matches}} #endif -template <> constexpr int S::g() { return 0; } // expected-note {{previous}} +template <> constexpr int S::g() { return 0; } #if __cplusplus < 201402L // expected-warning@-2 {{C++14}} +// expected-note@-3 {{previous}} #else -// expected-error@-4 {{does not match any declaration in 'S'}} +// expected-error@-5 {{no function template matches function template specialization 'g'}} +#endif +template <> int S::g() const; +#if __cplusplus < 201402L +// expected-error@-2 {{non-constexpr declaration of 'g<int>' follows constexpr declaration}} #endif -template <> int S::g() const; // expected-error {{non-constexpr declaration of 'g<int>' follows constexpr declaration}} // specializations can drop the 'constexpr' but not the implied 'const'. template <> char S::g() { return 0; } // expected-error {{no function template matches}} template <> double S::g() const { return 0; } // ok 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 new file mode 100644 index 0000000000000..2a57489083695 --- /dev/null +++ b/clang/test/CXX/temp/temp.spec/temp.expl.spec/p12.cpp @@ -0,0 +1,70 @@ +// 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(); + + template<> + constexpr void f0<short>(); // cxx11-error {{conflicting types for 'f0'}} + // cxx11-note@-1 {{previous declaration is here}} + // cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}} + + template<typename T> + void f1() const; // since-cxx14-note 2{{candidate template ignored: could not match 'void () const' against 'void ()'}} + + template<> + constexpr void f1<short>(); // since-cxx14-error {{no function template matches function template specialization 'f1'}} + // cxx11-warning@-1 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}} +}; + +template<> +constexpr void A::f0<long>(); // cxx11-error {{conflicting types for 'f0'}} + // cxx11-note@-1 {{previous declaration is here}} + // cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}} + +template<> +constexpr void A::f1<long>(); // since-cxx14-error {{no function template matches function template specialization 'f1'}} + // cxx11-warning@-1 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}} + +// FIXME: It's unclear whether [temp.expl.spec]p12 is intended to apply to +// members of a class template explicitly specialized for an implicitly +// instantiated specialization of that template. +template<typename T> +struct B { + void g0(); // since-cxx14-note {{previous declaration is here}} + // cxx11-note@-1 {{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@-1 {{previous declaration is here}} + + template<typename U> + void h0(); // since-cxx14-note {{previous declaration is here}} + + template<typename U> + void h1() const; // cxx11-note {{previous declaration is here}} +}; + +template<> +constexpr void B<short>::g0(); // since-cxx14-error {{constexpr declaration of 'g0' follows non-constexpr declaration}} + // cxx11-error@-1 {{out-of-line declaration of 'g0' does not match any declaration in 'B<short>'}} + // cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}} + +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@-1 {{constexpr declaration of 'g1' follows non-constexpr declaration}} + // cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}} + +template<> +template<typename U> +constexpr void B<long>::h0(); // since-cxx14-error {{constexpr declaration of 'h0' follows non-constexpr declaration}} + // cxx11-error@-1 {{out-of-line declaration of 'h0' does not match any declaration in 'B<long>'}} + // cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}} + +template<> +template<typename U> +constexpr void B<long>::h1(); // since-cxx14-error {{out-of-line declaration of 'h1' does not match any declaration in 'B<long>'}} + // cxx11-error@-1 {{constexpr declaration of 'h1' follows non-constexpr declaration}} + // cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}} + + _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits