fix(ergo-sigma): pre-v3 trees auto-upcast mixed-kind numeric operands - #54
Conversation
|
Warning Review limit reached
More reviews will be available in 43 minutes and 58 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThis PR adds automatic implicit numeric upcasting to ErgoTree evaluation for versions before v3. When evaluating mixed-kind numeric operands (e.g., Int + Long), the system ranks the numeric types and widens the lower-ranked operand to match the higher-ranked one before executing arithmetic or comparison operations, charging cast cost in the process. ChangesPre-v3 numeric auto-upcasting
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ergo-sigma/src/evaluator/opcodes/comparison.rs`:
- Around line 121-123: The EQ/NEQ path still allows post-v3 mixed numeric kinds
to reach require_comparable/eq_with_cost; add a guard after
apply_pre_v3_auto_upcast(l,r, cx) that detects sigma/version >= 3 and differing
numeric kinds and returns an evaluation error (same rejection behavior as Scala)
instead of proceeding to require_comparable. Implement the check in
comparison.rs within the EQ/NEQ handling: inspect the resolved Values (l, r),
use the existing context version flag on cx.ctx (or equivalent) to detect v3+,
detect mixed numeric kinds (e.g., Int vs Long/BigInt or type-kind mismatch), and
return an Err that mirrors deserialization rejection so mixed-kind numeric
equality is rejected before calling require_comparable or eq_with_cost.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e23e688a-7990-4a33-a24f-f1b31274efd9
📒 Files selected for processing (4)
ergo-sigma/src/evaluator/opcodes/arithmetic.rsergo-sigma/src/evaluator/opcodes/cast.rsergo-sigma/src/evaluator/opcodes/comparison.rsergo-sigma/src/evaluator/tests.rs
Scala DeserializationSigmaBuilder.applyUpcast (SigmaBuilder.scala:
741-756) auto-inserts an Upcast node on the narrower operand of a
mixed-kind numeric two-operand op at DESERIALIZATION when the tree
version is < 3 ('since v3 trees, Upcast nodes are not inserted
automatically' — the comment documents the consensus history).
Exactly three builder families route through it
(SigmaBuilder.scala:678-705): arithOp (Plus/Minus/Multiply/Divide/
Modulo/Min/Max), comparisonOp (GT/GE/LT/LE) and equalityOp (EQ/NEQ);
BitOp constructs directly and is not upcast. upcastTo is a no-op on
the already-max operand (syntax.scala:174), so exactly one node is
inserted per op.
We rejected such trees ('matching numeric types' TypeError) — a
false-reject on the mainnet-reachable v0-v2 surface: a crafted
pre-v3 tree with Int+Long validates in Scala and split us off.
This was the harness's last standing coal, previously misjudged as
a mis-blessed vector; the harness audit traced the bless to genuine
consensus behavior (cost 35 = Const 5 + Upcast 10 + Const 5 +
Plus 15) and sigma-rust ships the same coercion.
Implemented at EVAL time (parse-time node insertion would break
byte-identical reserialization): apply_pre_v3_auto_upcast widens the
narrower operand to the other's kind after both operand evals,
charging the Upcast NumericCastCostKind (10; 30 for a BigInt target)
exactly once, gated on !is_v3_ergo_tree(). Wired into all 13
affected eval fns. The upcast runs before add_arith_cost so the
BigInt arith rate keys on the upcast kind, matching the Scala node
type after builder rewriting.
Closes the last coal: ArithOp.numeric_kind_mismatch
int_long_coerced#0 — 2501 nice / 0 coal / 2501.
Equality additionally enforces Scala equalityOp's SameTypeConstrain
for the numeric kinds (reject_mixed_numeric_equality): at v3+ no
upcast happens and a mixed-kind EQ/NEQ previously fell through to
PartialEq's catch-all — EQ false and NEQ TRUE, validating a script
Scala rejects at deserialization (accept-vs-reject divergence,
flagged by CodeRabbit). Post-upcast mixed numeric kinds only exist
at v3+, so the guard is self-gating. Remaining residuals (no
vectors): non-numeric SameTypeConstrain mismatches (collection/tuple
carriers are not 1:1 with static types — a runtime guard could
falsely reject); mixed-kind arith/comparison at v3+ errors in both
implementations (eval-time here, parse-time there).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8cc67f9 to
c881649
Compare
|
Addressed in c881649 — verified against the Scala source first: Valid, with two refinements over the suggestion. Scala's Refinements:
Pinned by |
The last coal — and a reversed verdict
This vector was previously classified as mis-blessed ("JVM coerces" looked like a compiler artifact). The harness audit traced the bless to genuine consensus behavior, and the Scala source confirms it:
DeserializationSigmaBuilder.applyUpcast(SigmaBuilder.scala:741-756) auto-inserts anUpcastnode on the narrower operand of a mixed-kind numeric two-operand op at deserialization when the tree version is < 3:Exactly three builder families route through it (
SigmaBuilder.scala:678-705):arithOp(Plus/Minus/Multiply/Divide/Modulo/Min/Max),comparisonOp(GT/GE/LT/LE),equalityOp(EQ/NEQ).BitOpconstructs directly — not upcast.upcastTois a no-op on the already-max operand (syntax.scala:174), so exactly one node per op. The blessed cost reconciles: 35 = Const 5 + Upcast 10 + Const 5 + Plus 15. sigma-rust ships the same coercion.We rejected such trees ("matching numeric types" TypeError) — a false-reject on the mainnet-reachable v0–v2 surface: a crafted pre-v3 tree with
Int + Longvalidates in Scala and would split us off the chain.Fix
apply_pre_v3_auto_upcastincast.rs, applied at eval time (parse-time node insertion would break our byte-identical reserialization invariant): after both operand evals, the narrower operand widens to the other's kind, charging the UpcastNumericCastCostKind(10; 30 for a BigInt target — it's aTypeBasedCost, not flat) exactly once, gated on!is_v3_ergo_tree(). Wired into all 13 affected eval fns; the upcast runs beforeadd_arith_costso the BigInt arith rate keys on the upcast kind, matching the Scala node type after builder rewriting.UnsignedBigIntnever participates (v6-only carrier, unreachable pre-v3).Vectors
Tests (411 total, +5)
pre_v3_plus_mixed_kinds_auto_upcasts— Long(3) at v0; TypeError at v3 (gate both directions)pre_v3_auto_upcast_charges_numeric_cast_cost— twin-tree deltas: +10 fixed-width target, +30 BigInt targetpre_v3_comparison_mixed_kinds_auto_upcasts— GT with the right operand narrower (covers the r-side widening branch)pre_v3_equality_mixed_kinds_auto_upcasts— EQ/NEQ (without upcast the carriers differ and PartialEq would yield a wrongfalse, not an error)pre_v3_min_mixed_kinds_auto_upcasts— result at the wider kindReview notes
codex raised a charge-ordering point anchored on our
eval_upcast's charge-first order — but Scala'sUpcast.evalevaluates its input then charges (trees.scala:402-407), so the anchor was backwards. The genuine narrow residual (left-operand upcast charge lands between operand evals in Scala, after both here) is documented in the helper: totals are identical on every success path, a budget breach fires in both implementations, and exact charge-point parity is structurally unavailable at eval time (Scala places the node from static types at deserialization). Known pre-existing residual (no vector): at v3+, mixed-kind EQ/NEQ returnsfalsehere while Scala rejects at deserialization (SameTypeConstrain).cargo test -p ergo-sigma --lib(411) andcargo test -p ergo-ser --lib(324) green;cargo fmt --allapplied.🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
Bug Fixes
Tests