Skip to content

fix: reject non-unary lambdas at closure creation (JVM FuncValue.eval)#899

Open
mwaddip wants to merge 1 commit into
ergoplatform:developfrom
mwaddip:fix/funcvalue-non-unary-arity
Open

fix: reject non-unary lambdas at closure creation (JVM FuncValue.eval)#899
mwaddip wants to merge 1 commit into
ergoplatform:developfrom
mwaddip:fix/funcvalue-non-unary-arity

Conversation

@mwaddip

@mwaddip mwaddip commented Jun 10, 2026

Copy link
Copy Markdown

The JVM supports only unary functions at eval: FuncValue.eval (values.scala:1042-1056) builds its closure iff args.length == 1 and otherwise throws "Function must have 1 argument" — at closure creation, so a bound-but-never-applied non-unary lambda rejects too (BlockValue evaluates ValDefs eagerly). Apply.eval gates application arity the same way, which sigma-rust already matches.

sigma-rust evaluated non-unary lambdas to completion. Both serializers are arity-agnostic, so such trees are reachable from wire bytes — an accept/reject consensus divergence.

Gates Value::Lambda creation (the single construction site) on args.len() == 1. Eval-time only: a non-unary lambda on the dead branch of a lazy if still accepts. Tests drive the four JVM-blessed vector byte trees end-to-end.

The JVM supports only unary functions at eval: FuncValue.eval
(values.scala:1042-1056) builds its closure iff args.length == 1 and
otherwise throws "Function must have 1 argument" — at closure CREATION,
so a bound-but-never-applied non-unary lambda rejects too (BlockValue
evaluates ValDefs eagerly). Apply.eval (values.scala:1236-1244) gates
application arity the same way, which sigma-rust already matches.

sigma-rust created the Lambda value for any arity and evaluated
non-unary lambdas to completion. Both serializers are arity-agnostic
(FuncValueSerializer reads a numArgs VLQ), so such trees are reachable
from real wire bytes — an over-accept / chain-split divergence: a tree
carrying a 2-arg (or 0-arg) lambda validates on sigma-rust and fails
on the JVM.

Gate Value::Lambda creation (the single construction site) on
args.len() == 1. The reject is eval-time, NOT parse-time: a non-unary
lambda on the dead branch of a lazy `if` still accepts (pinned by the
vector twin).

Tests drive the four SANTA FuncValue.non_unary_arity vector byte trees
end-to-end (parse → proposition → eval): 2-arg applied, 2-arg bound,
0-arg applied each reject; the lazy-if twin accepts Int 5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant