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