You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+92Lines changed: 92 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,6 +9,98 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
10
10
---
11
11
12
+
## [0.4.3] - 2026-03-06
13
+
14
+
### Fixed
15
+
16
+
-**VM `is_some()` / `is_none()` were stubs that always returned `false`** — restored real ADT-aware logic for `Option<T>` values in `--vm` mode
17
+
-**VM `keys({})` returned an error on empty objects** — now correctly returns `[]` matching interpreter behaviour
18
+
-**VM `split(str, "")` did not split into characters** — empty delimiter now produces a char array (parity with interpreter)
19
+
-**VM `int(bool)` raised an error** — `true` → `1`, `false` → `0` now works in `--vm` mode
20
+
-**VM `sort()` only handled Int/Float** — String comparison and custom comparator function now supported
21
+
-**VM `ok()`/`err()` lowercase aliases silently fell through** — `"Ok" | "Some"` match arm appeared before `"ok"` alias, making lowercase calls return `unknown builtin`; arm order corrected
22
+
-**VM `float()` did not accept strings** — `float("3.14")` now parses correctly (parity with interpreter)
23
+
-**VM `entries({})` returned `Null` for empty object** — now returns `[]` (parity fix)
24
+
-**VM `find` / `flat_map` spawned a full Interpreter instance per call** — replaced with native VM loop implementations; no more per-call interpreter startup cost
25
+
-**VM missing builtins: `any`, `all`, `unique`, `sum`, `min_of`, `max_of`, `assert_ne`** — implemented natively in `vm/builtins.rs` AND registered in `vm/machine.rs` builtin registry (registration was the critical missing step — without it names resolved as `undefined variable`)
26
+
-**`pg.query` / `pg.execute` nested `block_on` deadlock** — the previous pattern `block_in_place(|| handle.block_on(async { rt.block_on(client.query) }))` is undefined/deadlock in Tokio; fixed by extracting a raw pointer to the client before `block_in_place`, then awaiting the query directly in the outer async block
27
+
-**`sus()` panic on no arguments** — `args.into_iter().next().unwrap()` → `unwrap_or(Value::Null)`
28
+
-**Parser `decorators.pop().unwrap()`** — replaced with `ok_or_else(ParseError)` to avoid panic on unexpected empty decorator list
29
+
-**8× `Mutex::lock().unwrap()` in interpreter `Environment`** — replaced with poison-recovery `lock().unwrap_or_else(|p| p.into_inner())` to prevent panic propagation if a spawned thread panics while holding the lock
30
+
-**Bare `unwrap()` in interpreter method dispatch path** (`mod.rs:1797`) — replaced with `unwrap_or(Value::Null)` to prevent panic on edge-case object mutation
31
+
-**Unsafe `unwrap()` in VM GetField handler** (`machine.rs:852`) — replaced with `expect("BUG: ...")` for better crash diagnostics
32
+
-**Compiler `loops.pop().unwrap()`** in While/Loop/For compile paths — replaced with `ok_or_else(CompileError)` to avoid panic on malformed AST
33
+
34
+
### Changed
35
+
36
+
- JIT `runtime.rs`: added `#![allow(dead_code)]` with explanatory comment — all unused functions are M2 NaN-boxing bridge infrastructure, intentionally kept ready
37
+
38
+
---
39
+
40
+
## [0.4.2] - 2026-01-15
41
+
42
+
### Fixed
43
+
44
+
-**Closure mutable capture (BUG-005)** — mutable variables captured in closures now persist mutations across invocations instead of resetting to the initial value
45
+
-**Unwrap safety sweep** — removed all bare `unwrap()` calls from production execution paths in `interpreter/builtins.rs` and `interpreter/call_builtin.rs`
46
+
-**LSP incremental sync** — fixed `textDocument/didChange` handler dropping partial edits in large files
47
+
-**REPL multi-line paste** — pasted blocks with embedded newlines no longer trigger premature evaluation
48
+
49
+
### Changed
50
+
51
+
- Extracted `call_builtin` and `call_native` into separate files (`interpreter/call_builtin.rs`, `vm/builtins.rs`) for readability — zero behaviour change
52
+
- Version bump: `0.4.1` → `0.4.2`
53
+
54
+
---
55
+
56
+
## [0.4.1] - 2026-01-08
57
+
58
+
### Added
59
+
60
+
-**`mysql` module** — `mysql.connect`, `mysql.query`, `mysql.execute`, `mysql.close` with parameterised queries and connection pooling (mirrors `pg` API)
forge test#integration tests pass (run after cargo build)
112
112
```
113
113
114
114
All 18 example files run successfully.
@@ -135,15 +135,35 @@ These rules are non-negotiable. Follow them on every change.
135
135
136
136
4.**Small, atomic commits.** One concern per commit. Never mix features.
137
137
5.**Tests before or alongside code.** Risky changes get tests first.
138
-
6.**No `unwrap()` in production paths.** Use `?` or proper error handling.
138
+
6.**No `unwrap()` in production paths.** Use `?` or proper error handling. If structurally impossible, use `expect("BUG: ...")` with an explanation.
139
139
7.**If it compiles but feels wrong, stop.** Check the design.
140
140
8.**Never remove a working execution path.** Interpreter, VM, and JIT must all keep working.
141
+
9.**VM parity is your responsibility.** When adding or fixing a builtin in the interpreter, port the same fix to `src/vm/builtins.rs`. The VM is not automatically in sync.
141
142
142
143
### After Every Change
143
144
144
-
9.**Run `cargo test`.** If tests fail, fix before committing.
145
-
10.**Run the examples.**`forge run examples/hello.fg` and `forge run examples/functional.fg` must pass.
146
-
11.**Check for regressions.** If you changed the VM, test with `--vm`. If you changed the JIT, test with `--jit`.
145
+
10.**Run `cargo test`.** If tests fail, fix before committing.
146
+
11.**Run the examples.**`forge run examples/hello.fg` and `forge run examples/functional.fg` must pass.
147
+
12.**Check for regressions.** If you changed the VM, test with `--vm`. If you changed the JIT, test with `--jit`.
148
+
13.**Update CHANGELOG.md.** Every PR that ships user-facing changes must have an entry under `[Unreleased]`. Format: `- Description of change ([#PR](link))`. On release, `[Unreleased]` is cut into a version block.
149
+
14.**Bump the version together.** When cutting a release, update `Cargo.toml` version, add CHANGELOG heading (e.g. `## [0.5.0] - 2026-03-06`), and tag the commit.
150
+
151
+
### CHANGELOG Format (Keep-a-Changelog)
152
+
153
+
```markdown
154
+
## [Unreleased]
155
+
### Added
156
+
- New feature X
157
+
### Fixed
158
+
- Bug Y in module Z
159
+
### Changed
160
+
- Behaviour of W
161
+
162
+
## [0.4.2] - 2026-01-15
163
+
...
164
+
```
165
+
166
+
Categories in order: `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`.
147
167
148
168
### Learnings (Append Here)
149
169
@@ -153,6 +173,11 @@ These rules are non-negotiable. Follow them on every change.
153
173
-**GitHub Actions runners:**`macos-13` is deprecated. Use `macos-latest` for both ARM and x86_64 targets.
154
174
-**Bytecode encoding:** Instructions are 32-bit. Format: `[op:8][a:8][b:8][c:8]` or `[op:8][a:8][bx:16]` or `[op:8][a:8][sbx:16]`. The `sbx` field is signed 16-bit stored as unsigned.
155
175
-**Constant dedup:**`Chunk::add_constant()` deduplicates via `identical()`. Don't add the same constant twice — it wastes the constant pool.
176
+
-**VM-interpreter parity is not automatic.** The two share no code. Every interpreter builtin fix must be manually ported to `src/vm/builtins.rs`. Known audit-tracked gaps: `sort()` string support, `split("")` char-splitting, `int(bool)`, `keys({})`, `is_some`/`is_none` — all fixed in March 2026.
177
+
-**`sort()` with custom comparator:**`sort_by` closure borrows `self` immutably but calling `self.call_value()` needs `&mut self`. Work around by collecting items first (releasing the `gc` borrow), then sort with `call_value` on cloned items.
178
+
-**GC borrow in closures:** Never call `self.alloc_string()` or `self.call_value()` inside a closure that still holds `self.gc.get()`. Always collect into a `Vec<String>` or `Vec<Value>` first to drop the GC borrow.
179
+
-**VM `TryCatch`:** The compiler currently drops the catch block (logs as TODO, M1.2.2). Until it's implemented, `--vm` mode does not catch runtime errors.
180
+
-**VM `Destructure`:** Similarly dropped by the compiler (M1.2.1). Any `let {a, b} = obj` in `--vm` mode is silently skipped.
0 commit comments