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).
197 lines
6.0 KiB
C++
197 lines
6.0 KiB
C++
//===----- 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
|