diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index cda3b1ee62f2b..9ad28b3884a6e 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -144,6 +144,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the they are now correctly accepted on such pointers. #### Bug Fixes to C++ Support +- Instantiate constexpr functions as needed before they are evaluated. (#GH73232) (#GH35052) (#GH100897) #### Bug Fixes to AST Handling diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 0a6f256afa2cc..891a5c1abc595 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -70,6 +70,7 @@ class Module; class NamespaceDecl; class ParmVarDecl; class RecordDecl; +class SemaProxy; class Stmt; class StringLiteral; class TagDecl; @@ -1438,6 +1439,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { private: const APValue *evaluateValueImpl(SmallVectorImpl *Notes, + SemaProxy *SP, bool IsConstantInitialization) const; public: @@ -1454,6 +1456,15 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { /// not. bool evaluateDestruction(SmallVectorImpl &Notes) const; + /// Evaluate the destruction of a variable that is required by the language to + /// have constant destruction. + /// + /// \pre hasConstantInitialization() + /// \return \c true if this variable has constant destruction, \c false if + /// not. + bool evaluateConstantDestruction(SmallVectorImpl &Notes, + SemaProxy &SP) const; + /// Determine whether this variable has constant initialization. /// /// This is only set in two cases: when the language semantics require @@ -1470,8 +1481,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable { /// Evaluate the initializer of this variable to determine whether it's a /// constant initializer. Should only be called once, after completing the /// definition of the variable. - bool checkForConstantInitialization( - SmallVectorImpl &Notes) const; + bool + checkForConstantInitialization(SmallVectorImpl &Notes, + SemaProxy &SP) const; void setInitStyle(InitializationStyle Style) { VarDeclBits.InitStyle = Style; diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 53107e2794174..51f51d0f24548 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -55,6 +55,7 @@ namespace clang { class ObjCPropertyRefExpr; class OpaqueValueExpr; class ParmVarDecl; + class SemaProxy; class StringLiteral; class TargetInfo; class ValueDecl; @@ -667,6 +668,13 @@ class Expr : public ValueStmt { bool EvaluateAsRValue(EvalResult &Result, const ASTContext &Ctx, bool InConstantContext = false) const; + /// Evaluate an expression that is required by the language to be a constant + /// expression, and fold the resulting rvalue constant into Result. If the + /// expression is a glvalue, an lvalue-to-rvalue conversion will be applied. + bool EvaluateAsMandatedConstantRValue(EvalResult &Result, + const ASTContext &Ctx, + SemaProxy &SP) const; + /// EvaluateAsBooleanCondition - Return true if this is a constant /// which we can fold and convert to a boolean condition using /// any crazy technique that we want to, even if the expression has @@ -743,6 +751,13 @@ class Expr : public ValueStmt { EvalResult &Result, bool IsConstantInitializer) const; + /// Evaluate an expression that is required by the language to be a constant + /// expression. + bool EvaluateAsMandatedConstantInitializer(EvalResult &Result, + const ASTContext &Ctx, + SemaProxy &SP, + const VarDecl *VD) const; + /// EvaluateWithSubstitution - Evaluate an expression as if from the context /// of a call to the given function with the given arguments, inside an /// unevaluated context. Returns true if the expression could be folded to a @@ -773,6 +788,12 @@ class Expr : public ValueStmt { EvalResult &Result, const ASTContext &Ctx, ConstantExprKind Kind = ConstantExprKind::Normal) const; + /// Evaluate an expression that is required by the language to be a constant + /// expression. + bool EvaluateAsMandatedConstantExpr( + EvalResult &Result, const ASTContext &Ctx, SemaProxy &SP, + ConstantExprKind Kind = ConstantExprKind::Normal) const; + /// If the current Expr is a pointer, this will try to statically /// determine the number of bytes available where the pointer is pointing. /// Returns true if all of the above holds and we were able to figure out the @@ -792,11 +813,11 @@ class Expr : public ValueStmt { bool EvaluateCharRangeAsString(std::string &Result, const Expr *SizeExpression, const Expr *PtrExpression, ASTContext &Ctx, - EvalResult &Status) const; + SemaProxy &SP, EvalResult &Status) const; bool EvaluateCharRangeAsString(APValue &Result, const Expr *SizeExpression, const Expr *PtrExpression, ASTContext &Ctx, - EvalResult &Status) const; + SemaProxy &SP, EvalResult &Status) const; /// If the current Expr can be evaluated to a pointer to a null-terminated /// constant string, return the constant string (without the terminating diff --git a/clang/include/clang/AST/SemaProxy.h b/clang/include/clang/AST/SemaProxy.h new file mode 100644 index 0000000000000..ab6a87f1e32a5 --- /dev/null +++ b/clang/include/clang/AST/SemaProxy.h @@ -0,0 +1,40 @@ +//===--- SemaProxy.h - Interface to language semantics ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the SemaProxy interface, used during language-mandated +// constant evaluation to act on, and query, the representation of the program +// according to language-defined semantics. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_SEMA_PROXY_H +#define LLVM_CLANG_AST_SEMA_PROXY_H + +#include "clang/Basic/SourceLocation.h" + +namespace clang { + +class FunctionDecl; + +/// Classes implementing SemaProxy present a restricted view of the (possibly +/// mutating) actions and queries defined by language semantics against the +/// representation of the program (i.e., the AST). Such a view is required in +/// order to evaluate certain expressions (e.g., C++'s manifestly +/// constant-evaluated expressions) according to language rules. +class SemaProxy { +public: + virtual ~SemaProxy() = default; + + virtual void + instantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function) = 0; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_AST_SEMA_PROXY_H diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index bb5697a16e090..249343f533e58 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -30,6 +30,7 @@ #include "clang/AST/ExternalASTSource.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OperationKinds.h" +#include "clang/AST/SemaProxy.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -909,6 +910,7 @@ class Sema final : public SemaBase { // 34. FixIt Helpers (SemaFixItUtils.cpp) // 35. Function Effects (SemaFunctionEffects.cpp) // 36. C++ Expansion Statements (SemaExpand.cpp) + // 37. Proxy to Sema for Constant Evaluation (SemaProxy.cpp) /// \name Semantic Analysis /// Implementations are in Sema.cpp @@ -15887,6 +15889,27 @@ class Sema final : public SemaBase { std::optional ComputeExpansionSize(CXXExpansionStmtPattern *Expansion); ///@} + + // + // + // ------------------------------------------------------------------------- + // + // + + /// \name Proxy to Sema for Constant Evaluation + /// Implementations are in SemaProxy.cpp + ///@{ +public: + SemaProxy &getProxyForEval() const { + assert(ProxyForEval); + return *ProxyForEval; + } + +private: + std::unique_ptr ProxyForEval; + + static SemaProxy *makeProxyForEval(Sema &SemaRef); + ///@} }; DeductionFailureInfo diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index f59485ec306e4..8319171ab1e89 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -21,6 +21,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/SemaProxy.h" #include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/StringExtras.h" @@ -1849,9 +1850,25 @@ bool CheckBitCast(InterpState &S, CodePtr OpPC, const Type *TargetType, return true; } -static void compileFunction(InterpState &S, const Function *Func) { +static void compileFunction(InterpState &S, const Function *Func, + CodePtr OpPC) { + const FunctionDecl *Fn = Func->getDecl(); + + // [C++26] [temp.inst] p5 + // [...] the function template specialization is implicitly instantiated + // when the specialization is referenced in a context that requires a function + // definition to exist or if the existence of the definition affects the + // semantics of the program. + if (FunctionDefinitionCanBeLazilyInstantiated(Fn) && S.inConstantContext()) { + SemaProxy *SP = S.getSemaProxy(); + if (!SP) + return; + SP->instantiateFunctionDefinition(S.Current->getLocation(OpPC), + const_cast(Fn)); + } + const FunctionDecl *Definition; - if (!Func->getDecl()->getBody(Definition)) + if (!Fn->getBody(Definition)) return; if (!Definition) return; @@ -1884,7 +1901,7 @@ bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func, } if (!Func->isFullyCompiled()) - compileFunction(S, Func); + compileFunction(S, Func, OpPC); if (!CheckCallable(S, OpPC, Func)) return false; @@ -1971,7 +1988,7 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func, } if (!Func->isFullyCompiled()) - compileFunction(S, Func); + compileFunction(S, Func, OpPC); if (!CheckCallable(S, OpPC, Func)) return cleanup(); @@ -2417,7 +2434,7 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize, // because the Call/CallVirt below might access the instance pointer // but the Function's information about them is wrong. if (!F->isFullyCompiled()) - compileFunction(S, F); + compileFunction(S, F, OpPC); if (!CheckCallable(S, OpPC, F)) return false; diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index f6338cda56e34..bab2a92464146 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -19,8 +19,8 @@ using namespace clang::interp; InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx, SourceMapper *M) - : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(M), P(P), Stk(Stk), - Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame), + : State(Ctx.getASTContext(), Parent.getSemaProxy(), Parent.getEvalStatus()), + M(M), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) { InConstantContext = Parent.InConstantContext; @@ -32,9 +32,9 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx, const Function *Func) - : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), P(P), - Stk(Stk), Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame), - StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), + : State(Ctx.getASTContext(), Parent.getSemaProxy(), Parent.getEvalStatus()), + M(nullptr), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), + Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) { InConstantContext = Parent.InConstantContext; CheckingPotentialConstantExpression = diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h index df3afdf8cbc24..e9e94d4ffeb9b 100644 --- a/clang/lib/AST/ByteCode/State.h +++ b/clang/lib/AST/ByteCode/State.h @@ -20,6 +20,7 @@ namespace clang { class OptionalDiagnostic; +class SemaProxy; /// Kinds of access we can perform on an object, for diagnostics. Note that /// we consider a member function call to be a kind of access, even though @@ -80,8 +81,8 @@ class SourceInfo; /// Interface for the VM to interact with the AST walker's context. class State { public: - State(ASTContext &ASTCtx, Expr::EvalStatus &EvalStatus) - : Ctx(ASTCtx), EvalStatus(EvalStatus) {} + State(ASTContext &ASTCtx, SemaProxy *Sema, Expr::EvalStatus &EvalStatus) + : Ctx(ASTCtx), Sema(Sema), EvalStatus(EvalStatus) {} virtual ~State(); virtual const Frame *getCurrentFrame() = 0; @@ -90,6 +91,7 @@ class State { Expr::EvalStatus &getEvalStatus() const { return EvalStatus; } ASTContext &getASTContext() const { return Ctx; } + SemaProxy *getSemaProxy() const { return Sema; } const LangOptions &getLangOpts() const { return Ctx.getLangOpts(); } /// Note that we have had a side-effect, and determine whether we should @@ -188,6 +190,7 @@ class State { EvaluationMode EvalMode; ASTContext &Ctx; + SemaProxy *Sema; Expr::EvalStatus &EvalStatus; private: diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 4eaef0d87f3e5..bb9b644901dd0 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -32,6 +32,7 @@ #include "clang/AST/Randstruct.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Redeclarable.h" +#include "clang/AST/SemaProxy.h" #include "clang/AST/Stmt.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/Type.h" @@ -2552,12 +2553,13 @@ EvaluatedStmt *VarDecl::getEvaluatedStmt() const { } const APValue *VarDecl::evaluateValue() const { - return evaluateValueImpl(/*Notes=*/nullptr, hasConstantInitialization()); + return evaluateValueImpl(/*Notes=*/nullptr, /*Sema=*/nullptr, + hasConstantInitialization()); } const APValue * VarDecl::evaluateValueImpl(SmallVectorImpl *Notes, - bool IsConstantInitialization) const { + SemaProxy *SP, bool IsConstantInitialization) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); const auto *Init = getInit(); @@ -2580,7 +2582,10 @@ VarDecl::evaluateValueImpl(SmallVectorImpl *Notes, Expr::EvalResult EStatus; EStatus.Diag = Notes; bool Result = - Init->EvaluateAsInitializer(Ctx, this, EStatus, IsConstantInitialization); + isConstexpr() + ? Init->EvaluateAsMandatedConstantInitializer(EStatus, Ctx, *SP, this) + : Init->EvaluateAsInitializer(Ctx, this, EStatus, + IsConstantInitialization); Eval->Evaluated = std::move(EStatus.Val); // In C++, or in C23 if we're initialising a 'constexpr' variable, this isn't @@ -2644,7 +2649,7 @@ bool VarDecl::hasConstantInitialization() const { } bool VarDecl::checkForConstantInitialization( - SmallVectorImpl &Notes) const { + SmallVectorImpl &Notes, SemaProxy &SP) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); // If we ask for the value before we know whether we have a constant // initializer, we can compute the wrong value (for example, due to @@ -2659,7 +2664,7 @@ bool VarDecl::checkForConstantInitialization( // Evaluate the initializer to check whether it's a constant expression. Eval->HasConstantInitialization = - evaluateValueImpl(&Notes, true) && Notes.empty(); + evaluateValueImpl(&Notes, &SP, true) && Notes.empty(); // If evaluation as a constant initializer failed, allow re-evaluation as a // non-constant initializer if we later find we want the value. diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h index 7ba3071668bd1..e824d2e1c47ac 100644 --- a/clang/lib/AST/ExprConstShared.h +++ b/clang/lib/AST/ExprConstShared.h @@ -30,6 +30,7 @@ class ASTContext; class CharUnits; class Expr; class CallExpr; +class FunctionDecl; } // namespace clang using namespace clang; /// Values returned by __builtin_classify_type, chosen to match the values @@ -110,4 +111,7 @@ std::optional EvalScalarMinMaxFp(const llvm::APFloat &A, const llvm::APFloat &B, std::optional RoundingMode, bool IsMin); +/// Whether we can instantiate FD during constant evaluation +bool FunctionDefinitionCanBeLazilyInstantiated(const FunctionDecl *FD); + #endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index ff44138d66816..b267dde5f3d62 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -48,6 +48,7 @@ #include "clang/AST/OSLog.h" #include "clang/AST/OptionalDiagnostic.h" #include "clang/AST/RecordLayout.h" +#include "clang/AST/SemaProxy.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" @@ -913,8 +914,9 @@ namespace { /// initialization. uint64_t ArrayInitIndex = -1; - EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) - : State(const_cast(C), S), CurrentCall(nullptr), + EvalInfo(const ASTContext &C, SemaProxy *Sema, Expr::EvalStatus &S, + EvaluationMode Mode) + : State(const_cast(C), Sema, S), CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), StepsLeft(C.getLangOpts().ConstexprStepLimit), EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp), @@ -7136,6 +7138,30 @@ static bool handleTrivialCopy(EvalInfo &Info, const ParmVarDecl *Param, CopyObjectRepresentation); } +bool FunctionDefinitionCanBeLazilyInstantiated(const FunctionDecl *FD) { + if (FD->isDefined() || !FD->isImplicitlyInstantiable() || !FD->isConstexpr()) + return false; + + FunctionDecl *Pattern = FD->getTemplateInstantiationPattern(); + return Pattern && Pattern->isDefined(); +} + +static void TryInstantiateFunctionBeforeCall(const FunctionDecl *FD, + EvalInfo &Info, + SourceLocation Loc) { + + // [C++26] [temp.inst] p5 + // [...] the function template specialization is implicitly instantiated + // when the specialization is referenced in a context that requires a function + // definition to exist or if the existence of the definition affects the + // semantics of the program. + + SemaProxy *SP = Info.getSemaProxy(); + if (SP && FunctionDefinitionCanBeLazilyInstantiated(FD) && + Info.InConstantContext) + SP->instantiateFunctionDefinition(Loc, const_cast(FD)); +} + /// Evaluate a function call. static bool HandleFunctionCall(SourceLocation CallLoc, const FunctionDecl *Callee, @@ -7555,6 +7581,8 @@ static bool HandleDestructionImpl(EvalInfo &Info, SourceRange CallRange, if (!Info.CheckCallLimit(CallRange.getBegin())) return false; + TryInstantiateFunctionBeforeCall(DD, Info, CallRange.getBegin()); + const FunctionDecl *Definition = nullptr; const Stmt *Body = DD->getBody(Definition); @@ -9047,9 +9075,12 @@ class ExprEvaluatorBase CallScope.destroy(); } + SourceLocation Loc = E->getExprLoc(); + + TryInstantiateFunctionBeforeCall(FD, Info, Loc); + const FunctionDecl *Definition = nullptr; Stmt *Body = FD->getBody(Definition); - SourceLocation Loc = E->getExprLoc(); // Treat the object argument as `this` when evaluating defaulted // special menmber functions @@ -11705,6 +11736,8 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E, return handleDefaultInitValue(T, Result); } + TryInstantiateFunctionBeforeCall(FD, Info, E->getBeginLoc()); + const FunctionDecl *Definition = nullptr; auto Body = FD->getBody(Definition); @@ -11746,6 +11779,8 @@ bool RecordExprEvaluator::VisitCXXInheritedCtorInitExpr( if (FD->isInvalidDecl() || FD->getParent()->isInvalidDecl()) return false; + TryInstantiateFunctionBeforeCall(FD, Info, E->getBeginLoc()); + const FunctionDecl *Definition = nullptr; auto Body = FD->getBody(Definition); @@ -21848,11 +21883,23 @@ bool Expr::EvaluateAsRValue(EvalResult &Result, const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsRValue"); - EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Result, + EvaluationMode::IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsRValue(this, Result, Ctx, Info); } +bool Expr::EvaluateAsMandatedConstantRValue(EvalResult &Result, + const ASTContext &Ctx, + SemaProxy &SP) const { + assert(!isValueDependent() && + "Expression evaluator can't be called on a dependent expression."); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsMandatedConstantRValue"); + EvalInfo Info(Ctx, &SP, Result, EvaluationMode::IgnoreSideEffects); + Info.InConstantContext = true; + return ::EvaluateAsRValue(this, Result, Ctx, Info); +} + bool Expr::EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx, bool InConstantContext) const { assert(!isValueDependent() && @@ -21869,7 +21916,8 @@ bool Expr::EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsInt"); - EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Result, + EvaluationMode::IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsInt(this, Result, Ctx, AllowSideEffects, Info); } @@ -21880,7 +21928,8 @@ bool Expr::EvaluateAsFixedPoint(EvalResult &Result, const ASTContext &Ctx, assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsFixedPoint"); - EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Result, + EvaluationMode::IgnoreSideEffects); Info.InConstantContext = InConstantContext; return ::EvaluateAsFixedPoint(this, Result, Ctx, AllowSideEffects, Info); } @@ -21911,7 +21960,7 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, "Expression evaluator can't be called on a dependent expression."); ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsLValue"); - EvalInfo Info(Ctx, Result, EvaluationMode::ConstantFold); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Result, EvaluationMode::ConstantFold); Info.InConstantContext = InConstantContext; LValue LV; CheckedTemporaries CheckedTemps; @@ -21938,11 +21987,12 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx, return true; } -static bool EvaluateDestruction(const ASTContext &Ctx, APValue::LValueBase Base, +static bool EvaluateDestruction(const ASTContext &Ctx, SemaProxy *SP, + APValue::LValueBase Base, APValue DestroyedValue, QualType Type, SourceLocation Loc, Expr::EvalStatus &EStatus, bool IsConstantDestruction) { - EvalInfo Info(Ctx, EStatus, + EvalInfo Info(Ctx, SP, EStatus, IsConstantDestruction ? EvaluationMode::ConstantExpression : EvaluationMode::ConstantFold); Info.setEvaluatingDecl(Base, DestroyedValue, @@ -21962,36 +22012,27 @@ static bool EvaluateDestruction(const ASTContext &Ctx, APValue::LValueBase Base, return true; } -bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, - ConstantExprKind Kind) const { - assert(!isValueDependent() && - "Expression evaluator can't be called on a dependent expression."); - bool IsConst; - if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) && - Result.Val.hasValue()) - return true; - - ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr"); - EvaluationMode EM = EvaluationMode::ConstantExpression; - EvalInfo Info(Ctx, Result, EM); +static bool EvaluateConstantExpr(Expr::EvalResult &Result, + const ASTContext &Ctx, EvalInfo &Info, + const Expr *E, ConstantExprKind Kind) { Info.InConstantContext = true; if (Info.EnableNewConstInterp) { - if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, Kind)) + if (!Info.Ctx.getInterpContext().evaluate(Info, E, Result.Val, Kind)) return false; - return CheckConstantExpression(Info, getExprLoc(), - getStorageType(Ctx, this), Result.Val, Kind); + return CheckConstantExpression(Info, E->getExprLoc(), + getStorageType(Ctx, E), Result.Val, Kind); } // The type of the object we're initializing is 'const T' for a class NTTP. - QualType T = getType(); + QualType T = E->getType(); if (Kind == ConstantExprKind::ClassTemplateArgument) T.addConst(); // If we're evaluating a prvalue, fake up a MaterializeTemporaryExpr to // represent the result of the evaluation. CheckConstantExpression ensures // this doesn't escape. - MaterializeTemporaryExpr BaseMTE(T, const_cast(this), true); + MaterializeTemporaryExpr BaseMTE(T, const_cast(E), true); APValue::LValueBase Base(&BaseMTE); Info.setEvaluatingDecl(Base, Result.Val); @@ -22002,14 +22043,14 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, // So we need to make sure temporary objects are destroyed after having // evaluating the expression (per C++23 [class.temporary]/p4). FullExpressionRAII Scope(Info); - if (!::EvaluateInPlace(Result.Val, Info, LVal, this) || - Result.HasSideEffects || !Scope.destroy()) + if (!::EvaluateInPlace(Result.Val, Info, LVal, E) || Result.HasSideEffects || + !Scope.destroy()) return false; if (!Info.discardCleanups()) llvm_unreachable("Unhandled cleanup; missing full expression marker?"); - if (!CheckConstantExpression(Info, getExprLoc(), getStorageType(Ctx, this), + if (!CheckConstantExpression(Info, E->getExprLoc(), getStorageType(Ctx, E), Result.Val, Kind)) return false; if (!CheckMemoryLeaks(Info)) @@ -22018,8 +22059,8 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, // If this is a class template argument, it's required to have constant // destruction too. if (Kind == ConstantExprKind::ClassTemplateArgument && - (!EvaluateDestruction(Ctx, Base, Result.Val, T, getBeginLoc(), Result, - true) || + (!EvaluateDestruction(Ctx, Info.getSemaProxy(), Base, Result.Val, T, + E->getBeginLoc(), Result, true) || Result.HasSideEffects)) { // FIXME: Prefix a note to indicate that the problem is lack of constant // destruction. @@ -22029,34 +22070,50 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, return true; } -bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, - Expr::EvalResult &EStatus, - bool IsConstantInitialization) const { +bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, + ConstantExprKind Kind) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); - assert(VD && "Need a valid VarDecl"); + bool IsConst; + if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) && + Result.Val.hasValue()) + return true; - llvm::TimeTraceScope TimeScope("EvaluateAsInitializer", [&] { - std::string Name; - llvm::raw_string_ostream OS(Name); - VD->printQualifiedName(OS); - return Name; - }); + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr"); + EvaluationMode EM = EvaluationMode::ConstantExpression; + EvalInfo Info(Ctx, /*Sema=*/nullptr, Result, EM); - EvalInfo Info(Ctx, EStatus, - (IsConstantInitialization && - (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) - ? EvaluationMode::ConstantExpression - : EvaluationMode::ConstantFold); - Info.setEvaluatingDecl(VD, EStatus.Val); - Info.InConstantContext = IsConstantInitialization; + return ::EvaluateConstantExpr(Result, Ctx, Info, this, Kind); +} + +bool Expr::EvaluateAsMandatedConstantExpr(EvalResult &Result, + const ASTContext &Ctx, + SemaProxy &Sema, + ConstantExprKind Kind) const { + assert(!isValueDependent() && + "Expression evaluator can't be called on a dependent expression."); + bool IsConst; + if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) && + Result.Val.hasValue()) + return true; + + ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsMandatedConstantExpr"); + EvaluationMode EM = EvaluationMode::ConstantExpression; + EvalInfo Info(Ctx, &Sema, Result, EM); + + return ::EvaluateConstantExpr(Result, Ctx, Info, this, Kind); +} +static bool EvaluateInitializer(const ASTContext &Ctx, EvalInfo &Info, + const VarDecl *VD, const Expr *E, + Expr::EvalResult &EStatus, + bool IsConstantInitialization) { SourceLocation DeclLoc = VD->getLocation(); QualType DeclTy = VD->getType(); if (Info.EnableNewConstInterp) { auto &InterpCtx = Ctx.getInterpContext(); - if (!InterpCtx.evaluateAsInitializer(Info, VD, this, EStatus.Val)) + if (!InterpCtx.evaluateAsInitializer(Info, VD, E, EStatus.Val)) return false; return CheckConstantExpression(Info, DeclLoc, DeclTy, EStatus.Val, @@ -22076,7 +22133,7 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, // serialization code calls ParmVarDecl::getDefaultArg() which strips the // outermost FullExpr, such as ExprWithCleanups. FullExpressionRAII Scope(Info); - if (!EvaluateInPlace(EStatus.Val, Info, LVal, this, + if (!EvaluateInPlace(EStatus.Val, Info, LVal, E, /*AllowNonLiteralTypes=*/true) || EStatus.HasSideEffects) return false; @@ -22095,59 +22152,126 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, CheckMemoryLeaks(Info); } -bool VarDecl::evaluateDestruction( - SmallVectorImpl &Notes) const { +bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, + Expr::EvalResult &EStatus, + bool IsConstantInitialization) const { + assert(!isValueDependent() && + "Expression evaluator can't be called on a dependent expression."); + assert(VD && "Need a valid VarDecl"); + + llvm::TimeTraceScope TimeScope("EvaluateAsInitializer", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + VD->printQualifiedName(OS); + return Name; + }); + + EvalInfo Info(Ctx, /*Sema=*/nullptr, EStatus, + (IsConstantInitialization && + (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23)) + ? EvaluationMode::ConstantExpression + : EvaluationMode::ConstantFold); + Info.setEvaluatingDecl(VD, EStatus.Val); + Info.InConstantContext = IsConstantInitialization; + + return ::EvaluateInitializer(Ctx, Info, VD, this, EStatus, + IsConstantInitialization); +} + +bool Expr::EvaluateAsMandatedConstantInitializer(EvalResult &EStatus, + const ASTContext &Ctx, + SemaProxy &Sema, + const VarDecl *VD) const { + assert(!isValueDependent() && + "Expression evaluator can't be called on a dependent expression."); + assert(VD && "Need a valid VarDecl"); + + llvm::TimeTraceScope TimeScope("EvaluateAsMandatedConstantInitializer", [&] { + std::string Name; + llvm::raw_string_ostream OS(Name); + VD->printQualifiedName(OS); + return Name; + }); + + EvalInfo Info(Ctx, &Sema, EStatus, EvaluationMode::ConstantExpression); + Info.setEvaluatingDecl(VD, EStatus.Val); + Info.InConstantContext = true; + + return ::EvaluateInitializer(Ctx, Info, VD, this, EStatus, + /*IsConstantInitialization=*/true); +} + +static bool evaluateDestruction(Expr::EvalStatus EStatus, EvalInfo &Info, + const VarDecl *VD, bool IsConstantDestruction) { + ASTContext &Ctx = VD->getASTContext(); + // This function is only meaningful for records and arrays of records. - QualType VarTy = getType(); + QualType VarTy = VD->getType(); if (VarTy->isArrayType()) { - QualType ElemTy = getASTContext().getBaseElementType(VarTy); + QualType ElemTy = Ctx.getBaseElementType(VarTy); if (!ElemTy->isRecordType()) { - ensureEvaluatedStmt()->HasConstantDestruction = true; + VD->ensureEvaluatedStmt()->HasConstantDestruction = true; return true; } } else if (!VarTy->isRecordType()) { - ensureEvaluatedStmt()->HasConstantDestruction = true; + VD->ensureEvaluatedStmt()->HasConstantDestruction = true; return true; } - Expr::EvalStatus EStatus; - EStatus.Diag = &Notes; - - // Only treat the destruction as constant destruction if we formally have - // constant initialization (or are usable in a constant expression). - bool IsConstantDestruction = hasConstantInitialization(); - ASTContext &Ctx = getASTContext(); + Info.InConstantContext = IsConstantDestruction; // Make a copy of the value for the destructor to mutate, if we know it. // Otherwise, treat the value as default-initialized; if the destructor works // anyway, then the destruction is constant (and must be essentially empty). APValue DestroyedValue; - if (getEvaluatedValue()) - DestroyedValue = *getEvaluatedValue(); + if (VD->getEvaluatedValue()) + DestroyedValue = *VD->getEvaluatedValue(); else if (!handleDefaultInitValue(VarTy, DestroyedValue)) return false; if (Ctx.getLangOpts().EnableNewConstInterp) { - EvalInfo Info(Ctx, EStatus, - IsConstantDestruction ? EvaluationMode::ConstantExpression - : EvaluationMode::ConstantFold); - Info.InConstantContext = IsConstantDestruction; - if (!Ctx.getInterpContext().evaluateDestruction(Info, this, + if (!Ctx.getInterpContext().evaluateDestruction(Info, VD, std::move(DestroyedValue))) return false; - ensureEvaluatedStmt()->HasConstantDestruction = true; + VD->ensureEvaluatedStmt()->HasConstantDestruction = true; return true; } - if (!EvaluateDestruction(Ctx, this, std::move(DestroyedValue), VarTy, - getLocation(), EStatus, IsConstantDestruction) || + if (!EvaluateDestruction(Ctx, Info.getSemaProxy(), VD, + std::move(DestroyedValue), VarTy, VD->getLocation(), + EStatus, IsConstantDestruction) || EStatus.HasSideEffects) return false; - ensureEvaluatedStmt()->HasConstantDestruction = true; + VD->ensureEvaluatedStmt()->HasConstantDestruction = true; return true; } +bool VarDecl::evaluateDestruction( + SmallVectorImpl &Notes) const { + Expr::EvalStatus EStatus; + EStatus.Diag = &Notes; + + bool IsConstantDestruction = hasConstantInitialization(); + + EvalInfo Info(getASTContext(), /*Sema=*/nullptr, EStatus, + IsConstantDestruction ? EvaluationMode::ConstantExpression + : EvaluationMode::ConstantFold); + return ::evaluateDestruction(EStatus, Info, this, IsConstantDestruction); +} + +bool VarDecl::evaluateConstantDestruction( + SmallVectorImpl &Notes, SemaProxy &SP) const { + Expr::EvalStatus EStatus; + EStatus.Diag = &Notes; + + bool IsConstantDestruction = hasConstantInitialization(); + + EvalInfo Info(getASTContext(), &SP, EStatus, + EvaluationMode::ConstantExpression); + return ::evaluateDestruction(EStatus, Info, this, IsConstantDestruction); +} + /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be /// constant folded, but discard the result. bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK) const { @@ -22165,7 +22289,8 @@ APSInt Expr::EvaluateKnownConstInt(const ASTContext &Ctx) const { ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstInt"); EvalResult EVResult; - EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects); + EvalInfo Info(Ctx, /*Sema=*/nullptr, EVResult, + EvaluationMode::IgnoreSideEffects); Info.InConstantContext = true; bool Result = ::EvaluateAsRValue(this, EVResult, Ctx, Info); @@ -22184,7 +22309,8 @@ APSInt Expr::EvaluateKnownConstIntCheckOverflow( ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateKnownConstIntCheckOverflow"); EvalResult EVResult; EVResult.Diag = Diag; - EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects); + EvalInfo Info(Ctx, /*Sema=*/nullptr, EVResult, + EvaluationMode::IgnoreSideEffects); Info.InConstantContext = true; Info.CheckingForUndefinedBehavior = true; @@ -22204,7 +22330,8 @@ void Expr::EvaluateForOverflow(const ASTContext &Ctx) const { bool IsConst; EvalResult EVResult; if (!FastEvaluateAsRValue(this, EVResult.Val, Ctx, IsConst)) { - EvalInfo Info(Ctx, EVResult, EvaluationMode::IgnoreSideEffects); + EvalInfo Info(Ctx, /*Sema=*/nullptr, EVResult, + EvaluationMode::IgnoreSideEffects); Info.CheckingForUndefinedBehavior = true; (void)::EvaluateAsRValue(Info, this, EVResult.Val); } @@ -22258,7 +22385,8 @@ static ICEDiag Worst(ICEDiag A, ICEDiag B) { return A.Kind >= B.Kind ? A : B; } static ICEDiag CheckEvalInICE(const Expr* E, const ASTContext &Ctx) { Expr::EvalResult EVResult; Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, + EvaluationMode::ConstantExpression); Info.InConstantContext = true; if (!::EvaluateAsRValue(E, EVResult, Ctx, Info) || EVResult.HasSideEffects || @@ -22763,7 +22891,8 @@ Expr::getIntegerConstantExpr(const ASTContext &Ctx) const { // value. EvalResult ExprResult; Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::IgnoreSideEffects); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, + EvaluationMode::IgnoreSideEffects); Info.InConstantContext = true; if (!::EvaluateAsInt(this, ExprResult, Ctx, SE_AllowSideEffects, Info)) @@ -22797,7 +22926,8 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result) const { // Build evaluation settings. Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, + EvaluationMode::ConstantExpression); bool IsConstExpr = ::EvaluateAsRValue(Info, this, Result ? *Result : Scratch) && @@ -22824,7 +22954,8 @@ bool Expr::EvaluateWithSubstitution(APValue &Value, ASTContext &Ctx, }); Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, + EvaluationMode::ConstantExpressionUnevaluated); Info.InConstantContext = true; if (Info.EnableNewConstInterp) { @@ -22910,7 +23041,7 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD, Expr::EvalStatus Status; Status.Diag = &Diags; - EvalInfo Info(FD->getASTContext(), Status, + EvalInfo Info(FD->getASTContext(), /*Sema=*/nullptr, Status, EvaluationMode::ConstantExpression); Info.InConstantContext = true; Info.CheckingPotentialConstantExpression = true; @@ -22960,7 +23091,7 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E, Expr::EvalStatus Status; Status.Diag = &Diags; - EvalInfo Info(FD->getASTContext(), Status, + EvalInfo Info(FD->getASTContext(), /*Sema=*/nullptr, Status, EvaluationMode::ConstantExpressionUnevaluated); Info.InConstantContext = true; Info.CheckingPotentialConstantExpression = true; @@ -22985,7 +23116,7 @@ std::optional Expr::tryEvaluateObjectSize(const ASTContext &Ctx, return std::nullopt; Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, EvaluationMode::ConstantFold); if (Info.EnableNewConstInterp) return Info.Ctx.getInterpContext().tryEvaluateObjectSize(Info, this, Type); return tryEvaluateBuiltinObjectSize(this, Type, Info); @@ -23044,7 +23175,7 @@ EvaluateBuiltinStrLen(const Expr *E, EvalInfo &Info, std::optional Expr::tryEvaluateString(ASTContext &Ctx) const { Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, EvaluationMode::ConstantFold); std::string StringResult; if (Info.EnableNewConstInterp) { @@ -23062,9 +23193,9 @@ template static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, const Expr *SizeExpression, const Expr *PtrExpression, - ASTContext &Ctx, + ASTContext &Ctx, SemaProxy &SP, Expr::EvalResult &Status) { - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); + EvalInfo Info(Ctx, &SP, Status, EvaluationMode::ConstantExpression); Info.InConstantContext = true; if (Info.EnableNewConstInterp) @@ -23117,22 +23248,22 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, bool Expr::EvaluateCharRangeAsString(std::string &Result, const Expr *SizeExpression, const Expr *PtrExpression, ASTContext &Ctx, - EvalResult &Status) const { + SemaProxy &SP, EvalResult &Status) const { return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, - PtrExpression, Ctx, Status); + PtrExpression, Ctx, SP, Status); } bool Expr::EvaluateCharRangeAsString(APValue &Result, const Expr *SizeExpression, const Expr *PtrExpression, ASTContext &Ctx, - EvalResult &Status) const { + SemaProxy &SP, EvalResult &Status) const { return EvaluateCharRangeAsStringImpl(this, Result, SizeExpression, - PtrExpression, Ctx, Status); + PtrExpression, Ctx, SP, Status); } std::optional Expr::tryEvaluateStrLen(const ASTContext &Ctx) const { Expr::EvalStatus Status; - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, EvaluationMode::ConstantFold); if (Info.EnableNewConstInterp) return Info.Ctx.getInterpContext().evaluateStrlen(Info, this); diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index ef729e22c1dc8..2472446b551c6 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -81,6 +81,7 @@ add_clang_library(clangSema SemaOpenMP.cpp SemaOverload.cpp SemaPPC.cpp + SemaProxy.cpp SemaPseudoObject.cpp SemaRISCV.cpp SemaStmt.cpp diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index e689c75eb566c..993393ec34316 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -324,7 +324,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, GlobalNewDeleteDeclared(false), DisableTypoCorrection(false), TyposCorrected(0), IsBuildingRecoveryCallExpr(false), CurrentInstantiationScope(nullptr), NonInstantiationEntries(0), - ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) { + ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context), + ProxyForEval(makeProxyForEval(*this)) { assert(pp.TUKind == TUKind); TUScope = nullptr; diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 8831a26224e7d..668a1e4f0248e 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -822,8 +822,8 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( SmallVector EvaluationDiags; Expr::EvalResult EvalResult; EvalResult.Diag = &EvaluationDiags; - if (!SubstitutedAtomicExpr.get()->EvaluateAsConstantExpr(EvalResult, - S.Context) || + if (!SubstitutedAtomicExpr.get()->EvaluateAsMandatedConstantExpr( + EvalResult, S.Context, S.getProxyForEval()) || !EvaluationDiags.empty()) { // C++2a [temp.constr.atomic]p1 // ...E shall be a constant expression of type bool. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 7e1b23c971a9c..2ac4fc29908ac 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15122,7 +15122,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { << Init->getSourceRange(); } } - (void)var->checkForConstantInitialization(Notes); + (void)var->checkForConstantInitialization(Notes, getProxyForEval()); Notes.clear(); } else if (CacheCulprit) { Notes.emplace_back(CacheCulprit->getExprLoc(), @@ -15131,7 +15131,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { } } else { // Evaluate the initializer to see if it's a constant initializer. - HasConstInit = var->checkForConstantInitialization(Notes); + HasConstInit = + var->checkForConstantInitialization(Notes, getProxyForEval()); } if (HasConstInit) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 37bb69da90b6c..d9c10a31fc6f2 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16551,8 +16551,8 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, CXXRecordDecl *ClassDecl) { if (VD->getInit() && !VD->getInit()->isValueDependent()) HasConstantInit = VD->evaluateValue(); SmallVector Notes; - if (!VD->evaluateDestruction(Notes) && VD->isConstexpr() && - HasConstantInit) { + if (!VD->evaluateConstantDestruction(Notes, getProxyForEval()) && + VD->isConstexpr() && HasConstantInit) { Diag(VD->getLocation(), diag::err_constexpr_var_requires_const_destruction) << VD; for (const PartialDiagnosticAt &Note : Notes) @@ -17968,7 +17968,8 @@ static bool EvaluateAsStringImpl(Sema &SemaRef, Expr *Message, SmallVector Notes; Status.Diag = &Notes; if (!Message->EvaluateCharRangeAsString(Result, EvaluatedSize.get(), - EvaluatedData.get(), Ctx, Status) || + EvaluatedData.get(), Ctx, + SemaRef.getProxyForEval(), Status) || !Notes.empty()) { SemaRef.Diag(Message->getBeginLoc(), ErrorOnInvalidMessage ? diag::err_user_defined_msg_constexpr diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 6d7cdf9f60dea..ace4e43d8c88b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18121,10 +18121,10 @@ Sema::VerifyIntegerConstantExpression(Expr *E, llvm::APSInt *Result, // Try to evaluate the expression, and produce diagnostics explaining why it's // not a constant expression as a side-effect. - bool Folded = - E->EvaluateAsRValue(EvalResult, Context, /*isConstantContext*/ true) && - EvalResult.Val.isInt() && !EvalResult.HasSideEffects && - (!getLangOpts().CPlusPlus || !EvalResult.HasUndefinedBehavior); + bool Folded = E->EvaluateAsMandatedConstantRValue(EvalResult, Context, + getProxyForEval()) && + EvalResult.Val.isInt() && !EvalResult.HasSideEffects && + (!getLangOpts().CPlusPlus || !EvalResult.HasUndefinedBehavior); if (!isa(E)) E = ConstantExpr::Create(Context, E, EvalResult.Val); @@ -18424,8 +18424,9 @@ ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) { APValue Cached; auto CheckConstantExpressionAndKeepResult = [&]() { Expr::EvalResult Eval; - bool Res = E.get()->EvaluateAsConstantExpr( - Eval, getASTContext(), ConstantExprKind::ImmediateInvocation); + bool Res = E.get()->EvaluateAsMandatedConstantExpr( + Eval, getASTContext(), getProxyForEval(), + ConstantExprKind::ImmediateInvocation); if (Res && !Eval.DiagEmitted) { Cached = std::move(Eval.Val); return true; @@ -18480,8 +18481,9 @@ static void EvaluateAndDiagnoseImmediateInvocation( Expr::EvalResult Eval; Eval.Diag = &Notes; ConstantExpr *CE = Candidate.getPointer(); - bool Result = CE->EvaluateAsConstantExpr( - Eval, SemaRef.getASTContext(), ConstantExprKind::ImmediateInvocation); + bool Result = CE->EvaluateAsMandatedConstantExpr( + Eval, SemaRef.getASTContext(), SemaRef.getProxyForEval(), + ConstantExprKind::ImmediateInvocation); if (!Result || !Notes.empty()) { SemaRef.FailedImmediateInvocations.insert(CE); Expr *InnerExpr = CE->getSubExpr()->IgnoreImplicit(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index eafda32198f11..89373731eb043 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6719,7 +6719,8 @@ Sema::EvaluateConvertedConstantExpression(Expr *E, QualType T, APValue &Value, else Kind = ConstantExprKind::Normal; - if (!E->EvaluateAsConstantExpr(Eval, Context, Kind) || + if (!E->EvaluateAsMandatedConstantExpr(Eval, Context, getProxyForEval(), + Kind) || (RequireInt && !Eval.Val.isInt())) { // The expression can't be folded, so we can't keep it at this position in // the AST. diff --git a/clang/lib/Sema/SemaProxy.cpp b/clang/lib/Sema/SemaProxy.cpp new file mode 100644 index 0000000000000..6ac5a7d4406d1 --- /dev/null +++ b/clang/lib/Sema/SemaProxy.cpp @@ -0,0 +1,49 @@ +//=== SemaProxy.cpp - Sema proxy for effectual constant evaluation --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a proxy to the Sema class that can be provided to the +// constant evaluator, thereby facilitating evaluations capable of acting on and +// querying the AST. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/SemaProxy.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Sema/SemaInternal.h" + +using namespace clang; + +namespace { + +class SemaProxyImpl : public clang::SemaProxy { +public: + SemaProxyImpl(clang::Sema &SemaRef) : SemaRef(SemaRef) {} + + void instantiateFunctionDefinition(SourceLocation PointOfInstantiation, + FunctionDecl *Function) override; + +private: + Sema &SemaRef; +}; + +void SemaProxyImpl::instantiateFunctionDefinition( + SourceLocation PointOfInstantiation, FunctionDecl *Function) { + SemaRef.InstantiateFunctionDefinition( + PointOfInstantiation, Function, /*Recursive=*/true, + /*DefinitionRequired=*/true, /*AtEndOfTU=*/false); +} + +} // anonymous namespace + +namespace clang { + +SemaProxy *Sema::makeProxyForEval(Sema &SemaRef) { + return new SemaProxyImpl(SemaRef); +} +} // end namespace clang diff --git a/clang/test/SemaCXX/constexpr-late-instantiation.cpp b/clang/test/SemaCXX/constexpr-late-instantiation.cpp index 94f5ab4a73616..4441a7f4c4765 100644 --- a/clang/test/SemaCXX/constexpr-late-instantiation.cpp +++ b/clang/test/SemaCXX/constexpr-late-instantiation.cpp @@ -20,24 +20,228 @@ constexpr T foo(T a) { return a; } +namespace GH73232 { +namespace ex1 { +template +constexpr void g(T); + +constexpr int f() { + g(0); + return 0; +} + +template +constexpr void g(T) {} + +constexpr auto z = f(); +} + +namespace ex2 { +template constexpr static void fromType(); + +void registerConverter() { fromType(); } +template struct QMetaTypeId {}; +template constexpr void fromType() { + (void)QMetaTypeId{}; +} +template <> struct QMetaTypeId {}; +} // namespace ex2 + +namespace ex3 { + +#if __cplusplus > 202302L +struct A { + consteval A(int i) { + chk(i); + } + constexpr void chk(auto) {} +}; +A a{1}; + +#endif + +} + +} // namespace GH73232 + + +namespace GH156255 { + +class X +{ +public: + constexpr int f( int x ) const + { + return g( x ); + } + +private: + + template + constexpr T g( T x ) const + { + return x; + } +}; + +// check that g is instantiated here. +constexpr int x = X().f( 1 ); +} + #if __cplusplus > 202002L +namespace instantiation_context_lookup { + +static constexpr int i = 42; +static constexpr int v = 8; + + +constexpr int f(auto); + +constexpr int g(int v = 42) { + static constexpr int i = 1; + return f(1); + return 0; +} + +constexpr int f(auto) { + return i + v; +} + +static_assert(g() == 50); + +} + +namespace GH35052 { + +template +constexpr int func(F f) { + if constexpr (f(1UL)) { + return 1; + } + return 0; +} + +int test() { + auto predicate = [](auto v) constexpr -> bool { return v == 1; }; + return func(predicate); // check that "predicate" is instantiated. +} + + +} // namespace GH35052 + +namespace GH100897 { + +template +constexpr auto foo() noexcept { + constexpr auto extract_size = []() constexpr -> int { + return 1; + }; + + constexpr int result = extract_size.template operator()(); + return result; +} + +void test() { foo(); } // check that the lambda gets instantiated. + +} // namespace GH100897 + +namespace from_constexpr_initializer { +template +struct basic_string { + constexpr void _M_construct(); + + constexpr basic_string() { + _M_construct(); + } + +}; + +basic_string a; + +template +constexpr void basic_string<_CharT>::_M_construct(){} + +constexpr basic_string z{}; +} // namespace from_constexpr_initializer + +namespace from_imm_invocation_in_immediate_escalating_fn { +template constexpr int f(); +consteval int g() { return f<0>(); } +template constexpr int f() { return V; } + +int h() { return [] { return g(); }(); } +} // namespace from_imm_invocation_in_immediate_escalating_fn + +namespace from_imm_invocation_in_non_escalating_fn { +template constexpr int f(); +consteval int g() { return f<0>(); } +template constexpr int f() { return V; } + +int h() { return g(); } +} // namespace from_imm_invocation_in_non_escalating_fn + +namespace from_template_argument { +template constexpr int f(); +consteval int g() { return f<0>(); } +template constexpr int f() { return V; } + +template consteval int h() { return V; } +int i() { return h(); } +} // namespace from_template_argument + +namespace from_constexpr_if { +template constexpr int f(); +consteval int g() { return f<0>(); } +template constexpr int f() { return V; } + +int h() { + if constexpr (g()) + return 1; + else + return 2; +} +} // namespace from_constexpr_if + +namespace from_static_assertion { +template constexpr int f(); +consteval int g() { return f<0>(); } +template constexpr int f() { return V; } + +static_assert(g() == 0); +} // namespace from_static_assertion + +namespace from_static_assert_message { +struct Msg { + consteval const char *data() { return "hello"; } + consteval unsigned size() { return 5; } +}; + +template constexpr Msg f(); +consteval Msg g() { return f<0>(); } +template constexpr Msg f() { return Msg{}; } + +static_assert(true, g()); +} // namespace from_static_assert_message + +namespace from_constexpr_destructor { +template constexpr int f() noexcept; +struct S { constexpr ~S() { (void) f<0>(); } }; +template constexpr int f() noexcept { return V; } + +void h() { constexpr S s; } +} // namespace from_constexpr_destructor + namespace GH115118 { struct foo { - // expected-note@-1 2{{while}} foo(const foo&) = default; foo(auto) requires([]() -> bool { return true; }()) - // expected-error@-1 {{non-constant expression}} - // expected-note@-2 {{undefined function}} \ - // expected-note@-2 {{declared}} {} }; -// FIXME: This will be fixed by https://github.com/llvm/llvm-project/pull/205557 struct bar { - // expected-note@-1 {{while}} foo x; // check that the lambda gets instantiated. };