17a6dacacSDimitry Andric //===----- EvaluationResult.cpp - Result class for the VM ------*- C++ -*-===// 27a6dacacSDimitry Andric // 37a6dacacSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 47a6dacacSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 57a6dacacSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67a6dacacSDimitry Andric // 77a6dacacSDimitry Andric //===----------------------------------------------------------------------===// 87a6dacacSDimitry Andric 97a6dacacSDimitry Andric #include "EvaluationResult.h" 107a6dacacSDimitry Andric #include "InterpState.h" 117a6dacacSDimitry Andric #include "Record.h" 127a6dacacSDimitry Andric #include "clang/AST/ExprCXX.h" 13*0fca6ea1SDimitry Andric #include "llvm/ADT/SetVector.h" 147a6dacacSDimitry Andric 157a6dacacSDimitry Andric namespace clang { 167a6dacacSDimitry Andric namespace interp { 177a6dacacSDimitry Andric 187a6dacacSDimitry Andric APValue EvaluationResult::toAPValue() const { 197a6dacacSDimitry Andric assert(!empty()); 207a6dacacSDimitry Andric switch (Kind) { 217a6dacacSDimitry Andric case LValue: 227a6dacacSDimitry Andric // Either a pointer or a function pointer. 237a6dacacSDimitry Andric if (const auto *P = std::get_if<Pointer>(&Value)) 24*0fca6ea1SDimitry Andric return P->toAPValue(Ctx->getASTContext()); 257a6dacacSDimitry Andric else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) 26*0fca6ea1SDimitry Andric return FP->toAPValue(Ctx->getASTContext()); 277a6dacacSDimitry Andric else 287a6dacacSDimitry Andric llvm_unreachable("Unhandled LValue type"); 297a6dacacSDimitry Andric break; 307a6dacacSDimitry Andric case RValue: 317a6dacacSDimitry Andric return std::get<APValue>(Value); 327a6dacacSDimitry Andric case Valid: 337a6dacacSDimitry Andric return APValue(); 347a6dacacSDimitry Andric default: 357a6dacacSDimitry Andric llvm_unreachable("Unhandled result kind?"); 367a6dacacSDimitry Andric } 377a6dacacSDimitry Andric } 387a6dacacSDimitry Andric 397a6dacacSDimitry Andric std::optional<APValue> EvaluationResult::toRValue() const { 407a6dacacSDimitry Andric if (Kind == RValue) 417a6dacacSDimitry Andric return toAPValue(); 427a6dacacSDimitry Andric 437a6dacacSDimitry Andric assert(Kind == LValue); 447a6dacacSDimitry Andric 457a6dacacSDimitry Andric // We have a pointer and want an RValue. 467a6dacacSDimitry Andric if (const auto *P = std::get_if<Pointer>(&Value)) 47*0fca6ea1SDimitry Andric return P->toRValue(*Ctx, getSourceType()); 487a6dacacSDimitry Andric else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope 49*0fca6ea1SDimitry Andric return FP->toAPValue(Ctx->getASTContext()); 507a6dacacSDimitry Andric llvm_unreachable("Unhandled lvalue kind"); 517a6dacacSDimitry Andric } 527a6dacacSDimitry Andric 537a6dacacSDimitry Andric static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc, 547a6dacacSDimitry Andric const FieldDecl *SubObjDecl) { 557a6dacacSDimitry Andric assert(SubObjDecl && "Subobject declaration does not exist"); 567a6dacacSDimitry Andric S.FFDiag(Loc, diag::note_constexpr_uninitialized) 577a6dacacSDimitry Andric << /*(name)*/ 1 << SubObjDecl; 587a6dacacSDimitry Andric S.Note(SubObjDecl->getLocation(), 597a6dacacSDimitry Andric diag::note_constexpr_subobject_declared_here); 607a6dacacSDimitry Andric } 617a6dacacSDimitry Andric 627a6dacacSDimitry Andric static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, 637a6dacacSDimitry Andric const Pointer &BasePtr, const Record *R); 647a6dacacSDimitry Andric 657a6dacacSDimitry Andric static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, 667a6dacacSDimitry Andric const Pointer &BasePtr, 677a6dacacSDimitry Andric const ConstantArrayType *CAT) { 687a6dacacSDimitry Andric bool Result = true; 69*0fca6ea1SDimitry Andric size_t NumElems = CAT->getZExtSize(); 707a6dacacSDimitry Andric QualType ElemType = CAT->getElementType(); 717a6dacacSDimitry Andric 727a6dacacSDimitry Andric if (ElemType->isRecordType()) { 737a6dacacSDimitry Andric const Record *R = BasePtr.getElemRecord(); 747a6dacacSDimitry Andric for (size_t I = 0; I != NumElems; ++I) { 757a6dacacSDimitry Andric Pointer ElemPtr = BasePtr.atIndex(I).narrow(); 767a6dacacSDimitry Andric Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R); 777a6dacacSDimitry Andric } 787a6dacacSDimitry Andric } else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) { 797a6dacacSDimitry Andric for (size_t I = 0; I != NumElems; ++I) { 807a6dacacSDimitry Andric Pointer ElemPtr = BasePtr.atIndex(I).narrow(); 817a6dacacSDimitry Andric Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT); 827a6dacacSDimitry Andric } 837a6dacacSDimitry Andric } else { 847a6dacacSDimitry Andric for (size_t I = 0; I != NumElems; ++I) { 857a6dacacSDimitry Andric if (!BasePtr.atIndex(I).isInitialized()) { 867a6dacacSDimitry Andric DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField()); 877a6dacacSDimitry Andric Result = false; 887a6dacacSDimitry Andric } 897a6dacacSDimitry Andric } 907a6dacacSDimitry Andric } 917a6dacacSDimitry Andric 927a6dacacSDimitry Andric return Result; 937a6dacacSDimitry Andric } 947a6dacacSDimitry Andric 957a6dacacSDimitry Andric static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, 967a6dacacSDimitry Andric const Pointer &BasePtr, const Record *R) { 977a6dacacSDimitry Andric assert(R); 987a6dacacSDimitry Andric bool Result = true; 997a6dacacSDimitry Andric // Check all fields of this record are initialized. 1007a6dacacSDimitry Andric for (const Record::Field &F : R->fields()) { 1017a6dacacSDimitry Andric Pointer FieldPtr = BasePtr.atField(F.Offset); 1027a6dacacSDimitry Andric QualType FieldType = F.Decl->getType(); 1037a6dacacSDimitry Andric 104*0fca6ea1SDimitry Andric // Don't check inactive union members. 105*0fca6ea1SDimitry Andric if (R->isUnion() && !FieldPtr.isActive()) 106*0fca6ea1SDimitry Andric continue; 107*0fca6ea1SDimitry Andric 1087a6dacacSDimitry Andric if (FieldType->isRecordType()) { 1097a6dacacSDimitry Andric Result &= CheckFieldsInitialized(S, Loc, FieldPtr, FieldPtr.getRecord()); 1107a6dacacSDimitry Andric } else if (FieldType->isIncompleteArrayType()) { 1117a6dacacSDimitry Andric // Nothing to do here. 112*0fca6ea1SDimitry Andric } else if (F.Decl->isUnnamedBitField()) { 113*0fca6ea1SDimitry Andric // Nothing do do here. 1147a6dacacSDimitry Andric } else if (FieldType->isArrayType()) { 1157a6dacacSDimitry Andric const auto *CAT = 1167a6dacacSDimitry Andric cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe()); 1177a6dacacSDimitry Andric Result &= CheckArrayInitialized(S, Loc, FieldPtr, CAT); 1187a6dacacSDimitry Andric } else if (!FieldPtr.isInitialized()) { 1197a6dacacSDimitry Andric DiagnoseUninitializedSubobject(S, Loc, F.Decl); 1207a6dacacSDimitry Andric Result = false; 1217a6dacacSDimitry Andric } 1227a6dacacSDimitry Andric } 1237a6dacacSDimitry Andric 1247a6dacacSDimitry Andric // Check Fields in all bases 1257a6dacacSDimitry Andric for (const Record::Base &B : R->bases()) { 1267a6dacacSDimitry Andric Pointer P = BasePtr.atField(B.Offset); 1277a6dacacSDimitry Andric if (!P.isInitialized()) { 128*0fca6ea1SDimitry Andric const Descriptor *Desc = BasePtr.getDeclDesc(); 129*0fca6ea1SDimitry Andric if (Desc->asDecl()) 1307a6dacacSDimitry Andric S.FFDiag(BasePtr.getDeclDesc()->asDecl()->getLocation(), 1317a6dacacSDimitry Andric diag::note_constexpr_uninitialized_base) 1327a6dacacSDimitry Andric << B.Desc->getType(); 133*0fca6ea1SDimitry Andric else 134*0fca6ea1SDimitry Andric S.FFDiag(BasePtr.getDeclDesc()->asExpr()->getExprLoc(), 135*0fca6ea1SDimitry Andric diag::note_constexpr_uninitialized_base) 136*0fca6ea1SDimitry Andric << B.Desc->getType(); 137*0fca6ea1SDimitry Andric 1387a6dacacSDimitry Andric return false; 1397a6dacacSDimitry Andric } 1407a6dacacSDimitry Andric Result &= CheckFieldsInitialized(S, Loc, P, B.R); 1417a6dacacSDimitry Andric } 1427a6dacacSDimitry Andric 1437a6dacacSDimitry Andric // TODO: Virtual bases 1447a6dacacSDimitry Andric 1457a6dacacSDimitry Andric return Result; 1467a6dacacSDimitry Andric } 1477a6dacacSDimitry Andric 148*0fca6ea1SDimitry Andric bool EvaluationResult::checkFullyInitialized(InterpState &S, 149*0fca6ea1SDimitry Andric const Pointer &Ptr) const { 1507a6dacacSDimitry Andric assert(Source); 151*0fca6ea1SDimitry Andric assert(empty()); 1527a6dacacSDimitry Andric 153*0fca6ea1SDimitry Andric if (Ptr.isZero()) 154*0fca6ea1SDimitry Andric return true; 1557a6dacacSDimitry Andric 156*0fca6ea1SDimitry Andric // We can't inspect dead pointers at all. Return true here so we can 157*0fca6ea1SDimitry Andric // diagnose them later. 158*0fca6ea1SDimitry Andric if (!Ptr.isLive()) 159*0fca6ea1SDimitry Andric return true; 160*0fca6ea1SDimitry Andric 161*0fca6ea1SDimitry Andric SourceLocation InitLoc; 162*0fca6ea1SDimitry Andric if (const auto *D = Source.dyn_cast<const Decl *>()) 163*0fca6ea1SDimitry Andric InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc(); 164*0fca6ea1SDimitry Andric else if (const auto *E = Source.dyn_cast<const Expr *>()) 165*0fca6ea1SDimitry Andric InitLoc = E->getExprLoc(); 1667a6dacacSDimitry Andric 1677a6dacacSDimitry Andric if (const Record *R = Ptr.getRecord()) 1687a6dacacSDimitry Andric return CheckFieldsInitialized(S, InitLoc, Ptr, R); 169*0fca6ea1SDimitry Andric 170*0fca6ea1SDimitry Andric if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>( 171*0fca6ea1SDimitry Andric Ptr.getType()->getAsArrayTypeUnsafe())) 1727a6dacacSDimitry Andric return CheckArrayInitialized(S, InitLoc, Ptr, CAT); 1737a6dacacSDimitry Andric 1747a6dacacSDimitry Andric return true; 1757a6dacacSDimitry Andric } 1767a6dacacSDimitry Andric 177*0fca6ea1SDimitry Andric static void collectBlocks(const Pointer &Ptr, 178*0fca6ea1SDimitry Andric llvm::SetVector<const Block *> &Blocks) { 179*0fca6ea1SDimitry Andric auto isUsefulPtr = [](const Pointer &P) -> bool { 180*0fca6ea1SDimitry Andric return P.isLive() && !P.isZero() && !P.isDummy() && 181*0fca6ea1SDimitry Andric !P.isUnknownSizeArray() && !P.isOnePastEnd() && P.isBlockPointer(); 182*0fca6ea1SDimitry Andric }; 1837a6dacacSDimitry Andric 184*0fca6ea1SDimitry Andric if (!isUsefulPtr(Ptr)) 185*0fca6ea1SDimitry Andric return; 186*0fca6ea1SDimitry Andric 187*0fca6ea1SDimitry Andric Blocks.insert(Ptr.block()); 188*0fca6ea1SDimitry Andric 189*0fca6ea1SDimitry Andric const Descriptor *Desc = Ptr.getFieldDesc(); 190*0fca6ea1SDimitry Andric if (!Desc) 191*0fca6ea1SDimitry Andric return; 192*0fca6ea1SDimitry Andric 193*0fca6ea1SDimitry Andric if (const Record *R = Desc->ElemRecord) { 194*0fca6ea1SDimitry Andric for (const Record::Field &F : R->fields()) { 195*0fca6ea1SDimitry Andric const Pointer &FieldPtr = Ptr.atField(F.Offset); 196*0fca6ea1SDimitry Andric assert(FieldPtr.block() == Ptr.block()); 197*0fca6ea1SDimitry Andric collectBlocks(FieldPtr, Blocks); 198*0fca6ea1SDimitry Andric } 199*0fca6ea1SDimitry Andric } else if (Desc->isPrimitive() && Desc->getPrimType() == PT_Ptr) { 200*0fca6ea1SDimitry Andric const Pointer &Pointee = Ptr.deref<Pointer>(); 201*0fca6ea1SDimitry Andric if (isUsefulPtr(Pointee) && !Blocks.contains(Pointee.block())) 202*0fca6ea1SDimitry Andric collectBlocks(Pointee, Blocks); 203*0fca6ea1SDimitry Andric 204*0fca6ea1SDimitry Andric } else if (Desc->isPrimitiveArray() && Desc->getPrimType() == PT_Ptr) { 205*0fca6ea1SDimitry Andric for (unsigned I = 0; I != Desc->getNumElems(); ++I) { 206*0fca6ea1SDimitry Andric const Pointer &ElemPointee = Ptr.atIndex(I).deref<Pointer>(); 207*0fca6ea1SDimitry Andric if (isUsefulPtr(ElemPointee) && !Blocks.contains(ElemPointee.block())) 208*0fca6ea1SDimitry Andric collectBlocks(ElemPointee, Blocks); 209*0fca6ea1SDimitry Andric } 210*0fca6ea1SDimitry Andric } else if (Desc->isCompositeArray()) { 211*0fca6ea1SDimitry Andric for (unsigned I = 0; I != Desc->getNumElems(); ++I) { 212*0fca6ea1SDimitry Andric const Pointer &ElemPtr = Ptr.atIndex(I).narrow(); 213*0fca6ea1SDimitry Andric collectBlocks(ElemPtr, Blocks); 214*0fca6ea1SDimitry Andric } 215*0fca6ea1SDimitry Andric } 2167a6dacacSDimitry Andric } 2177a6dacacSDimitry Andric 218*0fca6ea1SDimitry Andric bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx, 219*0fca6ea1SDimitry Andric const Pointer &Ptr, 220*0fca6ea1SDimitry Andric const SourceInfo &Info) { 221*0fca6ea1SDimitry Andric // Collect all blocks that this pointer (transitively) points to and 222*0fca6ea1SDimitry Andric // return false if any of them is a dynamic block. 223*0fca6ea1SDimitry Andric llvm::SetVector<const Block *> Blocks; 224*0fca6ea1SDimitry Andric 225*0fca6ea1SDimitry Andric collectBlocks(Ptr, Blocks); 226*0fca6ea1SDimitry Andric 227*0fca6ea1SDimitry Andric for (const Block *B : Blocks) { 228*0fca6ea1SDimitry Andric if (B->isDynamic()) { 229*0fca6ea1SDimitry Andric assert(B->getDescriptor()); 230*0fca6ea1SDimitry Andric assert(B->getDescriptor()->asExpr()); 231*0fca6ea1SDimitry Andric 232*0fca6ea1SDimitry Andric S.FFDiag(Info, diag::note_constexpr_dynamic_alloc) 233*0fca6ea1SDimitry Andric << Ptr.getType()->isReferenceType() << !Ptr.isRoot(); 234*0fca6ea1SDimitry Andric S.Note(B->getDescriptor()->asExpr()->getExprLoc(), 235*0fca6ea1SDimitry Andric diag::note_constexpr_dynamic_alloc_here); 236*0fca6ea1SDimitry Andric return false; 237*0fca6ea1SDimitry Andric } 2387a6dacacSDimitry Andric } 2397a6dacacSDimitry Andric 240*0fca6ea1SDimitry Andric return true; 2417a6dacacSDimitry Andric } 2427a6dacacSDimitry Andric 2437a6dacacSDimitry Andric } // namespace interp 2447a6dacacSDimitry Andric } // namespace clang 245