[clang] 180581c - [clang] Add support for consteval constructors

via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 20 03:34:18 PDT 2020


Author: Tyker
Date: 2020-03-20T11:33:54+01:00
New Revision: 180581cfcf5115388caa3b8ee20eac1fd35f2d11

URL: https://github.com/llvm/llvm-project/commit/180581cfcf5115388caa3b8ee20eac1fd35f2d11
DIFF: https://github.com/llvm/llvm-project/commit/180581cfcf5115388caa3b8ee20eac1fd35f2d11.diff

LOG: [clang] Add support for consteval constructors

Summary:
Changes:
 - handle immediate invocations for constructors.
 - add tests

after this patch i believe the implementation of consteval is nearly standard compliant, but IR-gen still needs to be taught not to emit consteval declarations.

Reviewers: rsmith

Reviewed By: rsmith

Subscribers: wchilders

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

Added: 
    

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaInit.cpp
    clang/lib/Sema/TreeTransform.h
    clang/test/SemaCXX/cxx2a-consteval.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f777e0ae4c81..92af99fe5719 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2466,7 +2466,7 @@ def err_invalid_consteval_take_address : Error<
   "cannot take address of consteval function %0 outside"
   " of an immediate invocation">;
 def err_invalid_consteval_call : Error<
-  "call to consteval function '%q0' is not a constant expression">;
+  "call to consteval function %q0 is not a constant expression">;
 def err_invalid_consteval_decl_kind : Error<
   "%0 cannot be declared consteval">;
 def err_invalid_constexpr : Error<

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 2cc770e5f3d1..37a5be48b97b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -14762,12 +14762,14 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
   if (getLangOpts().CUDA && !CheckCUDACall(ConstructLoc, Constructor))
     return ExprError();
 
-  return CXXConstructExpr::Create(
-      Context, DeclInitType, ConstructLoc, Constructor, Elidable,
-      ExprArgs, HadMultipleCandidates, IsListInitialization,
-      IsStdInitListInitialization, RequiresZeroInit,
-      static_cast<CXXConstructExpr::ConstructionKind>(ConstructKind),
-      ParenRange);
+  return CheckForImmediateInvocation(
+      CXXConstructExpr::Create(
+          Context, DeclInitType, ConstructLoc, Constructor, Elidable, ExprArgs,
+          HadMultipleCandidates, IsListInitialization,
+          IsStdInitListInitialization, RequiresZeroInit,
+          static_cast<CXXConstructExpr::ConstructionKind>(ConstructKind),
+          ParenRange),
+      Constructor);
 }
 
 ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c9de06ce76cb..eaded8e92d7c 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -15403,6 +15403,8 @@ static void EvaluateAndDiagnoseImmediateInvocation(
                                            SemaRef.getASTContext(), true);
   if (!Result || !Notes.empty()) {
     Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit();
+    if (auto *FunctionalCast = dyn_cast<CXXFunctionalCastExpr>(InnerExpr))
+      InnerExpr = FunctionalCast->getSubExpr();
     FunctionDecl *FD = nullptr;
     if (auto *Call = dyn_cast<CallExpr>(InnerExpr))
       FD = cast<FunctionDecl>(Call->getCalleeDecl());
@@ -15473,8 +15475,25 @@ static void RemoveNestedImmediateInvocation(
     }
     bool AlwaysRebuild() { return false; }
     bool ReplacingOriginal() { return true; }
+    bool AllowSkippingCXXConstructExpr() {
+      bool Res = AllowSkippingFirstCXXConstructExpr;
+      AllowSkippingFirstCXXConstructExpr = true;
+      return Res;
+    }
+    bool AllowSkippingFirstCXXConstructExpr = true;
   } Transformer(SemaRef, Rec.ReferenceToConsteval,
                 Rec.ImmediateInvocationCandidates, It);
+
+  /// CXXConstructExpr with a single argument are getting skipped by
+  /// TreeTransform in some situtation because they could be implicit. This
+  /// can only occur for the top-level CXXConstructExpr because it is used
+  /// nowhere in the expression being transformed therefore will not be rebuilt.
+  /// Setting AllowSkippingFirstCXXConstructExpr to false will prevent from
+  /// skipping the first CXXConstructExpr.
+  if (auto *OldExpr =
+          dyn_cast<CXXConstructExpr>(It->getPointer()->IgnoreImplicit()))
+    Transformer.AllowSkippingFirstCXXConstructExpr = false;
+
   ExprResult Res = Transformer.TransformExpr(It->getPointer()->getSubExpr());
   assert(Res.isUsable());
   Res = SemaRef.MaybeCreateExprWithCleanups(Res);

diff  --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 0e7b839f69dd..cefedc63c341 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -6434,12 +6434,14 @@ PerformConstructorInitialization(Sema &S,
     }
     S.MarkFunctionReferenced(Loc, Constructor);
 
-    CurInit = CXXTemporaryObjectExpr::Create(
-        S.Context, Constructor,
-        Entity.getType().getNonLValueExprType(S.Context), TSInfo,
-        ConstructorArgs, ParenOrBraceRange, HadMultipleCandidates,
-        IsListInitialization, IsStdInitListInitialization,
-        ConstructorInitRequiresZeroInit);
+    CurInit = S.CheckForImmediateInvocation(
+        CXXTemporaryObjectExpr::Create(
+            S.Context, Constructor,
+            Entity.getType().getNonLValueExprType(S.Context), TSInfo,
+            ConstructorArgs, ParenOrBraceRange, HadMultipleCandidates,
+            IsListInitialization, IsStdInitListInitialization,
+            ConstructorInitRequiresZeroInit),
+        Constructor);
   } else {
     CXXConstructExpr::ConstructionKind ConstructKind =
       CXXConstructExpr::CK_Complete;

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e803300bbf16..9186ef0deb2a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -157,6 +157,13 @@ class TreeTransform {
   /// existing lambdas.
   bool ReplacingOriginal() { return false; }
 
+  /// Wether CXXConstructExpr can be skipped when they are implicit.
+  /// They will be reconstructed when used if needed.
+  /// This is usefull when the user that cause rebuilding of the
+  /// CXXConstructExpr is outside of the expression at which the TreeTransform
+  /// started.
+  bool AllowSkippingCXXConstructExpr() { return true; }
+
   /// Returns the location of the entity being transformed, if that
   /// information was not available elsewhere in the AST.
   ///
@@ -11658,10 +11665,11 @@ TreeTransform<Derived>::TransformCXXConstructExpr(CXXConstructExpr *E) {
   // CXXConstructExprs other than for list-initialization and
   // CXXTemporaryObjectExpr are always implicit, so when we have
   // a 1-argument construction we just transform that argument.
-  if ((E->getNumArgs() == 1 ||
-       (E->getNumArgs() > 1 && getDerived().DropCallArgument(E->getArg(1)))) &&
-      (!getDerived().DropCallArgument(E->getArg(0))) &&
-      !E->isListInitialization())
+  if (getDerived().AllowSkippingCXXConstructExpr() &&
+      ((E->getNumArgs() == 1 ||
+        (E->getNumArgs() > 1 && getDerived().DropCallArgument(E->getArg(1)))) &&
+       (!getDerived().DropCallArgument(E->getArg(0))) &&
+       !E->isListInitialization()))
     return getDerived().TransformExpr(E->getArg(0));
 
   TemporaryBase Rebase(*this, /*FIXME*/ E->getBeginLoc(), DeclarationName());

diff  --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp
index 954f424f7d9a..a1716b4fa8c3 100644
--- a/clang/test/SemaCXX/cxx2a-consteval.cpp
+++ b/clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -260,6 +260,19 @@ auto l1 = [](int i) constexpr {
 
 }
 
+namespace std {
+
+template <typename T> struct remove_reference { using type = T; };
+template <typename T> struct remove_reference<T &> { using type = T; };
+template <typename T> struct remove_reference<T &&> { using type = T; };
+
+template <typename T>
+constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
+  return static_cast<typename std::remove_reference<T>::type &&>(t);
+}
+
+}
+
 namespace temporaries {
 
 struct A {
@@ -295,12 +308,12 @@ void test() {
   { int k = const_a_ref(A()); }
   { int k = const_a_ref(a); }
   { int k = rvalue_ref(A()); }
-  { int k = rvalue_ref(static_cast<const A&&>(a)); }
+  { int k = rvalue_ref(std::move(a)); }
   { int k = const_a_ref(A().ret_a()); }
   { int k = const_a_ref(to_lvalue_ref(A().ret_a())); }
-  { int k = const_a_ref(to_lvalue_ref(static_cast<const A&&>(a))); }
+  { int k = const_a_ref(to_lvalue_ref(std::move(a))); }
   { int k = by_value_a(A().ret_a()); }
-  { int k = by_value_a(to_lvalue_ref(static_cast<const A&&>(a))); }
+  { int k = by_value_a(to_lvalue_ref(std::move(a))); }
   { int k = (A().ret_a(), A().ret_i()); }
   { int k = (const_a_ref(A().ret_a()), A().ret_i()); }//
 }
@@ -353,10 +366,10 @@ void test() {
   { int k = const_a_ref(A()); }
   { int k = const_a_ref(a); }
   { int k = rvalue_ref(A()); }
-  { int k = rvalue_ref(static_cast<const A&&>(a)); }
+  { int k = rvalue_ref(std::move(a)); }
   { int k = const_a_ref(A().ret_a()); }
   { int k = const_a_ref(to_lvalue_ref(A().ret_a())); }
-  { int k = const_a_ref(to_lvalue_ref(static_cast<const A&&>(a))); }
+  { int k = const_a_ref(to_lvalue_ref(std::move(a))); }
   { int k = by_value_a(A().ret_a()); }
   { int k = by_value_a(to_lvalue_ref(static_cast<const A&&>(a))); }
   { int k = (A().ret_a(), A().ret_i()); }// expected-error {{is not a constant expression}}
@@ -388,6 +401,27 @@ void test() {
   // expected-note at -1 {{is not a constant expression}} expected-note at -1 {{temporary created here}}
 }
 
+struct S1 {
+  S1* ptr = nullptr;
+  consteval S1(int i) : ptr(this) {
+    if (this == ptr && i)
+      ptr = nullptr;
+  }
+  constexpr ~S1() {}
+};
+
+void test1() {
+  S1 s(1);
+  s = S1(1);
+  s = S1(0); // expected-error {{is not a constant expression}}
+  // expected-note at -1 {{is not a constant expression}} expected-note at -1 {{temporary created here}}
+}
+
+}
+namespace ctor {
+
+consteval int f_eval() { // expected-note+ {{declared here}}
+  return 0;
 }
 
 namespace std {
@@ -441,3 +475,103 @@ namespace override {
     };
   }
 }
+
+struct A {
+  int(*ptr)();
+  consteval A(int(*p)() = nullptr) : ptr(p) {}
+};
+
+struct B {
+  int(*ptr)();
+  B() : ptr(nullptr) {}
+  consteval B(int(*p)(), int) : ptr(p) {}
+};
+
+void test() {
+  { A a; }
+  { A a(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { B b(nullptr, 0); }
+  { B b(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { A a{}; }
+  { A a{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { B b{nullptr, 0}; }
+  { B b{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { A a = A(); }
+  { A a = A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { B b = B(nullptr, 0); }
+  { B b = B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { A a = A{}; }
+  { A a = A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { B b = B{nullptr, 0}; }
+  { B b = B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { A a; a = A(); }
+  { A a; a = A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { B b; b = B(nullptr, 0); }
+  { B b; b = B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { A a; a = A{}; }
+  { A a; a = A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { B b; b = B{nullptr, 0}; }
+  { B b; b = B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { A* a; a = new A(); }
+  { A* a; a = new A(&f_eval); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { B* b; b = new B(nullptr, 0); }
+  { B* b; b = new B(&f_eval, 0); } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { A* a; a = new A{}; }
+  { A* a; a = new A{&f_eval}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { B* b; b = new B{nullptr, 0}; }
+  { B* b; b = new B{&f_eval, 0}; } // expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+}
+
+}
+
+namespace copy_ctor {
+
+consteval int f_eval() { // expected-note+ {{declared here}}
+  return 0;
+}
+
+struct Copy {
+  int(*ptr)();
+  constexpr Copy(int(*p)() = nullptr) : ptr(p) {}
+  consteval Copy(const Copy&) = default;
+};
+
+constexpr const Copy &to_lvalue_ref(const Copy &&a) {
+  return a;
+}
+
+void test() {
+  constexpr const Copy C;
+  // there is no the copy constructor call when its argument is a prvalue because of garanteed copy elision.
+  // so we need to test with both prvalue and xvalues.
+  { Copy c(C); }
+  { Copy c((Copy(&f_eval))); }// expected-error {{cannot take address of consteval}}
+  { Copy c(std::move(C)); }
+  { Copy c(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy c(to_lvalue_ref((Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy c(to_lvalue_ref(std::move(C))); }
+  { Copy c(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy c = Copy(C); }
+  { Copy c = Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}}
+  { Copy c = Copy(std::move(C)); }
+  { Copy c = Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy c = Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy c = Copy(to_lvalue_ref(std::move(C))); }
+  { Copy c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy c; c = Copy(C); }
+  { Copy c; c = Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}}
+  { Copy c; c = Copy(std::move(C)); }
+  { Copy c; c = Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy c; c = Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy c; c = Copy(to_lvalue_ref(std::move(C))); }
+  { Copy c; c = Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy* c; c = new Copy(C); }
+  { Copy* c; c = new Copy(Copy(&f_eval)); }// expected-error {{cannot take address of consteval}}
+  { Copy* c; c = new Copy(std::move(C)); }
+  { Copy* c; c = new Copy(std::move(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy* c; c = new Copy(to_lvalue_ref(Copy(&f_eval))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+  { Copy* c; c = new Copy(to_lvalue_ref(std::move(C))); }
+  { Copy* c; c = new Copy(to_lvalue_ref(std::move(Copy(&f_eval)))); }// expected-error {{is not a constant expression}} expected-note {{to a consteval}}
+}
+
+} // namespace special_ctor


        


More information about the cfe-commits mailing list