Skip to content

fix: deserialize FunDef (0xd7) — polymorphic val definitions#882

Open
mwaddip wants to merge 1 commit into
ergoplatform:developfrom
mwaddip:fix/fundef-deserialization
Open

fix: deserialize FunDef (0xd7) — polymorphic val definitions#882
mwaddip wants to merge 1 commit into
ergoplatform:developfrom
mwaddip:fix/fundef-deserialization

Conversation

@mwaddip

@mwaddip mwaddip commented Jun 6, 2026

Copy link
Copy Markdown

FUN_DEF (0xd7) had no parse arm: any tree with a polymorphic definition (val id[T] = ...) failed deserialization with NotImplementedOpCode — a box guarded by such a chain-valid v3 tree shape would wedge a node on spend.

Mirrors the JVM (ValDefSerializer, one node, two opcodes): ValDef gains tpe_args: Vec<STypeVar>, the opcode is picked by tpe_args.is_empty(), and the FunDef parse arm reads the signed count byte + type parameters (full type encoding) before the rhs. The construct evaluates with no eval changes; applying a lambda whose argument type is still a type variable rejects, mirroring the JVM's apply-time Unknown type T (Value.checkType inside the closure).

Surfaced by SANTA's v6 HOF conformance vectors: 2 accepts + 3 rejects now match the JVM exactly.

…ions

Opcode FUN_DEF (0xd7) was defined in op_code.rs but had no parse arm, so
any tree containing a polymorphic definition (val id[T] = ...) failed
deserialization with NotImplementedOpCode — a box guarded by such a
chain-valid tree shape would wedge a node on spend.

The JVM models FunDef as ValDef with non-empty type parameters
(ValDefSerializer, registered under both opcodes): ValDef gains
tpe_args: Vec<STypeVar>, the opcode is chosen by tpe_args.is_empty()
at serialization, and the FunDef parse arm reads the count byte (signed,
mirroring the JVM's r.getByte() which fails on negative) plus the type
parameters with the full type encoding (putType/getType) before the rhs.

Evaluation of the *construct* (binding a polymorphic lambda) needs no
changes — BlockValue binds the rhs like any ValDef. Applying a lambda
whose argument type is still a type variable is rejected, mirroring the
JVM boundary: Value.checkType inside the closure reaches isValueOfType's
catch-all ('Unknown type T') at application time, never at binding.
Rust's Apply::new arg-type check already rejects this shape at parse;
the eval-side guard mirrors the JVM's own mechanism for Apply nodes
constructed programmatically.

Surfaced by SANTA's v6 HOF vectors (eval/v6/authored): the two accept
entries (concrete-arg apply -> Int 7; type-var lambda bound but never
applied -> Int 5) and three reject entries (id(7) through (x:T)=>x,
(x:T)=>5, (x:T)=>x+x) now all match the JVM.

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