https://github.com/katzdm updated https://github.com/llvm/llvm-project/pull/205557
>From 1a0ec89e52ee24e06ad8efcf2747586b6a469400 Mon Sep 17 00:00:00 2001 From: Dan Katz <[email protected]> Date: Mon, 22 Jun 2026 17:44:45 -0400 Subject: [PATCH 1/5] AST mutation from the constant evaluator. --- clang/docs/ReleaseNotes.rst | 1 + clang/include/clang/AST/Decl.h | 13 +- clang/include/clang/AST/Expr.h | 20 ++ clang/include/clang/AST/SemaProxy.h | 40 +++ clang/include/clang/Sema/Sema.h | 26 ++ clang/lib/AST/ByteCode/Interp.cpp | 32 +- clang/lib/AST/ByteCode/InterpState.cpp | 12 +- clang/lib/AST/ByteCode/State.h | 7 +- clang/lib/AST/Decl.cpp | 14 +- clang/lib/AST/ExprConstShared.h | 4 + clang/lib/AST/ExprConstant.cpp | 300 +++++++++++++----- clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/Sema.cpp | 3 +- clang/lib/Sema/SemaConcept.cpp | 4 +- clang/lib/Sema/SemaDecl.cpp | 5 +- clang/lib/Sema/SemaDeclCXX.cpp | 4 +- clang/lib/Sema/SemaEval.cpp | 48 +++ clang/lib/Sema/SemaExpr.cpp | 13 +- clang/lib/Sema/SemaOverload.cpp | 3 +- .../SemaCXX/constexpr-late-instantiation.cpp | 227 ++++++++++++- 20 files changed, 656 insertions(+), 121 deletions(-) create mode 100644 clang/include/clang/AST/SemaProxy.h create mode 100644 clang/lib/Sema/SemaEval.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 8bb17755b28f5..11f2f668b8d40 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -810,6 +810,7 @@ Bug Fixes to C++ Support - Fixed a crash in constant evaluation using placement new on an array which was later initialized. (#GH196450) - Fixed an issue where Clang incorrectly accepted invalid unqualified uses of local nested class names outside their declaring scope. (#GH184622) - Fixed a crash when parsing invalid friend declaration with storage-class specifier. (#GH186569) +- 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 2ea16d0ba6b03..26e4649d99252 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; @@ -1436,6 +1437,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { private: const APValue *evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes, + SemaProxy *SP, bool IsConstantInitialization) const; public: @@ -1452,6 +1454,15 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// not. bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const; + /// Evaluate the destruction of this variable, the destruction of which is + /// required by language rules to be constant. + /// + /// \pre hasConstantInitialization() + /// \return \c true if this variable has constant destruction, \c false if + /// not. + bool evaluateMandatedConstantDestruction( + SmallVectorImpl<PartialDiagnosticAt> &Notes, SemaProxy &SP) const; + /// Determine whether this variable has constant initialization. /// /// This is only set in two cases: when the language semantics require @@ -1469,7 +1480,7 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// constant initializer. Should only be called once, after completing the /// definition of the variable. bool checkForConstantInitialization( - SmallVectorImpl<PartialDiagnosticAt> &Notes) const; + SmallVectorImpl<PartialDiagnosticAt> &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 7c94c4d35641c..6ca1eb96b651a 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,12 @@ 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 +787,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 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 b8d760e7e0975..97b951bf6d672 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" @@ -907,6 +908,7 @@ class Sema final : public SemaBase { // 33. Types (SemaType.cpp) // 34. FixIt Helpers (SemaFixItUtils.cpp) // 35. Function Effects (SemaFunctionEffects.cpp) + // 36. Language-Mandated Constant Evaluation (SemaEval.cpp) /// \name Semantic Analysis /// Implementations are in Sema.cpp @@ -15811,6 +15813,30 @@ class Sema final : public SemaBase { void performFunctionEffectAnalysis(TranslationUnitDecl *TU); ///@} + + // + // + // ------------------------------------------------------------------------- + // + // + + /// \name Language-Mandated Constant Evaluation + /// Implementations are in SemaEval.cpp + ///@{ +public: + SemaProxy &getProxyForEval() const { + assert(ProxyForEval); + return *ProxyForEval; + } + +private: + std::unique_ptr<SemaProxy> ProxyForEval; + + static SemaProxy *makeProxyForEval(Sema &SemaRef); + + ///@} +public: + }; DeductionFailureInfo diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 3772def47408f..13de84a26eedc 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" @@ -1776,15 +1777,28 @@ bool CheckBitCast(InterpState &S, CodePtr OpPC, const Type *TargetType, return true; } -static void compileFunction(InterpState &S, const Function *Func) { - const FunctionDecl *Definition; - if (!Func->getDecl()->getBody(Definition)) - return; - if (!Definition) +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<FunctionDecl *>(Fn)); + } + Fn = Fn->getDefinition(); + if (!Fn) return; Compiler<ByteCodeEmitter>(S.getContext(), S.P) - .compileFunc(Definition, const_cast<Function *>(Func)); + .compileFunc(Fn, const_cast<Function *>(Func)); } bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func, @@ -1811,7 +1825,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; @@ -1898,7 +1912,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(); @@ -2316,7 +2330,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 2d6ed98e6b52c..ac6d45df2b9e6 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -19,9 +19,9 @@ 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), - StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), + : 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; CheckingPotentialConstantExpression = @@ -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 b23bf73ae803c..ed1c29198acfc 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,11 +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<PartialDiagnosticAt> *Notes, + SemaProxy *SP, bool IsConstantInitialization) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); @@ -2580,7 +2583,10 @@ VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *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 +2650,7 @@ bool VarDecl::hasConstantInitialization() const { } bool VarDecl::checkForConstantInitialization( - SmallVectorImpl<PartialDiagnosticAt> &Notes) const { + SmallVectorImpl<PartialDiagnosticAt> &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 +2665,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 619c79a1408f3..45c7db846fc70 100644 --- a/clang/lib/AST/ExprConstShared.h +++ b/clang/lib/AST/ExprConstShared.h @@ -29,6 +29,7 @@ class LangOptions; class ASTContext; class CharUnits; class Expr; +class FunctionDecl; } // namespace clang using namespace clang; /// Values returned by __builtin_classify_type, chosen to match the values @@ -89,4 +90,7 @@ std::optional<llvm::APFloat> EvalScalarMinMaxFp(const llvm::APFloat &A, const llvm::APFloat &B, std::optional<llvm::APSInt> 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 dde3b8bab43ec..306786746b0e3 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" @@ -909,9 +910,10 @@ namespace { /// initialization. uint64_t ArrayInitIndex = -1; - EvalInfo(const ASTContext &C, Expr::EvalStatus &S, EvaluationMode Mode) - : State(const_cast<ASTContext &>(C), S), CurrentCall(nullptr), - CallStackDepth(0), NextCallIndex(1), + EvalInfo(const ASTContext &C, SemaProxy *Sema, Expr::EvalStatus &S, + EvaluationMode Mode) + : State(const_cast<ASTContext &>(C), Sema, S), + CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), StepsLeft(C.getLangOpts().ConstexprStepLimit), EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp), BottomFrame(*this, SourceLocation(), /*Callee=*/nullptr, @@ -6997,6 +6999,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<FunctionDecl *>(FD)); +} + /// Evaluate a function call. static bool HandleFunctionCall(SourceLocation CallLoc, const FunctionDecl *Callee, @@ -7390,6 +7416,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); @@ -8857,9 +8885,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 @@ -11420,6 +11451,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); @@ -11461,6 +11494,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); @@ -21458,11 +21493,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() && @@ -21479,7 +21526,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); } @@ -21490,7 +21538,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); } @@ -21521,7 +21570,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; @@ -21548,11 +21597,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, @@ -21572,36 +21622,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<Expr*>(this), true); + MaterializeTemporaryExpr BaseMTE(T, const_cast<Expr*>(E), true); APValue::LValueBase Base(&BaseMTE); Info.setEvaluatingDecl(Base, Result.Val); @@ -21612,14 +21653,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) || + 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)) @@ -21628,8 +21669,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. @@ -21639,34 +21680,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, @@ -21686,7 +21743,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; @@ -21705,48 +21762,115 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD, CheckMemoryLeaks(Info); } -bool VarDecl::evaluateDestruction( - SmallVectorImpl<PartialDiagnosticAt> &Notes) const { - Expr::EvalStatus EStatus; - EStatus.Diag = &Notes; +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"); - // 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(); + 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(); + + 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(); - else if (!handleDefaultInitValue(getType(), DestroyedValue)) + if (VD->getEvaluatedValue()) + DestroyedValue = *VD->getEvaluatedValue(); + else if (!handleDefaultInitValue(VD->getType(), DestroyedValue)) return false; if (Ctx.getLangOpts().EnableNewConstInterp) { - EvalInfo Info(Ctx, EStatus, - IsConstantDestruction ? EvaluationMode::ConstantExpression - : EvaluationMode::ConstantFold); - Info.setEvaluatingDecl(this, DestroyedValue, + Info.setEvaluatingDecl(VD, DestroyedValue, EvalInfo::EvaluatingDeclKind::Dtor); - 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), getType(), - getLocation(), EStatus, IsConstantDestruction) || + if (!EvaluateDestruction(Ctx, Info.getSemaProxy(), VD, + std::move(DestroyedValue), VD->getType(), + VD->getLocation(), EStatus, IsConstantDestruction) || EStatus.HasSideEffects) return false; - ensureEvaluatedStmt()->HasConstantDestruction = true; + VD->ensureEvaluatedStmt()->HasConstantDestruction = true; return true; } +bool VarDecl::evaluateDestruction( + SmallVectorImpl<PartialDiagnosticAt> &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::evaluateMandatedConstantDestruction( + SmallVectorImpl<PartialDiagnosticAt> &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 { @@ -21764,7 +21888,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); @@ -21783,7 +21908,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; @@ -21803,7 +21929,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); } @@ -21857,7 +21984,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 || @@ -22361,7 +22489,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)) @@ -22395,7 +22524,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) && @@ -22422,7 +22552,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; LValue ThisVal; @@ -22498,7 +22629,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; @@ -22548,7 +22679,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; @@ -22573,7 +22704,7 @@ std::optional<uint64_t> 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); @@ -22632,7 +22763,7 @@ EvaluateBuiltinStrLen(const Expr *E, EvalInfo &Info, std::optional<std::string> 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) { @@ -22652,7 +22783,8 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, const Expr *PtrExpression, ASTContext &Ctx, Expr::EvalResult &Status) { - EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression); + EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, + EvaluationMode::ConstantExpression); Info.InConstantContext = true; if (Info.EnableNewConstInterp) @@ -22720,7 +22852,7 @@ bool Expr::EvaluateCharRangeAsString(APValue &Result, std::optional<uint64_t> 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 0ebf56ecffe69..a9d3cf9b02f53 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -52,6 +52,7 @@ add_clang_library(clangSema SemaDeclAttr.cpp SemaDeclCXX.cpp SemaDeclObjC.cpp + SemaEval.cpp SemaExceptionSpec.cpp SemaExpr.cpp SemaExprCXX.cpp diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 78fbc9e31842d..d086718bcb235 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 f41f37b8a3d19..acbcd04e808ed 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -795,8 +795,8 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow( SmallVector<PartialDiagnosticAt, 2> 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 d45c3eb35094f..f6f7e4beb2bd6 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15090,7 +15090,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(), @@ -15099,7 +15099,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 418ff01f3d98a..984de227220f8 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16516,8 +16516,8 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, CXXRecordDecl *ClassDecl) { if (VD->getInit() && !VD->getInit()->isValueDependent()) HasConstantInit = VD->evaluateValue(); SmallVector<PartialDiagnosticAt, 8> Notes; - if (!VD->evaluateDestruction(Notes) && VD->isConstexpr() && - HasConstantInit) { + if (!VD->evaluateMandatedConstantDestruction(Notes, getProxyForEval()) && + VD->isConstexpr() && HasConstantInit) { Diag(VD->getLocation(), diag::err_constexpr_var_requires_const_destruction) << VD; for (const PartialDiagnosticAt &Note : Notes) diff --git a/clang/lib/Sema/SemaEval.cpp b/clang/lib/Sema/SemaEval.cpp new file mode 100644 index 0000000000000..b43783bfb9091 --- /dev/null +++ b/clang/lib/Sema/SemaEval.cpp @@ -0,0 +1,48 @@ +//=== SemaEval.cpp - Sema handling of 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 Sema handling of language-mandated constant evaluation, +// which can perform actions and queries against the AST. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Decl.h" +#include "clang/AST/SemaProxy.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/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7c868d176e803..d53b01d551e1b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18050,7 +18050,8 @@ 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) && + E->EvaluateAsMandatedConstantRValue(EvalResult, Context, + getProxyForEval()) && EvalResult.Val.isInt() && !EvalResult.HasSideEffects && (!getLangOpts().CPlusPlus || !EvalResult.HasUndefinedBehavior); @@ -18352,8 +18353,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; @@ -18408,8 +18410,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 c663765573612..25ba2dc229be6 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -6708,7 +6708,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/test/SemaCXX/constexpr-late-instantiation.cpp b/clang/test/SemaCXX/constexpr-late-instantiation.cpp index 9aec0c90e61dc..e893d1f835ddf 100644 --- a/clang/test/SemaCXX/constexpr-late-instantiation.cpp +++ b/clang/test/SemaCXX/constexpr-late-instantiation.cpp @@ -1,5 +1,10 @@ -// RUN: %clang_cc1 %s -fsyntax-only -verify -// RUN: %clang_cc1 %s -fexperimental-new-constant-interpreter -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++14 -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify + +// RUN: %clang_cc1 %s -std=c++14 -fsyntax-only -fexperimental-new-constant-interpreter -verify +// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -fexperimental-new-constant-interpreter -verify +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fexperimental-new-constant-interpreter -verify template <typename T> constexpr T foo(T a); // expected-note {{declared here}} @@ -14,3 +19,221 @@ template <typename T> constexpr T foo(T a) { return a; } + +namespace GH73232 { +namespace ex1 { +template <typename T> +constexpr void g(T); + +constexpr int f() { + g(0); + return 0; +} + +template <typename T> +constexpr void g(T) {} + +constexpr auto z = f(); +} + +namespace ex2 { +template <typename> constexpr static void fromType(); + +void registerConverter() { fromType<int>(); } +template <typename> struct QMetaTypeId {}; +template <typename T> constexpr void fromType() { + (void)QMetaTypeId<T>{}; +} +template <> struct QMetaTypeId<int> {}; +} // 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<class T> + 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 <typename F> +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 GH115118 { + +// Currently fails an assertion due to GH199347. +/*struct foo { + foo(const foo&) = default; + foo(auto) + requires([]<int = 0>() -> bool { return true; }()) + {} +}; + +struct bar { + foo x; // check that the lambda gets instantiated. +};*/ + +} // namespace GH115118 + + +namespace GH100897 { + +template <typename> +constexpr auto foo() noexcept { + constexpr auto extract_size = []<typename argument_t>() constexpr -> int { + return 1; + }; + + constexpr int result = extract_size.template operator()<int>(); + return result; +} + +void test() { foo<void>(); } // check that the lambda gets instantiated. + +} // namespace GH100897 + +namespace from_constexpr_initializer { +template <typename _CharT> +struct basic_string { + constexpr void _M_construct(); + + constexpr basic_string() { + _M_construct(); + } + +}; + +basic_string<char *> a; + +template <typename _CharT> +constexpr void basic_string<_CharT>::_M_construct(){} + +constexpr basic_string<char*> z{}; +} // namespace from_constexpr_initializer + +namespace from_imm_invocation_in_immediate_escalating_fn { +template <int V> constexpr int f(); +consteval int g() { return f<0>(); } +template <int V> 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 <int V> constexpr int f(); +consteval int g() { return f<0>(); } +template <int V> constexpr int f() { return V; } + +int h() { return g(); } +} // namespace from_imm_invocation_in_non_escalating_fn + +namespace from_template_argument { +template <int V> constexpr int f(); +consteval int g() { return f<0>(); } +template <int V> constexpr int f() { return V; } + +template <int V> consteval int h() { return V; } +int i() { return h<g()>(); } +} // namespace from_template_argument + +namespace from_constexpr_if { +template <int V> constexpr int f(); +consteval int g() { return f<0>(); } +template <int V> constexpr int f() { return V; } + +int h() { + if constexpr (g()) + return 1; + else + return 2; +} +} // namespace from_constexpr_if + +namespace from_static_assertion { +template <int V> constexpr int f(); +consteval int g() { return f<0>(); } +template <int V> constexpr int f() { return V; } + +static_assert(g() == 0); +} // namespace from_static_assertion + +namespace from_constexpr_destructor { +template <int V> constexpr int f() noexcept; +struct S { constexpr ~S() { (void) f<0>(); } }; +template <int V> constexpr int f() noexcept { return V; } + +void h() { constexpr S s; } +} // namespace from_constexpr_destructor + +#endif >From 67c2baf172d440213b96580528a1dc1770c9aa5a Mon Sep 17 00:00:00 2001 From: Dan Katz <[email protected]> Date: Wed, 24 Jun 2026 16:50:34 -0400 Subject: [PATCH 2/5] Move 'SemaEval.cpp' to 'SemaProxy.cpp'. --- clang/include/clang/Sema/Sema.h | 6 +++--- clang/lib/Sema/CMakeLists.txt | 2 +- clang/lib/Sema/{SemaEval.cpp => SemaProxy.cpp} | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) rename clang/lib/Sema/{SemaEval.cpp => SemaProxy.cpp} (83%) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 97b951bf6d672..b9e322cab59ea 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -908,7 +908,7 @@ class Sema final : public SemaBase { // 33. Types (SemaType.cpp) // 34. FixIt Helpers (SemaFixItUtils.cpp) // 35. Function Effects (SemaFunctionEffects.cpp) - // 36. Language-Mandated Constant Evaluation (SemaEval.cpp) + // 36. Proxy to Sema for Constant Evaluation (SemaProxy.cpp) /// \name Semantic Analysis /// Implementations are in Sema.cpp @@ -15820,8 +15820,8 @@ class Sema final : public SemaBase { // // - /// \name Language-Mandated Constant Evaluation - /// Implementations are in SemaEval.cpp + /// \name Proxy to Sema for Constant Evaluation + /// Implementations are in SemaProxy.cpp ///@{ public: SemaProxy &getProxyForEval() const { diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index a9d3cf9b02f53..87c273bd52fd9 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -52,7 +52,6 @@ add_clang_library(clangSema SemaDeclAttr.cpp SemaDeclCXX.cpp SemaDeclObjC.cpp - SemaEval.cpp SemaExceptionSpec.cpp SemaExpr.cpp SemaExprCXX.cpp @@ -81,6 +80,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/SemaEval.cpp b/clang/lib/Sema/SemaProxy.cpp similarity index 83% rename from clang/lib/Sema/SemaEval.cpp rename to clang/lib/Sema/SemaProxy.cpp index b43783bfb9091..0dd4c5890a9bf 100644 --- a/clang/lib/Sema/SemaEval.cpp +++ b/clang/lib/Sema/SemaProxy.cpp @@ -1,4 +1,4 @@ -//=== SemaEval.cpp - Sema handling of effectual constant evaluation -------===// +//=== 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. @@ -6,8 +6,9 @@ // //===----------------------------------------------------------------------===// // -// This file implements Sema handling of language-mandated constant evaluation, -// which can perform actions and queries against the AST. +// 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. // //===----------------------------------------------------------------------===// >From 01abd04b18425ff2266c358bc4455e90eeac6a36 Mon Sep 17 00:00:00 2001 From: "Daniel M. Katz" <[email protected]> Date: Wed, 24 Jun 2026 16:52:43 -0400 Subject: [PATCH 3/5] Update clang/include/clang/AST/Decl.h Co-authored-by: Corentin Jabot <[email protected]> --- clang/include/clang/AST/Decl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 26e4649d99252..9e6f8d470c23b 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1454,8 +1454,8 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// not. bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const; - /// Evaluate the destruction of this variable, the destruction of which is - /// required by language rules to be constant. + /// 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 >From 092710df7085b3786c28dc7c5eaebab3bb763e67 Mon Sep 17 00:00:00 2001 From: Dan Katz <[email protected]> Date: Wed, 24 Jun 2026 17:12:14 -0400 Subject: [PATCH 4/5] Formatting. Also change pointer to ref in checkForConstantInitialization. --- clang/include/clang/AST/Decl.h | 13 +++++++------ clang/include/clang/AST/Expr.h | 7 ++++--- clang/include/clang/Sema/Sema.h | 1 - clang/lib/AST/ByteCode/InterpState.cpp | 4 ++-- clang/lib/AST/Decl.cpp | 15 +++++++-------- clang/lib/AST/ExprConstant.cpp | 20 ++++++++++---------- clang/lib/Sema/SemaDecl.cpp | 6 +++--- clang/lib/Sema/SemaDeclCXX.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 9 ++++----- clang/lib/Sema/SemaProxy.cpp | 6 +++--- 10 files changed, 41 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 9e6f8d470c23b..464978c406d08 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -1454,14 +1454,14 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// not. bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes) const; - /// Evaluate the destruction of a variable that is required by the language to have - /// constant destruction. + /// 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 evaluateMandatedConstantDestruction( - SmallVectorImpl<PartialDiagnosticAt> &Notes, SemaProxy &SP) const; + bool evaluateConstantDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes, + SemaProxy &SP) const; /// Determine whether this variable has constant initialization. /// @@ -1479,8 +1479,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> { /// 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<PartialDiagnosticAt> &Notes, SemaProxy *SP) const; + bool + checkForConstantInitialization(SmallVectorImpl<PartialDiagnosticAt> &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 6ca1eb96b651a..d4f1793b977fc 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -753,9 +753,10 @@ class Expr : public ValueStmt { /// 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; + 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 diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index b9e322cab59ea..c83f812785cbe 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -15836,7 +15836,6 @@ class Sema final : public SemaBase { ///@} public: - }; DeductionFailureInfo diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp index ac6d45df2b9e6..79d85c9b38616 100644 --- a/clang/lib/AST/ByteCode/InterpState.cpp +++ b/clang/lib/AST/ByteCode/InterpState.cpp @@ -20,8 +20,8 @@ using namespace clang::interp; InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx, SourceMapper *M) : State(Ctx.getASTContext(), Parent.getSemaProxy(), Parent.getEvalStatus()), - M(M), P(P), Stk(Stk), Ctx(Ctx), BottomFrame(*this), - Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit), + 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; CheckingPotentialConstantExpression = diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index ed1c29198acfc..0869732813c7a 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2559,8 +2559,7 @@ const APValue *VarDecl::evaluateValue() const { const APValue * VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes, - SemaProxy *SP, - bool IsConstantInitialization) const { + SemaProxy *SP, bool IsConstantInitialization) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); const auto *Init = getInit(); @@ -2583,10 +2582,10 @@ VarDecl::evaluateValueImpl(SmallVectorImpl<PartialDiagnosticAt> *Notes, Expr::EvalResult EStatus; EStatus.Diag = Notes; bool Result = - isConstexpr() ? - Init->EvaluateAsMandatedConstantInitializer(EStatus, Ctx, *SP, this) - : 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 @@ -2650,7 +2649,7 @@ bool VarDecl::hasConstantInitialization() const { } bool VarDecl::checkForConstantInitialization( - SmallVectorImpl<PartialDiagnosticAt> &Notes, SemaProxy *SP) const { + SmallVectorImpl<PartialDiagnosticAt> &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 @@ -2665,7 +2664,7 @@ bool VarDecl::checkForConstantInitialization( // Evaluate the initializer to check whether it's a constant expression. Eval->HasConstantInitialization = - evaluateValueImpl(&Notes, SP, 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/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 306786746b0e3..d7df049c11d62 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -912,8 +912,8 @@ namespace { EvalInfo(const ASTContext &C, SemaProxy *Sema, Expr::EvalStatus &S, EvaluationMode Mode) - : State(const_cast<ASTContext &>(C), Sema, S), - CurrentCall(nullptr), CallStackDepth(0), NextCallIndex(1), + : State(const_cast<ASTContext &>(C), Sema, S), CurrentCall(nullptr), + CallStackDepth(0), NextCallIndex(1), StepsLeft(C.getLangOpts().ConstexprStepLimit), EnableNewConstInterp(C.getLangOpts().EnableNewConstInterp), BottomFrame(*this, SourceLocation(), /*Callee=*/nullptr, @@ -21642,7 +21642,7 @@ static bool EvaluateConstantExpr(Expr::EvalResult &Result, // 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<Expr*>(E), true); + MaterializeTemporaryExpr BaseMTE(T, const_cast<Expr *>(E), true); APValue::LValueBase Base(&BaseMTE); Info.setEvaluatingDecl(Base, Result.Val); @@ -21653,8 +21653,8 @@ static bool EvaluateConstantExpr(Expr::EvalResult &Result, // 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, E) || - Result.HasSideEffects || !Scope.destroy()) + if (!::EvaluateInPlace(Result.Val, Info, LVal, E) || Result.HasSideEffects || + !Scope.destroy()) return false; if (!Info.discardCleanups()) @@ -21697,9 +21697,9 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx, } bool Expr::EvaluateAsMandatedConstantExpr(EvalResult &Result, - const ASTContext &Ctx, - SemaProxy &Sema, - ConstantExprKind Kind) const { + const ASTContext &Ctx, + SemaProxy &Sema, + ConstantExprKind Kind) const { assert(!isValueDependent() && "Expression evaluator can't be called on a dependent expression."); bool IsConst; @@ -21859,7 +21859,7 @@ bool VarDecl::evaluateDestruction( return ::evaluateDestruction(EStatus, Info, this, IsConstantDestruction); } -bool VarDecl::evaluateMandatedConstantDestruction( +bool VarDecl::evaluateConstantDestruction( SmallVectorImpl<PartialDiagnosticAt> &Notes, SemaProxy &SP) const { Expr::EvalStatus EStatus; EStatus.Diag = &Notes; @@ -22784,7 +22784,7 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result, ASTContext &Ctx, Expr::EvalResult &Status) { EvalInfo Info(Ctx, /*Sema=*/nullptr, Status, - EvaluationMode::ConstantExpression); + EvaluationMode::ConstantExpression); Info.InConstantContext = true; if (Info.EnableNewConstInterp) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f6f7e4beb2bd6..107928ed99f9a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15090,7 +15090,7 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { << Init->getSourceRange(); } } - (void)var->checkForConstantInitialization(Notes, &getProxyForEval()); + (void)var->checkForConstantInitialization(Notes, getProxyForEval()); Notes.clear(); } else if (CacheCulprit) { Notes.emplace_back(CacheCulprit->getExprLoc(), @@ -15099,8 +15099,8 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { } } else { // Evaluate the initializer to see if it's a constant initializer. - HasConstInit = var->checkForConstantInitialization(Notes, - &getProxyForEval()); + HasConstInit = + var->checkForConstantInitialization(Notes, getProxyForEval()); } if (HasConstInit) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 984de227220f8..7193243ca1953 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16516,7 +16516,7 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, CXXRecordDecl *ClassDecl) { if (VD->getInit() && !VD->getInit()->isValueDependent()) HasConstantInit = VD->evaluateValue(); SmallVector<PartialDiagnosticAt, 8> Notes; - if (!VD->evaluateMandatedConstantDestruction(Notes, getProxyForEval()) && + if (!VD->evaluateConstantDestruction(Notes, getProxyForEval()) && VD->isConstexpr() && HasConstantInit) { Diag(VD->getLocation(), diag::err_constexpr_var_requires_const_destruction) << VD; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d53b01d551e1b..2691105f6d96e 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -18049,11 +18049,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->EvaluateAsMandatedConstantRValue(EvalResult, Context, - getProxyForEval()) && - 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<ConstantExpr>(E)) E = ConstantExpr::Create(Context, E, EvalResult.Val); diff --git a/clang/lib/Sema/SemaProxy.cpp b/clang/lib/Sema/SemaProxy.cpp index 0dd4c5890a9bf..6ac5a7d4406d1 100644 --- a/clang/lib/Sema/SemaProxy.cpp +++ b/clang/lib/Sema/SemaProxy.cpp @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/AST/Decl.h" #include "clang/AST/SemaProxy.h" +#include "clang/AST/Decl.h" #include "clang/Basic/SourceLocation.h" #include "clang/Sema/SemaInternal.h" @@ -35,8 +35,8 @@ class SemaProxyImpl : public clang::SemaProxy { void SemaProxyImpl::instantiateFunctionDefinition( SourceLocation PointOfInstantiation, FunctionDecl *Function) { SemaRef.InstantiateFunctionDefinition( - PointOfInstantiation, Function, /*Recursive=*/true, - /*DefinitionRequired=*/true, /*AtEndOfTU=*/false); + PointOfInstantiation, Function, /*Recursive=*/true, + /*DefinitionRequired=*/true, /*AtEndOfTU=*/false); } } // anonymous namespace >From fe1bc6d15eca218d4659b895bd56ecd049e5d726 Mon Sep 17 00:00:00 2001 From: Dan Katz <[email protected]> Date: Wed, 24 Jun 2026 17:51:16 -0400 Subject: [PATCH 5/5] Instantiation from static_assert messages: The cornerstone of C++. --- clang/include/clang/AST/Expr.h | 4 ++-- clang/lib/AST/ExprConstant.cpp | 13 ++++++------- clang/lib/Sema/SemaDeclCXX.cpp | 3 ++- clang/test/SemaCXX/constexpr-late-instantiation.cpp | 13 +++++++++++++ 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index d4f1793b977fc..2fbd645eaed48 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -813,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/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d7df049c11d62..82b1f50cc7660 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -22781,10 +22781,9 @@ template <typename T> 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, /*Sema=*/nullptr, Status, - EvaluationMode::ConstantExpression); + EvalInfo Info(Ctx, &SP, Status, EvaluationMode::ConstantExpression); Info.InConstantContext = true; if (Info.EnableNewConstInterp) @@ -22837,17 +22836,17 @@ 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<uint64_t> Expr::tryEvaluateStrLen(const ASTContext &Ctx) const { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 7193243ca1953..4d74e71f636ed 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17933,7 +17933,8 @@ static bool EvaluateAsStringImpl(Sema &SemaRef, Expr *Message, SmallVector<PartialDiagnosticAt, 8> 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/test/SemaCXX/constexpr-late-instantiation.cpp b/clang/test/SemaCXX/constexpr-late-instantiation.cpp index e893d1f835ddf..01b6cab34659e 100644 --- a/clang/test/SemaCXX/constexpr-late-instantiation.cpp +++ b/clang/test/SemaCXX/constexpr-late-instantiation.cpp @@ -228,6 +228,19 @@ template <int V> 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 <int V> constexpr Msg f(); +consteval Msg g() { return f<0>(); } +template <int V> constexpr Msg f() { return Msg{}; } + +static_assert(true, g()); +} // namespace from_static_assert_message + namespace from_constexpr_destructor { template <int V> constexpr int f() noexcept; struct S { constexpr ~S() { (void) f<0>(); } }; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
