diff --git a/common/type_system/state.cpp b/common/type_system/state.cpp index ba0ebde8c4..e96e43eb88 100644 --- a/common/type_system/state.cpp +++ b/common/type_system/state.cpp @@ -1,4 +1,5 @@ #include "state.h" +#include "common/type_system/TypeSystem.h" /*! * Convert a (state ...) to the function required to go. Must be state. @@ -81,4 +82,51 @@ TypeSpec get_state_handler_type(StateHandler kind, const TypeSpec& state_type) { } result.add_or_modify_tag("behavior", state_type.last_arg().base_type()); return result; +} + +namespace { +TypeSpec func_to_state_type(const TypeSpec& func_type, const TypeSpec& proc_type) { + TypeSpec result("state"); + for (int i = 0; i < ((int)func_type.arg_count()) - 1; i++) { + result.add_arg(func_type.get_arg(i)); + } + result.add_arg(proc_type); + return result; +} +} // namespace + +std::optional get_state_type_from_enter_and_code(const TypeSpec& enter_func_type, + const TypeSpec& code_func_type, + const TypeSpec& proc_type, + const TypeSystem& ts) { + bool enter_real_func = + enter_func_type.base_type() == "function" && enter_func_type.arg_count() > 0; + bool code_real_func = code_func_type.base_type() == "function" && code_func_type.arg_count() > 0; + + if (enter_real_func && code_real_func) { + int i = 0; + TypeSpec result("state"); + for (; i < std::min((int)enter_func_type.arg_count(), (int)code_func_type.arg_count()) - 1; + i++) { + result.add_arg( + ts.lowest_common_ancestor(enter_func_type.get_arg(i), code_func_type.get_arg(i))); + } + + for (; i < ((int)enter_func_type.arg_count()) - 1; i++) { + result.add_arg(enter_func_type.get_arg(i)); + } + + for (; i < ((int)code_func_type.arg_count()) - 1; i++) { + result.add_arg(code_func_type.get_arg(i)); + } + + result.add_arg(proc_type); + return result; + } else if (enter_real_func) { + return func_to_state_type(enter_func_type, proc_type); + } else if (code_real_func) { + return func_to_state_type(code_func_type, proc_type); + } else { + return {}; + } } \ No newline at end of file diff --git a/common/type_system/state.h b/common/type_system/state.h index 1683fe1fb6..6803aa5f1d 100644 --- a/common/type_system/state.h +++ b/common/type_system/state.h @@ -1,5 +1,6 @@ #pragma once +#include #include "common/type_system/TypeSpec.h" /*! @@ -8,8 +9,15 @@ enum class StateHandler { ENTER, EXIT, CODE, TRANS, POST, EVENT }; +class TypeSystem; + TypeSpec state_to_go_function(const TypeSpec& state_type); StateHandler handler_name_to_kind(const std::string& name); std::string handler_kind_to_name(StateHandler kind); TypeSpec get_state_handler_type(const std::string& handler_name, const TypeSpec& state_type); -TypeSpec get_state_handler_type(StateHandler kind, const TypeSpec& state_type); \ No newline at end of file +TypeSpec get_state_handler_type(StateHandler kind, const TypeSpec& state_type); + +std::optional get_state_type_from_enter_and_code(const TypeSpec& enter_func_type, + const TypeSpec& code_func_type, + const TypeSpec& proc_type, + const TypeSystem& ts); \ No newline at end of file diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 2ba97c6503..a200ff2a90 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -2700,9 +2700,18 @@ void FunctionCallElement::update_from_stack(const Env& env, std::swap(all_pop_vars.at(0), all_pop_vars.at(1)); } - if (tp_type.kind == TP_Type::Kind::RUN_FUNCTION_IN_PROCESS_FUNCTION && - unstacked.at(0)->to_string(env) == "run-function-in-process") { - unstacked.at(0) = pool.form("run-now-in-process"); + if (tp_type.kind == TP_Type::Kind::RUN_FUNCTION_IN_PROCESS_FUNCTION) { + if (unstacked.at(0)->to_string(env) == "run-function-in-process") { + unstacked.at(0) = pool.form("run-now-in-process"); + } else { + // couldn't pop. need to add a cast. + TypeSpec failed_cast("function"); + for (int i = 0; i < ((int)unstacked.size()) - 1; i++) { + failed_cast.add_arg(TypeSpec("object")); + } + failed_cast.add_arg(TypeSpec("none")); + unstacked.at(0) = pool.form(failed_cast, unstacked.at(0)); + } } if (tp_type.kind == TP_Type::Kind::SET_TO_RUN_FUNCTION) { diff --git a/decompiler/main.cpp b/decompiler/main.cpp index 4946fd7b9f..b9984e6539 100644 --- a/decompiler/main.cpp +++ b/decompiler/main.cpp @@ -26,7 +26,14 @@ int main(int argc, char** argv) { } // collect all files to process - auto config = read_config_file(argv[1]); + Config config; + try { + config = read_config_file(argv[1]); + } catch (const std::exception& e) { + lg::error("Failed to parse config"); + return 1; + } + std::string in_folder = argv[2]; std::string out_folder = argv[3]; diff --git a/goalc/compiler/compilation/State.cpp b/goalc/compiler/compilation/State.cpp index 1646c2792a..b494528fd1 100644 --- a/goalc/compiler/compilation/State.cpp +++ b/goalc/compiler/compilation/State.cpp @@ -60,20 +60,33 @@ Val* Compiler::compile_define_state_hook(const goos::Object& form, do_set(form, code_field, code_value->to_gpr(form, env), code_value, env); // state name - TypeSpec state_type("state"); + auto state_type = get_state_type_from_enter_and_code(enter_value->type(), code_value->type(), + state_parent_type, m_ts); - for (int i = 0; i < (int)code_value->type().arg_count() - 1; i++) { - state_type.add_arg(code_value->type().get_arg(i)); - } - state_type.add_arg(state_parent_type); auto state_name = args.unnamed.at(0).as_symbol()->name; auto existing_var = m_symbol_types.find(state_name); - if (existing_var != m_symbol_types.end() && existing_var->second != state_type) { - throw_compiler_error(form, "define-state would redefine the type of symbol {} from {} to {}", - state_name, existing_var->second.print(), state_type.print()); + + TypeSpec type_to_use; + if (existing_var == m_symbol_types.end()) { + // we're a new state. we must have a type. + if (!state_type) { + throw_compiler_error(form, + "define-state doesn't have enough information to determine the type of " + "state {}, and it was not forward declared.", + state_name); + } + type_to_use = *state_type; + m_symbol_types[state_name] = *state_type; + } else { + type_to_use = existing_var->second; + if (state_type) { + typecheck(form, existing_var->second, *state_type, + fmt::format("type of state {}", state_name)); + } } - m_symbol_types[state_name] = state_type; - auto sym_val = env->function_env()->alloc_val(state_name, state_type); + + auto sym_val = env->function_env()->alloc_val(state_name, type_to_use); + env->emit_ir(form, sym_val, state_object); return get_none(); diff --git a/test/decompiler/reference/engine/game/effect-control_REF.gc b/test/decompiler/reference/engine/game/effect-control_REF.gc index 577c40db73..168fd7e230 100644 --- a/test/decompiler/reference/engine/game/effect-control_REF.gc +++ b/test/decompiler/reference/engine/game/effect-control_REF.gc @@ -476,7 +476,10 @@ ) ) ) - (s2-1 + ((the-as + (function object object object object object object object object none) + s2-1 + ) s1-0 s0-0 (the-as sparticle-launch-group s3-0) @@ -577,7 +580,19 @@ ) ) ) - (s2-3 s1-2 s0-2 s3-0 sv-224 sv-240 sv-256 sv-272 t3-1) + ((the-as + (function object object object object object object object object none) + s2-3 + ) + s1-2 + s0-2 + s3-0 + sv-224 + sv-240 + sv-256 + sv-272 + t3-1 + ) ) ) (-> s4-3 ppointer) diff --git a/test/offline/offline_test_main.cpp b/test/offline/offline_test_main.cpp index e1e991bcf5..e960a89362 100644 --- a/test/offline/offline_test_main.cpp +++ b/test/offline/offline_test_main.cpp @@ -293,7 +293,7 @@ CompareResult compare(Decompiler& dc, const std::vector& refs, b compare_result.failing_files.push_back(file.unique_name); compare_result.total_pass = false; fmt::print("Reference test failure on {}:\n", file.unique_name); - fmt::print("{}\n", diff_strings(result, ref)); + fmt::print("{}\n", diff_strings(ref, result)); if (dump_mode) { file_util::create_dir_if_needed("./failures");