diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1139,7 +1139,7 @@ /// \brief Describes whether we are in an expression constext which we have /// to handle differently. enum ExpressionKind { - EK_Decltype, EK_TemplateArgument, EK_Other + EK_Decltype, EK_TemplateArgument, EK_ConstexprVarInit, EK_Other } ExprContext; ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context, diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2962,11 +2962,18 @@ /// be a constant-expression. ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction, SourceLocation &EqualLoc) { + using EEC = Sema::ExpressionEvaluationContext; + assert(Tok.isOneOf(tok::equal, tok::l_brace) && "Data member initializer not starting with '=' or '{'"); + bool IsConstexpr = false; + if (auto *VD = dyn_cast_or_null(D)) + IsConstexpr = VD->isConstexpr(); + EnterExpressionEvaluationContext Context( - Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, D); + Actions, getLangOpts().CPlusPlus2a && IsConstexpr ? + EEC::ConstantEvaluated : EEC::PotentiallyEvaluated, D); if (TryConsumeToken(tok::equal, EqualLoc)) { if (Tok.is(tok::kw_delete)) { // In principle, an initializer of '= delete p;' is legal, but it will diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -1329,10 +1329,19 @@ // Parse the condition. StmtResult InitStmt; Sema::ConditionResult Cond; - if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc, - IsConstexpr ? Sema::ConditionKind::ConstexprIf - : Sema::ConditionKind::Boolean)) - return StmtError(); + + { + EnterExpressionEvaluationContext Unevaluated( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated, + /*LambdaContextDecl=*/nullptr, /*ExprContext=*/ + Sema::ExpressionEvaluationContextRecord::EK_Other, + /*ShouldEnter=*/IsConstexpr); + + if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc, + IsConstexpr ? Sema::ConditionKind::ConstexprIf + : Sema::ConditionKind::Boolean)) + return StmtError(); + } llvm::Optional ConstexprCondition; if (IsConstexpr) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16746,6 +16746,19 @@ Diag(D->getLocation(), diag::err_illegal_initializer); } +/// Determine whether the given declaration is a variable +/// which will be manifestly constant evaluated due to +/// a constexpr specifier. +static bool isManifestlyEvaluatedVar(Sema &SemaRef, const Decl *D) { + if (!SemaRef.getLangOpts().CPlusPlus2a) + return false; + + if (const VarDecl *Var = dyn_cast_or_null(D)) + return Var->isConstexpr(); + + return false; +} + /// Determine whether the given declaration is a global variable or /// static data member. static bool isNonlocalVariable(const Decl *D) { @@ -16777,7 +16790,12 @@ // If we are parsing the initializer for a static data member, push a // new expression evaluation context that is associated with this static // data member. - if (isNonlocalVariable(D)) + if (isManifestlyEvaluatedVar(*this, D)) { + using ExpressionKind = ExpressionEvaluationContextRecord::ExpressionKind; + + PushExpressionEvaluationContext( + ExpressionEvaluationContext::ConstantEvaluated, D, ExpressionKind::EK_ConstexprVarInit); + } else if (isNonlocalVariable(D)) PushExpressionEvaluationContext( ExpressionEvaluationContext::PotentiallyEvaluated, D); } @@ -16788,7 +16806,7 @@ if (!D || D->isInvalidDecl()) return; - if (isNonlocalVariable(D)) + if (isManifestlyEvaluatedVar(*this, D) || isNonlocalVariable(D)) PopExpressionEvaluationContext(); if (S && D->isOutOfLine()) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15369,9 +15369,16 @@ } } +// Check to see if this expression is part of a decltype specifier. +// We're not interested in evaluating this expression, immediately or otherwise. +static bool isInDeclType(Sema &SemaRef) { + return SemaRef.ExprEvalContexts.back().ExprContext == + Sema::ExpressionEvaluationContextRecord::EK_Decltype; +} + ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { if (!E.isUsable() || !Decl || !Decl->isConsteval() || isConstantEvaluated() || - RebuildingImmediateInvocation) + isInDeclType(*this) || RebuildingImmediateInvocation) return E; /// Opportunistically remove the callee from ReferencesToConsteval if we can. @@ -15532,11 +15539,12 @@ } void Sema::PopExpressionEvaluationContext() { + using ExpressionKind = ExpressionEvaluationContextRecord::ExpressionKind; + ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); unsigned NumTypos = Rec.NumTypos; if (!Rec.Lambdas.empty()) { - using ExpressionKind = ExpressionEvaluationContextRecord::ExpressionKind; if (Rec.ExprContext == ExpressionKind::EK_TemplateArgument || Rec.isUnevaluated() || (Rec.isConstantEvaluated() && !getLangOpts().CPlusPlus17)) { unsigned D; @@ -15577,7 +15585,16 @@ // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they // will never be constructed. - if (Rec.isUnevaluated() || Rec.isConstantEvaluated()) { + // + // FIXME: The condition Rec.ExprContext != EK_ConstexprVarInit + // has been added as a stop gap fix. After adding a ConstEvaluated + // context to constexpr variable initializers, an issue manifests here + // where the cleanups are dropped prior to VarDecl::evaluateValue, which + // subsequently requires them. This has been done as a temporary workaround + // as it was not obvious how to produce. + if (Rec.isUnevaluated() || + (Rec.isConstantEvaluated() && + Rec.ExprContext != ExpressionKind::EK_ConstexprVarInit)) { ExprCleanupObjects.erase(ExprCleanupObjects.begin() + Rec.NumCleanupObjects, ExprCleanupObjects.end()); Cleanup = Rec.ParentCleanup; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -4644,7 +4644,10 @@ Function->setInnerLocStart(PatternDecl->getInnerLocStart()); EnterExpressionEvaluationContext EvalContext( - *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); + *this, + PatternDecl->getConstexprKind() == CSK_consteval + ? Sema::ExpressionEvaluationContext::ConstantEvaluated + : Sema::ExpressionEvaluationContext::PotentiallyEvaluated); // Introduce a new scope where local variable instantiations will be // recorded, unless we're actually a member function within a local @@ -4947,8 +4950,11 @@ Var->setImplicitlyInline(); if (OldVar->getInit()) { + bool IsConstexpr = OldVar->isConstexpr() || isConstantEvaluated(); EnterExpressionEvaluationContext Evaluated( - *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); + *this, getLangOpts().CPlusPlus2a && IsConstexpr ? + Sema::ExpressionEvaluationContext::ConstantEvaluated : + Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); // Instantiate the initializer. ExprResult Init; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -12079,6 +12079,8 @@ // FIXME: Sema's lambda-building mechanism expects us to push an expression // evaluation context even if we're not transforming the function body. getSema().PushExpressionEvaluationContext( + E->getCallOperator()->getConstexprKind() == CSK_consteval ? + Sema::ExpressionEvaluationContext::ConstantEvaluated : Sema::ExpressionEvaluationContext::PotentiallyEvaluated); // Instantiate the body of the lambda expression.