clang 20.0.0git
EvaluationResult.cpp
Go to the documentation of this file.
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
17namespace clang {
18namespace interp {
19
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
41std::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
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
64static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc,
65 const Pointer &BasePtr, const Record *R);
66
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()) {
89 Result = false;
90 }
91 }
92 }
93
94 return Result;
95}
96
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()) {
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
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>(
173 return CheckArrayInitialized(S, InitLoc, Ptr, CAT);
174
175 return true;
176}
177
178static 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
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
StringRef P
const Decl * D
Expr * E
Defines the clang::Expr interface and subclasses for C++ expressions.
llvm::MachO::Record Record
Definition: MachO.h:31
SourceLocation Loc
Definition: SemaObjC.cpp:759
APValue - This class implements a discriminated union of [uninitialized] [APSInt] [APFloat],...
Definition: APValue.h:122
QualType getElementType() const
Definition: Type.h:3578
Represents the canonical version of C arrays with a specified constant size.
Definition: Type.h:3604
uint64_t getZExtSize() const
Return the size zero-extended as a uint64_t.
Definition: Type.h:3680
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
SourceLocation getLocation() const
Definition: DeclBase.h:446
This represents one expression.
Definition: Expr.h:110
Represents a member of a struct/union/class.
Definition: Decl.h:3030
A (possibly-)qualified type.
Definition: Type.h:941
Encodes a location in the source.
bool isIncompleteArrayType() const
Definition: Type.h:8083
bool isArrayType() const
Definition: Type.h:8075
bool isReferenceType() const
Definition: Type.h:8021
const ArrayType * getAsArrayTypeUnsafe() const
A variant of getAs<> for array types which silently discards qualifiers from the outermost type.
Definition: Type.h:8593
bool isRecordType() const
Definition: Type.h:8103
A memory block, either on the stack or in the heap.
Definition: InterpBlock.h:49
Holds all information required to evaluate constexpr code in a module.
Definition: Context.h:40
ASTContext & getASTContext() const
Returns the AST context.
Definition: Context.h:61
std::optional< APValue > toRValue() const
If the result is an LValue, convert that to an RValue and return it.
APValue toAPValue() const
Returns an APValue for the evaluation result.
bool checkReturnValue(InterpState &S, const Context &Ctx, const Pointer &Ptr, const SourceInfo &Info)
Check that none of the blocks the given pointer (transitively) points to are dynamically allocated.
bool checkFullyInitialized(InterpState &S, const Pointer &Ptr) const
Check that all subobjects of the given pointer have been initialized.
Interpreter context.
Definition: InterpState.h:36
A pointer to a memory block, live or dead.
Definition: Pointer.h:82
Pointer narrow() const
Restricts the scope of an array element pointer.
Definition: Pointer.h:183
bool isInitialized() const
Checks if an object was initialized.
Definition: Pointer.cpp:311
Pointer atIndex(uint64_t Idx) const
Offsets a pointer inside an array.
Definition: Pointer.h:149
bool isActive() const
Checks if the object is active.
Definition: Pointer.h:516
Pointer atField(unsigned Off) const
Creates a pointer to a field.
Definition: Pointer.h:168
T & deref() const
Dereferences the pointer, if it's live.
Definition: Pointer.h:623
QualType getType() const
Returns the type of the innermost field.
Definition: Pointer.h:338
bool isLive() const
Checks if the pointer is live.
Definition: Pointer.h:269
bool isZero() const
Checks if the pointer is null.
Definition: Pointer.h:260
bool isRoot() const
Pointer points directly to a block.
Definition: Pointer.h:428
const Descriptor * getDeclDesc() const
Accessor for information about the declaration site.
Definition: Pointer.h:283
const Record * getElemRecord() const
Returns the element record type, if this is a non-primive array.
Definition: Pointer.h:463
const FieldDecl * getField() const
Returns the field information.
Definition: Pointer.h:468
const Block * block() const
Definition: Pointer.h:569
const Descriptor * getFieldDesc() const
Accessors for information about the innermost field.
Definition: Pointer.h:328
const Record * getRecord() const
Returns the record descriptor of a class.
Definition: Pointer.h:461
Structure/Class descriptor.
Definition: Record.h:25
const RecordDecl * getDecl() const
Returns the underlying declaration.
Definition: Record.h:52
bool isUnion() const
Checks if the record is a union.
Definition: Record.h:56
llvm::iterator_range< const_base_iter > bases() const
Definition: Record.h:85
llvm::iterator_range< const_field_iter > fields() const
Definition: Record.h:77
Describes the statement/declaration an opcode was generated from.
Definition: Source.h:77
static void collectBlocks(const Pointer &Ptr, llvm::SetVector< const Block * > &Blocks)
static bool CheckFieldsInitialized(InterpState &S, SourceLocation Loc, const Pointer &BasePtr, const Record *R)
static void DiagnoseUninitializedSubobject(InterpState &S, SourceLocation Loc, const FieldDecl *SubObjDecl)
static bool CheckArrayInitialized(InterpState &S, SourceLocation Loc, const Pointer &BasePtr, const ConstantArrayType *CAT)
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
Describes a memory block created by an allocation site.
Definition: Descriptor.h:111
unsigned getNumElems() const
Returns the number of elements stored in the block.
Definition: Descriptor.h:237
bool isPrimitive() const
Checks if the descriptor is of a primitive.
Definition: Descriptor.h:251
bool isCompositeArray() const
Checks if the descriptor is of an array of composites.
Definition: Descriptor.h:244
SourceLocation getLocation() const
Definition: Descriptor.cpp:416
bool isPrimitiveArray() const
Checks if the descriptor is of an array of primitives.
Definition: Descriptor.h:242
PrimType getPrimType() const
Definition: Descriptor.h:224
const Record *const ElemRecord
Pointer to the record, if block contains records.
Definition: Descriptor.h:141