//===--- Interp.h - 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 // //===----------------------------------------------------------------------===// // // Definition of the interpreter state and entry point. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_AST_INTERP_INTERP_H #define LLVM_CLANG_AST_INTERP_INTERP_H #include #include #include "Function.h" #include "InterpFrame.h" #include "InterpStack.h" #include "InterpState.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 "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" #include "llvm/Support/Endian.h" namespace clang { namespace interp { using APInt = llvm::APInt; using APSInt = llvm::APSInt; /// Convers a value to an APValue. template bool ReturnValue(const T &V, APValue &R) { R = V.toAPValue(); return true; } /// Checks if the variable has externally defined storage. bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if the array is offsetable. bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a pointer is live and accesible. bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK); /// Checks if a pointer is null. bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr, CheckSubobjectKind CSK); /// Checks if a pointer is in range. bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, AccessKinds AK); /// Checks if a field from which a pointer is going to be derived is valid. bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr, CheckSubobjectKind CSK); /// Checks if a pointer points to const storage. bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a pointer points to a mutable field. bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a value can be loaded from a block. bool CheckLoad(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a value can be stored in a block. bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a method can be invoked on an object. bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a value can be initialized. bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a method can be called. bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F); /// Checks the 'this' pointer. bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); /// Checks if a method is pure virtual. bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD); template inline bool IsTrue(const T &V) { return !V.isZero(); } //===----------------------------------------------------------------------===// // Add, Sub, Mul //===----------------------------------------------------------------------===// template class OpAP> bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS, const T &RHS) { // Fast path - add the numbers with fixed width. T Result; if (!OpFW(LHS, RHS, Bits, &Result)) { S.Stk.push(Result); return true; } // If for some reason evaluation continues, use the truncated results. S.Stk.push(Result); // Slow path - compute the result using another bit of precision. APSInt Value = OpAP()(LHS.toAPSInt(Bits), RHS.toAPSInt(Bits)); // Report undefined behaviour, stopping if required. const Expr *E = S.Current->getExpr(OpPC); QualType Type = E->getType(); if (S.checkingForUndefinedBehavior()) { auto Trunc = Value.trunc(Result.bitWidth()).toString(10); auto Loc = E->getExprLoc(); S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type; return true; } else { S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type; return S.noteUndefinedBehavior(); } } template ::T> bool Add(InterpState &S, CodePtr OpPC) { const T &RHS = S.Stk.pop(); const T &LHS = S.Stk.pop(); const unsigned Bits = RHS.bitWidth() + 1; return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); } template ::T> bool Sub(InterpState &S, CodePtr OpPC) { const T &RHS = S.Stk.pop(); const T &LHS = S.Stk.pop(); const unsigned Bits = RHS.bitWidth() + 1; return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); } template ::T> bool Mul(InterpState &S, CodePtr OpPC) { const T &RHS = S.Stk.pop(); const T &LHS = S.Stk.pop(); const unsigned Bits = RHS.bitWidth() * 2; return AddSubMulHelper(S, OpPC, Bits, LHS, RHS); } //===----------------------------------------------------------------------===// // EQ, NE, GT, GE, LT, LE //===----------------------------------------------------------------------===// using CompareFn = llvm::function_ref; template bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { using BoolT = PrimConv::T; const T &RHS = S.Stk.pop(); const T &LHS = S.Stk.pop(); S.Stk.push(BoolT::from(Fn(LHS.compare(RHS)))); return true; } template bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) { return CmpHelper(S, OpPC, Fn); } template <> inline bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { using BoolT = PrimConv::T; const Pointer &RHS = S.Stk.pop(); const Pointer &LHS = S.Stk.pop(); if (!Pointer::hasSameBase(LHS, RHS)) { const SourceInfo &Loc = S.Current->getSource(OpPC); S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr); return false; } else { unsigned VL = LHS.getByteOffset(); unsigned VR = RHS.getByteOffset(); S.Stk.push(BoolT::from(Fn(Compare(VL, VR)))); return true; } } template <> inline bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) { using BoolT = PrimConv::T; const Pointer &RHS = S.Stk.pop(); const Pointer &LHS = S.Stk.pop(); if (LHS.isZero() && RHS.isZero()) { S.Stk.push(BoolT::from(Fn(ComparisonCategoryResult::Equal))); return true; } if (!Pointer::hasSameBase(LHS, RHS)) { S.Stk.push(BoolT::from(Fn(ComparisonCategoryResult::Unordered))); return true; } else { unsigned VL = LHS.getByteOffset(); unsigned VR = RHS.getByteOffset(); S.Stk.push(BoolT::from(Fn(Compare(VL, VR)))); return true; } } template ::T> bool EQ(InterpState &S, CodePtr OpPC) { return CmpHelperEQ(S, OpPC, [](ComparisonCategoryResult R) { return R == ComparisonCategoryResult::Equal; }); } template ::T> bool NE(InterpState &S, CodePtr OpPC) { return CmpHelperEQ(S, OpPC, [](ComparisonCategoryResult R) { return R != ComparisonCategoryResult::Equal; }); } template ::T> bool LT(InterpState &S, CodePtr OpPC) { return CmpHelper(S, OpPC, [](ComparisonCategoryResult R) { return R == ComparisonCategoryResult::Less; }); } template ::T> bool LE(InterpState &S, CodePtr OpPC) { return CmpHelper(S, OpPC, [](ComparisonCategoryResult R) { return R == ComparisonCategoryResult::Less || R == ComparisonCategoryResult::Equal; }); } template ::T> bool GT(InterpState &S, CodePtr OpPC) { return CmpHelper(S, OpPC, [](ComparisonCategoryResult R) { return R == ComparisonCategoryResult::Greater; }); } template ::T> bool GE(InterpState &S, CodePtr OpPC) { return CmpHelper(S, OpPC, [](ComparisonCategoryResult R) { return R == ComparisonCategoryResult::Greater || R == ComparisonCategoryResult::Equal; }); } //===----------------------------------------------------------------------===// // InRange //===----------------------------------------------------------------------===// template ::T> bool InRange(InterpState &S, CodePtr OpPC) { const T RHS = S.Stk.pop(); const T LHS = S.Stk.pop(); const T Value = S.Stk.pop(); S.Stk.push(LHS <= Value && Value <= RHS); return true; } //===----------------------------------------------------------------------===// // Dup, Pop, Test //===----------------------------------------------------------------------===// template ::T> bool Dup(InterpState &S, CodePtr OpPC) { S.Stk.push(S.Stk.peek()); return true; } template ::T> bool Pop(InterpState &S, CodePtr OpPC) { S.Stk.pop(); return true; } //===----------------------------------------------------------------------===// // Const //===----------------------------------------------------------------------===// template ::T> bool Const(InterpState &S, CodePtr OpPC, const T &Arg) { S.Stk.push(Arg); return true; } //===----------------------------------------------------------------------===// // Get/Set Local/Param/Global/This //===----------------------------------------------------------------------===// template ::T> bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { S.Stk.push(S.Current->getLocal(I)); return true; } template ::T> bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) { S.Current->setLocal(I, S.Stk.pop()); return true; } template ::T> bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) { if (S.checkingPotentialConstantExpression()) { return false; } S.Stk.push(S.Current->getParam(I)); return true; } template ::T> bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) { S.Current->setParam(I, S.Stk.pop()); return true; } template ::T> bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Obj = S.Stk.peek(); if (!CheckNull(S, OpPC, Obj, CSK_Field)) return false; if (!CheckRange(S, OpPC, Obj, CSK_Field)) return false; const Pointer &Field = Obj.atField(I); if (!CheckLoad(S, OpPC, Field)) return false; S.Stk.push(Field.deref()); return true; } template ::T> bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) { const T &Value = S.Stk.pop(); const Pointer &Obj = S.Stk.peek(); if (!CheckNull(S, OpPC, Obj, CSK_Field)) return false; if (!CheckRange(S, OpPC, Obj, CSK_Field)) return false; const Pointer &Field = Obj.atField(I); if (!CheckStore(S, OpPC, Field)) return false; Field.deref() = Value; return true; } template ::T> bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) { const Pointer &Obj = S.Stk.pop(); if (!CheckNull(S, OpPC, Obj, CSK_Field)) return false; if (!CheckRange(S, OpPC, Obj, CSK_Field)) return false; const Pointer &Field = Obj.atField(I); if (!CheckLoad(S, OpPC, Field)) return false; S.Stk.push(Field.deref()); return true; } template ::T> bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; const Pointer &Field = This.atField(I); if (!CheckLoad(S, OpPC, Field)) return false; S.Stk.push(Field.deref()); return true; } template ::T> bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) { if (S.checkingPotentialConstantExpression()) return false; const T &Value = S.Stk.pop(); const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; const Pointer &Field = This.atField(I); if (!CheckStore(S, OpPC, Field)) return false; Field.deref() = Value; return true; } template ::T> bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { auto *B = S.P.getGlobal(I); if (B->isExtern()) return false; S.Stk.push(B->deref()); return true; } template ::T> bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { // TODO: emit warning. return false; } template ::T> bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { S.P.getGlobal(I)->deref() = S.Stk.pop(); return true; } template ::T> bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) { if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; const Pointer &Field = This.atField(I); Field.deref() = S.Stk.pop(); Field.initialize(); return true; } template ::T> bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; const Pointer &Field = This.atField(F->Offset); const auto &Value = S.Stk.pop(); Field.deref() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx())); Field.initialize(); return true; } template ::T> bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; const Pointer &Field = This.atField(I); Field.deref() = S.Stk.pop(); Field.activate(); Field.initialize(); return true; } template ::T> bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) { const T &Value = S.Stk.pop(); const Pointer &Field = S.Stk.pop().atField(I); Field.deref() = Value; Field.activate(); Field.initialize(); return true; } template ::T> bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { const T &Value = S.Stk.pop(); const Pointer &Field = S.Stk.pop().atField(F->Offset); Field.deref() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx())); Field.activate(); Field.initialize(); return true; } template ::T> bool InitFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) { const T &Value = S.Stk.pop(); const Pointer &Ptr = S.Stk.pop(); const Pointer &Field = Ptr.atField(I); Field.deref() = Value; Field.activate(); Field.initialize(); return true; } //===----------------------------------------------------------------------===// // GetPtr Local/Param/Global/Field/This //===----------------------------------------------------------------------===// inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) { S.Stk.push(S.Current->getLocalPointer(I)); return true; } inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) { if (S.checkingPotentialConstantExpression()) { return false; } S.Stk.push(S.Current->getParamPointer(I)); return true; } inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) { S.Stk.push(S.P.getPtrGlobal(I)); return true; } inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) { const Pointer &Ptr = S.Stk.pop(); if (!CheckNull(S, OpPC, Ptr, CSK_Field)) return false; if (!CheckExtern(S, OpPC, Ptr)) return false; if (!CheckRange(S, OpPC, Ptr, CSK_Field)) return false; S.Stk.push(Ptr.atField(Off)); return true; } inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; S.Stk.push(This.atField(Off)); return true; } inline bool GetPtrActiveField(InterpState &S, CodePtr OpPC, uint32_t Off) { const Pointer &Ptr = S.Stk.pop(); if (!CheckNull(S, OpPC, Ptr, CSK_Field)) return false; if (!CheckRange(S, OpPC, Ptr, CSK_Field)) return false; Pointer Field = Ptr.atField(Off); Ptr.deactivate(); Field.activate(); S.Stk.push(std::move(Field)); return true; } inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; Pointer Field = This.atField(Off); This.deactivate(); Field.activate(); S.Stk.push(std::move(Field)); return true; } inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) { const Pointer &Ptr = S.Stk.pop(); if (!CheckNull(S, OpPC, Ptr, CSK_Base)) return false; S.Stk.push(Ptr.atField(Off)); return true; } inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) { if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; S.Stk.push(This.atField(Off)); return true; } inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl, const Pointer &Ptr) { Pointer Base = Ptr; while (Base.isBaseClass()) Base = Base.getBase(); auto *Field = Base.getRecord()->getVirtualBase(Decl); S.Stk.push(Base.atField(Field->Offset)); return true; } inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) { const Pointer &Ptr = S.Stk.pop(); if (!CheckNull(S, OpPC, Ptr, CSK_Base)) return false; return VirtBaseHelper(S, OpPC, D, Ptr); } inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) { if (S.checkingPotentialConstantExpression()) return false; const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; return VirtBaseHelper(S, OpPC, D, S.Current->getThis()); } //===----------------------------------------------------------------------===// // Load, Store, Init //===----------------------------------------------------------------------===// template ::T> bool Load(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek(); if (!CheckLoad(S, OpPC, Ptr)) return false; S.Stk.push(Ptr.deref()); return true; } template ::T> bool LoadPop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop(); if (!CheckLoad(S, OpPC, Ptr)) return false; S.Stk.push(Ptr.deref()); return true; } template ::T> bool Store(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop(); const Pointer &Ptr = S.Stk.peek(); if (!CheckStore(S, OpPC, Ptr)) return false; Ptr.deref() = Value; return true; } template ::T> bool StorePop(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop(); const Pointer &Ptr = S.Stk.pop(); if (!CheckStore(S, OpPC, Ptr)) return false; Ptr.deref() = Value; return true; } template ::T> bool StoreBitField(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop(); const Pointer &Ptr = S.Stk.peek(); if (!CheckStore(S, OpPC, Ptr)) return false; if (auto *FD = Ptr.getField()) { Ptr.deref() = Value.truncate(FD->getBitWidthValue(S.getCtx())); } else { Ptr.deref() = Value; } return true; } template ::T> bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop(); const Pointer &Ptr = S.Stk.pop(); if (!CheckStore(S, OpPC, Ptr)) return false; if (auto *FD = Ptr.getField()) { Ptr.deref() = Value.truncate(FD->getBitWidthValue(S.getCtx())); } else { Ptr.deref() = Value; } return true; } template ::T> bool InitPop(InterpState &S, CodePtr OpPC) { const T &Value = S.Stk.pop(); const Pointer &Ptr = S.Stk.pop(); if (!CheckInit(S, OpPC, Ptr)) return false; Ptr.initialize(); new (&Ptr.deref()) T(Value); return true; } template ::T> bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) { const T &Value = S.Stk.pop(); const Pointer &Ptr = S.Stk.peek().atIndex(Idx); if (!CheckInit(S, OpPC, Ptr)) return false; Ptr.initialize(); new (&Ptr.deref()) T(Value); return true; } template ::T> bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) { const T &Value = S.Stk.pop(); const Pointer &Ptr = S.Stk.pop().atIndex(Idx); if (!CheckInit(S, OpPC, Ptr)) return false; Ptr.initialize(); new (&Ptr.deref()) T(Value); return true; } //===----------------------------------------------------------------------===// // AddOffset, SubOffset //===----------------------------------------------------------------------===// template bool OffsetHelper(InterpState &S, CodePtr OpPC) { // Fetch the pointer and the offset. const T &Offset = S.Stk.pop(); const Pointer &Ptr = S.Stk.pop(); if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) return false; if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer)) return false; // Get a version of the index comparable to the type. T Index = T::from(Ptr.getIndex(), Offset.bitWidth()); // A zero offset does not change the pointer, but in the case of an array // it has to be adjusted to point to the first element instead of the array. if (Offset.isZero()) { S.Stk.push(Index.isZero() ? Ptr.atIndex(0) : Ptr); return true; } // Arrays of unknown bounds cannot have pointers into them. if (!CheckArray(S, OpPC, Ptr)) return false; // Compute the largest index into the array. unsigned MaxIndex = Ptr.getNumElems(); // Helper to report an invalid offset, computed as APSInt. auto InvalidOffset = [&]() { const unsigned Bits = Offset.bitWidth(); APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false); APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false); APSInt NewIndex = Add ? (APIndex + APOffset) : (APIndex - APOffset); S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index) << NewIndex << /*array*/ static_cast(!Ptr.inArray()) << static_cast(MaxIndex); return false; }; // If the new offset would be negative, bail out. if (Add && Offset.isNegative() && (Offset.isMin() || -Offset > Index)) return InvalidOffset(); if (!Add && Offset.isPositive() && Index < Offset) return InvalidOffset(); // If the new offset would be out of bounds, bail out. unsigned MaxOffset = MaxIndex - Ptr.getIndex(); if (Add && Offset.isPositive() && Offset > MaxOffset) return InvalidOffset(); if (!Add && Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset)) return InvalidOffset(); // Offset is valid - compute it on unsigned. int64_t WideIndex = static_cast(Index); int64_t WideOffset = static_cast(Offset); int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset); S.Stk.push(Ptr.atIndex(static_cast(Result))); return true; } template ::T> bool AddOffset(InterpState &S, CodePtr OpPC) { return OffsetHelper(S, OpPC); } template ::T> bool SubOffset(InterpState &S, CodePtr OpPC) { return OffsetHelper(S, OpPC); } //===----------------------------------------------------------------------===// // Destroy //===----------------------------------------------------------------------===// inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) { S.Current->destroy(I); return true; } //===----------------------------------------------------------------------===// // Cast, CastFP //===----------------------------------------------------------------------===// template bool Cast(InterpState &S, CodePtr OpPC) { using T = typename PrimConv::T; using U = typename PrimConv::T; S.Stk.push(U::from(S.Stk.pop())); return true; } //===----------------------------------------------------------------------===// // Zero, Nullptr //===----------------------------------------------------------------------===// template ::T> bool Zero(InterpState &S, CodePtr OpPC) { S.Stk.push(T::zero()); return true; } template ::T> inline bool Null(InterpState &S, CodePtr OpPC) { S.Stk.push(); return true; } //===----------------------------------------------------------------------===// // This, ImplicitThis //===----------------------------------------------------------------------===// inline bool This(InterpState &S, CodePtr OpPC) { // Cannot read 'this' in this mode. if (S.checkingPotentialConstantExpression()) { return false; } const Pointer &This = S.Current->getThis(); if (!CheckThis(S, OpPC, This)) return false; S.Stk.push(This); return true; } //===----------------------------------------------------------------------===// // Shr, Shl //===----------------------------------------------------------------------===// template ::T> unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) { // C++11 [expr.shift]p1: Shift width must be less than the bit width of // the shifted type. if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) { const Expr *E = S.Current->getExpr(OpPC); const APSInt Val = V.toAPSInt(); QualType Ty = E->getType(); S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits; return Bits; } else { return static_cast(V); } } template ::T> inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { if (RHS >= V.bitWidth()) { S.Stk.push(T::from(0, V.bitWidth())); } else { S.Stk.push(T::from(V >> RHS, V.bitWidth())); } return true; } template ::T> inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) { if (V.isSigned() && !S.getLangOpts().CPlusPlus20) { // C++11 [expr.shift]p2: A signed left shift must have a non-negative // operand, and must not overflow the corresponding unsigned type. // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to // E1 x 2^E2 module 2^N. if (V.isNegative()) { const Expr *E = S.Current->getExpr(OpPC); S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt(); } else if (V.countLeadingZeros() < RHS) { S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards); } } if (V.bitWidth() == 1) { S.Stk.push(V); } else if (RHS >= V.bitWidth()) { S.Stk.push(T::from(0, V.bitWidth())); } else { S.Stk.push(T::from(V.toUnsigned() << RHS, V.bitWidth())); } return true; } template inline bool Shr(InterpState &S, CodePtr OpPC) { const auto &RHS = S.Stk.pop::T>(); const auto &LHS = S.Stk.pop::T>(); const unsigned Bits = LHS.bitWidth(); if (RHS.isSigned() && RHS.isNegative()) { const SourceInfo &Loc = S.Current->getSource(OpPC); S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); return ShiftLeft(S, OpPC, LHS, Trunc(S, OpPC, Bits, -RHS)); } else { return ShiftRight(S, OpPC, LHS, Trunc(S, OpPC, Bits, RHS)); } } template inline bool Shl(InterpState &S, CodePtr OpPC) { const auto &RHS = S.Stk.pop::T>(); const auto &LHS = S.Stk.pop::T>(); const unsigned Bits = LHS.bitWidth(); if (RHS.isSigned() && RHS.isNegative()) { const SourceInfo &Loc = S.Current->getSource(OpPC); S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt(); return ShiftRight(S, OpPC, LHS, Trunc(S, OpPC, Bits, -RHS)); } else { return ShiftLeft(S, OpPC, LHS, Trunc(S, OpPC, Bits, RHS)); } } //===----------------------------------------------------------------------===// // NoRet //===----------------------------------------------------------------------===// inline bool NoRet(InterpState &S, CodePtr OpPC) { SourceLocation EndLoc = S.Current->getCallee()->getEndLoc(); S.FFDiag(EndLoc, diag::note_constexpr_no_return); return false; } //===----------------------------------------------------------------------===// // NarrowPtr, ExpandPtr //===----------------------------------------------------------------------===// inline bool NarrowPtr(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop(); S.Stk.push(Ptr.narrow()); return true; } inline bool ExpandPtr(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop(); S.Stk.push(Ptr.expand()); return true; } /// Interpreter entry point. bool Interpret(InterpState &S, APValue &Result); } // namespace interp } // namespace clang #endif