Skip to content

Strict default misses tensor flattening: matmul/relu turn a non-number element into 0 silently (#1361 follow-up) #1416

Open

Description

@InauguralPhysicist

Found by the ouroboros AOT strict-mirror round (#1361). Measured on the strict-default build (main 5ccd32a content), flag unset, rc 0:

print of (matmul of [[[1, "x"]], [[1], [1]]])
print of (relu of [1, "x", -2])
[1]
[1, 0, 0]

The tensor-flattening helper (tensor_to_flat, used by matmul, relu and the other tensor builtins on list inputs) substitutes 0 for a non-number element. That is the argument-type stand-in #1361 made raise by default. The flip covered the ARG_GUARD/STRICT_REQUIRE sites and missed this one, because it is a conversion helper, not a guard. The ouroboros AOT mirrors the helper, so both tiers agree on the wrong answer.

Done when

  • With the flag unset, a non-number element in a list passed to any tensor builtin raises a catchable type error naming the builtin. Under EIGS_STRICT=0 the current answer is kept.
  • tools/strict_differential.sh gains probe rows for these (the unset-equals-strict half covers them), and they go red with the guard removed.
  • Every caller of the flattening helper is enumerated from the source, and the table goes here. No other conversion helper carries the same silent stand-in; the search is shown.
  • The ouroboros AOT mirror is updated in the same pin-bump round (file there if needed).

Activity

  1. InauguralPhysicist commented on Oct 2, 2026

    @InauguralPhysicist
    CollaboratorAuthor

    Runtime repair is ready in #1494 at 4501847bf31137eeb54b67cb0bab67cebfb8e24d. The caller population was derived from actual tensor_to_flat call expressions (excluding its definition and comments):

    Builtin Calls
    matmul 2
    matmul_at 2
    matmul_bt 2
    softmax 1
    log_softmax 1
    relu 1
    leaky_relu 1
    tensor_save 1

    Total: 11 calls across 8 builtins. Actual targeted validation passed93/93; strict differential passed119 probes in strict/unset/opt-out modes,31 pins and255 valid mode rows. The original early-return implementation failed the nine added cases; removing either scanner arm also failed its named coverage check.

    The conversion search is not clean. Reading tensor_to_flat, tensor_unary, tensor_elementwise, tensor_flatten_recursive and the byte-conversion helpers found a separate verified str_from_bytes gap and a source-only zlib_bytes_arg lead. Those are tracked in #1590; no zlib runtime result is claimed. tensor_unary/tensor_elementwise already have named ARG_GUARD paths; tensor_flatten_recursive drops unsupported values rather than inserting zero and still needs contract review.

    The current ouroboros strict-default PR#262 still contains numeric-or-zero conversion in both list paths of aot_tensor_from_value (source inspection at1fa3e0296265b6b3193c9e72455f651414546043). ouroboros#263 tracks the required mirror in the pin-bump round. This issue stays open until the remaining acceptance work is reconciled.

  2. InauguralPhysicist commented on Oct 4, 2026

    @InauguralPhysicist
    CollaboratorAuthor

    The #1590 byte-conversion follow-up is implemented in #1616, candidate ecd0201589532ef165339440f60e325b1c9ea60f. The scoped contract is now documented: str_from_bytes and the four DEFLATE codec entry points reject consumed nonnumeric list elements under unset/explicit strict with a builtin-named, catchable type_mismatch; EIGS_STRICT=0 retains numeric-zero substitution. Text conversion stops at a numeric value whose byte conversion is NUL and does not inspect later elements; codecs consume the full list, including elements after numeric zero.

    Validation records show original text regression red (9/12), candidate enrolled section 42p green in release (12/12), leak-enabled ASan/UBSan (12/12), zlib (23/23), and 24/24 independent exact-output controls. A subsequent enabled-codec calibration rebuilt the original builtins_buf.c against the same-base zlib objects: 16/23 passed, with all seven expected strict text/codec checks failing. Restoring the candidate source byte-for-byte and rebuilding returned 23/23. Full required CI and downstream acceptance remain pending. The cross-repo ownership audit itself was source-only.

    AOT ownership was checked at ouroboros main 4b43ed2d248cad968bb7e50264c87c62b03c9692 and draft #262 1fa3e0296265b6b3193c9e72455f651414546043. Neither has a byte-conversion specialization: generic builtin emission in aot/compile.eigs goes through aot_call_dispatch / aot_call_vm_builtin to the linked runtime builtin, and aot/build.sh derives/links the runtime sources. Consequently no separate AOT byte-helper mirror is indicated; the pin bump must include #1616 and verify forwarding behavior under unset/strict/compat modes. The standard AOT build leaves zlib disabled, so enabled-codec parity requires an explicitly matched enabled runtime/link lane; the default lane only exercises the unavailable stubs.

    The distinct AOT-owned aot_tensor_from_value still has numeric-or-zero list conversion in both dimensions (main aot/aot_rt.h:1812,1822; draft :1961,1971). ouroboros #263 already owns that mirror; coordinate it with #243 and #262 rather than duplicating tracking. The current Dockerfile pin remains v0.43.0 (a6c50fba6a6250ea347a34500d6c9fa503a5c931). Mirror changes and the pin bump must land together, followed by actual byte-exact differentials and the seven-tier verdict on a clean worktree at the new pin.

    This does not yet satisfy the broad “no other conversion helper” assertion: candidate f64_from_bytes still leaves nonnumeric slots as zero (src/builtins_buf.c:262–269), while tensor_flatten_recursive still skips unsupported values (src/builtins_tensor.c:367–378). Those are source-only residual contract questions; #1590 does not repair them. Keep #1416 open until those audit boundaries and the existing AOT mirror acceptance are reconciled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:runtime-vmSubsystem: runtime-vmkind:silent-wrongWrong answer with rc 0 -- the worst class; outranks every other kind

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions