Tyker updated this revision to Diff 223021.
Tyker marked 16 inline comments as done.
Tyker added a comment.
i addressed almost all comments.
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/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);
@@ -62,3 +64,203 @@
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}}
+};
+
+}
+
+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));
+
+int i2 = f1(&f1, &f1, &f1, &f1, &f1, &f1, &f1);
+
+int i3 = f1(f1(f1(&f1, &f1), f1(&f1, &f1), f1(f1(&f1, &f1), &f1)));
+
+}
+
+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 to 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 to 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 to 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);
+};
+
+}
+
+namespace cleanup {
+
+struct A {
+ int *p = new int(42);
+ consteval int get() { return *p; }
+ constexpr ~A() { delete p; }
+};
+int k1 = A().get();
+
+struct B {
+ int *p = new int(42);
+ consteval int get() { return *p; }
+ consteval ~B() { delete p; }
+};
+int k2 = B().get();
+
+}
\ No newline at end of file
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -6183,6 +6183,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
@@ -1700,6 +1700,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/SemaOverload.cpp
===================================================================
--- clang/lib/Sema/SemaOverload.cpp
+++ clang/lib/Sema/SemaOverload.cpp
@@ -12436,8 +12436,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
@@ -12680,7 +12679,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
@@ -13517,7 +13517,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->
@@ -13713,7 +13713,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;
@@ -5994,7 +5995,7 @@
return ExprError();
}
- return MaybeBindToTemporary(TheCall);
+ return CheckForImmediateInvocation(MaybeBindToTemporary(TheCall), FDecl);
}
ExprResult
@@ -15059,6 +15060,138 @@
Rec.PossibleDerefs.clear();
}
+ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
+ if (!E.isUsable() || !Decl || !Decl->isConsteval() || isConstantEvaluated() ||
+ RebuildingImmediateInvocation)
+ return E;
+
+ /// Opportunistically remove the callee from ReferencesToConsteval if we can.
+ /// It's OK if this fails; we'll also remove this in
+ /// HandleImmediateInvocations, but catching it here allows us to avoid
+ /// walking the AST looking for it in simple cases.
+ if (auto *Call = dyn_cast<CallExpr>(E.get()->IgnoreImplicit()))
+ if (auto *DeclRef =
+ dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()))
+ ExprEvalContexts.back().ReferenceToConsteval.erase(DeclRef);
+
+ E = MaybeCreateExprWithCleanups(E);
+
+ ConstantExpr *Res = ConstantExpr::Create(
+ getASTContext(), E.get(),
+ ConstantExpr::getStorageKind(E.get()->getType().getTypePtr(),
+ getASTContext()));
+ ExprEvalContexts.back().ImmediateInvocationCandidates.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.ImmediateInvocationCandidates.size() == 0 &&
+ Rec.ReferenceToConsteval.size() == 0) ||
+ SemaRef.RebuildingImmediateInvocation)
+ return;
+
+ /// When we have more then 1 ImmediateInvocationCandidates we need to check
+ /// for nested ImmediateInvocationCandidates. when we have only 1 we only
+ /// need to remove ReferenceToConsteval in the immediate invocation.
+ if (Rec.ImmediateInvocationCandidates.size() > 1) {
+
+ /// Prevent sema calls during the tree transform from adding pointers that
+ /// are already in the sets.
+ llvm::SaveAndRestore<bool> DisableIITracking(
+ SemaRef.RebuildingImmediateInvocation, true);
+
+ /// Prevent diagnostic during tree transfrom as they are duplicates
+ Sema::TentativeAnalysisScope DisableDiag(SemaRef);
+
+ for (auto It = Rec.ImmediateInvocationCandidates.rbegin();
+ It != Rec.ImmediateInvocationCandidates.rend(); It++)
+ if (!It->getInt()) {
+ struct ComplexRemove : TreeTransform<ComplexRemove> {
+ using Base = TreeTransform<ComplexRemove>;
+ llvm::SmallPtrSetImpl<DeclRefExpr *> &DRSet;
+ decltype(Rec.ImmediateInvocationCandidates) &IISet;
+ decltype(
+ Rec.ImmediateInvocationCandidates)::reverse_iterator CurrentII;
+ ComplexRemove(
+ Sema &SemaRef, llvm::SmallPtrSetImpl<DeclRefExpr *> &DR,
+ decltype(Rec.ImmediateInvocationCandidates) &II,
+ decltype(
+ Rec.ImmediateInvocationCandidates)::reverse_iterator Current)
+ : Base(SemaRef), DRSet(DR), IISet(II), CurrentII(Current) {}
+ ExprResult TransformConstantExpr(ConstantExpr *E) {
+ E->dumpColor();
+ auto It =
+ std::find_if(CurrentII, IISet.rend(),
+ [E](llvm::PointerIntPair<ConstantExpr *, 1> Elem) {
+ return Elem.getPointer() == E;
+ });
+ if (It != IISet.rend()) {
+ 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;
+ }
+ bool AlwaysRebuild() { return false; }
+ bool ReplacingOriginal() { return true; }
+ } Transfromer(SemaRef, Rec.ReferenceToConsteval,
+ Rec.ImmediateInvocationCandidates, It);
+ ExprResult Res =
+ Transfromer.TransformExpr(It->getPointer()->getSubExpr());
+ It->getPointer()->setSubExpr(Res.get());
+ }
+ } else if (Rec.ImmediateInvocationCandidates.size() == 1 &&
+ Rec.ReferenceToConsteval.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.ReferenceToConsteval);
+ Visitor.TraverseStmt(
+ Rec.ImmediateInvocationCandidates.front().getPointer()->getSubExpr());
+ }
+ for (auto CE : Rec.ImmediateInvocationCandidates)
+ if (!CE.getInt())
+ EvaluateAndDiagnoseImmediateInvocation(SemaRef, CE.getPointer());
+ for (auto DR : Rec.ReferenceToConsteval) {
+ 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;
@@ -15092,6 +15225,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
@@ -16789,6 +16923,11 @@
if (Method->isVirtual() &&
!Method->getDevirtualizedMethod(Base, getLangOpts().AppleKext))
OdrUse = false;
+
+ if (auto *FD = dyn_cast<FunctionDecl>(E->getDecl()))
+ if (!isConstantEvaluated() && FD->isConsteval() &&
+ !RebuildingImmediateInvocation)
+ ExprEvalContexts.back().ReferenceToConsteval.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
@@ -2292,7 +2292,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
@@ -6910,7 +6910,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:
@@ -11225,6 +11227,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:
@@ -11244,9 +11254,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);
@@ -11254,8 +11267,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();
@@ -12140,16 +12152,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();
@@ -12461,16 +12475,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();
@@ -12839,6 +12855,9 @@
CXXCopyConstructor,
Const);
+ ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind(
+ Constexpr, ClassDecl->hasDefaultedConstevalCopyCtor());
+
DeclarationName Name
= Context.DeclarationNames.getCXXConstructorName(
Context.getCanonicalType(ClassType));
@@ -12851,8 +12870,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();
@@ -12970,6 +12988,9 @@
CXXMoveConstructor,
false);
+ ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind(
+ Constexpr, ClassDecl->hasDefaultedConstevalMoveCtor());
+
DeclarationName Name
= Context.DeclarationNames.getCXXConstructorName(
Context.getCanonicalType(ClassType));
@@ -12983,8 +13004,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
@@ -8788,6 +8788,20 @@
Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor)
<< ConstexprKind;
}
+ // C++20 [dcl.constexpr]p2: 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;
+ NewFD->setConstexprKind(CSK_constexpr);
+ }
}
// If __module_private__ was specified, mark the function accordingly.
@@ -13455,7 +13469,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;
+ RebuildingImmediateInvocation = false;
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
@@ -1941,6 +1941,15 @@
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();
+ Info.FFDiag(FD->getLocation(), diag::note_declared_at);
+ 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.
@@ -2050,6 +2059,11 @@
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;
+ Info.FFDiag(FD->getLocation(), diag::note_declared_at);
+ 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
@@ -98,6 +98,7 @@
DefaultedDestructorIsConstexpr(true),
HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
UserProvidedDefaultConstructor(false), DeclaredSpecialMembers(0),
+ DefaultedSpecialMemberIsConsteval(0),
ImplicitCopyConstructorCanHaveConstParamForVBase(true),
ImplicitCopyConstructorCanHaveConstParamForNonVBase(true),
ImplicitCopyAssignmentHasConstParam(true),
@@ -754,8 +755,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) {
@@ -765,8 +769,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
@@ -797,6 +806,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
@@ -816,15 +827,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 @@
}
};
+ /// Whether the AST is currently being rebuild to correct immediate
+ /// invocations. Immediate invocation candidates and references to consteval
+ /// functions aren't tracked when this is set.
+ bool RebuildingImmediateInvocation;
+
/// 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>
+ ImmediateInvocationCandidates;
+
+ /// Set of DeclRefExpr referencing a consteval function when used in a
+ /// context not already know to be immediately invoked.
+ llvm::SmallPtrSet<DeclRefExpr *, 8> ReferenceToConsteval;
+
/// \brief Describes whether we are in an expression constext which we have
/// to handle differently.
enum ExpressionKind {
@@ -5276,6 +5289,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<
+ "%0 cannot be declared consteval">;
def err_invalid_constexpr : Error<
"%select{function parameter|typedef}0 "
"cannot be %sub{select_constexpr_spec_kind}1">;
@@ -2454,7 +2461,7 @@
"is incompatible with C++ standards before C++20">,
InGroup<CXXPre2aCompat>, DefaultIgnore;
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">;
Index: clang/include/clang/Basic/DiagnosticASTKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticASTKinds.td
+++ clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -57,6 +57,9 @@
def note_constexpr_dynamic_alloc : Note<
"%select{pointer|reference}0 to %select{|subobject of }1"
"heap-allocated object is not a constant expression">;
+def note_consteval_address_accessible : Note<
+ "%select{pointer|reference}0 to 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
@@ -465,6 +465,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;
@@ -1016,6 +1019,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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits