Skip to content

Array slicing isn't properly tail recursive #831

@flwyd

Description

@flwyd

When I run this Jsonnet program on the following input with a stack size lower than about 3000, I get a stack overflow error which consists mostly of lines like <std>:231:22-42 thunk from <thunk from <function <build>>>. The build function in std uses tailstrict for recursive calls, but it doesn't seem to be working in this case. Maybe the + operation in the arguments is causing the issue? tailstrict is working in general; when run with a stack size of 3000 this program completes successfully, but if the tailstrict calls within the program are removed, it overflows even that stack size, with lots of bifurcate instances in the stack.

The bifurcate function recursively passes a stack of work to perform as the last argument to the function. local cur = stack[0] examines the top item on the stack; if it doesn't need to recurse further then it updates a cache and recurses with stack[1:]. If cur needs additional work, its sub-problems are put into an array and it recurses with next + stack. This is a Lisp cons style approach, but Jsonnet arrays aren't single-linked lists, so I'm not surprised that it's inefficient, just that the intended tail call elimination isn't happening.

Input (/tmp/day10/line7.txt), which is one challenging line of input from https://adventofcode.com/2025/day/10:

[.#.....##.] (0,3,6,9) (1,2,4,6,7,8,9) (0,5,9) (0,1,2,4,7,8) (0,6,7,8,9) (3,4,5,6,7,8) (0,3,5,7) (2,3,4,6,7) (2,3,4,5,6,7,8) (1,2,6,9) (2,7,8) (0) (0,2,3,4,5,6,7,9) {71,46,95,181,172,148,199,205,151,71}

Run: jsonnet -t 128 -s 1000 -S ./runner.jsonnet --tla-code-file 'solve=day10/day10.jsonnet' --tla-code 'files=[{name:'\''/tmp/day10/line7.txt'\'', content:importstr '\''/tmp/day10/line7.txt'\'', },]'

Output:

RUNTIME ERROR: max stack frames exceeded.
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        ...
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        <std>:231:22-42 thunk from <thunk from <function <build>>>
        day10/day10.jsonnet:83:17-25    thunk <cur> from <function <bifurcate>>
        day10/day10.jsonnet:84:32-35
        day10/day10.jsonnet:66:80-82    thunk from <function <levelParity>>
        day10/day10.jsonnet:66:38-83
        day10/day10.jsonnet:66:25-84    function <levelParity>
        day10/day10.jsonnet:84:20-43    thunk <parity> from <function <bifurcate>>
        day10/day10.jsonnet:85:43-49    thunk from <thunk <ways> from <function <bifurcate>>>
        <std>:1545:24-25        thunk from <function <anonymous>>
        <std>:1545:5-33 function <anonymous>
        day10/day10.jsonnet:85:21-50    thunk <ways> from <function <bifurcate>>
        day10/day10.jsonnet:90:16-20    thunk <halves> from <function <bifurcate>>
        day10/day10.jsonnet:(86:20)-(90:77)
        day10/day10.jsonnet:91:54-60    thunk from <function <bifurcate>>
        day10/day10.jsonnet:91:16-61
        <std>:1642:24-27        thunk from <function <anonymous>>
        <std>:32:25-26  thunk from <function <anonymous>>
        <std>:32:16-27  function <anonymous>
        <std>:1642:12-28        function <anonymous>
        day10/day10.jsonnet:91:8-62     function <bifurcate>
        day10/day10.jsonnet:95:7-54     function <bifurcate>
        day10/day10.jsonnet:102:3-101   function <solvePart2>
        day10/day10.jsonnet:109:12-33   object <anonymous>
        day10/day10.jsonnet:114:15-22   thunk from <thunk from <object <anonymous>>>
        day10/day10.jsonnet:29:47-48    function <anonymous>
        day10/day10.jsonnet:29:18-57    function <sum>
        day10/day10.jsonnet:114:10-43   object <anonymous>
        runner.jsonnet:56:54-71 thunk from <object <anonymous>>
        runner.jsonnet:57:25-51 object <anonymous>
        runner.jsonnet:61:38-73 function <anonymous>
        <std>:259:50-62 function <anonymous>

        runner.jsonnet:61:3-82  function <anonymous>
        Top-level function call

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions