aboutsummaryrefslogtreecommitdiff
path: root/lib/AST/Interp/Interp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/AST/Interp/Interp.cpp')
-rw-r--r--lib/AST/Interp/Interp.cpp417
1 files changed, 417 insertions, 0 deletions
diff --git a/lib/AST/Interp/Interp.cpp b/lib/AST/Interp/Interp.cpp
new file mode 100644
index 000000000000..1a8109cedf76
--- /dev/null
+++ b/lib/AST/Interp/Interp.cpp
@@ -0,0 +1,417 @@
+//===--- InterpState.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) {
+ const auto &Src = S.Current->getSource(OpPC);
+ if (Ptr.isZero()) {
+
+ 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()) {
+ 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, Function *F) {
+ const SourceLocation &Loc = S.Current->getLocation(OpPC);
+
+ if (F->isVirtual()) {
+ if (!S.getLangOpts().CPlusPlus2a) {
+ S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
+ return false;
+ }
+ }
+
+ if (!F->isConstexpr()) {
+ 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;
+}
+bool Interpret(InterpState &S, APValue &Result) {
+ CodePtr PC = S.Current->getPC();
+
+ for (;;) {
+ auto Op = PC.read<Opcode>();
+ CodePtr OpPC = PC;
+
+ switch (Op) {
+#define GET_INTERP
+#include "Opcodes.inc"
+#undef GET_INTERP
+ }
+ }
+}
+
+} // namespace interp
+} // namespace clang