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