- implemented processing of multiple return values in script functions.

This commit is contained in:
Christoph Oelckers 2017-01-18 01:27:50 +01:00
parent 232b64d332
commit ade9e4c3da
4 changed files with 80 additions and 27 deletions

View file

@ -9956,37 +9956,76 @@ ExpEmit FxJumpStatement::Emit(VMFunctionBuilder *build)
//==========================================================================
FxReturnStatement::FxReturnStatement(FxExpression *value, const FScriptPosition &pos)
: FxExpression(EFX_ReturnStatement, pos), Value(value)
: FxExpression(EFX_ReturnStatement, pos)
{
if (value != nullptr) Args.Push(value);
ValueType = TypeVoid;
}
FxReturnStatement::FxReturnStatement(FArgumentList &values, const FScriptPosition &pos)
: FxExpression(EFX_ReturnStatement, pos)
{
Args = std::move(values);
ValueType = TypeVoid;
}
FxReturnStatement::~FxReturnStatement()
{
SAFE_DELETE(Value);
}
FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
{
bool fail = false;
CHECKRESOLVED();
SAFE_RESOLVE_OPT(Value, ctx);
for (auto &Value : Args)
{
SAFE_RESOLVE_OPT(Value, ctx);
fail |= (Value == nullptr);
}
if (fail)
{
delete this;
return nullptr;
}
PPrototype *retproto;
if (Value == nullptr)
if (Args.Size() == 0)
{
TArray<PType *> none(0);
retproto = NewPrototype(none, none);
}
else
else if (Args.Size() == 1)
{
// If we already know the real return type we need at least try to cast the value to its proper type (unless in an anonymous function.)
if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() > 0 && ctx.Function->SymbolName != NAME_None)
{
Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[0], false, false);
Value = Value->Resolve(ctx);
ABORT(Value);
Args[0] = new FxTypeCast(Args[0], ctx.ReturnProto->ReturnTypes[0], false, false);
Args[0] = Args[0]->Resolve(ctx);
ABORT(Args[0]);
}
retproto = Value->ReturnProto();
retproto = Args[0]->ReturnProto();
}
else if (ctx.ReturnProto != nullptr && ctx.ReturnProto->ReturnTypes.Size() == Args.Size())
{
for (unsigned i = 0; i < Args.Size(); i++)
{
auto &Value = Args[0];
Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[i], false, false);
Value = Value->Resolve(ctx);
if (Value == nullptr) fail = true;
}
if (fail)
{
delete this;
return nullptr;
}
return this; // no point calling CheckReturn here.
}
else
{
ScriptPosition.Message(MSG_ERROR, "Incorrect number of return values. Got %u, but expected %u", Args.Size(), ctx.ReturnProto->ReturnTypes.Size());
delete this;
return nullptr;
}
ctx.CheckReturn(retproto, ScriptPosition);
@ -9996,8 +10035,20 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx)
ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
{
TArray<ExpEmit> outs;
ExpEmit out(0, REGT_NIL);
// If there's structs to destroy here we need to emit all returns before destroying them.
if (build->ConstructedStructs.Size())
{
for (auto ret : Args)
{
ExpEmit r = ret->Emit(build);
outs.Push(r);
}
}
// call the destructors for all structs requiring one.
// go in reverse order of construction
for (int i = build->ConstructedStructs.Size() - 1; i >= 0; i--)
@ -10013,19 +10064,19 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
// If we return nothing, use a regular RET opcode.
// Otherwise just return the value we're given.
if (Value == nullptr)
if (Args.Size() == 0)
{
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
}
else
else if (Args.Size() == 1)
{
out = Value->Emit(build);
out = outs.Size() > 0? outs[0] : Args[0]->Emit(build);
// Check if it is a function call that simplified itself
// into a tail call in which case we don't emit anything.
if (!out.Final)
{
if (Value->ValueType == TypeVoid)
if (Args[0]->ValueType == TypeVoid)
{ // Nothing is returned.
build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0);
}
@ -10035,6 +10086,14 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
}
}
}
else
{
for (unsigned i = 0; i < Args.Size(); i++)
{
out = outs.Size() > 0 ? outs[i] : Args[i]->Emit(build);
build->Emit(OP_RET, i < Args.Size() - 1 ? i : i+RET_FINAL, EncodeRegType(out), out.RegNum);
}
}
out.Final = true;
return out;
@ -10042,9 +10101,9 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
VMFunction *FxReturnStatement::GetDirectFunction()
{
if (Value != nullptr)
if (Args.Size() == 1)
{
return Value->GetDirectFunction();
return Args[0]->GetDirectFunction();
}
return nullptr;
}

View file

@ -1911,10 +1911,11 @@ public:
class FxReturnStatement : public FxExpression
{
FxExpression *Value;
FArgumentList Args;
public:
FxReturnStatement(FxExpression *value, const FScriptPosition &pos);
FxReturnStatement(FArgumentList &args, const FScriptPosition &pos);
~FxReturnStatement();
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);

View file

@ -3314,16 +3314,9 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
{
return new FxReturnStatement(nullptr, *ast);
}
else if (args.Size() == 1)
{
auto arg = args[0];
args[0] = nullptr;
return new FxReturnStatement(arg, *ast);
}
else
{
Error(ast, "Return with multiple values not implemented yet.");
return new FxReturnStatement(nullptr, *ast);
return new FxReturnStatement(args, *ast);
}
}

View file

@ -116,7 +116,7 @@ class PlayerPawn : Actor native
}
// This is for SBARINFO.
int/*, int*/ GetEffectTicsForItem(class<Inventory> item)
int, int GetEffectTicsForItem(class<Inventory> item)
{
let pg = (class<PowerupGiver>)(item);
if (pg != null)
@ -127,10 +127,10 @@ class PlayerPawn : Actor native
{
let maxtics = GetDefaultByType(powerupType).EffectTics;
if (maxtics == 0) maxtics = powerup.default.EffectTics;
return powerup.EffectTics/*, maxtics*/;
return powerup.EffectTics, maxtics;
}
}
return 0/*, 0*/;
return 0, 0;
}
native int GetMaxHealth();