diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -141,7 +141,10 @@ `GH54300 `_, `GH54301 `_, and `GH49430 `_. - +- Consider explicitly defaulted constexpr/consteval special member function + template instantiation to be constexpr/consteval even though a call to such + a function cannot appear in a constant expression. + (C++14 [dcl.constexpr]p6 (CWG DR647/CWG DR1358)) 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" @@ -7539,6 +7540,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 (MD->isTemplateInstantiation() && MD->isConstexpr()) + Constexpr = true; + 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,102 @@ static_assert(c == 8); } } + +namespace defaulted_special_member_template { +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 'defaulted_special_member_template::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 'defaulted_special_member_template::copy::copy' is not a constant expression}} \ + expected-note {{in call to 'copy(good0)'}} + fail1 = good0; // expected-error {{call to consteval function 'defaulted_special_member_template::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 'defaulted_special_member_template::move::move' is not a constant expression}} \ + expected-note {{in call to 'move(good1)'}} + fail2 = static_cast&&>(good1); // expected-error {{call to consteval function 'defaulted_special_member_template::move::operator=' is not a constant expression}} \ + expected-note {{in call to '&fail2->operator=(good1)'}} +} +} // namespace defaulted_special_member_template + +namespace multiple_default_constructors { +struct Foo { + Foo() {} // expected-note {{declared here}} +}; +struct Bar { + Bar() = default; +}; +struct Baz { + consteval Baz() {} +}; + +template +struct S { + T data; + S() requires (N==1) = default; + // This cannot be used in constexpr context. + S() requires (N==2) {} // expected-note {{declared here}} + consteval S() requires (N==3) = default; // expected-note {{non-constexpr constructor 'Foo' cannot be used in a constant expression}} +}; + +void func() { + // Explictly defaulted constructor. + S s1; + S s2; + // User provided constructor. + S s3; + S s4; + // Consteval explictly defaulted constructor. + S s5; // expected-error {{call to consteval function 'multiple_default_constructors::S::S' is not a constant expression}} \ + expected-note {{in call to 'S()'}} + S s6; + S s7; +} + +consteval int aConstevalFunction() { // expected-error {{consteval function never produces a constant expression}} + // Defaulted default constructors are implicitly consteval. + S s1; + + S s4; // expected-note {{non-constexpr constructor 'S' cannot be used in a constant expression}} + + S s2; + S s3; + return 0; +} + +} // namespace multiple_default_constructors