fix(builtins): four silent-wrong-answer contracts — min/max N-ary, predicate type-rejection, negative indices, directory SIGABRT (#312, #314, #316, #317)#454
Merged
Conversation
…edicate type-rejection, negative indices, directory SIGABRT (#312, #314, #316, #317) Batch fix from the 2026-07 backlog triage — all four are silent wrong answers or a first-touch crash, so they outrank the #419 feature queue (correctness before performance, first-run success before features): - #317 min/max: true N-ary reductions over a flat numeric list. The old code read only items[0..1] — `max of [1, 2, 3]` returned 2 (worse than the issue reported) — and `min of [5]` returned 0. A bare number now returns itself (mirrors sum); empty/non-numeric lists keep the 0 fallback. - #316 string predicates: index_of/contains/starts_with/ends_with reject non-string operands (miss: -1 / 0) instead of folding them to "" and spuriously matching everything (index_of returned 0, "found at index 0", violating its documented "or -1" contract). - #312 get_at/set_at/char_at: negative indices count from the end, matching the [] operator's documented semantics, including the 2-D row/col paths. Out-of-range keeps each builtin's old miss default. - #314 read_file_util: reject non-regular files up front — fopen succeeds on a directory and ftell reports LONG_MAX, which sailed into xmalloc's fatal-OOM abort (exit 134). A directory script arg now takes the clean "cannot read file" path (exit 1); covers load_file and --fmt/--lint too. Tests: new tests/test_builtin_contracts.eigs (33 asserts, suite section [105] via check_eigs_suite) + a shell check that a directory arg exits 1; CA4 in test_string_math.eigs updated — it codified the old char_at bug. Docs: BUILTINS.md rows updated; SPEC/COMPARISON untouched (negative indexing was already the documented semantics — the builtins now conform). Gates: release suite 2536/2536; make asan + detect_leaks=1 2536/2536, leak tally 0. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BoHJFSkaaB2H1dvcdW2ibs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Batch-fixes the four good-first-issue correctness bugs surfaced by the 2026-07 backlog triage — all silent wrong answers or a first-touch crash, so they outrank the #419 feature queue (correctness before performance, first-run success before advanced features). All four live in
src/builtins.c.min/maxreturn0for a single-element list instead of that element #317min/maxare true N-ary reductions over a flat numeric list. The old code read onlyitems[0..1]—max of [1, 2, 3]returned 2, worse than the issue reported ([3,1,2]only worked by coincidence) — andmin of [5]returned0. A bare number now returns itself (mirrorssum); empty/non-numeric lists keep the0fallback.docs/BUILTINS.mdrows reconciled.index_of,contains,starts_with,ends_with) silently coerce non-string args to "" and report spurious matches #316 string predicates reject non-string operands.index_of/contains/starts_with/ends_withfolded non-strings to"", and an empty needle/prefix/suffix matches everything —index_of of [[1,2,3], 2]returned0("found at index 0"), violating its documented "or -1" contract. Misses now answer-1(index_of) /0(the predicates).[]operator's documented negative indexing #312get_at/set_at/char_athonor negative indices exactly like the[]operator (negative indexing is already documented SPEC semantics — the builtins now conform), including the 2-D row/col paths. Out-of-range indices keep each builtin's existing miss default.fopensucceeds on a directory andftellreportsLONG_MAX, which sailed intoxmalloc's fatal-OOM abort (exit 134).read_file_utilnow rejects non-regular files up front, so a directory takes the existingError: cannot read file '...'path (exit 1) — coveringload_fileand--fmt/--linttoo.Tests: new
tests/test_builtin_contracts.eigs(33 asserts) registered as suite section [105] viacheck_eigs_suite, plus a shell check that a directory arg exits 1 with the clean error.CA4intest_string_math.eigsupdated — it codified the oldchar_atnegative-index bug.Gates: release suite 2536/2536;
make asan+ASAN_OPTIONS=detect_leaks=12536/2536 with leak tally 0; doc-examples [89]/[90] and stdlib-discoverability [99b] green.Checklist
make testpasses locallydocs/BUILTINS.md(no new builtins; changed rows updated)docs/STDLIB.md(n/a — no new library functions)Closes #312. Closes #314. Closes #316. Closes #317.
🤖 Generated with Claude Code
https://claude.ai/code/session_01BoHJFSkaaB2H1dvcdW2ibs
Generated by Claude Code