diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp index b9a8921436..8357887ba1 100644 --- a/modules/gdscript/editor/gdscript_docgen.cpp +++ b/modules/gdscript/editor/gdscript_docgen.cpp @@ -439,10 +439,9 @@ void GDScriptDocGen::_generate_docs(GDScript *p_script, const GDP::ClassNode *p_ method_doc.qualifiers += "static"; } - if (func_name == "_init") { + if (func_name == "_init" || func_name == "_static_init") { method_doc.return_type = "void"; - } else if (m_func->return_type) { - // `m_func->return_type->get_datatype()` is a metatype. + } else if (!m_func->get_datatype().is_variant()) { _doctype_from_gdtype(m_func->get_datatype(), method_doc.return_type, method_doc.return_enum, true); } else if (!m_func->body->has_return) { // If no `return` statement, then return type is `void`, not `Variant`. diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 35eb21b816..801776763c 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2515,19 +2515,22 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc } void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { + const bool has_expected_type = parser->current_function != nullptr; + const GDScriptParser::DataType expected_type = has_expected_type ? parser->current_function->get_datatype() : GDScriptParser::DataType(); + GDScriptParser::DataType result; - GDScriptParser::DataType expected_type; - bool has_expected_type = parser->current_function != nullptr; - if (has_expected_type) { - expected_type = parser->current_function->get_datatype(); - } - - if (p_return->return_value != nullptr) { - bool is_void_function = has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL; - bool is_call = p_return->return_value->type == GDScriptParser::Node::CALL; + if (p_return->return_value == nullptr) { + // Return type is `null` by default. + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::NIL; + result.is_constant = true; + } else { + const bool is_void_function = has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL; + const bool is_call = p_return->return_value->type == GDScriptParser::Node::CALL; if (is_void_function && is_call) { - // Pretend the call is a root expression to allow those that are "void". + // Pretend the call is a root expression to allow those that are `void`. reduce_call(static_cast(p_return->return_value), false, true); } else { reduce_expression(p_return->return_value); @@ -2561,28 +2564,30 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) { } result = p_return->return_value->get_datatype(); } - } else { - // Return type is null by default. - result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - result.kind = GDScriptParser::DataType::BUILTIN; - result.builtin_type = Variant::NIL; - result.is_constant = true; } if (has_expected_type && !expected_type.is_variant() && expected_type.is_hard_type()) { if (result.is_variant() || !result.is_hard_type()) { + p_return->use_conversion = true; mark_node_unsafe(p_return); if (!is_type_compatible(expected_type, result, true, p_return)) { downgrade_node_type_source(p_return); } } else if (!is_type_compatible(expected_type, result, true, p_return)) { - mark_node_unsafe(p_return); - if (!is_type_compatible(result, expected_type)) { + if (is_type_compatible(result, expected_type)) { + p_return->use_conversion = true; + mark_node_unsafe(p_return); + } else { push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return); } + } else { + if (!is_type_compatible(expected_type, result)) { + p_return->use_conversion = true; + } #ifdef DEBUG_ENABLED - } else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) { - parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION); + if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) { + parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION); + } #endif // DEBUG_ENABLED } } diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 581f1c8978..7a6fcc92a0 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -1834,23 +1834,30 @@ void GDScriptByteCodeGenerator::write_newline(int p_line) { } } -void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) { - if (!function->return_type.has_type() || p_return_value.type.has_type()) { - // Either the function is untyped or the return value is also typed. +void GDScriptByteCodeGenerator::write_return(const Address &p_return_value, bool p_use_conversion) { + if (!p_use_conversion) { + append_opcode(GDScriptFunction::OPCODE_RETURN); + append(p_return_value); + return; + } - // If this is a typed function, then we need to check for potential conversions. - if (function->return_type.has_type()) { - if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) { - // Typed array. + switch (function->return_type.kind) { + case GDScriptDataType::VARIANT: { + ERR_PRINT("Compiler bug: Unresolved return."); + + // Shouldn't get here, but fail-safe to a regular return. + append_opcode(GDScriptFunction::OPCODE_RETURN); + append(p_return_value); + } break; + case GDScriptDataType::BUILTIN: { + if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) { const GDScriptDataType &element_type = function->return_type.get_container_element_type(0); append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY); append(p_return_value); append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); append(element_type.builtin_type); append(element_type.native_type); - } else if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::DICTIONARY && - function->return_type.has_container_element_types()) { - // Typed dictionary. + } else if (function->return_type.builtin_type == Variant::DICTIONARY && function->return_type.has_container_element_types()) { const GDScriptDataType &key_type = function->return_type.get_container_element_type_or_variant(0); const GDScriptDataType &value_type = function->return_type.get_container_element_type_or_variant(1); append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_DICTIONARY); @@ -1861,72 +1868,29 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) { append(key_type.native_type); append(value_type.builtin_type); append(value_type.native_type); - } else if (function->return_type.kind == GDScriptDataType::BUILTIN && p_return_value.type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type != p_return_value.type.builtin_type) { - // Add conversion. + } else { append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN); append(p_return_value); append(function->return_type.builtin_type); - } else { - // Just assign. - append_opcode(GDScriptFunction::OPCODE_RETURN); - append(p_return_value); } - } else { - append_opcode(GDScriptFunction::OPCODE_RETURN); + } break; + case GDScriptDataType::NATIVE: { + append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_NATIVE); append(p_return_value); - } - } else { - switch (function->return_type.kind) { - case GDScriptDataType::BUILTIN: { - if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type(0)) { - const GDScriptDataType &element_type = function->return_type.get_container_element_type(0); - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY); - append(p_return_value); - append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); - append(element_type.builtin_type); - append(element_type.native_type); - } else if (function->return_type.builtin_type == Variant::DICTIONARY && function->return_type.has_container_element_types()) { - const GDScriptDataType &key_type = function->return_type.get_container_element_type_or_variant(0); - const GDScriptDataType &value_type = function->return_type.get_container_element_type_or_variant(1); - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_DICTIONARY); - append(p_return_value); - append(get_constant_pos(key_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); - append(get_constant_pos(value_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); - append(key_type.builtin_type); - append(key_type.native_type); - append(value_type.builtin_type); - append(value_type.native_type); - } else { - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN); - append(p_return_value); - append(function->return_type.builtin_type); - } - } break; - case GDScriptDataType::NATIVE: { - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_NATIVE); - append(p_return_value); - int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[function->return_type.native_type]; - Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx]; - class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); - append(class_idx); - } break; - case GDScriptDataType::GDSCRIPT: - case GDScriptDataType::SCRIPT: { - Variant script = function->return_type.script_type; - int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); + int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[function->return_type.native_type]; + Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx]; + class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); + append(class_idx); + } break; + case GDScriptDataType::SCRIPT: + case GDScriptDataType::GDSCRIPT: { + Variant script = function->return_type.script_type; + int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS); - append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_SCRIPT); - append(p_return_value); - append(script_idx); - } break; - default: { - ERR_PRINT("Compiler bug: unresolved return."); - - // Shouldn't get here, but fail-safe to a regular return; - append_opcode(GDScriptFunction::OPCODE_RETURN); - append(p_return_value); - } break; - } + append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_SCRIPT); + append(p_return_value); + append(script_idx); + } break; } } diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index c2aac17140..3abd22589f 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -552,7 +552,7 @@ public: virtual void write_continue() override; virtual void write_breakpoint() override; virtual void write_newline(int p_line) override; - virtual void write_return(const Address &p_return_value) override; + virtual void write_return(const Address &p_return_value, bool p_use_conversion) override; virtual void write_assert(const Address &p_test, const Address &p_message) override; virtual ~GDScriptByteCodeGenerator(); diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 3d33e7649f..84e4551eac 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -159,7 +159,7 @@ public: virtual void write_continue() = 0; virtual void write_breakpoint() = 0; virtual void write_newline(int p_line) = 0; - virtual void write_return(const Address &p_return_value) = 0; + virtual void write_return(const Address &p_return_value, bool p_use_conversion) = 0; virtual void write_assert(const Address &p_test, const Address &p_message) = 0; virtual ~GDScriptCodeGenerator() {} diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 0358cae233..8adbb6f3f9 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -2168,10 +2168,10 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui } if (return_n->void_return) { - // Always return "null", even if the expression is a call to a void function. - gen->write_return(codegen.add_constant(Variant())); + // Always return `null`, even if the expression is a call to a `void` function. + gen->write_return(codegen.add_constant(Variant()), false); } else { - gen->write_return(return_value); + gen->write_return(return_value, return_n->use_conversion); } if (return_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index fecd37c118..e4e6c15f8d 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1047,6 +1047,7 @@ public: struct ReturnNode : public Node { ExpressionNode *return_value = nullptr; bool void_return = false; + bool use_conversion = false; ReturnNode() { type = RETURN; diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 377262b51f..41f205e318 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -2818,8 +2818,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a Variant::construct(ret_type, retvalue, const_cast(&r), 1, ce); } else { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)", - Variant::get_type_name(r->get_type()), Variant::get_type_name(ret_type)); + err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)", + _get_var_type(r), Variant::get_type_name(ret_type)); #endif // DEBUG_ENABLED // Construct a base type anyway so type constraints are met. @@ -2848,9 +2848,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (r->get_type() != Variant::ARRAY) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "Array[%s]".)", - Variant::get_type_name(r->get_type()), Variant::get_type_name(builtin_type)); -#endif + err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "Array[%s]".)", + _get_var_type(r), Variant::get_type_name(builtin_type)); +#endif // DEBUG_ENABLED OPCODE_BREAK; } @@ -2858,7 +2858,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return an array of type "%s" where expected return type is "Array[%s]".)", + err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "Array[%s]".)", _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type)); #endif // DEBUG_ENABLED OPCODE_BREAK; @@ -2890,7 +2890,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (r->get_type() != Variant::DICTIONARY) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return a value of type "%s" where expected return type is "Dictionary[%s, %s]".)", + err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "Dictionary[%s, %s]".)", _get_var_type(r), _get_element_type(key_builtin_type, key_native_type, *key_script_type), _get_element_type(value_builtin_type, value_native_type, *value_script_type)); #endif // DEBUG_ENABLED @@ -2902,7 +2902,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (dictionary->get_typed_key_builtin() != ((uint32_t)key_builtin_type) || dictionary->get_typed_key_class_name() != key_native_type || dictionary->get_typed_key_script() != *key_script_type || dictionary->get_typed_value_builtin() != ((uint32_t)value_builtin_type) || dictionary->get_typed_value_class_name() != value_native_type || dictionary->get_typed_value_script() != *value_script_type) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return a dictionary of type "%s" where expected return type is "Dictionary[%s, %s]".)", + err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "Dictionary[%s, %s]".)", _get_var_type(r), _get_element_type(key_builtin_type, key_native_type, *key_script_type), _get_element_type(value_builtin_type, value_native_type, *value_script_type)); #endif // DEBUG_ENABLED @@ -2926,8 +2926,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a GD_ERR_BREAK(!nc); if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) { - err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)", - Variant::get_type_name(r->get_type()), nc->get_name()); +#ifdef DEBUG_ENABLED + err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)", + _get_var_type(r), nc->get_name()); +#endif // DEBUG_ENABLED OPCODE_BREAK; } @@ -2944,8 +2946,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a #endif // DEBUG_ENABLED if (ret_obj && !ClassDB::is_parent_class(ret_obj->get_class_name(), nc->get_name())) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)", - ret_obj->get_class_name(), nc->get_name()); + err_text = vformat(R"(Trying to return a value of type "%s" from a function whose return type is "%s".)", + _get_var_type(r), nc->get_name()); #endif // DEBUG_ENABLED OPCODE_BREAK; } @@ -2967,8 +2969,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) { #ifdef DEBUG_ENABLED - err_text = vformat(R"(Trying to return value of type "%s" from a function whose return type is "%s".)", - Variant::get_type_name(r->get_type()), GDScript::debug_get_script_name(Ref