Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -435,7 +435,10 @@ - No longer attempt to evaluate a consteval UDL function call at runtime when it is called through a template instantiation. This fixes `Issue 54578 `_. - +- Properly determine whether an instantiated, explicitly defaulted constexpr or + consteval special member function is still considered to be constexpr or + consteval instead of calculating it purely from the bases and fields of the + class being instantiated. - Implemented ``__builtin_source_location()``, which enables library support for ``std::source_location``. Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -1303,11 +1303,21 @@ // C++11 [dcl.constexpr]p4: // -- every constructor involved in initializing non-static data // members [...] shall be a constexpr constructor + // 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 (!Field->hasInClassInitializer() && !FieldRec->hasConstexprDefaultConstructor() && !isUnion()) // The standard requires any in-class initializer to be a constant // expression. We consider this to be a defect. - data().DefaultedDefaultConstructorIsConstexpr = false; + data().DefaultedDefaultConstructorIsConstexpr = + isa(this) + ? hasConstexprDefaultConstructor() + : false; // C++11 [class.copy]p8: // The implicitly-declared copy constructor for a class X will have Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -7544,6 +7544,17 @@ // FIXME: This should not apply if the member is deleted. bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, RD, CSM, HasConstParam); + + // 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 (!Constexpr && isa(RD)) + Constexpr = MD->isConstexpr(); + if ((getLangOpts().CPlusPlus20 || (getLangOpts().CPlusPlus14 ? !isa(MD) : isa(MD))) && Index: clang/test/SemaCXX/cxx0x-defaulted-functions.cpp =================================================================== --- clang/test/SemaCXX/cxx0x-defaulted-functions.cpp +++ 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 { Index: clang/test/SemaCXX/cxx2a-consteval.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-consteval.cpp +++ 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