瀏覽代碼

Merge pull request #46830 from vnen/gdscript-typed-arrays

GDScript typed arrays
Rémi Verschelde 4 年之前
父節點
當前提交
aba03110ba

+ 27 - 6
core/variant/array.cpp

@@ -139,7 +139,7 @@ uint32_t Array::hash() const {
 	return h;
 }
 
-void Array::_assign(const Array &p_array) {
+bool Array::_assign(const Array &p_array) {
 	if (_p->typed.type != Variant::OBJECT && _p->typed.type == p_array._p->typed.type) {
 		//same type or untyped, just reference, should be fine
 		_ref(p_array);
@@ -150,7 +150,7 @@ void Array::_assign(const Array &p_array) {
 			//for objects, it needs full validation, either can be converted or fail
 			for (int i = 0; i < p_array._p->array.size(); i++) {
 				if (!_p->typed.validate(p_array._p->array[i], "assign")) {
-					return;
+					return false;
 				}
 			}
 			_p->array = p_array._p->array; //then just copy, which is cheap anyway
@@ -168,10 +168,10 @@ void Array::_assign(const Array &p_array) {
 					Callable::CallError ce;
 					Variant::construct(_p->typed.type, new_array.write[i], (const Variant **)&ptr, 1, ce);
 					if (ce.error != Callable::CallError::CALL_OK) {
-						ERR_FAIL_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
+						ERR_FAIL_V_MSG(false, "Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
 					}
 				} else {
-					ERR_FAIL_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
+					ERR_FAIL_V_MSG(false, "Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(src_val.get_type()) + "' to '" + Variant::get_type_name(_p->typed.type) + "'.");
 				}
 			}
 
@@ -180,12 +180,13 @@ void Array::_assign(const Array &p_array) {
 	} else if (_p->typed.can_reference(p_array._p->typed)) { //same type or compatible
 		_ref(p_array);
 	} else {
-		ERR_FAIL_MSG("Assignment of arrays of incompatible types.");
+		ERR_FAIL_V_MSG(false, "Assignment of arrays of incompatible types.");
 	}
+	return true;
 }
 
 void Array::operator=(const Array &p_array) {
-	_assign(p_array);
+	_ref(p_array);
 }
 
 void Array::push_back(const Variant &p_value) {
@@ -528,6 +529,10 @@ Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_nam
 	_assign(p_from);
 }
 
+bool Array::typed_assign(const Array &p_other) {
+	return _assign(p_other);
+}
+
 void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
 	ERR_FAIL_COND_MSG(_p->array.size() > 0, "Type can only be set when array is empty.");
 	ERR_FAIL_COND_MSG(_p->refcount.get() > 1, "Type can only be set when array has no more than one user.");
@@ -542,6 +547,22 @@ void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Var
 	_p->typed.where = "TypedArray";
 }
 
+bool Array::is_typed() const {
+	return _p->typed.type != Variant::NIL;
+}
+
+uint32_t Array::get_typed_builtin() const {
+	return _p->typed.type;
+}
+
+StringName Array::get_typed_class_name() const {
+	return _p->typed.class_name;
+}
+
+Variant Array::get_typed_script() const {
+	return _p->typed.script;
+}
+
 Array::Array(const Array &p_from) {
 	_p = nullptr;
 	_ref(p_from);

+ 6 - 1
core/variant/array.h

@@ -48,7 +48,7 @@ class Array {
 
 protected:
 	Array(const Array &p_base, uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
-	void _assign(const Array &p_array);
+	bool _assign(const Array &p_array);
 
 public:
 	Variant &operator[](int p_idx);
@@ -111,7 +111,12 @@ public:
 
 	const void *id() const;
 
+	bool typed_assign(const Array &p_other);
 	void set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script);
+	bool is_typed() const;
+	uint32_t get_typed_builtin() const;
+	StringName get_typed_class_name() const;
+	Variant get_typed_script() const;
 	Array(const Array &p_from);
 	Array();
 	~Array();

+ 58 - 60
core/variant/variant_setget.cpp

@@ -875,65 +875,64 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
 		static uint64_t get_indexed_size(const Variant *base) { return m_max; }                                                    \
 	};
 
-#define INDEXED_SETGET_STRUCT_VARIANT(m_base_type)                                                    \
-	struct VariantIndexedSetGet_##m_base_type {                                                       \
-		static void get(const Variant *base, int64_t index, Variant *value, bool *oob) {              \
-			int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size();                 \
-			if (index < 0) {                                                                          \
-				index += size;                                                                        \
-			}                                                                                         \
-			if (index < 0 || index >= size) {                                                         \
-				*oob = true;                                                                          \
-				return;                                                                               \
-			}                                                                                         \
-			*value = (*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index];                     \
-			*oob = false;                                                                             \
-		}                                                                                             \
-		static void ptr_get(const void *base, int64_t index, void *member) {                          \
-			/* avoid ptrconvert for performance*/                                                     \
-			const m_base_type &v = *reinterpret_cast<const m_base_type *>(base);                      \
-			if (index < 0)                                                                            \
-				index += v.size();                                                                    \
-			OOB_TEST(index, v.size());                                                                \
-			PtrToArg<Variant>::encode(v[index], member);                                              \
-		}                                                                                             \
-		static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) { \
-			int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size();                 \
-			if (index < 0) {                                                                          \
-				index += size;                                                                        \
-			}                                                                                         \
-			if (index < 0 || index >= size) {                                                         \
-				*oob = true;                                                                          \
-				*valid = false;                                                                       \
-				return;                                                                               \
-			}                                                                                         \
-			(*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value;                     \
-			*oob = false;                                                                             \
-			*valid = true;                                                                            \
-		}                                                                                             \
-		static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {    \
-			int64_t size = VariantGetInternalPtr<m_base_type>::get_ptr(base)->size();                 \
-			if (index < 0) {                                                                          \
-				index += size;                                                                        \
-			}                                                                                         \
-			if (index < 0 || index >= size) {                                                         \
-				*oob = true;                                                                          \
-				return;                                                                               \
-			}                                                                                         \
-			(*VariantGetInternalPtr<m_base_type>::get_ptr(base))[index] = *value;                     \
-			*oob = false;                                                                             \
-		}                                                                                             \
-		static void ptr_set(void *base, int64_t index, const void *member) {                          \
-			/* avoid ptrconvert for performance*/                                                     \
-			m_base_type &v = *reinterpret_cast<m_base_type *>(base);                                  \
-			if (index < 0)                                                                            \
-				index += v.size();                                                                    \
-			OOB_TEST(index, v.size());                                                                \
-			v[index] = PtrToArg<Variant>::convert(member);                                            \
-		}                                                                                             \
-		static Variant::Type get_index_type() { return Variant::NIL; }                                \
-		static uint64_t get_indexed_size(const Variant *base) { return 0; }                           \
-	};
+struct VariantIndexedSetGet_Array {
+	static void get(const Variant *base, int64_t index, Variant *value, bool *oob) {
+		int64_t size = VariantGetInternalPtr<Array>::get_ptr(base)->size();
+		if (index < 0) {
+			index += size;
+		}
+		if (index < 0 || index >= size) {
+			*oob = true;
+			return;
+		}
+		*value = (*VariantGetInternalPtr<Array>::get_ptr(base))[index];
+		*oob = false;
+	}
+	static void ptr_get(const void *base, int64_t index, void *member) {
+		/* avoid ptrconvert for performance*/
+		const Array &v = *reinterpret_cast<const Array *>(base);
+		if (index < 0)
+			index += v.size();
+		OOB_TEST(index, v.size());
+		PtrToArg<Variant>::encode(v[index], member);
+	}
+	static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {
+		int64_t size = VariantGetInternalPtr<Array>::get_ptr(base)->size();
+		if (index < 0) {
+			index += size;
+		}
+		if (index < 0 || index >= size) {
+			*oob = true;
+			*valid = false;
+			return;
+		}
+		VariantGetInternalPtr<Array>::get_ptr(base)->set(index, *value);
+		*oob = false;
+		*valid = true;
+	}
+	static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {
+		int64_t size = VariantGetInternalPtr<Array>::get_ptr(base)->size();
+		if (index < 0) {
+			index += size;
+		}
+		if (index < 0 || index >= size) {
+			*oob = true;
+			return;
+		}
+		VariantGetInternalPtr<Array>::get_ptr(base)->set(index, *value);
+		*oob = false;
+	}
+	static void ptr_set(void *base, int64_t index, const void *member) {
+		/* avoid ptrconvert for performance*/
+		Array &v = *reinterpret_cast<Array *>(base);
+		if (index < 0)
+			index += v.size();
+		OOB_TEST(index, v.size());
+		v.set(index, PtrToArg<Variant>::convert(member));
+	}
+	static Variant::Type get_index_type() { return Variant::NIL; }
+	static uint64_t get_indexed_size(const Variant *base) { return 0; }
+};
 
 #define INDEXED_SETGET_STRUCT_DICT(m_base_type)                                                                                     \
 	struct VariantIndexedSetGet_##m_base_type {                                                                                     \
@@ -990,7 +989,6 @@ INDEXED_SETGET_STRUCT_TYPED(PackedVector3Array, Vector3)
 INDEXED_SETGET_STRUCT_TYPED(PackedStringArray, String)
 INDEXED_SETGET_STRUCT_TYPED(PackedColorArray, Color)
 
-INDEXED_SETGET_STRUCT_VARIANT(Array)
 INDEXED_SETGET_STRUCT_DICT(Dictionary)
 
 struct VariantIndexedSetterGetterInfo {

+ 21 - 13
modules/gdscript/gdscript.cpp

@@ -1310,21 +1310,29 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
 					return true; //function exists, call was successful
 				}
 			} else {
-				if (!member->data_type.is_type(p_value)) {
-					// Try conversion
-					Callable::CallError ce;
-					const Variant *value = &p_value;
-					Variant converted;
-					Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
-					if (ce.error == Callable::CallError::CALL_OK) {
-						members.write[member->index] = converted;
-						return true;
-					} else {
-						return false;
+				if (member->data_type.has_type) {
+					if (member->data_type.builtin_type == Variant::ARRAY && member->data_type.has_container_element_type()) {
+						// Typed array.
+						if (p_value.get_type() == Variant::ARRAY) {
+							return VariantInternal::get_array(&members.write[member->index])->typed_assign(p_value);
+						} else {
+							return false;
+						}
+					} else if (!member->data_type.is_type(p_value)) {
+						// Try conversion
+						Callable::CallError ce;
+						const Variant *value = &p_value;
+						Variant converted;
+						Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
+						if (ce.error == Callable::CallError::CALL_OK) {
+							members.write[member->index] = converted;
+							return true;
+						} else {
+							return false;
+						}
 					}
-				} else {
-					members.write[member->index] = p_value;
 				}
+				members.write[member->index] = p_value;
 			}
 			return true;
 		}

+ 195 - 24
modules/gdscript/gdscript_analyzer.cpp

@@ -413,6 +413,14 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
 		}
 		result.kind = GDScriptParser::DataType::BUILTIN;
 		result.builtin_type = GDScriptParser::get_builtin_type(first);
+
+		if (result.builtin_type == Variant::ARRAY) {
+			GDScriptParser::DataType container_type = resolve_datatype(p_type->container_type);
+
+			if (container_type.kind != GDScriptParser::DataType::VARIANT) {
+				result.set_container_element_type(container_type);
+			}
+		}
 	} else if (class_exists(first)) {
 		// Native engine classes.
 		result.kind = GDScriptParser::DataType::NATIVE;
@@ -513,6 +521,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
 		}
 	}
 
+	if (result.builtin_type != Variant::ARRAY && p_type->container_type != nullptr) {
+		push_error("Only arrays can specify the collection element type.", p_type);
+	}
+
 	p_type->set_datatype(result);
 	return result;
 }
@@ -535,9 +547,23 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
 				datatype.kind = GDScriptParser::DataType::VARIANT;
 				datatype.type_source = GDScriptParser::DataType::UNDETECTED;
 
+				GDScriptParser::DataType specified_type;
+				if (member.variable->datatype_specifier != nullptr) {
+					specified_type = resolve_datatype(member.variable->datatype_specifier);
+					specified_type.is_meta_type = false;
+				}
+
 				if (member.variable->initializer != nullptr) {
 					member.variable->set_datatype(datatype); // Allow recursive usage.
 					reduce_expression(member.variable->initializer);
+					if ((member.variable->infer_datatype || (member.variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && member.variable->initializer->type == GDScriptParser::Node::ARRAY) {
+						// Typed array.
+						GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.variable->initializer);
+						// Can only infer typed array if it has elements.
+						if ((member.variable->infer_datatype && array->elements.size() > 0) || member.variable->datatype_specifier != nullptr) {
+							update_array_literal_element_type(specified_type, array);
+						}
+					}
 					datatype = member.variable->initializer->get_datatype();
 					if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
 						datatype.type_source = GDScriptParser::DataType::INFERRED;
@@ -545,8 +571,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
 				}
 
 				if (member.variable->datatype_specifier != nullptr) {
-					datatype = resolve_datatype(member.variable->datatype_specifier);
-					datatype.is_meta_type = false;
+					datatype = specified_type;
 
 					if (member.variable->initializer != nullptr) {
 						if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
@@ -609,10 +634,23 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
 			case GDScriptParser::ClassNode::Member::CONSTANT: {
 				reduce_expression(member.constant->initializer);
 
+				GDScriptParser::DataType specified_type;
+
+				if (member.constant->datatype_specifier != nullptr) {
+					specified_type = resolve_datatype(member.constant->datatype_specifier);
+					specified_type.is_meta_type = false;
+				}
+
 				GDScriptParser::DataType datatype = member.constant->get_datatype();
 				if (member.constant->initializer) {
 					if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) {
-						const_fold_array(static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer));
+						GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.variable->initializer);
+						const_fold_array(array);
+
+						// Can only infer typed array if it has elements.
+						if (array->elements.size() > 0 || (member.variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) {
+							update_array_literal_element_type(specified_type, array);
+						}
 					} else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
 						const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer));
 					}
@@ -622,8 +660,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
 					}
 
 					if (member.constant->datatype_specifier != nullptr) {
-						datatype = resolve_datatype(member.constant->datatype_specifier);
-						datatype.is_meta_type = false;
+						datatype = specified_type;
 
 						if (!is_type_compatible(datatype, member.constant->initializer->get_datatype(), true)) {
 							push_error(vformat(R"(Value of type "%s" cannot be initialized to constant of type "%s".)", member.constant->initializer->get_datatype().to_string(), datatype.to_string()), member.constant->initializer);
@@ -1092,8 +1129,23 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
 	GDScriptParser::DataType type;
 	type.kind = GDScriptParser::DataType::VARIANT; // By default.
 
+	GDScriptParser::DataType specified_type;
+	if (p_variable->datatype_specifier != nullptr) {
+		specified_type = resolve_datatype(p_variable->datatype_specifier);
+		specified_type.is_meta_type = false;
+	}
+
 	if (p_variable->initializer != nullptr) {
 		reduce_expression(p_variable->initializer);
+		if ((p_variable->infer_datatype || (p_variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && p_variable->initializer->type == GDScriptParser::Node::ARRAY) {
+			// Typed array.
+			GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_variable->initializer);
+			// Can only infer typed array if it has elements.
+			if ((p_variable->infer_datatype && array->elements.size() > 0) || p_variable->datatype_specifier != nullptr) {
+				update_array_literal_element_type(specified_type, array);
+			}
+		}
+
 		type = p_variable->initializer->get_datatype();
 
 		if (p_variable->infer_datatype) {
@@ -1117,7 +1169,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
 	}
 
 	if (p_variable->datatype_specifier != nullptr) {
-		type = resolve_datatype(p_variable->datatype_specifier);
+		type = specified_type;
 		type.is_meta_type = false;
 
 		if (p_variable->initializer != nullptr) {
@@ -1362,6 +1414,12 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
 
 	if (p_return->return_value != nullptr) {
 		reduce_expression(p_return->return_value);
+		if (p_return->return_value->type == GDScriptParser::Node::ARRAY) {
+			// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
+			if (parser->current_function->get_datatype().has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) {
+				update_array_literal_element_type(parser->current_function->get_datatype(), static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
+			}
+		}
 		result = p_return->return_value->get_datatype();
 	} else {
 		// Return type is null by default.
@@ -1498,6 +1556,52 @@ void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
 	p_array->set_datatype(arr_type);
 }
 
+// When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed.
+// This function determines which type is that (if any).
+void GDScriptAnalyzer::update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal) {
+	GDScriptParser::DataType array_type = p_array_literal->get_datatype();
+	if (p_array_literal->elements.size() == 0) {
+		// Empty array literal, just make the same type as the storage.
+		array_type.set_container_element_type(p_base_type.get_container_element_type());
+	} else {
+		// Check if elements match.
+		bool all_same_type = true;
+		bool all_have_type = true;
+
+		GDScriptParser::DataType element_type;
+		for (int i = 0; i < p_array_literal->elements.size(); i++) {
+			if (i == 0) {
+				element_type = p_array_literal->elements[0]->get_datatype();
+			} else {
+				GDScriptParser::DataType this_element_type = p_array_literal->elements[i]->get_datatype();
+				if (this_element_type.has_no_type()) {
+					all_same_type = false;
+					all_have_type = false;
+					break;
+				} else if (element_type != this_element_type) {
+					if (!is_type_compatible(element_type, this_element_type, false)) {
+						if (is_type_compatible(this_element_type, element_type, false)) {
+							// This element is a super-type to the previous type, so we use the super-type.
+							element_type = this_element_type;
+						} else {
+							// It's incompatible.
+							all_same_type = false;
+							break;
+						}
+					}
+				}
+			}
+		}
+		if (all_same_type) {
+			array_type.set_container_element_type(element_type);
+		} else if (all_have_type) {
+			push_error(vformat(R"(Variant array is not compatible with an array of type "%s".)", p_base_type.get_container_element_type().to_string()), p_array_literal);
+		}
+	}
+	// Update the type on the value itself.
+	p_array_literal->set_datatype(array_type);
+}
+
 void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) {
 	reduce_expression(p_assignment->assignee);
 	reduce_expression(p_assignment->assigned_value);
@@ -1506,24 +1610,33 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
 		return;
 	}
 
-	if (p_assignment->assignee->get_datatype().is_constant) {
+	GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
+
+	// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
+	if (assignee_type.has_container_element_type() && p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY) {
+		update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value));
+	}
+
+	GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype();
+
+	if (assignee_type.is_constant) {
 		push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
 	}
 
-	if (!p_assignment->assignee->get_datatype().is_variant() && !p_assignment->assigned_value->get_datatype().is_variant()) {
+	if (!assignee_type.is_variant() && !assigned_value_type.is_variant()) {
 		bool compatible = true;
-		GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype();
+		GDScriptParser::DataType op_type = assigned_value_type;
 		if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
-			op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible, p_assignment->assigned_value);
+			op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value);
 		}
 
 		if (compatible) {
-			compatible = is_type_compatible(p_assignment->assignee->get_datatype(), op_type, true);
+			compatible = is_type_compatible(assignee_type, op_type, true);
 			if (!compatible) {
-				if (p_assignment->assignee->get_datatype().is_hard_type()) {
+				if (assignee_type.is_hard_type()) {
 					// Try reverse test since it can be a masked subtype.
-					if (!is_type_compatible(op_type, p_assignment->assignee->get_datatype(), true)) {
-						push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value);
+					if (!is_type_compatible(op_type, assignee_type, true)) {
+						push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
 					} else {
 						// TODO: Add warning.
 						mark_node_unsafe(p_assignment);
@@ -1534,11 +1647,11 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
 				}
 			}
 		} else {
-			push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", p_assignment->assignee->get_datatype().to_string(), p_assignment->assigned_value->get_datatype().to_string()), p_assignment);
+			push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
 		}
 	}
 
-	if (p_assignment->assignee->get_datatype().has_no_type() || p_assignment->assigned_value->get_datatype().is_variant()) {
+	if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
 		mark_node_unsafe(p_assignment);
 	}
 
@@ -1558,7 +1671,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
 			case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
 				GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
 				if (!id_type.is_hard_type()) {
-					id_type = p_assignment->assigned_value->get_datatype();
+					id_type = assigned_value_type;
 					id_type.type_source = GDScriptParser::DataType::INFERRED;
 					id_type.is_constant = false;
 					identifier->variable_source->set_datatype(id_type);
@@ -1567,7 +1680,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
 			case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
 				GDScriptParser::DataType id_type = identifier->bind_source->get_datatype();
 				if (!id_type.is_hard_type()) {
-					id_type = p_assignment->assigned_value->get_datatype();
+					id_type = assigned_value_type;
 					id_type.type_source = GDScriptParser::DataType::INFERRED;
 					id_type.is_constant = false;
 					identifier->variable_source->set_datatype(id_type);
@@ -1579,12 +1692,10 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
 		}
 	}
 
-	GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
-	GDScriptParser::DataType assigned_type = p_assignment->assigned_value->get_datatype();
 #ifdef DEBUG_ENABLED
-	if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_type.kind == GDScriptParser::DataType::BUILTIN && assigned_type.builtin_type == Variant::NIL) {
+	if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) {
 		parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name);
-	} else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_type.builtin_type == Variant::FLOAT) {
+	} else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
 		parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
 	}
 #endif
@@ -1728,8 +1839,12 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
 
 void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_await) {
 	bool all_is_constant = true;
+	Map<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
 	for (int i = 0; i < p_call->arguments.size(); i++) {
 		reduce_expression(p_call->arguments[i]);
+		if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) {
+			arrays[i] = static_cast<GDScriptParser::ArrayNode *>(p_call->arguments[i]);
+		}
 		all_is_constant = all_is_constant && p_call->arguments[i]->is_constant;
 	}
 
@@ -2007,6 +2122,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
 	List<GDScriptParser::DataType> par_types;
 
 	if (get_function_signature(p_call, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+		// If the function require typed arrays we must make literals be typed.
+		for (Map<int, GDScriptParser::ArrayNode *>::Element *E = arrays.front(); E; E = E->next()) {
+			int index = E->key();
+			if (index < par_types.size() && par_types[index].has_container_element_type()) {
+				update_array_literal_element_type(par_types[index], E->get());
+			}
+		}
 		validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
 
 		if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
@@ -2752,11 +2874,20 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
 					case Variant::TRANSFORM:
 					case Variant::PLANE:
 					case Variant::COLOR:
-					case Variant::ARRAY:
 					case Variant::DICTIONARY:
 						result_type.kind = GDScriptParser::DataType::VARIANT;
 						result_type.type_source = GDScriptParser::DataType::UNDETECTED;
 						break;
+					// Can have an element type.
+					case Variant::ARRAY:
+						if (base_type.has_container_element_type()) {
+							result_type = base_type.get_container_element_type();
+							result_type.type_source = base_type.type_source;
+						} else {
+							result_type.kind = GDScriptParser::DataType::VARIANT;
+							result_type.type_source = GDScriptParser::DataType::UNDETECTED;
+						}
+						break;
 					// Here for completeness.
 					case Variant::OBJECT:
 					case Variant::VARIANT_MAX:
@@ -2979,6 +3110,34 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
 		result.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
 	} else {
 		result.kind = GDScriptParser::DataType::BUILTIN;
+		result.builtin_type = p_property.type;
+		if (p_property.type == Variant::ARRAY && p_property.hint == PROPERTY_HINT_ARRAY_TYPE) {
+			// Check element type.
+			StringName elem_type_name = p_property.hint_string;
+			GDScriptParser::DataType elem_type;
+			elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+
+			Variant::Type elem_builtin_type = GDScriptParser::get_builtin_type(elem_type_name);
+			if (elem_builtin_type < Variant::VARIANT_MAX) {
+				// Builtin type.
+				elem_type.kind = GDScriptParser::DataType::BUILTIN;
+				elem_type.builtin_type = elem_builtin_type;
+			} else if (class_exists(elem_type_name)) {
+				elem_type.kind = GDScriptParser::DataType::NATIVE;
+				elem_type.builtin_type = Variant::OBJECT;
+				elem_type.native_type = p_property.hint_string;
+			} else if (ScriptServer::is_global_class(elem_type_name)) {
+				// Just load this as it shouldn't be a GDScript.
+				Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(elem_type_name));
+				elem_type.kind = GDScriptParser::DataType::SCRIPT;
+				elem_type.builtin_type = Variant::OBJECT;
+				elem_type.native_type = script->get_instance_base_type();
+				elem_type.script_type = script;
+			} else {
+				ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed array.");
+			}
+			result.set_container_element_type(elem_type);
+		}
 	}
 	return result;
 }
@@ -3257,6 +3416,18 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
 			// Enum value is also integer.
 			valid = true;
 		}
+		if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) {
+			// Check the element type.
+			if (p_target.has_container_element_type()) {
+				if (!p_source.has_container_element_type()) {
+					// TODO: Maybe this is valid but unsafe?
+					// Variant array can't be appended to typed array.
+					valid = false;
+				} else {
+					valid = is_type_compatible(p_target.get_container_element_type(), p_source.get_container_element_type(), false);
+				}
+			}
+		}
 		return valid;
 	}
 
@@ -3385,7 +3556,7 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
 #endif
 }
 
-bool GDScriptAnalyzer::class_exists(const StringName &p_class) {
+bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
 	StringName real_name = get_real_class_name(p_class);
 	return ClassDB::class_exists(real_name) && ClassDB::is_class_exposed(real_name);
 }

+ 2 - 1
modules/gdscript/gdscript_analyzer.h

@@ -103,10 +103,11 @@ class GDScriptAnalyzer {
 	bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
 	GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
 	GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
+	void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
 	bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
 	void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
 	void mark_node_unsafe(const GDScriptParser::Node *p_node);
-	bool class_exists(const StringName &p_class);
+	bool class_exists(const StringName &p_class) const;
 	Ref<GDScriptParserRef> get_parser_for(const String &p_path);
 #ifdef DEBUG_ENABLED
 	bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);

+ 34 - 5
modules/gdscript/gdscript_byte_codegen.cpp

@@ -599,10 +599,16 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
 		// Typed assignment.
 		switch (p_target.type.kind) {
 			case GDScriptDataType::BUILTIN: {
-				append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
-				append(p_target);
-				append(p_source);
-				append(p_target.type.builtin_type);
+				if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+					append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+					append(p_target);
+					append(p_source);
+				} else {
+					append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
+					append(p_target);
+					append(p_source);
+					append(p_target.type.builtin_type);
+				}
 			} break;
 			case GDScriptDataType::NATIVE: {
 				int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
@@ -633,7 +639,11 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
 			}
 		}
 	} else {
-		if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
+		if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+			append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+			append(p_target);
+			append(p_source);
+		} else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
 			// Need conversion..
 			append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
 			append(p_target);
@@ -980,6 +990,25 @@ void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, c
 	append(p_arguments.size());
 }
 
+void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) {
+	append(GDScriptFunction::OPCODE_CONSTRUCT_TYPED_ARRAY, 2 + p_arguments.size());
+	for (int i = 0; i < p_arguments.size(); i++) {
+		append(p_arguments[i]);
+	}
+	append(p_target);
+	if (p_element_type.script_type) {
+		Variant script_type = Ref<Script>(p_element_type.script_type);
+		int addr = get_constant_pos(script_type);
+		addr |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS;
+		append(addr);
+	} else {
+		append(Address()); // null.
+	}
+	append(p_arguments.size());
+	append(p_element_type.builtin_type);
+	append(p_element_type.native_type);
+}
+
 void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) {
 	append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY, 1 + p_arguments.size());
 	for (int i = 0; i < p_arguments.size(); i++) {

+ 1 - 0
modules/gdscript/gdscript_byte_codegen.h

@@ -445,6 +445,7 @@ public:
 	virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
 	virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
 	virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
+	virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override;
 	virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) override;
 	virtual void write_await(const Address &p_target, const Address &p_operand) override;
 	virtual void write_if(const Address &p_condition) override;

+ 1 - 0
modules/gdscript/gdscript_codegen.h

@@ -137,6 +137,7 @@ public:
 	virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
+	virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0;
 	virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) = 0;
 	virtual void write_await(const Address &p_target, const Address &p_operand) = 0;
 	virtual void write_if(const Address &p_condition) = 0;

+ 48 - 6
modules/gdscript/gdscript_compiler.cpp

@@ -153,6 +153,10 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 		}
 	}
 
+	if (p_datatype.has_container_element_type()) {
+		result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type()));
+	}
+
 	// Only hold strong reference to the script if it's not the owner of the
 	// element qualified with this type, to avoid cyclic references (leaks).
 	if (result.script_type && result.script_type == p_owner) {
@@ -376,10 +380,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 			Vector<GDScriptCodeGenerator::Address> values;
 
 			// Create the result temporary first since it's the last to be killed.
-			GDScriptDataType array_type;
-			array_type.has_type = true;
-			array_type.kind = GDScriptDataType::BUILTIN;
-			array_type.builtin_type = Variant::ARRAY;
+			GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype());
 			GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type);
 
 			for (int i = 0; i < an->elements.size(); i++) {
@@ -390,7 +391,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
 				values.push_back(val);
 			}
 
-			gen->write_construct_array(result, values);
+			if (array_type.has_container_element_type()) {
+				gen->write_construct_typed_array(result, array_type.get_container_element_type(), values);
+			} else {
+				gen->write_construct_array(result, values);
+			}
 
 			for (int i = 0; i < values.size(); i++) {
 				if (values[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -1733,8 +1738,17 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
 				const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);
 				// Should be already in stack when the block began.
 				GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
+				GDScriptParser::DataType local_type = lv->get_datatype();
 
 				if (lv->initializer != nullptr) {
+					// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
+					if (local_type.is_hard_type() && local_type.builtin_type == Variant::ARRAY) {
+						if (local_type.has_container_element_type()) {
+							codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+						} else {
+							codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>());
+						}
+					}
 					GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, lv->initializer);
 					if (error) {
 						return error;
@@ -1743,6 +1757,14 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
 					if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
 						codegen.generator->pop_temporary();
 					}
+				} else if (lv->get_datatype().is_hard_type()) {
+					// Initialize with default for type.
+					if (local_type.has_container_element_type()) {
+						codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+					} else if (local_type.kind == GDScriptParser::DataType::BUILTIN) {
+						codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+					}
+					// The `else` branch is for objects, in such case we leave it as `null`.
 				}
 			} break;
 			case GDScriptParser::Node::CONSTANT: {
@@ -1844,21 +1866,41 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
 				continue;
 			}
 
+			GDScriptParser::DataType field_type = field->get_datatype();
+
+			GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
 			if (field->initializer) {
 				// Emit proper line change.
 				codegen.generator->write_newline(field->initializer->start_line);
 
+				// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
+				if (field_type.is_hard_type() && field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type()) {
+					if (field_type.has_container_element_type()) {
+						codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+					} else {
+						codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
+					}
+				}
 				GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true);
 				if (error) {
 					memdelete(codegen.generator);
 					return error;
 				}
-				GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
 
 				codegen.generator->write_assign(dst_address, src_address);
 				if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
 					codegen.generator->pop_temporary();
 				}
+			} else if (field->get_datatype().is_hard_type()) {
+				codegen.generator->write_newline(field->start_line);
+
+				// Initialize with default for type.
+				if (field_type.has_container_element_type()) {
+					codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+				} else if (field_type.kind == GDScriptParser::DataType::BUILTIN) {
+					codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+				}
+				// The `else` branch is for objects, in such case we leave it as `null`.
 			}
 		}
 	}

+ 41 - 0
modules/gdscript/gdscript_disassembler.cpp

@@ -322,6 +322,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 				incr += 4;
 			} break;
+			case OPCODE_ASSIGN_TYPED_ARRAY: {
+				text += "assign typed array ";
+				text += DADDR(1);
+				text += " = ";
+				text += DADDR(2);
+
+				incr += 3;
+			} break;
 			case OPCODE_ASSIGN_TYPED_NATIVE: {
 				text += "assign typed native (";
 				text += DADDR(3);
@@ -426,6 +434,39 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
 
 				incr += 3 + argc;
 			} break;
+			case OPCODE_CONSTRUCT_TYPED_ARRAY: {
+				int argc = _code_ptr[ip + 1 + instr_var_args];
+
+				Ref<Script> script_type = get_constant(_code_ptr[ip + argc + 2]);
+				Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + argc + 4];
+				StringName native_type = get_global_name(_code_ptr[ip + argc + 5]);
+
+				String type_name;
+				if (script_type.is_valid() && script_type->is_valid()) {
+					type_name = script_type->get_path();
+				} else if (native_type != StringName()) {
+					type_name = native_type;
+				} else {
+					type_name = Variant::get_type_name(builtin_type);
+				}
+
+				text += " make_typed_array (";
+				text += type_name;
+				text += ") ";
+
+				text += DADDR(1 + argc);
+				text += " = [";
+
+				for (int i = 0; i < argc; i++) {
+					if (i > 0)
+						text += ", ";
+					text += DADDR(1 + i);
+				}
+
+				text += "]";
+
+				incr += 3 + argc;
+			} break;
 			case OPCODE_CONSTRUCT_DICTIONARY: {
 				int argc = _code_ptr[ip + 1 + instr_var_args];
 				text += "make_dict ";

+ 68 - 3
modules/gdscript/gdscript_function.h

@@ -43,7 +43,11 @@
 class GDScriptInstance;
 class GDScript;
 
-struct GDScriptDataType {
+class GDScriptDataType {
+private:
+	GDScriptDataType *container_element_type = nullptr;
+
+public:
 	enum Kind {
 		UNINITIALIZED,
 		BUILTIN,
@@ -71,7 +75,24 @@ struct GDScriptDataType {
 			case BUILTIN: {
 				Variant::Type var_type = p_variant.get_type();
 				bool valid = builtin_type == var_type;
-				if (!valid && p_allow_implicit_conversion) {
+				if (valid && builtin_type == Variant::ARRAY && has_container_element_type()) {
+					Array array = p_variant;
+					if (array.is_typed()) {
+						Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin();
+						StringName array_native_type = array.get_typed_class_name();
+						Ref<Script> array_script_type_ref = array.get_typed_script();
+
+						if (array_script_type_ref.is_valid()) {
+							valid = (container_element_type->kind == SCRIPT || container_element_type->kind == GDSCRIPT) && container_element_type->script_type == array_script_type_ref.ptr();
+						} else if (array_native_type != StringName()) {
+							valid = container_element_type->kind == NATIVE && container_element_type->native_type == array_native_type;
+						} else {
+							valid = container_element_type->kind == BUILTIN && container_element_type->builtin_type == array_builtin_type;
+						}
+					} else {
+						valid = false;
+					}
+				} else if (!valid && p_allow_implicit_conversion) {
 					valid = Variant::can_convert_strict(var_type, builtin_type);
 				}
 				return valid;
@@ -153,7 +174,49 @@ struct GDScriptDataType {
 		return info;
 	}
 
-	GDScriptDataType() {}
+	void set_container_element_type(const GDScriptDataType &p_element_type) {
+		container_element_type = memnew(GDScriptDataType(p_element_type));
+	}
+
+	GDScriptDataType get_container_element_type() const {
+		ERR_FAIL_COND_V(container_element_type == nullptr, GDScriptDataType());
+		return *container_element_type;
+	}
+
+	bool has_container_element_type() const {
+		return container_element_type != nullptr;
+	}
+
+	void unset_container_element_type() {
+		if (container_element_type) {
+			memdelete(container_element_type);
+		}
+		container_element_type = nullptr;
+	}
+
+	GDScriptDataType() = default;
+
+	GDScriptDataType &operator=(const GDScriptDataType &p_other) {
+		kind = p_other.kind;
+		has_type = p_other.has_type;
+		builtin_type = p_other.builtin_type;
+		native_type = p_other.native_type;
+		script_type = p_other.script_type;
+		script_type_ref = p_other.script_type_ref;
+		unset_container_element_type();
+		if (p_other.has_container_element_type()) {
+			set_container_element_type(p_other.get_container_element_type());
+		}
+		return *this;
+	}
+
+	GDScriptDataType(const GDScriptDataType &p_other) {
+		*this = p_other;
+	}
+
+	~GDScriptDataType() {
+		unset_container_element_type();
+	}
 };
 
 class GDScriptFunction {
@@ -179,6 +242,7 @@ public:
 		OPCODE_ASSIGN_TRUE,
 		OPCODE_ASSIGN_FALSE,
 		OPCODE_ASSIGN_TYPED_BUILTIN,
+		OPCODE_ASSIGN_TYPED_ARRAY,
 		OPCODE_ASSIGN_TYPED_NATIVE,
 		OPCODE_ASSIGN_TYPED_SCRIPT,
 		OPCODE_CAST_TO_BUILTIN,
@@ -187,6 +251,7 @@ public:
 		OPCODE_CONSTRUCT, // Only for basic types!
 		OPCODE_CONSTRUCT_VALIDATED, // Only for basic types!
 		OPCODE_CONSTRUCT_ARRAY,
+		OPCODE_CONSTRUCT_TYPED_ARRAY,
 		OPCODE_CONSTRUCT_DICTIONARY,
 		OPCODE_CALL,
 		OPCODE_CALL_RETURN,

+ 16 - 0
modules/gdscript/gdscript_parser.cpp

@@ -2674,6 +2674,19 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
 
 	type->type_chain.push_back(type_element);
 
+	if (match(GDScriptTokenizer::Token::BRACKET_OPEN)) {
+		// Typed collection (like Array[int]).
+		type->container_type = parse_type(false); // Don't allow void for array element type.
+		if (type->container_type == nullptr) {
+			push_error(R"(Expected type for collection after "[".)");
+			type = nullptr;
+		} else if (type->container_type->container_type != nullptr) {
+			push_error("Nested typed collections are not supported.");
+		}
+		consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
+		return type;
+	}
+
 	int chain_index = 1;
 	while (match(GDScriptTokenizer::Token::PERIOD)) {
 		make_completion_context(COMPLETION_TYPE_ATTRIBUTE, type, chain_index++);
@@ -3278,6 +3291,9 @@ String GDScriptParser::DataType::to_string() const {
 			if (builtin_type == Variant::NIL) {
 				return "null";
 			}
+			if (builtin_type == Variant::ARRAY && has_container_element_type()) {
+				return vformat("Array[%s]", container_element_type->to_string());
+			}
 			return Variant::get_type_name(builtin_type);
 		case NATIVE:
 			if (is_meta_type) {

+ 58 - 2
modules/gdscript/gdscript_parser.h

@@ -94,7 +94,12 @@ public:
 	struct VariableNode;
 	struct WhileNode;
 
-	struct DataType {
+	class DataType {
+	private:
+		// Private access so we can control memory management.
+		DataType *container_element_type = nullptr;
+
+	public:
 		enum Kind {
 			BUILTIN,
 			NATIVE,
@@ -104,7 +109,6 @@ public:
 			ENUM_VALUE, // Value from enumeration.
 			VARIANT, // Can be any type.
 			UNRESOLVED,
-			// TODO: Enum
 		};
 		Kind kind = UNRESOLVED;
 
@@ -136,6 +140,26 @@ public:
 		_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
 		String to_string() const;
 
+		_FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
+			container_element_type = memnew(DataType(p_type));
+		}
+
+		_FORCE_INLINE_ DataType get_container_element_type() const {
+			ERR_FAIL_COND_V(container_element_type == nullptr, DataType());
+			return *container_element_type;
+		}
+
+		_FORCE_INLINE_ bool has_container_element_type() const {
+			return container_element_type != nullptr;
+		}
+
+		_FORCE_INLINE_ void unset_container_element_type() {
+			if (container_element_type) {
+				memdelete(container_element_type);
+			};
+			container_element_type = nullptr;
+		}
+
 		bool operator==(const DataType &p_other) const {
 			if (type_source == UNDETECTED || p_other.type_source == UNDETECTED) {
 				return true; // Can be consireded equal for parsing purposes.
@@ -173,6 +197,37 @@ public:
 		bool operator!=(const DataType &p_other) const {
 			return !(this->operator==(p_other));
 		}
+
+		DataType &operator=(const DataType &p_other) {
+			kind = p_other.kind;
+			type_source = p_other.type_source;
+			is_constant = p_other.is_constant;
+			is_meta_type = p_other.is_meta_type;
+			is_coroutine = p_other.is_coroutine;
+			builtin_type = p_other.builtin_type;
+			native_type = p_other.native_type;
+			enum_type = p_other.enum_type;
+			script_type = p_other.script_type;
+			script_path = p_other.script_path;
+			class_type = p_other.class_type;
+			method_info = p_other.method_info;
+			enum_values = p_other.enum_values;
+			unset_container_element_type();
+			if (p_other.has_container_element_type()) {
+				set_container_element_type(p_other.get_container_element_type());
+			}
+			return *this;
+		}
+
+		DataType() = default;
+
+		DataType(const DataType &p_other) {
+			*this = p_other;
+		}
+
+		~DataType() {
+			unset_container_element_type();
+		}
 	};
 
 	struct ParserError {
@@ -987,6 +1042,7 @@ public:
 
 	struct TypeNode : public Node {
 		Vector<IdentifierNode *> type_chain;
+		TypeNode *container_type = nullptr;
 
 		TypeNode() {
 			type = TYPE;

+ 84 - 4
modules/gdscript/gdscript_vm.cpp

@@ -127,6 +127,14 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
 }
 
 #ifdef DEBUG_ENABLED
+static String _get_script_name(const Ref<Script> p_script) {
+	if (p_script->get_name().is_empty()) {
+		return p_script->get_path().get_file();
+	} else {
+		return p_script->get_name();
+	}
+}
+
 static String _get_var_type(const Variant *p_var) {
 	String basestr;
 
@@ -140,15 +148,30 @@ static String _get_var_type(const Variant *p_var) {
 				basestr = "previously freed";
 			}
 		} else {
+			basestr = bobj->get_class();
 			if (bobj->get_script_instance()) {
-				basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")";
-			} else {
-				basestr = bobj->get_class();
+				basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")";
 			}
 		}
 
 	} else {
-		basestr = Variant::get_type_name(p_var->get_type());
+		if (p_var->get_type() == Variant::ARRAY) {
+			basestr = "Array";
+			const Array *p_array = VariantInternal::get_array(p_var);
+			Variant::Type builtin_type = (Variant::Type)p_array->get_typed_builtin();
+			StringName native_type = p_array->get_typed_class_name();
+			Ref<Script> script_type = p_array->get_typed_script();
+
+			if (script_type.is_valid() && script_type->is_valid()) {
+				basestr += "[" + _get_script_name(script_type) + "]";
+			} else if (native_type != StringName()) {
+				basestr += "[" + native_type.operator String() + "]";
+			} else if (builtin_type != Variant::NIL) {
+				basestr += "[" + Variant::get_type_name(builtin_type) + "]";
+			}
+		} else {
+			basestr = Variant::get_type_name(p_var->get_type());
+		}
 	}
 
 	return basestr;
@@ -207,6 +230,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
 		&&OPCODE_ASSIGN_TRUE,                        \
 		&&OPCODE_ASSIGN_FALSE,                       \
 		&&OPCODE_ASSIGN_TYPED_BUILTIN,               \
+		&&OPCODE_ASSIGN_TYPED_ARRAY,                 \
 		&&OPCODE_ASSIGN_TYPED_NATIVE,                \
 		&&OPCODE_ASSIGN_TYPED_SCRIPT,                \
 		&&OPCODE_CAST_TO_BUILTIN,                    \
@@ -215,6 +239,7 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
 		&&OPCODE_CONSTRUCT,                          \
 		&&OPCODE_CONSTRUCT_VALIDATED,                \
 		&&OPCODE_CONSTRUCT_ARRAY,                    \
+		&&OPCODE_CONSTRUCT_TYPED_ARRAY,              \
 		&&OPCODE_CONSTRUCT_DICTIONARY,               \
 		&&OPCODE_CALL,                               \
 		&&OPCODE_CALL_RETURN,                        \
@@ -1077,6 +1102,31 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			}
 			DISPATCH_OPCODE;
 
+			OPCODE(OPCODE_ASSIGN_TYPED_ARRAY) {
+				CHECK_SPACE(3);
+				GET_INSTRUCTION_ARG(dst, 0);
+				GET_INSTRUCTION_ARG(src, 1);
+
+				Array *dst_arr = VariantInternal::get_array(dst);
+
+				if (src->get_type() != Variant::ARRAY) {
+#ifdef DEBUG_ENABLED
+					err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
+							   "' to a variable of type '" + +"'.";
+					OPCODE_BREAK;
+#endif
+				}
+				if (!dst_arr->typed_assign(*src)) {
+#ifdef DEBUG_ENABLED
+					err_text = "Trying to assign a typed array with an array of different type.'";
+					OPCODE_BREAK;
+#endif
+				}
+
+				ip += 3;
+			}
+			DISPATCH_OPCODE;
+
 			OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
 				CHECK_SPACE(4);
 				GET_INSTRUCTION_ARG(dst, 0);
@@ -1308,6 +1358,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 				}
 
 				GET_INSTRUCTION_ARG(dst, argc);
+				*dst = Variant(); // Clear potential previous typed array.
 
 				*dst = array;
 
@@ -1315,6 +1366,35 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
 			}
 			DISPATCH_OPCODE;
 
+			OPCODE(OPCODE_CONSTRUCT_TYPED_ARRAY) {
+				CHECK_SPACE(3 + instr_arg_count);
+				ip += instr_arg_count;
+
+				int argc = _code_ptr[ip + 1];
+
+				GET_INSTRUCTION_ARG(script_type, argc + 1);
+				Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 2];
+				int native_type_idx = _code_ptr[ip + 3];
+				GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count);
+				const StringName native_type = _global_names_ptr[native_type_idx];
+
+				Array array;
+				array.set_typed(builtin_type, native_type, script_type);
+				array.resize(argc);
+
+				for (int i = 0; i < argc; i++) {
+					array[i] = *(instruction_args[i]);
+				}
+
+				GET_INSTRUCTION_ARG(dst, argc);
+				*dst = Variant(); // Clear potential previous typed array.
+
+				*dst = array;
+
+				ip += 4;
+			}
+			DISPATCH_OPCODE;
+
 			OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
 				CHECK_SPACE(2 + instr_arg_count);