diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 0b7e4e50e6e..08c87634938 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 c8dfdbdd685..de94557b41d 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 cdeba374c7d..22004b899f9 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 ec7a2b0f1ca..73f88d2932f 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 8aa02b86c4b..03f555a85dc 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 6d42d152b91..7847ab28c7e 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 b34be111695..efa75528fc4 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 d4f4358ac16..0acc03be3d6 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