Merge pull request #26573 from godotengine/revert-26562-gdscript-no-implicit-cast
Revert "Forbid implicit type conversion in GDScript"
This commit is contained in:
commit
2bc981948d
4 changed files with 110 additions and 16 deletions
|
@ -328,8 +328,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!argument_types[i].is_type(*p_args[i])) {
|
if (!argument_types[i].is_type(*p_args[i], true)) {
|
||||||
if (argument_types[i].is_type(Variant())) {
|
if (argument_types[i].is_type(Variant(), true)) {
|
||||||
memnew_placement(&stack[i], Variant);
|
memnew_placement(&stack[i], Variant);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -339,7 +339,12 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
|
||||||
return Variant();
|
return Variant();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memnew_placement(&stack[i], Variant(*p_args[i]));
|
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
|
||||||
|
Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
|
||||||
|
memnew_placement(&stack[i], Variant(arg));
|
||||||
|
} else {
|
||||||
|
memnew_placement(&stack[i], Variant(*p_args[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (int i = p_argcount; i < _stack_size; i++) {
|
for (int i = p_argcount; i < _stack_size; i++) {
|
||||||
memnew_placement(&stack[i], Variant);
|
memnew_placement(&stack[i], Variant);
|
||||||
|
|
|
@ -55,7 +55,7 @@ struct GDScriptDataType {
|
||||||
StringName native_type;
|
StringName native_type;
|
||||||
Ref<Script> script_type;
|
Ref<Script> script_type;
|
||||||
|
|
||||||
bool is_type(const Variant &p_variant) const {
|
bool is_type(const Variant &p_variant, bool p_allow_implicit_conversion = false) const {
|
||||||
if (!has_type) return true; // Can't type check
|
if (!has_type) return true; // Can't type check
|
||||||
|
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
|
@ -63,7 +63,11 @@ struct GDScriptDataType {
|
||||||
break;
|
break;
|
||||||
case BUILTIN: {
|
case BUILTIN: {
|
||||||
Variant::Type var_type = p_variant.get_type();
|
Variant::Type var_type = p_variant.get_type();
|
||||||
return builtin_type == var_type;
|
bool valid = builtin_type == var_type;
|
||||||
|
if (!valid && p_allow_implicit_conversion) {
|
||||||
|
valid = Variant::can_convert_strict(var_type, builtin_type);
|
||||||
|
}
|
||||||
|
return valid;
|
||||||
} break;
|
} break;
|
||||||
case NATIVE: {
|
case NATIVE: {
|
||||||
if (p_variant.get_type() == Variant::NIL) {
|
if (p_variant.get_type() == Variant::NIL) {
|
||||||
|
|
|
@ -5801,7 +5801,7 @@ Variant::Operator GDScriptParser::_get_variant_operation(const OperatorNode::Ope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GDScriptParser::_is_type_compatible(const DataType &p_container, const DataType &p_expression) const {
|
bool GDScriptParser::_is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion) const {
|
||||||
// Ignore for completion
|
// Ignore for completion
|
||||||
if (!check_types || for_completion) {
|
if (!check_types || for_completion) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -5817,6 +5817,9 @@ bool GDScriptParser::_is_type_compatible(const DataType &p_container, const Data
|
||||||
|
|
||||||
if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) {
|
if (p_container.kind == DataType::BUILTIN && p_expression.kind == DataType::BUILTIN) {
|
||||||
bool valid = p_container.builtin_type == p_expression.builtin_type;
|
bool valid = p_container.builtin_type == p_expression.builtin_type;
|
||||||
|
if (p_allow_implicit_conversion) {
|
||||||
|
valid = valid || Variant::can_convert_strict(p_expression.builtin_type, p_container.builtin_type);
|
||||||
|
}
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6685,7 +6688,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
|
||||||
arg_type.native_type = mi.arguments[i].class_name;
|
arg_type.native_type = mi.arguments[i].class_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_is_type_compatible(arg_type, par_types[i])) {
|
if (!_is_type_compatible(arg_type, par_types[i], true)) {
|
||||||
types_match = false;
|
types_match = false;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -6927,7 +6930,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat
|
||||||
if (par_type.may_yield && p_call->arguments[i]->type == Node::TYPE_OPERATOR) {
|
if (par_type.may_yield && p_call->arguments[i]->type == Node::TYPE_OPERATOR) {
|
||||||
_add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i])));
|
_add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, p_call->line, _find_function_name(static_cast<OperatorNode *>(p_call->arguments[i])));
|
||||||
}
|
}
|
||||||
} else if (!_is_type_compatible(arg_types[i - arg_diff], par_type)) {
|
} else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) {
|
||||||
// Supertypes are acceptable for dynamic compliance
|
// Supertypes are acceptable for dynamic compliance
|
||||||
if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) {
|
if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) {
|
||||||
_set_error("At '" + callee_name + "()' call, argument " + itos(i - arg_diff + 1) + ". Assigned type (" +
|
_set_error("At '" + callee_name + "()' call, argument " + itos(i - arg_diff + 1) + ". Assigned type (" +
|
||||||
|
@ -7390,6 +7393,33 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
|
||||||
// Try supertype test
|
// Try supertype test
|
||||||
if (_is_type_compatible(expr_type, v.data_type)) {
|
if (_is_type_compatible(expr_type, v.data_type)) {
|
||||||
_mark_line_as_unsafe(v.line);
|
_mark_line_as_unsafe(v.line);
|
||||||
|
} else {
|
||||||
|
// Try with implicit conversion
|
||||||
|
if (v.data_type.kind != DataType::BUILTIN || !_is_type_compatible(v.data_type, expr_type, true)) {
|
||||||
|
_set_error("Assigned expression type (" + expr_type.to_string() + ") doesn't match the variable's type (" +
|
||||||
|
v.data_type.to_string() + ").",
|
||||||
|
v.line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace assignment with implict conversion
|
||||||
|
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
|
||||||
|
convert->line = v.line;
|
||||||
|
convert->function = GDScriptFunctions::TYPE_CONVERT;
|
||||||
|
|
||||||
|
ConstantNode *tgt_type = alloc_node<ConstantNode>();
|
||||||
|
tgt_type->line = v.line;
|
||||||
|
tgt_type->value = (int)v.data_type.builtin_type;
|
||||||
|
|
||||||
|
OperatorNode *convert_call = alloc_node<OperatorNode>();
|
||||||
|
convert_call->line = v.line;
|
||||||
|
convert_call->op = OperatorNode::OP_CALL;
|
||||||
|
convert_call->arguments.push_back(convert);
|
||||||
|
convert_call->arguments.push_back(v.expression);
|
||||||
|
convert_call->arguments.push_back(tgt_type);
|
||||||
|
|
||||||
|
v.expression = convert_call;
|
||||||
|
v.initial_assignment->arguments.write[1] = convert_call;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7430,7 +7460,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) {
|
||||||
// Check export hint
|
// Check export hint
|
||||||
if (v.data_type.has_type && v._export.type != Variant::NIL) {
|
if (v.data_type.has_type && v._export.type != Variant::NIL) {
|
||||||
DataType export_type = _type_from_property(v._export);
|
DataType export_type = _type_from_property(v._export);
|
||||||
if (!_is_type_compatible(v.data_type, export_type)) {
|
if (!_is_type_compatible(v.data_type, export_type, true)) {
|
||||||
_set_error("Export hint type (" + export_type.to_string() + ") doesn't match the variable's type (" +
|
_set_error("Export hint type (" + export_type.to_string() + ") doesn't match the variable's type (" +
|
||||||
v.data_type.to_string() + ").",
|
v.data_type.to_string() + ").",
|
||||||
v.line);
|
v.line);
|
||||||
|
@ -7551,7 +7581,7 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) {
|
||||||
} else {
|
} else {
|
||||||
p_function->return_type = _resolve_type(p_function->return_type, p_function->line);
|
p_function->return_type = _resolve_type(p_function->return_type, p_function->line);
|
||||||
|
|
||||||
if (!_is_type_compatible(p_function->argument_types[i], def_type)) {
|
if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) {
|
||||||
String arg_name = p_function->arguments[i];
|
String arg_name = p_function->arguments[i];
|
||||||
_set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" +
|
_set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" +
|
||||||
arg_name + "' (" + p_function->arguments[i] + ")",
|
arg_name + "' (" + p_function->arguments[i] + ")",
|
||||||
|
@ -7739,13 +7769,41 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
|
||||||
}
|
}
|
||||||
#endif // DEBUG_ENABLED
|
#endif // DEBUG_ENABLED
|
||||||
|
|
||||||
if (check_types && !_is_type_compatible(lv->datatype, assign_type)) {
|
if (!_is_type_compatible(lv->datatype, assign_type)) {
|
||||||
// Try supertype test
|
// Try supertype test
|
||||||
if (_is_type_compatible(assign_type, lv->datatype)) {
|
if (_is_type_compatible(assign_type, lv->datatype)) {
|
||||||
_mark_line_as_unsafe(lv->line);
|
_mark_line_as_unsafe(lv->line);
|
||||||
} else {
|
} else {
|
||||||
_set_error("Assigned value type (" + assign_type.to_string() + ") does not match variable type (" + lv->datatype.to_string() + ")", lv->line);
|
// Try implict conversion
|
||||||
return;
|
if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) {
|
||||||
|
_set_error("Assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" +
|
||||||
|
lv->datatype.to_string() + ").",
|
||||||
|
lv->line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Replace assignment with implict conversion
|
||||||
|
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
|
||||||
|
convert->line = lv->line;
|
||||||
|
convert->function = GDScriptFunctions::TYPE_CONVERT;
|
||||||
|
|
||||||
|
ConstantNode *tgt_type = alloc_node<ConstantNode>();
|
||||||
|
tgt_type->line = lv->line;
|
||||||
|
tgt_type->value = (int)lv->datatype.builtin_type;
|
||||||
|
|
||||||
|
OperatorNode *convert_call = alloc_node<OperatorNode>();
|
||||||
|
convert_call->line = lv->line;
|
||||||
|
convert_call->op = OperatorNode::OP_CALL;
|
||||||
|
convert_call->arguments.push_back(convert);
|
||||||
|
convert_call->arguments.push_back(lv->assign);
|
||||||
|
convert_call->arguments.push_back(tgt_type);
|
||||||
|
|
||||||
|
lv->assign = convert_call;
|
||||||
|
lv->assign_op->arguments.write[1] = convert_call;
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (lv->datatype.builtin_type == Variant::INT && assign_type.builtin_type == Variant::REAL) {
|
||||||
|
_add_warning(GDScriptWarning::NARROWING_CONVERSION, lv->line);
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lv->datatype.infer_type) {
|
if (lv->datatype.infer_type) {
|
||||||
|
@ -7847,8 +7905,35 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) {
|
||||||
if (_is_type_compatible(rh_type, lh_type)) {
|
if (_is_type_compatible(rh_type, lh_type)) {
|
||||||
_mark_line_as_unsafe(op->line);
|
_mark_line_as_unsafe(op->line);
|
||||||
} else {
|
} else {
|
||||||
_set_error("Assigned value type (" + rh_type.to_string() + ") does not match variable type (" + lh_type.to_string() + ")", op->line);
|
// Try implict conversion
|
||||||
return;
|
if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) {
|
||||||
|
_set_error("Assigned value type (" + rh_type.to_string() + ") doesn't match the variable's type (" +
|
||||||
|
lh_type.to_string() + ").",
|
||||||
|
op->line);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Replace assignment with implict conversion
|
||||||
|
BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>();
|
||||||
|
convert->line = op->line;
|
||||||
|
convert->function = GDScriptFunctions::TYPE_CONVERT;
|
||||||
|
|
||||||
|
ConstantNode *tgt_type = alloc_node<ConstantNode>();
|
||||||
|
tgt_type->line = op->line;
|
||||||
|
tgt_type->value = (int)lh_type.builtin_type;
|
||||||
|
|
||||||
|
OperatorNode *convert_call = alloc_node<OperatorNode>();
|
||||||
|
convert_call->line = op->line;
|
||||||
|
convert_call->op = OperatorNode::OP_CALL;
|
||||||
|
convert_call->arguments.push_back(convert);
|
||||||
|
convert_call->arguments.push_back(op->arguments[1]);
|
||||||
|
convert_call->arguments.push_back(tgt_type);
|
||||||
|
|
||||||
|
op->arguments.write[1] = convert_call;
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (lh_type.builtin_type == Variant::INT && rh_type.builtin_type == Variant::REAL) {
|
||||||
|
_add_warning(GDScriptWarning::NARROWING_CONVERSION, op->line);
|
||||||
|
}
|
||||||
|
#endif // DEBUG_ENABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
|
|
|
@ -607,7 +607,7 @@ private:
|
||||||
Variant::Operator _get_variant_operation(const OperatorNode::Operator &p_op) const;
|
Variant::Operator _get_variant_operation(const OperatorNode::Operator &p_op) const;
|
||||||
bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const;
|
bool _get_function_signature(DataType &p_base_type, const StringName &p_function, DataType &r_return_type, List<DataType> &r_arg_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) const;
|
||||||
bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const;
|
bool _get_member_type(const DataType &p_base_type, const StringName &p_member, DataType &r_member_type) const;
|
||||||
bool _is_type_compatible(const DataType &p_container, const DataType &p_expression) const;
|
bool _is_type_compatible(const DataType &p_container, const DataType &p_expression, bool p_allow_implicit_conversion = false) const;
|
||||||
|
|
||||||
DataType _reduce_node_type(Node *p_node);
|
DataType _reduce_node_type(Node *p_node);
|
||||||
DataType _reduce_function_call_type(const OperatorNode *p_call);
|
DataType _reduce_function_call_type(const OperatorNode *p_call);
|
||||||
|
|
Loading…
Reference in a new issue