summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/core_bind.cpp14
-rw-r--r--core/doc_data.cpp12
-rw-r--r--core/doc_data.h2
-rw-r--r--core/extension/gdextension_interface.cpp7
-rw-r--r--core/extension/gdextension_interface.h1
-rw-r--r--core/variant/array.cpp137
-rw-r--r--core/variant/array.h9
-rw-r--r--core/variant/container_type_validate.h11
-rw-r--r--core/variant/typed_array.h42
-rw-r--r--core/variant/variant_call.cpp11
-rw-r--r--core/variant/variant_internal.h19
-rw-r--r--core/variant/variant_parser.cpp148
-rw-r--r--doc/classes/Array.xml25
-rw-r--r--doc/classes/Dictionary.xml2
-rw-r--r--editor/doc_tools.cpp15
-rw-r--r--editor/editor_properties_array_dict.cpp158
-rw-r--r--editor/editor_properties_array_dict.h2
-rw-r--r--modules/gdscript/gdscript.cpp49
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp280
-rw-r--r--modules/gdscript/gdscript_analyzer.h13
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp34
-rw-r--r--modules/gdscript/gdscript_compiler.cpp27
-rw-r--r--modules/gdscript/gdscript_parser.cpp48
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/gdscript_vm.cpp87
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.gd (renamed from modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd204
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd7
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd7
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out2
-rw-r--r--scene/resources/packed_scene.cpp14
-rw-r--r--scene/resources/resource_format_text.cpp24
50 files changed, 943 insertions, 539 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 9de901754a..c752bdd057 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -717,7 +717,7 @@ TypedArray<PackedVector2Array> Geometry2D::decompose_polygon_in_convex(const Vec
TypedArray<PackedVector2Array> Geometry2D::merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::merge_polygons(p_polygon_a, p_polygon_b);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -739,7 +739,7 @@ TypedArray<PackedVector2Array> Geometry2D::clip_polygons(const Vector<Vector2> &
TypedArray<PackedVector2Array> Geometry2D::intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polygons(p_polygon_a, p_polygon_b);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -750,7 +750,7 @@ TypedArray<PackedVector2Array> Geometry2D::intersect_polygons(const Vector<Vecto
TypedArray<PackedVector2Array> Geometry2D::exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b) {
Vector<Vector<Point2>> polys = ::Geometry2D::exclude_polygons(p_polygon_a, p_polygon_b);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -761,7 +761,7 @@ TypedArray<PackedVector2Array> Geometry2D::exclude_polygons(const Vector<Vector2
TypedArray<PackedVector2Array> Geometry2D::clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
Vector<Vector<Point2>> polys = ::Geometry2D::clip_polyline_with_polygon(p_polyline, p_polygon);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -772,7 +772,7 @@ TypedArray<PackedVector2Array> Geometry2D::clip_polyline_with_polygon(const Vect
TypedArray<PackedVector2Array> Geometry2D::intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon) {
Vector<Vector<Point2>> polys = ::Geometry2D::intersect_polyline_with_polygon(p_polyline, p_polygon);
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -783,7 +783,7 @@ TypedArray<PackedVector2Array> Geometry2D::intersect_polyline_with_polygon(const
TypedArray<PackedVector2Array> Geometry2D::offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type) {
Vector<Vector<Point2>> polys = ::Geometry2D::offset_polygon(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type));
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
@@ -794,7 +794,7 @@ TypedArray<PackedVector2Array> Geometry2D::offset_polygon(const Vector<Vector2>
TypedArray<PackedVector2Array> Geometry2D::offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
Vector<Vector<Point2>> polys = ::Geometry2D::offset_polyline(p_polygon, p_delta, ::Geometry2D::PolyJoinType(p_join_type), ::Geometry2D::PolyEndType(p_end_type));
- Array ret;
+ TypedArray<PackedVector2Array> ret;
for (int i = 0; i < polys.size(); ++i) {
ret.push_back(polys[i]);
diff --git a/core/doc_data.cpp b/core/doc_data.cpp
index f90dbe1423..5e09e560d5 100644
--- a/core/doc_data.cpp
+++ b/core/doc_data.cpp
@@ -30,6 +30,14 @@
#include "doc_data.h"
+String DocData::get_default_value_string(const Variant &p_value) {
+ if (p_value.get_type() == Variant::ARRAY) {
+ return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace("\n", " ");
+ } else {
+ return p_value.get_construct_string().replace("\n", " ");
+ }
+}
+
void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) {
if (p_retinfo.type == Variant::INT && p_retinfo.hint == PROPERTY_HINT_INT_IS_POINTER) {
p_method.return_type = p_retinfo.hint_string;
@@ -105,7 +113,7 @@ void DocData::property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_propert
p_property.getter = p_memberinfo.getter;
if (p_memberinfo.has_default_value && p_memberinfo.default_value.get_type() != Variant::OBJECT) {
- p_property.default_value = p_memberinfo.default_value.get_construct_string().replace("\n", "");
+ p_property.default_value = get_default_value_string(p_memberinfo.default_value);
}
p_property.overridden = false;
@@ -148,7 +156,7 @@ void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const Met
int default_arg_index = i - (p_methodinfo.arguments.size() - p_methodinfo.default_arguments.size());
if (default_arg_index >= 0) {
Variant default_arg = p_methodinfo.default_arguments[default_arg_index];
- argument.default_value = default_arg.get_construct_string().replace("\n", "");
+ argument.default_value = get_default_value_string(default_arg);
}
p_method.arguments.push_back(argument);
}
diff --git a/core/doc_data.h b/core/doc_data.h
index 1cf4e4f206..c503a4e0d6 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -516,6 +516,8 @@ public:
}
};
+ static String get_default_value_string(const Variant &p_value);
+
static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo);
static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo);
static void property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo);
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 63ed60a710..3bea013fab 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -856,6 +856,12 @@ static GDExtensionVariantPtr gdextension_array_operator_index_const(GDExtensionC
return (GDExtensionVariantPtr)&self->operator[](p_index);
}
+void gdextension_array_ref(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from) {
+ Array *self = (Array *)p_self;
+ const Array *from = (const Array *)p_from;
+ self->_ref(*from);
+}
+
void gdextension_array_set_typed(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script) {
Array *self = reinterpret_cast<Array *>(p_self);
const StringName *class_name = reinterpret_cast<const StringName *>(p_class_name);
@@ -1136,6 +1142,7 @@ void gdextension_setup_interface(GDExtensionInterface *p_interface) {
gde_interface.array_operator_index = gdextension_array_operator_index;
gde_interface.array_operator_index_const = gdextension_array_operator_index_const;
+ gde_interface.array_ref = gdextension_array_ref;
gde_interface.array_set_typed = gdextension_array_set_typed;
/* Dictionary functions */
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 7f8f7374e9..876a09beff 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -551,6 +551,7 @@ typedef struct {
GDExtensionVariantPtr (*array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr
GDExtensionVariantPtr (*array_operator_index_const)(GDExtensionConstTypePtr p_self, GDExtensionInt p_index); // p_self should be an Array ptr
+ void (*array_ref)(GDExtensionTypePtr p_self, GDExtensionConstTypePtr p_from); // p_self should be an Array ptr
void (*array_set_typed)(GDExtensionTypePtr p_self, uint32_t p_type, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstVariantPtr p_script); // p_self should be an Array ptr
/* Dictionary functions */
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 94d1596514..d3c5ca801f 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -64,7 +64,7 @@ void Array::_ref(const Array &p_from) const {
_unref();
- _p = p_from._p;
+ _p = _fp;
}
void Array::_unref() const {
@@ -191,62 +191,73 @@ uint32_t Array::recursive_hash(int recursion_count) const {
return hash_fmix32(h);
}
-bool Array::_assign(const Array &p_array) {
- bool can_convert = p_array._p->typed.type == Variant::NIL;
- can_convert |= _p->typed.type == Variant::STRING && p_array._p->typed.type == Variant::STRING_NAME;
- can_convert |= _p->typed.type == Variant::STRING_NAME && p_array._p->typed.type == Variant::STRING;
+void Array::operator=(const Array &p_array) {
+ if (this == &p_array) {
+ return;
+ }
+ _ref(p_array);
+}
+
+void Array::assign(const Array &p_array) {
+ const ContainerTypeValidate &typed = _p->typed;
+ const ContainerTypeValidate &source_typed = p_array._p->typed;
- 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);
- } else if (_p->typed.type == Variant::NIL) { //from typed to untyped, must copy, but this is cheap anyway
+ if (typed == source_typed || typed.type == Variant::NIL || (source_typed.type == Variant::OBJECT && typed.can_reference(source_typed))) {
+ // from same to same or
+ // from anything to variants or
+ // from subclasses to base classes
_p->array = p_array._p->array;
- } else if (can_convert) { //from untyped to typed, must try to check if they are all valid
- if (_p->typed.type == Variant::OBJECT) {
- //for objects, it needs full validation, either can be converted or fail
- for (int i = 0; i < p_array._p->array.size(); i++) {
- const Variant &element = p_array._p->array[i];
- if (element.get_type() != Variant::OBJECT || !_p->typed.validate_object(element, "assign")) {
- return false;
- }
- }
- _p->array = p_array._p->array; //then just copy, which is cheap anyway
+ return;
+ }
- } else {
- //for non objects, we need to check if there is a valid conversion, which needs to happen one by one, so this is the worst case.
- Vector<Variant> new_array;
- new_array.resize(p_array._p->array.size());
- for (int i = 0; i < p_array._p->array.size(); i++) {
- Variant src_val = p_array._p->array[i];
- if (src_val.get_type() == _p->typed.type) {
- new_array.write[i] = src_val;
- } else if (Variant::can_convert_strict(src_val.get_type(), _p->typed.type)) {
- Variant *ptr = &src_val;
- 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_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_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) + "'.");
- }
+ const Variant *source = p_array._p->array.ptr();
+ int size = p_array._p->array.size();
+
+ if ((source_typed.type == Variant::NIL && typed.type == Variant::OBJECT) || (source_typed.type == Variant::OBJECT && source_typed.can_reference(typed))) {
+ // from variants to objects or
+ // from base classes to subclasses
+ for (int i = 0; i < size; i++) {
+ const Variant &element = source[i];
+ if (element.get_type() != Variant::NIL && (element.get_type() != Variant::OBJECT || !typed.validate_object(element, "assign"))) {
+ ERR_FAIL_MSG(vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(element.get_type()), Variant::get_type_name(typed.type)));
}
+ }
+ _p->array = p_array._p->array;
+ return;
+ }
- _p->array = new_array;
+ Vector<Variant> array;
+ array.resize(size);
+ Variant *data = array.ptrw();
+
+ if (source_typed.type == Variant::NIL && typed.type != Variant::OBJECT) {
+ // from variants to primitives
+ for (int i = 0; i < size; i++) {
+ const Variant *value = source + i;
+ if (value->get_type() == typed.type) {
+ data[i] = *value;
+ continue;
+ }
+ if (!Variant::can_convert_strict(value->get_type(), typed.type)) {
+ ERR_FAIL_MSG("Unable to convert array index " + itos(i) + " from '" + Variant::get_type_name(value->get_type()) + "' to '" + Variant::get_type_name(typed.type) + "'.");
+ }
+ Callable::CallError ce;
+ Variant::construct(typed.type, data[i], &value, 1, ce);
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
+ }
+ } else if (Variant::can_convert_strict(source_typed.type, typed.type)) {
+ // from primitives to different convertable primitives
+ for (int i = 0; i < size; i++) {
+ const Variant *value = source + i;
+ Callable::CallError ce;
+ Variant::construct(typed.type, data[i], &value, 1, ce);
+ ERR_FAIL_COND_MSG(ce.error, vformat(R"(Unable to convert array index %i from "%s" to "%s".)", i, Variant::get_type_name(value->get_type()), Variant::get_type_name(typed.type)));
}
- } else if (_p->typed.can_reference(p_array._p->typed)) { //same type or compatible
- _ref(p_array);
} else {
- ERR_FAIL_V_MSG(false, "Assignment of arrays of incompatible types.");
+ ERR_FAIL_MSG(vformat(R"(Cannot assign contents of "Array[%s]" to "Array[%s]".)", Variant::get_type_name(source_typed.type), Variant::get_type_name(typed.type)));
}
- return true;
-}
-void Array::operator=(const Array &p_array) {
- if (this == &p_array) {
- return;
- }
- _ref(p_array);
+ _p->array = array;
}
void Array::push_back(const Variant &p_value) {
@@ -269,7 +280,15 @@ void Array::append_array(const Array &p_array) {
Error Array::resize(int p_new_size) {
ERR_FAIL_COND_V_MSG(_p->read_only, ERR_LOCKED, "Array is in read-only state.");
- return _p->array.resize(p_new_size);
+ Variant::Type &variant_type = _p->typed.type;
+ int old_size = _p->array.size();
+ Error err = _p->array.resize_zeroed(p_new_size);
+ if (!err && variant_type != Variant::NIL && variant_type != Variant::OBJECT) {
+ for (int i = old_size; i < p_new_size; i++) {
+ VariantInternal::initialize(&_p->array.write[i], variant_type);
+ }
+ }
+ return err;
}
Error Array::insert(int p_pos, const Variant &p_value) {
@@ -403,24 +422,22 @@ Array Array::duplicate(bool p_deep) const {
Array Array::recursive_duplicate(bool p_deep, int recursion_count) const {
Array new_arr;
+ new_arr._p->typed = _p->typed;
if (recursion_count > MAX_RECURSION) {
ERR_PRINT("Max recursion reached");
return new_arr;
}
- int element_count = size();
- new_arr.resize(element_count);
- new_arr._p->typed = _p->typed;
if (p_deep) {
recursion_count++;
+ int element_count = size();
+ new_arr.resize(element_count);
for (int i = 0; i < element_count; i++) {
new_arr[i] = get(i).recursive_duplicate(true, recursion_count);
}
} else {
- for (int i = 0; i < element_count; i++) {
- new_arr[i] = get(i);
- }
+ new_arr._p->array = _p->array;
}
return new_arr;
@@ -737,11 +754,7 @@ Array::Array(const Array &p_from, uint32_t p_type, const StringName &p_class_nam
_p = memnew(ArrayPrivate);
_p->refcount.init();
set_typed(p_type, p_class_name, p_script);
- _assign(p_from);
-}
-
-bool Array::typed_assign(const Array &p_other) {
- return _assign(p_other);
+ assign(p_from);
}
void Array::set_typed(uint32_t p_type, const StringName &p_class_name, const Variant &p_script) {
@@ -763,6 +776,10 @@ bool Array::is_typed() const {
return _p->typed.type != Variant::NIL;
}
+bool Array::is_same_typed(const Array &p_other) const {
+ return _p->typed == p_other._p->typed;
+}
+
uint32_t Array::get_typed_builtin() const {
return _p->typed.type;
}
diff --git a/core/variant/array.h b/core/variant/array.h
index d9ca3278fb..4ef8ba8ce7 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -43,13 +43,11 @@ class Callable;
class Array {
mutable ArrayPrivate *_p;
- void _ref(const Array &p_from) const;
void _unref() const;
-protected:
- bool _assign(const Array &p_array);
-
public:
+ void _ref(const Array &p_from) const;
+
Variant &operator[](int p_idx);
const Variant &operator[](int p_idx) const;
@@ -68,6 +66,7 @@ public:
uint32_t recursive_hash(int recursion_count) const;
void operator=(const Array &p_array);
+ void assign(const Array &p_array);
void push_back(const Variant &p_value);
_FORCE_INLINE_ void append(const Variant &p_value) { push_back(p_value); } //for python compatibility
void append_array(const Array &p_array);
@@ -120,9 +119,9 @@ 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;
+ bool is_same_typed(const Array &p_other) const;
uint32_t get_typed_builtin() const;
StringName get_typed_class_name() const;
Variant get_typed_script() const;
diff --git a/core/variant/container_type_validate.h b/core/variant/container_type_validate.h
index 377be03bdf..796b66dc77 100644
--- a/core/variant/container_type_validate.h
+++ b/core/variant/container_type_validate.h
@@ -74,8 +74,15 @@ struct ContainerTypeValidate {
return true;
}
+ _FORCE_INLINE_ bool operator==(const ContainerTypeValidate &p_type) const {
+ return type == p_type.type && class_name == p_type.class_name && script == p_type.script;
+ }
+ _FORCE_INLINE_ bool operator!=(const ContainerTypeValidate &p_type) const {
+ return type != p_type.type || class_name != p_type.class_name || script != p_type.script;
+ }
+
// Coerces String and StringName into each other when needed.
- _FORCE_INLINE_ bool validate(Variant &inout_variant, const char *p_operation = "use") {
+ _FORCE_INLINE_ bool validate(Variant &inout_variant, const char *p_operation = "use") const {
if (type == Variant::NIL) {
return true;
}
@@ -102,7 +109,7 @@ struct ContainerTypeValidate {
return validate_object(inout_variant, p_operation);
}
- _FORCE_INLINE_ bool validate_object(const Variant &p_variant, const char *p_operation = "use") {
+ _FORCE_INLINE_ bool validate_object(const Variant &p_variant, const char *p_operation = "use") const {
ERR_FAIL_COND_V(p_variant.get_type() != Variant::OBJECT, false);
#ifdef DEBUG_ENABLED
diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h
index 5aeabbacf8..03e557819b 100644
--- a/core/variant/typed_array.h
+++ b/core/variant/typed_array.h
@@ -40,14 +40,9 @@
template <class T>
class TypedArray : public Array {
public:
- template <class U>
- _FORCE_INLINE_ void operator=(const TypedArray<U> &p_array) {
- static_assert(__is_base_of(T, U));
- _assign(p_array);
- }
-
_FORCE_INLINE_ void operator=(const Array &p_array) {
- _assign(p_array);
+ ERR_FAIL_COND_MSG(!is_same_typed(p_array), "Cannot assign an array with a different element type.");
+ _ref(p_array);
}
_FORCE_INLINE_ TypedArray(const Variant &p_variant) :
Array(Array(p_variant), Variant::OBJECT, T::get_class_static(), Variant()) {
@@ -62,22 +57,23 @@ public:
//specialization for the rest of variant types
-#define MAKE_TYPED_ARRAY(m_type, m_variant_type) \
- template <> \
- class TypedArray<m_type> : public Array { \
- public: \
- _FORCE_INLINE_ void operator=(const Array &p_array) { \
- _assign(p_array); \
- } \
- _FORCE_INLINE_ TypedArray(const Variant &p_variant) : \
- Array(Array(p_variant), m_variant_type, StringName(), Variant()) { \
- } \
- _FORCE_INLINE_ TypedArray(const Array &p_array) : \
- Array(p_array, m_variant_type, StringName(), Variant()) { \
- } \
- _FORCE_INLINE_ TypedArray() { \
- set_typed(m_variant_type, StringName(), Variant()); \
- } \
+#define MAKE_TYPED_ARRAY(m_type, m_variant_type) \
+ template <> \
+ class TypedArray<m_type> : public Array { \
+ public: \
+ _FORCE_INLINE_ void operator=(const Array &p_array) { \
+ ERR_FAIL_COND_MSG(!is_same_typed(p_array), "Cannot assign an array with a different element type."); \
+ _ref(p_array); \
+ } \
+ _FORCE_INLINE_ TypedArray(const Variant &p_variant) : \
+ Array(Array(p_variant), m_variant_type, StringName(), Variant()) { \
+ } \
+ _FORCE_INLINE_ TypedArray(const Array &p_array) : \
+ Array(p_array, m_variant_type, StringName(), Variant()) { \
+ } \
+ _FORCE_INLINE_ TypedArray() { \
+ set_typed(m_variant_type, StringName(), Variant()); \
+ } \
};
MAKE_TYPED_ARRAY(bool, Variant::BOOL)
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index e7d1a2478f..0c0c8f657a 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -2189,6 +2189,7 @@ static void _register_variant_builtin_methods() {
bind_method(Array, is_empty, sarray(), varray());
bind_method(Array, clear, sarray(), varray());
bind_method(Array, hash, sarray(), varray());
+ bind_method(Array, assign, sarray("array"), varray());
bind_method(Array, push_back, sarray("value"), varray());
bind_method(Array, push_front, sarray("value"), varray());
bind_method(Array, append, sarray("value"), varray());
@@ -2223,8 +2224,8 @@ static void _register_variant_builtin_methods() {
bind_method(Array, all, sarray("method"), varray());
bind_method(Array, max, sarray(), varray());
bind_method(Array, min, sarray(), varray());
- bind_method(Array, typed_assign, sarray("array"), varray());
bind_method(Array, is_typed, sarray(), varray());
+ bind_method(Array, is_same_typed, sarray("array"), varray());
bind_method(Array, get_typed_builtin, sarray(), varray());
bind_method(Array, get_typed_class_name, sarray(), varray());
bind_method(Array, get_typed_script, sarray(), varray());
@@ -2402,7 +2403,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedStringArray, remove_at, sarray("index"), varray());
bind_method(PackedStringArray, insert, sarray("at_index", "value"), varray());
bind_method(PackedStringArray, fill, sarray("value"), varray());
- bind_method(PackedStringArray, resize, sarray("new_size"), varray());
+ bind_methodv(PackedStringArray, resize, &PackedStringArray::resize_zeroed, sarray("new_size"), varray());
bind_method(PackedStringArray, clear, sarray(), varray());
bind_method(PackedStringArray, has, sarray("value"), varray());
bind_method(PackedStringArray, reverse, sarray(), varray());
@@ -2426,7 +2427,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector2Array, remove_at, sarray("index"), varray());
bind_method(PackedVector2Array, insert, sarray("at_index", "value"), varray());
bind_method(PackedVector2Array, fill, sarray("value"), varray());
- bind_method(PackedVector2Array, resize, sarray("new_size"), varray());
+ bind_methodv(PackedVector2Array, resize, &PackedVector2Array::resize_zeroed, sarray("new_size"), varray());
bind_method(PackedVector2Array, clear, sarray(), varray());
bind_method(PackedVector2Array, has, sarray("value"), varray());
bind_method(PackedVector2Array, reverse, sarray(), varray());
@@ -2450,7 +2451,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedVector3Array, remove_at, sarray("index"), varray());
bind_method(PackedVector3Array, insert, sarray("at_index", "value"), varray());
bind_method(PackedVector3Array, fill, sarray("value"), varray());
- bind_method(PackedVector3Array, resize, sarray("new_size"), varray());
+ bind_methodv(PackedVector3Array, resize, &PackedVector3Array::resize_zeroed, sarray("new_size"), varray());
bind_method(PackedVector3Array, clear, sarray(), varray());
bind_method(PackedVector3Array, has, sarray("value"), varray());
bind_method(PackedVector3Array, reverse, sarray(), varray());
@@ -2474,7 +2475,7 @@ static void _register_variant_builtin_methods() {
bind_method(PackedColorArray, remove_at, sarray("index"), varray());
bind_method(PackedColorArray, insert, sarray("at_index", "value"), varray());
bind_method(PackedColorArray, fill, sarray("value"), varray());
- bind_method(PackedColorArray, resize, sarray("new_size"), varray());
+ bind_methodv(PackedColorArray, resize, &PackedColorArray::resize_zeroed, sarray("new_size"), varray());
bind_method(PackedColorArray, clear, sarray(), varray());
bind_method(PackedColorArray, has, sarray("value"), varray());
bind_method(PackedColorArray, reverse, sarray(), varray());
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 21e1d21342..0d55ee4ae2 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -58,7 +58,13 @@ public:
init_basis(v);
break;
case Variant::TRANSFORM3D:
- init_transform(v);
+ init_transform3d(v);
+ break;
+ case Variant::PROJECTION:
+ init_projection(v);
+ break;
+ case Variant::COLOR:
+ init_color(v);
break;
case Variant::STRING_NAME:
init_string_name(v);
@@ -209,13 +215,12 @@ public:
// Should be in the same order as Variant::Type for consistency.
// Those primitive and vector types don't need an `init_` method:
- // Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, Color, RID.
+ // Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, RID.
// Object is a special case, handled via `object_assign_null`.
_FORCE_INLINE_ static void init_string(Variant *v) {
memnew_placement(v->_data._mem, String);
v->type = Variant::STRING;
}
-
_FORCE_INLINE_ static void init_transform2d(Variant *v) {
v->_data._transform2d = (Transform2D *)Variant::Pools::_bucket_small.alloc();
memnew_placement(v->_data._transform2d, Transform2D);
@@ -231,7 +236,7 @@ public:
memnew_placement(v->_data._basis, Basis);
v->type = Variant::BASIS;
}
- _FORCE_INLINE_ static void init_transform(Variant *v) {
+ _FORCE_INLINE_ static void init_transform3d(Variant *v) {
v->_data._transform3d = (Transform3D *)Variant::Pools::_bucket_medium.alloc();
memnew_placement(v->_data._transform3d, Transform3D);
v->type = Variant::TRANSFORM3D;
@@ -241,6 +246,10 @@ public:
memnew_placement(v->_data._projection, Projection);
v->type = Variant::PROJECTION;
}
+ _FORCE_INLINE_ static void init_color(Variant *v) {
+ memnew_placement(v->_data._mem, Color);
+ v->type = Variant::COLOR;
+ }
_FORCE_INLINE_ static void init_string_name(Variant *v) {
memnew_placement(v->_data._mem, StringName);
v->type = Variant::STRING_NAME;
@@ -1191,7 +1200,7 @@ struct VariantInitializer<Basis> {
template <>
struct VariantInitializer<Transform3D> {
- static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform(v); }
+ static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_transform3d(v); }
};
template <>
struct VariantInitializer<Projection> {
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 87874deb8d..a40fcfbd47 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -910,7 +910,6 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
bool at_key = true;
String key;
- Token token2;
bool need_comma = false;
while (true) {
@@ -920,18 +919,18 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
}
if (at_key) {
- Error err = get_token(p_stream, token2, line, r_err_str);
+ Error err = get_token(p_stream, token, line, r_err_str);
if (err != OK) {
return err;
}
- if (token2.type == TK_PARENTHESIS_CLOSE) {
+ if (token.type == TK_PARENTHESIS_CLOSE) {
value = ref.is_valid() ? Variant(ref) : Variant(obj);
return OK;
}
if (need_comma) {
- if (token2.type != TK_COMMA) {
+ if (token.type != TK_COMMA) {
r_err_str = "Expected '}' or ','";
return ERR_PARSE_ERROR;
} else {
@@ -940,31 +939,31 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
}
}
- if (token2.type != TK_STRING) {
+ if (token.type != TK_STRING) {
r_err_str = "Expected property name as string";
return ERR_PARSE_ERROR;
}
- key = token2.value;
+ key = token.value;
- err = get_token(p_stream, token2, line, r_err_str);
+ err = get_token(p_stream, token, line, r_err_str);
if (err != OK) {
return err;
}
- if (token2.type != TK_COLON) {
+ if (token.type != TK_COLON) {
r_err_str = "Expected ':'";
return ERR_PARSE_ERROR;
}
at_key = false;
} else {
- Error err = get_token(p_stream, token2, line, r_err_str);
+ Error err = get_token(p_stream, token, line, r_err_str);
if (err != OK) {
return err;
}
Variant v;
- err = parse_value(token2, v, p_stream, line, r_err_str, p_res_parser);
+ err = parse_value(token, v, p_stream, line, r_err_str, p_res_parser);
if (err) {
return err;
}
@@ -1026,6 +1025,89 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
}
+ } else if (id == "Array") {
+ Error err = OK;
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_BRACKET_OPEN) {
+ r_err_str = "Expected '['";
+ return ERR_PARSE_ERROR;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_IDENTIFIER) {
+ r_err_str = "Expected type identifier";
+ return ERR_PARSE_ERROR;
+ }
+
+ static HashMap<StringName, Variant::Type> builtin_types;
+ if (builtin_types.is_empty()) {
+ for (int i = 1; i < Variant::VARIANT_MAX; i++) {
+ builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i;
+ }
+ }
+
+ Array array = Array();
+ bool got_bracket_token = false;
+ if (builtin_types.has(token.value)) {
+ array.set_typed(builtin_types.get(token.value), StringName(), Variant());
+ } else if (token.value == "Resource" || token.value == "SubResource" || token.value == "ExtResource") {
+ Variant resource;
+ err = parse_value(token, resource, p_stream, line, r_err_str, p_res_parser);
+ if (err) {
+ if (token.value == "Resource" && err == ERR_PARSE_ERROR && r_err_str == "Expected '('" && token.type == TK_BRACKET_CLOSE) {
+ err = OK;
+ r_err_str = String();
+ array.set_typed(Variant::OBJECT, token.value, Variant());
+ got_bracket_token = true;
+ } else {
+ return err;
+ }
+ } else {
+ Ref<Script> script = resource;
+ if (script.is_valid() && script->is_valid()) {
+ array.set_typed(Variant::OBJECT, script->get_instance_base_type(), script);
+ }
+ }
+ } else if (ClassDB::class_exists(token.value)) {
+ array.set_typed(Variant::OBJECT, token.value, Variant());
+ }
+
+ if (!got_bracket_token) {
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_BRACKET_CLOSE) {
+ r_err_str = "Expected ']'";
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_OPEN) {
+ r_err_str = "Expected '('";
+ return ERR_PARSE_ERROR;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_BRACKET_OPEN) {
+ r_err_str = "Expected '['";
+ return ERR_PARSE_ERROR;
+ }
+
+ Array values;
+ err = _parse_array(values, p_stream, line, r_err_str, p_res_parser);
+ if (err) {
+ return err;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_CLOSE) {
+ r_err_str = "Expected ')'";
+ return ERR_PARSE_ERROR;
+ }
+
+ array.assign(values);
+
+ value = array;
} else if (id == "PackedByteArray" || id == "PoolByteArray" || id == "ByteArray") {
Vector<uint8_t> args;
Error err = _parse_construct<uint8_t>(p_stream, args, line, r_err_str);
@@ -1843,6 +1925,38 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} break;
case Variant::ARRAY: {
+ Array array = p_variant;
+ if (array.get_typed_builtin() != Variant::NIL) {
+ p_store_string_func(p_store_string_ud, "Array[");
+
+ Variant::Type builtin_type = (Variant::Type)array.get_typed_builtin();
+ StringName class_name = array.get_typed_class_name();
+ Ref<Script> script = array.get_typed_script();
+
+ if (script.is_valid()) {
+ String resource_text = String();
+ if (p_encode_res_func) {
+ resource_text = p_encode_res_func(p_encode_res_ud, script);
+ }
+ if (resource_text.is_empty() && script->get_path().is_resource_file()) {
+ resource_text = "Resource(\"" + script->get_path() + "\")";
+ }
+
+ if (!resource_text.is_empty()) {
+ p_store_string_func(p_store_string_ud, resource_text);
+ } else {
+ ERR_PRINT("Failed to encode a path to a custom script for an array type.");
+ p_store_string_func(p_store_string_ud, class_name);
+ }
+ } else if (class_name != StringName()) {
+ p_store_string_func(p_store_string_ud, class_name);
+ } else {
+ p_store_string_func(p_store_string_ud, Variant::get_type_name(builtin_type));
+ }
+
+ p_store_string_func(p_store_string_ud, "](");
+ }
+
if (recursion_count > MAX_RECURSION) {
ERR_PRINT("Max recursion reached");
p_store_string_func(p_store_string_ud, "[]");
@@ -1850,7 +1964,6 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
recursion_count++;
p_store_string_func(p_store_string_ud, "[");
- Array array = p_variant;
int len = array.size();
for (int i = 0; i < len; i++) {
if (i > 0) {
@@ -1862,11 +1975,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_store_string_func(p_store_string_ud, "]");
}
+ if (array.get_typed_builtin() != Variant::NIL) {
+ p_store_string_func(p_store_string_ud, ")");
+ }
+
} break;
case Variant::PACKED_BYTE_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedByteArray(");
- String s;
Vector<uint8_t> data = p_variant;
int len = data.size();
const uint8_t *ptr = data.ptr();
@@ -1954,15 +2070,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
int len = data.size();
const String *ptr = data.ptr();
- String s;
- //write_string("\n");
-
for (int i = 0; i < len; i++) {
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
- String str = ptr[i];
- p_store_string_func(p_store_string_ud, "\"" + str.c_escape() + "\"");
+ p_store_string_func(p_store_string_ud, "\"" + ptr[i].c_escape() + "\"");
}
p_store_string_func(p_store_string_ud, ")");
@@ -2010,9 +2122,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
if (i > 0) {
p_store_string_func(p_store_string_ud, ", ");
}
-
p_store_string_func(p_store_string_ud, rtos_fix(ptr[i].r) + ", " + rtos_fix(ptr[i].g) + ", " + rtos_fix(ptr[i].b) + ", " + rtos_fix(ptr[i].a));
}
+
p_store_string_func(p_store_string_ud, ")");
} break;
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 9be5e60f4a..213a2254af 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -59,14 +59,14 @@
<param index="2" name="class_name" type="StringName" />
<param index="3" name="script" type="Variant" />
<description>
- Creates a typed array from the [param base] array. The base array can't be already typed.
+ Creates a typed array from the [param base] array.
</description>
</constructor>
<constructor name="Array">
<return type="Array" />
<param index="0" name="from" type="Array" />
<description>
- Constructs an [Array] as a copy of the given [Array].
+ Returns the same array as [param from]. If you need a copy of the array, use [method duplicate].
</description>
</constructor>
<constructor name="Array">
@@ -200,6 +200,13 @@
[/codeblock]
</description>
</method>
+ <method name="assign">
+ <return type="void" />
+ <param index="0" name="array" type="Array" />
+ <description>
+ Assigns elements of another [param array] into the array. Resizes the array to match [param array]. Performs type conversions if the array is typed.
+ </description>
+ </method>
<method name="back" qualifiers="const">
<return type="Variant" />
<description>
@@ -395,6 +402,13 @@
Returns [code]true[/code] if the array is read-only. See [method make_read_only]. Arrays are automatically read-only if declared with [code]const[/code] keyword.
</description>
</method>
+ <method name="is_same_typed" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="array" type="Array" />
+ <description>
+ Returns [code]true[/code] if the array is typed the same as [param array].
+ </description>
+ </method>
<method name="is_typed" qualifiers="const">
<return type="bool" />
<description>
@@ -609,13 +623,6 @@
[/codeblocks]
</description>
</method>
- <method name="typed_assign">
- <return type="bool" />
- <param index="0" name="array" type="Array" />
- <description>
- Assigns a different [Array] to this array reference. It the array is typed, the new array's type must be compatible and its elements will be automatically converted.
- </description>
- </method>
</methods>
<operators>
<operator name="operator !=">
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml
index 776a9f6fc1..1591aa59bf 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -153,7 +153,7 @@
<return type="Dictionary" />
<param index="0" name="from" type="Dictionary" />
<description>
- Returns the same array as [param from]. If you need a copy of the array, use [method duplicate].
+ Returns the same dictionary as [param from]. If you need a copy of the dictionary, use [method duplicate].
</description>
</constructor>
</constructors>
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 5bdef32c60..d71ef78d88 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -461,7 +461,7 @@ void DocTools::generate(bool p_basic_types) {
}
if (default_value_valid && default_value.get_type() != Variant::OBJECT) {
- prop.default_value = default_value.get_construct_string().replace("\n", " ");
+ prop.default_value = DocData::get_default_value_string(default_value);
}
StringName setter = ClassDB::get_property_setter(name, E.name);
@@ -591,7 +591,7 @@ void DocTools::generate(bool p_basic_types) {
tid.name = E;
tid.type = "Color";
tid.data_type = "color";
- tid.default_value = Variant(ThemeDB::get_singleton()->get_default_theme()->get_color(E, cname)).get_construct_string().replace("\n", " ");
+ tid.default_value = DocData::get_default_value_string(ThemeDB::get_singleton()->get_default_theme()->get_color(E, cname));
c.theme_properties.push_back(tid);
}
@@ -772,8 +772,7 @@ void DocTools::generate(bool p_basic_types) {
int darg_idx = mi.default_arguments.size() - mi.arguments.size() + j;
if (darg_idx >= 0) {
- Variant default_arg = mi.default_arguments[darg_idx];
- ad.default_value = default_arg.get_construct_string().replace("\n", " ");
+ ad.default_value = DocData::get_default_value_string(mi.default_arguments[darg_idx]);
}
method.arguments.push_back(ad);
@@ -817,7 +816,7 @@ void DocTools::generate(bool p_basic_types) {
DocData::PropertyDoc property;
property.name = pi.name;
property.type = Variant::get_type_name(pi.type);
- property.default_value = v.get(pi.name).get_construct_string().replace("\n", " ");
+ property.default_value = DocData::get_default_value_string(v.get(pi.name));
c.properties.push_back(property);
}
@@ -948,8 +947,7 @@ void DocTools::generate(bool p_basic_types) {
int darg_idx = j - (mi.arguments.size() - mi.default_arguments.size());
if (darg_idx >= 0) {
- Variant default_arg = mi.default_arguments[darg_idx];
- ad.default_value = default_arg.get_construct_string().replace("\n", " ");
+ ad.default_value = DocData::get_default_value_string(mi.default_arguments[darg_idx]);
}
md.arguments.push_back(ad);
@@ -993,8 +991,7 @@ void DocTools::generate(bool p_basic_types) {
int darg_idx = j - (ai.arguments.size() - ai.default_arguments.size());
if (darg_idx >= 0) {
- Variant default_arg = ai.default_arguments[darg_idx];
- ad.default_value = default_arg.get_construct_string().replace("\n", " ");
+ ad.default_value = DocData::get_default_value_string(ai.default_arguments[darg_idx]);
}
atd.arguments.push_back(ad);
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 28c0b047d8..b96ac9dbcb 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -158,17 +158,32 @@ EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() {
///////////////////// ARRAY ///////////////////////////
+void EditorPropertyArray::initialize_array(Variant &p_array) {
+ if (array_type == Variant::ARRAY && subtype != Variant::NIL) {
+ Array array;
+ StringName subtype_class;
+ Ref<Script> subtype_script;
+ if (subtype == Variant::OBJECT && !subtype_hint_string.is_empty()) {
+ if (ClassDB::class_exists(subtype_hint_string)) {
+ subtype_class = subtype_hint_string;
+ }
+ }
+ array.set_typed(subtype, subtype_class, subtype_script);
+ p_array = array;
+ } else {
+ VariantInternal::initialize(&p_array, array_type);
+ }
+}
+
void EditorPropertyArray::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
if (p_property.begins_with("indices")) {
int index = p_property.get_slice("/", 1).to_int();
- Variant array = object->get_array();
+
+ Variant array = object->get_array().duplicate();
array.set(index, p_value);
- emit_changed(get_edited_property(), array, "", true);
- if (array.get_type() == Variant::ARRAY) {
- array = array.call("duplicate"); // Duplicate, so undo/redo works better.
- }
object->set_array(array);
+ emit_changed(get_edited_property(), array, "", true);
}
}
@@ -188,18 +203,12 @@ void EditorPropertyArray::_change_type_menu(int p_index) {
}
Variant value;
- Callable::CallError ce;
- Variant::construct(Variant::Type(p_index), value, nullptr, 0, ce);
- Variant array = object->get_array();
+ VariantInternal::initialize(&value, Variant::Type(p_index));
+
+ Variant array = object->get_array().duplicate();
array.set(changing_type_index, value);
emit_changed(get_edited_property(), array, "", true);
-
- if (array.get_type() == Variant::ARRAY) {
- array = array.call("duplicate"); // Duplicate, so undo/redo works better.
- }
-
- object->set_array(array);
update_property();
}
@@ -234,6 +243,8 @@ void EditorPropertyArray::update_property() {
return;
}
+ object->set_array(array);
+
int size = array.call("size");
int max_page = MAX(0, size - 1) / page_length;
page_index = MIN(page_index, max_page);
@@ -305,12 +316,6 @@ void EditorPropertyArray::update_property() {
paginator->update(page_index, max_page);
paginator->set_visible(max_page > 0);
- if (array.get_type() == Variant::ARRAY) {
- array = array.call("duplicate");
- }
-
- object->set_array(array);
-
int amount = MIN(size - offset, page_length);
for (int i = 0; i < amount; i++) {
bool reorder_is_from_current_page = reorder_from_index / page_length == page_index;
@@ -401,7 +406,7 @@ void EditorPropertyArray::update_property() {
}
void EditorPropertyArray::_remove_pressed(int p_index) {
- Variant array = object->get_array();
+ Variant array = object->get_array().duplicate();
array.call("remove_at", p_index);
emit_changed(get_edited_property(), array, "", false);
@@ -469,8 +474,9 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d
// Handle the case where array is not initialized yet.
if (!array.is_array()) {
- Callable::CallError ce;
- Variant::construct(array_type, array, nullptr, 0, ce);
+ initialize_array(array);
+ } else {
+ array = array.duplicate();
}
// Loop the file array and add to existing array.
@@ -483,13 +489,7 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d
}
}
- if (array.get_type() == Variant::ARRAY) {
- array = array.call("duplicate");
- }
-
emit_changed(get_edited_property(), array, "", false);
- object->set_array(array);
-
update_property();
}
}
@@ -536,10 +536,8 @@ void EditorPropertyArray::_notification(int p_what) {
void EditorPropertyArray::_edit_pressed() {
Variant array = get_edited_object()->get(get_edited_property());
- if (!array.is_array()) {
- Callable::CallError ce;
- Variant::construct(array_type, array, nullptr, 0, ce);
-
+ if (!array.is_array() && edit->is_pressed()) {
+ initialize_array(array);
get_edited_object()->set(get_edited_property(), array);
}
@@ -560,37 +558,10 @@ void EditorPropertyArray::_length_changed(double p_page) {
return;
}
- Variant array = object->get_array();
- int previous_size = array.call("size");
-
+ Variant array = object->get_array().duplicate();
array.call("resize", int(p_page));
- if (array.get_type() == Variant::ARRAY) {
- if (subtype != Variant::NIL) {
- int size = array.call("size");
- for (int i = previous_size; i < size; i++) {
- if (array.get(i).get_type() == Variant::NIL) {
- Callable::CallError ce;
- Variant r;
- Variant::construct(subtype, r, nullptr, 0, ce);
- array.set(i, r);
- }
- }
- }
- array = array.call("duplicate"); // Duplicate, so undo/redo works better.
- } else {
- int size = array.call("size");
- // Pool*Array don't initialize their elements, have to do it manually.
- for (int i = previous_size; i < size; i++) {
- Callable::CallError ce;
- Variant r;
- Variant::construct(array.get(i).get_type(), r, nullptr, 0, ce);
- array.set(i, r);
- }
- }
-
emit_changed(get_edited_property(), array, "", false);
- object->set_array(array);
update_property();
}
@@ -677,14 +648,13 @@ void EditorPropertyArray::_reorder_button_up() {
if (reorder_from_index != reorder_to_index) {
// Move the element.
- Variant array = object->get_array();
+ Variant array = object->get_array().duplicate();
Variant value_to_move = array.get(reorder_from_index);
array.call("remove_at", reorder_from_index);
array.call("insert", reorder_to_index, value_to_move);
emit_changed(get_edited_property(), array, "", false);
- object->set_array(array);
update_property();
}
@@ -742,14 +712,13 @@ void EditorPropertyDictionary::_property_changed(const String &p_property, Varia
object->set_new_item_value(p_value);
} else if (p_property.begins_with("indices")) {
int index = p_property.get_slice("/", 1).to_int();
- Dictionary dict = object->get_dict();
+
+ Dictionary dict = object->get_dict().duplicate();
Variant key = dict.get_key_at_index(index);
dict[key] = p_value;
- emit_changed(get_edited_property(), dict, "", true);
-
- dict = dict.duplicate(); // Duplicate, so undo/redo works better.
object->set_dict(dict);
+ emit_changed(get_edited_property(), dict, "", true);
}
}
@@ -769,24 +738,19 @@ void EditorPropertyDictionary::_add_key_value() {
return;
}
- Dictionary dict = object->get_dict();
-
+ Dictionary dict = object->get_dict().duplicate();
dict[object->get_new_item_key()] = object->get_new_item_value();
object->set_new_item_key(Variant());
object->set_new_item_value(Variant());
emit_changed(get_edited_property(), dict, "", false);
-
- dict = dict.duplicate(); // Duplicate, so undo/redo works better.
- object->set_dict(dict);
update_property();
}
void EditorPropertyDictionary::_change_type_menu(int p_index) {
if (changing_type_index < 0) {
Variant value;
- Callable::CallError ce;
- Variant::construct(Variant::Type(p_index), value, nullptr, 0, ce);
+ VariantInternal::initialize(&value, Variant::Type(p_index));
if (changing_type_index == -1) {
object->set_new_item_key(value);
} else {
@@ -796,12 +760,10 @@ void EditorPropertyDictionary::_change_type_menu(int p_index) {
return;
}
- Dictionary dict = object->get_dict();
-
+ Dictionary dict = object->get_dict().duplicate();
if (p_index < Variant::VARIANT_MAX) {
Variant value;
- Callable::CallError ce;
- Variant::construct(Variant::Type(p_index), value, nullptr, 0, ce);
+ VariantInternal::initialize(&value, Variant::Type(p_index));
Variant key = dict.get_key_at_index(changing_type_index);
dict[key] = value;
} else {
@@ -810,9 +772,6 @@ void EditorPropertyDictionary::_change_type_menu(int p_index) {
}
emit_changed(get_edited_property(), dict, "", false);
-
- dict = dict.duplicate(); // Duplicate, so undo/redo works better.
- object->set_dict(dict);
update_property();
}
@@ -836,6 +795,7 @@ void EditorPropertyDictionary::update_property() {
}
Dictionary dict = updated_val;
+ object->set_dict(updated_val);
edit->set_text(vformat(TTR("Dictionary (size %d)"), dict.size()));
@@ -883,9 +843,6 @@ void EditorPropertyDictionary::update_property() {
int amount = MIN(size - offset, page_length);
int total_amount = page_index == max_page ? amount + 2 : amount; // For the "Add Key/Value Pair" box on last page.
- dict = dict.duplicate();
-
- object->set_dict(dict);
VBoxContainer *add_vbox = nullptr;
double default_float_step = EDITOR_GET("interface/inspector/default_float_step");
@@ -1225,9 +1182,8 @@ void EditorPropertyDictionary::_notification(int p_what) {
void EditorPropertyDictionary::_edit_pressed() {
Variant prop_val = get_edited_object()->get(get_edited_property());
- if (prop_val.get_type() == Variant::NIL) {
- Callable::CallError ce;
- Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce);
+ if (prop_val.get_type() == Variant::NIL && edit->is_pressed()) {
+ VariantInternal::initialize(&prop_val, Variant::DICTIONARY);
get_edited_object()->set(get_edited_property(), prop_val);
}
@@ -1272,14 +1228,13 @@ EditorPropertyDictionary::EditorPropertyDictionary() {
void EditorPropertyLocalizableString::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
if (p_property.begins_with("indices")) {
int index = p_property.get_slice("/", 1).to_int();
- Dictionary dict = object->get_dict();
+
+ Dictionary dict = object->get_dict().duplicate();
Variant key = dict.get_key_at_index(index);
dict[key] = p_value;
- emit_changed(get_edited_property(), dict, "", true);
-
- dict = dict.duplicate(); // Duplicate, so undo/redo works better.
object->set_dict(dict);
+ emit_changed(get_edited_property(), dict, "", true);
}
}
@@ -1288,29 +1243,22 @@ void EditorPropertyLocalizableString::_add_locale_popup() {
}
void EditorPropertyLocalizableString::_add_locale(const String &p_locale) {
- Dictionary dict = object->get_dict();
-
+ Dictionary dict = object->get_dict().duplicate();
object->set_new_item_key(p_locale);
object->set_new_item_value(String());
dict[object->get_new_item_key()] = object->get_new_item_value();
emit_changed(get_edited_property(), dict, "", false);
-
- dict = dict.duplicate(); // Duplicate, so undo/redo works better.
- object->set_dict(dict);
update_property();
}
void EditorPropertyLocalizableString::_remove_item(Object *p_button, int p_index) {
- Dictionary dict = object->get_dict();
+ Dictionary dict = object->get_dict().duplicate();
Variant key = dict.get_key_at_index(p_index);
dict.erase(key);
emit_changed(get_edited_property(), dict, "", false);
-
- dict = dict.duplicate(); // Duplicate, so undo/redo works better.
- object->set_dict(dict);
update_property();
}
@@ -1330,6 +1278,7 @@ void EditorPropertyLocalizableString::update_property() {
}
Dictionary dict = updated_val;
+ object->set_dict(dict);
edit->set_text(vformat(TTR("Localizable String (size %d)"), dict.size()));
@@ -1376,10 +1325,6 @@ void EditorPropertyLocalizableString::update_property() {
int amount = MIN(size - offset, page_length);
- dict = dict.duplicate();
-
- object->set_dict(dict);
-
for (int i = 0; i < amount; i++) {
String prop_name;
Variant key;
@@ -1451,9 +1396,8 @@ void EditorPropertyLocalizableString::_notification(int p_what) {
void EditorPropertyLocalizableString::_edit_pressed() {
Variant prop_val = get_edited_object()->get(get_edited_property());
- if (prop_val.get_type() == Variant::NIL) {
- Callable::CallError ce;
- Variant::construct(Variant::DICTIONARY, prop_val, nullptr, 0, ce);
+ if (prop_val.get_type() == Variant::NIL && edit->is_pressed()) {
+ VariantInternal::initialize(&prop_val, Variant::DICTIONARY);
get_edited_object()->set(get_edited_property(), prop_val);
}
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index 73a16e3687..3b880c60a8 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -102,6 +102,8 @@ class EditorPropertyArray : public EditorProperty {
HBoxContainer *reorder_selected_element_hbox = nullptr;
Button *reorder_selected_button = nullptr;
+ void initialize_array(Variant &p_array);
+
void _page_changed(int p_page);
void _reorder_button_gui_input(const Ref<InputEvent> &p_event);
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index d9b8a540c0..a8667e9cec 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -706,11 +706,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
}
members_cache.push_back(member.variable->export_info);
- Variant default_value;
- if (member.variable->initializer && member.variable->initializer->is_constant) {
- default_value = member.variable->initializer->reduced_value;
- GDScriptCompiler::convert_to_initializer_type(default_value, member.variable);
- }
+ Variant default_value = analyzer.make_variable_default_value(member.variable);
member_default_values_cache[member.variable->identifier->name] = default_value;
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
@@ -1525,41 +1521,24 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name);
if (E) {
const GDScript::MemberInfo *member = &E->value;
- if (member->setter) {
- const Variant *val = &p_value;
+ Variant value = p_value;
+ if (member->data_type.has_type && !member->data_type.is_type(value)) {
+ const Variant *args = &p_value;
Callable::CallError err;
- callp(member->setter, &val, 1, err);
- if (err.error == Callable::CallError::CALL_OK) {
- return true; //function exists, call was successful
- } else {
+ Variant::construct(member->data_type.builtin_type, value, &args, 1, err);
+ if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) {
return false;
}
+ }
+ if (member->setter) {
+ const Variant *args = &value;
+ Callable::CallError err;
+ callp(member->setter, &args, 1, err);
+ return err.error == Callable::CallError::CALL_OK;
} else {
- 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;
- }
- }
- }
- members.write[member->index] = p_value;
+ members.write[member->index] = value;
+ return true;
}
- return true;
}
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 8dd65a700a..f788236af3 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -580,6 +580,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
if (result.builtin_type == Variant::ARRAY) {
GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->container_type));
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
+ container_type.is_constant = false;
result.set_container_element_type(container_type);
}
}
@@ -1571,18 +1572,18 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer);
- if ((p_assignable->infer_datatype && array->elements.size() > 0) || (has_specified_type && specified_type.has_container_element_type())) {
- update_array_literal_element_type(specified_type, array);
+ if (has_specified_type && specified_type.has_container_element_type()) {
+ update_array_literal_element_type(array, specified_type.get_container_element_type());
}
}
- if (is_constant) {
- if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer), true);
- } else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer), true);
- }
- if (!p_assignable->initializer->is_constant) {
+ if (is_constant && !p_assignable->initializer->is_constant) {
+ bool is_initializer_value_reduced = false;
+ Variant initializer_value = make_expression_reduced_value(p_assignable->initializer, is_initializer_value_reduced);
+ if (is_initializer_value_reduced) {
+ p_assignable->initializer->is_constant = true;
+ p_assignable->initializer->reduced_value = initializer_value;
+ } else {
push_error(vformat(R"(Assigned value for %s "%s" isn't a constant expression.)", p_kind, p_assignable->identifier->name), p_assignable->initializer);
}
}
@@ -1630,6 +1631,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
} else {
push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
}
+ } else if (specified_type.has_container_element_type() && !initializer_type.has_container_element_type()) {
+ mark_node_unsafe(p_assignable->initializer);
#ifdef DEBUG_ENABLED
} else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignable->initializer, GDScriptWarning::NARROWING_CONVERSION);
@@ -1970,11 +1973,8 @@ 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 (has_expected_type && expected_type.has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) {
- update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
- }
+ if (p_return->return_value->type == GDScriptParser::Node::ARRAY && has_expected_type && expected_type.has_container_element_type()) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_return->return_value), expected_type.get_container_element_type());
}
if (has_expected_type && expected_type.is_hard_type() && expected_type.kind == GDScriptParser::DataType::BUILTIN && expected_type.builtin_type == Variant::NIL) {
push_error("A void function cannot return a value.", p_return);
@@ -2183,49 +2183,26 @@ void GDScriptAnalyzer::update_const_expression_builtin_type(GDScriptParser::Expr
// 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;
- }
- }
- }
- }
+void GDScriptAnalyzer::update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type) {
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ GDScriptParser::ExpressionNode *element_node = p_array->elements[i];
+ if (element_node->is_constant) {
+ update_const_expression_builtin_type(element_node, p_element_type, "include");
+ }
+ const GDScriptParser::DataType &element_type = element_node->get_datatype();
+ if (element_type.has_no_type() || element_type.is_variant() || !element_type.is_hard_type()) {
+ mark_node_unsafe(element_node);
+ continue;
}
- if (all_same_type) {
- element_type.is_constant = false;
- 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);
+ if (!is_type_compatible(p_element_type, element_type, true, p_array)) {
+ push_error(vformat(R"(Cannot have an element of type "%s" in an array of type "Array[%s]".)", element_type.to_string(), p_element_type.to_string()), element_node);
+ return;
}
}
- // Update the type on the value itself.
- p_array_literal->set_datatype(array_type);
+
+ GDScriptParser::DataType array_type = p_array->get_datatype();
+ array_type.set_container_element_type(p_element_type);
+ p_array->set_datatype(array_type);
}
void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) {
@@ -2243,8 +2220,8 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
// 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));
+ if (p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY && assignee_type.has_container_element_type()) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value), assignee_type.get_container_element_type());
}
if (p_assignment->operation == GDScriptParser::AssignmentNode::OP_NONE && assignee_type.is_hard_type() && p_assignment->assigned_value->is_constant) {
@@ -2322,6 +2299,9 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
// weak non-variant assignee and incompatible result
downgrades_assignee = true;
}
+ } else if (assignee_type.has_container_element_type() && !op_type.has_container_element_type()) {
+ // typed array assignee and untyped array result
+ mark_node_unsafe(p_assignment);
}
}
}
@@ -2822,7 +2802,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
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.value);
+ update_array_literal_element_type(E.value, par_types[index].get_container_element_type());
}
}
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
@@ -2933,6 +2913,10 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
}
}
+ if (p_cast->operand->type == GDScriptParser::Node::ARRAY && cast_type.has_container_element_type()) {
+ update_array_literal_element_type(static_cast<GDScriptParser::ArrayNode *>(p_cast->operand), cast_type.get_container_element_type());
+ }
+
if (!cast_type.is_variant()) {
GDScriptParser::DataType op_type = p_cast->operand->get_datatype();
if (op_type.is_variant() || !op_type.is_hard_type()) {
@@ -3038,10 +3022,12 @@ void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::Ide
p_identifier->set_datatype(p_identifier_datatype);
Error err = OK;
- GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr();
- ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path));
- scr = scr->find_class(p_identifier_datatype.class_type->fqcn);
- p_identifier->reduced_value = scr;
+ Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err);
+ if (err) {
+ push_error(vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path), p_identifier);
+ return;
+ }
+ p_identifier->reduced_value = scr->find_class(p_identifier_datatype.class_type->fqcn);
p_identifier->is_constant = true;
}
@@ -3585,12 +3571,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true);
} else {
reduce_expression(p_subscript->base);
-
- if (p_subscript->base->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base), false);
- } else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base), false);
- }
}
GDScriptParser::DataType result_type;
@@ -3915,58 +3895,146 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
p_unary_op->set_datatype(result);
}
-void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const) {
+Variant GDScriptAnalyzer::make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced) {
+ Variant value;
+
+ if (p_expression->is_constant) {
+ is_reduced = true;
+ value = p_expression->reduced_value;
+ } else if (p_expression->type == GDScriptParser::Node::ARRAY) {
+ value = make_array_reduced_value(static_cast<GDScriptParser::ArrayNode *>(p_expression), is_reduced);
+ } else if (p_expression->type == GDScriptParser::Node::DICTIONARY) {
+ value = make_dictionary_reduced_value(static_cast<GDScriptParser::DictionaryNode *>(p_expression), is_reduced);
+ } else if (p_expression->type == GDScriptParser::Node::SUBSCRIPT) {
+ value = make_subscript_reduced_value(static_cast<GDScriptParser::SubscriptNode *>(p_expression), is_reduced);
+ }
+
+ return value;
+}
+
+Variant GDScriptAnalyzer::make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced) {
+ Array array = p_array->get_datatype().has_container_element_type() ? make_array_from_element_datatype(p_array->get_datatype().get_container_element_type()) : Array();
+
+ array.resize(p_array->elements.size());
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
- if (element->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element), p_is_const);
- } else if (element->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element), p_is_const);
+ bool is_element_value_reduced = false;
+ Variant element_value = make_expression_reduced_value(element, is_element_value_reduced);
+ if (!is_element_value_reduced) {
+ return Variant();
}
- if (!element->is_constant) {
- return;
- }
+ array[i] = element_value;
}
- Array array;
- array.resize(p_array->elements.size());
- for (int i = 0; i < p_array->elements.size(); i++) {
- array[i] = p_array->elements[i]->reduced_value;
- }
- if (p_is_const) {
- array.make_read_only();
- }
- p_array->is_constant = true;
- p_array->reduced_value = array;
+ array.make_read_only();
+
+ is_reduced = true;
+ return array;
}
-void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const) {
+Variant GDScriptAnalyzer::make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced) {
+ Dictionary dictionary;
+
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
- if (element.value->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value), p_is_const);
- } else if (element.value->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value), p_is_const);
+ bool is_element_key_reduced = false;
+ Variant element_key = make_expression_reduced_value(element.key, is_element_key_reduced);
+ if (!is_element_key_reduced) {
+ return Variant();
}
- if (!element.key->is_constant || !element.value->is_constant) {
- return;
+ bool is_element_value_reduced = false;
+ Variant element_value = make_expression_reduced_value(element.value, is_element_value_reduced);
+ if (!is_element_value_reduced) {
+ return Variant();
}
+
+ dictionary[element_key] = element_value;
}
- Dictionary dict;
- for (int i = 0; i < p_dictionary->elements.size(); i++) {
- const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
- dict[element.key->reduced_value] = element.value->reduced_value;
+ dictionary.make_read_only();
+
+ is_reduced = true;
+ return dictionary;
+}
+
+Variant GDScriptAnalyzer::make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced) {
+ bool is_base_value_reduced = false;
+ Variant base_value = make_expression_reduced_value(p_subscript->base, is_base_value_reduced);
+ if (!is_base_value_reduced) {
+ return Variant();
+ }
+
+ if (p_subscript->is_attribute) {
+ bool is_valid = false;
+ Variant value = base_value.get_named(p_subscript->attribute->name, is_valid);
+ if (is_valid) {
+ is_reduced = true;
+ return value;
+ } else {
+ return Variant();
+ }
+ } else {
+ bool is_index_value_reduced = false;
+ Variant index_value = make_expression_reduced_value(p_subscript->index, is_index_value_reduced);
+ if (!is_index_value_reduced) {
+ return Variant();
+ }
+
+ bool is_valid = false;
+ Variant value = base_value.get(index_value, &is_valid);
+ if (is_valid) {
+ is_reduced = true;
+ return value;
+ } else {
+ return Variant();
+ }
+ }
+}
+
+Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node) {
+ Array array;
+
+ Ref<Script> script_type = p_element_datatype.script_type;
+ if (p_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
+ Error err = OK;
+ Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err);
+ if (err) {
+ push_error(vformat(R"(Error while getting cache for script "%s".)", p_element_datatype.script_path), p_source_node);
+ return array;
+ }
+ script_type.reference_ptr(scr->find_class(p_element_datatype.class_type->fqcn));
}
- if (p_is_const) {
- dict.make_read_only();
+
+ array.set_typed(p_element_datatype.builtin_type, p_element_datatype.native_type, script_type);
+
+ return array;
+}
+
+Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNode *p_variable) {
+ Variant result = Variant();
+
+ if (p_variable->initializer) {
+ bool is_initializer_value_reduced = false;
+ Variant initializer_value = make_expression_reduced_value(p_variable->initializer, is_initializer_value_reduced);
+ if (is_initializer_value_reduced) {
+ result = initializer_value;
+ }
+ } else {
+ GDScriptParser::DataType datatype = p_variable->get_datatype();
+ if (datatype.is_hard_type() && datatype.kind == GDScriptParser::DataType::BUILTIN && datatype.builtin_type != Variant::OBJECT) {
+ if (datatype.builtin_type == Variant::ARRAY && datatype.has_container_element_type()) {
+ result = make_array_from_element_datatype(datatype.get_container_element_type());
+ } else {
+ VariantInternal::initialize(&result, datatype.builtin_type);
+ }
+ }
}
- p_dictionary->is_constant = true;
- p_dictionary->reduced_value = dict;
+
+ return result;
}
GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
@@ -4437,14 +4505,8 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
}
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(), p_allow_implicit_conversion);
- }
+ if (p_target.has_container_element_type() && p_source.has_container_element_type()) {
+ valid = p_target.get_container_element_type() == p_source.get_container_element_type();
}
}
return valid;
@@ -4546,7 +4608,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
return ClassDB::is_parent_class(src_native, GDScript::get_class_static());
}
while (src_class != nullptr) {
- if (src_class->fqcn == p_target.class_type->fqcn) {
+ if (src_class == p_target.class_type || src_class->fqcn == p_target.class_type->fqcn) {
return true;
}
src_class = src_class->base_type.class_type;
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index cd2c4c6569..613399fb40 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -102,10 +102,13 @@ class GDScriptAnalyzer {
void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root = false);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
- void const_fold_array(GDScriptParser::ArrayNode *p_array, bool p_is_const);
- void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary, bool p_is_const);
+ Variant make_expression_reduced_value(GDScriptParser::ExpressionNode *p_expression, bool &is_reduced);
+ Variant make_array_reduced_value(GDScriptParser::ArrayNode *p_array, bool &is_reduced);
+ Variant make_dictionary_reduced_value(GDScriptParser::DictionaryNode *p_dictionary, bool &is_reduced);
+ Variant make_subscript_reduced_value(GDScriptParser::SubscriptNode *p_subscript, bool &is_reduced);
// Helpers.
+ Array make_array_from_element_datatype(const GDScriptParser::DataType &p_element_datatype, const GDScriptParser::Node *p_source_node = nullptr);
GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
static GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type);
GDScriptParser::DataType type_from_property(const PropertyInfo &p_property, bool p_is_arg = false) const;
@@ -117,7 +120,7 @@ class GDScriptAnalyzer {
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_const_expression_builtin_type(GDScriptParser::ExpressionNode *p_expression, const GDScriptParser::DataType &p_type, const char *p_usage, bool p_is_cast = false);
- void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
+ void update_array_literal_element_type(GDScriptParser::ArrayNode *p_array, const GDScriptParser::DataType &p_element_type);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
@@ -125,7 +128,7 @@ class GDScriptAnalyzer {
void mark_lambda_use_self();
bool class_exists(const StringName &p_class) const;
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
- static void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype);
+ void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype);
#ifdef DEBUG_ENABLED
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
#endif
@@ -137,6 +140,8 @@ public:
Error resolve_dependencies();
Error analyze();
+ Variant make_variable_default_value(GDScriptParser::VariableNode *p_variable);
+
GDScriptAnalyzer(GDScriptParser *p_parser);
};
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index e19dda090e..ec7a2b0f1c 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -826,9 +826,13 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
switch (p_target.type.kind) {
case GDScriptDataType::BUILTIN: {
if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+ const GDScriptDataType &element_type = p_target.type.get_container_element_type();
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_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_ASSIGN_TYPED_BUILTIN);
append(p_target);
@@ -868,9 +872,13 @@ void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_ta
void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+ const GDScriptDataType &element_type = p_target.type.get_container_element_type();
append_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_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 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_opcode(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
@@ -1326,14 +1334,7 @@ void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_tar
append(p_arguments[i]);
}
append(get_call_target(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_CONSTANT << GDScriptFunction::ADDR_BITS;
- append(addr);
- } else {
- append(Address()); // null.
- }
+ append(get_constant_pos(p_element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
append(p_arguments.size());
append(p_element_type.builtin_type);
append(p_element_type.native_type);
@@ -1608,14 +1609,10 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) {
// Typed array.
const GDScriptDataType &element_type = function->return_type.get_container_element_type();
-
- Variant script = element_type.script_type;
- int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
-
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
- append(script_idx);
- append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT);
+ append(get_constant_pos(element_type.script_type) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS));
+ append(element_type.builtin_type);
append(element_type.native_type);
} else if (function->return_type.kind == GDScriptDataType::BUILTIN && p_return_value.type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type != p_return_value.type.builtin_type) {
// Add conversion.
@@ -1636,15 +1633,10 @@ void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
case GDScriptDataType::BUILTIN: {
if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) {
const GDScriptDataType &element_type = function->return_type.get_container_element_type();
-
- Variant script = function->return_type.script_type;
- int script_idx = get_constant_pos(script);
- script_idx |= (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
-
append_opcode(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY);
append(p_return_value);
- append(script_idx);
- append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT);
+ 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_RETURN_TYPED_BUILTIN);
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 6acdc9f212..373d8a3efd 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1904,14 +1904,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
bool initialized = false;
if (lv->initializer != nullptr) {
- // For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
- if (local_type.has_type && local_type.builtin_type == Variant::ARRAY) {
- if (local_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
- } else {
- codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>());
- }
- }
GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, err, lv->initializer);
if (err) {
return err;
@@ -2052,14 +2044,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
// 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.has_type && field_type.builtin_type == Variant::ARRAY) {
- if (field_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
- } else {
- codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
- }
- }
GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true);
if (r_error) {
memdelete(codegen.generator);
@@ -2100,17 +2084,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
return nullptr;
}
GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
-
- // For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
- GDScriptDataType par_type = dst_addr.type;
- if (par_type.has_type && par_type.builtin_type == Variant::ARRAY) {
- if (par_type.has_container_element_type()) {
- codegen.generator->write_construct_typed_array(dst_addr, par_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
- } else {
- codegen.generator->write_construct_array(dst_addr, Vector<GDScriptCodeGenerator::Address>());
- }
- }
-
codegen.generator->write_assign_default_parameter(dst_addr, src_addr, parameter->use_conversion_assign);
if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 66374d0a6d..bb08b017c9 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -51,46 +51,8 @@
static HashMap<StringName, Variant::Type> builtin_types;
Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
if (builtin_types.is_empty()) {
- builtin_types["bool"] = Variant::BOOL;
- builtin_types["int"] = Variant::INT;
- builtin_types["float"] = Variant::FLOAT;
- builtin_types["String"] = Variant::STRING;
- builtin_types["Vector2"] = Variant::VECTOR2;
- builtin_types["Vector2i"] = Variant::VECTOR2I;
- builtin_types["Rect2"] = Variant::RECT2;
- builtin_types["Rect2i"] = Variant::RECT2I;
- builtin_types["Transform2D"] = Variant::TRANSFORM2D;
- builtin_types["Vector3"] = Variant::VECTOR3;
- builtin_types["Vector3i"] = Variant::VECTOR3I;
- builtin_types["Vector4"] = Variant::VECTOR4;
- builtin_types["Vector4i"] = Variant::VECTOR4I;
- builtin_types["AABB"] = Variant::AABB;
- builtin_types["Plane"] = Variant::PLANE;
- builtin_types["Quaternion"] = Variant::QUATERNION;
- builtin_types["Basis"] = Variant::BASIS;
- builtin_types["Transform3D"] = Variant::TRANSFORM3D;
- builtin_types["Projection"] = Variant::PROJECTION;
- builtin_types["Color"] = Variant::COLOR;
- builtin_types["RID"] = Variant::RID;
- builtin_types["Object"] = Variant::OBJECT;
- builtin_types["StringName"] = Variant::STRING_NAME;
- builtin_types["NodePath"] = Variant::NODE_PATH;
- builtin_types["Dictionary"] = Variant::DICTIONARY;
- builtin_types["Callable"] = Variant::CALLABLE;
- builtin_types["Signal"] = Variant::SIGNAL;
- builtin_types["Array"] = Variant::ARRAY;
- builtin_types["PackedByteArray"] = Variant::PACKED_BYTE_ARRAY;
- builtin_types["PackedInt32Array"] = Variant::PACKED_INT32_ARRAY;
- builtin_types["PackedInt64Array"] = Variant::PACKED_INT64_ARRAY;
- builtin_types["PackedFloat32Array"] = Variant::PACKED_FLOAT32_ARRAY;
- builtin_types["PackedFloat64Array"] = Variant::PACKED_FLOAT64_ARRAY;
- builtin_types["PackedStringArray"] = Variant::PACKED_STRING_ARRAY;
- builtin_types["PackedVector2Array"] = Variant::PACKED_VECTOR2_ARRAY;
- builtin_types["PackedVector3Array"] = Variant::PACKED_VECTOR3_ARRAY;
- builtin_types["PackedColorArray"] = Variant::PACKED_COLOR_ARRAY;
- // NIL is not here, hence the -1.
- if (builtin_types.size() != Variant::VARIANT_MAX - 1) {
- ERR_PRINT("Outdated parser: amount of built-in types don't match the amount of types in Variant.");
+ for (int i = 1; i < Variant::VARIANT_MAX; i++) {
+ builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i;
}
}
@@ -982,14 +944,14 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
// Run with a loop because order doesn't matter.
for (int i = 0; i < 2; i++) {
- if (function->name == "set") {
+ if (function->name == SNAME("set")) {
if (setter_used) {
push_error(R"(Properties can only have one setter.)");
} else {
parse_property_setter(property);
setter_used = true;
}
- } else if (function->name == "get") {
+ } else if (function->name == SNAME("get")) {
if (getter_used) {
push_error(R"(Properties can only have one getter.)");
} else {
@@ -2921,7 +2883,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
// Arguments.
CompletionType ct = COMPLETION_CALL_ARGUMENTS;
- if (call->function_name == "load") {
+ if (call->function_name == SNAME("load")) {
ct = COMPLETION_RESOURCE_PATH;
}
push_completion_call(call);
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 74e12d0b5e..bb4549c15c 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -190,7 +190,7 @@ public:
case SCRIPT:
return script_type == p_other.script_type;
case CLASS:
- return class_type == p_other.class_type;
+ return class_type == p_other.class_type || class_type->fqcn == p_other.class_type->fqcn;
case RESOLVING:
case UNRESOLVED:
break;
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 4ea4438b5e..e18a4a6190 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -47,6 +47,16 @@ static String _get_script_name(const Ref<Script> p_script) {
}
}
+static String _get_element_type(Variant::Type builtin_type, const StringName &native_type, const Ref<Script> &script_type) {
+ if (script_type.is_valid() && script_type->is_valid()) {
+ return _get_script_name(script_type);
+ } else if (native_type != StringName()) {
+ return native_type.operator String();
+ } else {
+ return Variant::get_type_name(builtin_type);
+ }
+}
+
static String _get_var_type(const Variant *p_var) {
String basestr;
@@ -75,15 +85,8 @@ static String _get_var_type(const Variant *p_var) {
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) + "]";
+ if (builtin_type != Variant::NIL) {
+ basestr += "[" + _get_element_type(builtin_type, p_array->get_typed_class_name(), p_array->get_typed_script()) + "]";
}
} else {
basestr = Variant::get_type_name(p_var->get_type());
@@ -101,10 +104,7 @@ Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataT
// Typed array.
if (p_data_type.has_container_element_type()) {
const GDScriptDataType &element_type = p_data_type.get_container_element_type();
- array.set_typed(
- element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT,
- element_type.native_type,
- element_type.script_type);
+ array.set_typed(element_type.builtin_type, element_type.native_type, element_type.script_type);
}
return array;
@@ -131,6 +131,8 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
#ifdef DEBUG_ENABLED
if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
+ } else if (p_err.expected == Variant::ARRAY && argptrs[errorarg]->get_type() == p_err.expected) {
+ err_text = "Invalid type in " + p_where + ". The array of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") does not have the same element type as the expected typed array argument.";
} else
#endif // DEBUG_ENABLED
{
@@ -518,7 +520,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (!argument_types[i].is_type(*p_args[i], true)) {
r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_err.argument = i;
- r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
+ r_err.expected = argument_types[i].builtin_type;
return _get_default_variant_for_data_type(return_type);
}
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
@@ -1174,27 +1176,37 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
DISPATCH_OPCODE;
OPCODE(OPCODE_ASSIGN_TYPED_ARRAY) {
- CHECK_SPACE(3);
+ CHECK_SPACE(6);
GET_VARIANT_PTR(dst, 0);
GET_VARIANT_PTR(src, 1);
- Array *dst_arr = VariantInternal::get_array(dst);
+ GET_VARIANT_PTR(script_type, 2);
+ Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 4];
+ int native_type_idx = _code_ptr[ip + 5];
+ GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count);
+ const StringName native_type = _global_names_ptr[native_type_idx];
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 '" + +"'.";
-#endif
+ err_text = vformat(R"(Trying to assign a value of type "%s" to a variable of type "Array[%s]".)",
+ _get_var_type(src), _get_element_type(builtin_type, native_type, *script_type));
+#endif // DEBUG_ENABLED
OPCODE_BREAK;
}
- if (!dst_arr->typed_assign(*src)) {
+
+ Array *array = VariantInternal::get_array(src);
+
+ if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) {
#ifdef DEBUG_ENABLED
- err_text = "Trying to assign a typed array with an array of different type.'";
-#endif
+ err_text = vformat(R"(Trying to assign an array of type "%s" to a variable of type "Array[%s]".)",
+ _get_var_type(src), _get_element_type(builtin_type, native_type, *script_type));
+#endif // DEBUG_ENABLED
OPCODE_BREAK;
}
- ip += 3;
+ *dst = *src;
+
+ ip += 6;
}
DISPATCH_OPCODE;
@@ -1469,9 +1481,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
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]);
}
@@ -1479,7 +1489,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;
+ *dst = Array(array, builtin_type, native_type, *script_type);
ip += 4;
}
@@ -2486,30 +2496,25 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (r->get_type() != Variant::ARRAY) {
#ifdef DEBUG_ENABLED
- err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "Array[%s]".)",
- Variant::get_type_name(r->get_type()), Variant::get_type_name(builtin_type));
-#endif
+ err_text = vformat(R"(Trying to return a value of type "%s" where expected return type is "Array[%s]".)",
+ _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type));
+#endif // DEBUG_ENABLED
OPCODE_BREAK;
}
- Array array;
- array.set_typed(builtin_type, native_type, *script_type);
+ Array *array = VariantInternal::get_array(r);
+ if (array->get_typed_builtin() != ((uint32_t)builtin_type) || array->get_typed_class_name() != native_type || array->get_typed_script() != *script_type || array->get_typed_class_name() != native_type) {
#ifdef DEBUG_ENABLED
- bool valid = array.typed_assign(*VariantInternal::get_array(r));
-#else
- array.typed_assign(*VariantInternal::get_array(r));
+ err_text = vformat(R"(Trying to return an array of type "%s" where expected return type is "Array[%s]".)",
+ _get_var_type(r), _get_element_type(builtin_type, native_type, *script_type));
#endif // DEBUG_ENABLED
-
- // Assign the return value anyway since we want it to be the valid type.
- retvalue = array;
-
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Trying to return a typed array with an array of different type.'";
OPCODE_BREAK;
}
+ retvalue = *array;
+
+#ifdef DEBUG_ENABLED
exit_ok = true;
#endif // DEBUG_ENABLED
OPCODE_BREAK;
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out
index 6f7f0783f0..015ad756f8 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Cannot get index "true" from "[0, 1]".
+Invalid index type "bool" for a base of type "Array".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd
new file mode 100644
index 0000000000..ce50cccb3c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.gd
@@ -0,0 +1,4 @@
+func test():
+ var differently: Array[float] = [1.0]
+ var typed: Array[int] = differently
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out
new file mode 100644
index 0000000000..c6d39781ee
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assign_differently_typed.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type Array[float] to variable "typed" with specified type Array[int].
diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.gd
index 9f86d0531c..9f86d0531c 100644
--- a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.gd
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out
new file mode 100644
index 0000000000..8530783673
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_assignment.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot include a value of type "String" as "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd
new file mode 100644
index 0000000000..25cde1d40b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.gd
@@ -0,0 +1,4 @@
+func test():
+ var unconvertable := 1
+ var typed: Array[Object] = [unconvertable]
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out
new file mode 100644
index 0000000000..dfe3443761
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_init_with_unconvertable_in_literal.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot have an element of type "int" in an array of type "Array[Object]".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd
new file mode 100644
index 0000000000..1a90bd121e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.gd
@@ -0,0 +1,7 @@
+func expect_typed(typed: Array[int]):
+ print(typed.size())
+
+func test():
+ var differently: Array[float] = [1.0]
+ expect_typed(differently)
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out
new file mode 100644
index 0000000000..297e1283e8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/typed_array_pass_differently_to_typed.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid argument for "expect_typed()" function: argument 1 should be "Array[int]" but is "Array[float]".
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out
index 082e3ade19..2729c5b6c7 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_as_default_parameter.out
@@ -2,7 +2,7 @@ GDTEST_OK
[0]
0
[1]
-2
+0
[2]
2
ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
new file mode 100644
index 0000000000..e1e6134fd4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd
@@ -0,0 +1,204 @@
+class A: pass
+class B extends A: pass
+
+enum E { E0 = 391 }
+
+func floats_identity(floats: Array[float]): return floats
+
+class Members:
+ var one: Array[int] = [104]
+ var two: Array[int] = one
+
+ func check_passing() -> bool:
+ assert(str(one) == '[104]')
+ assert(str(two) == '[104]')
+ two.push_back(582)
+ assert(str(one) == '[104, 582]')
+ assert(str(two) == '[104, 582]')
+ two = [486]
+ assert(str(one) == '[104, 582]')
+ assert(str(two) == '[486]')
+ return true
+
+
+@warning_ignore("unsafe_method_access")
+@warning_ignore("assert_always_true")
+@warning_ignore("return_value_discarded")
+func test():
+ var untyped_basic = [459]
+ assert(str(untyped_basic) == '[459]')
+ assert(untyped_basic.get_typed_builtin() == TYPE_NIL)
+
+ var inferred_basic := [366]
+ assert(str(inferred_basic) == '[366]')
+ assert(inferred_basic.get_typed_builtin() == TYPE_NIL)
+
+ var typed_basic: Array = [521]
+ assert(str(typed_basic) == '[521]')
+ assert(typed_basic.get_typed_builtin() == TYPE_NIL)
+
+
+ var empty_floats: Array[float] = []
+ assert(str(empty_floats) == '[]')
+ assert(empty_floats.get_typed_builtin() == TYPE_FLOAT)
+
+ untyped_basic = empty_floats
+ assert(untyped_basic.get_typed_builtin() == TYPE_FLOAT)
+
+ inferred_basic = empty_floats
+ assert(inferred_basic.get_typed_builtin() == TYPE_FLOAT)
+
+ typed_basic = empty_floats
+ assert(typed_basic.get_typed_builtin() == TYPE_FLOAT)
+
+ empty_floats.push_back(705.0)
+ untyped_basic.push_back(430.0)
+ inferred_basic.push_back(263.0)
+ typed_basic.push_back(518.0)
+ assert(str(empty_floats) == '[705, 430, 263, 518]')
+ assert(str(untyped_basic) == '[705, 430, 263, 518]')
+ assert(str(inferred_basic) == '[705, 430, 263, 518]')
+ assert(str(typed_basic) == '[705, 430, 263, 518]')
+
+
+ const constant_float := 950.0
+ const constant_int := 170
+ var typed_float := 954.0
+ var filled_floats: Array[float] = [constant_float, constant_int, typed_float, empty_floats[1] + empty_floats[2]]
+ assert(str(filled_floats) == '[950, 170, 954, 693]')
+ assert(filled_floats.get_typed_builtin() == TYPE_FLOAT)
+
+ var casted_floats := [empty_floats[2] * 2] as Array[float]
+ assert(str(casted_floats) == '[526]')
+ assert(casted_floats.get_typed_builtin() == TYPE_FLOAT)
+
+ var returned_floats = (func () -> Array[float]: return [554]).call()
+ assert(str(returned_floats) == '[554]')
+ assert(returned_floats.get_typed_builtin() == TYPE_FLOAT)
+
+ var passed_floats = floats_identity([663.0 if randf() > 0.5 else 663.0])
+ assert(str(passed_floats) == '[663]')
+ assert(passed_floats.get_typed_builtin() == TYPE_FLOAT)
+
+ var default_floats = (func (floats: Array[float] = [364.0]): return floats).call()
+ assert(str(default_floats) == '[364]')
+ assert(default_floats.get_typed_builtin() == TYPE_FLOAT)
+
+ var typed_int := 556
+ var converted_floats: Array[float] = [typed_int]
+ assert(str(converted_floats) == '[556]')
+ assert(converted_floats.get_typed_builtin() == TYPE_FLOAT)
+
+
+ const constant_basic = [228]
+ assert(str(constant_basic) == '[228]')
+ assert(constant_basic.get_typed_builtin() == TYPE_NIL)
+
+ const constant_floats: Array[float] = [constant_float - constant_basic[0] - constant_int]
+ assert(str(constant_floats) == '[552]')
+ assert(constant_floats.get_typed_builtin() == TYPE_FLOAT)
+
+
+ var source_floats: Array[float] = [999.74]
+ untyped_basic = source_floats
+ var destination_floats: Array[float] = untyped_basic
+ destination_floats[0] -= 0.74
+ assert(str(source_floats) == '[999]')
+ assert(str(untyped_basic) == '[999]')
+ assert(str(destination_floats) == '[999]')
+ assert(destination_floats.get_typed_builtin() == TYPE_FLOAT)
+
+
+ var duplicated_floats := empty_floats.duplicate().slice(2, 3)
+ duplicated_floats[0] *= 3
+ assert(str(duplicated_floats) == '[789]')
+ assert(duplicated_floats.get_typed_builtin() == TYPE_FLOAT)
+
+
+ var b_objects: Array[B] = [B.new(), null]
+ assert(b_objects.size() == 2)
+ assert(b_objects.get_typed_builtin() == TYPE_OBJECT)
+ assert(b_objects.get_typed_script() == B)
+
+ var a_objects: Array[A] = [A.new(), B.new(), null, b_objects[0]]
+ assert(a_objects.size() == 4)
+ assert(a_objects.get_typed_builtin() == TYPE_OBJECT)
+ assert(a_objects.get_typed_script() == A)
+
+ var a_passed = (func check_a_passing(a_objects: Array[A]): return a_objects.size()).call(a_objects)
+ assert(a_passed == 4)
+
+ var b_passed = (func check_b_passing(basic: Array): return basic[0] != null).call(b_objects)
+ assert(b_passed == true)
+
+
+ var empty_strings: Array[String] = []
+ var empty_bools: Array[bool] = []
+ var empty_basic_one := []
+ var empty_basic_two := []
+ assert(empty_strings == empty_bools)
+ assert(empty_basic_one == empty_basic_two)
+ assert(empty_strings.hash() == empty_bools.hash())
+ assert(empty_basic_one.hash() == empty_basic_two.hash())
+
+
+ var assign_source: Array[int] = [527]
+ var assign_target: Array[int] = []
+ assign_target.assign(assign_source)
+ assert(str(assign_source) == '[527]')
+ assert(str(assign_target) == '[527]')
+ assign_source.push_back(657)
+ assert(str(assign_source) == '[527, 657]')
+ assert(str(assign_target) == '[527]')
+
+
+ var defaults_passed = (func check_defaults_passing(one: Array[int] = [], two := one):
+ one.push_back(887)
+ two.push_back(198)
+ assert(str(one) == '[887, 198]')
+ assert(str(two) == '[887, 198]')
+ two = [130]
+ assert(str(one) == '[887, 198]')
+ assert(str(two) == '[130]')
+ return true
+ ).call()
+ assert(defaults_passed == true)
+
+
+ var members := Members.new()
+ var members_passed := members.check_passing()
+ assert(members_passed == true)
+
+
+ var resized_basic: Array = []
+ resized_basic.resize(1)
+ assert(typeof(resized_basic[0]) == TYPE_NIL)
+ assert(resized_basic[0] == null)
+
+ var resized_ints: Array[int] = []
+ resized_ints.resize(1)
+ assert(typeof(resized_ints[0]) == TYPE_INT)
+ assert(resized_ints[0] == 0)
+
+ var resized_arrays: Array[Array] = []
+ resized_arrays.resize(1)
+ assert(typeof(resized_arrays[0]) == TYPE_ARRAY)
+ resized_arrays[0].resize(1)
+ resized_arrays[0][0] = 523
+ assert(str(resized_arrays) == '[[523]]')
+
+ var resized_objects: Array[Object] = []
+ resized_objects.resize(1)
+ assert(typeof(resized_objects[0]) == TYPE_NIL)
+ assert(resized_objects[0] == null)
+
+
+ var typed_enums: Array[E] = []
+ typed_enums.resize(1)
+ assert(str(typed_enums) == '[0]')
+ typed_enums[0] = E.E0
+ assert(str(typed_enums) == '[391]')
+ assert(typed_enums.get_typed_builtin() == TYPE_INT)
+
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
deleted file mode 100644
index ad2e6558d7..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
+++ /dev/null
@@ -1,2 +0,0 @@
-GDTEST_ANALYZER_ERROR
-Cannot assign a value of type Array[String] to constant "arr" with specified type Array[int].
diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd
new file mode 100644
index 0000000000..e9dbc1b640
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.gd
@@ -0,0 +1,4 @@
+func test():
+ var basic := [1]
+ var typed: Array[int] = basic
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out
new file mode 100644
index 0000000000..bca700b4ec
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_basic_to_typed.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/typed_array_assign_basic_to_typed.gd
+>> 3
+>> Trying to assign an array of type "Array" to a variable of type "Array[int]".
diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd
new file mode 100644
index 0000000000..920352a6ea
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.gd
@@ -0,0 +1,4 @@
+func test():
+ var differently: Variant = [1.0] as Array[float]
+ var typed: Array[int] = differently
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out
new file mode 100644
index 0000000000..402ab38fb3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_assign_differently_typed.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/typed_array_assign_differently_typed.gd
+>> 3
+>> Trying to assign an array of type "Array[float]" to a variable of type "Array[int]".
diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd
new file mode 100644
index 0000000000..e1fd0f7168
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.gd
@@ -0,0 +1,7 @@
+func expect_typed(typed: Array[int]):
+ print(typed.size())
+
+func test():
+ var basic := [1]
+ expect_typed(basic)
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out
new file mode 100644
index 0000000000..6f210e944e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_basic_to_typed.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/typed_array_pass_basic_to_typed.gd
+>> 6
+>> Invalid type in function 'expect_typed' in base 'RefCounted ()'. The array of argument 1 (Array) does not have the same element type as the expected typed array argument.
diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd
new file mode 100644
index 0000000000..e2d2721e8c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.gd
@@ -0,0 +1,7 @@
+func expect_typed(typed: Array[int]):
+ print(typed.size())
+
+func test():
+ var differently: Variant = [1.0] as Array[float]
+ expect_typed(differently)
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out
new file mode 100644
index 0000000000..3cd4e25bd8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/typed_array_pass_differently_to_typed.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/typed_array_pass_differently_to_typed.gd
+>> 6
+>> Invalid type in function 'expect_typed' in base 'RefCounted ()'. The array of argument 1 (Array[float]) does not have the same element type as the expected typed array argument.
diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd
new file mode 100644
index 0000000000..ec444b4ffa
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.gd
@@ -0,0 +1,6 @@
+func test():
+ var untyped: Variant = 32
+ var typed: Array[int] = [untyped]
+ assert(typed.get_typed_builtin() == TYPE_INT)
+ assert(str(typed) == '[32]')
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/typed_array_init_with_untyped_in_literal.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+ok
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index e497a628aa..7984ec735e 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -314,7 +314,19 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
//must make a copy, because this res is local to scene
}
}
- } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) {
+ }
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = node->get(snames[nprops[j].name], &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
+ }
+ }
+ }
+ if (p_edit_state == GEN_EDIT_STATE_INSTANCE && value.get_type() != Variant::OBJECT) {
value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor
}
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 0ba177f882..608d15019e 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -636,6 +636,18 @@ Error ResourceLoaderText::load() {
}
}
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
+ }
+ }
+ }
+
if (set_valid) {
res->set(assign, value);
}
@@ -746,6 +758,18 @@ Error ResourceLoaderText::load() {
}
}
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = resource->get(assign, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
+ }
+ }
+ }
+
if (set_valid) {
resource->set(assign, value);
}