Browse Source

GDScript: Call setter on simple setter chain without getter

Fixes a bug where a member variable was being set directly before
calling the setter.
George Marques 1 year ago
parent
commit
87c90a573c

+ 13 - 0
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.
 				// Get at (potential) root stack pos, so it can be returned.
 				GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, chain.back()->get()->base);
 				GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, chain.back()->get()->base);
+
 				if (r_error) {
 				if (r_error) {
 					return GDScriptCodeGenerator::Address();
 					return GDScriptCodeGenerator::Address();
 				}
 				}
 
 
 				GDScriptCodeGenerator::Address prev_base = base;
 				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 {
 				struct ChainInfo {
 					bool is_named = false;
 					bool is_named = false;
 					GDScriptCodeGenerator::Address base;
 					GDScriptCodeGenerator::Address base;
@@ -1218,6 +1228,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 							gen->write_end_jump_if_shared();
 							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) {
 				if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {

+ 13 - 0
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)

+ 4 - 0
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)