Browse Source

GDScript: Fix incorrect setter call for reference types

Danil Alexeev 1 year ago
parent
commit
8c82fd15d2

+ 9 - 1
modules/gdscript/gdscript_compiler.cpp

@@ -1064,6 +1064,8 @@ 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);
+				const bool base_known_type = base.type.has_type;
+				const bool base_is_shared = Variant::is_type_shared(base.type.builtin_type);
 
 				if (r_error) {
 					return GDScriptCodeGenerator::Address();
@@ -1074,7 +1076,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 				// 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) {
+				if ((!base_known_type || !base_is_shared) && 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;
@@ -1229,8 +1231,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 						}
 					}
 				} else if (base_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+					if (!base_known_type) {
+						gen->write_jump_if_shared(base);
+					}
 					// Save the temp value back to the base by calling its setter.
 					gen->write_call(GDScriptCodeGenerator::Address(), base, member_property_setter_function, { assigned });
+					if (!base_known_type) {
+						gen->write_end_jump_if_shared();
+					}
 				}
 
 				if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {

+ 62 - 0
modules/gdscript/tests/scripts/runtime/features/setter_chain_shared_types.gd

@@ -0,0 +1,62 @@
+# GH-94667
+
+class Inner:
+	var subprop: Vector2:
+		set(value):
+			prints("subprop setter", value)
+			subprop = value
+		get:
+			print("subprop getter")
+			return subprop
+
+	func _to_string() -> String:
+		return "<Inner>"
+
+var prop1:
+	set(value):
+		prints("prop1 setter", value)
+		prop1 = value
+
+var prop2: Inner:
+	set(value):
+		prints("prop2 setter", value)
+		prop2 = value
+
+var prop3:
+	set(value):
+		prints("prop3 setter", value)
+		prop3 = value
+	get:
+		print("prop3 getter")
+		return prop3
+
+var prop4: Inner:
+	set(value):
+		prints("prop4 setter", value)
+		prop4 = value
+	get:
+		print("prop4 getter")
+		return prop4
+
+func test():
+	print("===")
+	prop1 = Vector2()
+	prop1.x = 1.0
+	print("---")
+	prop1 = Inner.new()
+	prop1.subprop.x = 1.0
+
+	print("===")
+	prop2 = Inner.new()
+	prop2.subprop.x = 1.0
+
+	print("===")
+	prop3 = Vector2()
+	prop3.x = 1.0
+	print("---")
+	prop3 = Inner.new()
+	prop3.subprop.x = 1.0
+
+	print("===")
+	prop4 = Inner.new()
+	prop4.subprop.x = 1.0

+ 26 - 0
modules/gdscript/tests/scripts/runtime/features/setter_chain_shared_types.out

@@ -0,0 +1,26 @@
+GDTEST_OK
+===
+prop1 setter (0, 0)
+prop1 setter (1, 0)
+---
+prop1 setter <Inner>
+subprop getter
+subprop setter (1, 0)
+===
+prop2 setter <Inner>
+subprop getter
+subprop setter (1, 0)
+===
+prop3 setter (0, 0)
+prop3 getter
+prop3 setter (1, 0)
+---
+prop3 setter <Inner>
+prop3 getter
+subprop getter
+subprop setter (1, 0)
+===
+prop4 setter <Inner>
+prop4 getter
+subprop getter
+subprop setter (1, 0)