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 {
// TODO: Add warning.
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) {
#ifdef DEBUG_ENABLED
@ -552,6 +553,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
if (member.variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(member.variable->initializer);
member.variable->use_conversion_assign = true;
}
}
} else if (member.variable->infer_datatype) {
@ -1145,6 +1147,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
} else {
// TODO: Add warning.
mark_node_unsafe(p_variable->initializer);
p_variable->use_conversion_assign = true;
}
#ifdef DEBUG_ENABLED
} 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()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(p_variable->initializer);
p_variable->use_conversion_assign = true;
}
}
} else if (p_variable->infer_datatype) {
@ -1608,10 +1612,12 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
} else {
// TODO: Add warning.
mark_node_unsafe(p_assignment);
p_assignment->use_conversion_assign = true;
}
} else {
// TODO: Warning in this case.
mark_node_unsafe(p_assignment);
p_assignment->use_conversion_assign = true;
}
}
} else {
@ -1621,6 +1627,9 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
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) {

View file

@ -779,63 +779,43 @@ void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const
append(p_name);
}
void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
if (p_target.type.has_type && !p_source.type.has_type) {
// Typed assignment.
switch (p_target.type.kind) {
case GDScriptDataType::BUILTIN: {
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);
void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) {
switch (p_target.type.kind) {
case GDScriptDataType::BUILTIN: {
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);
append(class_idx);
} break;
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);
} else {
append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
append(p_target.type.builtin_type);
}
}
} else {
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);
} 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_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(class_idx);
} break;
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(p_target.type.builtin_type);
} else {
// Either untyped assignment or already type-checked by the parser
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_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) {
append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
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_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_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_false(const Address &p_target) 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_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_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_false(const Address &p_target) = 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);
} else {
// 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) {
@ -1792,7 +1796,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (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) {
codegen.generator->pop_temporary();
}
@ -1930,7 +1938,11 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
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) {
codegen.generator->pop_temporary();
}

View file

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