Преглед на файлове

GDScript: Error when assigning return value of void function

This also makes built-in method calls empty the return value when the
method is void, to avoid keeping returning a garbage value in such case.
George Marques преди 2 години
родител
ревизия
0c15844551

+ 2 - 0
core/variant/variant_call.cpp

@@ -65,11 +65,13 @@ static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...) const, Variant *b
 
 template <class T, class... P>
 static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
+	VariantInternal::clear(&r_ret);
 	call_with_variant_args_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals);
 }
 
 template <class T, class... P>
 static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
+	VariantInternal::clear(&r_ret);
 	call_with_variant_argsc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals);
 }
 

+ 14 - 14
modules/gdscript/gdscript_compiler.cpp

@@ -538,20 +538,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 				// Construct a built-in type.
 				Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
 
-				gen->write_construct(result, vtype, arguments);
+				gen->write_construct(return_addr, vtype, arguments);
 			} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
 				// Variant utility function.
-				gen->write_call_utility(result, call->function_name, arguments);
+				gen->write_call_utility(return_addr, call->function_name, arguments);
 			} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
 				// GDScript utility function.
-				gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
+				gen->write_call_gdscript_utility(return_addr, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
 			} else {
 				// Regular function.
 				const GDScriptParser::ExpressionNode *callee = call->callee;
 
 				if (call->is_super) {
 					// Super call.
-					gen->write_super_call(result, call->function_name, arguments);
+					gen->write_super_call(return_addr, call->function_name, arguments);
 				} else {
 					if (callee->type == GDScriptParser::Node::IDENTIFIER) {
 						// Self function call.
@@ -563,22 +563,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 
 							if (_have_exact_arguments(method, arguments)) {
 								// Exact arguments, use ptrcall.
-								gen->write_call_ptrcall(result, self, method, arguments);
+								gen->write_call_ptrcall(return_addr, self, method, arguments);
 							} else {
 								// Not exact arguments, but still can use method bind call.
-								gen->write_call_method_bind(result, self, method, arguments);
+								gen->write_call_method_bind(return_addr, self, method, arguments);
 							}
 						} else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
 							GDScriptCodeGenerator::Address self;
 							self.mode = GDScriptCodeGenerator::Address::CLASS;
 							if (within_await) {
-								gen->write_call_async(result, self, call->function_name, arguments);
+								gen->write_call_async(return_addr, self, call->function_name, arguments);
 							} else {
 								gen->write_call(return_addr, self, call->function_name, arguments);
 							}
 						} else {
 							if (within_await) {
-								gen->write_call_self_async(result, call->function_name, arguments);
+								gen->write_call_self_async(return_addr, call->function_name, arguments);
 							} else {
 								gen->write_call_self(return_addr, call->function_name, arguments);
 							}
@@ -589,18 +589,18 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 						if (subscript->is_attribute) {
 							// May be static built-in method call.
 							if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) {
-								gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
+								gen->write_call_builtin_type_static(return_addr, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
 							} else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") &&
 									ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) {
 								// It's a static native method call.
-								gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);
+								gen->write_call_native_static(return_addr, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);
 							} else {
 								GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
 								if (r_error) {
 									return GDScriptCodeGenerator::Address();
 								}
 								if (within_await) {
-									gen->write_call_async(result, base, call->function_name, arguments);
+									gen->write_call_async(return_addr, base, call->function_name, arguments);
 								} else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
 									// Native method, use faster path.
 									StringName class_name;
@@ -613,16 +613,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 										MethodBind *method = ClassDB::get_method(class_name, call->function_name);
 										if (_have_exact_arguments(method, arguments)) {
 											// Exact arguments, use ptrcall.
-											gen->write_call_ptrcall(result, base, method, arguments);
+											gen->write_call_ptrcall(return_addr, base, method, arguments);
 										} else {
 											// Not exact arguments, but still can use method bind call.
-											gen->write_call_method_bind(result, base, method, arguments);
+											gen->write_call_method_bind(return_addr, base, method, arguments);
 										}
 									} else {
 										gen->write_call(return_addr, base, call->function_name, arguments);
 									}
 								} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
-									gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
+									gen->write_call_builtin_type(return_addr, base, base.type.builtin_type, call->function_name, arguments);
 								} else {
 									gen->write_call(return_addr, base, call->function_name, arguments);
 								}

+ 20 - 0
modules/gdscript/gdscript_vm.cpp

@@ -1533,8 +1533,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 				Callable::CallError err;
 				if (call_ret) {
 					GET_INSTRUCTION_ARG(ret, argc + 1);
+#ifdef DEBUG_ENABLED
+					Variant::Type base_type = base->get_type();
+					Object *base_obj = base->get_validated_object();
+					StringName base_class = base_obj ? base_obj->get_class_name() : StringName();
+#endif
 					base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err);
 #ifdef DEBUG_ENABLED
+					if (ret->get_type() == Variant::NIL) {
+						if (base_type == Variant::OBJECT) {
+							if (base_obj) {
+								MethodBind *method = ClassDB::get_method(base_class, *methodname);
+								if (*methodname == CoreStringNames::get_singleton()->_free || (method && !method->has_return())) {
+									err_text = R"(Trying to get a return value of a method that returns "void")";
+									OPCODE_BREAK;
+								}
+							}
+						} else if (Variant::has_builtin_method(base_type, *methodname) && !Variant::has_builtin_method_return_value(base_type, *methodname)) {
+							err_text = R"(Trying to get a return value of a method that returns "void")";
+							OPCODE_BREAK;
+						}
+					}
+
 					if (!call_async && ret->get_type() == Variant::OBJECT) {
 						// Check if getting a function state without await.
 						bool was_freed = false;

+ 4 - 0
modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.gd

@@ -0,0 +1,4 @@
+func test():
+	var obj
+	obj = Node.new()
+	print(obj.free())

+ 6 - 0
modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_free_call.out

@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_free_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"

+ 4 - 0
modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.gd

@@ -0,0 +1,4 @@
+func test():
+	var value
+	value = []
+	print(value.reverse())

+ 6 - 0
modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_builtin_method_call.out

@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_void_builtin_method_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"

+ 4 - 0
modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.gd

@@ -0,0 +1,4 @@
+func test():
+	var obj
+	obj = RefCounted.new()
+	print(obj.notify_property_list_changed())

+ 6 - 0
modules/gdscript/tests/scripts/runtime/errors/use_return_value_of_void_native_method_call.out

@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/use_return_value_of_void_native_method_call.gd
+>> 4
+>> Trying to get a return value of a method that returns "void"

+ 4 - 4
modules/gdscript/tests/scripts/runtime/features/array_string_stringname_equivalent.gd

@@ -15,10 +15,10 @@ func test():
 	var string_array: Array[String] = []
 	var stringname_array: Array[StringName] = []
 
-	assert(!string_array.push_back(&"abc"))
+	string_array.push_back(&"abc")
 	print("Array[String] insert converted: ", typeof(string_array[0]) == TYPE_STRING)
 
-	assert(!stringname_array.push_back("abc"))
+	stringname_array.push_back("abc")
 	print("Array[StringName] insert converted: ", typeof(stringname_array[0]) == TYPE_STRING_NAME)
 
 	print("StringName in Array[String]: ", &"abc" in string_array)
@@ -28,8 +28,8 @@ func test():
 	assert(!packed_string_array.push_back("abc"))
 	print("StringName in PackedStringArray: ", &"abc" in packed_string_array)
 
-	assert(!string_array.push_back("abc"))
+	string_array.push_back("abc")
 	print("StringName finds String in Array: ", string_array.find(&"abc"))
 
-	assert(!stringname_array.push_back(&"abc"))
+	stringname_array.push_back(&"abc")
 	print("String finds StringName in Array: ", stringname_array.find("abc"))