tvix: miscompilation in recursive formals

#255
Opened by tazjin at 2023-03-02T19·57+00

Recursive formals that capture their siblings through deferred upvalues can be miscompiled, for example:

tvix-repl> ({ b ? !a, a }: b) { a = true; } 
error[E006]: expected value of type 'bool', but found a 'internal[deferred_upvalue]'
 --> [code]:1:8
  |
1 | ({ b ? !a, a }: b) { a = true; } 
  |        ^^

This is compiled as such (note that this is on top of the generator chain, hence OpReturn is present):

=== compiled thunk @ 0x55ffb46f7170 (4 ops) ===
0x0      1  OpConstant("a"@0)
0x1      |  OpConstant(true@1)
0x2      |  OpAttrs(Count(1))
0x3      |  OpReturn
=== compiled thunk @ 0x55ffb46fde90 (2 ops) ===
0x0      1  OpGetUpvalue(UpvalueIdx(0))
0x1      |  OpReturn
=== compiled lambda @ 0x55ffb46fe4b0 (18 ops) ===
 0x0      1  OpForce
 0x1      |  OpValidateClosedFormals
 0x2      |  OpGetLocal(StackIdx(0))
 0x3      |  OpConstant("b"@0)
 0x4      |  OpAttrsTrySelect
 0x5      |  OpJumpIfNotFound(JumpOffset(1))
 0x6      |  OpJump(JumpOffset(4))
 0x7      |  OpThunkSuspended(ConstantIdx(1))
 0x8      |  DataDeferredLocal(StackIdx(2))
 0x9      |  OpForce
 0xa      |  OpInvert
 0xb      |  OpGetLocal(StackIdx(0))
 0xc      |  OpConstant("a"@2)
 0xd      |  OpAttrsSelect
 0xe      |  OpFinalise(StackIdx(1))
 0xf      |  OpGetLocal(StackIdx(1))
0x10      |  OpCloseScope(Count(3))
0x11      |  OpReturn
=== compiled thunk @ 0x55ffb46fad00 (5 ops) ===
0x0      1  OpConstant(thunk(0x55ffb46f7170)@0)
0x1      |  OpConstant(closure(0x55ffb46fe4b0)@1)
0x2      |  OpForce
0x3      |  OpCall
0x4      |  OpReturn
=== compiled toplevel @ 0x55ffb46faf80 (3 ops) ===
0x0      1  OpConstant(thunk(0x55ffb46fad00)@0)
0x1      |  OpForce
0x2      |  OpReturn

The large lambda in the middle has an issue between 0x5 and 0x9: Something about the jump being issued before we emit finaliser instructions upsets the balance. Need to investigate.

  1. Interesting discovery about this:

    tvix-repl> ({ b ? a * a, a }: b) { a = 2; }
    => 4 :: int
    

    but

    tvix-repl> ({ b ? !a, a }: b) { a = true; }
    error[E006]: expected value of type 'bool', but found a 'internal[deferred_upvalue]'
    

    and

    error[E006]: expected value of type 'number (either int or float)', but found a 'internal[deferred_upvalue]'
     --> [code]:1:8
      |
    1 | ({ b ? -a, a }: b) { a = 2; }
      |        ^^
    

    It only happens on unary operators? :thonking:

    tazjin at 2023-03-03T07·12+00

  2. Fixed in cl/8196

    tazjin at 2023-03-03T08·04+00

  3. tazjin closed this issue at 2023-03-14T09·18+00