diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 93471172dc3..2253e04afa2 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2195,101 +2195,39 @@ void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) { } void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { - bool list_resolved = false; + GDScriptParser::DataType variable_type; + GDScriptParser::DataType list_type; - // Optimize constant range() call to not allocate an array. - // Use int, Vector2i, Vector3i instead, which also can be used as range iterators. - if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) { - GDScriptParser::CallNode *call = static_cast(p_for->list); - GDScriptParser::Node::Type callee_type = call->get_callee_type(); - if (callee_type == GDScriptParser::Node::IDENTIFIER) { - GDScriptParser::IdentifierNode *callee = static_cast(call->callee); - if (callee->name == "range") { - list_resolved = true; - if (call->arguments.is_empty()) { - push_error(R"*(Invalid call for "range()" function. Expected at least 1 argument, none given.)*", call->callee); - } else if (call->arguments.size() > 3) { - push_error(vformat(R"*(Invalid call for "range()" function. Expected at most 3 arguments, %d given.)*", call->arguments.size()), call->callee); - } else { - // Now we can optimize it. - bool can_reduce = true; - Vector args; - args.resize(call->arguments.size()); - for (int i = 0; i < call->arguments.size(); i++) { - GDScriptParser::ExpressionNode *argument = call->arguments[i]; - reduce_expression(argument); + if (p_for->list) { + resolve_node(p_for->list, false); - if (argument->is_constant) { - if (argument->reduced_value.get_type() != Variant::INT && argument->reduced_value.get_type() != Variant::FLOAT) { - can_reduce = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, Variant::get_type_name(argument->reduced_value.get_type())), argument); - } - if (can_reduce) { - args.write[i] = argument->reduced_value; - } - } else { - can_reduce = false; - GDScriptParser::DataType argument_type = argument->get_datatype(); - if (argument_type.is_variant() || !argument_type.is_hard_type()) { - mark_node_unsafe(argument); - } - if (!argument_type.is_variant() && (argument_type.builtin_type != Variant::INT && argument_type.builtin_type != Variant::FLOAT)) { - if (!argument_type.is_hard_type()) { - downgrade_node_type_source(argument); - } else { - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, argument_type.to_string()), argument); - } - } - } + bool is_range = false; + if (p_for->list->type == GDScriptParser::Node::CALL) { + GDScriptParser::CallNode *call = static_cast(p_for->list); + if (call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) { + if (static_cast(call->callee)->name == "range") { + if (call->arguments.is_empty()) { + push_error(R"*(Invalid call for "range()" function. Expected at least 1 argument, none given.)*", call); + } else if (call->arguments.size() > 3) { + push_error(vformat(R"*(Invalid call for "range()" function. Expected at most 3 arguments, %d given.)*", call->arguments.size()), call); } - - Variant reduced; - - if (can_reduce) { - switch (args.size()) { - case 1: - reduced = (int32_t)args[0]; - break; - case 2: - reduced = Vector2i(args[0], args[1]); - break; - case 3: - reduced = Vector3i(args[0], args[1], args[2]); - break; - } - p_for->list->is_constant = true; - p_for->list->reduced_value = reduced; - } - } - - if (p_for->list->is_constant) { - p_for->list->set_datatype(type_from_variant(p_for->list->reduced_value, p_for->list)); - } else { - GDScriptParser::DataType list_type; - list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - list_type.kind = GDScriptParser::DataType::BUILTIN; - list_type.builtin_type = Variant::ARRAY; - p_for->list->set_datatype(list_type); + is_range = true; + variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = Variant::INT; } } } - } - GDScriptParser::DataType variable_type; - String list_visible_type = ""; - if (list_resolved) { - variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; - variable_type.kind = GDScriptParser::DataType::BUILTIN; - variable_type.builtin_type = Variant::INT; - list_visible_type = "Array[int]"; // NOTE: `range()` has `Array` return type. - } else if (p_for->list) { - resolve_node(p_for->list, false); - GDScriptParser::DataType list_type = p_for->list->get_datatype(); - list_visible_type = list_type.to_string(); + list_type = p_for->list->get_datatype(); + if (!list_type.is_hard_type()) { mark_node_unsafe(p_for->list); } - if (list_type.is_variant()) { + + if (is_range) { + // Already solved. + } else if (list_type.is_variant()) { variable_type.kind = GDScriptParser::DataType::VARIANT; mark_node_unsafe(p_for->list); } else if (list_type.has_container_element_type(0)) { @@ -2342,7 +2280,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { mark_node_unsafe(p_for->variable); p_for->use_conversion_assign = true; } else { - push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_visible_type, specified_type.to_string()), p_for->datatype_specifier); + push_error(vformat(R"(Unable to iterate on value of type "%s" with variable of type "%s".)", list_type.to_string(), specified_type.to_string()), p_for->datatype_specifier); } } else if (!is_type_compatible(specified_type, variable_type)) { p_for->use_conversion_assign = true; diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index 41f79ed9a49..486ffbe40a0 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -1542,16 +1542,35 @@ void GDScriptByteCodeGenerator::write_end_jump_if_shared() { if_jmp_addrs.pop_back(); } -void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) { +void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type, bool p_is_range) { Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type); - Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type); // Store state. for_counter_variables.push_back(counter); - for_container_variables.push_back(container); + + if (p_is_range) { + GDScriptDataType int_type; + int_type.has_type = true; + int_type.kind = GDScriptDataType::BUILTIN; + int_type.builtin_type = Variant::INT; + + Address range_from(Address::LOCAL_VARIABLE, add_local("@range_from", int_type), int_type); + Address range_to(Address::LOCAL_VARIABLE, add_local("@range_to", int_type), int_type); + Address range_step(Address::LOCAL_VARIABLE, add_local("@range_step", int_type), int_type); + + // Store state. + for_range_from_variables.push_back(range_from); + for_range_to_variables.push_back(range_to); + for_range_step_variables.push_back(range_step); + } else { + Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type); + + // Store state. + for_container_variables.push_back(container); + } } -void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_list) { +void GDScriptByteCodeGenerator::write_for_list_assignment(const Address &p_list) { const Address &container = for_container_variables.back()->get(); // Assign container. @@ -1560,16 +1579,33 @@ void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_list) { append(p_list); } -void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_conversion) { +void GDScriptByteCodeGenerator::write_for_range_assignment(const Address &p_from, const Address &p_to, const Address &p_step) { + const Address &range_from = for_range_from_variables.back()->get(); + const Address &range_to = for_range_to_variables.back()->get(); + const Address &range_step = for_range_step_variables.back()->get(); + + // Assign range args. + write_assign(range_from, p_from); + write_assign(range_to, p_to); + write_assign(range_step, p_step); +} + +void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_conversion, bool p_is_range) { const Address &counter = for_counter_variables.back()->get(); - const Address &container = for_container_variables.back()->get(); + const Address &container = p_is_range ? Address() : for_container_variables.back()->get(); + const Address &range_from = p_is_range ? for_range_from_variables.back()->get() : Address(); + const Address &range_to = p_is_range ? for_range_to_variables.back()->get() : Address(); + const Address &range_step = p_is_range ? for_range_step_variables.back()->get() : Address(); current_breaks_to_patch.push_back(List()); GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN; GDScriptFunction::Opcode iterate_opcode = GDScriptFunction::OPCODE_ITERATE; - if (container.type.has_type) { + if (p_is_range) { + begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_RANGE; + iterate_opcode = GDScriptFunction::OPCODE_ITERATE_RANGE; + } else if (container.type.has_type) { if (container.type.kind == GDScriptDataType::BUILTIN) { switch (container.type.builtin_type) { case Variant::INT: @@ -1665,19 +1701,30 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_ // Begin loop. append_opcode(begin_opcode); append(counter); - append(container); + if (p_is_range) { + append(range_from); + append(range_to); + append(range_step); + } else { + append(container); + } append(p_use_conversion ? temp : p_variable); for_jmp_addrs.push_back(opcodes.size()); append(0); // End of loop address, will be patched. append_opcode(GDScriptFunction::OPCODE_JUMP); - append(opcodes.size() + 6); // Skip over 'continue' code. + append(opcodes.size() + (p_is_range ? 7 : 6)); // Skip over 'continue' code. // Next iteration. int continue_addr = opcodes.size(); continue_addrs.push_back(continue_addr); append_opcode(iterate_opcode); append(counter); - append(container); + if (p_is_range) { + append(range_to); + append(range_step); + } else { + append(container); + } append(p_use_conversion ? temp : p_variable); for_jmp_addrs.push_back(opcodes.size()); append(0); // Jump destination, will be patched. @@ -1690,7 +1737,7 @@ void GDScriptByteCodeGenerator::write_for(const Address &p_variable, bool p_use_ } } -void GDScriptByteCodeGenerator::write_endfor() { +void GDScriptByteCodeGenerator::write_endfor(bool p_is_range) { // Jump back to loop check. append_opcode(GDScriptFunction::OPCODE_JUMP); append(continue_addrs.back()->get()); @@ -1710,7 +1757,13 @@ void GDScriptByteCodeGenerator::write_endfor() { // Pop state. for_counter_variables.pop_back(); - for_container_variables.pop_back(); + if (p_is_range) { + for_range_from_variables.pop_back(); + for_range_to_variables.pop_back(); + for_range_step_variables.pop_back(); + } else { + for_container_variables.pop_back(); + } } void GDScriptByteCodeGenerator::start_while_condition() { diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h index 0eae5c86747..592e2f993ae 100644 --- a/modules/gdscript/gdscript_byte_codegen.h +++ b/modules/gdscript/gdscript_byte_codegen.h @@ -144,6 +144,9 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator { List for_jmp_addrs; List
for_counter_variables; List
for_container_variables; + List
for_range_from_variables; + List
for_range_to_variables; + List
for_range_step_variables; List while_jmp_addrs; List continue_addrs; @@ -535,10 +538,11 @@ public: virtual void write_endif() override; virtual void write_jump_if_shared(const Address &p_value) override; virtual void write_end_jump_if_shared() override; - virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override; - virtual void write_for_assignment(const Address &p_list) override; - virtual void write_for(const Address &p_variable, bool p_use_conversion) override; - virtual void write_endfor() override; + virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type, bool p_is_range) override; + virtual void write_for_list_assignment(const Address &p_list) override; + virtual void write_for_range_assignment(const Address &p_from, const Address &p_to, const Address &p_step) override; + virtual void write_for(const Address &p_variable, bool p_use_conversion, bool p_is_range) override; + virtual void write_endfor(bool p_is_range) override; virtual void start_while_condition() override; virtual void write_while(const Address &p_condition) override; virtual void write_endwhile() override; diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h index 831daa5a695..2ff31d51312 100644 --- a/modules/gdscript/gdscript_codegen.h +++ b/modules/gdscript/gdscript_codegen.h @@ -148,10 +148,11 @@ public: virtual void write_endif() = 0; virtual void write_jump_if_shared(const Address &p_value) = 0; virtual void write_end_jump_if_shared() = 0; - virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0; - virtual void write_for_assignment(const Address &p_list) = 0; - virtual void write_for(const Address &p_variable, bool p_use_conversion) = 0; - virtual void write_endfor() = 0; + virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type, bool p_is_range) = 0; + virtual void write_for_list_assignment(const Address &p_list) = 0; + virtual void write_for_range_assignment(const Address &p_from, const Address &p_to, const Address &p_step) = 0; + virtual void write_for(const Address &p_variable, bool p_use_conversion, bool p_is_range) = 0; + virtual void write_endfor(bool p_is_range) = 0; virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation. virtual void write_while(const Address &p_condition) = 0; virtual void write_endwhile() = 0; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 74b04d36a8f..0bbdb1c0d1e 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -2045,20 +2045,64 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype(), codegen.script)); - gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script)); - - GDScriptCodeGenerator::Address list = _parse_expression(codegen, err, for_n->list); - if (err) { - return err; + // Optimize `range()` call to not allocate an array. + GDScriptParser::CallNode *range_call = nullptr; + if (for_n->list && for_n->list->type == GDScriptParser::Node::CALL) { + GDScriptParser::CallNode *call = static_cast(for_n->list); + if (call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) { + if (static_cast(call->callee)->name == "range") { + range_call = call; + } + } } - gen->write_for_assignment(list); + gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype(), codegen.script), range_call != nullptr); - if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) { - codegen.generator->pop_temporary(); + if (range_call != nullptr) { + Vector args; + args.resize(range_call->arguments.size()); + + for (int j = 0; j < args.size(); j++) { + args.write[j] = _parse_expression(codegen, err, range_call->arguments[j]); + if (err) { + return err; + } + } + + switch (args.size()) { + case 1: + gen->write_for_range_assignment(codegen.add_constant(0), args[0], codegen.add_constant(1)); + break; + case 2: + gen->write_for_range_assignment(args[0], args[1], codegen.add_constant(1)); + break; + case 3: + gen->write_for_range_assignment(args[0], args[1], args[2]); + break; + default: + _set_error(R"*(Analyzer bug: Wrong "range()" argument count.)*", range_call); + return ERR_BUG; + } + + for (int j = 0; j < args.size(); j++) { + if (args[j].mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } + } + } else { + GDScriptCodeGenerator::Address list = _parse_expression(codegen, err, for_n->list); + if (err) { + return err; + } + + gen->write_for_list_assignment(list); + + if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) { + codegen.generator->pop_temporary(); + } } - gen->write_for(iterator, for_n->use_conversion_assign); + gen->write_for(iterator, for_n->use_conversion_assign, range_call != nullptr); // Loop variables must be cleared even when `break`/`continue` is used. List loop_locals = _add_block_locals(codegen, for_n->loop); @@ -2070,7 +2114,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui return err; } - gen->write_endfor(); + gen->write_endfor(range_call != nullptr); _clear_block_locals(codegen, loop_locals); // Outside loop, after block - for `break` and normal exit. diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp index 5618053d839..56b9f1dc3c3 100644 --- a/modules/gdscript/gdscript_disassembler.cpp +++ b/modules/gdscript/gdscript_disassembler.cpp @@ -553,7 +553,7 @@ void GDScriptFunction::disassemble(const Vector &p_code_lines) const { case OPCODE_CONSTRUCT_ARRAY: { int instr_var_args = _code_ptr[++ip]; int argc = _code_ptr[ip + 1 + instr_var_args]; - text += " make_array "; + text += "make_array "; text += DADDR(1 + argc); text += " = ["; @@ -585,7 +585,7 @@ void GDScriptFunction::disassemble(const Vector &p_code_lines) const { type_name = Variant::get_type_name(builtin_type); } - text += " make_typed_array ("; + text += "make_typed_array ("; text += type_name; text += ") "; @@ -1181,9 +1181,25 @@ void GDScriptFunction::disassemble(const Vector &p_code_lines) const { incr += 5; } break; DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE_BEGIN); + case OPCODE_ITERATE_BEGIN_RANGE: { + text += "for-init "; + text += DADDR(5); + text += " in range from "; + text += DADDR(2); + text += " to "; + text += DADDR(3); + text += " step "; + text += DADDR(4); + text += " counter "; + text += DADDR(1); + text += " end "; + text += itos(_code_ptr[ip + 6]); + + incr += 7; + } break; case OPCODE_ITERATE: { text += "for-loop "; - text += DADDR(2); + text += DADDR(3); text += " in "; text += DADDR(2); text += " counter "; @@ -1194,6 +1210,20 @@ void GDScriptFunction::disassemble(const Vector &p_code_lines) const { incr += 5; } break; DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE); + case OPCODE_ITERATE_RANGE: { + text += "for-loop "; + text += DADDR(4); + text += " in range to "; + text += DADDR(2); + text += " step "; + text += DADDR(3); + text += " counter "; + text += DADDR(1); + text += " end "; + text += itos(_code_ptr[ip + 5]); + + incr += 6; + } break; case OPCODE_STORE_GLOBAL: { text += "store global "; text += DADDR(1); diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h index 10928077599..5d9ded8fd58 100644 --- a/modules/gdscript/gdscript_function.h +++ b/modules/gdscript/gdscript_function.h @@ -345,6 +345,7 @@ public: OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY, OPCODE_ITERATE_BEGIN_OBJECT, + OPCODE_ITERATE_BEGIN_RANGE, OPCODE_ITERATE, OPCODE_ITERATE_INT, OPCODE_ITERATE_FLOAT, @@ -366,6 +367,7 @@ public: OPCODE_ITERATE_PACKED_COLOR_ARRAY, OPCODE_ITERATE_PACKED_VECTOR4_ARRAY, OPCODE_ITERATE_OBJECT, + OPCODE_ITERATE_RANGE, OPCODE_STORE_GLOBAL, OPCODE_STORE_NAMED_GLOBAL, OPCODE_TYPE_ADJUST_BOOL, diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index 987694fc69f..37205a06a0b 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -343,6 +343,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR4_ARRAY, \ &&OPCODE_ITERATE_BEGIN_OBJECT, \ + &&OPCODE_ITERATE_BEGIN_RANGE, \ &&OPCODE_ITERATE, \ &&OPCODE_ITERATE_INT, \ &&OPCODE_ITERATE_FLOAT, \ @@ -364,6 +365,7 @@ void (*type_init_function_table[])(Variant *) = { &&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \ &&OPCODE_ITERATE_PACKED_VECTOR4_ARRAY, \ &&OPCODE_ITERATE_OBJECT, \ + &&OPCODE_ITERATE_RANGE, \ &&OPCODE_STORE_GLOBAL, \ &&OPCODE_STORE_NAMED_GLOBAL, \ &&OPCODE_TYPE_ADJUST_BOOL, \ @@ -3345,6 +3347,39 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_ITERATE_BEGIN_RANGE) { + CHECK_SPACE(6); + + GET_VARIANT_PTR(counter, 0); + GET_VARIANT_PTR(from_ptr, 1); + GET_VARIANT_PTR(to_ptr, 2); + GET_VARIANT_PTR(step_ptr, 3); + + int64_t from = *VariantInternal::get_int(from_ptr); + int64_t to = *VariantInternal::get_int(to_ptr); + int64_t step = *VariantInternal::get_int(step_ptr); + + VariantInternal::initialize(counter, Variant::INT); + *VariantInternal::get_int(counter) = from; + + bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0); + + if (do_continue) { + GET_VARIANT_PTR(iterator, 4); + VariantInternal::initialize(iterator, Variant::INT); + *VariantInternal::get_int(iterator) = from; + + // Skip regular iterate. + ip += 7; + } else { + // Jump to end of loop. + int jumpto = _code_ptr[ip + 6]; + GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); + ip = jumpto; + } + } + DISPATCH_OPCODE; + OPCODE(OPCODE_ITERATE) { CHECK_SPACE(4); @@ -3678,6 +3713,33 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a } DISPATCH_OPCODE; + OPCODE(OPCODE_ITERATE_RANGE) { + CHECK_SPACE(5); + + GET_VARIANT_PTR(counter, 0); + GET_VARIANT_PTR(to_ptr, 1); + GET_VARIANT_PTR(step_ptr, 2); + + int64_t to = *VariantInternal::get_int(to_ptr); + int64_t step = *VariantInternal::get_int(step_ptr); + + int64_t *count = VariantInternal::get_int(counter); + + *count += step; + + if ((step < 0 && *count <= to) || (step > 0 && *count >= to)) { + int jumpto = _code_ptr[ip + 5]; + GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size); + ip = jumpto; + } else { + GET_VARIANT_PTR(iterator, 3); + *VariantInternal::get_int(iterator) = *count; + + ip += 6; // Loop again. + } + } + DISPATCH_OPCODE; + OPCODE(OPCODE_STORE_GLOBAL) { CHECK_SPACE(3); int global_idx = _code_ptr[ip + 2]; diff --git a/modules/gdscript/tests/scripts/runtime/features/for_range_large_ints.gd b/modules/gdscript/tests/scripts/runtime/features/for_range_large_ints.gd new file mode 100644 index 00000000000..36a1525cc6a --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_range_large_ints.gd @@ -0,0 +1,7 @@ +# GH-83293 + +func test(): + for x in range(1 << 31, (1 << 31) + 3): + print(x) + for x in range(1 << 62, (1 << 62) + 3): + print(x) diff --git a/modules/gdscript/tests/scripts/runtime/features/for_range_large_ints.out b/modules/gdscript/tests/scripts/runtime/features/for_range_large_ints.out new file mode 100644 index 00000000000..08932d9bab7 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/for_range_large_ints.out @@ -0,0 +1,7 @@ +GDTEST_OK +2147483648 +2147483649 +2147483650 +4611686018427387904 +4611686018427387905 +4611686018427387906