From 87c90a573c26ddcbe1b5d9f523b57a89d76dc6df Mon Sep 17 00:00:00 2001 From: George Marques Date: Tue, 9 Jul 2024 12:45:07 -0300 Subject: [PATCH] GDScript: Call setter on simple setter chain without getter Fixes a bug where a member variable was being set directly before calling the setter. --- modules/gdscript/gdscript_compiler.cpp | 13 +++++++++++++ .../features/simple_setter_chain_call_setter.gd | 13 +++++++++++++ .../features/simple_setter_chain_call_setter.out | 4 ++++ 3 files changed, 30 insertions(+) create mode 100644 modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd create mode 100644 modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 5469dad3f788..b0ac4aa80077 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1064,12 +1064,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Get at (potential) root stack pos, so it can be returned. GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, chain.back()->get()->base); + if (r_error) { return GDScriptCodeGenerator::Address(); } GDScriptCodeGenerator::Address prev_base = base; + // In case the base has a setter, don't use the address directly, as we want to call that setter. + // So use a temp value instead and call the setter at the end. + GDScriptCodeGenerator::Address base_temp; + if (base.mode == GDScriptCodeGenerator::Address::MEMBER && member_property_has_setter && !member_property_is_in_setter) { + base_temp = codegen.add_temporary(base.type); + gen->write_assign(base_temp, base); + prev_base = base_temp; + } + struct ChainInfo { bool is_named = false; GDScriptCodeGenerator::Address base; @@ -1218,6 +1228,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code gen->write_end_jump_if_shared(); } } + } else if (base_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + // Save the temp value back to the base by calling its setter. + gen->write_call(GDScriptCodeGenerator::Address(), base, member_property_setter_function, { assigned }); } if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) { diff --git a/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd new file mode 100644 index 000000000000..9e27a500bf52 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.gd @@ -0,0 +1,13 @@ +# https://github.com/godotengine/godot/issues/85952 + +var vec: Vector2 = Vector2.ZERO: + set(new_vec): + prints("setting vec from", vec, "to", new_vec) + if new_vec == Vector2(1, 1): + vec = new_vec + +func test(): + vec.x = 2 + vec.y = 2 + + prints("vec is", vec) diff --git a/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out new file mode 100644 index 000000000000..31b3b3a3a80b --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/simple_setter_chain_call_setter.out @@ -0,0 +1,4 @@ +GDTEST_OK +setting vec from (0, 0) to (2, 0) +setting vec from (0, 0) to (0, 2) +vec is (0, 0)