xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/EvaluationResult.cpp (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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/SetVector.h"
14 
15 namespace clang {
16 namespace interp {
17 
18 APValue EvaluationResult::toAPValue() const {
19   assert(!empty());
20   switch (Kind) {
21   case LValue:
22     // Either a pointer or a function pointer.
23     if (const auto *P = std::get_if<Pointer>(&Value))
24       return P->toAPValue(Ctx->getASTContext());
25     else if (const auto *FP = std::get_if<FunctionPointer>(&Value))
26       return FP->toAPValue(Ctx->getASTContext());
27     else
28       llvm_unreachable("Unhandled LValue type");
29     break;
30   case RValue:
31     return std::get<APValue>(Value);
32   case Valid:
33     return APValue();
34   default:
35     llvm_unreachable("Unhandled result kind?");
36   }
37 }
38 
39 std::optional<APValue> EvaluationResult::toRValue() const {
40   if (Kind == RValue)
41     return toAPValue();
42 
43   assert(Kind == LValue);
44 
45   // We have a pointer and want an RValue.
46   if (const auto *P = std::get_if<Pointer>(&Value))
47     return P->toRValue(*Ctx, getSourceType());
48   else if (const auto *FP = std::get_if<FunctionPointer>(&Value)) // Nope
49     return FP->toAPValue(Ctx->getASTContext());
50   llvm_unreachable("Unhandled lvalue kind");
51 }
52 
53 static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc,
54                                            const FieldDecl *SubObjDecl) {
55   assert(SubObjDecl && "Subobject declaration does not exist");
56   S.FFDiag(Loc, diag::note_constexpr_uninitialized)
57       << /*(name)*/ 1 << SubObjDecl;
58   S.Note(SubObjDecl->getLocation(),
59          diag::note_constexpr_subobject_declared_here);
60 }
61 
62 static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
63                                    const Pointer &BasePtr, const Record *R);
64 
65 static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc,
66                                   const Pointer &BasePtr,
67                                   const ConstantArrayType *CAT) {
68   bool Result = true;
69   size_t NumElems = CAT->getZExtSize();
70   QualType ElemType = CAT->getElementType();
71 
72   if (ElemType->isRecordType()) {
73     const Record *R = BasePtr.getElemRecord();
74     for (size_t I = 0; I != NumElems; ++I) {
75       Pointer ElemPtr = BasePtr.atIndex(I).narrow();
76       Result &= CheckFieldsInitialized(S, Loc, ElemPtr, R);
77     }
78   } else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
79     for (size_t I = 0; I != NumElems; ++I) {
80       Pointer ElemPtr = BasePtr.atIndex(I).narrow();
81       Result &= CheckArrayInitialized(S, Loc, ElemPtr, ElemCAT);
82     }
83   } else {
84     for (size_t I = 0; I != NumElems; ++I) {
85       if (!BasePtr.atIndex(I).isInitialized()) {
86         DiagnoseUninitializedSubobject(S, Loc, BasePtr.getField());
87         Result = false;
88       }
89     }
90   }
91 
92   return Result;
93 }
94 
95 static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
96                                    const Pointer &BasePtr, const Record *R) {
97   assert(R);
98   bool Result = true;
99   // Check all fields of this record are initialized.
100   for (const Record::Field &F : R->fields()) {
101     Pointer FieldPtr = BasePtr.atField(F.Offset);
102     QualType FieldType = F.Decl->getType();
103 
104     // Don't check inactive union members.
105     if (R->isUnion() && !FieldPtr.isActive())
106       continue;
107 
108     if (FieldType->isRecordType()) {
109       Result &= CheckFieldsInitialized(S, Loc, FieldPtr, FieldPtr.getRecord());
110     } else if (FieldType->isIncompleteArrayType()) {
111       // Nothing to do here.
112     } else if (F.Decl->isUnnamedBitField()) {
113       // Nothing do do here.
114     } else if (FieldType->isArrayType()) {
115       const auto *CAT =
116           cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
117       Result &= CheckArrayInitialized(S, Loc, FieldPtr, CAT);
118     } else if (!FieldPtr.isInitialized()) {
119       DiagnoseUninitializedSubobject(S, Loc, F.Decl);
120       Result = false;
121     }
122   }
123 
124   // Check Fields in all bases
125   for (const Record::Base &B : R->bases()) {
126     Pointer P = BasePtr.atField(B.Offset);
127     if (!P.isInitialized()) {
128       const Descriptor *Desc = BasePtr.getDeclDesc();
129       if (Desc->asDecl())
130         S.FFDiag(BasePtr.getDeclDesc()->asDecl()->getLocation(),
131                  diag::note_constexpr_uninitialized_base)
132             << B.Desc->getType();
133       else
134         S.FFDiag(BasePtr.getDeclDesc()->asExpr()->getExprLoc(),
135                  diag::note_constexpr_uninitialized_base)
136             << B.Desc->getType();
137 
138       return false;
139     }
140     Result &= CheckFieldsInitialized(S, Loc, P, B.R);
141   }
142 
143   // TODO: Virtual bases
144 
145   return Result;
146 }
147 
148 bool EvaluationResult::checkFullyInitialized(InterpState &S,
149                                              const Pointer &Ptr) const {
150   assert(Source);
151   assert(empty());
152 
153   if (Ptr.isZero())
154     return true;
155 
156   // We can't inspect dead pointers at all. Return true here so we can
157   // diagnose them later.
158   if (!Ptr.isLive())
159     return true;
160 
161   SourceLocation InitLoc;
162   if (const auto *D = Source.dyn_cast<const Decl *>())
163     InitLoc = cast<VarDecl>(D)->getAnyInitializer()->getExprLoc();
164   else if (const auto *E = Source.dyn_cast<const Expr *>())
165     InitLoc = E->getExprLoc();
166 
167   if (const Record *R = Ptr.getRecord())
168     return CheckFieldsInitialized(S, InitLoc, Ptr, R);
169 
170   if (const auto *CAT = dyn_cast_if_present<ConstantArrayType>(
171           Ptr.getType()->getAsArrayTypeUnsafe()))
172     return CheckArrayInitialized(S, InitLoc, Ptr, CAT);
173 
174   return true;
175 }
176 
177 static void collectBlocks(const Pointer &Ptr,
178                           llvm::SetVector<const Block *> &Blocks) {
179   auto isUsefulPtr = [](const Pointer &P) -> bool {
180     return P.isLive() && !P.isZero() && !P.isDummy() &&
181            !P.isUnknownSizeArray() && !P.isOnePastEnd() && P.isBlockPointer();
182   };
183 
184   if (!isUsefulPtr(Ptr))
185     return;
186 
187   Blocks.insert(Ptr.block());
188 
189   const Descriptor *Desc = Ptr.getFieldDesc();
190   if (!Desc)
191     return;
192 
193   if (const Record *R = Desc->ElemRecord) {
194     for (const Record::Field &F : R->fields()) {
195       const Pointer &FieldPtr = Ptr.atField(F.Offset);
196       assert(FieldPtr.block() == Ptr.block());
197       collectBlocks(FieldPtr, Blocks);
198     }
199   } else if (Desc->isPrimitive() && Desc->getPrimType() == PT_Ptr) {
200     const Pointer &Pointee = Ptr.deref<Pointer>();
201     if (isUsefulPtr(Pointee) && !Blocks.contains(Pointee.block()))
202       collectBlocks(Pointee, Blocks);
203 
204   } else if (Desc->isPrimitiveArray() && Desc->getPrimType() == PT_Ptr) {
205     for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
206       const Pointer &ElemPointee = Ptr.atIndex(I).deref<Pointer>();
207       if (isUsefulPtr(ElemPointee) && !Blocks.contains(ElemPointee.block()))
208         collectBlocks(ElemPointee, Blocks);
209     }
210   } else if (Desc->isCompositeArray()) {
211     for (unsigned I = 0; I != Desc->getNumElems(); ++I) {
212       const Pointer &ElemPtr = Ptr.atIndex(I).narrow();
213       collectBlocks(ElemPtr, Blocks);
214     }
215   }
216 }
217 
218 bool EvaluationResult::checkReturnValue(InterpState &S, const Context &Ctx,
219                                         const Pointer &Ptr,
220                                         const SourceInfo &Info) {
221   // Collect all blocks that this pointer (transitively) points to and
222   // return false if any of them is a dynamic block.
223   llvm::SetVector<const Block *> Blocks;
224 
225   collectBlocks(Ptr, Blocks);
226 
227   for (const Block *B : Blocks) {
228     if (B->isDynamic()) {
229       assert(B->getDescriptor());
230       assert(B->getDescriptor()->asExpr());
231 
232       S.FFDiag(Info, diag::note_constexpr_dynamic_alloc)
233           << Ptr.getType()->isReferenceType() << !Ptr.isRoot();
234       S.Note(B->getDescriptor()->asExpr()->getExprLoc(),
235              diag::note_constexpr_dynamic_alloc_here);
236       return false;
237     }
238   }
239 
240   return true;
241 }
242 
243 } // namespace interp
244 } // namespace clang
245