xref: /llvm-project/clang/lib/AST/ByteCode/EvaluationResult.cpp (revision 3f07af93dc013621176f5931ebc8dd07d299b277)
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