MOD-14427 Fix numeric operations on homogeneous array. Operation will now promote to the next type if results overflows#1520
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1520 +/- ##
==========================================
+ Coverage 77.94% 78.05% +0.11%
==========================================
Files 15 15
Lines 3935 3942 +7
==========================================
+ Hits 3067 3077 +10
+ Misses 868 865 -3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| _ => unreachable!(), | ||
| } | ||
| } | ||
| ret |
There was a problem hiding this comment.
Moved ret value reused after match consumption
High Severity
NumOpResult does not derive Copy, so match ret in the else branch moves ret. The code then attempts to return ret on line 243 after the if/else, which is a use-after-move. In the else path (promotion case), ret is consumed by the match to extract the value for num1_slice.insert(...), and is no longer available as the arm's return value. This affects every integer type variant expanded by the macro.
Additional Locations (1)
There was a problem hiding this comment.
You are wrong, the inner value of ret impl Copy so its ok
| match ret { | ||
| NumOpResult::I64(v) => num1_slice.insert(index, v)?, | ||
| NumOpResult::U64(v) => num1_slice.insert(index, v)?, | ||
| _ => unreachable!(), | ||
| } | ||
| } | ||
| ret |
There was a problem hiding this comment.
Bug: Use-after-move on ret
NumOpResult does not derive Copy (it can't, because INumber is not Copy). The match ret { ... } in the else branch moves ret, but then ret is used as the return value after the if/else block on line 243. Rust's borrow checker considers all branches — since ret is potentially moved in the else path, it's unavailable after the if/else. This should be a compile error.
The fact that i64/u64 inner values are Copy doesn't help — the outer enum is still consumed by the match.
Suggested fix: Match on a reference instead:
match &ret {
NumOpResult::I64(v) => num1_slice.insert(index, *v)?,
NumOpResult::U64(v) => num1_slice.insert(index, *v)?,
_ => unreachable!(),
}This borrows ret rather than consuming it, so ret remains available for the return on line 243.
There was a problem hiding this comment.
Again, read the comments above
| num1_slice.remove(index); | ||
| num1_slice.insert(index, new_val)?; | ||
| } | ||
| Ok(NumOpResult::F64(new_val)) |
There was a problem hiding this comment.
Behavioral change: Return value no longer reflects stored precision
Previously, the half-float and float paths returned the value as stored (after narrowing to $hf_type/$f_type), reflecting the actual precision loss:
// Old half-float:
*num1 = <$hf_type>::from_f64(new_val);
Ok(NumOpResult::F64(f64::from(*num1))) // returned the narrowed value
// Old float:
*num1 = new_val as $f_type;
Ok(NumOpResult::F64(*num1 as f64)) // returned the narrowed valueNow both paths return NumOpResult::F64(new_val) — the full f64 result, even when the stored value was narrowed to fit.
This means for non-promotion cases (where the value fits in f16/f32), the command returns a different value than what's actually stored. For example, an operation on an f16 array might return 1.000976... but actually store 1.0 in f16 precision.
Is this change intentional? If the goal is to always return the "mathematically correct" result regardless of storage precision, that's a valid design choice — but it should be documented. If not, consider returning f64::from(*num1) in the if narrowed.is_finite() branch (when the value fits without promotion).
…perations-wrap-results-for-smaller-typed-arrays-8-16-32-instead-of-promoting


Note
Medium Risk
Touches core numeric update logic for JSON values/typed arrays, changing overflow handling and type promotion behavior; regressions could affect arithmetic correctness across commands and numeric types.
Overview
Fixes
JSON.NUMINCRBY/JSON.NUMMULTBY/JSON.NUMPOWBYbehavior for homogeneous (typed) numeric arrays so results no longer silently overflow or produceinfin-place.Integer operations now compute via
i128and, when a result no longer fits the underlying typed-array element type, the element is replaced viaIArrayinsertion to trigger type promotion to the smallest fitting integer type (including correct handling of largeu64values and overflow errors at true limits). Float/half-float ops now validate finiteness and either promote to wider storage on narrowing overflow or raise numeric overflow when the computed result is non-finite.Adds extensive pytest coverage for typed-array promotion/overflow across
i8/u8/i16/u16/i32/u32/i64/u64andf16/bf16/f32/f64.Written by Cursor Bugbot for commit 09fe718. This will update automatically on new commits. Configure here.