From 379b9b8b66c64224914489850a398e4c676758e9 Mon Sep 17 00:00:00 2001 From: x Date: Wed, 7 Oct 2026 04:04:22 -0500 Subject: [PATCH] release: cut v0.45.0 The boolean type (#1637), phase 1 of the value-model release (#1644). This is a breaking change; the CHANGELOG lists the migrations. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01KC99CmwatKssQYggkBCgwF --- CHANGELOG.md | 47 +++++++++++++++++++++++++++ VERSION | 2 +- changes/breaking/1637-boolean-type.md | 42 ------------------------ 3 files changed, 48 insertions(+), 43 deletions(-) delete mode 100644 changes/breaking/1637-boolean-type.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 94474dff..6ca4d150 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,53 @@ All notable changes to EigenScript are documented here. ## [Unreleased] +## [0.45.0] - 2026-10-07 + +### Breaking changes + +- **A distinct `bool` type (#1637).** Comparisons and `not` return `true`/`false` of type `bool` + instead of the numbers `1`/`0`. Truthiness is unchanged (`false` is falsy, `true` truthy). + Arithmetic on a bool raises, and so does `==`/`!=` between a bool and a number: migrate + `(pred of x) == 1` to `if pred of x:` or `== true`. + `list_contains`/`list_index_of` raise on bool vs number the same way. Any + builtin given a bool where it does not take one raises, even under + `EIGS_STRICT=0`: as its argument, at a position of its argument list, or + as an element of a list it reads as numbers. The slots that take a bool are + listed in `tests/bool_fuzz_anyvalue.txt`. A bool slice bound, index, `range` + bound or `at` line raises. Deep `==` stops at the first unequal pair, and a + bool/number pair raises when the walk reaches it. Trace tapes are format v6; + a v5 tape is refused, and so is a record of a kind its builtin cannot + return. Embedding hosts declare a recorded name's kinds with + `eigs_trace_declare_kind`. `db_execute`/`db_query_value`/`db_query_json` take a bool + parameter and bind it as an SQL boolean, so a bool read from a boolean + column can be written back. `json_build` writes a bool value as JSON + `true`/`false`, and `what is` a bool answers the bool. The embedding API's + `eigs_value_as_num` answers NaN for a bool (0.0 for any other non-number). +- **Wrong-typed numbers that used to be read as garbage now raise (#1637).** The + bool work routed every C number read through a checking accessor, which + also changes some non-bool cases: + - a string `at` line (`what is x at "s"`) raises; it answered null; + - a non-number net timeout (`net_accept`/`net_dial`/`net_recv`), a + non-number `http_serve` port, and a non-number code byte in a + `vm_run_bytecode`/`sandbox_run` descriptor raise; they were read as + garbage or as 0 (a non-number `param_count` placeholder such as `[]` + still means 0 parameters; only a bool there raises); + - the tensor mutators `numerical_grad*`/`sgd_update*` raise on a non-number + cell or row in every strict mode, `EIGS_STRICT=0` included: v0.44.0 read + a non-number cell as 0 (and `numerical_grad_rows`/`_cols` wrote into it + in place) and skipped a null row; + - a non-number `screen_put` color and a non-number, non-bool + `write_bytes` append flag raise in every strict mode (neither had a + documented `EIGS_STRICT=0` answer); + - a non-number cell `gather` selects raises under `EIGS_STRICT` (it was + 0); `EIGS_STRICT=0` keeps the 0. + `EIGS_STRICT=0` keeps every answer it documents for non-bool values: + `str_from_bytes`, `inflate`/`deflate` and the other byte-list builtins + still read a non-number element as 0 there (a bool raises in every mode). + Embedders that include `eigenscript.h`: a Value's number is `VAL_NUM_RAW(v)` + and a slot's is `SLOT_NUM_RAW(s)` (the members were renamed so an unchecked + read does not compile). + ## [0.44.0] - 2026-10-05 - HTTP routes and the static root are frozen once `http_serve` starts (#1140). A `code` route's worker points at the spawning server, so `http_route`, `http_route_authed` or `http_static` called from one wrote the live route table while every other worker read it lock-free: one request could register a route for the whole process, and `route_count++` with no fence let a concurrent lookup `strcmp` a half-filled slot (ThreadSanitizer: 10 reports on a /mutate-vs-/ hammer before, 0 after). The three builtins now raise "must register before http_serve" once serving, as `http_response_header` already did. An uncaught error in a `code` route's source now answers 500 with the generic body `{"error": "internal error"}` instead of `200 null`; the message goes only to the server's stderr, since it can carry paths or values a client must not see. diff --git a/VERSION b/VERSION index a8ab6c96..bcce5d06 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.44.0 +0.45.0 diff --git a/changes/breaking/1637-boolean-type.md b/changes/breaking/1637-boolean-type.md deleted file mode 100644 index f25d9db8..00000000 --- a/changes/breaking/1637-boolean-type.md +++ /dev/null @@ -1,42 +0,0 @@ -- **A distinct `bool` type (#1637).** Comparisons and `not` return `true`/`false` of type `bool` - instead of the numbers `1`/`0`. Truthiness is unchanged (`false` is falsy, `true` truthy). - Arithmetic on a bool raises, and so does `==`/`!=` between a bool and a number: migrate - `(pred of x) == 1` to `if pred of x:` or `== true`. - `list_contains`/`list_index_of` raise on bool vs number the same way. Any - builtin given a bool where it does not take one raises, even under - `EIGS_STRICT=0`: as its argument, at a position of its argument list, or - as an element of a list it reads as numbers. The slots that take a bool are - listed in `tests/bool_fuzz_anyvalue.txt`. A bool slice bound, index, `range` - bound or `at` line raises. Deep `==` stops at the first unequal pair, and a - bool/number pair raises when the walk reaches it. Trace tapes are format v6; - a v5 tape is refused, and so is a record of a kind its builtin cannot - return. Embedding hosts declare a recorded name's kinds with - `eigs_trace_declare_kind`. `db_execute`/`db_query_value`/`db_query_json` take a bool - parameter and bind it as an SQL boolean, so a bool read from a boolean - column can be written back. `json_build` writes a bool value as JSON - `true`/`false`, and `what is` a bool answers the bool. The embedding API's - `eigs_value_as_num` answers NaN for a bool (0.0 for any other non-number). -- **Wrong-typed numbers that used to be read as garbage now raise (#1637).** The - bool work routed every C number read through a checking accessor, which - also changes some non-bool cases: - - a string `at` line (`what is x at "s"`) raises; it answered null; - - a non-number net timeout (`net_accept`/`net_dial`/`net_recv`), a - non-number `http_serve` port, and a non-number code byte in a - `vm_run_bytecode`/`sandbox_run` descriptor raise; they were read as - garbage or as 0 (a non-number `param_count` placeholder such as `[]` - still means 0 parameters; only a bool there raises); - - the tensor mutators `numerical_grad*`/`sgd_update*` raise on a non-number - cell or row in every strict mode, `EIGS_STRICT=0` included: v0.44.0 read - a non-number cell as 0 (and `numerical_grad_rows`/`_cols` wrote into it - in place) and skipped a null row; - - a non-number `screen_put` color and a non-number, non-bool - `write_bytes` append flag raise in every strict mode (neither had a - documented `EIGS_STRICT=0` answer); - - a non-number cell `gather` selects raises under `EIGS_STRICT` (it was - 0); `EIGS_STRICT=0` keeps the 0. - `EIGS_STRICT=0` keeps every answer it documents for non-bool values: - `str_from_bytes`, `inflate`/`deflate` and the other byte-list builtins - still read a non-number element as 0 there (a bool raises in every mode). - Embedders that include `eigenscript.h`: a Value's number is `VAL_NUM_RAW(v)` - and a slot's is `SLOT_NUM_RAW(s)` (the members were renamed so an unchecked - read does not compile).