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