diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17,6 +17,7 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ComparisonCategories.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecordLayout.h" @@ -7202,10 +7203,24 @@ static bool defaultedSpecialMemberIsConstexpr( Sema &S, CXXRecordDecl *ClassDecl, Sema::CXXSpecialMember CSM, bool ConstArg, CXXConstructorDecl *InheritedCtor = nullptr, - Sema::InheritedConstructorInfo *Inherited = nullptr) { + Sema::InheritedConstructorInfo *Inherited = nullptr, + CXXMethodDecl *SpecialMemberDecl = nullptr) { if (!S.getLangOpts().CPlusPlus11) return false; + // C++14 [dcl.constexpr]p6 (CWG DR647/CWG DR1358): + // If the instantiated template specialization of a constexpr function + // template or member function of a class template would fail to satisfy + // the requirements for a constexpr function or constexpr constructor, that + // specialization is still a constexpr function or constexpr constructor, + // even though a call to such a function cannot appear in a constant + // expression. + if (SpecialMemberDecl && + (isa(ClassDecl) || + SpecialMemberDecl->isFunctionTemplateSpecialization()) + && SpecialMemberDecl->isConstexpr()) + return true; + // C++11 [dcl.constexpr]p4: // In the definition of a constexpr constructor [...] bool Ctor = true; @@ -7537,8 +7552,8 @@ // destructors in C++14 and C++17), this is checked elsewhere. // // FIXME: This should not apply if the member is deleted. - bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM, - HasConstParam); + bool Constexpr = defaultedSpecialMemberIsConstexpr( + *this, RD, CSM, HasConstParam, nullptr, nullptr, MD); if ((getLangOpts().CPlusPlus20 || (getLangOpts().CPlusPlus14 ? !isa(MD) : isa(MD))) && diff --git a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp --- a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp +++ b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp @@ -44,18 +44,20 @@ } template struct S : T { - constexpr S() = default; - constexpr S(const S&) = default; - constexpr S(S&&) = default; + constexpr S() = default; // expected-note {{previous declaration is here}} + constexpr S(const S&) = default; // expected-note {{previous declaration is here}} + constexpr S(S&&) = default; // expected-note {{previous declaration is here}} }; struct lit { constexpr lit() {} }; S s_lit; // ok S s_bar; // ok struct Friends { - friend S::S(); - friend S::S(const S&); - friend S::S(S&&); + // FIXME: these error may or may not be correct; there is an open question on + // the CWG reflectors about this. + friend S::S(); // expected-error {{non-constexpr declaration of 'S' follows constexpr declaration}} + friend S::S(const S&); // expected-error {{non-constexpr declaration of 'S' follows constexpr declaration}} + friend S::S(S&&); // expected-error {{non-constexpr declaration of 'S' follows constexpr declaration}} }; namespace DefaultedFnExceptionSpec { diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp --- a/clang/test/SemaCXX/cxx2a-consteval.cpp +++ b/clang/test/SemaCXX/cxx2a-consteval.cpp @@ -766,3 +766,55 @@ static_assert(c == 8); } } + +namespace DefaultedTemp { +template +struct default_ctor { + T data; + consteval default_ctor() = default; // expected-note {{non-constexpr constructor 'foo' cannot be used in a constant expression}} +}; + +template +struct copy { + T data; + + consteval copy(const copy &) = default; // expected-note {{non-constexpr constructor 'foo' cannot be used in a constant expression}} + consteval copy &operator=(const copy &) = default; // expected-note {{non-constexpr function 'operator=' cannot be used in a constant expression}} + copy() = default; +}; + +template +struct move { + T data; + + consteval move(move &&) = default; // expected-note {{non-constexpr constructor 'foo' cannot be used in a constant expression}} + consteval move &operator=(move &&) = default; // expected-note {{non-constexpr function 'operator=' cannot be used in a constant expression}} + move() = default; +}; + +struct foo { + foo() {} // expected-note {{declared here}} + foo(const foo &) {} // expected-note {{declared here}} + foo(foo &&) {} // expected-note {{declared here}} + + foo& operator=(const foo &) { return *this; } // expected-note {{declared here}} + foo& operator=(foo &&) { return *this; } // expected-note {{declared here}} +}; + +void func() { + default_ctor fail0; // expected-error {{call to consteval function 'DefaultedTemp::default_ctor::default_ctor' is not a constant expression}} \ + expected-note {{in call to 'default_ctor()'}} + + copy good0; + copy fail1{good0}; // expected-error {{call to consteval function 'DefaultedTemp::copy::copy' is not a constant expression}} \ + expected-note {{in call to 'copy(good0)'}} + fail1 = good0; // expected-error {{call to consteval function 'DefaultedTemp::copy::operator=' is not a constant expression}} \ + expected-note {{in call to '&fail1->operator=(good0)'}} + + move good1; + move fail2{static_cast&&>(good1)}; // expected-error {{call to consteval function 'DefaultedTemp::move::move' is not a constant expression}} \ + expected-note {{in call to 'move(good1)'}} + fail2 = static_cast&&>(good1); // expected-error {{call to consteval function 'DefaultedTemp::move::operator=' is not a constant expression}} \ + expected-note {{in call to '&fail2->operator=(good1)'}} +} +} // namespace DefaultedTemp