[clang] [clang][Interp] Add an EvaluationResult class (PR #71315)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 27 05:24:42 PST 2023
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/71315 at github.com>
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/71315
>From 047249032d70e486380e8790915a8edeb70add56 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Mon, 27 Nov 2023 12:10:48 +0100
Subject: [PATCH 1/2] [clang][Interp][NFC] Remove unused include
---
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index f45e8624a7741a5..f7f8e6c73d84e21 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -15,7 +15,6 @@
#include "Function.h"
#include "PrimType.h"
#include "Program.h"
-#include "State.h"
using namespace clang;
using namespace clang::interp;
>From 63542819d018c6cfec64af759b79af15fb2eb1e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 31 Oct 2023 14:57:51 +0100
Subject: [PATCH 2/2] EvaluationResult
---
clang/lib/AST/CMakeLists.txt | 1 +
clang/lib/AST/ExprConstant.cpp | 23 ++-
clang/lib/AST/Interp/ByteCodeEmitter.cpp | 13 +-
clang/lib/AST/Interp/ByteCodeEmitter.h | 5 +-
clang/lib/AST/Interp/ByteCodeExprGen.cpp | 4 +-
clang/lib/AST/Interp/ByteCodeExprGen.h | 4 -
clang/lib/AST/Interp/Context.cpp | 106 ++++++++----
clang/lib/AST/Interp/Context.h | 3 +
clang/lib/AST/Interp/EvalEmitter.cpp | 153 +++++------------
clang/lib/AST/Interp/EvalEmitter.h | 11 +-
clang/lib/AST/Interp/EvaluationResult.cpp | 196 ++++++++++++++++++++++
clang/lib/AST/Interp/EvaluationResult.h | 111 ++++++++++++
clang/lib/AST/Interp/Interp.cpp | 92 ----------
clang/lib/AST/Interp/Interp.h | 16 +-
clang/lib/AST/Interp/Opcodes.td | 2 -
clang/lib/AST/Interp/Pointer.cpp | 132 ++++++++++++---
clang/lib/AST/Interp/Pointer.h | 3 +-
clang/test/AST/Interp/records.cpp | 1 -
18 files changed, 574 insertions(+), 302 deletions(-)
create mode 100644 clang/lib/AST/Interp/EvaluationResult.cpp
create mode 100644 clang/lib/AST/Interp/EvaluationResult.h
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index fe3f8c485ec1c56..ebcb3952198a5b5 100644
--- a/clang/lib/AST/CMakeLists.txt
+++ b/clang/lib/AST/CMakeLists.txt
@@ -75,6 +75,7 @@ add_clang_library(clangAST
Interp/Function.cpp
Interp/InterpBuiltin.cpp
Interp/Floating.cpp
+ Interp/EvaluationResult.cpp
Interp/Interp.cpp
Interp/InterpBlock.cpp
Interp/InterpFrame.cpp
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 16697e5f076a8f8..6555ed884b9effd 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15405,11 +15405,13 @@ static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) {
if (Info.EnableNewConstInterp) {
if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result))
return false;
- } else {
- if (!::Evaluate(Result, Info, E))
- return false;
+ return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result,
+ ConstantExprKind::Normal);
}
+ if (!::Evaluate(Result, Info, E))
+ return false;
+
// Implicit lvalue-to-rvalue cast.
if (E->isGLValue()) {
LValue LV;
@@ -15637,6 +15639,13 @@ bool Expr::EvaluateAsConstantExpr(EvalResult &Result, const ASTContext &Ctx,
EvalInfo Info(Ctx, Result, EM);
Info.InConstantContext = true;
+ if (Info.EnableNewConstInterp) {
+ if (!Info.Ctx.getInterpContext().evaluate(Info, this, Result.Val))
+ return false;
+ return CheckConstantExpression(Info, getExprLoc(),
+ getStorageType(Ctx, this), Result.Val, Kind);
+ }
+
// The type of the object we're initializing is 'const T' for a class NTTP.
QualType T = getType();
if (Kind == ConstantExprKind::ClassTemplateArgument)
@@ -15712,10 +15721,16 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
Info.setEvaluatingDecl(VD, Value);
Info.InConstantContext = IsConstantInitialization;
+ SourceLocation DeclLoc = VD->getLocation();
+ QualType DeclTy = VD->getType();
+
if (Info.EnableNewConstInterp) {
auto &InterpCtx = const_cast<ASTContext &>(Ctx).getInterpContext();
if (!InterpCtx.evaluateAsInitializer(Info, VD, Value))
return false;
+
+ return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
+ ConstantExprKind::Normal);
} else {
LValue LVal;
LVal.set(VD);
@@ -15733,8 +15748,6 @@ bool Expr::EvaluateAsInitializer(APValue &Value, const ASTContext &Ctx,
llvm_unreachable("Unhandled cleanup; missing full expression marker?");
}
- SourceLocation DeclLoc = VD->getLocation();
- QualType DeclTy = VD->getType();
return CheckConstantExpression(Info, DeclLoc, DeclTy, Value,
ConstantExprKind::Normal) &&
CheckMemoryLeaks(Info);
diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
index 89b7708c0c2a12f..dedd51db95e0d6c 100644
--- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
@@ -20,8 +20,7 @@
using namespace clang;
using namespace clang::interp;
-Expected<Function *>
-ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
+Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
// Set up argument indices.
unsigned ParamOffset = 0;
SmallVector<PrimType, 8> ParamTypes;
@@ -115,10 +114,6 @@ ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
// Compile the function body.
if (!IsEligibleForCompilation || !visitFunc(FuncDecl)) {
- // Return a dummy function if compilation failed.
- if (BailLocation)
- return llvm::make_error<ByteCodeGenError>(*BailLocation);
-
Func->setIsFullyCompiled(true);
return Func;
}
@@ -178,11 +173,7 @@ int32_t ByteCodeEmitter::getOffset(LabelTy Label) {
return 0ull;
}
-bool ByteCodeEmitter::bail(const SourceLocation &Loc) {
- if (!BailLocation)
- BailLocation = Loc;
- return false;
-}
+bool ByteCodeEmitter::bail(const SourceLocation &Loc) { return false; }
/// Helper to write bytecode and bail out if 32-bit offsets become invalid.
/// Pointers will be automatically marshalled as 32-bit IDs.
diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.h b/clang/lib/AST/Interp/ByteCodeEmitter.h
index 5520f8c3006106f..4ef36d784dc4d59 100644
--- a/clang/lib/AST/Interp/ByteCodeEmitter.h
+++ b/clang/lib/AST/Interp/ByteCodeEmitter.h
@@ -17,7 +17,6 @@
#include "PrimType.h"
#include "Program.h"
#include "Source.h"
-#include "llvm/Support/Error.h"
namespace clang {
namespace interp {
@@ -32,7 +31,7 @@ class ByteCodeEmitter {
public:
/// Compiles the function into the module.
- llvm::Expected<Function *> compileFunc(const FunctionDecl *FuncDecl);
+ Function *compileFunc(const FunctionDecl *FuncDecl);
protected:
ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {}
@@ -81,8 +80,6 @@ class ByteCodeEmitter {
LabelTy NextLabel = 0;
/// Offset of the next local variable.
unsigned NextLocalOffset = 0;
- /// Location of a failure.
- std::optional<SourceLocation> BailLocation;
/// Label information for linker.
llvm::DenseMap<LabelTy, unsigned> LabelOffsets;
/// Location of label relocations.
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index f7f8e6c73d84e21..3f5ac1081252dce 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -2141,7 +2141,9 @@ bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) {
// Return the value
if (VarT)
return this->emitRet(*VarT, VD);
- return this->emitRetValue(VD);
+
+ // Return non-primitive values as pointers here.
+ return this->emitRet(PT_Ptr, VD);
}
template <class Emitter>
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index bc1d5d11a115135..61ed794f6ed4660 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -190,10 +190,6 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
if (!visitInitializer(Init))
return false;
- if ((Init->getType()->isArrayType() || Init->getType()->isRecordType()) &&
- !this->emitCheckGlobalCtor(Init))
- return false;
-
return this->emitPopPtr(Init);
}
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index cb96e56fb5e1ad8..b1b0a5d101b3767 100644
--- a/clang/lib/AST/Interp/Context.cpp
+++ b/clang/lib/AST/Interp/Context.cpp
@@ -30,18 +30,8 @@ Context::~Context() {}
bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
assert(Stk.empty());
Function *Func = P->getFunction(FD);
- if (!Func || !Func->hasBody()) {
- if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) {
- Func = *R;
- } else {
- handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) {
- Parent.FFDiag(Err.getRange().getBegin(),
- diag::err_experimental_clang_interp_failed)
- << Err.getRange();
- });
- return false;
- }
- }
+ if (!Func || !Func->hasBody())
+ Func = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD);
APValue DummyResult;
if (!Run(Parent, Func, DummyResult)) {
@@ -54,36 +44,90 @@ bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
- if (Check(Parent, C.interpretExpr(E))) {
- assert(Stk.empty());
-#ifndef NDEBUG
- // Make sure we don't rely on some value being still alive in
- // InterpStack memory.
+
+ auto Res = C.interpretExpr(E);
+
+ if (Res.isInvalid()) {
Stk.clear();
+ return false;
+ }
+
+ assert(Stk.empty());
+#ifndef NDEBUG
+ // Make sure we don't rely on some value being still alive in
+ // InterpStack memory.
+ Stk.clear();
#endif
- return true;
+
+ // Implicit lvalue-to-rvalue conversion.
+ if (E->isGLValue()) {
+ std::optional<APValue> RValueResult = Res.toRValue();
+ if (!RValueResult) {
+ return false;
+ }
+ Result = *RValueResult;
+ } else {
+ Result = Res.toAPValue();
}
+ return true;
+}
+
+bool Context::evaluate(State &Parent, const Expr *E, APValue &Result) {
+ assert(Stk.empty());
+ ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
+
+ auto Res = C.interpretExpr(E);
+ if (Res.isInvalid()) {
+ Stk.clear();
+ return false;
+ }
+
+ assert(Stk.empty());
+#ifndef NDEBUG
+ // Make sure we don't rely on some value being still alive in
+ // InterpStack memory.
Stk.clear();
- return false;
+#endif
+ Result = Res.toAPValue();
+ return true;
}
bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
APValue &Result) {
assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
- if (Check(Parent, C.interpretDecl(VD))) {
- assert(Stk.empty());
-#ifndef NDEBUG
- // Make sure we don't rely on some value being still alive in
- // InterpStack memory.
+
+ auto Res = C.interpretDecl(VD);
+ if (Res.isInvalid()) {
Stk.clear();
-#endif
- return true;
+ return false;
}
+ assert(Stk.empty());
+#ifndef NDEBUG
+ // Make sure we don't rely on some value being still alive in
+ // InterpStack memory.
Stk.clear();
- return false;
+#endif
+
+ // Ensure global variables are fully initialized.
+ if (shouldBeGloballyIndexed(VD) && !Res.isInvalid() &&
+ (VD->getType()->isRecordType() || VD->getType()->isArrayType())) {
+ assert(Res.isLValue());
+
+ if (!Res.checkFullyInitialized(C.getState()))
+ return false;
+
+ // lvalue-to-rvalue conversion.
+ std::optional<APValue> RValueResult = Res.toRValue();
+ if (!RValueResult)
+ return false;
+ Result = *RValueResult;
+
+ } else
+ Result = Res.toAPValue();
+ return true;
}
const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); }
@@ -231,12 +275,8 @@ const Function *Context::getOrCreateFunction(const FunctionDecl *FD) {
return Func;
if (!Func || WasNotDefined) {
- if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD))
- Func = *R;
- else {
- llvm::consumeError(R.takeError());
- return nullptr;
- }
+ if (auto F = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD))
+ Func = F;
}
return Func;
diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h
index 7649caef2242816..ab83a8d13224670 100644
--- a/clang/lib/AST/Interp/Context.h
+++ b/clang/lib/AST/Interp/Context.h
@@ -51,6 +51,9 @@ class Context final {
/// Evaluates a toplevel expression as an rvalue.
bool evaluateAsRValue(State &Parent, const Expr *E, APValue &Result);
+ /// Like evaluateAsRvalue(), but does no implicit lvalue-to-rvalue conversion.
+ bool evaluate(State &Parent, const Expr *E, APValue &Result);
+
/// Evaluates a toplevel initializer.
bool evaluateAsInitializer(State &Parent, const VarDecl *VD, APValue &Result);
diff --git a/clang/lib/AST/Interp/EvalEmitter.cpp b/clang/lib/AST/Interp/EvalEmitter.cpp
index 9bc42057c5f5782..458fd7a21754672 100644
--- a/clang/lib/AST/Interp/EvalEmitter.cpp
+++ b/clang/lib/AST/Interp/EvalEmitter.cpp
@@ -19,7 +19,7 @@ using namespace clang::interp;
EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
InterpStack &Stk, APValue &Result)
- : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) {
+ : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {
// Create a dummy frame for the interpreter which does not have locals.
S.Current =
new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
@@ -33,20 +33,22 @@ EvalEmitter::~EvalEmitter() {
}
}
-llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
- if (this->visitExpr(E))
- return true;
- if (BailLocation)
- return llvm::make_error<ByteCodeGenError>(*BailLocation);
- return false;
+EvaluationResult EvalEmitter::interpretExpr(const Expr *E) {
+ EvalResult.setSource(E);
+
+ if (!this->visitExpr(E))
+ EvalResult.setInvalid();
+
+ return std::move(this->EvalResult);
}
-llvm::Expected<bool> EvalEmitter::interpretDecl(const VarDecl *VD) {
- if (this->visitDecl(VD))
- return true;
- if (BailLocation)
- return llvm::make_error<ByteCodeGenError>(*BailLocation);
- return false;
+EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD) {
+ EvalResult.setSource(VD);
+
+ if (!this->visitDecl(VD))
+ EvalResult.setInvalid();
+
+ return std::move(this->EvalResult);
}
void EvalEmitter::emitLabel(LabelTy Label) {
@@ -77,11 +79,7 @@ Scope::Local EvalEmitter::createLocal(Descriptor *D) {
return {Off, D};
}
-bool EvalEmitter::bail(const SourceLocation &Loc) {
- if (!BailLocation)
- BailLocation = Loc;
- return false;
-}
+bool EvalEmitter::bail(const SourceLocation &Loc) { return false; }
bool EvalEmitter::jumpTrue(const LabelTy &Label) {
if (isActive()) {
@@ -116,104 +114,37 @@ template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) {
if (!isActive())
return true;
using T = typename PrimConv<OpType>::T;
- return ReturnValue<T>(S.Stk.pop<T>(), Result);
+ EvalResult.setValue(S.Stk.pop<T>().toAPValue());
+ return true;
+}
+
+template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+ EvalResult.setPointer(S.Stk.pop<Pointer>());
+ return true;
+}
+template <> bool EvalEmitter::emitRet<PT_FnPtr>(const SourceInfo &Info) {
+ if (!isActive())
+ return true;
+ EvalResult.setFunctionPointer(S.Stk.pop<FunctionPointer>());
+ return true;
}
-bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; }
+bool EvalEmitter::emitRetVoid(const SourceInfo &Info) {
+ EvalResult.setValid();
+ return true;
+}
bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
- // Method to recursively traverse composites.
- std::function<bool(QualType, const Pointer &, APValue &)> Composite;
- Composite = [this, &Composite](QualType Ty, const Pointer &Ptr, APValue &R) {
- if (const auto *AT = Ty->getAs<AtomicType>())
- Ty = AT->getValueType();
-
- if (const auto *RT = Ty->getAs<RecordType>()) {
- const auto *Record = Ptr.getRecord();
- assert(Record && "Missing record descriptor");
-
- bool Ok = true;
- if (RT->getDecl()->isUnion()) {
- const FieldDecl *ActiveField = nullptr;
- APValue Value;
- for (const auto &F : Record->fields()) {
- const Pointer &FP = Ptr.atField(F.Offset);
- QualType FieldTy = F.Decl->getType();
- if (FP.isActive()) {
- if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
- TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value));
- } else {
- Ok &= Composite(FieldTy, FP, Value);
- }
- break;
- }
- }
- R = APValue(ActiveField, Value);
- } else {
- unsigned NF = Record->getNumFields();
- unsigned NB = Record->getNumBases();
- unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
-
- R = APValue(APValue::UninitStruct(), NB, NF);
-
- for (unsigned I = 0; I < NF; ++I) {
- const Record::Field *FD = Record->getField(I);
- QualType FieldTy = FD->Decl->getType();
- const Pointer &FP = Ptr.atField(FD->Offset);
- APValue &Value = R.getStructField(I);
-
- if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
- TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value));
- } else {
- Ok &= Composite(FieldTy, FP, Value);
- }
- }
-
- for (unsigned I = 0; I < NB; ++I) {
- const Record::Base *BD = Record->getBase(I);
- QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);
- const Pointer &BP = Ptr.atField(BD->Offset);
- Ok &= Composite(BaseTy, BP, R.getStructBase(I));
- }
-
- for (unsigned I = 0; I < NV; ++I) {
- const Record::Base *VD = Record->getVirtualBase(I);
- QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);
- const Pointer &VP = Ptr.atField(VD->Offset);
- Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
- }
- }
- return Ok;
- }
-
- if (Ty->isIncompleteArrayType()) {
- R = APValue(APValue::UninitArray(), 0, 0);
- return true;
- }
-
- if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
- const size_t NumElems = Ptr.getNumElems();
- QualType ElemTy = AT->getElementType();
- R = APValue(APValue::UninitArray{}, NumElems, NumElems);
-
- bool Ok = true;
- for (unsigned I = 0; I < NumElems; ++I) {
- APValue &Slot = R.getArrayInitializedElt(I);
- const Pointer &EP = Ptr.atIndex(I);
- if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
- TYPE_SWITCH(*T, Ok &= ReturnValue<T>(EP.deref<T>(), Slot));
- } else {
- Ok &= Composite(ElemTy, EP.narrow(), Slot);
- }
- }
- return Ok;
- }
- llvm_unreachable("invalid value to return");
- };
-
- // Return the composite type.
const auto &Ptr = S.Stk.pop<Pointer>();
- return Composite(Ptr.getType(), Ptr, Result);
+ if (std::optional<APValue> APV = Ptr.toRValue(S.getCtx())) {
+ EvalResult.setValue(*APV);
+ return true;
+ }
+
+ EvalResult.setInvalid();
+ return false;
}
bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
diff --git a/clang/lib/AST/Interp/EvalEmitter.h b/clang/lib/AST/Interp/EvalEmitter.h
index 5a9be18c34a03bc..e2516056ff5d2ca 100644
--- a/clang/lib/AST/Interp/EvalEmitter.h
+++ b/clang/lib/AST/Interp/EvalEmitter.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_INTERP_EVALEMITTER_H
#define LLVM_CLANG_AST_INTERP_EVALEMITTER_H
+#include "EvaluationResult.h"
#include "InterpState.h"
#include "PrimType.h"
#include "Source.h"
@@ -33,8 +34,10 @@ class EvalEmitter : public SourceMapper {
using AddrTy = uintptr_t;
using Local = Scope::Local;
- llvm::Expected<bool> interpretExpr(const Expr *E);
- llvm::Expected<bool> interpretDecl(const VarDecl *VD);
+ EvaluationResult interpretExpr(const Expr *E);
+ EvaluationResult interpretDecl(const VarDecl *VD);
+
+ InterpState &getState() { return S; }
protected:
EvalEmitter(Context &Ctx, Program &P, State &Parent, InterpStack &Stk,
@@ -86,7 +89,7 @@ class EvalEmitter : public SourceMapper {
/// Callee evaluation state.
InterpState S;
/// Location to write the result to.
- APValue &Result;
+ EvaluationResult EvalResult;
/// Temporaries which require storage.
llvm::DenseMap<unsigned, std::unique_ptr<char[]>> Locals;
@@ -100,8 +103,6 @@ class EvalEmitter : public SourceMapper {
// The emitter always tracks the current instruction and sets OpPC to a token
// value which is mapped to the location of the opcode being evaluated.
CodePtr OpPC;
- /// Location of a failure.
- std::optional<SourceLocation> BailLocation;
/// Location of the current instruction.
SourceInfo CurrentSource;
diff --git a/clang/lib/AST/Interp/EvaluationResult.cpp b/clang/lib/AST/Interp/EvaluationResult.cpp
new file mode 100644
index 000000000000000..a14dc87f1dfde69
--- /dev/null
+++ b/clang/lib/AST/Interp/EvaluationResult.cpp
@@ -0,0 +1,196 @@
+//===----- EvaluationResult.cpp - Result class for the VM ------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "EvaluationResult.h"
+#include "Context.h"
+#include "InterpState.h"
+#include "Record.h"
+#include "clang/AST/ExprCXX.h"
+
+namespace clang {
+namespace interp {
+
+APValue EvaluationResult::toAPValue() const {
+ assert(!empty());
+ switch (Kind) {
+ case LValue:
+ // Either a pointer or a function pointer.
+ if (const auto *P = std::get_if<Pointer>(&Value))
+ return P->toAPValue();
+ else if (const auto *FP = std::get_if<FunctionPointer>(&Value))
+ return FP->toAPValue();
+ else
+ llvm_unreachable("Unhandled LValue type");
+ break;
+ case RValue:
+ return std::get<APValue>(Value);
+ case Valid:
+ return APValue();
+ default:
+ llvm_unreachable("Unhandled result kind?");
+ }
+}
+
+std::optional<APValue> EvaluationResult::toRValue() const {
+ if (Kind == RValue)
+ return toAPValue();
+
+ assert(Kind == LValue);
+
+ // We have a pointer and want an RValue.
+ if (const auto *P = std::get_if<Pointer>(&Value))
+ return P->toRValue(*Ctx);
+ else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
+ return FP->toAPValue();
+ llvm_unreachable("Unhandled lvalue kind");
+}
+
+static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc,
+ const FieldDecl *SubObjDecl) {
+ assert(SubObjDecl && "Subobject declaration does not exist");
+ S.FFDiag(Loc, diag::note_constexpr_uninitialized)
+ << /*(name)*/ 1 << SubObjDecl;
+ S.Note(SubObjDecl->getLocation(),
+ diag::note_constexpr_subobject_declared_here);
+}
+
+static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
+ const Pointer &BasePtr, const Record *R);
+
+static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc,
+ const Pointer &BasePtr,
+ const ConstantArrayType *CAT) {
+ bool Result = true;
+ size_t NumElems = CAT->getSize().getZExtValue();
+ QualType ElemType = CAT->getElementType();
+
+ if (ElemType->isRecordType()) {
+ const Record *R = BasePtr.getElemRecord();
+ for (size_t I = 0; I != NumElems; ++I) {
+ Pointer ElemPtr = BasePtr.atIndex(I).narrow();
+ Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R);
+ }
+ } else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
+ for (size_t I = 0; I != NumElems; ++I) {
+ Pointer ElemPtr = BasePtr.atIndex(I).narrow();
+ Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT);
+ }
+ } else {
+ for (size_t I = 0; I != NumElems; ++I) {
+ if (!BasePtr.atIndex(I).isInitialized()) {
+ DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField());
+ Result = false;
+ }
+ }
+ }
+
+ return Result;
+}
+
+static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
+ const Pointer &BasePtr, const Record *R) {
+ assert(R);
+ bool Result = true;
+ // Check all fields of this record are initialized.
+ for (const Record::Field &F : R->fields()) {
+ Pointer FieldPtr = BasePtr.atField(F.Offset);
+ QualType FieldType = F.Decl->getType();
+
+ if (FieldType->isRecordType()) {
+ Result &= CheckFieldsInitialized(S, Loc, FieldPtr, FieldPtr.getRecord());
+ } else if (FieldType->isIncompleteArrayType()) {
+ // Nothing to do here.
+ } else if (FieldType->isArrayType()) {
+ const auto *CAT =
+ cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
+ Result &= CheckArrayInitialized(S, Loc, FieldPtr, CAT);
+ } else if (!FieldPtr.isInitialized()) {
+ DiagnoseUninitializedSubobject(S, Loc, F.Decl);
+ Result = false;
+ }
+ }
+
+ // Check Fields in all bases
+ for (const Record::Base &B : R->bases()) {
+ Pointer P = BasePtr.atField(B.Offset);
+ if (!P.isInitialized()) {
+ S.FFDiag(BasePtr.getDeclDesc()->asDecl()->getLocation(),
+ diag::note_constexpr_uninitialized_base)
+ << B.Desc->getType();
+ return false;
+ }
+ Result &= CheckFieldsInitialized(S, Loc, P, B.R);
+ }
+
+ // TODO: Virtual bases
+
+ return Result;
+}
+
+bool EvaluationResult::checkFullyInitialized(InterpState &S) const {
+ assert(Source);
+ assert(isLValue());
+
+ // Our Source must be a VarDecl.
+ const Decl *SourceDecl = Source.dyn_cast<const Decl *>();
+ assert(SourceDecl);
+ const auto *VD = cast<VarDecl>(SourceDecl);
+ assert(VD->getType()->isRecordType() || VD->getType()->isArrayType());
+ SourceLocation InitLoc = VD->getAnyInitializer()->getExprLoc();
+
+ const Pointer &Ptr = *std::get_if<Pointer>(&Value);
+ assert(!Ptr.isZero());
+
+ if (const Record *R = Ptr.getRecord())
+ return CheckFieldsInitialized(S, InitLoc, Ptr, R);
+ const auto *CAT =
+ cast<ConstantArrayType>(Ptr.getType()->getAsArrayTypeUnsafe());
+ return CheckArrayInitialized(S, InitLoc, Ptr, CAT);
+
+ return true;
+}
+
+void EvaluationResult::dump() const {
+ assert(Ctx);
+ auto &OS = llvm::errs();
+ const ASTContext &ASTCtx = Ctx->getASTContext();
+
+ switch (Kind) {
+ case Empty:
+ OS << "Empty\n";
+ break;
+ case RValue:
+ OS << "RValue: ";
+ std::get<APValue>(Value).dump(OS, ASTCtx);
+ break;
+ case LValue: {
+ assert(Source);
+ QualType SourceType;
+ if (const auto *D = Source.dyn_cast<const Decl *>()) {
+ if (const auto *VD = dyn_cast<ValueDecl>(D))
+ SourceType = VD->getType();
+ } else if (const auto *E = Source.dyn_cast<const Expr *>()) {
+ SourceType = E->getType();
+ }
+
+ OS << "LValue: ";
+ if (const auto *P = std::get_if<Pointer>(&Value))
+ P->toAPValue().printPretty(OS, ASTCtx, SourceType);
+ else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
+ FP->toAPValue().printPretty(OS, ASTCtx, SourceType);
+ OS << "\n";
+ break;
+ }
+
+ default:
+ llvm_unreachable("Can't print that.");
+ }
+}
+
+} // namespace interp
+} // namespace clang
diff --git a/clang/lib/AST/Interp/EvaluationResult.h b/clang/lib/AST/Interp/EvaluationResult.h
new file mode 100644
index 000000000000000..2b9fc16f1a0abc7
--- /dev/null
+++ b/clang/lib/AST/Interp/EvaluationResult.h
@@ -0,0 +1,111 @@
+//===------ EvaluationResult.h - Result class for the VM -------*- 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_EVALUATION_RESULT_H
+#define LLVM_CLANG_AST_INTERP_EVALUATION_RESULT_H
+
+#include "FunctionPointer.h"
+#include "Pointer.h"
+#include "clang/AST/APValue.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include <optional>
+#include <variant>
+
+namespace clang {
+namespace interp {
+class EvalEmitter;
+class Context;
+
+/// Defines the result of an evaluation.
+///
+/// The result might be in different forms--one of the pointer types,
+/// an APValue, or nothing.
+///
+/// We use this class to inspect and diagnose the result, as well as
+/// convert it to the requested form.
+class EvaluationResult final {
+public:
+ enum ResultKind {
+ Empty, // Initial state.
+ LValue, // Result is an lvalue/pointer.
+ RValue, // Result is an rvalue.
+ Invalid, // Result is invalid.
+ Valid, // Result is valid and empty.
+ };
+
+ using DeclTy = llvm::PointerUnion<const Decl *, const Expr *>;
+
+private:
+ const Context *Ctx = nullptr;
+ std::variant<std::monostate, Pointer, FunctionPointer, APValue> Value;
+ ResultKind Kind = Empty;
+ DeclTy Source = nullptr; // Currently only needed for dump().
+
+ EvaluationResult(ResultKind Kind) : Kind(Kind) {
+ // Leave everything empty. Can be used as an
+ // error marker or for void return values.
+ assert(Kind == Valid || Kind == Invalid);
+ }
+
+ void setSource(DeclTy D) { Source = D; }
+
+ void setValue(const APValue &V) {
+ assert(empty());
+ assert(!V.isLValue());
+ Value = std::move(V);
+ Kind = RValue;
+ }
+ void setPointer(const Pointer P) {
+ assert(empty());
+ Value = P;
+ Kind = LValue;
+ }
+ void setFunctionPointer(const FunctionPointer &P) {
+ assert(empty());
+ Value = P;
+ Kind = LValue;
+ }
+ void setInvalid() {
+ assert(empty());
+ Kind = Invalid;
+ }
+ void setValid() {
+ assert(empty());
+ Kind = Valid;
+ }
+
+public:
+ EvaluationResult(const Context *Ctx) : Ctx(Ctx) {}
+
+ bool empty() const { return Kind == Empty; }
+ bool isInvalid() const { return Kind == Invalid; }
+ bool isLValue() const { return Kind == LValue; }
+ bool isRValue() const { return Kind == RValue; }
+
+ /// Returns an APValue for the evaluation result. The returned
+ /// APValue might be an LValue or RValue.
+ APValue toAPValue() const;
+
+ /// If the result is an LValue, convert that to an RValue
+ /// and return it. This may fail, e.g. if the result is an
+ /// LValue and we can't read from it.
+ std::optional<APValue> toRValue() const;
+
+ bool checkFullyInitialized(InterpState &S) const;
+
+ /// Dump to stderr.
+ void dump() const;
+
+ friend class EvalEmitter;
+};
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 13b77e9a87725c7..b25354ce2b506e0 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -425,98 +425,6 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
return false;
}
-static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI,
- const FieldDecl *SubObjDecl) {
- assert(SubObjDecl && "Subobject declaration does not exist");
- S.FFDiag(SI, diag::note_constexpr_uninitialized)
- << /*(name)*/ 1 << SubObjDecl;
- S.Note(SubObjDecl->getLocation(),
- diag::note_constexpr_subobject_declared_here);
-}
-
-static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
- const Pointer &BasePtr, const Record *R);
-
-static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC,
- const Pointer &BasePtr,
- const ConstantArrayType *CAT) {
- bool Result = true;
- size_t NumElems = CAT->getSize().getZExtValue();
- QualType ElemType = CAT->getElementType();
-
- if (ElemType->isRecordType()) {
- const Record *R = BasePtr.getElemRecord();
- for (size_t I = 0; I != NumElems; ++I) {
- Pointer ElemPtr = BasePtr.atIndex(I).narrow();
- Result &= CheckFieldsInitialized(S, OpPC, ElemPtr, R);
- }
- } else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
- for (size_t I = 0; I != NumElems; ++I) {
- Pointer ElemPtr = BasePtr.atIndex(I).narrow();
- Result &= CheckArrayInitialized(S, OpPC, ElemPtr, ElemCAT);
- }
- } else {
- for (size_t I = 0; I != NumElems; ++I) {
- if (!BasePtr.atIndex(I).isInitialized()) {
- DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC),
- BasePtr.getField());
- Result = false;
- }
- }
- }
-
- return Result;
-}
-
-static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
- const Pointer &BasePtr, const Record *R) {
- assert(R);
- bool Result = true;
- // Check all fields of this record are initialized.
- for (const Record::Field &F : R->fields()) {
- Pointer FieldPtr = BasePtr.atField(F.Offset);
- QualType FieldType = F.Decl->getType();
-
- if (FieldType->isRecordType()) {
- Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord());
- } else if (FieldType->isIncompleteArrayType()) {
- // Nothing to do here.
- } else if (FieldType->isArrayType()) {
- const auto *CAT =
- cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
- Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT);
- } else if (!FieldPtr.isInitialized()) {
- DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), F.Decl);
- Result = false;
- }
- }
-
- // Check Fields in all bases
- for (const Record::Base &B : R->bases()) {
- Pointer P = BasePtr.atField(B.Offset);
- if (!P.isInitialized()) {
- S.FFDiag(BasePtr.getDeclDesc()->asDecl()->getLocation(),
- diag::note_constexpr_uninitialized_base)
- << B.Desc->getType();
- return false;
- }
- Result &= CheckFieldsInitialized(S, OpPC, P, B.R);
- }
-
- // TODO: Virtual bases
-
- return Result;
-}
-
-bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This) {
- assert(!This.isZero());
- if (const Record *R = This.getRecord())
- return CheckFieldsInitialized(S, OpPC, This, R);
- const auto *CAT =
- cast<ConstantArrayType>(This.getType()->getAsArrayTypeUnsafe());
- return CheckArrayInitialized(S, OpPC, This, CAT);
-}
-
bool CheckPotentialReinterpretCast(InterpState &S, CodePtr OpPC,
const Pointer &Ptr) {
if (!S.inConstantContext())
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 4f7778bdd2ff333..adf4ab1390fb4c7 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -109,9 +109,6 @@ bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
/// Checks if a method is pure virtual.
bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);
-/// Checks that all fields are initialized after a constructor call.
-bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This);
-
/// Checks if reinterpret casts are legal in the current context.
bool CheckPotentialReinterpretCast(InterpState &S, CodePtr OpPC,
const Pointer &Ptr);
@@ -1048,8 +1045,12 @@ inline bool InitGlobalTempComp(InterpState &S, CodePtr OpPC,
const Pointer &P = S.Stk.peek<Pointer>();
APValue *Cached = Temp->getOrCreateValue(true);
- *Cached = P.toRValue(S.getCtx());
- return true;
+ if (std::optional<APValue> APV = P.toRValue(S.getCtx())) {
+ *Cached = *APV;
+ return true;
+ }
+
+ return false;
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
@@ -1847,11 +1848,6 @@ inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
return NarrowPtr(S, OpPC);
}
-inline bool CheckGlobalCtor(InterpState &S, CodePtr OpPC) {
- const Pointer &Obj = S.Stk.peek<Pointer>();
- return CheckCtorCall(S, OpPC, Obj);
-}
-
inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) {
if (Func->hasThisPointer()) {
size_t ThisOffset =
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 69068e87d5720ab..df01d5a29160192 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -375,8 +375,6 @@ def GetLocal : AccessOpcode { let HasCustomEval = 1; }
// [] -> [Pointer]
def SetLocal : AccessOpcode { let HasCustomEval = 1; }
-def CheckGlobalCtor : Opcode {}
-
// [] -> [Value]
def GetGlobal : AccessOpcode;
// [Value] -> []
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index e979b99b0fdd0a0..0fcac6c0fe69c8b 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -231,33 +231,121 @@ bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
}
-APValue Pointer::toRValue(const Context &Ctx) const {
- // Primitives.
- if (getFieldDesc()->isPrimitive()) {
- PrimType PT = *Ctx.classify(getType());
- TYPE_SWITCH(PT, return deref<T>().toAPValue());
- llvm_unreachable("Unhandled PrimType?");
- }
+std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
+ // Method to recursively traverse composites.
+ std::function<bool(QualType, const Pointer &, APValue &)> Composite;
+ Composite = [&Composite, &Ctx](QualType Ty, const Pointer &Ptr, APValue &R) {
+ if (const auto *AT = Ty->getAs<AtomicType>())
+ Ty = AT->getValueType();
+
+ // Invalid pointers.
+ if (Ptr.isDummy() || !Ptr.isLive() ||
+ (!Ptr.isUnknownSizeArray() && Ptr.isOnePastEnd()))
+ return false;
- APValue Result;
- // Records.
- if (getFieldDesc()->isRecord()) {
- const Record *R = getRecord();
- Result =
- APValue(APValue::UninitStruct(), R->getNumBases(), R->getNumFields());
-
- for (unsigned I = 0; I != R->getNumFields(); ++I) {
- const Pointer &FieldPtr = this->atField(R->getField(I)->Offset);
- Result.getStructField(I) = FieldPtr.toRValue(Ctx);
+ // Primitive values.
+ if (std::optional<PrimType> T = Ctx.classify(Ty)) {
+ if (T == PT_Ptr || T == PT_FnPtr) {
+ R = Ptr.toAPValue();
+ } else {
+ TYPE_SWITCH(*T, R = Ptr.deref<T>().toAPValue());
+ }
+ return true;
}
- for (unsigned I = 0; I != R->getNumBases(); ++I) {
- const Pointer &BasePtr = this->atField(R->getBase(I)->Offset);
- Result.getStructBase(I) = BasePtr.toRValue(Ctx);
+ if (const auto *RT = Ty->getAs<RecordType>()) {
+ const auto *Record = Ptr.getRecord();
+ assert(Record && "Missing record descriptor");
+
+ bool Ok = true;
+ if (RT->getDecl()->isUnion()) {
+ const FieldDecl *ActiveField = nullptr;
+ APValue Value;
+ for (const auto &F : Record->fields()) {
+ const Pointer &FP = Ptr.atField(F.Offset);
+ QualType FieldTy = F.Decl->getType();
+ if (FP.isActive()) {
+ if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
+ TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
+ } else {
+ Ok &= Composite(FieldTy, FP, Value);
+ }
+ break;
+ }
+ }
+ R = APValue(ActiveField, Value);
+ } else {
+ unsigned NF = Record->getNumFields();
+ unsigned NB = Record->getNumBases();
+ unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases();
+
+ R = APValue(APValue::UninitStruct(), NB, NF);
+
+ for (unsigned I = 0; I < NF; ++I) {
+ const Record::Field *FD = Record->getField(I);
+ QualType FieldTy = FD->Decl->getType();
+ const Pointer &FP = Ptr.atField(FD->Offset);
+ APValue &Value = R.getStructField(I);
+
+ if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
+ TYPE_SWITCH(*T, Value = FP.deref<T>().toAPValue());
+ } else {
+ Ok &= Composite(FieldTy, FP, Value);
+ }
+ }
+
+ for (unsigned I = 0; I < NB; ++I) {
+ const Record::Base *BD = Record->getBase(I);
+ QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl);
+ const Pointer &BP = Ptr.atField(BD->Offset);
+ Ok &= Composite(BaseTy, BP, R.getStructBase(I));
+ }
+
+ for (unsigned I = 0; I < NV; ++I) {
+ const Record::Base *VD = Record->getVirtualBase(I);
+ QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl);
+ const Pointer &VP = Ptr.atField(VD->Offset);
+ Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I));
+ }
+ }
+ return Ok;
}
- }
- // TODO: Arrays
+ if (Ty->isIncompleteArrayType()) {
+ R = APValue(APValue::UninitArray(), 0, 0);
+ return true;
+ }
+
+ if (const auto *AT = Ty->getAsArrayTypeUnsafe()) {
+ const size_t NumElems = Ptr.getNumElems();
+ QualType ElemTy = AT->getElementType();
+ R = APValue(APValue::UninitArray{}, NumElems, NumElems);
+
+ bool Ok = true;
+ for (unsigned I = 0; I < NumElems; ++I) {
+ APValue &Slot = R.getArrayInitializedElt(I);
+ const Pointer &EP = Ptr.atIndex(I);
+ if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
+ TYPE_SWITCH(*T, Slot = EP.deref<T>().toAPValue());
+ } else {
+ Ok &= Composite(ElemTy, EP.narrow(), Slot);
+ }
+ }
+ return Ok;
+ }
+ llvm_unreachable("invalid value to return");
+ };
+
+ if (isZero())
+ return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {}, false,
+ true);
+
+ if (isDummy() || !isLive())
+ return std::nullopt;
+ // Return the composite type.
+ APValue Result;
+ if (!Composite(getType(), *this, Result))
+ return std::nullopt;
return Result;
}
diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index a8f6e62fa76d356..8ccaff41ded8daf 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -98,7 +98,7 @@ class Pointer {
}
/// Converts the pointer to an APValue that is an rvalue.
- APValue toRValue(const Context &Ctx) const;
+ std::optional<APValue> toRValue(const Context &Ctx) const;
/// Offsets a pointer inside an array.
[[nodiscard]] Pointer atIndex(unsigned Idx) const {
@@ -379,6 +379,7 @@ class Pointer {
return *reinterpret_cast<T *>(Pointee->rawData() + Base +
sizeof(InitMapPtr));
+ assert(Offset + sizeof(T) <= Pointee->getDescriptor()->getAllocSize());
return *reinterpret_cast<T *>(Pointee->rawData() + Offset);
}
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index 280eaf34898ceca..dd99152c87ddf3b 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -170,7 +170,6 @@ class Bar { // expected-note {{definition of 'Bar' is not complete}} \
// ref-error {{has incomplete type 'const Bar'}}
};
constexpr Bar B; // expected-error {{must be initialized by a constant expression}} \
- // expected-error {{failed to evaluate an expression}} \
// ref-error {{must be initialized by a constant expression}}
constexpr Bar *pb = nullptr;
More information about the cfe-commits
mailing list