blob: e92d686c724cc8898e70eb10077532f6f117655f [file] [log] [blame]
//===----- 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->getZExtSize();
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 (F.Decl->isUnnamedBitField()) {
// Nothing do 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 Pointer &Ptr) const {
assert(Source);
assert(empty());
// 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();
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);
}
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;
}
case Invalid:
OS << "Invalid\n";
break;
case Valid:
OS << "Valid\n";
break;
}
}
} // namespace interp
} // namespace clang