diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 9fbfe3ef8..fe32fbb3e 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -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 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 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; } diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index c31c42b07..8b43d28fa 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -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); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index c1cd274f0..da10c0cfb 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -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); } } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index b7799d731..c3f3c6768 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -116,7 +116,7 @@ class PlayerPawn : Actor native } // This is for SBARINFO. - int/*, int*/ GetEffectTicsForItem(class item) + int, int GetEffectTicsForItem(class item) { let pg = (class)(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();