[clang] [clang][bytecode] Stop relying on `CheckEvaluationResult()` (PR #183706)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 8 23:40:46 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/183706
>From 8d79a5b0c8d9946701b4cef825b035021a4fc624 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 16 Feb 2026 08:25:42 +0100
Subject: [PATCH] EvalSettings
---
clang/lib/AST/ByteCode/ByteCodeEmitter.h | 1 +
clang/lib/AST/ByteCode/Compiler.cpp | 16 +-
clang/lib/AST/ByteCode/Context.cpp | 109 ++++--
clang/lib/AST/ByteCode/Context.h | 38 +-
clang/lib/AST/ByteCode/Disasm.cpp | 5 +-
clang/lib/AST/ByteCode/EvalEmitter.cpp | 45 ++-
clang/lib/AST/ByteCode/EvalEmitter.h | 18 +-
clang/lib/AST/ByteCode/EvalSettings.h | 35 ++
clang/lib/AST/ByteCode/EvaluationResult.cpp | 360 +++++++++++++++++-
clang/lib/AST/ByteCode/EvaluationResult.h | 41 +-
clang/lib/AST/ByteCode/Interp.cpp | 2 +
clang/lib/AST/ByteCode/InterpState.cpp | 27 +-
clang/lib/AST/ByteCode/InterpState.h | 9 +-
clang/lib/AST/ExprConstShared.h | 43 +++
clang/lib/AST/ExprConstant.cpp | 267 ++++++-------
.../ByteCode/codegen-constexpr-unknown.cpp | 8 +-
clang/test/AST/ByteCode/unused-variables.cpp | 109 ++++++
clang/test/CodeGenCXX/global-init.cpp | 8 +
clang/test/SemaCXX/PR19955.cpp | 3 +
19 files changed, 896 insertions(+), 248 deletions(-)
create mode 100644 clang/lib/AST/ByteCode/EvalSettings.h
create mode 100644 clang/test/AST/ByteCode/unused-variables.cpp
diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.h b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
index dd18341d52a09..c5bd3b494c8df 100644
--- a/clang/lib/AST/ByteCode/ByteCodeEmitter.h
+++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.h
@@ -62,6 +62,7 @@ class ByteCodeEmitter {
/// We're always emitting bytecode.
bool isActive() const { return true; }
bool checkingForUndefinedBehavior() const { return false; }
+ bool constantFolding() const { return false; }
/// Callback for local registration.
Local createLocal(Descriptor *D);
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index 6d8b84a83a4ad..13760509deec6 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3211,6 +3211,9 @@ bool Compiler<Emitter>::VisitMaterializeTemporaryExpr(
// the temporary is explicitly static, create a global variable.
OptPrimType SubExprT = classify(SubExpr);
if (E->getStorageDuration() == SD_Static) {
+ if (this->constantFolding())
+ return false;
+
UnsignedOrNone GlobalIndex = P.createGlobal(E);
if (!GlobalIndex)
return false;
@@ -5049,6 +5052,9 @@ const Function *Compiler<Emitter>::getFunction(const FunctionDecl *FD) {
template <class Emitter>
bool Compiler<Emitter>::visitExpr(const Expr *E, bool DestroyToplevelScope) {
+ if (E->getType().isNull())
+ return false;
+
LocalScope<Emitter> RootScope(this, ScopeKind::FullExpression);
// If we won't destroy the toplevel scope, check for memory leaks first.
@@ -7476,10 +7482,10 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
}
// In case we need to re-visit a declaration.
- auto revisit = [&](const VarDecl *VD) -> bool {
+ auto revisit = [&](const VarDecl *VD, bool ConstexprUnknown = true) -> bool {
if (!this->emitPushCC(VD->hasConstantInitialization(), E))
return false;
- auto VarState = this->visitDecl(VD, /*IsConstexprUnknown=*/true);
+ auto VarState = this->visitDecl(VD, ConstexprUnknown);
if (!this->emitPopCC(E))
return false;
@@ -7525,7 +7531,7 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if (!Ctx.getLangOpts().CPlusPlus) {
if (VD->getAnyInitializer() && DeclType.isConstant(Ctx.getASTContext()) &&
!VD->isWeak())
- return revisit(VD);
+ return revisit(VD, DeclType->isPointerType());
return this->emitDummyPtr(D, E);
}
@@ -7559,10 +7565,12 @@ bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) {
if (VD->isLocalVarDecl() && typeShouldBeVisited(DeclType) && VD->getInit() &&
!VD->getInit()->isValueDependent()) {
if (VD->evaluateValue()) {
+ bool ConstexprUnknown = !DeclType.isConstant(Ctx.getASTContext()) &&
+ !DeclType->isReferenceType();
// Revisit the variable declaration, but make sure it's associated with a
// different evaluation, so e.g. mutable reads don't work on it.
EvalIDScope _(Ctx);
- return revisit(VD);
+ return revisit(VD, ConstexprUnknown);
}
if (IsReference)
diff --git a/clang/lib/AST/ByteCode/Context.cpp b/clang/lib/AST/ByteCode/Context.cpp
index 879d51e6a2c3e..3fbfc114857f4 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -36,7 +36,8 @@ Context::Context(ASTContext &Ctx) : Ctx(Ctx), P(new Program(*this)) {
Context::~Context() {}
-bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
+bool Context::isPotentialConstantExpr(const EvalSettings &Settings,
+ const FunctionDecl *FD) {
assert(Stk.empty());
// Get a function handle.
@@ -53,15 +54,16 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
++EvalID;
// And run it.
- return Run(Parent, Func);
+ return Run(Settings, Func);
}
-void Context::isPotentialConstantExprUnevaluated(State &Parent, const Expr *E,
+void Context::isPotentialConstantExprUnevaluated(const EvalSettings &Settings,
+ const Expr *E,
const FunctionDecl *FD) {
assert(Stk.empty());
++EvalID;
size_t StackSizeBefore = Stk.size();
- Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+ Compiler<EvalEmitter> C(*this, *P, Settings, Stk);
if (!C.interpretCall(FD, E)) {
C.cleanup();
@@ -75,7 +77,37 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
size_t StackSizeBefore = Stk.size();
Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
- auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue());
+ auto Res = C.interpretExpr(E);
+
+ if (Res.isInvalid()) {
+ C.cleanup();
+ Stk.clearTo(StackSizeBefore);
+ return false;
+ }
+
+ if (!Recursing) {
+ // We *can* actually get here with a non-empty stack, since
+ // things like InterpState::noteSideEffect() exist.
+ C.cleanup();
+#ifndef NDEBUG
+ // Make sure we don't rely on some value being still alive in
+ // InterpStack memory.
+ Stk.clearTo(StackSizeBefore);
+#endif
+ }
+
+ Result = Res.stealAPValue();
+ return true;
+}
+
+bool Context::evaluateAsRValue(const EvalSettings &Settings, const Expr *E,
+ APValue &Result) {
+ ++EvalID;
+ bool Recursing = !Stk.empty();
+ size_t StackSizeBefore = Stk.size();
+ Compiler<EvalEmitter> C(*this, *P, Settings, Stk);
+
+ auto Res = C.interpretExpr(E);
if (Res.isInvalid()) {
C.cleanup();
@@ -99,12 +131,12 @@ bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
return true;
}
-bool Context::evaluate(State &Parent, const Expr *E, APValue &Result,
- ConstantExprKind Kind) {
+bool Context::evaluate(const EvalSettings &Settings, const Expr *E,
+ APValue &Result) {
++EvalID;
bool Recursing = !Stk.empty();
size_t StackSizeBefore = Stk.size();
- Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+ Compiler<EvalEmitter> C(*this, *P, Settings, Stk);
auto Res = C.interpretExpr(E, /*ConvertResultToRValue=*/false,
/*DestroyToplevelScope=*/true);
@@ -128,16 +160,17 @@ bool Context::evaluate(State &Parent, const Expr *E, APValue &Result,
return true;
}
-bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
- const Expr *Init, APValue &Result) {
+bool Context::evaluateAsInitializer(const EvalSettings &Settings,
+ const VarDecl *VD, const Expr *Init,
+ APValue &Result) {
++EvalID;
bool Recursing = !Stk.empty();
size_t StackSizeBefore = Stk.size();
- Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+ Compiler<EvalEmitter> C(*this, *P, Settings, Stk);
bool CheckGlobalInitialized =
- shouldBeGloballyIndexed(VD) &&
(VD->getType()->isRecordType() || VD->getType()->isArrayType());
+
auto Res = C.interpretDecl(VD, Init, CheckGlobalInitialized);
if (Res.isInvalid()) {
C.cleanup();
@@ -161,21 +194,23 @@ bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
}
template <typename ResultT>
-bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
- const Expr *PtrExpr, ResultT &Result) {
+bool Context::evaluateStringRepr(const EvalSettings &Settings,
+ const Expr *SizeExpr, const Expr *PtrExpr,
+ ResultT &Result) {
assert(Stk.empty());
- Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+ Compiler<EvalEmitter> C(*this, *P, Settings, Stk);
// Evaluate size value.
APValue SizeValue;
- if (!evaluateAsRValue(Parent, SizeExpr, SizeValue))
+ if (!evaluateAsRValue(Settings, SizeExpr, SizeValue))
return false;
if (!SizeValue.isInt())
return false;
uint64_t Size = SizeValue.getInt().getZExtValue();
- auto PtrRes = C.interpretAsPointer(PtrExpr, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(PtrExpr, [&](InterpState &S,
+ const Pointer &Ptr) {
if (Size == 0) {
if constexpr (std::is_same_v<ResultT, APValue>)
Result = APValue(APValue::UninitArray{}, 0, 0);
@@ -190,7 +225,7 @@ bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
return false;
if (Size > Ptr.getNumElems()) {
- Parent.FFDiag(SizeExpr, diag::note_constexpr_access_past_end) << AK_Read;
+ S.FFDiag(SizeExpr, diag::note_constexpr_access_past_end) << AK_Read;
Size = Ptr.getNumElems();
}
@@ -223,28 +258,30 @@ bool Context::evaluateStringRepr(State &Parent, const Expr *SizeExpr,
return true;
}
-bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr,
- const Expr *PtrExpr, APValue &Result) {
+bool Context::evaluateCharRange(const EvalSettings &Settings,
+ const Expr *SizeExpr, const Expr *PtrExpr,
+ APValue &Result) {
assert(SizeExpr);
assert(PtrExpr);
- return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result);
+ return evaluateStringRepr(Settings, SizeExpr, PtrExpr, Result);
}
-bool Context::evaluateCharRange(State &Parent, const Expr *SizeExpr,
- const Expr *PtrExpr, std::string &Result) {
+bool Context::evaluateCharRange(const EvalSettings &Settings,
+ const Expr *SizeExpr, const Expr *PtrExpr,
+ std::string &Result) {
assert(SizeExpr);
assert(PtrExpr);
- return evaluateStringRepr(Parent, SizeExpr, PtrExpr, Result);
+ return evaluateStringRepr(Settings, SizeExpr, PtrExpr, Result);
}
-bool Context::evaluateString(State &Parent, const Expr *E,
+bool Context::evaluateString(const EvalSettings &Settings, const Expr *E,
std::string &Result) {
assert(Stk.empty());
- Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+ Compiler<EvalEmitter> C(*this, *P, Settings, Stk);
- auto PtrRes = C.interpretAsPointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(E, [&](InterpState &, const Pointer &Ptr) {
if (!Ptr.isBlockPointer())
return false;
@@ -288,12 +325,13 @@ bool Context::evaluateString(State &Parent, const Expr *E,
return true;
}
-std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) {
+std::optional<uint64_t> Context::evaluateStrlen(const EvalSettings &Settings,
+ const Expr *E) {
assert(Stk.empty());
- Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+ Compiler<EvalEmitter> C(*this, *P, Settings, Stk);
std::optional<uint64_t> Result;
- auto PtrRes = C.interpretAsPointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(E, [&](InterpState &, const Pointer &Ptr) {
if (!Ptr.isBlockPointer())
return false;
@@ -335,13 +373,14 @@ std::optional<uint64_t> Context::evaluateStrlen(State &Parent, const Expr *E) {
}
std::optional<uint64_t>
-Context::tryEvaluateObjectSize(State &Parent, const Expr *E, unsigned Kind) {
+Context::tryEvaluateObjectSize(const EvalSettings &Settings, const Expr *E,
+ unsigned Kind) {
assert(Stk.empty());
- Compiler<EvalEmitter> C(*this, *P, Parent, Stk);
+ Compiler<EvalEmitter> C(*this, *P, Settings, Stk);
std::optional<uint64_t> Result;
- auto PtrRes = C.interpretAsPointer(E, [&](const Pointer &Ptr) {
+ auto PtrRes = C.interpretAsPointer(E, [&](InterpState &, const Pointer &Ptr) {
const Descriptor *DeclDesc = Ptr.getDeclDesc();
if (!DeclDesc)
return false;
@@ -490,8 +529,8 @@ const llvm::fltSemantics &Context::getFloatSemantics(QualType T) const {
return Ctx.getFloatTypeSemantics(T);
}
-bool Context::Run(State &Parent, const Function *Func) {
- InterpState State(Parent, *P, Stk, *this, Func);
+bool Context::Run(const EvalSettings &Settings, const Function *Func) {
+ InterpState State(Settings, *P, Stk, *this, Func);
if (Interpret(State)) {
assert(Stk.empty());
return true;
diff --git a/clang/lib/AST/ByteCode/Context.h b/clang/lib/AST/ByteCode/Context.h
index 1b8c25732a262..fd762a81c4659 100644
--- a/clang/lib/AST/ByteCode/Context.h
+++ b/clang/lib/AST/ByteCode/Context.h
@@ -30,6 +30,7 @@ namespace interp {
class Function;
class Program;
class State;
+struct EvalSettings;
enum PrimType : uint8_t;
struct ParamOffset {
@@ -48,33 +49,40 @@ class Context final {
~Context();
/// Checks if a function is a potential constant expression.
- bool isPotentialConstantExpr(State &Parent, const FunctionDecl *FD);
- void isPotentialConstantExprUnevaluated(State &Parent, const Expr *E,
+ bool isPotentialConstantExpr(const EvalSettings &Settings,
+ const FunctionDecl *FD);
+ void isPotentialConstantExprUnevaluated(const EvalSettings &Settings,
+ const Expr *E,
const FunctionDecl *FD);
- /// Evaluates a toplevel expression as an rvalue.
+ // FIXME: Get rid of this version and switch to the one taking
+ // EvalSettings always.
bool evaluateAsRValue(State &Parent, const Expr *E, APValue &Result);
+ /// Evaluates a toplevel expression as an rvalue.
+ bool evaluateAsRValue(const EvalSettings &Settings, const Expr *E,
+ APValue &Result);
/// Like evaluateAsRvalue(), but does no implicit lvalue-to-rvalue conversion.
- bool evaluate(State &Parent, const Expr *E, APValue &Result,
- ConstantExprKind Kind);
+ bool evaluate(const EvalSettings &Settings, const Expr *E, APValue &Result);
/// Evaluates a toplevel initializer.
- bool evaluateAsInitializer(State &Parent, const VarDecl *VD, const Expr *Init,
- APValue &Result);
+ bool evaluateAsInitializer(const EvalSettings &Settings, const VarDecl *VD,
+ const Expr *Init, APValue &Result);
- bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
+ bool evaluateCharRange(const EvalSettings &Settings, const Expr *SizeExpr,
const Expr *PtrExpr, APValue &Result);
- bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
+ bool evaluateCharRange(const EvalSettings &Settings, const Expr *SizeExpr,
const Expr *PtrExpr, std::string &Result);
/// Evaluate \param E and if it can be evaluated to a null-terminated string,
/// copy the result into \param Result.
- bool evaluateString(State &Parent, const Expr *E, std::string &Result);
+ bool evaluateString(const EvalSettings &Settings, const Expr *E,
+ std::string &Result);
/// Evalute \param E and if it can be evaluated to a string literal,
/// run strlen() on it.
- std::optional<uint64_t> evaluateStrlen(State &Parent, const Expr *E);
+ std::optional<uint64_t> evaluateStrlen(const EvalSettings &Settings,
+ const Expr *E);
/// If \param E evaluates to a pointer the number of accessible bytes
/// past the pointer is estimated in \param Result as if evaluated by
@@ -86,8 +94,8 @@ class Context final {
/// as the one referred to by E are considered, when Kind & 1 == 0
/// bytes belonging to the same storage (stack, heap allocation,
/// global variable) are considered.
- std::optional<uint64_t> tryEvaluateObjectSize(State &Parent, const Expr *E,
- unsigned Kind);
+ std::optional<uint64_t> tryEvaluateObjectSize(const EvalSettings &Settings,
+ const Expr *E, unsigned Kind);
/// Returns the AST context.
ASTContext &getASTContext() const { return Ctx; }
@@ -170,10 +178,10 @@ class Context final {
private:
friend class EvalIDScope;
/// Runs a function.
- bool Run(State &Parent, const Function *Func);
+ bool Run(const EvalSettings &Settings, const Function *Func);
template <typename ResultT>
- bool evaluateStringRepr(State &Parent, const Expr *SizeExpr,
+ bool evaluateStringRepr(const EvalSettings &Settings, const Expr *SizeExpr,
const Expr *PtrExpr, ResultT &Result);
/// Current compilation context.
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp
index 35937e3483e38..e50abf429b2bc 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -613,9 +613,6 @@ LLVM_DUMP_METHOD void EvaluationResult::dump() const {
OS << "Invalid\n";
} else {
OS << "Value: ";
-#ifndef NDEBUG
- assert(Ctx);
- Value.dump(OS, Ctx->getASTContext());
-#endif
+ Value.dump(OS, Ctx.getASTContext());
}
}
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.cpp b/clang/lib/AST/ByteCode/EvalEmitter.cpp
index 1bc82b2ecab1a..a9721ca6e406f 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.cpp
+++ b/clang/lib/AST/ByteCode/EvalEmitter.cpp
@@ -11,13 +11,20 @@
#include "IntegralAP.h"
#include "Interp.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
using namespace clang;
using namespace clang::interp;
+// FIXME: Get rid of this constructor.
EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
InterpStack &Stk)
- : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {}
+ : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(Ctx) {}
+
+EvalEmitter::EvalEmitter(Context &Ctx, Program &P, const EvalSettings &Settings,
+ InterpStack &Stk)
+ : Ctx(Ctx), P(P), S(Settings, P, Stk, Ctx, this), EvalResult(Ctx),
+ ConstexprKind(Settings.ConstexprKind) {}
EvalEmitter::~EvalEmitter() {
for (auto &V : Locals) {
@@ -188,6 +195,18 @@ template <PrimType OpType> bool EvalEmitter::emitRet(SourceInfo Info) {
return true;
}
+template <> bool EvalEmitter::emitRet<PT_MemberPtr>(SourceInfo Info) {
+ if (!isActive())
+ return true;
+
+ const MemberPointer &MP = S.Stk.pop<MemberPointer>();
+ if (!EvalResult.checkMemberPointer(S, MP, Info, ConstexprKind))
+ return false;
+
+ EvalResult.takeValue(MP.toAPValue(Ctx.getASTContext()));
+ return true;
+}
+
template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
if (!isActive())
return true;
@@ -195,15 +214,19 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
// If we're returning a raw pointer, call our callback.
if (this->PtrCB)
- return (*this->PtrCB)(Ptr);
+ return (*this->PtrCB)(S, Ptr);
- if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
+ if (!EvalResult.checkDynamicAllocations(S, Ctx, Ptr, Info))
return false;
+
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;
// Function pointers are always returned as lvalues.
if (Ptr.isFunctionPointer()) {
+ if (!EvalResult.checkFunctionPointer(S, Ptr, Info, ConstexprKind))
+ return false;
+
EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
return true;
}
@@ -225,6 +248,9 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
Ptr.block()->getEvalID() != Ctx.getEvalID())
return false;
+ if (!EvalResult.checkLValueFields(S, Ptr, Info, ConstexprKind))
+ return false;
+
if (std::optional<APValue> V =
Ptr.toRValue(Ctx, EvalResult.getSourceType())) {
EvalResult.takeValue(std::move(*V));
@@ -232,13 +258,8 @@ template <> bool EvalEmitter::emitRet<PT_Ptr>(SourceInfo Info) {
return false;
}
} else {
- // If this is pointing to a local variable, just return
- // the result, even if the pointer is dead.
- // This will later be diagnosed by CheckLValueConstantExpression.
- if (Ptr.isBlockPointer() && !Ptr.block()->isStatic()) {
- EvalResult.takeValue(Ptr.toAPValue(Ctx.getASTContext()));
- return true;
- }
+ if (!EvalResult.checkLValue(S, Ptr, Info, ConstexprKind))
+ return false;
if (!Ptr.isLive() && !Ptr.isTemporary())
return false;
@@ -257,10 +278,12 @@ bool EvalEmitter::emitRetVoid(SourceInfo Info) {
bool EvalEmitter::emitRetValue(SourceInfo Info) {
const auto &Ptr = S.Stk.pop<Pointer>();
- if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info))
+ if (!EvalResult.checkDynamicAllocations(S, Ctx, Ptr, Info))
return false;
if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr))
return false;
+ if (!EvalResult.checkLValueFields(S, Ptr, Info, ConstexprKind))
+ return false;
if (std::optional<APValue> APV =
Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) {
diff --git a/clang/lib/AST/ByteCode/EvalEmitter.h b/clang/lib/AST/ByteCode/EvalEmitter.h
index a9f87db5d7f8d..045512c5582eb 100644
--- a/clang/lib/AST/ByteCode/EvalEmitter.h
+++ b/clang/lib/AST/ByteCode/EvalEmitter.h
@@ -33,11 +33,16 @@ class EvalEmitter : public SourceMapper {
using LabelTy = uint32_t;
using AddrTy = uintptr_t;
using Local = Scope::Local;
- using PtrCallback = llvm::function_ref<bool(const Pointer &)>;
+ using PtrCallback = llvm::function_ref<bool(InterpState &, const Pointer &)>;
- EvaluationResult interpretExpr(const Expr *E,
- bool ConvertResultToRValue = false,
+ EvaluationResult interpretExpr(const Expr *E) {
+ return interpretExpr(E, /*ConvertResultToRValue=*/E->isGLValue(),
+ /*DestroyToplevelScope=*/false);
+ }
+
+ EvaluationResult interpretExpr(const Expr *E, bool ConvertResultToRValue,
bool DestroyToplevelScope = false);
+
EvaluationResult interpretDecl(const VarDecl *VD, const Expr *Init,
bool CheckFullyInitialized);
/// Interpret the given Expr to a Pointer.
@@ -49,8 +54,14 @@ class EvalEmitter : public SourceMapper {
/// Clean up all resources.
void cleanup();
+ bool constantFolding() const {
+ return S.EvalMode == EvaluationMode::ConstantFold;
+ }
+
protected:
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk);
+ EvalEmitter(Context &Ctx, Program &P, const EvalSettings &Settings,
+ InterpStack &Stk);
virtual ~EvalEmitter();
@@ -109,6 +120,7 @@ class EvalEmitter : public SourceMapper {
InterpState S;
/// Location to write the result to.
EvaluationResult EvalResult;
+ ConstantExprKind ConstexprKind = ConstantExprKind::Normal;
/// Whether the result should be converted to an RValue.
bool ConvertResultToRValue = false;
/// Whether we should check if the result has been fully
diff --git a/clang/lib/AST/ByteCode/EvalSettings.h b/clang/lib/AST/ByteCode/EvalSettings.h
new file mode 100644
index 0000000000000..df4dcda515ded
--- /dev/null
+++ b/clang/lib/AST/ByteCode/EvalSettings.h
@@ -0,0 +1,35 @@
+//===--------------------------- EvalSettings.h -----------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_EVALSETTINGS_H
+#define LLVM_CLANG_AST_INTERP_EVALSETTINGS_H
+
+#include "State.h"
+
+namespace clang {
+namespace interp {
+
+struct EvalSettings {
+ Expr::EvalStatus &EvalStatus;
+ const EvaluationMode EvalMode;
+ const ConstantExprKind ConstexprKind;
+
+ bool InConstantContext = false;
+ bool CheckingPotentialConstantExpression = false;
+ bool CheckingForUndefinedBehavior = false;
+
+ EvalSettings(EvaluationMode EvalMode, Expr::EvalStatus &EvalStatus,
+ ConstantExprKind ConstexprKind = ConstantExprKind::Normal)
+ : EvalStatus(EvalStatus), EvalMode(EvalMode),
+ ConstexprKind(ConstexprKind) {}
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/clang/lib/AST/ByteCode/EvaluationResult.cpp b/clang/lib/AST/ByteCode/EvaluationResult.cpp
index 039848f00764e..f3df32c48f34a 100644
--- a/clang/lib/AST/ByteCode/EvaluationResult.cpp
+++ b/clang/lib/AST/ByteCode/EvaluationResult.cpp
@@ -7,16 +7,36 @@
//===----------------------------------------------------------------------===//
#include "EvaluationResult.h"
+#include "../ExprConstShared.h"
#include "InterpState.h"
#include "Pointer.h"
#include "Record.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include <iterator>
namespace clang {
namespace interp {
+QualType EvaluationResult::getStorageType() const {
+ if (const auto *E = Source.dyn_cast<const Expr *>()) {
+ if (E->isPRValue())
+ return E->getType();
+
+ return Ctx.getASTContext().getLValueReferenceType(E->getType());
+ }
+
+ if (const auto *D =
+ dyn_cast_if_present<ValueDecl>(Source.dyn_cast<const Decl *>()))
+ return D->getType();
+ return QualType();
+}
+
static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc,
const FieldDecl *SubObjDecl) {
assert(SubObjDecl && "Subobject declaration does not exist");
@@ -210,9 +230,10 @@ static void collectBlocks(const Pointer &Ptr,
}
}
-bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,
- const Pointer &Ptr,
- const SourceInfo &Info) {
+bool EvaluationResult::checkDynamicAllocations(InterpState &S,
+ const Context &Ctx,
+ const Pointer &Ptr,
+ SourceInfo Info) {
// Collect all blocks that this pointer (transitively) points to and
// return false if any of them is a dynamic block.
llvm::SetVector<const Block *> Blocks;
@@ -236,5 +257,338 @@ bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,
return true;
}
+static bool isGlobalLValue(const Pointer &Ptr) {
+ if (Ptr.isBlockPointer() && Ptr.block()->isDynamic())
+ return true;
+ if (Ptr.isTypeidPointer())
+ return true;
+
+ const Descriptor *Desc = Ptr.getDeclDesc();
+ return ::isGlobalLValue(Desc->asValueDecl(), Desc->asExpr());
+}
+
+/// Check if the given function pointer can be returned from an evaluation.
+static bool checkFunctionPtr(InterpState &S, const Pointer &Ptr,
+ QualType PtrType, SourceInfo Info,
+ ConstantExprKind ConstexprKind) {
+ assert(Ptr.isFunctionPointer());
+ const FunctionPointer &FuncPtr = Ptr.asFunctionPointer();
+ const FunctionDecl *FD = FuncPtr.getFunction()->getDecl();
+ // E.g. ObjC block pointers.
+ if (!FD)
+ return true;
+ if (FD->isImmediateFunction()) {
+ S.FFDiag(Info, diag::note_consteval_address_accessible)
+ << !PtrType->isAnyPointerType();
+ S.Note(FD->getLocation(), diag::note_declared_at);
+ return false;
+ }
+
+ // __declspec(dllimport) must be handled very carefully:
+ // We must never initialize an expression with the thunk in C++.
+ // Doing otherwise would allow the same id-expression to yield
+ // different addresses for the same function in different translation
+ // units. However, this means that we must dynamically initialize the
+ // expression with the contents of the import address table at runtime.
+ //
+ // The C language has no notion of ODR; furthermore, it has no notion of
+ // dynamic initialization. This means that we are permitted to
+ // perform initialization with the address of the thunk.
+ if (S.getLangOpts().CPlusPlus && !isForManglingOnly(ConstexprKind) &&
+ FD->hasAttr<DLLImportAttr>())
+ // FIXME: Diagnostic!
+ return false;
+ return true;
+}
+
+static bool lvalFields(InterpState &S, const ASTContext &Ctx,
+ const Pointer &Ptr, QualType PtrType, SourceInfo Info,
+ ConstantExprKind ConstexprKind,
+ llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks);
+static bool lval(InterpState &S, const ASTContext &Ctx, const Pointer &Ptr,
+ QualType PtrType, SourceInfo Info,
+ ConstantExprKind ConstexprKind,
+ llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks) {
+ if (Ptr.isFunctionPointer())
+ return checkFunctionPtr(S, Ptr, PtrType, Info, ConstexprKind);
+
+ if (!Ptr.isBlockPointer())
+ return true;
+
+ const Descriptor *DeclDesc = Ptr.block()->getDescriptor();
+ const Expr *BaseE = DeclDesc->asExpr();
+ const ValueDecl *BaseVD = DeclDesc->asValueDecl();
+ assert(BaseE || BaseVD);
+ bool IsReferenceType = PtrType->isReferenceType();
+ bool IsSubObj = !Ptr.isRoot() || (Ptr.inArray() && !Ptr.isArrayRoot());
+
+ if (!isGlobalLValue(Ptr)) {
+ if (S.getLangOpts().CPlusPlus11) {
+ S.FFDiag(Info, diag::note_constexpr_non_global, 1)
+ << IsReferenceType << IsSubObj
+ << !!DeclDesc->asValueDecl() // DeclDesc->IsTemporary
+ << DeclDesc->asValueDecl();
+ const VarDecl *VarD = DeclDesc->asVarDecl();
+ if (VarD && VarD->isConstexpr()) {
+ // Non-static local constexpr variables have unintuitive semantics:
+ // constexpr int a = 1;
+ // constexpr const int *p = &a;
+ // ... is invalid because the address of 'a' is not constant. Suggest
+ // adding a 'static' in this case.
+ S.Note(VarD->getLocation(), diag::note_constexpr_not_static)
+ << VarD
+ << FixItHint::CreateInsertion(VarD->getBeginLoc(), "static ");
+ } else {
+ if (const ValueDecl *VD = DeclDesc->asValueDecl())
+ S.Note(VD->getLocation(), diag::note_declared_at);
+ else if (const Expr *E = DeclDesc->asExpr())
+ S.Note(E->getExprLoc(), diag::note_constexpr_temporary_here);
+ }
+ } else {
+ S.FFDiag(Info);
+ }
+ return false;
+ }
+
+ if (const auto *VD = dyn_cast_if_present<VarDecl>(BaseVD)) {
+ // Check if this is a thread-local variable.
+ if (VD->getTLSKind()) {
+ // FIXME: Diagnostic!
+ return false;
+ }
+
+ // A dllimport variable never acts like a constant, unless we're
+ // evaluating a value for use only in name mangling, and unless it's a
+ // static local. For the latter case, we'd still need to evaluate the
+ // constant expression in case we're inside a (inlined) function.
+ if (!isForManglingOnly(ConstexprKind) && VD->hasAttr<DLLImportAttr>() &&
+ !VD->isStaticLocal())
+ return false;
+
+ // In CUDA/HIP device compilation, only device side variables have
+ // constant addresses.
+ if (S.getLangOpts().CUDA && S.getLangOpts().CUDAIsDevice &&
+ Ctx.CUDAConstantEvalCtx.NoWrongSidedVars) {
+ if ((!VD->hasAttr<CUDADeviceAttr>() && !VD->hasAttr<CUDAConstantAttr>() &&
+ !VD->getType()->isCUDADeviceBuiltinSurfaceType() &&
+ !VD->getType()->isCUDADeviceBuiltinTextureType()) ||
+ VD->hasAttr<HIPManagedAttr>())
+ return false;
+ }
+
+ return true;
+ }
+
+ if (const auto *MTE = dyn_cast_if_present<MaterializeTemporaryExpr>(BaseE)) {
+ QualType TempType = Ptr.getType();
+
+ if (TempType.isDestructedType()) {
+ S.FFDiag(MTE->getExprLoc(),
+ diag::note_constexpr_unsupported_temporary_nontrivial_dtor)
+ << TempType;
+ return false;
+ }
+
+ if (Ptr.getFieldDesc()->isPrimitive() &&
+ Ptr.getFieldDesc()->getPrimType() == PT_Ptr) {
+ // Recurse!
+ Pointer Pointee = Ptr.deref<Pointer>();
+ if (CheckedBlocks.insert(Pointee.block()).second) {
+ if (!lval(S, Ctx, Pointee, Pointee.getType(),
+ Ptr.getDeclDesc()->getLoc(), ConstexprKind, CheckedBlocks))
+ return false;
+ }
+ } else if (Ptr.getRecord()) {
+ return lvalFields(S, Ctx, Ptr, Ptr.getType(), Info,
+ ConstantExprKind::Normal, CheckedBlocks);
+ }
+ }
+
+ return true;
+}
+
+static bool lvalFields(InterpState &S, const ASTContext &Ctx,
+ const Pointer &Ptr, QualType PtrType, SourceInfo Info,
+ ConstantExprKind ConstexprKind,
+ llvm::SmallPtrSet<const Block *, 4> &CheckedBlocks) {
+ if (!Ptr.isBlockPointer())
+ return true;
+
+ const Descriptor *FieldDesc = Ptr.getFieldDesc();
+ if (const Record *R = Ptr.getRecord()) {
+ if (!R->hasPtrField())
+ return true;
+ for (const Record::Field &F : R->fields()) {
+ if (F.Desc->isPrimitive() && F.Desc->getPrimType() == PT_Ptr) {
+ QualType FieldType = F.Decl->getType();
+ if (!Ptr.atField(F.Offset).isLive())
+ return false;
+
+ Pointer Pointee = Ptr.atField(F.Offset).deref<Pointer>();
+ if (CheckedBlocks.insert(Pointee.block()).second) {
+ if (!lval(S, Ctx, Pointee, FieldType, Info, ConstexprKind,
+ CheckedBlocks))
+ return false;
+ }
+ } else {
+ Pointer FieldPtr = Ptr.atField(F.Offset);
+ if (!lvalFields(S, Ctx, FieldPtr, F.Decl->getType(), Info,
+ ConstexprKind, CheckedBlocks))
+ return false;
+ }
+ }
+
+ for (const Record::Base &B : R->bases()) {
+ Pointer BasePtr = Ptr.atField(B.Offset);
+ if (!lvalFields(S, Ctx, BasePtr, B.Desc->getType(), Info, ConstexprKind,
+ CheckedBlocks))
+ return false;
+ }
+ for (const Record::Base &B : R->virtual_bases()) {
+ Pointer BasePtr = Ptr.atField(B.Offset);
+ if (!lvalFields(S, Ctx, BasePtr, B.Desc->getType(), Info, ConstexprKind,
+ CheckedBlocks))
+ return false;
+ }
+
+ return true;
+ }
+ if (FieldDesc->isPrimitiveArray()) {
+ if (FieldDesc->getPrimType() == PT_Ptr) {
+ for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+ if (!Ptr.isLive())
+ return false;
+ Pointer Pointee = Ptr.elem<Pointer>(I);
+ if (CheckedBlocks.insert(Pointee.block()).second) {
+ if (!lval(S, Ctx, Pointee, FieldDesc->getElemQualType(), Info,
+ ConstexprKind, CheckedBlocks))
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ if (FieldDesc->isCompositeArray()) {
+ if (FieldDesc->ElemRecord && !FieldDesc->ElemRecord->hasPtrField())
+ return true;
+
+ for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
+ Pointer Elem = Ptr.atIndex(I).narrow();
+ if (!lvalFields(S, Ctx, Elem, FieldDesc->getElemQualType(), Info,
+ ConstexprKind, CheckedBlocks))
+ return false;
+ }
+ return true;
+ }
+ if (FieldDesc->isPrimitive() && FieldDesc->getPrimType() == PT_MemberPtr) {
+ MemberPointer MP = Ptr.deref<MemberPointer>();
+ if (!EvaluationResult::checkMemberPointer(S, MP, Info, ConstexprKind))
+ return false;
+ }
+
+ return true;
+}
+
+/// Toplevel accessor to check all lvalue fields.
+bool EvaluationResult::checkLValueFields(InterpState &S, const Pointer &Ptr,
+ SourceInfo Info,
+ ConstantExprKind ConstexprKind) {
+ QualType SourceType = getStorageType();
+ llvm::SmallPtrSet<const Block *, 4> CheckedBlocks;
+
+ return lvalFields(S, Ctx.getASTContext(), Ptr, SourceType, Info,
+ ConstexprKind, CheckedBlocks);
+}
+
+bool EvaluationResult::checkLValue(InterpState &S, const Pointer &Ptr,
+ SourceInfo Info,
+ ConstantExprKind ConstexprKind) {
+ if (Ptr.isZero())
+ return true;
+
+ QualType SourceType = getStorageType();
+ if (Ptr.isFunctionPointer())
+ return checkFunctionPtr(S, Ptr, SourceType, Info, ConstexprKind);
+
+ bool IsReferenceType = SourceType->isReferenceType();
+ if (Ptr.isTypeidPointer()) {
+ if (isTemplateArgument(ConstexprKind)) {
+ S.FFDiag(Info, diag::note_constexpr_invalid_template_arg)
+ << IsReferenceType << /*IsSubObj=*/false << /*InvalidBaseKind=*/0;
+ return false;
+ }
+ return true;
+ }
+
+ if (!Ptr.isBlockPointer())
+ return true;
+
+ const Descriptor *DeclDesc = Ptr.getDeclDesc();
+ const Expr *BaseE = DeclDesc->asExpr();
+ const ValueDecl *BaseVD = DeclDesc->asValueDecl();
+ assert(BaseE || BaseVD);
+ bool IsSubObj = !Ptr.isRoot() || (Ptr.inArray() && !Ptr.isArrayRoot());
+
+ // Additional restrictions apply in a template argument. We only enforce the
+ // C++20 restrictions here; additional syntactic and semantic restrictions
+ // are applied elsewhere.
+ if (isTemplateArgument(ConstexprKind)) {
+ int InvalidBaseKind = -1;
+ StringRef Ident;
+ if (isa_and_nonnull<StringLiteral>(BaseE))
+ InvalidBaseKind = 1;
+ else if (isa_and_nonnull<MaterializeTemporaryExpr>(BaseE) ||
+ isa_and_nonnull<LifetimeExtendedTemporaryDecl>(BaseVD))
+ InvalidBaseKind = 2;
+ else if (const auto *PE = dyn_cast_if_present<PredefinedExpr>(BaseE)) {
+ InvalidBaseKind = 3;
+ Ident = PE->getIdentKindName();
+ IsSubObj = true;
+ }
+
+ if (InvalidBaseKind != -1) {
+ S.FFDiag(Info, diag::note_constexpr_invalid_template_arg)
+ << IsReferenceType << IsSubObj << InvalidBaseKind << Ident;
+ return false;
+ }
+ }
+
+ llvm::SmallPtrSet<const Block *, 4> CheckedBlocks;
+ if (!lval(S, Ctx.getASTContext(), Ptr, SourceType, Info, ConstexprKind,
+ CheckedBlocks)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool EvaluationResult::checkMemberPointer(InterpState &S,
+ const MemberPointer &MemberPtr,
+ SourceInfo Info,
+ ConstantExprKind ConstexprKind) {
+ const CXXMethodDecl *MD = MemberPtr.getMemberFunction();
+ if (!MD)
+ return true;
+
+ if (MD->isImmediateFunction()) {
+ S.FFDiag(Info, diag::note_consteval_address_accessible)
+ << /*pointer=*/false;
+ S.Note(MD->getLocation(), diag::note_declared_at);
+ return false;
+ }
+
+ if (isForManglingOnly(ConstexprKind) || MD->isVirtual() ||
+ !MD->hasAttr<DLLImportAttr>()) {
+ return true;
+ }
+ return false;
+}
+
+bool EvaluationResult::checkFunctionPointer(InterpState &S, const Pointer &Ptr,
+ SourceInfo Info,
+ ConstantExprKind ConstexprKind) {
+ return checkFunctionPtr(S, Ptr, getStorageType(), Info, ConstexprKind);
+}
+
} // namespace interp
} // namespace clang
diff --git a/clang/lib/AST/ByteCode/EvaluationResult.h b/clang/lib/AST/ByteCode/EvaluationResult.h
index c296cc98ca375..a98ee1dc32c9b 100644
--- a/clang/lib/AST/ByteCode/EvaluationResult.h
+++ b/clang/lib/AST/ByteCode/EvaluationResult.h
@@ -17,6 +17,7 @@ namespace clang {
namespace interp {
class EvalEmitter;
class Context;
+class MemberPointer;
class Pointer;
class SourceInfo;
class InterpState;
@@ -39,9 +40,7 @@ class EvaluationResult final {
using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
private:
-#ifndef NDEBUG
- const Context *Ctx = nullptr;
-#endif
+ const Context &Ctx;
APValue Value;
ResultKind Kind = Empty;
DeclTy Source = nullptr;
@@ -62,31 +61,37 @@ class EvaluationResult final {
Kind = Valid;
}
+ QualType getStorageType() const;
+
public:
-#ifndef NDEBUG
- EvaluationResult(const Context *Ctx) : Ctx(Ctx) {}
-#else
- EvaluationResult(const Context *Ctx) {}
-#endif
+ EvaluationResult(const Context &Ctx) : Ctx(Ctx) {}
bool empty() const { return Kind == Empty; }
bool isInvalid() const { return Kind == Invalid; }
-
- /// Returns an APValue for the evaluation result.
- APValue toAPValue() const {
- assert(!empty());
- assert(!isInvalid());
- return Value;
- }
-
APValue stealAPValue() { return std::move(Value); }
/// Check that all subobjects of the given pointer have been initialized.
bool checkFullyInitialized(InterpState &S, const Pointer &Ptr) const;
/// Check that none of the blocks the given pointer (transitively) points
/// to are dynamically allocated.
- bool checkReturnValue(InterpState &S, const Context &Ctx, const Pointer &Ptr,
- const SourceInfo &Info);
+ bool checkDynamicAllocations(InterpState &S, const Context &Ctx,
+ const Pointer &Ptr, SourceInfo Info);
+
+ /// Check the given pointer as an lvalue, i.e. make sure it's a global
+ /// lvalue and diagnose if it's not.
+ bool checkLValue(InterpState &S, const Pointer &Ptr, SourceInfo Info,
+ ConstantExprKind ConstexprKind);
+ /// Check all fields of the given pointer.
+ bool checkLValueFields(InterpState &S, const Pointer &Ptr, SourceInfo Info,
+ ConstantExprKind ConstexprKind);
+
+ /// Check if the given member pointer can be returned from an evaluation.
+ static bool checkMemberPointer(InterpState &S, const MemberPointer &MemberPtr,
+ SourceInfo Info,
+ ConstantExprKind ConstexprKind);
+ /// Check if the given function pointer can be returned from an evaluation.
+ bool checkFunctionPointer(InterpState &S, const Pointer &Ptr, SourceInfo Info,
+ ConstantExprKind ConstexprKind);
QualType getSourceType() const {
if (const auto *D =
diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp
index ebc7220aa5671..d926fa9b15128 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -890,6 +890,8 @@ bool CheckFinalLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return false;
if (!CheckMutable(S, OpPC, Ptr))
return false;
+ if (!S.inConstantContext() && isConstexprUnknown(Ptr))
+ return false;
return true;
}
diff --git a/clang/lib/AST/ByteCode/InterpState.cpp b/clang/lib/AST/ByteCode/InterpState.cpp
index fd69559af5917..dd62e8da195db 100644
--- a/clang/lib/AST/ByteCode/InterpState.cpp
+++ b/clang/lib/AST/ByteCode/InterpState.cpp
@@ -30,18 +30,31 @@ InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
EvalMode = Parent.EvalMode;
}
-InterpState::InterpState(const State &Parent, Program &P, InterpStack &Stk,
- Context &Ctx, const Function *Func)
- : State(Ctx.getASTContext(), Parent.getEvalStatus()), M(nullptr), P(P),
+InterpState::InterpState(const EvalSettings &Settings, Program &P,
+ InterpStack &Stk, Context &Ctx, SourceMapper *M)
+ : State(Ctx.getASTContext(), Settings.EvalStatus), M(M), P(P), Stk(Stk),
+ Ctx(Ctx), BottomFrame(*this), Current(&BottomFrame),
+ StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
+ InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) {
+ InConstantContext = Settings.InConstantContext;
+ CheckingPotentialConstantExpression =
+ Settings.CheckingPotentialConstantExpression;
+ CheckingForUndefinedBehavior = Settings.CheckingForUndefinedBehavior;
+ EvalMode = Settings.EvalMode;
+}
+
+InterpState::InterpState(const EvalSettings &Settings, Program &P,
+ InterpStack &Stk, Context &Ctx, const Function *Func)
+ : State(Ctx.getASTContext(), Settings.EvalStatus), M(nullptr), P(P),
Stk(Stk), Ctx(Ctx),
BottomFrame(*this, Func, nullptr, CodePtr(), Func->getArgSize()),
Current(&BottomFrame), StepsLeft(Ctx.getLangOpts().ConstexprStepLimit),
InfiniteSteps(StepsLeft == 0), EvalID(Ctx.getEvalID()) {
- InConstantContext = Parent.InConstantContext;
+ InConstantContext = Settings.InConstantContext;
CheckingPotentialConstantExpression =
- Parent.CheckingPotentialConstantExpression;
- CheckingForUndefinedBehavior = Parent.CheckingForUndefinedBehavior;
- EvalMode = Parent.EvalMode;
+ Settings.CheckingPotentialConstantExpression;
+ CheckingForUndefinedBehavior = Settings.CheckingForUndefinedBehavior;
+ EvalMode = Settings.EvalMode;
}
bool InterpState::inConstantContext() const {
diff --git a/clang/lib/AST/ByteCode/InterpState.h b/clang/lib/AST/ByteCode/InterpState.h
index 8ed92432f1c7e..e5772da463f02 100644
--- a/clang/lib/AST/ByteCode/InterpState.h
+++ b/clang/lib/AST/ByteCode/InterpState.h
@@ -15,6 +15,7 @@
#include "Context.h"
#include "DynamicAllocator.h"
+#include "EvalSettings.h"
#include "Floating.h"
#include "Function.h"
#include "InterpFrame.h"
@@ -35,10 +36,14 @@ struct StdAllocatorCaller {
/// Interpreter context.
class InterpState final : public State, public SourceMapper {
public:
+ // FIXME: Get rid of this constructor as well.
InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
SourceMapper *M = nullptr);
- InterpState(const State &Parent, Program &P, InterpStack &Stk, Context &Ctx,
- const Function *Func);
+
+ InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk,
+ Context &Ctx, SourceMapper *M = nullptr);
+ InterpState(const EvalSettings &Settings, Program &P, InterpStack &Stk,
+ Context &Ctx, const Function *Func);
~InterpState();
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index 619c79a1408f3..5035306a8249c 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
#define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
+#include "ByteCode/State.h"
#include "clang/Basic/TypeTraits.h"
#include <cstdint>
#include <optional>
@@ -89,4 +90,46 @@ std::optional<llvm::APFloat>
EvalScalarMinMaxFp(const llvm::APFloat &A, const llvm::APFloat &B,
std::optional<llvm::APSInt> RoundingMode, bool IsMin);
+/// Determines whether the given kind of constant expression is only ever
+/// used for name mangling. If so, it's permitted to reference things that we
+/// can't generate code for (in particular, dllimported functions).
+inline bool isForManglingOnly(ConstantExprKind Kind) {
+ switch (Kind) {
+ case ConstantExprKind::Normal:
+ case ConstantExprKind::ClassTemplateArgument:
+ case ConstantExprKind::ImmediateInvocation:
+ // Note that non-type template arguments of class type are emitted as
+ // template parameter objects.
+ return false;
+
+ case ConstantExprKind::NonClassTemplateArgument:
+ return true;
+ }
+ llvm_unreachable("unknown ConstantExprKind");
+}
+
+inline bool isTemplateArgument(ConstantExprKind Kind) {
+ switch (Kind) {
+ case ConstantExprKind::Normal:
+ case ConstantExprKind::ImmediateInvocation:
+ return false;
+
+ case ConstantExprKind::ClassTemplateArgument:
+ case ConstantExprKind::NonClassTemplateArgument:
+ return true;
+ }
+ llvm_unreachable("unknown ConstantExprKind");
+}
+
+/// Should this call expression be treated as forming an opaque constant?
+inline bool isOpaqueConstantCall(const CallExpr *E) {
+ unsigned Builtin = E->getBuiltinCallee();
+ return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
+ Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
+ Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
+ Builtin == Builtin::BI__builtin_function_start);
+}
+
+bool isGlobalLValue(const ValueDecl *D, const Expr *E);
+
#endif
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 429fef0a1afa8..f48ab04980618 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -33,6 +33,7 @@
//===----------------------------------------------------------------------===//
#include "ByteCode/Context.h"
+#include "ByteCode/EvalSettings.h"
#include "ByteCode/Frame.h"
#include "ByteCode/State.h"
#include "ExprConstShared.h"
@@ -145,37 +146,6 @@ namespace {
return E && E->getType()->isPointerType() && tryUnwrapAllocSizeCall(E);
}
- /// Determines whether the given kind of constant expression is only ever
- /// used for name mangling. If so, it's permitted to reference things that we
- /// can't generate code for (in particular, dllimported functions).
- static bool isForManglingOnly(ConstantExprKind Kind) {
- switch (Kind) {
- case ConstantExprKind::Normal:
- case ConstantExprKind::ClassTemplateArgument:
- case ConstantExprKind::ImmediateInvocation:
- // Note that non-type template arguments of class type are emitted as
- // template parameter objects.
- return false;
-
- case ConstantExprKind::NonClassTemplateArgument:
- return true;
- }
- llvm_unreachable("unknown ConstantExprKind");
- }
-
- static bool isTemplateArgument(ConstantExprKind Kind) {
- switch (Kind) {
- case ConstantExprKind::Normal:
- case ConstantExprKind::ImmediateInvocation:
- return false;
-
- case ConstantExprKind::ClassTemplateArgument:
- case ConstantExprKind::NonClassTemplateArgument:
- return true;
- }
- llvm_unreachable("unknown ConstantExprKind");
- }
-
/// The bound to claim that an array of unknown bound has.
/// The value in MostDerivedArraySize is undefined in this case. So, set it
/// to an arbitrary value that's likely to loudly break things if it's used.
@@ -1926,31 +1896,30 @@ static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) {
return true;
}
-/// Should this call expression be treated as forming an opaque constant?
-static bool IsOpaqueConstantCall(const CallExpr *E) {
- unsigned Builtin = E->getBuiltinCallee();
- return (Builtin == Builtin::BI__builtin___CFStringMakeConstantString ||
- Builtin == Builtin::BI__builtin___NSStringMakeConstantString ||
- Builtin == Builtin::BI__builtin_ptrauth_sign_constant ||
- Builtin == Builtin::BI__builtin_function_start);
-}
-
static bool IsOpaqueConstantCall(const LValue &LVal) {
const auto *BaseExpr =
llvm::dyn_cast_if_present<CallExpr>(LVal.Base.dyn_cast<const Expr *>());
- return BaseExpr && IsOpaqueConstantCall(BaseExpr);
+ return BaseExpr && isOpaqueConstantCall(BaseExpr);
}
static bool IsGlobalLValue(APValue::LValueBase B) {
+ if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>())
+ return true;
+
+ return isGlobalLValue(B.dyn_cast<const ValueDecl *>(),
+ B.dyn_cast<const Expr *>());
+}
+
+bool isGlobalLValue(const ValueDecl *D, const Expr *E) {
// C++11 [expr.const]p3 An address constant expression is a prvalue core
// constant expression of pointer type that evaluates to...
// ... a null pointer value, or a prvalue core constant expression of type
// std::nullptr_t.
- if (!B)
+ if (!D && !E)
return true;
- if (const ValueDecl *D = B.dyn_cast<const ValueDecl*>()) {
+ if (D) {
// ... the address of an object with static storage duration,
if (const VarDecl *VD = dyn_cast<VarDecl>(D))
return VD->hasGlobalStorage();
@@ -1962,10 +1931,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
return isa<FunctionDecl, MSGuidDecl, UnnamedGlobalConstantDecl>(D);
}
- if (B.is<TypeInfoLValue>() || B.is<DynamicAllocLValue>())
- return true;
-
- const Expr *E = B.get<const Expr*>();
+ assert(E);
switch (E->getStmtClass()) {
default:
return false;
@@ -1986,7 +1952,7 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
case Expr::ObjCBoxedExprClass:
return cast<ObjCBoxedExpr>(E)->isExpressibleAsConstantInitializer();
case Expr::CallExprClass:
- return IsOpaqueConstantCall(cast<CallExpr>(E));
+ return isOpaqueConstantCall(cast<CallExpr>(E));
// For GCC compatibility, &&label has static storage duration.
case Expr::AddrLabelExprClass:
return true;
@@ -2007,6 +1973,8 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
// an expression might be a global lvalue.
return true;
}
+
+ return false;
}
static const ValueDecl *GetLValueBaseDecl(const LValue &LVal) {
@@ -10327,7 +10295,7 @@ static bool isOneByteCharacterType(QualType T) {
bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
unsigned BuiltinOp) {
- if (IsOpaqueConstantCall(E))
+ if (isOpaqueConstantCall(E))
return Success(E);
switch (BuiltinOp) {
@@ -20705,12 +20673,8 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) {
if (!CheckLiteralType(Info, E))
return false;
- if (Info.EnableNewConstInterp) {
- if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result))
- return false;
- return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result,
- ConstantExprKind::Normal);
- }
+ if (Info.EnableNewConstInterp)
+ return Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result);
if (!::Evaluate(Result, Info, E))
return false;
@@ -20842,6 +20806,18 @@ 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");
+
+ bool IsConst;
+ if (FastEvaluateAsRValue(this, Result.Val, Ctx, IsConst) &&
+ Result.Val.hasValue())
+ return true;
+
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::IgnoreSideEffects, Result);
+ S.InConstantContext = InConstantContext;
+ return Ctx.getInterpContext().evaluateAsRValue(S, this, Result.Val);
+ }
+
EvalInfo Info(Ctx, Result, EvaluationMode::IgnoreSideEffects);
Info.InConstantContext = InConstantContext;
return ::EvaluateAsRValue(this, Result, Ctx, Info);
@@ -20905,22 +20881,18 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx,
"Expression evaluator can't be called on a dependent expression.");
ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsLValue");
+
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::ConstantFold, Result);
+ S.InConstantContext = InConstantContext;
+ return Ctx.getInterpContext().evaluate(S, this, Result.Val);
+ }
+
EvalInfo Info(Ctx, Result, EvaluationMode::ConstantFold);
Info.InConstantContext = InConstantContext;
LValue LV;
CheckedTemporaries CheckedTemps;
- if (Info.EnableNewConstInterp) {
- if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val,
- ConstantExprKind::Normal))
- return false;
-
- LV.setFrom(Ctx, Result.Val);
- return CheckLValueConstantExpression(
- Info, getExprLoc(), Ctx.getLValueReferenceType(getType()), LV,
- ConstantExprKind::Normal, CheckedTemps);
- }
-
if (!EvaluateLValue(this, LV, Info) || !Info.discardCleanups() ||
Result.HasSideEffects ||
!CheckLValueConstantExpression(Info, getExprLoc(),
@@ -20966,17 +20938,16 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
return true;
ExprTimeTraceScope TimeScope(this, Ctx, "EvaluateAsConstantExpr");
- EvaluationMode EM = EvaluationMode::ConstantExpression;
- EvalInfo Info(Ctx, Result, EM);
- Info.InConstantContext = true;
- if (Info.EnableNewConstInterp) {
- if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val, Kind))
- return false;
- return CheckConstantExpression(Info, getExprLoc(),
- getStorageType(Ctx, this), Result.Val, Kind);
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::ConstantExpression, Result, Kind);
+ S.InConstantContext = true;
+ return Ctx.getInterpContext().evaluate(S, this, Result.Val);
}
+ EvalInfo Info(Ctx, Result, EvaluationMode::ConstantExpression);
+ Info.InConstantContext = true;
+
// The type of the object we're initializing is 'const T' for a class NTTP.
QualType T = getType();
if (Kind == ConstantExprKind::ClassTemplateArgument)
@@ -21041,52 +21012,50 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
Expr::EvalStatus EStatus;
EStatus.Diag = &Notes;
- EvalInfo Info(Ctx, EStatus,
- (IsConstantInitialization &&
- (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
- ? EvaluationMode::ConstantExpression
- : EvaluationMode::ConstantFold);
- Info.setEvaluatingDecl(VD, Value);
- Info.InConstantContext = IsConstantInitialization;
+ EvaluationMode EvalMode =
+ (IsConstantInitialization &&
+ (Ctx.getLangOpts().CPlusPlus || Ctx.getLangOpts().C23))
+ ? EvaluationMode::ConstantExpression
+ : EvaluationMode::ConstantFold;
+
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvalMode, EStatus);
+ S.InConstantContext = IsConstantInitialization;
+ return Ctx.getInterpContext().evaluateAsInitializer(S, VD, this, Value);
+ }
SourceLocation DeclLoc = VD->getLocation();
QualType DeclTy = VD->getType();
- if (Info.EnableNewConstInterp) {
- auto &InterpCtx = Ctx.getInterpContext();
- if (!InterpCtx.evaluateAsInitializer(Info, VD, this, Value))
- return false;
-
- return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
- ConstantExprKind::Normal);
- } else {
- LValue LVal;
- LVal.set(VD);
-
- {
- // C++23 [intro.execution]/p5
- // A full-expression is ... an init-declarator ([dcl.decl]) or a
- // mem-initializer.
- // So we need to make sure temporary objects are destroyed after having
- // evaluated the expression (per C++23 [class.temporary]/p4).
- //
- // FIXME: Otherwise this may break test/Modules/pr68702.cpp because the
- // serialization code calls ParmVarDecl::getDefaultArg() which strips the
- // outermost FullExpr, such as ExprWithCleanups.
- FullExpressionRAII Scope(Info);
- if (!EvaluateInPlace(Value, Info, LVal, this,
- /*AllowNonLiteralTypes=*/true) ||
- EStatus.HasSideEffects)
- return false;
- }
+ EvalInfo Info(Ctx, EStatus, EvalMode);
+ Info.setEvaluatingDecl(VD, Value);
+ Info.InConstantContext = IsConstantInitialization;
- // At this point, any lifetime-extended temporaries are completely
- // initialized.
- Info.performLifetimeExtension();
+ LValue LVal;
+ LVal.set(VD);
- if (!Info.discardCleanups())
- llvm_unreachable("Unhandled cleanup; missing full expression marker?");
+ {
+ // C++23 [intro.execution]/p5
+ // A full-expression is ... an init-declarator ([dcl.decl]) or a
+ // mem-initializer.
+ // So we need to make sure temporary objects are destroyed after having
+ // evaluated the expression (per C++23 [class.temporary]/p4).
+ //
+ // FIXME: Otherwise this may break test/Modules/pr68702.cpp because the
+ // serialization code calls ParmVarDecl::getDefaultArg() which strips the
+ // outermost FullExpr, such as ExprWithCleanups.
+ FullExpressionRAII Scope(Info);
+ if (!EvaluateInPlace(Value, Info, LVal, this,
+ /*AllowNonLiteralTypes=*/true) ||
+ EStatus.HasSideEffects)
+ return false;
}
+ // At this point, any lifetime-extended temporaries are completely
+ // initialized.
+ Info.performLifetimeExtension();
+
+ if (!Info.discardCleanups())
+ llvm_unreachable("Unhandled cleanup; missing full expression marker?");
return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
ConstantExprKind::Normal) &&
@@ -21871,20 +21840,23 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
return Name;
});
+ const ASTContext &Ctx = FD->getASTContext();
Expr::EvalStatus Status;
Status.Diag = &Diags;
- EvalInfo Info(FD->getASTContext(), Status,
- EvaluationMode::ConstantExpression);
- Info.InConstantContext = true;
- Info.CheckingPotentialConstantExpression = true;
-
// The constexpr VM attempts to compile all methods to bytecode here.
- if (Info.EnableNewConstInterp) {
- Info.Ctx.getInterpContext().isPotentialConstantExpr(Info, FD);
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::ConstantExpression, Status);
+ S.InConstantContext = true;
+ S.CheckingPotentialConstantExpression = true;
+ FD->getASTContext().getInterpContext().isPotentialConstantExpr(S, FD);
return Diags.empty();
}
+ EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression);
+ Info.InConstantContext = true;
+ Info.CheckingPotentialConstantExpression = true;
+
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : nullptr;
@@ -21920,20 +21892,24 @@ bool Expr::isPotentialConstantExprUnevaluated(Expr *E,
PartialDiagnosticAt> &Diags) {
assert(!E->isValueDependent() &&
"Expression evaluator can't be called on a dependent expression.");
-
+ const ASTContext &Ctx = FD->getASTContext();
Expr::EvalStatus Status;
Status.Diag = &Diags;
- EvalInfo Info(FD->getASTContext(), Status,
- EvaluationMode::ConstantExpressionUnevaluated);
- Info.InConstantContext = true;
- Info.CheckingPotentialConstantExpression = true;
-
- if (Info.EnableNewConstInterp) {
- Info.Ctx.getInterpContext().isPotentialConstantExprUnevaluated(Info, E, FD);
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::ConstantExpressionUnevaluated,
+ Status);
+ S.InConstantContext = true;
+ S.CheckingPotentialConstantExpression = true;
+ FD->getASTContext().getInterpContext().isPotentialConstantExprUnevaluated(
+ S, E, FD);
return Diags.empty();
}
+ EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpressionUnevaluated);
+ Info.InConstantContext = true;
+ Info.CheckingPotentialConstantExpression = true;
+
// Fabricate a call stack frame to give the arguments a plausible cover story.
CallStackFrame Frame(Info, SourceLocation(), FD, /*This=*/nullptr,
/*CallExpr=*/nullptr, CallRef());
@@ -21949,9 +21925,12 @@ std::optional<uint64_t> Expr::tryEvaluateObjectSize(const ASTContext &Ctx,
return std::nullopt;
Expr::EvalStatus Status;
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::ConstantFold, Status);
+ return Ctx.getInterpContext().tryEvaluateObjectSize(S, this, Type);
+ }
+
EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold);
- if (Info.EnableNewConstInterp)
- return Info.Ctx.getInterpContext().tryEvaluateObjectSize(Info, this, Type);
return tryEvaluateBuiltinObjectSize(this, Type, Info);
}
@@ -22008,15 +21987,16 @@ EvaluateBuiltinStrLen(const Expr *E, EvalInfo &Info,
std::optional<std::string> Expr::tryEvaluateString(ASTContext &Ctx) const {
Expr::EvalStatus Status;
- EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold);
std::string StringResult;
- if (Info.EnableNewConstInterp) {
- if (!Info.Ctx.getInterpContext().evaluateString(Info, this, StringResult))
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::ConstantFold, Status);
+ if (!Ctx.getInterpContext().evaluateString(S, this, StringResult))
return std::nullopt;
return StringResult;
}
+ EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold);
if (EvaluateBuiltinStrLen(this, Info, &StringResult))
return StringResult;
return std::nullopt;
@@ -22028,13 +22008,15 @@ static bool EvaluateCharRangeAsStringImpl(const Expr *, T &Result,
const Expr *PtrExpression,
ASTContext &Ctx,
Expr::EvalResult &Status) {
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::ConstantExpression, Status);
+ S.InConstantContext = true;
+ return Ctx.getInterpContext().evaluateCharRange(S, SizeExpression,
+ PtrExpression, Result);
+ }
+
EvalInfo Info(Ctx, Status, EvaluationMode::ConstantExpression);
Info.InConstantContext = true;
-
- if (Info.EnableNewConstInterp)
- return Info.Ctx.getInterpContext().evaluateCharRange(Info, SizeExpression,
- PtrExpression, Result);
-
LValue String;
FullExpressionRAII Scope(Info);
APSInt SizeValue;
@@ -22096,10 +22078,13 @@ bool Expr::EvaluateCharRangeAsString(APValue &Result,
std::optional<uint64_t> Expr::tryEvaluateStrLen(const ASTContext &Ctx) const {
Expr::EvalStatus Status;
- EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold);
- if (Info.EnableNewConstInterp)
- return Info.Ctx.getInterpContext().evaluateStrlen(Info, this);
+ if (Ctx.getLangOpts().EnableNewConstInterp) {
+ interp::EvalSettings S(EvaluationMode::ConstantFold, Status);
+ return Ctx.getInterpContext().evaluateStrlen(S, this);
+ }
+
+ EvalInfo Info(Ctx, Status, EvaluationMode::ConstantFold);
return EvaluateBuiltinStrLen(this, Info);
}
diff --git a/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp b/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
index d5312c09d0bd8..20a1f805c0d4b 100644
--- a/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
+++ b/clang/test/AST/ByteCode/codegen-constexpr-unknown.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -fcxx-exceptions -o - %s | FileCheck %s --check-prefix=CHECK
-// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -fcxx-exceptions -o - %s -fexperimental-new-constant-interpreter -DINTERP | FileCheck %s --check-prefix=CHECK,INTERP
+// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -fcxx-exceptions -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux -emit-llvm -fcxx-exceptions -o - %s -fexperimental-new-constant-interpreter -DINTERP | FileCheck %s
/// CodeGenFunction::ConstantFoldsToSimpleInteger() for the if condition
/// needs to succeed and return true.
@@ -7,8 +7,6 @@
/// variable to the topmost scope, otherwise we will pick the call scope
/// of to_address and de-allocate the local variable at the end of the
/// to_address call.
-/// FIXME: This is not currently correct since we still mark p as
-/// constexpr-unknown and then reject it when comparing.
extern void abort2();
constexpr const int* to_address(const int *a) {
return a;
@@ -23,7 +21,7 @@ void rightscope() {
// CHECK-NEXT: entry:
// CHECK-NEXT: %p = alloca i32
// CHECK-NEXT: store i32 0, ptr %p
-// INTERP-NEXT: call noundef ptr @_Z10to_addressPKi(ptr noundef %p)
+// CHECK-NEXT: ret void
/// In the if expression below, the read from s.i should fail.
diff --git a/clang/test/AST/ByteCode/unused-variables.cpp b/clang/test/AST/ByteCode/unused-variables.cpp
new file mode 100644
index 0000000000000..95ea6ee852415
--- /dev/null
+++ b/clang/test/AST/ByteCode/unused-variables.cpp
@@ -0,0 +1,109 @@
+// RUN: %clang_cc1 -std=c++23 -Wunused -fexperimental-new-constant-interpreter -verify=both,expected %s
+// RUN: %clang_cc1 -std=c++23 -Wunused -verify=both,ref %s
+
+
+// both-no-diagnostics
+namespace BaseUninitializedField {
+ struct __optional_storage_base {
+ int value;
+ template <class _UArg> constexpr __optional_storage_base(_UArg) {}
+ };
+
+ struct optional : __optional_storage_base {
+ template <class _Up>
+ constexpr optional(_Up &&) : __optional_storage_base(0) {}
+ };
+ int main_x;
+ void test() { optional opt{main_x}; }
+}
+
+
+namespace BaseInvalidLValue {
+ int *addressof(int &);
+ struct in_place_t {
+ } in_place;
+ template <class> struct __optional_storage_base {
+ int *__value_;
+ template <class _UArg>
+ constexpr __optional_storage_base(in_place_t, _UArg &&__uarg) {
+ int &__trans_tmp_1(__uarg);
+ int &__val = __trans_tmp_1;
+ int &__r(__val);
+ __value_ = addressof(__r);
+ }
+ };
+ template <class _Tp>
+ struct __optional_copy_base : __optional_storage_base<_Tp> {
+ using __optional_storage_base<_Tp>::__optional_storage_base;
+ };
+ template <class _Tp> struct __optional_move_base : __optional_copy_base<_Tp> {
+ using __optional_copy_base<_Tp>::__optional_copy_base;
+ };
+ template <class _Tp>
+ struct __optional_copy_assign_base : __optional_move_base<_Tp> {
+ using __optional_move_base<_Tp>::__optional_move_base;
+ };
+ template <class _Tp>
+ struct __optional_move_assign_base : __optional_copy_assign_base<_Tp> {
+ using __optional_copy_assign_base<_Tp>::__optional_copy_assign_base;
+ };
+ struct optional : __optional_move_assign_base<int> {
+ template <class _Up>
+ constexpr optional(_Up &&__v) : __optional_move_assign_base(in_place, __v) {}
+ };
+ int test() {
+ int x;
+ /// With -Wunused, we will call EvaluateAsInitializer() on the variable here and if that
+ /// succeeds, it will be reported unused. It should NOT succeed because the __value_ is an
+ /// invalid lvalue.
+ optional opt{x};
+ return 0;
+ }
+}
+
+namespace NonConstantInitChecksLValue {
+ template <class _Tp, class>
+ concept __weakly_equality_comparable_with = requires(_Tp __t) { __t; };
+ template <class _Ip>
+ concept input_or_output_iterator = requires(_Ip __i) { __i; };
+ template <class _Sp, class _Ip>
+ concept sentinel_for = __weakly_equality_comparable_with<_Sp, _Ip>;
+
+ template <input_or_output_iterator _Iter, sentinel_for<_Iter> _Sent = _Iter>
+ struct subrange {
+ _Iter __begin_;
+ _Sent __end_;
+ constexpr subrange(auto, _Sent __sent) : __begin_(), __end_(__sent) {}
+ };
+ struct forward_iterator {
+ int *it_;
+ };
+ void test() {
+ using Range = subrange<forward_iterator>;
+ int buffer[]{};
+ /// The EvaluateAsInitializer() call needs to check the LValue and not just the lvalue fields.
+ Range input(forward_iterator{}, {buffer});
+ }
+}
+
+namespace PtrInBase {
+ int *addressof(int &);
+ template <class> struct __optional_storage_base {
+ int *__value_;
+ template <class _UArg>
+ constexpr __optional_storage_base(_UArg &&__uarg) {
+ int &__trans_tmp_1(__uarg);
+ int &__val = __trans_tmp_1;
+ int &__r(__val);
+ __value_ = addressof(__r);
+ }
+ };
+ struct optional : __optional_storage_base<int> {
+ template <class _Up>
+ constexpr optional(_Up &&__v) : __optional_storage_base(__v) {}
+ };
+ void test() {
+ int x;
+ optional opt{x};
+ }
+}
diff --git a/clang/test/CodeGenCXX/global-init.cpp b/clang/test/CodeGenCXX/global-init.cpp
index 52039a5208223..f10f1be4ce95d 100644
--- a/clang/test/CodeGenCXX/global-init.cpp
+++ b/clang/test/CodeGenCXX/global-init.cpp
@@ -6,6 +6,14 @@
// RUN: | FileCheck -check-prefix CHECK-NOBUILTIN %s
// RUN: %clang_cc1 %std_cxx17- -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - | FileCheck %s
+// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK,PRE17
+// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm %s -o - -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK-NOEXC,PRE17
+// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm -mframe-pointer=non-leaf %s -o - -fexperimental-new-constant-interpreter \
+// RUN: | FileCheck -check-prefix CHECK-FP %s
+// RUN: %clang_cc1 %std_cxx98-14 -triple=x86_64-apple-darwin10 -emit-llvm %s -o - -fno-builtin -fexperimental-new-constant-interpreter \
+// RUN: | FileCheck -check-prefix CHECK-NOBUILTIN %s
+// RUN: %clang_cc1 %std_cxx17- -triple=x86_64-apple-darwin10 -emit-llvm -fexceptions %s -o - -fexperimental-new-constant-interpreter | FileCheck %s
+
struct A {
A();
~A();
diff --git a/clang/test/SemaCXX/PR19955.cpp b/clang/test/SemaCXX/PR19955.cpp
index cbbe2fe9af164..6fa22ab846374 100644
--- a/clang/test/SemaCXX/PR19955.cpp
+++ b/clang/test/SemaCXX/PR19955.cpp
@@ -1,5 +1,8 @@
// RUN: %clang_cc1 -triple i686-win32 -verify -std=c++11 %s
// RUN: %clang_cc1 -triple i686-mingw32 -verify -std=c++11 %s
+// RUN: %clang_cc1 -triple i686-win32 -verify -std=c++11 %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -triple i686-mingw32 -verify -std=c++11 %s -fexperimental-new-constant-interpreter
+
extern int __attribute__((dllimport)) var;
constexpr int *varp = &var; // expected-error {{must be initialized by a constant expression}}
More information about the cfe-commits
mailing list