xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp (revision 7ef62cebc2f965b0f640263e179276928885e33d)
1 //===------- Interp.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   if (Ptr.isZero()) {
205     const auto &Src = S.Current->getSource(OpPC);
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     const auto &Src = S.Current->getSource(OpPC);
217     bool IsTemp = Ptr.isTemporary();
218 
219     S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
220 
221     if (IsTemp)
222       S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
223     else
224       S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
225 
226     return false;
227   }
228 
229   return true;
230 }
231 
232 bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
233                CheckSubobjectKind CSK) {
234   if (!Ptr.isZero())
235     return true;
236   const SourceInfo &Loc = S.Current->getSource(OpPC);
237   S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK;
238   return false;
239 }
240 
241 bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
242                 AccessKinds AK) {
243   if (!Ptr.isOnePastEnd())
244     return true;
245   const SourceInfo &Loc = S.Current->getSource(OpPC);
246   S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK;
247   return false;
248 }
249 
250 bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
251                 CheckSubobjectKind CSK) {
252   if (!Ptr.isElementPastEnd())
253     return true;
254   const SourceInfo &Loc = S.Current->getSource(OpPC);
255   S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK;
256   return false;
257 }
258 
259 bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
260   assert(Ptr.isLive() && "Pointer is not live");
261   if (!Ptr.isConst()) {
262     return true;
263   }
264 
265   const QualType Ty = Ptr.getType();
266   const SourceInfo &Loc = S.Current->getSource(OpPC);
267   S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;
268   return false;
269 }
270 
271 bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
272   assert(Ptr.isLive() && "Pointer is not live");
273   if (!Ptr.isMutable()) {
274     return true;
275   }
276 
277   const SourceInfo &Loc = S.Current->getSource(OpPC);
278   const FieldDecl *Field = Ptr.getField();
279   S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;
280   S.Note(Field->getLocation(), diag::note_declared_at);
281   return false;
282 }
283 
284 bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
285   if (!CheckLive(S, OpPC, Ptr, AK_Read))
286     return false;
287   if (!CheckExtern(S, OpPC, Ptr))
288     return false;
289   if (!CheckRange(S, OpPC, Ptr, AK_Read))
290     return false;
291   if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
292     return false;
293   if (!CheckActive(S, OpPC, Ptr, AK_Read))
294     return false;
295   if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
296     return false;
297   if (!CheckMutable(S, OpPC, Ptr))
298     return false;
299   return true;
300 }
301 
302 bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
303   if (!CheckLive(S, OpPC, Ptr, AK_Assign))
304     return false;
305   if (!CheckExtern(S, OpPC, Ptr))
306     return false;
307   if (!CheckRange(S, OpPC, Ptr, AK_Assign))
308     return false;
309   if (!CheckGlobal(S, OpPC, Ptr))
310     return false;
311   if (!CheckConst(S, OpPC, Ptr))
312     return false;
313   return true;
314 }
315 
316 bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
317   if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
318     return false;
319   if (!CheckExtern(S, OpPC, Ptr))
320     return false;
321   if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
322     return false;
323   return true;
324 }
325 
326 bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
327   if (!CheckLive(S, OpPC, Ptr, AK_Assign))
328     return false;
329   if (!CheckRange(S, OpPC, Ptr, AK_Assign))
330     return false;
331   return true;
332 }
333 
334 bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
335 
336   if (F->isVirtual()) {
337     if (!S.getLangOpts().CPlusPlus20) {
338       const SourceLocation &Loc = S.Current->getLocation(OpPC);
339       S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
340       return false;
341     }
342   }
343 
344   if (!F->isConstexpr()) {
345     const SourceLocation &Loc = S.Current->getLocation(OpPC);
346     if (S.getLangOpts().CPlusPlus11) {
347       const FunctionDecl *DiagDecl = F->getDecl();
348 
349       // If this function is not constexpr because it is an inherited
350       // non-constexpr constructor, diagnose that directly.
351       auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
352       if (CD && CD->isInheritingConstructor()) {
353         auto *Inherited = CD->getInheritedConstructor().getConstructor();
354         if (!Inherited->isConstexpr())
355           DiagDecl = CD = Inherited;
356       }
357 
358       // FIXME: If DiagDecl is an implicitly-declared special member function
359       // or an inheriting constructor, we should be much more explicit about why
360       // it's not constexpr.
361       if (CD && CD->isInheritingConstructor())
362         S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1)
363           << CD->getInheritedConstructor().getConstructor()->getParent();
364       else
365         S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
366           << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
367       S.Note(DiagDecl->getLocation(), diag::note_declared_at);
368     } else {
369       S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
370     }
371     return false;
372   }
373 
374   return true;
375 }
376 
377 bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) {
378   if (!This.isZero())
379     return true;
380 
381   const SourceInfo &Loc = S.Current->getSource(OpPC);
382 
383   bool IsImplicit = false;
384   if (auto *E = dyn_cast_or_null<CXXThisExpr>(Loc.asExpr()))
385     IsImplicit = E->isImplicit();
386 
387   if (S.getLangOpts().CPlusPlus11)
388     S.FFDiag(Loc, diag::note_constexpr_this) << IsImplicit;
389   else
390     S.FFDiag(Loc);
391 
392   return false;
393 }
394 
395 bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
396   if (!MD->isPure())
397     return true;
398   const SourceInfo &E = S.Current->getSource(OpPC);
399   S.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << MD;
400   S.Note(MD->getLocation(), diag::note_declared_at);
401   return false;
402 }
403 
404 static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI,
405                                            QualType SubObjType,
406                                            SourceLocation SubObjLoc) {
407   S.FFDiag(SI, diag::note_constexpr_uninitialized) << true << SubObjType;
408   if (SubObjLoc.isValid())
409     S.Note(SubObjLoc, diag::note_constexpr_subobject_declared_here);
410 }
411 
412 static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
413                                    const Pointer &BasePtr, const Record *R);
414 
415 static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC,
416                                   const Pointer &BasePtr,
417                                   const ConstantArrayType *CAT) {
418   bool Result = true;
419   size_t NumElems = CAT->getSize().getZExtValue();
420   QualType ElemType = CAT->getElementType();
421 
422   if (isa<RecordType>(ElemType.getTypePtr())) {
423     const Record *R = BasePtr.getElemRecord();
424     for (size_t I = 0; I != NumElems; ++I) {
425       Pointer ElemPtr = BasePtr.atIndex(I).narrow();
426       Result &= CheckFieldsInitialized(S, OpPC, ElemPtr, R);
427     }
428   } else if (auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
429     for (size_t I = 0; I != NumElems; ++I) {
430       Pointer ElemPtr = BasePtr.atIndex(I).narrow();
431       Result &= CheckArrayInitialized(S, OpPC, ElemPtr, ElemCAT);
432     }
433   } else {
434     for (size_t I = 0; I != NumElems; ++I) {
435       if (!BasePtr.atIndex(I).isInitialized()) {
436         DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType,
437                                        BasePtr.getFieldDesc()->getLocation());
438         Result = false;
439       }
440     }
441   }
442 
443   return Result;
444 }
445 
446 static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
447                                    const Pointer &BasePtr, const Record *R) {
448   assert(R);
449   bool Result = true;
450   // Check all fields of this record are initialized.
451   for (const Record::Field &F : R->fields()) {
452     Pointer FieldPtr = BasePtr.atField(F.Offset);
453     QualType FieldType = F.Decl->getType();
454 
455     if (FieldType->isRecordType()) {
456       Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord());
457     } else if (FieldType->isArrayType()) {
458       const auto *CAT =
459           cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
460       Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT);
461     } else if (!FieldPtr.isInitialized()) {
462       DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC),
463                                      F.Decl->getType(), F.Decl->getLocation());
464       Result = false;
465     }
466   }
467   return Result;
468 }
469 
470 bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This) {
471   assert(!This.isZero());
472   const Record *R = This.getRecord();
473   return CheckFieldsInitialized(S, OpPC, This, R);
474 }
475 
476 bool Interpret(InterpState &S, APValue &Result) {
477   // The current stack frame when we started Interpret().
478   // This is being used by the ops to determine wheter
479   // to return from this function and thus terminate
480   // interpretation.
481   const InterpFrame *StartFrame = S.Current;
482   assert(!S.Current->isRoot());
483   CodePtr PC = S.Current->getPC();
484 
485   // Empty program.
486   if (!PC)
487     return true;
488 
489   for (;;) {
490     auto Op = PC.read<Opcode>();
491     CodePtr OpPC = PC;
492 
493     switch (Op) {
494 #define GET_INTERP
495 #include "Opcodes.inc"
496 #undef GET_INTERP
497     }
498   }
499 }
500 
501 } // namespace interp
502 } // namespace clang
503