Address review: revert := deep-copy change, keep reference-assign semantics

Reverts the arithmetic deep-copy added to := in chaiscript_eval.hpp,
restoring its original reference-assign behavior as requested.
The optimizer fix (heap-allocated loop counter) remains and is
sufficient to prevent the #635 segfault.

Updated tests to use correct expected values for reference-assign
semantics and added more thorough coverage: nested loops, multiple
async calls, and value-assign comparison.

Requested by @lefticus in PR #671 review.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
leftibot 2026-04-12 16:34:40 -06:00
parent f430afd470
commit 276fa9d13a
3 changed files with 56 additions and 14 deletions

View File

@ -473,11 +473,7 @@ namespace chaiscript {
}
} else if (this->text == ":=") {
if (params[0].is_undef() || Boxed_Value::type_match(params[0], params[1])) {
if (params[1].get_type_info().is_arithmetic() && params[0].get_type_info().is_arithmetic()) {
params[0].assign(Boxed_Number::clone(params[1]));
} else {
params[0].assign(params[1]);
}
params[0].assign(params[1]);
params[0].reset_return_value();
} else {
throw exception::eval_error("Mismatched types in equation");

View File

@ -1,7 +1,41 @@
// Verify that := performs a value copy, not an alias
var a = 10
var b = 20
a := b
++b
assert_equal(20, a)
assert_equal(21, b)
// Issue #635: optimized for loop with := must not produce dangling values
// := is reference-assign, so ret aliases i and sees the loop exit value
// Basic: capture loop variable via :=
var ret = 0
for (var i = 0; i < 100; ++i) {
ret := i
}
assert_equal(100, ret)
// Return from function containing optimized for loop
def loop_result() {
var ret = 0
for (var i = 0; i < 200; ++i) {
ret := i
}
return ret
}
assert_equal(200, loop_result())
// Multiple calls return consistent results
assert_equal(loop_result(), loop_result())
// Nested optimized for loops
var outer_val = 0
var inner_val = 0
for (var i = 0; i < 10; ++i) {
for (var j = 0; j < 10; ++j) {
inner_val := j
}
outer_val := i
}
assert_equal(10, outer_val)
assert_equal(10, inner_val)
// Value-assign (=) captures last body-iteration value, not exit value
var ret2 = 0
for (var i = 0; i < 100; ++i) {
ret2 = i
}
assert_equal(99, ret2)

View File

@ -1,3 +1,6 @@
// Issue #635: async + optimized for loop must not segfault from dangling pointer
// := is reference-assign, so ret aliases i and sees the loop exit value
var func = fun(){
var ret = 0;
for (var i = 0; i < 1000; ++i) {
@ -9,5 +12,14 @@ var func = fun(){
var&fut1 = async(func);
var fut2 = async(func);
assert_equal(999, fut1.get())
assert_equal(999, fut2.get())
assert_equal(1000, fut1.get())
assert_equal(1000, fut2.get())
// Multiple concurrent async calls to stress the scenario
var results = []
for (var n = 0; n < 5; ++n) {
results.push_back(async(func))
}
for (var n = 0; n < 5; ++n) {
assert_equal(1000, results[n].get())
}