xref: /llvm-project/clang/lib/AST/ByteCode/EvaluationResult.cpp (revision 208584d91ae138d752d89436e3df12fa8f2e60a8)
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         SourceLocation TypeBeginLoc = BS.getBaseTypeLoc();
134         S.FFDiag(TypeBeginLoc, diag::note_constexpr_uninitialized_base)
135             << B.Desc->getType() << SourceRange(TypeBeginLoc, BS.getEndLoc());
136       } else {
137         S.FFDiag(Desc->getLocation(), diag::note_constexpr_uninitialized_base)
138             << B.Desc->getType();
139       }
140       return false;
141     }
142     Result &= CheckFieldsInitialized(S, Loc, P, B.R);
143   }
144 
145   // TODO: Virtual bases
146 
147   return Result;
148 }
149 
150 bool EvaluationResult::checkFullyInitialized(InterpState &S,
151                                              const Pointer &Ptr) const {
152   assert(Source);
153   assert(empty());
154 
155   if (Ptr.isZero())
156     return true;
157 
158   // We can't inspect dead pointers at all. Return true here so we can
159   // diagnose them later.
160   if (!Ptr.isLive())
161     return true;
162 
163   SourceLocation InitLoc;
164   if (const auto *D = Source.dyn_cast<const Decl *>())
165     InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc();
166   else if (const auto *E = Source.dyn_cast<const Expr *>())
167     InitLoc = E->getExprLoc();
168 
169   if (const Record *R = Ptr.getRecord())
170     return CheckFieldsInitialized(S, InitLoc, Ptr, R);
171 
172   if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>(
173           Ptr.getType()->getAsArrayTypeUnsafe()))
174     return CheckArrayInitialized(S, InitLoc, Ptr, CAT);
175 
176   return true;
177 }
178 
179 static void collectBlocks(const Pointer &Ptr,
180                           llvm::SetVector<const Block *> &Blocks) {
181   auto isUsefulPtr = [](const Pointer &P) -> bool {
182     return P.isLive() && !P.isZero() && !P.isDummy() && P.isDereferencable() &&
183            !P.isUnknownSizeArray() && !P.isOnePastEnd();
184   };
185 
186   if (!isUsefulPtr(Ptr))
187     return;
188 
189   Blocks.insert(Ptr.block());
190 
191   const Descriptor *Desc = Ptr.getFieldDesc();
192   if (!Desc)
193     return;
194 
195   if (const Record *R = Desc->ElemRecord) {
196     for (const Record::Field &F : R->fields()) {
197       const Pointer &FieldPtr = Ptr.atField(F.Offset);
198       assert(FieldPtr.block() == Ptr.block());
199       collectBlocks(FieldPtr, Blocks);
200     }
201   } else if (Desc->isPrimitive() && Desc->getPrimType() == PT_Ptr) {
202     const Pointer &Pointee = Ptr.deref<Pointer>();
203     if (isUsefulPtr(Pointee) && !Blocks.contains(Pointee.block()))
204       collectBlocks(Pointee, Blocks);
205 
206   } else if (Desc->isPrimitiveArray() && Desc->getPrimType() == PT_Ptr) {
207     for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
208       const Pointer &ElemPointee = Ptr.atIndex(I).deref<Pointer>();
209       if (isUsefulPtr(ElemPointee) && !Blocks.contains(ElemPointee.block()))
210         collectBlocks(ElemPointee, Blocks);
211     }
212   } else if (Desc->isCompositeArray()) {
213     for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
214       const Pointer &ElemPtr = Ptr.atIndex(I).narrow();
215       collectBlocks(ElemPtr, Blocks);
216     }
217   }
218 }
219 
220 bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,
221                                         const Pointer &Ptr,
222                                         const SourceInfo &Info) {
223   // Collect all blocks that this pointer (transitively) points to and
224   // return false if any of them is a dynamic block.
225   llvm::SetVector<const Block *> Blocks;
226 
227   collectBlocks(Ptr, Blocks);
228 
229   for (const Block *B : Blocks) {
230     if (B->isDynamic()) {
231       assert(B->getDescriptor());
232       assert(B->getDescriptor()->asExpr());
233 
234       S.FFDiag(Info, diag::note_constexpr_dynamic_alloc)
235           << Ptr.getType()->isReferenceType() << !Ptr.isRoot();
236       S.Note(B->getDescriptor()->asExpr()->getExprLoc(),
237              diag::note_constexpr_dynamic_alloc_here);
238       return false;
239     }
240   }
241 
242   return true;
243 }
244 
245 } // namespace interp
246 } // namespace clang
247