[clang][bytecode][NFC] Move Call ops into Interp.cpp (#107104)
They are quite long and not templated.
This commit is contained in:
@@ -986,6 +986,241 @@ void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
|
||||
}
|
||||
}
|
||||
|
||||
bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
uint32_t VarArgSize) {
|
||||
if (Func->hasThisPointer()) {
|
||||
size_t ArgSize = Func->getArgSize() + VarArgSize;
|
||||
size_t ThisOffset = ArgSize - (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, VarArgSize);
|
||||
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;
|
||||
}
|
||||
|
||||
bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
uint32_t VarArgSize) {
|
||||
if (Func->hasThisPointer()) {
|
||||
size_t ArgSize = Func->getArgSize() + VarArgSize;
|
||||
size_t ThisOffset = ArgSize - (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()) {
|
||||
assert(ThisPtr.isZero());
|
||||
} else {
|
||||
if (!CheckInvoke(S, OpPC, ThisPtr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CheckCallable(S, OpPC, Func))
|
||||
return false;
|
||||
|
||||
// FIXME: The isConstructor() check here is not always right. The current
|
||||
// constant evaluator is somewhat inconsistent in when it allows a function
|
||||
// call when checking for a constant expression.
|
||||
if (Func->hasThisPointer() && S.checkingPotentialConstantExpression() &&
|
||||
!Func->isConstructor())
|
||||
return false;
|
||||
|
||||
if (!CheckCallDepth(S, OpPC))
|
||||
return false;
|
||||
|
||||
auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
|
||||
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;
|
||||
}
|
||||
|
||||
bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
uint32_t VarArgSize) {
|
||||
assert(Func->hasThisPointer());
|
||||
assert(Func->isVirtual());
|
||||
size_t ArgSize = Func->getArgSize() + VarArgSize;
|
||||
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
|
||||
Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
|
||||
|
||||
const CXXRecordDecl *DynamicDecl = nullptr;
|
||||
{
|
||||
Pointer TypePtr = ThisPtr;
|
||||
while (TypePtr.isBaseClass())
|
||||
TypePtr = TypePtr.getBase();
|
||||
|
||||
QualType DynamicType = TypePtr.getType();
|
||||
if (DynamicType->isPointerType() || DynamicType->isReferenceType())
|
||||
DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
|
||||
else
|
||||
DynamicDecl = DynamicType->getAsCXXRecordDecl();
|
||||
}
|
||||
assert(DynamicDecl);
|
||||
|
||||
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 go up the hierarchy of this field
|
||||
// the furthest we can go.
|
||||
while (ThisPtr.isBaseClass())
|
||||
ThisPtr = ThisPtr.getBase();
|
||||
}
|
||||
}
|
||||
|
||||
if (!Call(S, OpPC, Func, VarArgSize))
|
||||
return false;
|
||||
|
||||
// Covariant return types. The return type of Overrider is a pointer
|
||||
// or reference to a class type.
|
||||
if (Overrider != InitialFunction &&
|
||||
Overrider->getReturnType()->isPointerOrReferenceType() &&
|
||||
InitialFunction->getReturnType()->isPointerOrReferenceType()) {
|
||||
QualType OverriderPointeeType =
|
||||
Overrider->getReturnType()->getPointeeType();
|
||||
QualType InitialPointeeType =
|
||||
InitialFunction->getReturnType()->getPointeeType();
|
||||
// We've called Overrider above, but calling code expects us to return what
|
||||
// InitialFunction returned. According to the rules for covariant return
|
||||
// types, what InitialFunction returns needs to be a base class of what
|
||||
// Overrider returns. So, we need to do an upcast here.
|
||||
unsigned Offset = S.getContext().collectBaseOffset(
|
||||
InitialPointeeType->getAsRecordDecl(),
|
||||
OverriderPointeeType->getAsRecordDecl());
|
||||
return GetPtrBasePop(S, OpPC, Offset);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
|
||||
const CallExpr *CE) {
|
||||
const FunctionPointer &FuncPtr = S.Stk.pop<FunctionPointer>();
|
||||
|
||||
const Function *F = FuncPtr.getFunction();
|
||||
if (!F) {
|
||||
const auto *E = cast<CallExpr>(S.Current->getExpr(OpPC));
|
||||
S.FFDiag(E, diag::note_constexpr_null_callee)
|
||||
<< const_cast<Expr *>(E->getCallee()) << E->getSourceRange();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FuncPtr.isValid() || !F->getDecl())
|
||||
return Invalid(S, OpPC);
|
||||
|
||||
assert(F);
|
||||
|
||||
// This happens when the call expression has been cast to
|
||||
// something else, but we don't support that.
|
||||
if (S.Ctx.classify(F->getDecl()->getReturnType()) !=
|
||||
S.Ctx.classify(CE->getType()))
|
||||
return false;
|
||||
|
||||
// Check argument nullability state.
|
||||
if (F->hasNonNullAttr()) {
|
||||
if (!CheckNonNullArgs(S, OpPC, F, CE, ArgSize))
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(ArgSize >= F->getWrittenArgSize());
|
||||
uint32_t VarArgSize = ArgSize - F->getWrittenArgSize();
|
||||
|
||||
// We need to do this explicitly here since we don't have the necessary
|
||||
// information to do it automatically.
|
||||
if (F->isThisPointerExplicit())
|
||||
VarArgSize -= align(primSize(PT_Ptr));
|
||||
|
||||
if (F->isVirtual())
|
||||
return CallVirt(S, OpPC, F, VarArgSize);
|
||||
|
||||
return Call(S, OpPC, F, VarArgSize);
|
||||
}
|
||||
|
||||
bool Interpret(InterpState &S, APValue &Result) {
|
||||
// The current stack frame when we started Interpret().
|
||||
// This is being used by the ops to determine wheter
|
||||
|
||||
@@ -147,6 +147,17 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
|
||||
/// Copy the contents of Src into Dest.
|
||||
bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest);
|
||||
|
||||
bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
uint32_t VarArgSize);
|
||||
bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
uint32_t VarArgSize);
|
||||
bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
uint32_t VarArgSize);
|
||||
bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,
|
||||
const CallExpr *CE);
|
||||
bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
|
||||
const CallExpr *CE);
|
||||
|
||||
/// Checks if the shift operation is legal.
|
||||
template <typename LT, typename RT>
|
||||
bool CheckShift(InterpState &S, CodePtr OpPC, const LT &LHS, const RT &RHS,
|
||||
@@ -2593,243 +2604,6 @@ inline bool ArrayDecay(InterpState &S, CodePtr OpPC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
uint32_t VarArgSize) {
|
||||
if (Func->hasThisPointer()) {
|
||||
size_t ArgSize = Func->getArgSize() + VarArgSize;
|
||||
size_t ThisOffset = ArgSize - (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, VarArgSize);
|
||||
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;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
|
||||
uint32_t VarArgSize) {
|
||||
if (Func->hasThisPointer()) {
|
||||
size_t ArgSize = Func->getArgSize() + VarArgSize;
|
||||
size_t ThisOffset = ArgSize - (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()) {
|
||||
assert(ThisPtr.isZero());
|
||||
} else {
|
||||
if (!CheckInvoke(S, OpPC, ThisPtr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CheckCallable(S, OpPC, Func))
|
||||
return false;
|
||||
|
||||
// FIXME: The isConstructor() check here is not always right. The current
|
||||
// constant evaluator is somewhat inconsistent in when it allows a function
|
||||
// call when checking for a constant expression.
|
||||
if (Func->hasThisPointer() && S.checkingPotentialConstantExpression() &&
|
||||
!Func->isConstructor())
|
||||
return false;
|
||||
|
||||
if (!CheckCallDepth(S, OpPC))
|
||||
return false;
|
||||
|
||||
auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
|
||||
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,
|
||||
uint32_t VarArgSize) {
|
||||
assert(Func->hasThisPointer());
|
||||
assert(Func->isVirtual());
|
||||
size_t ArgSize = Func->getArgSize() + VarArgSize;
|
||||
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
|
||||
Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
|
||||
|
||||
const CXXRecordDecl *DynamicDecl = nullptr;
|
||||
{
|
||||
Pointer TypePtr = ThisPtr;
|
||||
while (TypePtr.isBaseClass())
|
||||
TypePtr = TypePtr.getBase();
|
||||
|
||||
QualType DynamicType = TypePtr.getType();
|
||||
if (DynamicType->isPointerType() || DynamicType->isReferenceType())
|
||||
DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
|
||||
else
|
||||
DynamicDecl = DynamicType->getAsCXXRecordDecl();
|
||||
}
|
||||
assert(DynamicDecl);
|
||||
|
||||
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 go up the hierarchy of this field
|
||||
// the furthest we can go.
|
||||
while (ThisPtr.isBaseClass())
|
||||
ThisPtr = ThisPtr.getBase();
|
||||
}
|
||||
}
|
||||
|
||||
if (!Call(S, OpPC, Func, VarArgSize))
|
||||
return false;
|
||||
|
||||
// Covariant return types. The return type of Overrider is a pointer
|
||||
// or reference to a class type.
|
||||
if (Overrider != InitialFunction &&
|
||||
Overrider->getReturnType()->isPointerOrReferenceType() &&
|
||||
InitialFunction->getReturnType()->isPointerOrReferenceType()) {
|
||||
QualType OverriderPointeeType =
|
||||
Overrider->getReturnType()->getPointeeType();
|
||||
QualType InitialPointeeType =
|
||||
InitialFunction->getReturnType()->getPointeeType();
|
||||
// We've called Overrider above, but calling code expects us to return what
|
||||
// InitialFunction returned. According to the rules for covariant return
|
||||
// types, what InitialFunction returns needs to be a base class of what
|
||||
// Overrider returns. So, we need to do an upcast here.
|
||||
unsigned Offset = S.getContext().collectBaseOffset(
|
||||
InitialPointeeType->getAsRecordDecl(),
|
||||
OverriderPointeeType->getAsRecordDecl());
|
||||
return GetPtrBasePop(S, OpPC, Offset);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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, uint32_t ArgSize,
|
||||
const CallExpr *CE) {
|
||||
const FunctionPointer &FuncPtr = S.Stk.pop<FunctionPointer>();
|
||||
|
||||
const Function *F = FuncPtr.getFunction();
|
||||
if (!F) {
|
||||
const auto *E = cast<CallExpr>(S.Current->getExpr(OpPC));
|
||||
S.FFDiag(E, diag::note_constexpr_null_callee)
|
||||
<< const_cast<Expr *>(E->getCallee()) << E->getSourceRange();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FuncPtr.isValid() || !F->getDecl())
|
||||
return Invalid(S, OpPC);
|
||||
|
||||
assert(F);
|
||||
|
||||
// This happens when the call expression has been cast to
|
||||
// something else, but we don't support that.
|
||||
if (S.Ctx.classify(F->getDecl()->getReturnType()) !=
|
||||
S.Ctx.classify(CE->getType()))
|
||||
return false;
|
||||
|
||||
// Check argument nullability state.
|
||||
if (F->hasNonNullAttr()) {
|
||||
if (!CheckNonNullArgs(S, OpPC, F, CE, ArgSize))
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(ArgSize >= F->getWrittenArgSize());
|
||||
uint32_t VarArgSize = ArgSize - F->getWrittenArgSize();
|
||||
|
||||
// We need to do this explicitly here since we don't have the necessary
|
||||
// information to do it automatically.
|
||||
if (F->isThisPointerExplicit())
|
||||
VarArgSize -= align(primSize(PT_Ptr));
|
||||
|
||||
if (F->isVirtual())
|
||||
return CallVirt(S, OpPC, F, VarArgSize);
|
||||
|
||||
return Call(S, OpPC, F, VarArgSize);
|
||||
}
|
||||
|
||||
inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) {
|
||||
assert(Func);
|
||||
S.Stk.push<FunctionPointer>(Func);
|
||||
|
||||
Reference in New Issue
Block a user