Author: Utkarsh Saxena
Date: 2022-08-12T12:13:06+02:00
New Revision: 72ac7cac3f14c905426ba7ff0c6c36ee3b3fb375

URL: 
https://github.com/llvm/llvm-project/commit/72ac7cac3f14c905426ba7ff0c6c36ee3b3fb375
DIFF: 
https://github.com/llvm/llvm-project/commit/72ac7cac3f14c905426ba7ff0c6c36ee3b3fb375.diff

LOG: Handle explicitly defaulted consteval special members.

Followup patch for D128083

Previously, using a non-consteval constructor from an consteval constructor 
would code generates the consteval constructor.
Example
```
template <typename T>
struct S {
  T i;
  consteval S() = default;
};
struct Foo {
    Foo() {}
};
void func() {
  S<Foo> three; // incorrectly accepted by clang.
}
```

This happened because clang erroneously disregards `consteval` specifier for a 
`consteval explicitly defaulted special member functions in a class template` 
if it has dependent data members without a `consteval default constructor`.

According to
```
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.
```

Therefore the `consteval defaulted constructor of a class template` should be 
considered `consteval` even if the data members' default constructors are not 
consteval.
Keeping this constructor `consteval` allows complaining while processing the 
call to data member constructors.
(Same applies for other special member functions).

This works fine even when we have more than one default constructors since we 
process the constructors after the templates are instantiated.

This does not address initialization issues raised in
[2602](https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2602) and 
compiler divergence seen in https://godbolt.org/z/va9EMvvMe

Fixes: https://github.com/llvm/llvm-project/issues/51593

Differential Revision: https://reviews.llvm.org/D131479

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/SemaCXX/cxx0x-defaulted-functions.cpp
    clang/test/SemaCXX/cxx2a-consteval.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f2339da1a04be..572961939d859 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -141,7 +141,10 @@ C++20 Feature Support
   `GH54300 <https://github.com/llvm/llvm-project/issues/54300>`_,
   `GH54301 <https://github.com/llvm/llvm-project/issues/54301>`_,
   and `GH49430 <https://github.com/llvm/llvm-project/issues/49430>`_.
-
+- 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
index ab3af1298be51..78c39197e47bb 100644
--- 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 @@ bool 
Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD,
   // 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<CXXDestructorDecl>(MD)
                                   : isa<CXXConstructorDecl>(MD))) &&

diff  --git a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp 
b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp
index 73916fd3027e3..0c3dd1ea7aa27 100644
--- a/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp
+++ b/clang/test/SemaCXX/cxx0x-defaulted-functions.cpp
@@ -44,18 +44,20 @@ void tester() {
 }
 
 template<typename T> 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<lit> s_lit; // ok
 S<bar> s_bar; // ok
 
 struct Friends {
-  friend S<bar>::S();
-  friend S<bar>::S(const S&);
-  friend S<bar>::S(S&&);
+  // FIXME: these error may or may not be correct; there is an open question on
+  // the CWG reflectors about this.
+  friend S<bar>::S();          // expected-error {{non-constexpr declaration 
of 'S' follows constexpr declaration}}
+  friend S<bar>::S(const S&);  // expected-error {{non-constexpr declaration 
of 'S' follows constexpr declaration}}
+  friend S<bar>::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
index df2a9925c0154..8438413b6f4a8 100644
--- a/clang/test/SemaCXX/cxx2a-consteval.cpp
+++ b/clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -766,3 +766,102 @@ void test() {
   static_assert(c == 8);
 }
 }
+
+namespace defaulted_special_member_template {
+template <typename T>
+struct default_ctor {
+  T data;
+  consteval default_ctor() = default; // expected-note {{non-constexpr 
constructor 'foo' cannot be used in a constant expression}}
+};
+
+template <typename T>
+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 <typename T>
+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<foo> fail0; // expected-error {{call to consteval function 
'defaulted_special_member_template::default_ctor<defaulted_special_member_template::foo>::default_ctor'
 is not a constant expression}} \
+                              expected-note {{in call to 'default_ctor()'}}
+
+  copy<foo> good0;
+  copy<foo> fail1{good0}; // expected-error {{call to consteval function 
'defaulted_special_member_template::copy<defaulted_special_member_template::foo>::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<defaulted_special_member_template::foo>::operator='
 is not a constant expression}} \
+                             expected-note {{in call to 
'&fail1->operator=(good0)'}}
+
+  move<foo> good1;
+  move<foo> fail2{static_cast<move<foo>&&>(good1)}; // expected-error {{call 
to consteval function 
'defaulted_special_member_template::move<defaulted_special_member_template::foo>::move'
 is not a constant expression}} \
+                                                       expected-note {{in call 
to 'move(good1)'}}
+  fail2 = static_cast<move<foo>&&>(good1);          // expected-error {{call 
to consteval function 
'defaulted_special_member_template::move<defaulted_special_member_template::foo>::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 <typename T, unsigned N>
+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<Foo, 1> s1;
+  S<Bar, 1> s2;
+  // User provided constructor.
+  S<Foo, 2> s3;
+  S<Bar, 2> s4;
+  // Consteval explictly defaulted constructor.
+  S<Foo, 3> s5; // expected-error {{call to consteval function 
'multiple_default_constructors::S<multiple_default_constructors::Foo, 3>::S' is 
not a constant expression}} \
+                   expected-note {{in call to 'S()'}}
+  S<Bar, 3> s6;
+  S<Baz, 3> s7;
+}
+
+consteval int aConstevalFunction() { // expected-error {{consteval function 
never produces a constant expression}}
+  // Defaulted default constructors are implicitly consteval.
+  S<Bar, 1> s1;
+
+  S<Baz, 2> s4; // expected-note {{non-constexpr constructor 'S' cannot be 
used in a constant expression}}
+
+  S<Bar, 3> s2;
+  S<Baz, 3> s3;
+  return 0;
+}
+
+} // namespace multiple_default_constructors


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to