blob: 0873b870fc8b288fc90945590e24af65c2822da0 [file] [edit]
//===----- 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 "InterpState.h"
#include "Pointer.h"
#include "Record.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include <iterator>
namespace clang {
namespace interp {
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,
PtrView BasePtr, const Record *R);
static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc,
PtrView BasePtr) {
const Descriptor *BaseDesc = BasePtr.getFieldDesc();
assert(BaseDesc->isArray());
size_t NumElems = BaseDesc->getNumElems();
if (NumElems == 0)
return true;
bool Result = true;
if (BaseDesc->isPrimitiveArray()) {
if (BasePtr.allElementsInitialized())
return true;
DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField());
return false;
}
const Descriptor *ElemDesc = BaseDesc->ElemDesc;
if (ElemDesc->isRecord()) {
const Record *R = ElemDesc->ElemRecord;
for (size_t I = 0; I != NumElems; ++I) {
PtrView ElemPtr = BasePtr.atIndex(I).narrow();
Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R);
}
} else {
assert(ElemDesc->isArray());
for (size_t I = 0; I != NumElems; ++I) {
PtrView ElemPtr = BasePtr.atIndex(I).narrow();
Result &= CheckArrayInitialized(S, Loc, ElemPtr);
}
}
return Result;
}
static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
PtrView BasePtr, const Record *R) {
assert(R);
bool Result = true;
// Check all fields of this record are initialized.
for (const Record::Field &F : R->fields()) {
PtrView FieldPtr = BasePtr.atField(F.Offset);
// Don't check inactive union members.
if (R->isUnion() && !FieldPtr.isActive())
continue;
QualType FieldType = F.Decl->getType();
const Descriptor *FieldDesc = FieldPtr.getFieldDesc();
if (FieldDesc->isRecord()) {
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 (FieldDesc->isArray()) {
Result &= CheckArrayInitialized(S, Loc, FieldPtr);
} else if (!FieldPtr.isInitialized()) {
DiagnoseUninitializedSubobject(S, Loc, F.Decl);
Result = false;
}
}
// Check Fields in all bases
for (auto [I, B] : llvm::enumerate(R->bases())) {
PtrView P = BasePtr.atField(B.Offset);
if (!P.isInitialized()) {
const Descriptor *Desc = BasePtr.getDeclDesc();
if (const auto *CD = dyn_cast_if_present<CXXRecordDecl>(R->getDecl())) {
const auto &BS = *std::next(CD->bases_begin(), I);
SourceLocation TypeBeginLoc = BS.getBaseTypeLoc();
S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base)
<< B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc());
} else {
S.FFDiag(Desc->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());
if (Ptr.isZero())
return true;
if (!Ptr.isBlockPointer())
return true;
// We can't inspect dead pointers at all. Return true here so we can
// diagnose them later.
if (!Ptr.isLive())
return true;
SourceLocation InitLoc;
if (const auto *D = dyn_cast<const Decl *>(Source))
InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc();
else if (const auto *E = dyn_cast<const Expr *>(Source))
InitLoc = E->getExprLoc();
if (const Record *R = Ptr.getRecord())
return CheckFieldsInitialized(S, InitLoc, Ptr.view(), R);
if (isa_and_nonnull<ConstantArrayType>(Ptr.getType()->getAsArrayTypeUnsafe()))
return CheckArrayInitialized(S, InitLoc, Ptr.view());
return true;
}
static bool isOrHasPtr(const Descriptor *D) {
if ((D->isPrimitive() || D->isPrimitiveArray()) && D->getPrimType() == PT_Ptr)
return true;
if (D->ElemRecord)
return D->ElemRecord->hasPtrField();
return false;
}
static void collectBlocks(PtrView Ptr, llvm::SetVector<const Block *> &Blocks) {
auto isUsefulPtr = [](const Pointer &P) -> bool {
return P.isLive() && P.isBlockPointer() && !P.isZero() && !P.isDummy() &&
P.isDereferencable() && !P.isUnknownSizeArray() && !P.isOnePastEnd();
};
if (!Ptr.isLive() || Ptr.isZero() || Ptr.isDummy() ||
Ptr.isUnknownSizeArray() || Ptr.isOnePastEnd())
return;
Blocks.insert(Ptr.Pointee);
const Descriptor *Desc = Ptr.getFieldDesc();
if (!Desc)
return;
if (const Record *R = Desc->ElemRecord) {
if (!R->hasPtrField())
return;
for (const Record::Base &B : R->bases()) {
if (!B.R->hasPtrField())
continue;
PtrView BasePtr = Ptr.atField(B.Offset);
collectBlocks(BasePtr, Blocks);
}
for (const Record::Field &F : R->fields()) {
if (!isOrHasPtr(F.Desc))
continue;
PtrView FieldPtr = Ptr.atField(F.Offset);
collectBlocks(FieldPtr, Blocks);
}
return;
}
if (Desc->isPrimitive() && Desc->getPrimType() == PT_Ptr) {
Pointer Pointee = Ptr.deref<Pointer>();
if (isUsefulPtr(Pointee) && !Blocks.contains(Pointee.block()))
collectBlocks(Pointee.view(), Blocks);
return;
}
if (Desc->isPrimitiveArray() && Desc->getPrimType() == PT_Ptr) {
for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
Pointer ElemPointee = Ptr.elem<Pointer>(I);
if (isUsefulPtr(ElemPointee) && !Blocks.contains(ElemPointee.block()))
collectBlocks(ElemPointee.view(), Blocks);
}
return;
}
if (Desc->isCompositeArray() && isOrHasPtr(Desc->ElemDesc)) {
for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
PtrView ElemPtr = Ptr.atIndex(I).narrow();
collectBlocks(ElemPtr, Blocks);
}
}
}
bool EvaluationResult::checkDynamicAllocations(InterpState &S,
const Context &Ctx,
const Pointer &Ptr,
SourceInfo Info) {
if (!Ptr.isBlockPointer())
return true;
// Collect all blocks that this pointer (transitively) points to and
// return false if any of them is a dynamic block.
llvm::SetVector<const Block *> Blocks;
collectBlocks(Ptr.view(), Blocks);
for (const Block *B : Blocks) {
if (B->isDynamic()) {
assert(B->getDescriptor());
assert(B->getDescriptor()->asExpr());
bool IsSubobj = !Ptr.isRoot() || Ptr.isArrayElement();
S.FFDiag(Info, diag::note_constexpr_dynamic_alloc)
<< Ptr.getType()->isReferenceType() << IsSubobj;
S.Note(B->getDescriptor()->asExpr()->getExprLoc(),
diag::note_constexpr_dynamic_alloc_here);
return false;
}
}
return true;
}
} // namespace interp
} // namespace clang