diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 0b7e4e50e6e1..08c876349388 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -121,6 +121,13 @@ [/codeblock] + + + + + + + diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index c8dfdbdd6859..de94557b41d4 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -562,7 +562,6 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type GDScriptParser::DataType result; result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - result.builtin_type = Variant::OBJECT; if (p_type->type_chain.is_empty()) { // void. @@ -584,6 +583,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } else if (first == SNAME("Object")) { // Object is treated like a native type, not a built-in. result.kind = GDScriptParser::DataType::NATIVE; + result.builtin_type = Variant::OBJECT; result.native_type = SNAME("Object"); } else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) { // Built-in types. @@ -604,6 +604,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } else if (class_exists(first)) { // Native engine classes. result.kind = GDScriptParser::DataType::NATIVE; + result.builtin_type = Variant::OBJECT; result.native_type = first; } else if (ScriptServer::is_global_class(first)) { if (parser->script_path == ScriptServer::get_global_class_path(first)) { @@ -1338,6 +1339,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root case GDScriptParser::Node::SELF: case GDScriptParser::Node::SUBSCRIPT: case GDScriptParser::Node::TERNARY_OPERATOR: + case GDScriptParser::Node::TYPE_TEST: case GDScriptParser::Node::UNARY_OPERATOR: reduce_expression(static_cast(p_node), p_is_root); break; @@ -2196,6 +2198,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre case GDScriptParser::Node::TERNARY_OPERATOR: reduce_ternary_op(static_cast(p_expression), p_is_root); break; + case GDScriptParser::Node::TYPE_TEST: + reduce_type_test(static_cast(p_expression)); + break; case GDScriptParser::Node::UNARY_OPERATOR: reduce_unary_op(static_cast(p_expression)); break; @@ -2502,13 +2507,7 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) { void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_op) { reduce_expression(p_binary_op->left_operand); - - if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST && p_binary_op->right_operand && p_binary_op->right_operand->type == GDScriptParser::Node::IDENTIFIER) { - reduce_identifier(static_cast(p_binary_op->right_operand), true); - } else { - reduce_expression(p_binary_op->right_operand); - } - // TODO: Right operand must be a valid type with the `is` operator. Need to check here. + reduce_expression(p_binary_op->right_operand); GDScriptParser::DataType left_type; if (p_binary_op->left_operand) { @@ -2546,19 +2545,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o } } } else { - if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) { - GDScriptParser::DataType test_type = right_type; - test_type.is_meta_type = false; - - if (!is_type_compatible(test_type, left_type)) { - push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)"), p_binary_op->left_operand); - p_binary_op->reduced_value = false; - } else { - p_binary_op->reduced_value = true; - } - } else { - ERR_PRINT("Parser bug: unknown binary operation."); - } + ERR_PRINT("Parser bug: unknown binary operation."); } p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value, p_binary_op)); @@ -2567,24 +2554,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o GDScriptParser::DataType result; - if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) { - GDScriptParser::DataType test_type = right_type; - test_type.is_meta_type = false; - - if (!is_type_compatible(test_type, left_type) && !is_type_compatible(left_type, test_type)) { - if (left_type.is_hard_type()) { - push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", left_type.to_string(), test_type.to_string()), p_binary_op->left_operand); - } else { - // TODO: Warning. - mark_node_unsafe(p_binary_op); - } - } - - // "is" operator is always a boolean anyway. - result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - result.kind = GDScriptParser::DataType::BUILTIN; - result.builtin_type = Variant::BOOL; - } else if ((p_binary_op->variant_op == Variant::OP_EQUAL || p_binary_op->variant_op == Variant::OP_NOT_EQUAL) && + if ((p_binary_op->variant_op == Variant::OP_EQUAL || p_binary_op->variant_op == Variant::OP_NOT_EQUAL) && ((left_type.kind == GDScriptParser::DataType::BUILTIN && left_type.builtin_type == Variant::NIL) || (right_type.kind == GDScriptParser::DataType::BUILTIN && right_type.builtin_type == Variant::NIL))) { // "==" and "!=" operators always return a boolean when comparing to null. result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -4107,6 +4077,48 @@ void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternar p_ternary_op->set_datatype(result); } +void GDScriptAnalyzer::reduce_type_test(GDScriptParser::TypeTestNode *p_type_test) { + GDScriptParser::DataType result; + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::BOOL; + p_type_test->set_datatype(result); + + if (!p_type_test->operand || !p_type_test->test_type) { + return; + } + + reduce_expression(p_type_test->operand); + GDScriptParser::DataType operand_type = p_type_test->operand->get_datatype(); + GDScriptParser::DataType test_type = type_from_metatype(resolve_datatype(p_type_test->test_type)); + p_type_test->test_datatype = test_type; + + if (!operand_type.is_set() || !test_type.is_set()) { + return; + } + + if (p_type_test->operand->is_constant) { + p_type_test->is_constant = true; + p_type_test->reduced_value = false; + + if (!is_type_compatible(test_type, operand_type)) { + push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", operand_type.to_string(), test_type.to_string()), p_type_test->operand); + } else if (is_type_compatible(test_type, type_from_variant(p_type_test->operand->reduced_value, p_type_test->operand))) { + p_type_test->reduced_value = test_type.builtin_type != Variant::OBJECT || !p_type_test->operand->reduced_value.is_null(); + } + + return; + } + + if (!is_type_compatible(test_type, operand_type) && !is_type_compatible(operand_type, test_type)) { + if (operand_type.is_hard_type()) { + push_error(vformat(R"(Expression is of type "%s" so it can't be of type "%s".)", operand_type.to_string(), test_type.to_string()), p_type_test->operand); + } else { + downgrade_node_type_source(p_type_test->operand); + } + } +} + void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) { reduce_expression(p_unary_op->operand); diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index cdeba374c7d4..22004b899f93 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -100,6 +100,7 @@ class GDScriptAnalyzer { void reduce_self(GDScriptParser::SelfNode *p_self); void reduce_subscript(GDScriptParser::SubscriptNode *p_subscript); void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false); + void reduce_type_test(GDScriptParser::TypeTestNode *p_type_test); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); Variant make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced); diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index ec7a2b0f1caf..73f88d2932f8 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -607,18 +607,44 @@ void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, V append(p_operator); } -void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) { - append_opcode(GDScriptFunction::OPCODE_EXTENDS_TEST); - append(p_source); - append(p_type); - append(p_target); -} - -void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) { - append_opcode(GDScriptFunction::OPCODE_IS_BUILTIN); - append(p_source); - append(p_target); - append(p_type); +void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) { + switch (p_type.kind) { + case GDScriptDataType::BUILTIN: { + if (p_type.builtin_type == Variant::ARRAY && p_type.has_container_element_type()) { + const GDScriptDataType &element_type = p_type.get_container_element_type(); + append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_ARRAY); + append(p_target); + append(p_source); + 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 { + append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_BUILTIN); + append(p_target); + append(p_source); + append(p_type.builtin_type); + } + } break; + case GDScriptDataType::NATIVE: { + append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_NATIVE); + append(p_target); + append(p_source); + append(p_type.native_type); + } break; + case GDScriptDataType::SCRIPT: + case GDScriptDataType::GDSCRIPT: { + const Variant &script = p_type.script_type; + append_opcode(GDScriptFunction::OPCODE_TYPE_TEST_SCRIPT); + append(p_target); + append(p_source); + append(get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS)); + } break; + default: { + ERR_PRINT("Compiler bug: unresolved type in type test."); + append_opcode(GDScriptFunction::OPCODE_ASSIGN_FALSE); + append(p_target); + } + } } void GDScriptByteCodeGenerator::write_and_left_operand(const Address &p_left_operand) { diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 8aa02b86c4bd..03f555a85dcc 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -454,8 +454,7 @@ public: virtual void write_type_adjust(const Address &p_target, Variant::Type p_new_type) override; virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) override; virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override; - virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) override; - virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) override; + virtual void write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override; virtual void write_and_left_operand(const Address &p_left_operand) override; virtual void write_and_right_operand(const Address &p_right_operand) override; virtual void write_end_and(const Address &p_target) override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 6d42d152b910..7847ab28c7ec 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -90,8 +90,7 @@ public: virtual void write_type_adjust(const Address &p_target, Variant::Type p_new_type) = 0; virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) = 0; virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0; - virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) = 0; - virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) = 0; + virtual void write_type_test(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0; virtual void write_and_left_operand(const Address &p_left_operand) = 0; virtual void write_and_right_operand(const Address &p_right_operand) = 0; virtual void write_end_and(const Address &p_target) = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index b34be1116958..efa75528fc4f 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -148,13 +148,8 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D } } break; case GDScriptParser::DataType::ENUM: - result.has_type = true; result.kind = GDScriptDataType::BUILTIN; - if (p_datatype.is_meta_type) { - result.builtin_type = Variant::DICTIONARY; - } else { - result.builtin_type = Variant::INT; - } + result.builtin_type = p_datatype.builtin_type; break; case GDScriptParser::DataType::RESOLVING: case GDScriptParser::DataType::UNRESOLVED: { @@ -494,17 +489,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } break; case GDScriptParser::Node::CAST: { const GDScriptParser::CastNode *cn = static_cast(p_expression); - GDScriptParser::DataType og_cast_type = cn->get_datatype(); - GDScriptDataType cast_type = _gdtype_from_datatype(og_cast_type, codegen.script); + GDScriptDataType cast_type = _gdtype_from_datatype(cn->get_datatype(), codegen.script); GDScriptCodeGenerator::Address result; if (cast_type.has_type) { - if (og_cast_type.kind == GDScriptParser::DataType::ENUM) { - // Enum types are usually treated as dictionaries, but in this case we want to cast to an integer. - cast_type.kind = GDScriptDataType::BUILTIN; - cast_type.builtin_type = Variant::INT; - } - // Create temporary for result first since it will be deleted last. result = codegen.add_temporary(cast_type); @@ -817,28 +805,6 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code gen->pop_temporary(); } } break; - case GDScriptParser::BinaryOpNode::OP_TYPE_TEST: { - GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, binary->left_operand); - - if (binary->right_operand->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast(binary->right_operand)->name) != Variant::VARIANT_MAX) { - // `is` with builtin type) - Variant::Type type = GDScriptParser::get_builtin_type(static_cast(binary->right_operand)->name); - gen->write_type_test_builtin(result, operand, type); - } else { - GDScriptCodeGenerator::Address type = _parse_expression(codegen, r_error, binary->right_operand); - if (r_error) { - return GDScriptCodeGenerator::Address(); - } - gen->write_type_test(result, operand, type); - if (type.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); - } - } - - if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - gen->pop_temporary(); - } - } break; default: { GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand); GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand); @@ -894,6 +860,28 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return result; } break; + case GDScriptParser::Node::TYPE_TEST: { + const GDScriptParser::TypeTestNode *type_test = static_cast(p_expression); + GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(type_test->get_datatype(), codegen.script)); + + GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, type_test->operand); + GDScriptDataType test_type = _gdtype_from_datatype(type_test->test_datatype, codegen.script); + if (r_error) { + return GDScriptCodeGenerator::Address(); + } + + if (test_type.has_type) { + gen->write_type_test(result, operand, test_type); + } else { + gen->write_assign_true(result); + } + + if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + gen->pop_temporary(); + } + + return result; + } break; case GDScriptParser::Node::ASSIGNMENT: { const GDScriptParser::AssignmentNode *assignment = static_cast(p_expression); diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index d4f4358ac16e..0acc03be3d62 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -135,23 +135,56 @@ void GDScriptFunction::disassemble(const Vector &p_code_lines) const { incr += 5; } break; - case OPCODE_EXTENDS_TEST: { - text += "is object "; - text += DADDR(3); - text += " = "; + case OPCODE_TYPE_TEST_BUILTIN: { + text += "type test "; text += DADDR(1); - text += " is "; + text += " = "; text += DADDR(2); + text += " is "; + text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 3])); incr += 4; } break; - case OPCODE_IS_BUILTIN: { - text += "is builtin "; - text += DADDR(2); - text += " = "; + case OPCODE_TYPE_TEST_ARRAY: { + text += "type test "; text += DADDR(1); + text += " = "; + text += DADDR(2); + text += " is Array["; + + Ref