xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp (revision dd41de95a84d979615a2ef11df6850622bf6184e)
1 //===--- InterpState.cpp - Interpreter 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 #include "Interp.h"
10 #include <limits>
11 #include <vector>
12 #include "Function.h"
13 #include "InterpFrame.h"
14 #include "InterpStack.h"
15 #include "Opcode.h"
16 #include "PrimType.h"
17 #include "Program.h"
18 #include "State.h"
19 #include "clang/AST/ASTContext.h"
20 #include "clang/AST/ASTDiagnostic.h"
21 #include "clang/AST/CXXInheritance.h"
22 #include "clang/AST/Expr.h"
23 #include "clang/AST/ExprCXX.h"
24 #include "llvm/ADT/APSInt.h"
25 
26 using namespace clang;
27 using namespace clang::interp;
28 
29 //===----------------------------------------------------------------------===//
30 // Ret
31 //===----------------------------------------------------------------------===//
32 
33 template <PrimType Name, class T = typename PrimConv<Name>::T>
34 static bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
35   S.CallStackDepth--;
36   const T &Ret = S.Stk.pop<T>();
37 
38   assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
39   if (!S.checkingPotentialConstantExpression())
40     S.Current->popArgs();
41 
42   if (InterpFrame *Caller = S.Current->Caller) {
43     PC = S.Current->getRetPC();
44     delete S.Current;
45     S.Current = Caller;
46     S.Stk.push<T>(Ret);
47   } else {
48     delete S.Current;
49     S.Current = nullptr;
50     if (!ReturnValue<T>(Ret, Result))
51       return false;
52   }
53   return true;
54 }
55 
56 static bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
57   S.CallStackDepth--;
58 
59   assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
60   if (!S.checkingPotentialConstantExpression())
61     S.Current->popArgs();
62 
63   if (InterpFrame *Caller = S.Current->Caller) {
64     PC = S.Current->getRetPC();
65     delete S.Current;
66     S.Current = Caller;
67   } else {
68     delete S.Current;
69     S.Current = nullptr;
70   }
71   return true;
72 }
73 
74 static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result) {
75   llvm::report_fatal_error("Interpreter cannot return values");
76 }
77 
78 //===----------------------------------------------------------------------===//
79 // Jmp, Jt, Jf
80 //===----------------------------------------------------------------------===//
81 
82 static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) {
83   PC += Offset;
84   return true;
85 }
86 
87 static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) {
88   if (S.Stk.pop<bool>()) {
89     PC += Offset;
90   }
91   return true;
92 }
93 
94 static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
95   if (!S.Stk.pop<bool>()) {
96     PC += Offset;
97   }
98   return true;
99 }
100 
101 static bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
102                              AccessKinds AK) {
103   if (Ptr.isInitialized())
104     return true;
105   if (!S.checkingPotentialConstantExpression()) {
106     const SourceInfo &Loc = S.Current->getSource(OpPC);
107     S.FFDiag(Loc, diag::note_constexpr_access_uninit) << AK << false;
108   }
109   return false;
110 }
111 
112 static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
113                         AccessKinds AK) {
114   if (Ptr.isActive())
115     return true;
116 
117   // Get the inactive field descriptor.
118   const FieldDecl *InactiveField = Ptr.getField();
119 
120   // Walk up the pointer chain to find the union which is not active.
121   Pointer U = Ptr.getBase();
122   while (!U.isActive()) {
123     U = U.getBase();
124   }
125 
126   // Find the active field of the union.
127   Record *R = U.getRecord();
128   assert(R && R->isUnion() && "Not a union");
129   const FieldDecl *ActiveField = nullptr;
130   for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
131     const Pointer &Field = U.atField(R->getField(I)->Offset);
132     if (Field.isActive()) {
133       ActiveField = Field.getField();
134       break;
135     }
136   }
137 
138   const SourceInfo &Loc = S.Current->getSource(OpPC);
139   S.FFDiag(Loc, diag::note_constexpr_access_inactive_union_member)
140       << AK << InactiveField << !ActiveField << ActiveField;
141   return false;
142 }
143 
144 static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
145                            AccessKinds AK) {
146   if (auto ID = Ptr.getDeclID()) {
147     if (!Ptr.isStaticTemporary())
148       return true;
149 
150     if (Ptr.getDeclDesc()->getType().isConstQualified())
151       return true;
152 
153     if (S.P.getCurrentDecl() == ID)
154       return true;
155 
156     const SourceInfo &E = S.Current->getSource(OpPC);
157     S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
158     S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
159     return false;
160   }
161   return true;
162 }
163 
164 static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
165   if (auto ID = Ptr.getDeclID()) {
166     if (!Ptr.isStatic())
167       return true;
168 
169     if (S.P.getCurrentDecl() == ID)
170       return true;
171 
172     S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_modify_global);
173     return false;
174   }
175   return true;
176 }
177 
178 namespace clang {
179 namespace interp {
180 
181 bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
182   if (!Ptr.isExtern())
183     return true;
184 
185   if (!S.checkingPotentialConstantExpression()) {
186     auto *VD = Ptr.getDeclDesc()->asValueDecl();
187     const SourceInfo &Loc = S.Current->getSource(OpPC);
188     S.FFDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
189     S.Note(VD->getLocation(), diag::note_declared_at);
190   }
191   return false;
192 }
193 
194 bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
195   if (!Ptr.isUnknownSizeArray())
196     return true;
197   const SourceInfo &E = S.Current->getSource(OpPC);
198   S.FFDiag(E, diag::note_constexpr_unsized_array_indexed);
199   return false;
200 }
201 
202 bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
203                AccessKinds AK) {
204   const auto &Src = S.Current->getSource(OpPC);
205   if (Ptr.isZero()) {
206 
207     if (Ptr.isField())
208       S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field;
209     else
210       S.FFDiag(Src, diag::note_constexpr_access_null) << AK;
211 
212     return false;
213   }
214 
215   if (!Ptr.isLive()) {
216     bool IsTemp = Ptr.isTemporary();
217 
218     S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
219 
220     if (IsTemp)
221       S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
222     else
223       S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
224 
225     return false;
226   }
227 
228   return true;
229 }
230 
231 bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
232                CheckSubobjectKind CSK) {
233   if (!Ptr.isZero())
234     return true;
235   const SourceInfo &Loc = S.Current->getSource(OpPC);
236   S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK;
237   return false;
238 }
239 
240 bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
241                 AccessKinds AK) {
242   if (!Ptr.isOnePastEnd())
243     return true;
244   const SourceInfo &Loc = S.Current->getSource(OpPC);
245   S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK;
246   return false;
247 }
248 
249 bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
250                 CheckSubobjectKind CSK) {
251   if (!Ptr.isElementPastEnd())
252     return true;
253   const SourceInfo &Loc = S.Current->getSource(OpPC);
254   S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK;
255   return false;
256 }
257 
258 bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
259   assert(Ptr.isLive() && "Pointer is not live");
260   if (!Ptr.isConst()) {
261     return true;
262   }
263 
264   const QualType Ty = Ptr.getType();
265   const SourceInfo &Loc = S.Current->getSource(OpPC);
266   S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;
267   return false;
268 }
269 
270 bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
271   assert(Ptr.isLive() && "Pointer is not live");
272   if (!Ptr.isMutable()) {
273     return true;
274   }
275 
276   const SourceInfo &Loc = S.Current->getSource(OpPC);
277   const FieldDecl *Field = Ptr.getField();
278   S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;
279   S.Note(Field->getLocation(), diag::note_declared_at);
280   return false;
281 }
282 
283 bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
284   if (!CheckLive(S, OpPC, Ptr, AK_Read))
285     return false;
286   if (!CheckExtern(S, OpPC, Ptr))
287     return false;
288   if (!CheckRange(S, OpPC, Ptr, AK_Read))
289     return false;
290   if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
291     return false;
292   if (!CheckActive(S, OpPC, Ptr, AK_Read))
293     return false;
294   if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
295     return false;
296   if (!CheckMutable(S, OpPC, Ptr))
297     return false;
298   return true;
299 }
300 
301 bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
302   if (!CheckLive(S, OpPC, Ptr, AK_Assign))
303     return false;
304   if (!CheckExtern(S, OpPC, Ptr))
305     return false;
306   if (!CheckRange(S, OpPC, Ptr, AK_Assign))
307     return false;
308   if (!CheckGlobal(S, OpPC, Ptr))
309     return false;
310   if (!CheckConst(S, OpPC, Ptr))
311     return false;
312   return true;
313 }
314 
315 bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
316   if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
317     return false;
318   if (!CheckExtern(S, OpPC, Ptr))
319     return false;
320   if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
321     return false;
322   return true;
323 }
324 
325 bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
326   if (!CheckLive(S, OpPC, Ptr, AK_Assign))
327     return false;
328   if (!CheckRange(S, OpPC, Ptr, AK_Assign))
329     return false;
330   return true;
331 }
332 
333 bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) {
334   const SourceLocation &Loc = S.Current->getLocation(OpPC);
335 
336   if (F->isVirtual()) {
337     if (!S.getLangOpts().CPlusPlus20) {
338       S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
339       return false;
340     }
341   }
342 
343   if (!F->isConstexpr()) {
344     if (S.getLangOpts().CPlusPlus11) {
345       const FunctionDecl *DiagDecl = F->getDecl();
346 
347       // If this function is not constexpr because it is an inherited
348       // non-constexpr constructor, diagnose that directly.
349       auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
350       if (CD && CD->isInheritingConstructor()) {
351         auto *Inherited = CD->getInheritedConstructor().getConstructor();
352         if (!Inherited->isConstexpr())
353           DiagDecl = CD = Inherited;
354       }
355 
356       // FIXME: If DiagDecl is an implicitly-declared special member function
357       // or an inheriting constructor, we should be much more explicit about why
358       // it's not constexpr.
359       if (CD && CD->isInheritingConstructor())
360         S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1)
361           << CD->getInheritedConstructor().getConstructor()->getParent();
362       else
363         S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
364           << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
365       S.Note(DiagDecl->getLocation(), diag::note_declared_at);
366     } else {
367       S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
368     }
369     return false;
370   }
371 
372   return true;
373 }
374 
375 bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) {
376   if (!This.isZero())
377     return true;
378 
379   const SourceInfo &Loc = S.Current->getSource(OpPC);
380 
381   bool IsImplicit = false;
382   if (auto *E = dyn_cast_or_null<CXXThisExpr>(Loc.asExpr()))
383     IsImplicit = E->isImplicit();
384 
385   if (S.getLangOpts().CPlusPlus11)
386     S.FFDiag(Loc, diag::note_constexpr_this) << IsImplicit;
387   else
388     S.FFDiag(Loc);
389 
390   return false;
391 }
392 
393 bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
394   if (!MD->isPure())
395     return true;
396   const SourceInfo &E = S.Current->getSource(OpPC);
397   S.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << MD;
398   S.Note(MD->getLocation(), diag::note_declared_at);
399   return false;
400 }
401 bool Interpret(InterpState &S, APValue &Result) {
402   CodePtr PC = S.Current->getPC();
403 
404   for (;;) {
405     auto Op = PC.read<Opcode>();
406     CodePtr OpPC = PC;
407 
408     switch (Op) {
409 #define GET_INTERP
410 #include "Opcodes.inc"
411 #undef GET_INTERP
412     }
413   }
414 }
415 
416 } // namespace interp
417 } // namespace clang
418