Implement Static Function Pointers

This commit is contained in:
Ricardo Luís Vaz Silva 2023-02-10 09:44:32 -03:00 committed by Christoph Oelckers
commit e3704605d8
23 changed files with 876 additions and 51 deletions

View file

@ -297,6 +297,13 @@ bool AreCompatiblePointerTypes(PType *dest, PType *source, bool forcompare)
if (forcompare && tocls->IsDescendantOf(fromcls)) return true;
return (fromcls->IsDescendantOf(tocls));
}
if(source->isFunctionPointer() && dest->isFunctionPointer())
{
auto from = static_cast<PFunctionPointer*>(source);
auto to = static_cast<PFunctionPointer*>(dest);
return to->PointedType == TypeVoid || (from->PointedType == to->PointedType && from->ArgFlags == to->ArgFlags && FScopeBarrier::CheckSidesForFunctionPointer(from->Scope, to->Scope));
// TODO allow narrowing argument types and widening return types via cast, ex.: Function<Actor(Object or Class<Object>)> to Function<Object(Actor or Class<Actor>)>
}
}
return false;
}
@ -8379,6 +8386,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
PContainerType *cls = nullptr;
bool staticonly = false;
bool novirtual = false;
bool fnptr = false;
bool isreadonly = false;
PContainerType *ccls = nullptr;
@ -8996,6 +9004,22 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
cls = static_cast<PStruct*>(Self->ValueType);
Self->ValueType = NewPointer(Self->ValueType);
}
else if (Self->ValueType->isFunctionPointer())
{
auto fn = static_cast<PFunctionPointer*>(Self->ValueType);
if(MethodName == NAME_Call && fn->PointedType != TypeVoid)
{ // calling a Function<void> pointer isn't allowed
fnptr = true;
afd_override = fn->FakeFunction;
}
else
{
ScriptPosition.Message(MSG_ERROR, "Unknown function %s", MethodName.GetChars());
delete this;
return nullptr;
}
}
else
{
ScriptPosition.Message(MSG_ERROR, "Invalid expression on left hand side of %s", MethodName.GetChars());
@ -9115,8 +9139,8 @@ isresolved:
}
// do not pass the self pointer to static functions.
auto self = (afd->Variants[0].Flags & VARF_Method) ? Self : nullptr;
auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, staticonly|novirtual);
auto self = ( fnptr || (afd->Variants[0].Flags & VARF_Method)) ? Self : nullptr;
auto x = new FxVMFunctionCall(self, afd, ArgList, ScriptPosition, (staticonly || novirtual) && !fnptr);
if (Self == self) Self = nullptr;
delete this;
return x->Resolve(ctx);
@ -9130,7 +9154,7 @@ isresolved:
//==========================================================================
FxVMFunctionCall::FxVMFunctionCall(FxExpression *self, PFunction *func, FArgumentList &args, const FScriptPosition &pos, bool novirtual)
: FxExpression(EFX_VMFunctionCall, pos)
: FxExpression(EFX_VMFunctionCall, pos) , FnPtrCall((self && self->ValueType) ? self->ValueType->isFunctionPointer() : false)
{
Self = self;
Function = func;
@ -9204,7 +9228,7 @@ VMFunction *FxVMFunctionCall::GetDirectFunction(PFunction *callingfunc, const Ve
// definition can call that function directly without wrapping
// it inside VM code.
if (ArgList.Size() == 0 && !(Function->Variants[0].Flags & VARF_Virtual) && CheckAccessibility(ver) && CheckFunctionCompatiblity(ScriptPosition, callingfunc, Function))
if (ArgList.Size() == 0 && !(Function->Variants[0].Flags & VARF_Virtual) && !FnPtrCall && CheckAccessibility(ver) && CheckFunctionCompatiblity(ScriptPosition, callingfunc, Function))
{
unsigned imp = Function->GetImplicitArgs();
if (Function->Variants[0].ArgFlags.Size() > imp && !(Function->Variants[0].ArgFlags[imp] & VARF_Optional)) return nullptr;
@ -9229,7 +9253,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
auto &argtypes = proto->ArgumentTypes;
auto &argnames = Function->Variants[0].ArgNames;
auto &argflags = Function->Variants[0].ArgFlags;
auto &defaults = Function->Variants[0].Implementation->DefaultArgs;
auto *defaults = FnPtrCall ? nullptr : &Function->Variants[0].Implementation->DefaultArgs;
int implicit = Function->GetImplicitArgs();
@ -9297,6 +9321,12 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
if (ArgList[i]->ExprType == EFX_NamedNode)
{
if(FnPtrCall)
{
ScriptPosition.Message(MSG_ERROR, "Named arguments not supported in function pointer calls");
delete this;
return nullptr;
}
if (!(flag & VARF_Optional))
{
ScriptPosition.Message(MSG_ERROR, "Cannot use a named argument here - not all required arguments have been passed.");
@ -9346,7 +9376,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
}
if (ntype->GetRegCount() == 1)
{
auto x = new FxConstant(ntype, defaults[i + k + skipdefs + implicit], ScriptPosition);
auto x = new FxConstant(ntype, (*defaults)[i + k + skipdefs + implicit], ScriptPosition);
ArgList.Insert(i + k, x);
}
else
@ -9355,7 +9385,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
FxConstant *cs[4] = { nullptr };
for (int l = 0; l < ntype->GetRegCount(); l++)
{
cs[l] = new FxConstant(TypeFloat64, defaults[l + i + k + skipdefs + implicit], ScriptPosition);
cs[l] = new FxConstant(TypeFloat64, (*defaults)[l + i + k + skipdefs + implicit], ScriptPosition);
}
FxExpression *x = new FxVectorValue(cs[0], cs[1], cs[2], cs[3], ScriptPosition);
ArgList.Insert(i + k, x);
@ -9492,6 +9522,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
{
ValueType = TypeVoid;
}
return this;
}
@ -9517,11 +9548,14 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
}
}
VMFunction *vmfunc = Function->Variants[0].Implementation;
bool staticcall = ((vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual);
VMFunction *vmfunc = FnPtrCall ? nullptr : Function->Variants[0].Implementation;
bool staticcall = (FnPtrCall || (vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual);
count = 0;
FunctionCallEmitter emitters(vmfunc);
assert(!FnPtrCall || (FnPtrCall && Self && Self->ValueType && Self->ValueType->isFunctionPointer()));
FunctionCallEmitter emitters(FnPtrCall ? FunctionCallEmitter(PType::toFunctionPointer(Self->ValueType)) : FunctionCallEmitter(vmfunc));
// Emit code to pass implied parameters
ExpEmit selfemit;
if (Function->Variants[0].Flags & VARF_Method)
@ -9565,43 +9599,56 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
}
}
}
else if (FnPtrCall)
{
assert(Self != nullptr);
selfemit = Self->Emit(build);
assert(selfemit.RegType == REGT_POINTER);
staticcall = false;
}
else staticcall = true;
// Emit code to pass explicit parameters
for (unsigned i = 0; i < ArgList.Size(); ++i)
{
emitters.AddParameter(build, ArgList[i]);
}
// Complete the parameter list from the defaults.
auto &defaults = Function->Variants[0].Implementation->DefaultArgs;
for (unsigned i = emitters.Count(); i < defaults.Size(); i++)
if(!FnPtrCall)
{
switch (defaults[i].Type)
// Complete the parameter list from the defaults.
auto &defaults = Function->Variants[0].Implementation->DefaultArgs;
for (unsigned i = emitters.Count(); i < defaults.Size(); i++)
{
default:
case REGT_INT:
emitters.AddParameterIntConst(defaults[i].i);
break;
case REGT_FLOAT:
emitters.AddParameterFloatConst(defaults[i].f);
break;
case REGT_POINTER:
emitters.AddParameterPointerConst(defaults[i].a);
break;
case REGT_STRING:
emitters.AddParameterStringConst(defaults[i].s());
break;
switch (defaults[i].Type)
{
default:
case REGT_INT:
emitters.AddParameterIntConst(defaults[i].i);
break;
case REGT_FLOAT:
emitters.AddParameterFloatConst(defaults[i].f);
break;
case REGT_POINTER:
emitters.AddParameterPointerConst(defaults[i].a);
break;
case REGT_STRING:
emitters.AddParameterStringConst(defaults[i].s());
break;
}
}
}
ArgList.DeleteAndClear();
ArgList.ShrinkToFit();
if (!staticcall) emitters.SetVirtualReg(selfemit.RegNum);
int resultcount = vmfunc->Proto->ReturnTypes.Size() == 0 ? 0 : max(AssignCount, 1);
assert((unsigned)resultcount <= vmfunc->Proto->ReturnTypes.Size());
PPrototype * proto = FnPtrCall ? static_cast<PPrototype*>(static_cast<PFunctionPointer*>(Self->ValueType)->PointedType) : vmfunc->Proto;
int resultcount = proto->ReturnTypes.Size() == 0 ? 0 : max(AssignCount, 1);
assert((unsigned)resultcount <= proto->ReturnTypes.Size());
for (int i = 0; i < resultcount; i++)
{
emitters.AddReturn(vmfunc->Proto->ReturnTypes[i]->GetRegType(), vmfunc->Proto->ReturnTypes[i]->GetRegCount());
emitters.AddReturn(proto->ReturnTypes[i]->GetRegType(), proto->ReturnTypes[i]->GetRegCount());
}
return emitters.EmitCall(build, resultcount > 1? &ReturnRegs : nullptr);
}
@ -11629,6 +11676,107 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build)
return emitters.EmitCall(build);
}
//==========================================================================
//
//==========================================================================
FxFunctionPtrCast::FxFunctionPtrCast(PFunctionPointer *ftype, FxExpression *x)
: FxExpression(EFX_FunctionPtrCast, x->ScriptPosition)
{
ValueType = ftype;
basex = x;
}
//==========================================================================
//
//
//
//==========================================================================
FxFunctionPtrCast::~FxFunctionPtrCast()
{
SAFE_DELETE(basex);
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxFunctionPtrCast::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE(basex, ctx);
if (!(basex->ValueType && basex->ValueType->isFunctionPointer()))
{
delete this;
return nullptr;
}
auto to = static_cast<PFunctionPointer *>(ValueType);
auto from = static_cast<PFunctionPointer *>(basex->ValueType);
if(to->PointedType == TypeVoid)
{ // no need to do anything for (Function<void)(...) casts
basex->ValueType = ValueType;
auto x = basex;
basex = nullptr;
delete this;
return x;
}
else if(from->PointedType == TypeVoid)
{ // nothing to check at compile-time for casts from Function<void>
return this;
}
else
{
// TODO allow narrowing argument types and widening return types via cast, ex.: Function<Actor(Object or Class<Object>)> to Function<Object(Actor or Class<Actor>)>
ScriptPosition.Message(MSG_ERROR, "Cannot cast %s to %s. The types are incompatible.", basex->ValueType->DescriptiveName(), to->DescriptiveName());
delete this;
return nullptr;
}
}
//==========================================================================
//
//
//
//==========================================================================
PFunction *NativeFunctionPointerCast(PFunction *from, const PFunctionPointer *to)
{
// TODO allow narrowing argument types and widening return types via cast, ex.: Function<Actor(Object or Class<Object>)> to Function<Object(Actor or Class<Actor>)>
return (to->PointedType == TypeVoid || (from &&
( from->Variants[0].Proto == static_cast<PPrototype*>(to->PointedType)
&& from->Variants[0].ArgFlags == to->ArgFlags
&& FScopeBarrier::CheckSidesForFunctionPointer(FScopeBarrier::SideFromFlags(from->Variants[0].Flags), to->Scope)
))) ? from : nullptr;
}
DEFINE_ACTION_FUNCTION_NATIVE(DObject, BuiltinFunctionPtrCast, NativeFunctionPointerCast)
{
PARAM_PROLOGUE;
PARAM_POINTER(from, PFunction);
PARAM_POINTER(to, PFunctionPointer);
ACTION_RETURN_POINTER(NativeFunctionPointerCast(from, to));
}
ExpEmit FxFunctionPtrCast::Emit(VMFunctionBuilder *build)
{
ExpEmit funcptr = basex->Emit(build);
auto sym = FindBuiltinFunction(NAME_BuiltinFunctionPtrCast);
assert(sym);
FunctionCallEmitter emitters(sym->Variants[0].Implementation);
emitters.AddParameter(funcptr, false);
emitters.AddParameterPointerConst(ValueType);
emitters.AddReturn(REGT_POINTER);
return emitters.EmitCall(build);
}
//==========================================================================
//
// declares a single local variable (no arrays)