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