123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- //===------- Interp.cpp - Interpreter for the constexpr VM ------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "Interp.h"
- #include <limits>
- #include <vector>
- #include "Function.h"
- #include "InterpFrame.h"
- #include "InterpStack.h"
- #include "Opcode.h"
- #include "PrimType.h"
- #include "Program.h"
- #include "State.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/AST/ASTDiagnostic.h"
- #include "clang/AST/CXXInheritance.h"
- #include "clang/AST/Expr.h"
- #include "clang/AST/ExprCXX.h"
- #include "llvm/ADT/APSInt.h"
- using namespace clang;
- using namespace clang::interp;
- //===----------------------------------------------------------------------===//
- // Ret
- //===----------------------------------------------------------------------===//
- template <PrimType Name, class T = typename PrimConv<Name>::T>
- static bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
- S.CallStackDepth--;
- const T &Ret = S.Stk.pop<T>();
- assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
- if (!S.checkingPotentialConstantExpression())
- S.Current->popArgs();
- if (InterpFrame *Caller = S.Current->Caller) {
- PC = S.Current->getRetPC();
- delete S.Current;
- S.Current = Caller;
- S.Stk.push<T>(Ret);
- } else {
- delete S.Current;
- S.Current = nullptr;
- if (!ReturnValue<T>(Ret, Result))
- return false;
- }
- return true;
- }
- static bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
- S.CallStackDepth--;
- assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
- if (!S.checkingPotentialConstantExpression())
- S.Current->popArgs();
- if (InterpFrame *Caller = S.Current->Caller) {
- PC = S.Current->getRetPC();
- delete S.Current;
- S.Current = Caller;
- } else {
- delete S.Current;
- S.Current = nullptr;
- }
- return true;
- }
- static bool RetValue(InterpState &S, CodePtr &Pt, APValue &Result) {
- llvm::report_fatal_error("Interpreter cannot return values");
- }
- //===----------------------------------------------------------------------===//
- // Jmp, Jt, Jf
- //===----------------------------------------------------------------------===//
- static bool Jmp(InterpState &S, CodePtr &PC, int32_t Offset) {
- PC += Offset;
- return true;
- }
- static bool Jt(InterpState &S, CodePtr &PC, int32_t Offset) {
- if (S.Stk.pop<bool>()) {
- PC += Offset;
- }
- return true;
- }
- static bool Jf(InterpState &S, CodePtr &PC, int32_t Offset) {
- if (!S.Stk.pop<bool>()) {
- PC += Offset;
- }
- return true;
- }
- static bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- AccessKinds AK) {
- if (Ptr.isInitialized())
- return true;
- if (!S.checkingPotentialConstantExpression()) {
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_access_uninit) << AK << false;
- }
- return false;
- }
- static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- AccessKinds AK) {
- if (Ptr.isActive())
- return true;
- // Get the inactive field descriptor.
- const FieldDecl *InactiveField = Ptr.getField();
- // Walk up the pointer chain to find the union which is not active.
- Pointer U = Ptr.getBase();
- while (!U.isActive()) {
- U = U.getBase();
- }
- // Find the active field of the union.
- Record *R = U.getRecord();
- assert(R && R->isUnion() && "Not a union");
- const FieldDecl *ActiveField = nullptr;
- for (unsigned I = 0, N = R->getNumFields(); I < N; ++I) {
- const Pointer &Field = U.atField(R->getField(I)->Offset);
- if (Field.isActive()) {
- ActiveField = Field.getField();
- break;
- }
- }
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_access_inactive_union_member)
- << AK << InactiveField << !ActiveField << ActiveField;
- return false;
- }
- static bool CheckTemporary(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- AccessKinds AK) {
- if (auto ID = Ptr.getDeclID()) {
- if (!Ptr.isStaticTemporary())
- return true;
- if (Ptr.getDeclDesc()->getType().isConstQualified())
- return true;
- if (S.P.getCurrentDecl() == ID)
- return true;
- const SourceInfo &E = S.Current->getSource(OpPC);
- S.FFDiag(E, diag::note_constexpr_access_static_temporary, 1) << AK;
- S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
- return false;
- }
- return true;
- }
- static bool CheckGlobal(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- if (auto ID = Ptr.getDeclID()) {
- if (!Ptr.isStatic())
- return true;
- if (S.P.getCurrentDecl() == ID)
- return true;
- S.FFDiag(S.Current->getLocation(OpPC), diag::note_constexpr_modify_global);
- return false;
- }
- return true;
- }
- namespace clang {
- namespace interp {
- bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- if (!Ptr.isExtern())
- return true;
- if (!S.checkingPotentialConstantExpression()) {
- auto *VD = Ptr.getDeclDesc()->asValueDecl();
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_ltor_non_constexpr, 1) << VD;
- S.Note(VD->getLocation(), diag::note_declared_at);
- }
- return false;
- }
- bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- if (!Ptr.isUnknownSizeArray())
- return true;
- const SourceInfo &E = S.Current->getSource(OpPC);
- S.FFDiag(E, diag::note_constexpr_unsized_array_indexed);
- return false;
- }
- bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- AccessKinds AK) {
- if (Ptr.isZero()) {
- const auto &Src = S.Current->getSource(OpPC);
- if (Ptr.isField())
- S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field;
- else
- S.FFDiag(Src, diag::note_constexpr_access_null) << AK;
- return false;
- }
- if (!Ptr.isLive()) {
- const auto &Src = S.Current->getSource(OpPC);
- bool IsTemp = Ptr.isTemporary();
- S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
- if (IsTemp)
- S.Note(Ptr.getDeclLoc(), diag::note_constexpr_temporary_here);
- else
- S.Note(Ptr.getDeclLoc(), diag::note_declared_at);
- return false;
- }
- return true;
- }
- bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- CheckSubobjectKind CSK) {
- if (!Ptr.isZero())
- return true;
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_null_subobject) << CSK;
- return false;
- }
- bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- AccessKinds AK) {
- if (!Ptr.isOnePastEnd())
- return true;
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_access_past_end) << AK;
- return false;
- }
- bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
- CheckSubobjectKind CSK) {
- if (!Ptr.isElementPastEnd())
- return true;
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_past_end_subobject) << CSK;
- return false;
- }
- bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- assert(Ptr.isLive() && "Pointer is not live");
- if (!Ptr.isConst()) {
- return true;
- }
- const QualType Ty = Ptr.getType();
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;
- return false;
- }
- bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- assert(Ptr.isLive() && "Pointer is not live");
- if (!Ptr.isMutable()) {
- return true;
- }
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- const FieldDecl *Field = Ptr.getField();
- S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read << Field;
- S.Note(Field->getLocation(), diag::note_declared_at);
- return false;
- }
- bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- if (!CheckLive(S, OpPC, Ptr, AK_Read))
- return false;
- if (!CheckExtern(S, OpPC, Ptr))
- return false;
- if (!CheckRange(S, OpPC, Ptr, AK_Read))
- return false;
- if (!CheckInitialized(S, OpPC, Ptr, AK_Read))
- return false;
- if (!CheckActive(S, OpPC, Ptr, AK_Read))
- return false;
- if (!CheckTemporary(S, OpPC, Ptr, AK_Read))
- return false;
- if (!CheckMutable(S, OpPC, Ptr))
- return false;
- return true;
- }
- bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- if (!CheckLive(S, OpPC, Ptr, AK_Assign))
- return false;
- if (!CheckExtern(S, OpPC, Ptr))
- return false;
- if (!CheckRange(S, OpPC, Ptr, AK_Assign))
- return false;
- if (!CheckGlobal(S, OpPC, Ptr))
- return false;
- if (!CheckConst(S, OpPC, Ptr))
- return false;
- return true;
- }
- bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- if (!CheckLive(S, OpPC, Ptr, AK_MemberCall))
- return false;
- if (!CheckExtern(S, OpPC, Ptr))
- return false;
- if (!CheckRange(S, OpPC, Ptr, AK_MemberCall))
- return false;
- return true;
- }
- bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
- if (!CheckLive(S, OpPC, Ptr, AK_Assign))
- return false;
- if (!CheckRange(S, OpPC, Ptr, AK_Assign))
- return false;
- return true;
- }
- bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
- if (F->isVirtual()) {
- if (!S.getLangOpts().CPlusPlus20) {
- const SourceLocation &Loc = S.Current->getLocation(OpPC);
- S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
- return false;
- }
- }
- if (!F->isConstexpr()) {
- const SourceLocation &Loc = S.Current->getLocation(OpPC);
- if (S.getLangOpts().CPlusPlus11) {
- const FunctionDecl *DiagDecl = F->getDecl();
- // If this function is not constexpr because it is an inherited
- // non-constexpr constructor, diagnose that directly.
- auto *CD = dyn_cast<CXXConstructorDecl>(DiagDecl);
- if (CD && CD->isInheritingConstructor()) {
- auto *Inherited = CD->getInheritedConstructor().getConstructor();
- if (!Inherited->isConstexpr())
- DiagDecl = CD = Inherited;
- }
- // FIXME: If DiagDecl is an implicitly-declared special member function
- // or an inheriting constructor, we should be much more explicit about why
- // it's not constexpr.
- if (CD && CD->isInheritingConstructor())
- S.FFDiag(Loc, diag::note_constexpr_invalid_inhctor, 1)
- << CD->getInheritedConstructor().getConstructor()->getParent();
- else
- S.FFDiag(Loc, diag::note_constexpr_invalid_function, 1)
- << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
- S.Note(DiagDecl->getLocation(), diag::note_declared_at);
- } else {
- S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
- }
- return false;
- }
- return true;
- }
- bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This) {
- if (!This.isZero())
- return true;
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- bool IsImplicit = false;
- if (auto *E = dyn_cast_or_null<CXXThisExpr>(Loc.asExpr()))
- IsImplicit = E->isImplicit();
- if (S.getLangOpts().CPlusPlus11)
- S.FFDiag(Loc, diag::note_constexpr_this) << IsImplicit;
- else
- S.FFDiag(Loc);
- return false;
- }
- bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
- if (!MD->isPure())
- return true;
- const SourceInfo &E = S.Current->getSource(OpPC);
- S.FFDiag(E, diag::note_constexpr_pure_virtual_call, 1) << MD;
- S.Note(MD->getLocation(), diag::note_declared_at);
- return false;
- }
- static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI,
- QualType SubObjType,
- SourceLocation SubObjLoc) {
- S.FFDiag(SI, diag::note_constexpr_uninitialized) << true << SubObjType;
- if (SubObjLoc.isValid())
- S.Note(SubObjLoc, diag::note_constexpr_subobject_declared_here);
- }
- static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
- const Pointer &BasePtr, const Record *R);
- static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC,
- const Pointer &BasePtr,
- const ConstantArrayType *CAT) {
- bool Result = true;
- size_t NumElems = CAT->getSize().getZExtValue();
- QualType ElemType = CAT->getElementType();
- if (isa<RecordType>(ElemType.getTypePtr())) {
- const Record *R = BasePtr.getElemRecord();
- for (size_t I = 0; I != NumElems; ++I) {
- Pointer ElemPtr = BasePtr.atIndex(I).narrow();
- Result &= CheckFieldsInitialized(S, OpPC, ElemPtr, R);
- }
- } else if (auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
- for (size_t I = 0; I != NumElems; ++I) {
- Pointer ElemPtr = BasePtr.atIndex(I).narrow();
- Result &= CheckArrayInitialized(S, OpPC, ElemPtr, ElemCAT);
- }
- } else {
- for (size_t I = 0; I != NumElems; ++I) {
- if (!BasePtr.atIndex(I).isInitialized()) {
- DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType,
- BasePtr.getFieldDesc()->getLocation());
- Result = false;
- }
- }
- }
- return Result;
- }
- static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
- const Pointer &BasePtr, const Record *R) {
- assert(R);
- bool Result = true;
- // Check all fields of this record are initialized.
- for (const Record::Field &F : R->fields()) {
- Pointer FieldPtr = BasePtr.atField(F.Offset);
- QualType FieldType = F.Decl->getType();
- if (FieldType->isRecordType()) {
- Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord());
- } else if (FieldType->isArrayType()) {
- const auto *CAT =
- cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
- Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT);
- } else if (!FieldPtr.isInitialized()) {
- DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC),
- F.Decl->getType(), F.Decl->getLocation());
- Result = false;
- }
- }
- return Result;
- }
- bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This) {
- assert(!This.isZero());
- const Record *R = This.getRecord();
- return CheckFieldsInitialized(S, OpPC, This, R);
- }
- bool Interpret(InterpState &S, APValue &Result) {
- // The current stack frame when we started Interpret().
- // This is being used by the ops to determine wheter
- // to return from this function and thus terminate
- // interpretation.
- const InterpFrame *StartFrame = S.Current;
- assert(!S.Current->isRoot());
- CodePtr PC = S.Current->getPC();
- // Empty program.
- if (!PC)
- return true;
- for (;;) {
- auto Op = PC.read<Opcode>();
- CodePtr OpPC = PC;
- switch (Op) {
- #define GET_INTERP
- #include "Opcodes.inc"
- #undef GET_INTERP
- }
- }
- }
- } // namespace interp
- } // namespace clang
|