[clang] [Clang] Instantiate functions from constant evaluation. (PR #205557)
Daniel M. Katz via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 15 06:52:44 PDT 2026
https://github.com/katzdm updated https://github.com/llvm/llvm-project/pull/205557
>From 3a95c8989b6e6bbc1cadc554749c95130bc4f1d7 Mon Sep 17 00:00:00 2001
From: Dan Katz <katzdm at gmail.com>
Date: Mon, 22 Jun 2026 17:44:45 -0400
Subject: [PATCH 1/6] 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 | 306 +++++++++++++-----
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, 659 insertions(+), 124 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 d1303d4c98507..36b0d8f8bddb1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -816,6 +816,7 @@ Bug Fixes to C++ Support
- 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)
- Fixed a missing vtable for ``dynamic_cast<FinalClass *>(this)`` in a function template. (#GH198511)
+- 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 2f09c19178f09..513dbff4f4638 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"
@@ -1803,15 +1804,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,
@@ -1838,7 +1852,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;
@@ -1925,7 +1939,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();
@@ -2343,7 +2357,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 1d359339b9104..7b438c671a2cc 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,
@@ -7397,6 +7423,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);
@@ -8864,9 +8892,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
@@ -11427,6 +11458,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);
@@ -11468,6 +11501,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);
@@ -21465,11 +21500,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() &&
@@ -21486,7 +21533,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);
}
@@ -21497,7 +21545,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);
}
@@ -21528,7 +21577,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;
@@ -21555,11 +21604,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,
@@ -21579,36 +21629,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);
@@ -21619,14 +21660,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))
@@ -21635,8 +21676,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.
@@ -21646,34 +21687,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,
@@ -21693,7 +21750,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;
@@ -21712,59 +21769,126 @@ bool Expr::EvaluateAsInitializer(const ASTContext &Ctx, const VarDecl *VD,
CheckMemoryLeaks(Info);
}
-bool VarDecl::evaluateDestruction(
- SmallVectorImpl<PartialDiagnosticAt> &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<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 {
@@ -21782,7 +21906,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);
@@ -21801,7 +21926,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;
@@ -21821,7 +21947,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);
}
@@ -21875,7 +22002,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 ||
@@ -22379,7 +22507,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))
@@ -22413,7 +22542,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) &&
@@ -22440,7 +22570,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;
@@ -22516,7 +22647,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;
@@ -22566,7 +22697,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;
@@ -22591,7 +22722,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);
@@ -22650,7 +22781,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) {
@@ -22670,7 +22801,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)
@@ -22738,7 +22870,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 ffce0a146865e..9a173b4723916 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 9ed7b9b311148cc6b77094547b62652af2f6f0db Mon Sep 17 00:00:00 2001
From: Dan Katz <katzdm at gmail.com>
Date: Wed, 24 Jun 2026 16:50:34 -0400
Subject: [PATCH 2/6] 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 5595022bfa950a38e43e34b2b5a4fe58686892d6 Mon Sep 17 00:00:00 2001
From: "Daniel M. Katz" <katzdm at gmail.com>
Date: Wed, 24 Jun 2026 16:52:43 -0400
Subject: [PATCH 3/6] Update clang/include/clang/AST/Decl.h
Co-authored-by: Corentin Jabot <corentinjabot at gmail.com>
---
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 447466b0fa865cd28ce0739915636e860ed6ab2a Mon Sep 17 00:00:00 2001
From: Dan Katz <katzdm at gmail.com>
Date: Wed, 24 Jun 2026 17:12:14 -0400
Subject: [PATCH 4/6] 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 7b438c671a2cc..41a5d281dca14 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,
@@ -21649,7 +21649,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);
@@ -21660,8 +21660,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())
@@ -21704,9 +21704,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;
@@ -21877,7 +21877,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;
@@ -22802,7 +22802,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 9a173b4723916..70befe8d42db2 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 7628efc682f668cc941cea4e1fc0d319e057fbb3 Mon Sep 17 00:00:00 2001
From: Dan Katz <katzdm at gmail.com>
Date: Wed, 24 Jun 2026 17:51:16 -0400
Subject: [PATCH 5/6] 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 41a5d281dca14..51d14d2ab40f6 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -22799,10 +22799,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)
@@ -22855,17 +22854,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 70befe8d42db2..a31a270697bc0 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>(); } };
>From 2b5b4a15165213f4388f8f73ba71b8ab9c8166f7 Mon Sep 17 00:00:00 2001
From: Dan Katz <katzdm at gmail.com>
Date: Fri, 26 Jun 2026 08:27:51 -0400
Subject: [PATCH 6/6] Fix regression pointed out during review.
---
clang/lib/AST/ByteCode/Interp.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index 513dbff4f4638..e5769b670099f 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -1820,12 +1820,15 @@ static void compileFunction(InterpState &S, const Function *Func,
SP->instantiateFunctionDefinition(S.Current->getLocation(OpPC),
const_cast<FunctionDecl *>(Fn));
}
- Fn = Fn->getDefinition();
- if (!Fn)
+
+ const FunctionDecl *Definition;
+ if (!Fn->getBody(Definition))
+ return;
+ if (!Definition)
return;
Compiler<ByteCodeEmitter>(S.getContext(), S.P)
- .compileFunc(Fn, const_cast<Function *>(Func));
+ .compileFunc(Definition, const_cast<Function *>(Func));
}
bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
More information about the cfe-commits
mailing list