Skip to content

JIT: %json~ literal containing an array builds as {} (+ heap corruption at exit in the minimal repro); context-dependent #3690

Description

@borisbat

A %json~ ... %% reader-macro literal that contains an array anywhere in it builds as an empty object {} under -jit in some compilation contexts — and the minimal repro also corrupts the heap at exit. Interpreted execution is always correct.

Minimal repro

require daslib/json_boost

[export]
def main {
    var a = %json~ [1, 2, 3] %%
    print("bare array: {write_json(a)}\n")
    var b = %json~ { "xs" : [1, 2, 3] } %%
    print("nested array: {write_json(b)}\n")
}
$ bin/Release/daslang.exe probe.das          # interpreted — correct
bare array: [ 1, 2, 3 ]
nested array: { "xs" : [ 1, 2, 3 ] }

$ bin/Release/daslang.exe -jit probe.das     # JIT — both collapse, then heap corruption
bare array: {}
nested array: {}
deleting 000000DF186FE540 80, which is not a chunk pointer (or chunk size mismatch)
[daslang atexit] FATAL: g_envTotal=1 at exit (Initialize/Shutdown not balanced)

Object-only literals (no arrays anywhere) are fine under -jit.

Context-dependence — the interesting part

The same literal misbehaves or works depending on the compilation context, which smells like generic-instance selection/mangling rather than a plain emitter hole:

Context -jit result
Bare program root ([export] def main, repro above) broken ({} + heap corruption at exit)
Minimal dastest file (one [test], requires only testing_boost + json_boost) correct
tests/json/test_json_edge.das (has %json~ [1, 2, 3] %% at line ~656) correct, 128/128
utils/dasllama-ladder/test_ladder_store.das (dastest file with a wide require graph: sqlite_migrate + sql_linq + json_boost + a path-required module) broken — every array-bearing literal parses back as non-array

In the broken dastest case there is no crash, just wrong values; the crash shows on the program-root repro. Suspect: the reader macro's array construction lowers through to_array_move(JsonValue?[N]) / to_table_move(...) generic instances, and under -jit the wrong instance (or a bad specialization) gets bound in some instance mixes — possibly related to the known generic-mangling hash-collision class (instances differing only in inner qualifiers).

Environment

  • master 9fc37bc08 (2026-08-10) + a few site-only commits, Windows 11, MSVC Release build, dasLLVM JIT (-jit), fresh DLL cache (cache-miss codegen in every run above).
  • Found while writing utils/dasllama-ladder/test_ladder_store.das (the dasllama.io arc); that suite runs interpreted as a workaround (which is also the extended_checks lane for utils services, so nothing regressed in CI).

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