aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/AST/Interp/Interp.h
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/AST/Interp/Interp.h')
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Interp.h1338
1 files changed, 1214 insertions, 124 deletions
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Interp.h b/contrib/llvm-project/clang/lib/AST/Interp/Interp.h
index a63c5a871ba3..65c54ed9c89b 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Interp.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Interp.h
@@ -13,9 +13,10 @@
#ifndef LLVM_CLANG_AST_INTERP_INTERP_H
#define LLVM_CLANG_AST_INTERP_INTERP_H
-#include <limits>
-#include <vector>
+#include "Boolean.h"
+#include "Floating.h"
#include "Function.h"
+#include "FunctionPointer.h"
#include "InterpFrame.h"
#include "InterpStack.h"
#include "InterpState.h"
@@ -30,14 +31,15 @@
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/Support/Endian.h"
+#include <limits>
+#include <type_traits>
namespace clang {
namespace interp {
-using APInt = llvm::APInt;
using APSInt = llvm::APSInt;
-/// Convers a value to an APValue.
+/// Convert a value to an APValue.
template <typename T> bool ReturnValue(const T &V, APValue &R) {
R = V.toAPValue();
return true;
@@ -49,9 +51,13 @@ 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.
+/// Checks if a pointer is live and accessible.
bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK);
+
+/// Checks if a pointer is a dummy pointer.
+bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
+
/// Checks if a pointer is null.
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK);
@@ -64,15 +70,25 @@ bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
CheckSubobjectKind CSK);
+/// Checks if Ptr is a one-past-the-end pointer.
+bool CheckSubobject(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 the Descriptor is of a constexpr or const global variable.
+bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc);
+
/// 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);
+bool CheckInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ AccessKinds AK);
+
/// Checks if a value can be stored in a block.
bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
@@ -83,7 +99,11 @@ bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
/// Checks if a method can be called.
-bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F);
+bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
+
+/// Checks if calling the currently active function would exceed
+/// the allowed call depth.
+bool CheckCallDepth(InterpState &S, CodePtr OpPC);
/// Checks the 'this' pointer.
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
@@ -91,7 +111,151 @@ 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 <typename T> inline bool IsTrue(const T &V) { return !V.isZero(); }
+/// Checks if reinterpret casts are legal in the current context.
+bool CheckPotentialReinterpretCast(InterpState &S, CodePtr OpPC,
+ const Pointer &Ptr);
+
+/// Sets the given integral value to the pointer, which is of
+/// a std::{weak,partial,strong}_ordering type.
+bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
+ const Pointer &Ptr, const APSInt &IntValue);
+
+/// Checks if the shift operation is legal.
+template <typename LT, typename RT>
+bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
+ unsigned Bits) {
+ if (RHS.isNegative()) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
+ return false;
+ }
+
+ // C++11 [expr.shift]p1: Shift width must be less than the bit width of
+ // the shifted type.
+ if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ const APSInt Val = RHS.toAPSInt();
+ QualType Ty = E->getType();
+ S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
+ return false;
+ }
+
+ if (LHS.isSigned() && !S.getLangOpts().CPlusPlus20) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ // C++11 [expr.shift]p2: A signed left shift must have a non-negative
+ // operand, and must not overflow the corresponding unsigned type.
+ if (LHS.isNegative())
+ S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << LHS.toAPSInt();
+ else if (LHS.toUnsigned().countLeadingZeros() < static_cast<unsigned>(RHS))
+ S.CCEDiag(E, diag::note_constexpr_lshift_discards);
+ }
+
+ // C++2a [expr.shift]p2: [P0907R4]:
+ // E1 << E2 is the unique value congruent to
+ // E1 x 2^E2 module 2^N.
+ return true;
+}
+
+/// Checks if Div/Rem operation on LHS and RHS is valid.
+template <typename T>
+bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
+ if (RHS.isZero()) {
+ const auto *Op = cast<BinaryOperator>(S.Current->getExpr(OpPC));
+ S.FFDiag(Op, diag::note_expr_divide_by_zero)
+ << Op->getRHS()->getSourceRange();
+ return false;
+ }
+
+ if (LHS.isSigned() && LHS.isMin() && RHS.isNegative() && RHS.isMinusOne()) {
+ APSInt LHSInt = LHS.toAPSInt();
+ SmallString<32> Trunc;
+ (-LHSInt.extend(LHSInt.getBitWidth() + 1)).toString(Trunc, 10);
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ const Expr *E = S.Current->getExpr(OpPC);
+ S.CCEDiag(Loc, diag::note_constexpr_overflow) << Trunc << E->getType();
+ return false;
+ }
+ return true;
+}
+
+/// Checks if the result of a floating-point operation is valid
+/// in the current context.
+bool CheckFloatResult(InterpState &S, CodePtr OpPC, const Floating &Result,
+ APFloat::opStatus Status);
+
+/// Checks why the given DeclRefExpr is invalid.
+bool CheckDeclRef(InterpState &S, CodePtr OpPC, const DeclRefExpr *DR);
+
+/// Interpreter entry point.
+bool Interpret(InterpState &S, APValue &Result);
+
+/// Interpret a builtin function.
+bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
+ const CallExpr *Call);
+
+/// Interpret an offsetof operation.
+bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
+ llvm::ArrayRef<int64_t> ArrayIndices, int64_t &Result);
+
+enum class ArithOp { Add, Sub };
+
+//===----------------------------------------------------------------------===//
+// Returning values
+//===----------------------------------------------------------------------===//
+
+void cleanupAfterFunctionCall(InterpState &S, CodePtr OpPC);
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Ret(InterpState &S, CodePtr &PC, APValue &Result) {
+ const T &Ret = S.Stk.pop<T>();
+
+ // Make sure returned pointers are live. We might be trying to return a
+ // pointer or reference to a local variable.
+ // Just return false, since a diagnostic has already been emitted in Sema.
+ if constexpr (std::is_same_v<T, Pointer>) {
+ // FIXME: We could be calling isLive() here, but the emitted diagnostics
+ // seem a little weird, at least if the returned expression is of
+ // pointer type.
+ // Null pointers are considered live here.
+ if (!Ret.isZero() && !Ret.isLive())
+ return false;
+ }
+
+ assert(S.Current);
+ assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
+ if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
+ cleanupAfterFunctionCall(S, PC);
+
+ 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;
+}
+
+inline bool RetVoid(InterpState &S, CodePtr &PC, APValue &Result) {
+ assert(S.Current->getFrameOffset() == S.Stk.size() && "Invalid frame");
+
+ if (!S.checkingPotentialConstantExpression() || S.Current->Caller)
+ cleanupAfterFunctionCall(S, PC);
+
+ 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;
+}
//===----------------------------------------------------------------------===//
// Add, Sub, Mul
@@ -118,13 +282,19 @@ bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
const Expr *E = S.Current->getExpr(OpPC);
QualType Type = E->getType();
if (S.checkingForUndefinedBehavior()) {
- auto Trunc = Value.trunc(Result.bitWidth()).toString(10);
+ SmallString<32> Trunc;
+ Value.trunc(Result.bitWidth()).toString(Trunc, 10);
auto Loc = E->getExprLoc();
- S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type;
+ S.report(Loc, diag::warn_integer_constant_overflow)
+ << Trunc << Type << E->getSourceRange();
return true;
} else {
S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type;
- return S.noteUndefinedBehavior();
+ if (!S.noteUndefinedBehavior()) {
+ S.Stk.pop<T>();
+ return false;
+ }
+ return true;
}
}
@@ -136,6 +306,16 @@ bool Add(InterpState &S, CodePtr OpPC) {
return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
}
+inline bool Addf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &RHS = S.Stk.pop<Floating>();
+ const Floating &LHS = S.Stk.pop<Floating>();
+
+ Floating Result;
+ auto Status = Floating::add(LHS, RHS, RM, &Result);
+ S.Stk.push<Floating>(Result);
+ return CheckFloatResult(S, OpPC, Result, Status);
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Sub(InterpState &S, CodePtr OpPC) {
const T &RHS = S.Stk.pop<T>();
@@ -144,6 +324,16 @@ bool Sub(InterpState &S, CodePtr OpPC) {
return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
}
+inline bool Subf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &RHS = S.Stk.pop<Floating>();
+ const Floating &LHS = S.Stk.pop<Floating>();
+
+ Floating Result;
+ auto Status = Floating::sub(LHS, RHS, RM, &Result);
+ S.Stk.push<Floating>(Result);
+ return CheckFloatResult(S, OpPC, Result, Status);
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool Mul(InterpState &S, CodePtr OpPC) {
const T &RHS = S.Stk.pop<T>();
@@ -152,6 +342,348 @@ bool Mul(InterpState &S, CodePtr OpPC) {
return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
}
+inline bool Mulf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &RHS = S.Stk.pop<Floating>();
+ const Floating &LHS = S.Stk.pop<Floating>();
+
+ Floating Result;
+ auto Status = Floating::mul(LHS, RHS, RM, &Result);
+ S.Stk.push<Floating>(Result);
+ return CheckFloatResult(S, OpPC, Result, Status);
+}
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS & RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitAnd(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitAnd(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS | RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitOr(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitOr(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS ^ RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitXor(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitXor(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS % RHS' on the stack (the remainder of dividing LHS by RHS).
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Rem(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ if (!CheckDivRem(S, OpPC, LHS, RHS))
+ return false;
+
+ const unsigned Bits = RHS.bitWidth() * 2;
+ T Result;
+ if (!T::rem(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS / RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Div(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ if (!CheckDivRem(S, OpPC, LHS, RHS))
+ return false;
+
+ const unsigned Bits = RHS.bitWidth() * 2;
+ T Result;
+ if (!T::div(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+inline bool Divf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Floating &RHS = S.Stk.pop<Floating>();
+ const Floating &LHS = S.Stk.pop<Floating>();
+
+ if (!CheckDivRem(S, OpPC, LHS, RHS))
+ return false;
+
+ Floating Result;
+ auto Status = Floating::div(LHS, RHS, RM, &Result);
+ S.Stk.push<Floating>(Result);
+ return CheckFloatResult(S, OpPC, Result, Status);
+}
+
+//===----------------------------------------------------------------------===//
+// Inv
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Inv(InterpState &S, CodePtr OpPC) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const T &Val = S.Stk.pop<T>();
+ const unsigned Bits = Val.bitWidth();
+ Boolean R;
+ Boolean::inv(BoolT::from(Val, Bits), &R);
+
+ S.Stk.push<BoolT>(R);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Neg
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Neg(InterpState &S, CodePtr OpPC) {
+ const T &Value = S.Stk.pop<T>();
+ T Result;
+
+ if (!T::neg(Value, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+
+ assert(isIntegralType(Name) &&
+ "don't expect other types to fail at constexpr negation");
+ S.Stk.push<T>(Result);
+
+ APSInt NegatedValue = -Value.toAPSInt(Value.bitWidth() + 1);
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+
+ if (S.checkingForUndefinedBehavior()) {
+ SmallString<32> Trunc;
+ NegatedValue.trunc(Result.bitWidth()).toString(Trunc, 10);
+ auto Loc = E->getExprLoc();
+ S.report(Loc, diag::warn_integer_constant_overflow)
+ << Trunc << Type << E->getSourceRange();
+ return true;
+ }
+
+ S.CCEDiag(E, diag::note_constexpr_overflow) << NegatedValue << Type;
+ return S.noteUndefinedBehavior();
+}
+
+enum class PushVal : bool {
+ No,
+ Yes,
+};
+enum class IncDecOp {
+ Inc,
+ Dec,
+};
+
+template <typename T, IncDecOp Op, PushVal DoPush>
+bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ const T &Value = Ptr.deref<T>();
+ T Result;
+
+ if constexpr (DoPush == PushVal::Yes)
+ S.Stk.push<T>(Value);
+
+ if constexpr (Op == IncDecOp::Inc) {
+ if (!T::increment(Value, &Result)) {
+ Ptr.deref<T>() = Result;
+ return true;
+ }
+ } else {
+ if (!T::decrement(Value, &Result)) {
+ Ptr.deref<T>() = Result;
+ return true;
+ }
+ }
+
+ // Something went wrong with the previous operation. Compute the
+ // result with another bit of precision.
+ unsigned Bits = Value.bitWidth() + 1;
+ APSInt APResult;
+ if constexpr (Op == IncDecOp::Inc)
+ APResult = ++Value.toAPSInt(Bits);
+ else
+ APResult = --Value.toAPSInt(Bits);
+
+ // Report undefined behaviour, stopping if required.
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+ if (S.checkingForUndefinedBehavior()) {
+ SmallString<32> Trunc;
+ APResult.trunc(Result.bitWidth()).toString(Trunc, 10);
+ auto Loc = E->getExprLoc();
+ S.report(Loc, diag::warn_integer_constant_overflow)
+ << Trunc << Type << E->getSourceRange();
+ return true;
+ }
+
+ S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type;
+ return S.noteUndefinedBehavior();
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value increased by one back to the pointer
+/// 4) Pushes the original (pre-inc) value on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Inc(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
+ return false;
+
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value increased by one back to the pointer
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool IncPop(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
+ return false;
+
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value decreased by one back to the pointer
+/// 4) Pushes the original (pre-dec) value on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Dec(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
+ return false;
+
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value decreased by one back to the pointer
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool DecPop(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
+ return false;
+
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr);
+}
+
+template <IncDecOp Op, PushVal DoPush>
+bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ llvm::RoundingMode RM) {
+ Floating Value = Ptr.deref<Floating>();
+ Floating Result;
+
+ if constexpr (DoPush == PushVal::Yes)
+ S.Stk.push<Floating>(Value);
+
+ llvm::APFloat::opStatus Status;
+ if constexpr (Op == IncDecOp::Inc)
+ Status = Floating::increment(Value, RM, &Result);
+ else
+ Status = Floating::decrement(Value, RM, &Result);
+
+ Ptr.deref<Floating>() = Result;
+
+ return CheckFloatResult(S, OpPC, Result, Status);
+}
+
+inline bool Incf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
+ return false;
+
+ return IncDecFloatHelper<IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr, RM);
+}
+
+inline bool IncfPop(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
+ return false;
+
+ return IncDecFloatHelper<IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, RM);
+}
+
+inline bool Decf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
+ return false;
+
+ return IncDecFloatHelper<IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr, RM);
+}
+
+inline bool DecfPop(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
+ return false;
+
+ return IncDecFloatHelper<IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, RM);
+}
+
+/// 1) Pops the value from the stack.
+/// 2) Pushes the bitwise complemented value on the stack (~V).
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Comp(InterpState &S, CodePtr OpPC) {
+ const T &Val = S.Stk.pop<T>();
+ T Result;
+ if (!T::comp(Val, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+
+ return false;
+}
+
//===----------------------------------------------------------------------===//
// EQ, NE, GT, GE, LT, LE
//===----------------------------------------------------------------------===//
@@ -172,6 +704,29 @@ bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) {
return CmpHelper<T>(S, OpPC, Fn);
}
+/// Function pointers cannot be compared in an ordered way.
+template <>
+inline bool CmpHelper<FunctionPointer>(InterpState &S, CodePtr OpPC,
+ CompareFn Fn) {
+ const auto &RHS = S.Stk.pop<FunctionPointer>();
+ const auto &LHS = S.Stk.pop<FunctionPointer>();
+
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
+ << LHS.toDiagnosticString(S.getCtx())
+ << RHS.toDiagnosticString(S.getCtx());
+ return false;
+}
+
+template <>
+inline bool CmpHelperEQ<FunctionPointer>(InterpState &S, CodePtr OpPC,
+ CompareFn Fn) {
+ const auto &RHS = S.Stk.pop<FunctionPointer>();
+ const auto &LHS = S.Stk.pop<FunctionPointer>();
+ S.Stk.push<Boolean>(Boolean::from(Fn(LHS.compare(RHS))));
+ return true;
+}
+
template <>
inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
using BoolT = PrimConv<PT_Bool>::T;
@@ -180,7 +735,9 @@ inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
if (!Pointer::hasSameBase(LHS, RHS)) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
+ S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
+ << LHS.toDiagnosticString(S.getCtx())
+ << RHS.toDiagnosticString(S.getCtx());
return false;
} else {
unsigned VL = LHS.getByteOffset();
@@ -207,6 +764,16 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
} else {
unsigned VL = LHS.getByteOffset();
unsigned VR = RHS.getByteOffset();
+
+ // In our Pointer class, a pointer to an array and a pointer to the first
+ // element in the same array are NOT equal. They have the same Base value,
+ // but a different Offset. This is a pretty rare case, so we fix this here
+ // by comparing pointers to the first elements.
+ if (LHS.isArrayRoot())
+ VL = LHS.atIndex(0).getByteOffset();
+ if (RHS.isArrayRoot())
+ VR = RHS.atIndex(0).getByteOffset();
+
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
return true;
}
@@ -220,6 +787,30 @@ bool EQ(InterpState &S, CodePtr OpPC) {
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CMP3(InterpState &S, CodePtr OpPC, const ComparisonCategoryInfo *CmpInfo) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+ const Pointer &P = S.Stk.peek<Pointer>();
+
+ ComparisonCategoryResult CmpResult = LHS.compare(RHS);
+ if (CmpResult == ComparisonCategoryResult::Unordered) {
+ // This should only happen with pointers.
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
+ << LHS.toDiagnosticString(S.getCtx())
+ << RHS.toDiagnosticString(S.getCtx());
+ return false;
+ }
+
+ assert(CmpInfo);
+ const auto *CmpValueInfo = CmpInfo->getValueInfo(CmpResult);
+ assert(CmpValueInfo);
+ assert(CmpValueInfo->hasValidIntValue());
+ APSInt IntValue = CmpValueInfo->getIntValue();
+ return SetThreeWayComparisonField(S, OpPC, P, IntValue);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
bool NE(InterpState &S, CodePtr OpPC) {
return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
return R != ComparisonCategoryResult::Equal;
@@ -302,10 +893,16 @@ bool Const(InterpState &S, CodePtr OpPC, const T &Arg) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
- S.Stk.push<T>(S.Current->getLocal<T>(I));
+ const Pointer &Ptr = S.Current->getLocalPointer(I);
+ if (!CheckLoad(S, OpPC, Ptr))
+ return false;
+ S.Stk.push<T>(Ptr.deref<T>());
return true;
}
+/// 1) Pops the value from the stack.
+/// 2) Writes the value to the local variable with the
+/// given offset.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
S.Current->setLocal<T>(I, S.Stk.pop<T>());
@@ -327,6 +924,8 @@ bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Peeks a pointer on the stack
+/// 2) Pushes the value of the pointer's field on the stack
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
const Pointer &Obj = S.Stk.peek<Pointer>();
@@ -352,10 +951,13 @@ bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) {
const Pointer &Field = Obj.atField(I);
if (!CheckStore(S, OpPC, Field))
return false;
+ Field.initialize();
Field.deref<T>() = Value;
return true;
}
+/// 1) Pops a pointer from the stack
+/// 2) Pushes the value of the pointer's field on the stack
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
const Pointer &Obj = S.Stk.pop<Pointer>();
@@ -401,13 +1003,24 @@ bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
- auto *B = S.P.getGlobal(I);
+ const Block *B = S.P.getGlobal(I);
+
+ if (!CheckConstant(S, OpPC, B->getDescriptor()))
+ return false;
if (B->isExtern())
return false;
S.Stk.push<T>(B->deref<T>());
return true;
}
+/// Same as GetGlobal, but without the checks.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool GetGlobalUnchecked(InterpState &S, CodePtr OpPC, uint32_t I) {
+ auto *B = S.P.getGlobal(I);
+ S.Stk.push<T>(B->deref<T>());
+ return true;
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
// TODO: emit warning.
@@ -420,6 +1033,39 @@ bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Converts the value on top of the stack to an APValue
+/// 2) Sets that APValue on \Temp
+/// 3) Initialized global with index \I with that
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool InitGlobalTemp(InterpState &S, CodePtr OpPC, uint32_t I,
+ const LifetimeExtendedTemporaryDecl *Temp) {
+ assert(Temp);
+ const T Value = S.Stk.peek<T>();
+ APValue APV = Value.toAPValue();
+ APValue *Cached = Temp->getOrCreateValue(true);
+ *Cached = APV;
+
+ S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
+ return true;
+}
+
+/// 1) Converts the value on top of the stack to an APValue
+/// 2) Sets that APValue on \Temp
+/// 3) Initialized global with index \I with that
+inline bool InitGlobalTempComp(InterpState &S, CodePtr OpPC,
+ const LifetimeExtendedTemporaryDecl *Temp) {
+ assert(Temp);
+ const Pointer &P = S.Stk.peek<Pointer>();
+ APValue *Cached = Temp->getOrCreateValue(true);
+
+ if (std::optional<APValue> APV = P.toRValue(S.getCtx())) {
+ *Cached = *APV;
+ return true;
+ }
+
+ return false;
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
if (S.checkingPotentialConstantExpression())
@@ -433,14 +1079,18 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+// FIXME: The Field pointer here is too much IMO and we could instead just
+// pass an Offset + BitWidth pair.
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
+bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F,
+ uint32_t FieldOffset) {
+ assert(F->isBitField());
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 Pointer &Field = This.atField(FieldOffset);
const auto &Value = S.Stk.pop<T>();
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
Field.initialize();
@@ -461,10 +1111,13 @@ bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Pops the value from the stack
+/// 2) Peeks a pointer from the stack
+/// 3) Pushes the value to field I of the pointer on the stack
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
const T &Value = S.Stk.pop<T>();
- const Pointer &Field = S.Stk.pop<Pointer>().atField(I);
+ const Pointer &Field = S.Stk.peek<Pointer>().atField(I);
Field.deref<T>() = Value;
Field.activate();
Field.initialize();
@@ -473,8 +1126,9 @@ bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
+ assert(F->isBitField());
const T &Value = S.Stk.pop<T>();
- const Pointer &Field = S.Stk.pop<Pointer>().atField(F->Offset);
+ const Pointer &Field = S.Stk.peek<Pointer>().atField(F->Offset);
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
Field.activate();
Field.initialize();
@@ -514,14 +1168,19 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Pops a Pointer from the stack
+/// 2) Pushes Pointer.atField(Off) on the stack
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
- if (!CheckNull(S, OpPC, Ptr, CSK_Field))
+ if (S.inConstantContext() && !CheckNull(S, OpPC, Ptr, CSK_Field))
return false;
if (!CheckExtern(S, OpPC, Ptr))
return false;
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
return false;
+ if (!CheckSubobject(S, OpPC, Ptr, CSK_Field))
+ return false;
+
S.Stk.push<Pointer>(Ptr.atField(Off));
return true;
}
@@ -562,10 +1221,32 @@ inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) {
return true;
}
+inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Derived))
+ return false;
+ if (!CheckSubobject(S, OpPC, Ptr, CSK_Derived))
+ return false;
+ S.Stk.push<Pointer>(Ptr.atFieldSub(Off));
+ return true;
+}
+
inline bool GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckNull(S, OpPC, Ptr, CSK_Base))
+ return false;
+ if (!CheckSubobject(S, OpPC, Ptr, CSK_Base))
+ return false;
+ S.Stk.push<Pointer>(Ptr.atField(Off));
+ return true;
+}
+
+inline bool GetPtrBasePop(InterpState &S, CodePtr OpPC, uint32_t Off) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckNull(S, OpPC, Ptr, CSK_Base))
return false;
+ if (!CheckSubobject(S, OpPC, Ptr, CSK_Base))
+ return false;
S.Stk.push<Pointer>(Ptr.atField(Off));
return true;
}
@@ -580,6 +1261,12 @@ inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
return true;
}
+inline bool InitPtrPop(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ Ptr.initialize();
+ return true;
+}
+
inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
const Pointer &Ptr) {
Pointer Base = Ptr;
@@ -636,6 +1323,8 @@ bool Store(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
}
@@ -646,6 +1335,8 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
}
@@ -656,11 +1347,12 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
- if (auto *FD = Ptr.getField()) {
+ if (!Ptr.isRoot())
+ Ptr.initialize();
+ if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
- } else {
+ else
Ptr.deref<T>() = Value;
- }
return true;
}
@@ -670,11 +1362,12 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
- if (auto *FD = Ptr.getField()) {
+ if (!Ptr.isRoot())
+ Ptr.initialize();
+ if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
- } else {
+ else
Ptr.deref<T>() = Value;
- }
return true;
}
@@ -689,6 +1382,9 @@ bool InitPop(InterpState &S, CodePtr OpPC) {
return true;
}
+/// 1) Pops the value from the stack
+/// 2) Peeks a pointer and gets its index \Idx
+/// 3) Sets the value on the pointer, leaving the pointer on the stack.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
const T &Value = S.Stk.pop<T>();
@@ -700,6 +1396,7 @@ bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
return true;
}
+/// The same as InitElem, but pops the pointer as well.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
const T &Value = S.Stk.pop<T>();
@@ -715,74 +1412,151 @@ bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
// AddOffset, SubOffset
//===----------------------------------------------------------------------===//
-template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) {
- // Fetch the pointer and the offset.
- const T &Offset = S.Stk.pop<T>();
- const Pointer &Ptr = S.Stk.pop<Pointer>();
- if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
- return false;
+template <class T, ArithOp Op>
+bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
+ const Pointer &Ptr) {
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.
+ // A zero offset does not change the pointer.
if (Offset.isZero()) {
- S.Stk.push<Pointer>(Index.isZero() ? Ptr.atIndex(0) : Ptr);
+ S.Stk.push<Pointer>(Ptr);
return true;
}
+
+ if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
+ return false;
+
// Arrays of unknown bounds cannot have pointers into them.
if (!CheckArray(S, OpPC, Ptr))
return false;
+ // Get a version of the index comparable to the type.
+ T Index = T::from(Ptr.getIndex(), Offset.bitWidth());
// Compute the largest index into the array.
- unsigned MaxIndex = Ptr.getNumElems();
+ T MaxIndex = T::from(Ptr.getNumElems(), Offset.bitWidth());
+ bool Invalid = false;
// Helper to report an invalid offset, computed as APSInt.
- auto InvalidOffset = [&]() {
+ auto DiagInvalidOffset = [&]() -> void {
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);
+ APSInt NewIndex =
+ (Op == ArithOp::Add) ? (APIndex + APOffset) : (APIndex - APOffset);
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
<< NewIndex
<< /*array*/ static_cast<int>(!Ptr.inArray())
<< static_cast<unsigned>(MaxIndex);
- return false;
+ Invalid = true;
};
- // 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();
+ T MaxOffset = T::from(MaxIndex - Index, Offset.bitWidth());
+ if constexpr (Op == ArithOp::Add) {
+ // If the new offset would be negative, bail out.
+ if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
+ DiagInvalidOffset();
- // 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();
+ // If the new offset would be out of bounds, bail out.
+ if (Offset.isPositive() && Offset > MaxOffset)
+ DiagInvalidOffset();
+ } else {
+ // If the new offset would be negative, bail out.
+ if (Offset.isPositive() && Index < Offset)
+ DiagInvalidOffset();
+
+ // If the new offset would be out of bounds, bail out.
+ if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
+ DiagInvalidOffset();
+ }
+
+ if (Invalid && !Ptr.isDummy() && S.getLangOpts().CPlusPlus)
+ return false;
// Offset is valid - compute it on unsigned.
int64_t WideIndex = static_cast<int64_t>(Index);
int64_t WideOffset = static_cast<int64_t>(Offset);
- int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset);
+ int64_t Result;
+ if constexpr (Op == ArithOp::Add)
+ Result = WideIndex + WideOffset;
+ else
+ Result = WideIndex - WideOffset;
+
S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result)));
return true;
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool AddOffset(InterpState &S, CodePtr OpPC) {
- return OffsetHelper<T, true>(S, OpPC);
+ const T &Offset = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ return OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr);
}
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool SubOffset(InterpState &S, CodePtr OpPC) {
- return OffsetHelper<T, false>(S, OpPC);
+ const T &Offset = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+ return OffsetHelper<T, ArithOp::Sub>(S, OpPC, Offset, Ptr);
+}
+
+template <ArithOp Op>
+static inline bool IncDecPtrHelper(InterpState &S, CodePtr OpPC,
+ const Pointer &Ptr) {
+ using OneT = Integral<8, false>;
+
+ const Pointer &P = Ptr.deref<Pointer>();
+ if (!CheckNull(S, OpPC, P, CSK_ArrayIndex))
+ return false;
+
+ // Get the current value on the stack.
+ S.Stk.push<Pointer>(P);
+
+ // Now the current Ptr again and a constant 1.
+ OneT One = OneT::from(1);
+ if (!OffsetHelper<OneT, Op>(S, OpPC, One, P))
+ return false;
+
+ // Store the new value.
+ Ptr.deref<Pointer>() = S.Stk.pop<Pointer>();
+ return true;
+}
+
+static inline bool IncPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Increment))
+ return false;
+
+ return IncDecPtrHelper<ArithOp::Add>(S, OpPC, Ptr);
}
+static inline bool DecPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckInitialized(S, OpPC, Ptr, AK_Decrement))
+ return false;
+
+ return IncDecPtrHelper<ArithOp::Sub>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a Pointer from the stack.
+/// 2) Pops another Pointer from the stack.
+/// 3) Pushes the different of the indices of the two pointers on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool SubPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+
+ if (!Pointer::hasSameBase(LHS, RHS) && S.getLangOpts().CPlusPlus) {
+ // TODO: Diagnose.
+ return false;
+ }
+
+ T A = T::from(LHS.getIndex());
+ T B = T::from(RHS.getIndex());
+ return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B);
+}
//===----------------------------------------------------------------------===//
// Destroy
@@ -804,6 +1578,127 @@ template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
return true;
}
+/// 1) Pops a Floating from the stack.
+/// 2) Pushes a new floating on the stack that uses the given semantics.
+inline bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem,
+ llvm::RoundingMode RM) {
+ Floating F = S.Stk.pop<Floating>();
+ Floating Result = F.toSemantics(Sem, RM);
+ S.Stk.push<Floating>(Result);
+ return true;
+}
+
+/// Like Cast(), but we cast to an arbitrary-bitwidth integral, so we need
+/// to know what bitwidth the result should be.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
+ S.Stk.push<IntegralAP<false>>(
+ IntegralAP<false>::from(S.Stk.pop<T>(), BitWidth));
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
+ S.Stk.push<IntegralAP<true>>(
+ IntegralAP<true>::from(S.Stk.pop<T>(), BitWidth));
+ return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastIntegralFloating(InterpState &S, CodePtr OpPC,
+ const llvm::fltSemantics *Sem,
+ llvm::RoundingMode RM) {
+ const T &From = S.Stk.pop<T>();
+ APSInt FromAP = From.toAPSInt();
+ Floating Result;
+
+ auto Status = Floating::fromIntegral(FromAP, *Sem, RM, Result);
+ S.Stk.push<Floating>(Result);
+
+ return CheckFloatResult(S, OpPC, Result, Status);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastFloatingIntegral(InterpState &S, CodePtr OpPC) {
+ const Floating &F = S.Stk.pop<Floating>();
+
+ if constexpr (std::is_same_v<T, Boolean>) {
+ S.Stk.push<T>(T(F.isNonZero()));
+ return true;
+ } else {
+ APSInt Result(std::max(8u, T::bitWidth()),
+ /*IsUnsigned=*/!T::isSigned());
+ auto Status = F.convertToInteger(Result);
+
+ // Float-to-Integral overflow check.
+ if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+
+ S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
+ if (S.noteUndefinedBehavior()) {
+ S.Stk.push<T>(T(Result));
+ return true;
+ }
+ return false;
+ }
+
+ S.Stk.push<T>(T(Result));
+ return CheckFloatResult(S, OpPC, F, Status);
+ }
+}
+
+static inline bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC,
+ uint32_t BitWidth) {
+ const Floating &F = S.Stk.pop<Floating>();
+
+ APSInt Result(BitWidth, /*IsUnsigned=*/true);
+ auto Status = F.convertToInteger(Result);
+
+ // Float-to-Integral overflow check.
+ if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+
+ S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
+ return S.noteUndefinedBehavior();
+ }
+
+ S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
+ return CheckFloatResult(S, OpPC, F, Status);
+}
+
+static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC,
+ uint32_t BitWidth) {
+ const Floating &F = S.Stk.pop<Floating>();
+
+ APSInt Result(BitWidth, /*IsUnsigned=*/false);
+ auto Status = F.convertToInteger(Result);
+
+ // Float-to-Integral overflow check.
+ if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+
+ S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
+ return S.noteUndefinedBehavior();
+ }
+
+ S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
+ return CheckFloatResult(S, OpPC, F, Status);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!CheckPotentialReinterpretCast(S, OpPC, Ptr))
+ return false;
+
+ S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Zero, Nullptr
//===----------------------------------------------------------------------===//
@@ -814,6 +1709,16 @@ bool Zero(InterpState &S, CodePtr OpPC) {
return true;
}
+static inline bool ZeroIntAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
+ S.Stk.push<IntegralAP<false>>(IntegralAP<false>::zero(BitWidth));
+ return true;
+}
+
+static inline bool ZeroIntAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
+ S.Stk.push<IntegralAP<true>>(IntegralAP<true>::zero(BitWidth));
+ return true;
+}
+
template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool Null(InterpState &S, CodePtr OpPC) {
S.Stk.push<T>();
@@ -838,88 +1743,53 @@ inline bool This(InterpState &S, CodePtr OpPC) {
return true;
}
+inline bool RVOPtr(InterpState &S, CodePtr OpPC) {
+ assert(S.Current->getFunction()->hasRVO());
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ S.Stk.push<Pointer>(S.Current->getRVOPtr());
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Shr, Shl
//===----------------------------------------------------------------------===//
-template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::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<unsigned>(V);
- }
-}
+template <PrimType NameL, PrimType NameR>
+inline bool Shr(InterpState &S, CodePtr OpPC) {
+ using LT = typename PrimConv<NameL>::T;
+ using RT = typename PrimConv<NameR>::T;
+ const auto &RHS = S.Stk.pop<RT>();
+ const auto &LHS = S.Stk.pop<LT>();
+ const unsigned Bits = LHS.bitWidth();
-template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
-inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
- if (RHS >= V.bitWidth()) {
- S.Stk.push<T>(T::from(0, V.bitWidth()));
- } else {
- S.Stk.push<T>(T::from(V >> RHS, V.bitWidth()));
- }
- return true;
-}
+ if (!CheckShift(S, OpPC, LHS, RHS, Bits))
+ return false;
-template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::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);
- }
- }
+ typename LT::AsUnsigned R;
+ LT::AsUnsigned::shiftRight(LT::AsUnsigned::from(LHS),
+ LT::AsUnsigned::from(RHS), Bits, &R);
+ S.Stk.push<LT>(LT::from(R));
- if (V.bitWidth() == 1) {
- S.Stk.push<T>(V);
- } else if (RHS >= V.bitWidth()) {
- S.Stk.push<T>(T::from(0, V.bitWidth()));
- } else {
- S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth()));
- }
return true;
}
-template <PrimType TL, PrimType TR>
-inline bool Shr(InterpState &S, CodePtr OpPC) {
- const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
- const auto &LHS = S.Stk.pop<typename PrimConv<TL>::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<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
- } else {
- return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
- }
-}
-
-template <PrimType TL, PrimType TR>
+template <PrimType NameL, PrimType NameR>
inline bool Shl(InterpState &S, CodePtr OpPC) {
- const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
- const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
+ using LT = typename PrimConv<NameL>::T;
+ using RT = typename PrimConv<NameR>::T;
+ const auto &RHS = S.Stk.pop<RT>();
+ const auto &LHS = S.Stk.pop<LT>();
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<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
- } else {
- return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
- }
+ if (!CheckShift(S, OpPC, LHS, RHS, Bits))
+ return false;
+
+ typename LT::AsUnsigned R;
+ LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
+ LT::AsUnsigned::from(RHS, Bits), Bits, &R);
+ S.Stk.push<LT>(LT::from(R));
+ return true;
}
//===----------------------------------------------------------------------===//
@@ -948,8 +1818,228 @@ inline bool ExpandPtr(InterpState &S, CodePtr OpPC) {
return true;
}
-/// Interpreter entry point.
-bool Interpret(InterpState &S, APValue &Result);
+// 1) Pops an integral value from the stack
+// 2) Peeks a pointer
+// 3) Pushes a new pointer that's a narrowed array
+// element of the peeked pointer with the value
+// from 1) added as offset.
+//
+// This leaves the original pointer on the stack and pushes a new one
+// with the offset applied and narrowed.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool ArrayElemPtr(InterpState &S, CodePtr OpPC) {
+ const T &Offset = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+
+ if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
+ return false;
+
+ return NarrowPtr(S, OpPC);
+}
+
+/// Just takes a pointer and checks if its' an incomplete
+/// array type.
+inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!Ptr.isUnknownSizeArray()) {
+ S.Stk.push<Pointer>(Ptr.atIndex(0));
+ return true;
+ }
+
+ const SourceInfo &E = S.Current->getSource(OpPC);
+ S.FFDiag(E, diag::note_constexpr_unsupported_unsized_array);
+
+ return false;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool ArrayElemPtrPop(InterpState &S, CodePtr OpPC) {
+ const T &Offset = S.Stk.pop<T>();
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ if (!OffsetHelper<T, ArithOp::Add>(S, OpPC, Offset, Ptr))
+ return false;
+
+ return NarrowPtr(S, OpPC);
+}
+
+inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func) {
+ if (Func->hasThisPointer()) {
+ size_t ThisOffset =
+ Func->getArgSize() - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
+
+ const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
+
+ // If the current function is a lambda static invoker and
+ // the function we're about to call is a lambda call operator,
+ // skip the CheckInvoke, since the ThisPtr is a null pointer
+ // anyway.
+ if (!(S.Current->getFunction() &&
+ S.Current->getFunction()->isLambdaStaticInvoker() &&
+ Func->isLambdaCallOperator())) {
+ if (!CheckInvoke(S, OpPC, ThisPtr))
+ return false;
+ }
+
+ if (S.checkingPotentialConstantExpression())
+ return false;
+ }
+
+ if (!CheckCallable(S, OpPC, Func))
+ return false;
+
+ if (!CheckCallDepth(S, OpPC))
+ return false;
+
+ auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC);
+ InterpFrame *FrameBefore = S.Current;
+ S.Current = NewFrame.get();
+
+ APValue CallResult;
+ // Note that we cannot assert(CallResult.hasValue()) here since
+ // Ret() above only sets the APValue if the curent frame doesn't
+ // have a caller set.
+ if (Interpret(S, CallResult)) {
+ NewFrame.release(); // Frame was delete'd already.
+ assert(S.Current == FrameBefore);
+ return true;
+ }
+
+ // Interpreting the function failed somehow. Reset to
+ // previous state.
+ S.Current = FrameBefore;
+ return false;
+}
+
+inline bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func) {
+ assert(Func->hasThisPointer());
+ assert(Func->isVirtual());
+ size_t ThisOffset =
+ Func->getArgSize() - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
+ Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
+
+ const CXXRecordDecl *DynamicDecl =
+ ThisPtr.getDeclDesc()->getType()->getAsCXXRecordDecl();
+ const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl());
+ const auto *InitialFunction = cast<CXXMethodDecl>(Func->getDecl());
+ const CXXMethodDecl *Overrider = S.getContext().getOverridingFunction(
+ DynamicDecl, StaticDecl, InitialFunction);
+
+ if (Overrider != InitialFunction) {
+ // DR1872: An instantiated virtual constexpr function can't be called in a
+ // constant expression (prior to C++20). We can still constant-fold such a
+ // call.
+ if (!S.getLangOpts().CPlusPlus20 && Overrider->isVirtual()) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ S.CCEDiag(E, diag::note_constexpr_virtual_call) << E->getSourceRange();
+ }
+
+ Func = S.getContext().getOrCreateFunction(Overrider);
+
+ const CXXRecordDecl *ThisFieldDecl =
+ ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl();
+ if (Func->getParentDecl()->isDerivedFrom(ThisFieldDecl)) {
+ // If the function we call is further DOWN the hierarchy than the
+ // FieldDesc of our pointer, just get the DeclDesc instead, which
+ // is the furthest we might go up in the hierarchy.
+ ThisPtr = ThisPtr.getDeclPtr();
+ }
+ }
+
+ return Call(S, OpPC, Func);
+}
+
+inline bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,
+ const CallExpr *CE) {
+ auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC);
+
+ InterpFrame *FrameBefore = S.Current;
+ S.Current = NewFrame.get();
+
+ if (InterpretBuiltin(S, PC, Func, CE)) {
+ NewFrame.release();
+ return true;
+ }
+ S.Current = FrameBefore;
+ return false;
+}
+
+inline bool CallPtr(InterpState &S, CodePtr OpPC) {
+ const FunctionPointer &FuncPtr = S.Stk.pop<FunctionPointer>();
+
+ const Function *F = FuncPtr.getFunction();
+ if (!F || !F->isConstexpr())
+ return false;
+
+ if (F->isVirtual())
+ return CallVirt(S, OpPC, F);
+
+ return Call(S, OpPC, F);
+}
+
+inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) {
+ assert(Func);
+ S.Stk.push<FunctionPointer>(Func);
+ return true;
+}
+
+/// Just emit a diagnostic. The expression that caused emission of this
+/// op is not valid in a constant context.
+inline bool Invalid(InterpState &S, CodePtr OpPC) {
+ const SourceLocation &Loc = S.Current->getLocation(OpPC);
+ S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr)
+ << S.Current->getRange(OpPC);
+ return false;
+}
+
+/// Same here, but only for casts.
+inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind) {
+ const SourceLocation &Loc = S.Current->getLocation(OpPC);
+ S.FFDiag(Loc, diag::note_constexpr_invalid_cast)
+ << static_cast<unsigned>(Kind) << S.Current->getRange(OpPC);
+ return false;
+}
+
+inline bool InvalidDeclRef(InterpState &S, CodePtr OpPC,
+ const DeclRefExpr *DR) {
+ assert(DR);
+ return CheckDeclRef(S, OpPC, DR);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
+ llvm::SmallVector<int64_t> ArrayIndices;
+ for (size_t I = 0; I != E->getNumExpressions(); ++I)
+ ArrayIndices.emplace_back(S.Stk.pop<int64_t>());
+
+ int64_t Result;
+ if (!InterpretOffsetOf(S, OpPC, E, ArrayIndices, Result))
+ return false;
+
+ S.Stk.push<T>(T::from(Result));
+
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Read opcode arguments
+//===----------------------------------------------------------------------===//
+
+template <typename T> inline T ReadArg(InterpState &S, CodePtr &OpPC) {
+ if constexpr (std::is_pointer<T>::value) {
+ uint32_t ID = OpPC.read<uint32_t>();
+ return reinterpret_cast<T>(S.P.getNativePointer(ID));
+ } else {
+ return OpPC.read<T>();
+ }
+}
+
+template <> inline Floating ReadArg<Floating>(InterpState &S, CodePtr &OpPC) {
+ Floating F = Floating::deserialize(*OpPC);
+ OpPC += align(F.bytesToSerialize());
+ return F;
+}
} // namespace interp
} // namespace clang