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