Tyker updated this revision to Diff 219240.
Tyker added a comment.

Changes:

- Rebased
- Fixed typos


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D63960/new/

https://reviews.llvm.org/D63960

Files:
  clang/include/clang/AST/DeclCXX.h
  clang/include/clang/Basic/DiagnosticASTKinds.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/DeclCXX.cpp
  clang/lib/AST/ExprConstant.cpp
  clang/lib/Sema/Sema.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaLambda.cpp
  clang/lib/Sema/SemaOverload.cpp
  clang/lib/Sema/TreeTransform.h
  clang/lib/Serialization/ASTReaderDecl.cpp
  clang/lib/Serialization/ASTWriter.cpp
  clang/test/SemaCXX/cxx2a-consteval.cpp

Index: clang/test/SemaCXX/cxx2a-consteval.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-consteval.cpp
+++ clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -12,6 +12,7 @@
 }
 
 constexpr auto l_eval = [](int i) consteval {
+// expected-note@-1+ {{declared here}}
 
   return i;
 };
@@ -23,6 +24,7 @@
 
 struct A {
   consteval int f1(int i) const {
+// expected-note@-1 {{declared here}}
     return i;
   }
   consteval A(int i);
@@ -56,3 +58,183 @@
 consteval int main() { // expected-error {{'main' is not allowed to be declared consteval}}
   return 0;
 }
+
+consteval int f_eval(int i) {
+// expected-note@-1+ {{declared here}}
+  return i;
+}
+
+namespace taking_address {
+
+using func_type = int(int);
+
+func_type* p1 = (&f_eval);
+// expected-error@-1 {{take address}}
+func_type* p7 = __builtin_addressof(f_eval);
+// expected-error@-1 {{take address}}
+
+auto p = f_eval;
+// expected-error@-1 {{take address}}
+
+auto m1 = &basic_sema::A::f1;
+// expected-error@-1 {{take address}}
+auto l1 = &decltype(basic_sema::l_eval)::operator();
+// expected-error@-1 {{take address}}
+
+consteval int f(int i) {
+// expected-note@-1+ {{declared here}}
+  return i;
+}
+
+auto ptr = &f;
+// expected-error@-1 {{take address}}
+
+auto f1() {
+  return &f;
+// expected-error@-1 {{take address}}
+}
+
+}
+
+namespace invalid_function {
+using size_t = unsigned long;
+struct A {
+  consteval void *operator new(size_t count);
+  // expected-error@-1 {{operator new cannot be declared consteval}}
+  consteval void *operator new[](size_t count);
+  // expected-error@-1 {{operator new[] cannot be declared consteval}}
+  consteval void operator delete(void* ptr);
+  // expected-error@-1 {{operator delete cannot be declared consteval}}
+  consteval void operator delete[](void* ptr);
+  // expected-error@-1 {{operator delete[] cannot be declared consteval}}
+  consteval ~A();
+  // expected-error@-1 {{destructor cannot be marked consteval}}
+};
+
+}
+
+namespace nested {
+consteval int f() {
+  return 0;
+}
+
+consteval int f1(...) {
+  return 1;
+}
+
+enum E {};
+
+using T = int(&)();
+
+consteval auto operator+ (E, int(*a)()) {
+  return 0;
+}
+
+void d() {
+  auto i = f1(E() + &f);
+}
+
+auto l0 = [](auto) consteval {
+  return 0;
+};
+
+int i0 = l0(&f1);
+
+int i1 = f1(l0(4));
+
+}
+
+namespace user_defined_literal {
+
+consteval int operator"" _test(unsigned long long i) {
+// expected-note@-1+ {{declared here}}
+  return 0;
+}
+
+int i = 0_test;
+
+auto ptr = &operator"" _test;
+// expected-error@-1 {{take address}}
+
+}
+
+namespace return_address {
+
+consteval int f() {
+  return 0;
+}
+
+consteval int(*ret1(int i))() {
+  return &f;
+}
+
+auto ptr = ret1(0);
+// expected-error@-1 {{could not be evaluated}}
+// expected-note@-2 {{pointer on a consteval}}
+
+struct A {
+  consteval int f(int) {
+    return 0;    
+  }
+};
+
+using mem_ptr_type = int (A::*)(int);
+
+template<mem_ptr_type ptr>
+struct C {};
+
+C<&A::f> c;
+// expected-error@-1 {{is not a constant expression}}
+// expected-note@-2 {{pointer on a consteval}}
+
+consteval mem_ptr_type ret2() {
+  return &A::f;
+}
+
+C<ret2()> c1;
+// expected-error@-1 {{is not a constant expression}}
+// expected-note@-2 {{pointer on a consteval}}
+
+}
+
+namespace context {
+
+int g_i;
+// expected-note@-1 {{declared here}}
+
+consteval int f(int) {
+  return 0;
+}
+
+constexpr int c_i = 0;
+
+int t1 = f(g_i);
+// expected-error@-1 {{could not be evaluated}}
+// expected-note@-2 {{read of non-const variable}}
+int t3 = f(c_i);
+
+constexpr int f_c(int i) {
+// expected-note@-1 {{declared here}}
+  int t = f(i);
+// expected-error@-1 {{could not be evaluated}}
+// expected-note@-2 {{read of non-const variable}}
+  return f(0);  
+}
+
+consteval int f_eval(int i) {
+  return f(i);
+}
+
+auto l0 = [](int i) consteval {
+  return f(i);
+};
+
+auto l1 = [](int i) constexpr {
+// expected-note@-1 {{declared here}}
+  int t = f(i);
+// expected-error@-1 {{could not be evaluated}}
+// expected-note@-2 {{read of non-const variable}}
+  return f(0);  
+};
+
+}
\ No newline at end of file
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -6176,6 +6176,7 @@
   Record->push_back(Data.ComputedVisibleConversions);
   Record->push_back(Data.UserProvidedDefaultConstructor);
   Record->push_back(Data.DeclaredSpecialMembers);
+  Record->push_back(Data.DefaultedSpecialMemberIsConsteval);
   Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForVBase);
   Record->push_back(Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase);
   Record->push_back(Data.ImplicitCopyAssignmentHasConstParam);
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -1699,6 +1699,7 @@
   Data.ComputedVisibleConversions = Record.readInt();
   Data.UserProvidedDefaultConstructor = Record.readInt();
   Data.DeclaredSpecialMembers = Record.readInt();
+  Data.DefaultedSpecialMemberIsConsteval = Record.readInt();
   Data.ImplicitCopyConstructorCanHaveConstParamForVBase = Record.readInt();
   Data.ImplicitCopyConstructorCanHaveConstParamForNonVBase = Record.readInt();
   Data.ImplicitCopyAssignmentHasConstParam = Record.readInt();
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -11104,7 +11104,7 @@
       RecoveryTSI);
 }
 
-template<typename Derived>
+template <typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformCXXConstructExpr(CXXConstructExpr *E) {
   // CXXConstructExprs other than for list-initialization and
Index: clang/lib/Sema/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -12416,8 +12416,7 @@
       if (CheckFunctionCall(FnDecl, TheCall,
                             FnDecl->getType()->castAs<FunctionProtoType>()))
         return ExprError();
-
-      return MaybeBindToTemporary(TheCall);
+      return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FnDecl);
     } else {
       // We matched a built-in operator. Convert the arguments, then
       // break out so that we will build the appropriate built-in
@@ -12660,7 +12659,8 @@
                   isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(),
                   VariadicDoesNotApply);
 
-        return MaybeBindToTemporary(TheCall);
+        return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall),
+                                           FnDecl);
       } else {
         // We matched a built-in operator. Convert the arguments, then
         // break out so that we will build the appropriate built-in
@@ -13497,7 +13497,7 @@
   if (CheckFunctionCall(Method, TheCall, Proto))
     return true;
 
-  return MaybeBindToTemporary(TheCall);
+  return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), Method);
 }
 
 /// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator->
@@ -13693,7 +13693,7 @@
   if (CheckFunctionCall(FD, UDL, nullptr))
     return ExprError();
 
-  return MaybeBindToTemporary(UDL);
+  return CheckForImmediateInvocation(MaybeBindToTemporary(UDL), FD);
 }
 
 /// Build a call to 'begin' or 'end' for a C++11 for-range statement. If the
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -1197,7 +1197,9 @@
   // Enter a new evaluation context to insulate the lambda from any
   // cleanups from the enclosing full-expression.
   PushExpressionEvaluationContext(
-      ExpressionEvaluationContext::PotentiallyEvaluated);
+      LSI->CallOperator->isConsteval()
+          ? ExpressionEvaluationContext::ConstantEvaluated
+          : ExpressionEvaluationContext::PotentiallyEvaluated);
 }
 
 void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope,
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -45,6 +45,7 @@
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
 #include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/SaveAndRestore.h"
 using namespace clang;
 using namespace sema;
 
@@ -6001,7 +6002,7 @@
       return ExprError();
   }
 
-  return MaybeBindToTemporary(TheCall);
+  return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl);
 }
 
 ExprResult
@@ -15024,6 +15025,130 @@
   Rec.PossibleDerefs.clear();
 }
 
+ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
+  if (!E.isUsable() || !Decl || !Decl->isConsteval() || isConstantEvaluated() ||
+      !hasImmediateInvocationTracking)
+    return E;
+  if (auto *Call = dyn_cast<CallExpr>(E.get()))
+    if (auto *DeclRef =
+            dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()))
+      ExprEvalContexts.back().ReferenceOnConsteval.erase(DeclRef);
+  ConstantExpr *Res = ConstantExpr::Create(
+      getASTContext(), E.get(),
+      ConstantExpr::getStorageKind(E.get()->getType().getTypePtr(),
+                                   getASTContext()));
+  ExprEvalContexts.back().ImmediateInvocationsCandidates.emplace_back(Res, 0);
+  return Res;
+}
+
+static void EvaluateAndDiagnoseImmediateInvocation(Sema &SemaRef,
+                                                   ConstantExpr *CE) {
+  llvm::SmallVector<PartialDiagnosticAt, 8> Notes;
+  Expr::EvalResult Eval;
+  Eval.Diag = &Notes;
+  if (!CE->getSubExpr()->EvaluateAsConstantExpr(Eval, Expr::EvaluateForCodeGen,
+                                                SemaRef.getASTContext())) {
+    SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call)
+        << cast<FunctionDecl>(
+               cast<CallExpr>(CE->getSubExpr())->getCalleeDecl());
+    for (auto &Note : Notes)
+      SemaRef.Diag(Note.first, Note.second);
+    return;
+  }
+  CE->MoveIntoResult(Eval.Val, SemaRef.getASTContext());
+}
+
+static void
+HandleImmediateInvocations(Sema &SemaRef,
+                           Sema::ExpressionEvaluationContextRecord &Rec) {
+  if ((Rec.ImmediateInvocationsCandidates.size() == 0 &&
+       Rec.ReferenceOnConsteval.size() == 0) ||
+      !SemaRef.hasImmediateInvocationTracking)
+    return;
+  struct ExprCompType {
+    bool operator()(llvm::PointerIntPair<ConstantExpr *, 1> LHS,
+                    llvm::PointerIntPair<ConstantExpr *, 1> RHS) {
+      return LHS.getPointer()->getBeginLoc() < RHS.getPointer()->getBeginLoc();
+    }
+  };
+
+  llvm::sort(Rec.ImmediateInvocationsCandidates, ExprCompType{});
+
+  /// When we have more then 1 ImmediateInvocationsCandidates we need to check
+  /// for nested ImmediateInvocationsCandidates. when we have only 1 we only
+  /// need to remove ReferenceOnConsteval in the immediate invocation.
+  if (Rec.ImmediateInvocationsCandidates.size() > 1) {
+
+    /// Prevent sema calls during the tree transform from adding pointers that
+    /// are already in the sets.
+    llvm::SaveAndRestore<bool> DisableIITracking(
+        SemaRef.hasImmediateInvocationTracking, false);
+
+    for (auto &CE : Rec.ImmediateInvocationsCandidates)
+      if (!CE.getInt()) {
+        /// Prevent diagnostic during tree transfrom as they are duplicates
+        Sema::TentativeAnalysisScope DisableDiag(SemaRef);
+        struct ComplexRemove : TreeTransform<ComplexRemove> {
+          using Base = TreeTransform<ComplexRemove>;
+          llvm::SmallPtrSetImpl<DeclRefExpr *> &DRSet;
+          decltype(Rec.ImmediateInvocationsCandidates) &IISet;
+          ComplexRemove(Sema &SemaRef, llvm::SmallPtrSetImpl<DeclRefExpr *> &DR,
+                        decltype(Rec.ImmediateInvocationsCandidates) &II)
+              : Base(SemaRef), DRSet(DR), IISet(II) {}
+          ExprResult TransformConstantExpr(ConstantExpr *E) {
+            auto It = llvm::lower_bound(
+                IISet, llvm::PointerIntPair<ConstantExpr *, 1>(E, 0),
+                ExprCompType{});
+            if (It != IISet.end()) {
+              It->setInt(1); // Mark as deleted
+              return Base::TransformExpr(It->getPointer()->getSubExpr());
+            } else
+              return Base::TransformConstantExpr(It->getPointer());
+          }
+          /// Base::TransfromCXXOperatorCallExpr doesn't traverse the callee so
+          /// we need to remove its DeclRefExpr from the DRSet.
+          ExprResult TransformCXXOperatorCallExpr(CXXOperatorCallExpr *E) {
+            DRSet.erase(cast<DeclRefExpr>(E->getCallee()->IgnoreImplicit()));
+            return Base::TransformCXXOperatorCallExpr(E);
+          }
+          ExprResult TransformDeclRefExpr(DeclRefExpr *E) {
+            DRSet.erase(E);
+            return E;
+          }
+          /// FIXME: Add an option to tree transfrom that prevents rebuilding
+          /// nodes that only need rewiring and use this option here.
+          bool AlwaysRebuild() { return false; }
+          bool ReplacingOriginal() { return true; }
+        } Transfromer(SemaRef, Rec.ReferenceOnConsteval,
+                      Rec.ImmediateInvocationsCandidates);
+        ExprResult Res =
+            Transfromer.TransformExpr(CE.getPointer()->getSubExpr());
+        CE.getPointer()->setSubExpr(Res.get());
+      }
+  } else if (Rec.ImmediateInvocationsCandidates.size() == 1 &&
+             Rec.ReferenceOnConsteval.size()) {
+    struct SimpleRemove : RecursiveASTVisitor<SimpleRemove> {
+      llvm::SmallPtrSetImpl<DeclRefExpr *> &DRSet;
+      SimpleRemove(llvm::SmallPtrSetImpl<DeclRefExpr *> &S) : DRSet(S) {}
+      bool VisitDeclRefExpr(DeclRefExpr *E) {
+        DRSet.erase(E);
+        return DRSet.size();
+      }
+    } Visitor(Rec.ReferenceOnConsteval);
+    Visitor.TraverseStmt(
+        Rec.ImmediateInvocationsCandidates.front().getPointer()->getSubExpr());
+  }
+  for (auto CE : Rec.ImmediateInvocationsCandidates)
+    if (!CE.getInt())
+      EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE.getPointer());
+  for (auto DR : Rec.ReferenceOnConsteval) {
+    auto *FD = cast<FunctionDecl>(DR->getDecl());
+    SemaRef.Diag(DR->getBeginLoc(), diag::err_invalid_consteval_take_address)
+        << FD;
+    SemaRef.Diag(FD->getLocation(), diag::note_declared_at);
+  }
+}
+
 void Sema::PopExpressionEvaluationContext() {
   ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back();
   unsigned NumTypos = Rec.NumTypos;
@@ -15057,6 +15182,7 @@
   }
 
   WarnOnPendingNoDerefs(Rec);
+  HandleImmediateInvocations(*this, Rec);
 
   // When are coming out of an unevaluated context, clear out any
   // temporaries that we may have created as part of the evaluation of
@@ -16753,6 +16879,11 @@
     if (Method->isVirtual() &&
         !Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext))
       OdrUse = false;
+
+  if (auto *FD = dyn_cast<FunctionDecl>(E->getDecl()))
+    if (!isConstantEvaluated() && FD->isConsteval() &&
+        hasImmediateInvocationTracking)
+      ExprEvalContexts.back().ReferenceOnConsteval.insert(E);
   MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse);
 }
 
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -1872,7 +1872,8 @@
   if (!Inits.count(Field)) {
     if (Kind == Sema::CheckConstexprKind::Diagnose) {
       if (!Diagnosed) {
-        SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init);
+        SemaRef.Diag(Dcl->getLocation(), diag::err_constexpr_ctor_missing_init)
+            << Dcl->isConsteval();
         Diagnosed = true;
       }
       SemaRef.Diag(Field->getLocation(),
@@ -2230,7 +2231,7 @@
       !Expr::isPotentialConstantExpr(Dcl, Diags)) {
     SemaRef.Diag(Dcl->getLocation(),
                  diag::ext_constexpr_function_never_constant_expr)
-        << isa<CXXConstructorDecl>(Dcl);
+        << isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval();
     for (size_t I = 0, N = Diags.size(); I != N; ++I)
       SemaRef.Diag(Diags[I].first, Diags[I].second);
     // Don't return false here: we allow this for compatibility in
@@ -6839,7 +6840,9 @@
     //   If a function is explicitly defaulted on its first declaration, it is
     //   implicitly considered to be constexpr if the implicit declaration
     //   would be.
-    MD->setConstexprKind(Constexpr ? CSK_constexpr : CSK_unspecified);
+    MD->setConstexprKind(
+        Constexpr ? (MD->isConsteval() ? CSK_consteval : CSK_constexpr)
+                  : CSK_unspecified);
 
     if (!Type->hasExceptionSpec()) {
       // C++2a [except.spec]p3:
@@ -11155,6 +11158,14 @@
   SpecialMem->setType(QT);
 }
 
+static ConstexprSpecKind
+DetermineSpecialMemberConstexprKind(bool IsConstexprValid,
+                                    bool HasDefaultedConstevalSM) {
+  return IsConstexprValid
+             ? (HasDefaultedConstevalSM ? CSK_consteval : CSK_constexpr)
+             : CSK_unspecified;
+}
+
 CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor(
                                                      CXXRecordDecl *ClassDecl) {
   // C++ [class.ctor]p5:
@@ -11174,9 +11185,12 @@
                                                      CXXDefaultConstructor,
                                                      false);
 
+  ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind(
+      Constexpr, ClassDecl->hasDefaultedConstevalDefaultCtor());
+
   // Create the actual constructor declaration.
-  CanQualType ClassType
-    = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
+  CanQualType ClassType =
+      Context.getCanonicalType(Context.getTypeDeclType(ClassDecl));
   SourceLocation ClassLoc = ClassDecl->getLocation();
   DeclarationName Name
     = Context.DeclarationNames.getCXXConstructorName(ClassType);
@@ -11184,8 +11198,7 @@
   CXXConstructorDecl *DefaultCon = CXXConstructorDecl::Create(
       Context, ClassDecl, ClassLoc, NameInfo, /*Type*/ QualType(),
       /*TInfo=*/nullptr, ExplicitSpecifier(),
-      /*isInline=*/true, /*isImplicitlyDeclared=*/true,
-      Constexpr ? CSK_constexpr : CSK_unspecified);
+      /*isInline=*/true, /*isImplicitlyDeclared=*/true, ConstexprKind);
   DefaultCon->setAccess(AS_public);
   DefaultCon->setDefaulted();
 
@@ -12065,16 +12078,18 @@
                                                      CXXCopyAssignment,
                                                      Const);
 
+  ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind(
+      Constexpr, ClassDecl->hasDefaultedConstevalCopyAssignment());
+
   //   An implicitly-declared copy assignment operator is an inline public
   //   member of its class.
   DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
   SourceLocation ClassLoc = ClassDecl->getLocation();
   DeclarationNameInfo NameInfo(Name, ClassLoc);
-  CXXMethodDecl *CopyAssignment = CXXMethodDecl::Create(
-      Context, ClassDecl, ClassLoc, NameInfo, QualType(),
-      /*TInfo=*/nullptr, /*StorageClass=*/SC_None,
-      /*isInline=*/true, Constexpr ? CSK_constexpr : CSK_unspecified,
-      SourceLocation());
+  CXXMethodDecl *CopyAssignment =
+      CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(),
+                            /*TInfo=*/nullptr, /*StorageClass=*/SC_None,
+                            /*isInline=*/true, ConstexprKind, SourceLocation());
   CopyAssignment->setAccess(AS_public);
   CopyAssignment->setDefaulted();
   CopyAssignment->setImplicit();
@@ -12386,16 +12401,18 @@
                                                      CXXMoveAssignment,
                                                      false);
 
+  ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind(
+      Constexpr, ClassDecl->hasDefaultedConstevalMoveAssignment());
+
   //   An implicitly-declared move assignment operator is an inline public
   //   member of its class.
   DeclarationName Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal);
   SourceLocation ClassLoc = ClassDecl->getLocation();
   DeclarationNameInfo NameInfo(Name, ClassLoc);
-  CXXMethodDecl *MoveAssignment = CXXMethodDecl::Create(
-      Context, ClassDecl, ClassLoc, NameInfo, QualType(),
-      /*TInfo=*/nullptr, /*StorageClass=*/SC_None,
-      /*isInline=*/true, Constexpr ? CSK_constexpr : CSK_unspecified,
-      SourceLocation());
+  CXXMethodDecl *MoveAssignment =
+      CXXMethodDecl::Create(Context, ClassDecl, ClassLoc, NameInfo, QualType(),
+                            /*TInfo=*/nullptr, /*StorageClass=*/SC_None,
+                            /*isInline=*/true, ConstexprKind, SourceLocation());
   MoveAssignment->setAccess(AS_public);
   MoveAssignment->setDefaulted();
   MoveAssignment->setImplicit();
@@ -12764,6 +12781,9 @@
                                                      CXXCopyConstructor,
                                                      Const);
 
+  ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind(
+      Constexpr, ClassDecl->hasDefaultedConstevalCopyCtor());
+
   DeclarationName Name
     = Context.DeclarationNames.getCXXConstructorName(
                                            Context.getCanonicalType(ClassType));
@@ -12776,8 +12796,7 @@
       Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/nullptr,
       ExplicitSpecifier(),
       /*isInline=*/true,
-      /*isImplicitlyDeclared=*/true,
-      Constexpr ? CSK_constexpr : CSK_unspecified);
+      /*isImplicitlyDeclared=*/true, ConstexprKind);
   CopyConstructor->setAccess(AS_public);
   CopyConstructor->setDefaulted();
 
@@ -12895,6 +12914,9 @@
                                                      CXXMoveConstructor,
                                                      false);
 
+  ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind(
+      Constexpr, ClassDecl->hasDefaultedConstevalMoveCtor());
+
   DeclarationName Name
     = Context.DeclarationNames.getCXXConstructorName(
                                            Context.getCanonicalType(ClassType));
@@ -12908,8 +12930,7 @@
       Context, ClassDecl, ClassLoc, NameInfo, QualType(), /*TInfo=*/nullptr,
       ExplicitSpecifier(),
       /*isInline=*/true,
-      /*isImplicitlyDeclared=*/true,
-      Constexpr ? CSK_constexpr : CSK_unspecified);
+      /*isImplicitlyDeclared=*/true, ConstexprKind);
   MoveConstructor->setAccess(AS_public);
   MoveConstructor->setDefaulted();
 
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -8780,9 +8780,24 @@
       // C++11 [dcl.constexpr]p3: functions declared constexpr are required to
       // be either constructors or to return a literal type. Therefore,
       // destructors cannot be declared constexpr.
-      if (isa<CXXDestructorDecl>(NewFD)) {
+      if (isa<CXXDestructorDecl>(NewFD))
         Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor)
             << ConstexprKind;
+      // C++20 [dcl.constexpr]p2: A destructor, an allocation function, or a
+      // deallocation function shall not be declared with the consteval
+      // specifier.
+      if (ConstexprKind == CSK_consteval) {
+        if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD)) {
+          if (MD->getOverloadedOperator() == OO_New ||
+              MD->getOverloadedOperator() == OO_Array_New ||
+              MD->getOverloadedOperator() == OO_Delete ||
+              MD->getOverloadedOperator() == OO_Array_Delete) {
+            Diag(D.getDeclSpec().getConstexprSpecLoc(),
+                 diag::err_invalid_consteval_decl_kind)
+                << MD->getOverloadedOperator() - OO_New;
+            NewFD->setConstexprKind(CSK_constexpr);
+          }
+        }
       }
     }
 
@@ -13441,7 +13456,9 @@
   // Do not push if it is a lambda because one is already pushed when building
   // the lambda in ActOnStartOfLambdaDefinition().
   if (!isLambdaCallOperator(FD))
-    PushExpressionEvaluationContext(ExprEvalContexts.back().Context);
+    PushExpressionEvaluationContext(
+        FD->isConsteval() ? ExpressionEvaluationContext::ConstantEvaluated
+                          : ExprEvalContexts.back().Context);
 
   // Check for defining attributes before the check for redefinition.
   if (const auto *Attr = FD->getAttr<AliasAttr>()) {
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -160,6 +160,7 @@
       CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) {
   TUScope = nullptr;
   isConstantEvaluatedOverride = false;
+  hasImmediateInvocationTracking = true;
 
   LoadedExternalKnownNamespaces = false;
   for (unsigned I = 0; I != NSAPI::NumNSNumberLiteralMethods; ++I)
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -1926,6 +1926,14 @@
   APValue::LValueBase Base = LVal.getLValueBase();
   const SubobjectDesignator &Designator = LVal.getLValueDesignator();
 
+  if (auto *VD = LVal.getLValueBase().dyn_cast<const ValueDecl *>())
+    if (auto *FD = dyn_cast<FunctionDecl>(VD))
+      if (FD->isConsteval()) {
+        Info.FFDiag(Loc, diag::note_consteval_address_accessible)
+            << !Type->isAnyPointerType();
+        return false;
+      }
+
   // Check that the object is a global. Note that the fake 'this' object we
   // manufacture when checking potential constant expressions is conservatively
   // assumed to be global here.
@@ -2007,6 +2015,10 @@
   const auto *FD = dyn_cast_or_null<CXXMethodDecl>(Member);
   if (!FD)
     return true;
+  if (FD->isConsteval()) {
+    Info.FFDiag(Loc, diag::note_consteval_address_accessible) << /*pointer*/ 0;
+    return false;
+  }
   return Usage == Expr::EvaluateForMangling || FD->isVirtual() ||
          !FD->hasAttr<DLLImportAttr>();
 }
Index: clang/lib/AST/DeclCXX.cpp
===================================================================
--- clang/lib/AST/DeclCXX.cpp
+++ clang/lib/AST/DeclCXX.cpp
@@ -97,6 +97,7 @@
       HasConstexprDefaultConstructor(false),
       HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
       UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
+      DefaultedSpecialMemberIsConsteval(0),
       ImplicitCopyConstructorCanHaveConstParamForVBase(true),
       ImplicitCopyConstructorCanHaveConstParamForNonVBase(true),
       ImplicitCopyAssignmentHasConstParam(true),
@@ -738,8 +739,11 @@
         data().UserProvidedDefaultConstructor = true;
       if (Constructor->isConstexpr())
         data().HasConstexprDefaultConstructor = true;
-      if (Constructor->isDefaulted())
+      if (Constructor->isDefaulted()) {
         data().HasDefaultedDefaultConstructor = true;
+        if (Constructor->isConsteval())
+          data().DefaultedSpecialMemberIsConsteval |= SMF_DefaultConstructor;
+      }
     }
 
     if (!FunTmpl) {
@@ -749,8 +753,13 @@
 
         if (Quals & Qualifiers::Const)
           data().HasDeclaredCopyConstructorWithConstParam = true;
-      } else if (Constructor->isMoveConstructor())
+        if (Constructor->isConsteval() && Constructor->isDefaulted())
+          data().DefaultedSpecialMemberIsConsteval |= SMF_CopyConstructor;
+      } else if (Constructor->isMoveConstructor()) {
         SMKind |= SMF_MoveConstructor;
+        if (Constructor->isConsteval() && Constructor->isDefaulted())
+          data().DefaultedSpecialMemberIsConsteval |= SMF_MoveConstructor;
+      }
     }
 
     // C++11 [dcl.init.aggr]p1: DR1518
@@ -781,6 +790,8 @@
   if (const auto *DD = dyn_cast<CXXDestructorDecl>(D)) {
     SMKind |= SMF_Destructor;
 
+    if (DD->isConsteval() && DD->isDefaulted())
+      data().DefaultedSpecialMemberIsConsteval |= SMF_Destructor;
     if (DD->isUserProvided())
       data().HasIrrelevantDestructor = false;
     // If the destructor is explicitly defaulted and not trivial or not public
@@ -800,15 +811,22 @@
     if (Method->isCopyAssignmentOperator()) {
       SMKind |= SMF_CopyAssignment;
 
+      if (Method->isConsteval() && Method->isDefaulted())
+        data().DefaultedSpecialMemberIsConsteval |= SMF_CopyAssignment;
+
       const auto *ParamTy =
           Method->getParamDecl(0)->getType()->getAs<ReferenceType>();
       if (!ParamTy || ParamTy->getPointeeType().isConstQualified())
         data().HasDeclaredCopyAssignmentWithConstParam = true;
     }
 
-    if (Method->isMoveAssignmentOperator())
+    if (Method->isMoveAssignmentOperator()) {
       SMKind |= SMF_MoveAssignment;
 
+      if (Method->isConsteval() && Method->isDefaulted())
+        data().DefaultedSpecialMemberIsConsteval |= SMF_CopyAssignment;
+    }
+
     // Keep the list of conversion functions up-to-date.
     if (auto *Conversion = dyn_cast<CXXConversionDecl>(D)) {
       // FIXME: We use the 'unsafe' accessor for the access specifier here,
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -803,6 +803,11 @@
     }
   };
 
+  /// Should be set for most operation.
+  /// It is unset to disable tracking of immediate invocation candidates and
+  /// reference on consteval.
+  bool hasImmediateInvocationTracking;
+
   /// Used to change context to isConstantEvaluated without pushing a heavy
   /// ExpressionEvaluationContextRecord object.
   bool isConstantEvaluatedOverride;
@@ -1062,6 +1067,14 @@
 
     llvm::SmallPtrSet<const Expr *, 8> PossibleDerefs;
 
+    /// Set of candidate for starting an immediate invocation.
+    llvm::SmallVector<llvm::PointerIntPair<ConstantExpr *, 1>, 8>
+        ImmediateInvocationsCandidates;
+
+    /// Set of DeclRefExpr referencing a consteval function when used in a
+    /// context not already know to be immediately invoked.
+    llvm::SmallPtrSet<DeclRefExpr *, 8> ReferenceOnConsteval;
+
     /// \brief Describes whether we are in an expression constext which we have
     /// to handle differently.
     enum ExpressionKind {
@@ -5274,6 +5287,10 @@
   /// it simply returns the passed in expression.
   ExprResult MaybeBindToTemporary(Expr *E);
 
+  /// Wrap the expression in a ConstantExpr if it is a potention immediate
+  /// invocation
+  ExprResult CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl);
+
   bool CompleteConstructorCall(CXXConstructorDecl *Constructor,
                                MultiExprArg ArgsPtr,
                                SourceLocation Loc,
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2357,6 +2357,13 @@
   "'constexpr' non-static member function will not be implicitly 'const' "
   "in C++14; add 'const' to avoid a change in behavior">,
   InGroup<DiagGroup<"constexpr-not-const">>;
+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 %0 could not be evaluated">;
+def err_invalid_consteval_decl_kind : Error<
+  "operator %select{new|delete|new[]|delete[]}0 cannot be declared consteval">;
 def err_invalid_constexpr : Error<
   "%select{function parameter|typedef}0 "
   "cannot be %sub{select_constexpr_spec_kind}1">;
@@ -2442,7 +2449,7 @@
   "variables defined in a constexpr %select{function|constructor}0 must be "
   "initialized">;
 def ext_constexpr_function_never_constant_expr : ExtWarn<
-  "constexpr %select{function|constructor}0 never produces a "
+  "%select{constexpr|consteval}1 %select{function|constructor}0 never produces a "
   "constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
 def err_attr_cond_never_constant_expr : Error<
   "%0 attribute expression never produces a constant expression">;
@@ -2480,7 +2487,7 @@
 def err_constexpr_union_ctor_no_init : Error<
   "constexpr union constructor does not initialize any member">;
 def err_constexpr_ctor_missing_init : Error<
-  "constexpr constructor must initialize all members">;
+  "%select{constexpr|consteval}0 constructor must initialize all members">;
 def note_constexpr_ctor_missing_init : Note<
   "member not initialized by constructor">;
 def note_non_literal_no_constexpr_ctors : Note<
Index: clang/include/clang/Basic/DiagnosticASTKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticASTKinds.td
+++ clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -53,6 +53,9 @@
 def note_constexpr_non_global : Note<
   "%select{pointer|reference}0 to %select{|subobject of }1"
   "%select{temporary|%3}2 is not a constant expression">;
+def note_consteval_address_accessible : Note<
+  "%select{pointer|reference}0 on a consteval declaration "
+  "is not a constant expression">;
 def note_constexpr_uninitialized : Note<
   "%select{|sub}0object of type %1 is not initialized">;
 def note_constexpr_subobject_declared_here : Note<
Index: clang/include/clang/AST/DeclCXX.h
===================================================================
--- clang/include/clang/AST/DeclCXX.h
+++ clang/include/clang/AST/DeclCXX.h
@@ -461,6 +461,9 @@
     /// either by the user or implicitly.
     unsigned DeclaredSpecialMembers : 6;
 
+    /// The special member which have been defaulted with the consteval keyword.
+    unsigned DefaultedSpecialMemberIsConsteval : 6;
+
     /// Whether an implicit copy constructor could have a const-qualified
     /// parameter, for initializing virtual bases and for other subobjects.
     unsigned ImplicitCopyConstructorCanHaveConstParamForVBase : 1;
@@ -1012,6 +1015,41 @@
            needsImplicitMoveConstructor();
   }
 
+  /// Determine whether this class has a defaulted consteval default
+  /// constructor.
+  bool hasDefaultedConstevalDefaultCtor() const {
+    return data().DefaultedSpecialMemberIsConsteval & SMF_DefaultConstructor;
+  }
+
+  /// Determine whether this class has a defaulted consteval copy
+  /// constructor.
+  bool hasDefaultedConstevalCopyCtor() const {
+    return data().DefaultedSpecialMemberIsConsteval & SMF_CopyConstructor;
+  }
+
+  /// Determine whether this class has a defaulted consteval move
+  /// constructor.
+  bool hasDefaultedConstevalMoveCtor() const {
+    return data().DefaultedSpecialMemberIsConsteval & SMF_MoveConstructor;
+  }
+
+  /// Determine whether this class has a defaulted consteval copy assignment
+  /// operator.
+  bool hasDefaultedConstevalCopyAssignment() const {
+    return data().DefaultedSpecialMemberIsConsteval & SMF_CopyAssignment;
+  }
+
+  /// Determine whether this class has a defaulted consteval move assignment
+  /// operator.
+  bool hasDefaultedConstevalMoveAssignment() const {
+    return data().DefaultedSpecialMemberIsConsteval & SMF_MoveAssignment;
+  }
+
+  /// Determine whether this class has a defaulted consteval destructor.
+  bool hasDefaultedConstevalDestructor() const {
+    return data().DefaultedSpecialMemberIsConsteval & SMF_Destructor;
+  }
+
   /// Set that we attempted to declare an implicit copy
   /// constructor, but overload resolution failed so we deleted it.
   void setImplicitCopyConstructorIsDeleted() {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to