1//===--- Context.h - Context for the constexpr 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// Defines the constexpr execution context.
10//
11// The execution context manages cached bytecode and the global context.
12// It invokes the compiler and interpreter, propagating errors.
13//
14//===----------------------------------------------------------------------===//
15
16#ifndef LLVM_CLANG_AST_INTERP_CONTEXT_H
17#define LLVM_CLANG_AST_INTERP_CONTEXT_H
18
19#include "FrameAllocator.h"
20#include "InterpStack.h"
21#include "clang/AST/ASTContext.h"
22
23namespace clang {
24class LangOptions;
25class FunctionDecl;
26class VarDecl;
27class APValue;
28class BlockExpr;
29
30namespace interp {
31class Function;
32class Program;
33class State;
34enum PrimType : uint8_t;
35
36struct ParamOffset {
37 unsigned Offset;
38 bool IsPtr;
39};
40
41struct FuncParam {
42 unsigned Index;
43 bool IsPtr;
44};
45
46class EvalIDScope;
47/// Holds all information required to evaluate constexpr code in a module.
48class Context final {
49public:
50 /// Initialises the constexpr VM.
51 explicit Context(ASTContext &Ctx);
52
53 /// Cleans up the constexpr VM.
54 ~Context();
55
56 /// Checks if a function is a potential constant expression.
57 bool isPotentialConstantExpr(State &Parent, const FunctionDecl *FD);
58 void isPotentialConstantExprUnevaluated(State &Parent, const Expr *E,
59 const FunctionDecl *FD);
60
61 /// Evaluates a toplevel expression as an rvalue.
62 bool evaluateAsRValue(State &Parent, const Expr *E, APValue &Result);
63
64 /// Like evaluateAsRvalue(), but does no implicit lvalue-to-rvalue conversion.
65 bool evaluate(State &Parent, const Expr *E, APValue &Result,
66 ConstantExprKind Kind);
67
68 /// Evaluates a toplevel initializer.
69 bool evaluateAsInitializer(State &Parent, const VarDecl *VD, const Expr *Init,
70 APValue &Result);
71
72 /// Evaluates the destruction of a variable.
73 bool evaluateDestruction(State &Parent, const VarDecl *VD, APValue Value);
74
75 bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
76 const Expr *PtrExpr, APValue &Result);
77 bool evaluateCharRange(State &Parent, const Expr *SizeExpr,
78 const Expr *PtrExpr, std::string &Result);
79
80 /// Evaluate \param E and if it can be evaluated to a null-terminated string,
81 /// copy the result into \param Result.
82 bool evaluateString(State &Parent, const Expr *E, std::string &Result);
83
84 /// Evalute \param E and if it can be evaluated to a string literal,
85 /// run strlen() on it.
86 std::optional<uint64_t> evaluateStrlen(State &Parent, const Expr *E);
87
88 /// If \param E evaluates to a pointer the number of accessible bytes
89 /// past the pointer is estimated in \param Result as if evaluated by
90 /// the builtin function __builtin_object_size. This is a best effort
91 /// approximation, when Kind & 2 == 0 the object size is less
92 /// than or equal to the estimated size, when Kind & 2 == 1 the
93 /// true value is greater than or equal to the estimated size.
94 /// When Kind & 1 == 1 only bytes belonging to the same subobject
95 /// as the one referred to by E are considered, when Kind & 1 == 0
96 /// bytes belonging to the same storage (stack, heap allocation,
97 /// global variable) are considered.
98 std::optional<uint64_t> tryEvaluateObjectSize(State &Parent, const Expr *E,
99 unsigned Kind, bool IsDynamic);
100
101 std::optional<bool> evaluateWithSubstitution(State &Parent,
102 const FunctionDecl *Callee,
103 ArrayRef<const Expr *> Args,
104 const Expr *This,
105 const Expr *Condition);
106
107 /// Returns the AST context.
108 ASTContext &getASTContext() const { return Ctx; }
109 /// Returns the language options.
110 const LangOptions &getLangOpts() const;
111 /// Returns CHAR_BIT.
112 unsigned getCharBit() const;
113 /// Return the floating-point semantics for T.
114 const llvm::fltSemantics &getFloatSemantics(QualType T) const;
115 /// Return the size of T in bits.
116 uint32_t getBitWidth(QualType T) const { return Ctx.getIntWidth(T); }
117
118 /// Classifies a type.
119 OptPrimType classify(QualType T) const;
120
121 /// Classifies an expression.
122 OptPrimType classify(const Expr *E) const {
123 assert(E);
124 if (E->isGLValue())
125 return PT_Ptr;
126
127 return classify(T: E->getType());
128 }
129
130 bool canClassify(QualType T) const {
131 T = T.getCanonicalType();
132 if (const auto *BT = dyn_cast<BuiltinType>(Val&: T)) {
133 if (BT->isInteger() || BT->isFloatingPoint())
134 return true;
135 if (BT->getKind() == BuiltinType::NullPtr ||
136 BT->getKind() == BuiltinType::BoundMember)
137 return true;
138 }
139 if (T->isPointerOrReferenceType())
140 return true;
141
142 if (T->isArrayType() || T->isRecordType() || T->isAnyComplexType() ||
143 T->isVectorType())
144 return false;
145
146 if (T->isEnumeralType())
147 return true;
148
149 return classify(T) != std::nullopt;
150 }
151 bool canClassify(const Expr *E) const {
152 if (E->isGLValue())
153 return true;
154 return canClassify(T: E->getType());
155 }
156
157 const CXXMethodDecl *
158 getOverridingFunction(const CXXRecordDecl *DynamicDecl,
159 const CXXRecordDecl *StaticDecl,
160 const CXXMethodDecl *InitialFunction) const;
161
162 const Function *getOrCreateFunction(const FunctionDecl *FuncDecl);
163 const Function *getOrCreateObjCBlock(const BlockExpr *E);
164
165 /// Returns whether we should create a global variable for the
166 /// given ValueDecl.
167 static bool shouldBeGloballyIndexed(const ValueDecl *VD) {
168 if (const auto *V = dyn_cast<VarDecl>(Val: VD))
169 return V->hasGlobalStorage() || V->isConstexpr();
170
171 return false;
172 }
173
174 /// Returns the program. This is only needed for unittests.
175 Program &getProgram() const { return *P; }
176
177 unsigned collectBaseOffset(const RecordDecl *BaseDecl,
178 const RecordDecl *DerivedDecl) const;
179
180 const Record *getRecord(const RecordDecl *D) const;
181
182 unsigned getEvalID() const { return EvalID; }
183
184 /// Unevaluated builtins don't get their arguments put on the stack
185 /// automatically. They instead operate on the AST of their Call
186 /// Expression.
187 /// Similar information is available via ASTContext::BuiltinInfo,
188 /// but that is not correct for our use cases.
189 static bool isUnevaluatedBuiltin(unsigned ID);
190
191private:
192 friend class EvalIDScope;
193 /// Runs a function.
194 bool Run(State &Parent, const Function *Func);
195
196 template <typename ResultT>
197 bool evaluateStringRepr(State &Parent, const Expr *SizeExpr,
198 const Expr *PtrExpr, ResultT &Result);
199
200 /// Current compilation context.
201 ASTContext &Ctx;
202 /// Interpreter stack, shared across invocations.
203 InterpStack Stk;
204 /// (Function) frame allocator, also shared.
205 FrameAllocator FrameAlloc;
206 /// Constexpr program.
207 std::unique_ptr<Program> P;
208 /// ID identifying an evaluation.
209 unsigned EvalID = 0;
210 /// Cached widths (in bits) of common types, for a faster classify().
211 unsigned ShortWidth;
212 unsigned IntWidth;
213 unsigned LongWidth;
214 unsigned LongLongWidth;
215};
216
217class EvalIDScope {
218public:
219 EvalIDScope(Context &Ctx) : Ctx(Ctx), OldID(Ctx.EvalID) { ++Ctx.EvalID; }
220 ~EvalIDScope() { Ctx.EvalID = OldID; }
221 EvalIDScope(const EvalIDScope &) = delete;
222 EvalIDScope &operator=(const EvalIDScope &) = delete;
223
224private:
225 Context &Ctx;
226 const unsigned OldID;
227};
228
229} // namespace interp
230} // namespace clang
231
232#endif
233