Merge pull request #49112 from vnen/gdscript-assign-type-check

GDScript: Use analyzer data to decide assignment conversion
This commit is contained in:
Rémi Verschelde 2021-05-26 20:34:43 +02:00 committed by GitHub
commit 78bbb2cae1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 51 deletions

View file

@ -543,6 +543,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} else { } else {
// TODO: Add warning. // TODO: Add warning.
mark_node_unsafe(member.variable->initializer); mark_node_unsafe(member.variable->initializer);
member.variable->use_conversion_assign = true;
} }
} else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
@ -552,6 +553,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
if (member.variable->initializer->get_datatype().is_variant()) { if (member.variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign. // TODO: Warn unsafe assign.
mark_node_unsafe(member.variable->initializer); mark_node_unsafe(member.variable->initializer);
member.variable->use_conversion_assign = true;
} }
} }
} else if (member.variable->infer_datatype) { } else if (member.variable->infer_datatype) {
@ -1145,6 +1147,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
} else { } else {
// TODO: Add warning. // TODO: Add warning.
mark_node_unsafe(p_variable->initializer); mark_node_unsafe(p_variable->initializer);
p_variable->use_conversion_assign = true;
} }
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
} else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) { } else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
@ -1154,6 +1157,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
if (p_variable->initializer->get_datatype().is_variant()) { if (p_variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign. // TODO: Warn unsafe assign.
mark_node_unsafe(p_variable->initializer); mark_node_unsafe(p_variable->initializer);
p_variable->use_conversion_assign = true;
} }
} }
} else if (p_variable->infer_datatype) { } else if (p_variable->infer_datatype) {
@ -1608,10 +1612,12 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
} else { } else {
// TODO: Add warning. // TODO: Add warning.
mark_node_unsafe(p_assignment); mark_node_unsafe(p_assignment);
p_assignment->use_conversion_assign = true;
} }
} else { } else {
// TODO: Warning in this case. // TODO: Warning in this case.
mark_node_unsafe(p_assignment); mark_node_unsafe(p_assignment);
p_assignment->use_conversion_assign = true;
} }
} }
} else { } else {
@ -1621,6 +1627,9 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if (assignee_type.has_no_type() || assigned_value_type.is_variant()) { if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
mark_node_unsafe(p_assignment); mark_node_unsafe(p_assignment);
if (assignee_type.is_hard_type()) {
p_assignment->use_conversion_assign = true;
}
} }
if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) { if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {

View file

@ -779,63 +779,43 @@ void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const
append(p_name); append(p_name);
} }
void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) { void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) {
if (p_target.type.has_type && !p_source.type.has_type) { switch (p_target.type.kind) {
// Typed assignment. case GDScriptDataType::BUILTIN: {
switch (p_target.type.kind) { if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
case GDScriptDataType::BUILTIN: { append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
append(p_target);
append(p_source);
} else {
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
append(p_target.type.builtin_type);
}
} break;
case GDScriptDataType::NATIVE: {
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
append(p_target); append(p_target);
append(p_source); append(p_source);
append(class_idx); } else {
} break; append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = p_target.type.script_type;
int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
append(p_target);
append(p_source);
append(idx);
} break;
default: {
ERR_PRINT("Compiler bug: unresolved assign.");
// Shouldn't get here, but fail-safe to a regular assignment
append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target); append(p_target);
append(p_source); append(p_source);
append(p_target.type.builtin_type);
} }
} } break;
} else { case GDScriptDataType::NATIVE: {
if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) { int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2); Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
append(p_target); append(p_target);
append(p_source); append(p_source);
} else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) { append(class_idx);
// Need conversion.. } break;
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2); case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = p_target.type.script_type;
int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
append(p_target); append(p_target);
append(p_source); append(p_source);
append(p_target.type.builtin_type); append(idx);
} else { } break;
// Either untyped assignment or already type-checked by the parser default: {
ERR_PRINT("Compiler bug: unresolved assign.");
// Shouldn't get here, but fail-safe to a regular assignment
append(GDScriptFunction::OPCODE_ASSIGN, 2); append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target); append(p_target);
append(p_source); append(p_source);
@ -843,6 +823,24 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
} }
} }
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()) {
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
append(p_target);
append(p_source);
} else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
// Need conversion.
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
append(p_target.type.builtin_type);
} else {
append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target);
append(p_source);
}
}
void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) { void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1); append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target); append(p_target);

View file

@ -450,6 +450,7 @@ public:
virtual void write_set_member(const Address &p_value, const StringName &p_name) override; virtual void write_set_member(const Address &p_value, const StringName &p_name) override;
virtual void write_get_member(const Address &p_target, const StringName &p_name) override; virtual void write_get_member(const Address &p_target, const StringName &p_name) override;
virtual void write_assign(const Address &p_target, const Address &p_source) override; virtual void write_assign(const Address &p_target, const Address &p_source) override;
virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override;
virtual void write_assign_true(const Address &p_target) override; virtual void write_assign_true(const Address &p_target) override;
virtual void write_assign_false(const Address &p_target) override; virtual void write_assign_false(const Address &p_target) override;
virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override; virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override;

View file

@ -111,6 +111,7 @@ public:
virtual void write_set_member(const Address &p_value, const StringName &p_name) = 0; virtual void write_set_member(const Address &p_value, const StringName &p_name) = 0;
virtual void write_get_member(const Address &p_target, const StringName &p_name) = 0; virtual void write_get_member(const Address &p_target, const StringName &p_name) = 0;
virtual void write_assign(const Address &p_target, const Address &p_source) = 0; virtual void write_assign(const Address &p_target, const Address &p_source) = 0;
virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0;
virtual void write_assign_true(const Address &p_target) = 0; virtual void write_assign_true(const Address &p_target) = 0;
virtual void write_assign_false(const Address &p_target) = 0; virtual void write_assign_false(const Address &p_target) = 0;
virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0; virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0;

View file

@ -1084,7 +1084,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args); gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args);
} else { } else {
// Just assign. // Just assign.
gen->write_assign(target, op_result); if (assignment->use_conversion_assign) {
gen->write_assign_with_conversion(target, op_result);
} else {
gen->write_assign(target, op_result);
}
} }
if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) { if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@ -1792,7 +1796,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (error) { if (error) {
return error; return error;
} }
gen->write_assign(local, src_address); if (lv->use_conversion_assign) {
gen->write_assign_with_conversion(local, src_address);
} else {
gen->write_assign(local, src_address);
}
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) { if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary(); codegen.generator->pop_temporary();
} }
@ -1930,7 +1938,11 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
return nullptr; return nullptr;
} }
codegen.generator->write_assign(dst_address, src_address); if (field->use_conversion_assign) {
codegen.generator->write_assign_with_conversion(dst_address, src_address);
} else {
codegen.generator->write_assign(dst_address, src_address);
}
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) { if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary(); codegen.generator->pop_temporary();
} }

View file

@ -370,6 +370,7 @@ public:
Variant::Operator variant_op = Variant::OP_MAX; Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *assignee = nullptr; ExpressionNode *assignee = nullptr;
ExpressionNode *assigned_value = nullptr; ExpressionNode *assigned_value = nullptr;
bool use_conversion_assign = false;
AssignmentNode() { AssignmentNode() {
type = ASSIGNMENT; type = ASSIGNMENT;
@ -1119,6 +1120,7 @@ public:
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED; MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
int assignments = 0; int assignments = 0;
int usages = 0; int usages = 0;
bool use_conversion_assign = false;
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
String doc_description; String doc_description;
#endif // TOOLS_ENABLED #endif // TOOLS_ENABLED