GDScript: Fix issues with typed arrays
This commit is contained in:
parent
e9de988020
commit
5909f9f075
50 changed files with 959 additions and 555 deletions
|
|
@ -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;
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
return true;
|
||||
if (member->setter) {
|
||||
const Variant *args = &value;
|
||||
Callable::CallError err;
|
||||
callp(member->setter, &args, 1, err);
|
||||
return err.error == Callable::CallError::CALL_OK;
|
||||
} else {
|
||||
members.write[member->index] = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
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);
|
||||
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 (!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.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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (p_is_const) {
|
||||
dict.make_read_only();
|
||||
}
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
if (!dst_arr->typed_assign(*src)) {
|
||||
#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 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;
|
||||
}
|
||||
|
||||
ip += 3;
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
*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
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
Array array;
|
||||
array.set_typed(builtin_type, native_type, *script_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 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
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
OPCODE_BREAK;
|
||||
}
|
||||
|
||||
retvalue = *array;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
exit_ok = true;
|
||||
#endif // DEBUG_ENABLED
|
||||
OPCODE_BREAK;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot get index "true" from "[0, 1]".
|
||||
Invalid index type "bool" for a base of type "Array".
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
func test():
|
||||
var differently: Array[float] = [1.0]
|
||||
var typed: Array[int] = differently
|
||||
print('not ok')
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a value of type Array[float] to variable "typed" with specified type Array[int].
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot include a value of type "String" as "int".
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
func test():
|
||||
var unconvertable := 1
|
||||
var typed: Array[Object] = [unconvertable]
|
||||
print('not ok')
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot have an element of type "int" in an array of type "Array[Object]".
|
||||
|
|
@ -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')
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Invalid argument for "expect_typed()" function: argument 1 should be "Array[int]" but is "Array[float]".
|
||||
|
|
@ -2,7 +2,7 @@ GDTEST_OK
|
|||
[0]
|
||||
0
|
||||
[1]
|
||||
2
|
||||
0
|
||||
[2]
|
||||
2
|
||||
ok
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_OK
|
||||
ok
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
GDTEST_ANALYZER_ERROR
|
||||
Cannot assign a value of type Array[String] to constant "arr" with specified type Array[int].
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
func test():
|
||||
var basic := [1]
|
||||
var typed: Array[int] = basic
|
||||
print('not ok')
|
||||
|
|
@ -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]".
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
func test():
|
||||
var differently: Variant = [1.0] as Array[float]
|
||||
var typed: Array[int] = differently
|
||||
print('not ok')
|
||||
|
|
@ -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]".
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
func expect_typed(typed: Array[int]):
|
||||
print(typed.size())
|
||||
|
||||
func test():
|
||||
var basic := [1]
|
||||
expect_typed(basic)
|
||||
print('not ok')
|
||||
|
|
@ -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.
|
||||
|
|
@ -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')
|
||||
|
|
@ -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.
|
||||
|
|
@ -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')
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
GDTEST_OK
|
||||
ok
|
||||
Loading…
Add table
Add a link
Reference in a new issue