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