From 7c6a17647fdd12a673623634a1e0fc659e989490 Mon Sep 17 00:00:00 2001 From: HolonProduction Date: Tue, 27 Jan 2026 16:47:14 +0100 Subject: [PATCH] GDScript: Prevent function states from leaking into typed temporaries, when overwriting with coroutines --- modules/gdscript/gdscript_compiler.cpp | 7 ++++-- .../await_with_overwritten_coroutine.gd | 24 +++++++++++++++++++ .../await_with_overwritten_coroutine.out | 4 ++++ 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 modules/gdscript/tests/scripts/runtime/features/await_with_overwritten_coroutine.gd create mode 100644 modules/gdscript/tests/scripts/runtime/features/await_with_overwritten_coroutine.out diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 44e2daac97..0824b4399a 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -603,12 +603,15 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code case GDScriptParser::Node::CALL: { const GDScriptParser::CallNode *call = static_cast(p_expression); bool is_awaited = p_expression == awaited_node; - GDScriptDataType type = _gdtype_from_datatype(call->get_datatype(), codegen.script); GDScriptCodeGenerator::Address result; if (p_root) { result = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL); + } else if (is_awaited) { + // When called with await, we need to use an untyped temporary regardless of whether the base + // class function is a coroutine, because an inherited class could return a function state object. + result = codegen.add_temporary(); } else { - result = codegen.add_temporary(type); + result = codegen.add_temporary(_gdtype_from_datatype(call->get_datatype(), codegen.script)); } Vector arguments; diff --git a/modules/gdscript/tests/scripts/runtime/features/await_with_overwritten_coroutine.gd b/modules/gdscript/tests/scripts/runtime/features/await_with_overwritten_coroutine.gd new file mode 100644 index 0000000000..6b2769ab27 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/await_with_overwritten_coroutine.gd @@ -0,0 +1,24 @@ +class BaseClass: + signal release_bool + + func get_bool() -> bool: + return true + +class InheritedClass extends BaseClass: + func get_bool() -> bool: + await release_bool + return false + + +var instance: BaseClass = InheritedClass.new() + +func test() -> void: + @warning_ignore("missing_await") + await_with_overwritten_coroutine() + instance.release_bool.emit() + +func await_with_overwritten_coroutine(): + var res_v := await instance.get_bool() + print(res_v == true) + var result: bool = bool(res_v) + print(result == true) diff --git a/modules/gdscript/tests/scripts/runtime/features/await_with_overwritten_coroutine.out b/modules/gdscript/tests/scripts/runtime/features/await_with_overwritten_coroutine.out new file mode 100644 index 0000000000..f19faa633c --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/await_with_overwritten_coroutine.out @@ -0,0 +1,4 @@ +GDTEST_OK +~~ WARNING at line 21: (REDUNDANT_AWAIT) "await" keyword is unnecessary because the expression isn't a coroutine nor a signal. +false +false