229 lines · cpp
1//===----- EvaluationResult.cpp - Result class for the VM ------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "EvaluationResult.h"10#include "InterpState.h"11#include "Pointer.h"12#include "Record.h"13#include "llvm/ADT/STLExtras.h"14#include "llvm/ADT/SetVector.h"15#include <iterator>16 17namespace clang {18namespace interp {19 20static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc,21 const FieldDecl *SubObjDecl) {22 assert(SubObjDecl && "Subobject declaration does not exist");23 S.FFDiag(Loc, diag::note_constexpr_uninitialized)24 << /*(name)*/ 1 << SubObjDecl;25 S.Note(SubObjDecl->getLocation(),26 diag::note_constexpr_subobject_declared_here);27}28 29static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,30 const Pointer &BasePtr, const Record *R);31 32static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc,33 const Pointer &BasePtr,34 const ConstantArrayType *CAT) {35 size_t NumElems = CAT->getZExtSize();36 37 if (NumElems == 0)38 return true;39 40 bool Result = true;41 QualType ElemType = CAT->getElementType();42 43 if (ElemType->isRecordType()) {44 const Record *R = BasePtr.getElemRecord();45 for (size_t I = 0; I != NumElems; ++I) {46 Pointer ElemPtr = BasePtr.atIndex(I).narrow();47 Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R);48 }49 } else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {50 for (size_t I = 0; I != NumElems; ++I) {51 Pointer ElemPtr = BasePtr.atIndex(I).narrow();52 Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT);53 }54 } else {55 // Primitive arrays.56 if (S.getContext().canClassify(ElemType)) {57 if (BasePtr.allElementsInitialized()) {58 return true;59 } else {60 DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField());61 return false;62 }63 }64 65 for (size_t I = 0; I != NumElems; ++I) {66 if (!BasePtr.isElementInitialized(I)) {67 DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField());68 Result = false;69 }70 }71 }72 73 return Result;74}75 76static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,77 const Pointer &BasePtr, const Record *R) {78 assert(R);79 bool Result = true;80 // Check all fields of this record are initialized.81 for (const Record::Field &F : R->fields()) {82 Pointer FieldPtr = BasePtr.atField(F.Offset);83 QualType FieldType = F.Decl->getType();84 85 // Don't check inactive union members.86 if (R->isUnion() && !FieldPtr.isActive())87 continue;88 89 if (FieldType->isRecordType()) {90 Result &= CheckFieldsInitialized(S, Loc, FieldPtr, FieldPtr.getRecord());91 } else if (FieldType->isIncompleteArrayType()) {92 // Nothing to do here.93 } else if (F.Decl->isUnnamedBitField()) {94 // Nothing do do here.95 } else if (FieldType->isArrayType()) {96 const auto *CAT =97 cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());98 Result &= CheckArrayInitialized(S, Loc, FieldPtr, CAT);99 } else if (!FieldPtr.isInitialized()) {100 DiagnoseUninitializedSubobject(S, Loc, F.Decl);101 Result = false;102 }103 }104 105 // Check Fields in all bases106 for (auto [I, B] : llvm::enumerate(R->bases())) {107 Pointer P = BasePtr.atField(B.Offset);108 if (!P.isInitialized()) {109 const Descriptor *Desc = BasePtr.getDeclDesc();110 if (const auto *CD = dyn_cast_if_present<CXXRecordDecl>(R->getDecl())) {111 const auto &BS = *std::next(CD->bases_begin(), I);112 SourceLocation TypeBeginLoc = BS.getBaseTypeLoc();113 S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base)114 << B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc());115 } else {116 S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base)117 << B.Desc->getType();118 }119 return false;120 }121 Result &= CheckFieldsInitialized(S, Loc, P, B.R);122 }123 124 // TODO: Virtual bases125 126 return Result;127}128 129bool EvaluationResult::checkFullyInitialized(InterpState &S,130 const Pointer &Ptr) const {131 assert(Source);132 assert(empty());133 134 if (Ptr.isZero())135 return true;136 if (!Ptr.isBlockPointer())137 return true;138 139 // We can't inspect dead pointers at all. Return true here so we can140 // diagnose them later.141 if (!Ptr.isLive())142 return true;143 144 SourceLocation InitLoc;145 if (const auto *D = dyn_cast<const Decl *>(Source))146 InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc();147 else if (const auto *E = dyn_cast<const Expr *>(Source))148 InitLoc = E->getExprLoc();149 150 if (const Record *R = Ptr.getRecord())151 return CheckFieldsInitialized(S, InitLoc, Ptr, R);152 153 if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>(154 Ptr.getType()->getAsArrayTypeUnsafe()))155 return CheckArrayInitialized(S, InitLoc, Ptr, CAT);156 157 return true;158}159 160static void collectBlocks(const Pointer &Ptr,161 llvm::SetVector<const Block *> &Blocks) {162 auto isUsefulPtr = [](const Pointer &P) -> bool {163 return P.isLive() && P.isBlockPointer() && !P.isZero() && !P.isDummy() &&164 P.isDereferencable() && !P.isUnknownSizeArray() && !P.isOnePastEnd();165 };166 167 if (!isUsefulPtr(Ptr))168 return;169 170 Blocks.insert(Ptr.block());171 172 const Descriptor *Desc = Ptr.getFieldDesc();173 if (!Desc)174 return;175 176 if (const Record *R = Desc->ElemRecord) {177 for (const Record::Field &F : R->fields()) {178 const Pointer &FieldPtr = Ptr.atField(F.Offset);179 assert(FieldPtr.block() == Ptr.block());180 collectBlocks(FieldPtr, Blocks);181 }182 } else if (Desc->isPrimitive() && Desc->getPrimType() == PT_Ptr) {183 const Pointer &Pointee = Ptr.deref<Pointer>();184 if (isUsefulPtr(Pointee) && !Blocks.contains(Pointee.block()))185 collectBlocks(Pointee, Blocks);186 187 } else if (Desc->isPrimitiveArray() && Desc->getPrimType() == PT_Ptr) {188 for (unsigned I = 0; I != Desc->getNumElems(); ++I) {189 const Pointer &ElemPointee = Ptr.elem<Pointer>(I);190 if (isUsefulPtr(ElemPointee) && !Blocks.contains(ElemPointee.block()))191 collectBlocks(ElemPointee, Blocks);192 }193 } else if (Desc->isCompositeArray()) {194 for (unsigned I = 0; I != Desc->getNumElems(); ++I) {195 const Pointer &ElemPtr = Ptr.atIndex(I).narrow();196 collectBlocks(ElemPtr, Blocks);197 }198 }199}200 201bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,202 const Pointer &Ptr,203 const SourceInfo &Info) {204 // Collect all blocks that this pointer (transitively) points to and205 // return false if any of them is a dynamic block.206 llvm::SetVector<const Block *> Blocks;207 208 collectBlocks(Ptr, Blocks);209 210 for (const Block *B : Blocks) {211 if (B->isDynamic()) {212 assert(B->getDescriptor());213 assert(B->getDescriptor()->asExpr());214 215 bool IsSubobj = !Ptr.isRoot() || Ptr.isArrayElement();216 S.FFDiag(Info, diag::note_constexpr_dynamic_alloc)217 << Ptr.getType()->isReferenceType() << IsSubobj;218 S.Note(B->getDescriptor()->asExpr()->getExprLoc(),219 diag::note_constexpr_dynamic_alloc_here);220 return false;221 }222 }223 224 return true;225}226 227} // namespace interp228} // namespace clang229