xref: /llvm-project/clang/lib/AST/ByteCode/EvaluationResult.cpp (revision a07aba5d44204a7ca0d891a3da05af9960081e4c)
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         S.FFDiag(BS.getBaseTypeLoc(), diag::note_constexpr_uninitialized_base)
134             << B.Desc->getType() << BS.getSourceRange();
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 = Source.dyn_cast<const Decl *>())
164     InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc();
165   else if (const auto *E = Source.dyn_cast<const Expr *>())
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() &&
182            !P.isUnknownSizeArray() && !P.isOnePastEnd() && P.isBlockPointer();
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