xref: /freebsd/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp (revision 59c8e88e72633afbc47a4ace0d2170d00d51f7dc)
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 static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result) {
30   llvm::report_fatal_error("Interpreter cannot return values");
31 }
32 
33 //===----------------------------------------------------------------------===//
34 // Jmp, Jt, Jf
35 //===----------------------------------------------------------------------===//
36 
37 static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) {
38   PC += Offset;
39   return true;
40 }
41 
42 static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) {
43   if (S.Stk.pop<bool>()) {
44     PC += Offset;
45   }
46   return true;
47 }
48 
49 static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
50   if (!S.Stk.pop<bool>()) {
51     PC += Offset;
52   }
53   return true;
54 }
55 
56 static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
57                         AccessKinds AK) {
58   if (Ptr.isActive())
59     return true;
60 
61   // Get the inactive field descriptor.
62   const FieldDecl *InactiveField = Ptr.getField();
63 
64   // Walk up the pointer chain to find the union which is not active.
65   Pointer U = Ptr.getBase();
66   while (!U.isActive()) {
67     U = U.getBase();
68   }
69 
70   // Find the active field of the union.
71   const Record *R = U.getRecord();
72   assert(R && R->isUnion() && "Not a union");
73   const FieldDecl *ActiveField = nullptr;
74   for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
75     const Pointer &Field = U.atField(R->getField(I)->Offset);
76     if (Field.isActive()) {
77       ActiveField = Field.getField();
78       break;
79     }
80   }
81 
82   const SourceInfo &Loc = S.Current->getSource(OpPC);
83   S.FFDiag(Loc, diag::note_constexpr_access_inactive_union_member)
84       << AK << InactiveField << !ActiveField << ActiveField;
85   return false;
86 }
87 
88 static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
89                            AccessKinds AK) {
90   if (auto ID = Ptr.getDeclID()) {
91     if (!Ptr.isStaticTemporary())
92       return true;
93 
94     if (Ptr.getDeclDesc()->getType().isConstQualified())
95       return true;
96 
97     if (S.P.getCurrentDecl() == ID)
98       return true;
99 
100     const SourceInfo &E = S.Current->getSource(OpPC);
101     S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
102     S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
103     return false;
104   }
105   return true;
106 }
107 
108 static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
109   if (auto ID = Ptr.getDeclID()) {
110     if (!Ptr.isStatic())
111       return true;
112 
113     if (S.P.getCurrentDecl() == ID)
114       return true;
115 
116     S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_modify_global);
117     return false;
118   }
119   return true;
120 }
121 
122 namespace clang {
123 namespace interp {
124 
125 bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
126   if (!Ptr.isExtern())
127     return true;
128 
129   if (!S.checkingPotentialConstantExpression()) {
130     const auto *VD = Ptr.getDeclDesc()->asValueDecl();
131     const SourceInfo &Loc = S.Current->getSource(OpPC);
132     S.FFDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
133     S.Note(VD->getLocation(), diag::note_declared_at);
134   }
135   return false;
136 }
137 
138 bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
139   if (!Ptr.isUnknownSizeArray())
140     return true;
141   const SourceInfo &E = S.Current->getSource(OpPC);
142   S.FFDiag(E, diag::note_constexpr_unsized_array_indexed);
143   return false;
144 }
145 
146 bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
147                AccessKinds AK) {
148   if (Ptr.isZero()) {
149     const auto &Src = S.Current->getSource(OpPC);
150 
151     if (Ptr.isField())
152       S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field;
153     else
154       S.FFDiag(Src, diag::note_constexpr_access_null) << AK;
155 
156     return false;
157   }
158 
159   if (!Ptr.isLive()) {
160     const auto &Src = S.Current->getSource(OpPC);
161     bool IsTemp = Ptr.isTemporary();
162 
163     S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
164 
165     if (IsTemp)
166       S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
167     else
168       S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
169 
170     return false;
171   }
172 
173   return true;
174 }
175 
176 bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
177                CheckSubobjectKind CSK) {
178   if (!Ptr.isZero())
179     return true;
180   const SourceInfo &Loc = S.Current->getSource(OpPC);
181   S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK;
182   return false;
183 }
184 
185 bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
186                 AccessKinds AK) {
187   if (!Ptr.isOnePastEnd())
188     return true;
189   const SourceInfo &Loc = S.Current->getSource(OpPC);
190   S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK;
191   return false;
192 }
193 
194 bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
195                 CheckSubobjectKind CSK) {
196   if (!Ptr.isElementPastEnd())
197     return true;
198   const SourceInfo &Loc = S.Current->getSource(OpPC);
199   S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK;
200   return false;
201 }
202 
203 bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
204   assert(Ptr.isLive() && "Pointer is not live");
205   if (!Ptr.isConst())
206     return true;
207 
208   // The This pointer is writable in constructors and destructors,
209   // even if isConst() returns true.
210   if (const Function *Func = S.Current->getFunction();
211       Func && (Func->isConstructor() || Func->isDestructor()) &&
212       Ptr.block() == S.Current->getThis().block()) {
213     return true;
214   }
215 
216   const QualType Ty = Ptr.getType();
217   const SourceInfo &Loc = S.Current->getSource(OpPC);
218   S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;
219   return false;
220 }
221 
222 bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
223   assert(Ptr.isLive() && "Pointer is not live");
224   if (!Ptr.isMutable()) {
225     return true;
226   }
227 
228   const SourceInfo &Loc = S.Current->getSource(OpPC);
229   const FieldDecl *Field = Ptr.getField();
230   S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;
231   S.Note(Field->getLocation(), diag::note_declared_at);
232   return false;
233 }
234 
235 bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
236                       AccessKinds AK) {
237   if (Ptr.isInitialized())
238     return true;
239 
240   if (!S.checkingPotentialConstantExpression()) {
241     const SourceInfo &Loc = S.Current->getSource(OpPC);
242     S.FFDiag(Loc, diag::note_constexpr_access_uninit)
243         << AK << /*uninitialized=*/true;
244   }
245   return false;
246 }
247 
248 bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
249   if (!CheckLive(S, OpPC, Ptr, AK_Read))
250     return false;
251   if (!CheckExtern(S, OpPC, Ptr))
252     return false;
253   if (!CheckRange(S, OpPC, Ptr, AK_Read))
254     return false;
255   if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
256     return false;
257   if (!CheckActive(S, OpPC, Ptr, AK_Read))
258     return false;
259   if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
260     return false;
261   if (!CheckMutable(S, OpPC, Ptr))
262     return false;
263   return true;
264 }
265 
266 bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
267   if (!CheckLive(S, OpPC, Ptr, AK_Assign))
268     return false;
269   if (!CheckExtern(S, OpPC, Ptr))
270     return false;
271   if (!CheckRange(S, OpPC, Ptr, AK_Assign))
272     return false;
273   if (!CheckGlobal(S, OpPC, Ptr))
274     return false;
275   if (!CheckConst(S, OpPC, Ptr))
276     return false;
277   return true;
278 }
279 
280 bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
281   if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
282     return false;
283   if (!CheckExtern(S, OpPC, Ptr))
284     return false;
285   if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
286     return false;
287   return true;
288 }
289 
290 bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
291   if (!CheckLive(S, OpPC, Ptr, AK_Assign))
292     return false;
293   if (!CheckRange(S, OpPC, Ptr, AK_Assign))
294     return false;
295   return true;
296 }
297 
298 bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
299 
300   if (F->isVirtual() && !S.getLangOpts().CPlusPlus20) {
301     const SourceLocation &Loc = S.Current->getLocation(OpPC);
302     S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
303     return false;
304   }
305 
306   if (!F->isConstexpr()) {
307     // Don't emit anything if we're checking for a potential constant
308     // expression. That will happen later when actually executing.
309     if (S.checkingPotentialConstantExpression())
310       return false;
311 
312     const SourceLocation &Loc = S.Current->getLocation(OpPC);
313     if (S.getLangOpts().CPlusPlus11) {
314       const FunctionDecl *DiagDecl = F->getDecl();
315 
316       // If this function is not constexpr because it is an inherited
317       // non-constexpr constructor, diagnose that directly.
318       const auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
319       if (CD && CD->isInheritingConstructor()) {
320         const auto *Inherited = CD->getInheritedConstructor().getConstructor();
321         if (!Inherited->isConstexpr())
322           DiagDecl = CD = Inherited;
323       }
324 
325       // FIXME: If DiagDecl is an implicitly-declared special member function
326       // or an inheriting constructor, we should be much more explicit about why
327       // it's not constexpr.
328       if (CD && CD->isInheritingConstructor())
329         S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1)
330           << CD->getInheritedConstructor().getConstructor()->getParent();
331       else
332         S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
333           << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
334       S.Note(DiagDecl->getLocation(), diag::note_declared_at);
335     } else {
336       S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
337     }
338     return false;
339   }
340 
341   return true;
342 }
343 
344 bool CheckCallDepth(InterpState &S, CodePtr OpPC) {
345   if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) {
346     S.FFDiag(S.Current->getSource(OpPC),
347              diag::note_constexpr_depth_limit_exceeded)
348         << S.getLangOpts().ConstexprCallDepth;
349     return false;
350   }
351 
352   return true;
353 }
354 
355 bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) {
356   if (!This.isZero())
357     return true;
358 
359   const SourceInfo &Loc = S.Current->getSource(OpPC);
360 
361   bool IsImplicit = false;
362   if (const auto *E = dyn_cast_if_present<CXXThisExpr>(Loc.asExpr()))
363     IsImplicit = E->isImplicit();
364 
365   if (S.getLangOpts().CPlusPlus11)
366     S.FFDiag(Loc, diag::note_constexpr_this) << IsImplicit;
367   else
368     S.FFDiag(Loc);
369 
370   return false;
371 }
372 
373 bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
374   if (!MD->isPure())
375     return true;
376   const SourceInfo &E = S.Current->getSource(OpPC);
377   S.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << MD;
378   S.Note(MD->getLocation(), diag::note_declared_at);
379   return false;
380 }
381 
382 static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI,
383                                            const FieldDecl *SubObjDecl) {
384   assert(SubObjDecl && "Subobject declaration does not exist");
385   S.FFDiag(SI, diag::note_constexpr_uninitialized)
386       << /*(name)*/ 1 << SubObjDecl;
387   S.Note(SubObjDecl->getLocation(),
388          diag::note_constexpr_subobject_declared_here);
389 }
390 
391 static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
392                                    const Pointer &BasePtr, const Record *R);
393 
394 static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC,
395                                   const Pointer &BasePtr,
396                                   const ConstantArrayType *CAT) {
397   bool Result = true;
398   size_t NumElems = CAT->getSize().getZExtValue();
399   QualType ElemType = CAT->getElementType();
400 
401   if (ElemType->isRecordType()) {
402     const Record *R = BasePtr.getElemRecord();
403     for (size_t I = 0; I != NumElems; ++I) {
404       Pointer ElemPtr = BasePtr.atIndex(I).narrow();
405       Result &= CheckFieldsInitialized(S, OpPC, ElemPtr, R);
406     }
407   } else if (const auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
408     for (size_t I = 0; I != NumElems; ++I) {
409       Pointer ElemPtr = BasePtr.atIndex(I).narrow();
410       Result &= CheckArrayInitialized(S, OpPC, ElemPtr, ElemCAT);
411     }
412   } else {
413     for (size_t I = 0; I != NumElems; ++I) {
414       if (!BasePtr.atIndex(I).isInitialized()) {
415         DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC),
416                                        BasePtr.getField());
417         Result = false;
418       }
419     }
420   }
421 
422   return Result;
423 }
424 
425 static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
426                                    const Pointer &BasePtr, const Record *R) {
427   assert(R);
428   bool Result = true;
429   // Check all fields of this record are initialized.
430   for (const Record::Field &F : R->fields()) {
431     Pointer FieldPtr = BasePtr.atField(F.Offset);
432     QualType FieldType = F.Decl->getType();
433 
434     if (FieldType->isRecordType()) {
435       Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord());
436     } else if (FieldType->isArrayType()) {
437       const auto *CAT =
438           cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
439       Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT);
440     } else if (!FieldPtr.isInitialized()) {
441       DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), F.Decl);
442       Result = false;
443     }
444   }
445 
446   // Check Fields in all bases
447   for (const Record::Base &B : R->bases()) {
448     Pointer P = BasePtr.atField(B.Offset);
449     Result &= CheckFieldsInitialized(S, OpPC, P, B.R);
450   }
451 
452   // TODO: Virtual bases
453 
454   return Result;
455 }
456 
457 bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This) {
458   assert(!This.isZero());
459   if (const Record *R = This.getRecord())
460     return CheckFieldsInitialized(S, OpPC, This, R);
461   const auto *CAT =
462       cast<ConstantArrayType>(This.getType()->getAsArrayTypeUnsafe());
463   return CheckArrayInitialized(S, OpPC, This, CAT);
464 }
465 
466 bool CheckFloatResult(InterpState &S, CodePtr OpPC, APFloat::opStatus Status) {
467   // In a constant context, assume that any dynamic rounding mode or FP
468   // exception state matches the default floating-point environment.
469   if (S.inConstantContext())
470     return true;
471 
472   const SourceInfo &E = S.Current->getSource(OpPC);
473   FPOptions FPO = E.asExpr()->getFPFeaturesInEffect(S.Ctx.getLangOpts());
474 
475   if ((Status & APFloat::opInexact) &&
476       FPO.getRoundingMode() == llvm::RoundingMode::Dynamic) {
477     // Inexact result means that it depends on rounding mode. If the requested
478     // mode is dynamic, the evaluation cannot be made in compile time.
479     S.FFDiag(E, diag::note_constexpr_dynamic_rounding);
480     return false;
481   }
482 
483   if ((Status != APFloat::opOK) &&
484       (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic ||
485        FPO.getExceptionMode() != LangOptions::FPE_Ignore ||
486        FPO.getAllowFEnvAccess())) {
487     S.FFDiag(E, diag::note_constexpr_float_arithmetic_strict);
488     return false;
489   }
490 
491   if ((Status & APFloat::opStatus::opInvalidOp) &&
492       FPO.getExceptionMode() != LangOptions::FPE_Ignore) {
493     // There is no usefully definable result.
494     S.FFDiag(E);
495     return false;
496   }
497 
498   return true;
499 }
500 
501 bool Interpret(InterpState &S, APValue &Result) {
502   // The current stack frame when we started Interpret().
503   // This is being used by the ops to determine wheter
504   // to return from this function and thus terminate
505   // interpretation.
506   const InterpFrame *StartFrame = S.Current;
507   assert(!S.Current->isRoot());
508   CodePtr PC = S.Current->getPC();
509 
510   // Empty program.
511   if (!PC)
512     return true;
513 
514   for (;;) {
515     auto Op = PC.read<Opcode>();
516     CodePtr OpPC = PC;
517 
518     switch (Op) {
519 #define GET_INTERP
520 #include "Opcodes.inc"
521 #undef GET_INTERP
522     }
523   }
524 }
525 
526 } // namespace interp
527 } // namespace clang
528