[clang] 4e7cf1b - [clang][Interp] Add an EvaluationResult class (#71315)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 19 01:08:08 PST 2024
Author: Timm Baeder
Date: 2024-01-19T10:08:03+01:00
New Revision: 4e7cf1b1ed3824d1298d4232922f54c6295eab50
URL: https://github.com/llvm/llvm-project/commit/4e7cf1b1ed3824d1298d4232922f54c6295eab50
DIFF: https://github.com/llvm/llvm-project/commit/4e7cf1b1ed3824d1298d4232922f54c6295eab50.diff
LOG: [clang][Interp] Add an EvaluationResult class (#71315)
Add an `EvaluationResult` class. This contains the result either as a
`Pointer` or as a `APValue`.
This way, we can inspect the result of the evaluation and diagnose
problems with it (e.g. uninitialized fields in global initializers or
pointers pointing to things they shouldn't point to).
Added:
clang/lib/AST/Interp/EvaluationResult.cpp
clang/lib/AST/Interp/EvaluationResult.h
Modified:
clang/lib/AST/CMakeLists.txt
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/Interp/ByteCodeEmitter.cpp
clang/lib/AST/Interp/ByteCodeEmitter.h
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/ByteCodeStmtGen.cpp
clang/lib/AST/Interp/Context.cpp
clang/lib/AST/Interp/Context.h
clang/lib/AST/Interp/EvalEmitter.cpp
clang/lib/AST/Interp/EvalEmitter.h
clang/lib/AST/Interp/Interp.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/Opcodes.td
clang/lib/AST/Interp/Pointer.cpp
clang/lib/AST/Interp/Pointer.h
clang/test/AST/Interp/records.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt
index fe3f8c485ec1c5..ebcb3952198a5b 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 0884988c48b4e8..f1d07d022b2584 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15439,11 +15439,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;
@@ -15671,6 +15673,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)
@@ -15746,10 +15755,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);
@@ -15779,8 +15794,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 045263447cbc91..fd2a92d9d3f91e 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;
@@ -120,10 +119,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;
}
@@ -183,12 +178,6 @@ int32_t ByteCodeEmitter::getOffset(LabelTy Label) {
return 0ull;
}
-bool ByteCodeEmitter::bail(const SourceLocation &Loc) {
- if (!BailLocation)
- BailLocation = 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.
template <typename T>
diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.h b/clang/lib/AST/Interp/ByteCodeEmitter.h
index 5520f8c3006106..03de286582c916 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) {}
@@ -49,11 +48,6 @@ class ByteCodeEmitter {
virtual bool visitExpr(const Expr *E) = 0;
virtual bool visitDecl(const VarDecl *E) = 0;
- /// Bails out if a given node cannot be compiled.
- bool bail(const Stmt *S) { return bail(S->getBeginLoc()); }
- bool bail(const Decl *D) { return bail(D->getBeginLoc()); }
- bool bail(const SourceLocation &Loc);
-
/// Emits jumps.
bool jumpTrue(const LabelTy &Label);
bool jumpFalse(const LabelTy &Label);
@@ -81,8 +75,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 82ef743e6a7819..06518281132685 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -398,7 +398,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
}
if (!LT || !RT || !T)
- return this->bail(BO);
+ return false;
// Pointer arithmetic special case.
if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) {
@@ -478,7 +478,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
case BO_LAnd:
llvm_unreachable("Already handled earlier");
default:
- return this->bail(BO);
+ return false;
}
llvm_unreachable("Unhandled binary op");
@@ -531,7 +531,7 @@ bool ByteCodeExprGen<Emitter>::VisitPointerArithBinOp(const BinaryOperator *E) {
else if (Op == BO_Sub)
return this->emitSubOffset(OffsetType, E);
- return this->bail(E);
+ return false;
}
template <class Emitter>
@@ -2389,7 +2389,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>
@@ -2409,7 +2411,7 @@ bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
std::optional<unsigned> GlobalIndex = P.createGlobal(VD, Init);
if (!GlobalIndex)
- return this->bail(VD);
+ return false;
assert(Init);
{
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index 48005ce05724b5..df4cb736299cb6 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -191,10 +191,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/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 38067be73e2543..a2d8c4e13010c6 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -282,7 +282,7 @@ bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) {
default: {
if (auto *Exp = dyn_cast<Expr>(S))
return this->discard(Exp);
- return this->bail(S);
+ return false;
}
}
}
diff --git a/clang/lib/AST/Interp/Context.cpp b/clang/lib/AST/Interp/Context.cpp
index 17abb71635839c..75a300bcbace1a 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(); }
@@ -234,12 +278,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 7649caef224281..ab83a8d1322467 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 0ff0bde8fd17e8..a60f893de8bda7 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,12 +79,6 @@ Scope::Local EvalEmitter::createLocal(Descriptor *D) {
return {Off, D};
}
-bool EvalEmitter::bail(const SourceLocation &Loc) {
- if (!BailLocation)
- BailLocation = Loc;
- return false;
-}
-
bool EvalEmitter::jumpTrue(const LabelTy &Label) {
if (isActive()) {
if (S.Stk.pop<bool>())
@@ -116,125 +112,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;
}
-bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { 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) {
+ 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;
- }
-
- // Complex types.
- if (const auto *CT = Ty->getAs<ComplexType>()) {
- QualType ElemTy = CT->getElementType();
- std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
- assert(ElemT);
-
- if (ElemTy->isIntegerType()) {
- INT_TYPE_SWITCH(*ElemT, {
- auto V1 = Ptr.atIndex(0).deref<T>();
- auto V2 = Ptr.atIndex(1).deref<T>();
- Result = APValue(V1.toAPSInt(), V2.toAPSInt());
- return true;
- });
- } else if (ElemTy->isFloatingType()) {
- Result = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),
- Ptr.atIndex(1).deref<Floating>().getAPFloat());
- return true;
- }
- return false;
- }
- 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 5a9be18c34a03b..deb2ebc4e61fa0 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,
@@ -51,10 +54,6 @@ class EvalEmitter : public SourceMapper {
virtual bool visitExpr(const Expr *E) = 0;
virtual bool visitDecl(const VarDecl *VD) = 0;
- bool bail(const Stmt *S) { return bail(S->getBeginLoc()); }
- bool bail(const Decl *D) { return bail(D->getBeginLoc()); }
- bool bail(const SourceLocation &Loc);
-
/// Emits jumps.
bool jumpTrue(const LabelTy &Label);
bool jumpFalse(const LabelTy &Label);
@@ -86,7 +85,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 +99,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 00000000000000..a14dc87f1dfde6
--- /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 00000000000000..2b9fc16f1a0abc
--- /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
diff erent 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 ac03837408a4d3..807b860f3565d3 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -498,98 +498,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 5321f9617feff2..65c54ed9c89b61 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -111,9 +111,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);
@@ -1061,8 +1058,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>
@@ -1863,11 +1864,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 6e0f1c939460d0..24747b6b98c163 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;
def GetGlobalUnchecked : AccessOpcode;
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index e979b99b0fdd0a..5af1d6d52e93e8 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -231,33 +231,143 @@ 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;
+ }
+ // Complex types.
+ if (const auto *CT = Ty->getAs<ComplexType>()) {
+ QualType ElemTy = CT->getElementType();
+ std::optional<PrimType> ElemT = Ctx.classify(ElemTy);
+ assert(ElemT);
+
+ if (ElemTy->isIntegerType()) {
+ INT_TYPE_SWITCH(*ElemT, {
+ auto V1 = Ptr.atIndex(0).deref<T>();
+ auto V2 = Ptr.atIndex(1).deref<T>();
+ R = APValue(V1.toAPSInt(), V2.toAPSInt());
+ return true;
+ });
+ } else if (ElemTy->isFloatingType()) {
+ R = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(),
+ Ptr.atIndex(1).deref<Floating>().getAPFloat());
+ return true;
+ }
+ return false;
+ }
+
+ 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 a8f6e62fa76d35..8ccaff41ded8da 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 800dc6f910be6b..c6aac7938a2971 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