Files
bux-lang/docs/QUALITY_PLAN.md
T
dimgigov adc5c743a6 fix: Array move ownership, selfhost -g flags, LSP workspace/symbol
Session 36: prevent UAF when Array is moved into a struct field (Nexus
headers), align selfhost cc flags with bootstrap debug/release, and add
workspace/symbol search to bux-lsp 0.6.

- Parser: zero headers after embedding so auto-drop is a no-op
- Request_WantsKeepAlive uses RequestHeader_Get again safely
- Selfhost: default -O0 -g; --release -O2 -DNDEBUG; BUX_CFLAGS
- LSP: workspace/symbol + smoke; version 0.6.0
2026-07-19 22:59:02 +03:00

33 KiB
Raw Blame History

Bux — План към „добър“ език (v0.5 → v1.0)

Дата: 2026-07-19
Текущо: v0.5.x — Nexus keep-alive, header Array ownership fix, LSP 0.6 workspace/symbol, selfhost -g
Цел: Език, с който се пишат реални проекти комфортно, безопасно (по избор) и с надежден toolchain.


Диагноза (къде сме)

Слой Състояние Оценка
Frontend (lex/parse) Пълен Pratt parser, recovery ★★★★☆
Sema / generics Monomorphization, trait bounds basic ★★★★☆
HIR → C Tuples + fat func ABI в bootstrap и selfhost ★★★★☆
Selfhost (src/) ~12k LOC, binary-identical loop, closures+tuples ★★★★★
Gradual ownership @[Checked], &/&mut, move, Drop, lifetime elision ★★★★☆
Concurrency M:N tasks + channels + async ★★★★☆
Stdlib Array/Map/Set/String/Iter HOF разширени ★★★★☆
Tooling LSP 0.5 hover/def/outline/refs/rename + fmt/test/doc ★★★★★
Ecosystem / registry path+git + file + HTTP index (bux search/add) ★★★★☆
Документация README + QUALITY_PLAN синхронизирани (2026-07-15) ★★★★☆

Силна ниша: gradual ownership (C-скорост на писане + opt-in Rust-safety).
Слабо място: ergonomics на stdlib + maturity на tooling + пълнота на borrow checker.


Какво значи „добър“ за Bux

  1. Ежедневен DX — колекции, string, assert, грешки, които разбираш за секунди.
  2. Предвидима безопасност@[Checked] да хваща 80% от UAF/double-borrow без lifetime hell.
  3. Selfhost като dogfood — компилаторът и apps (nexus, boko) са proof.
  4. Инструменти — fmt, test, LSP, package install без ръчна магия.
  5. Стабилна спецификация — LanguageRef = реалното поведение.

Не целим „по-добър Rust“. Целим единствения език с gradual safety + Go-стил concurrency без GC.


Фази

A — Ergonomics & Stdlib (P0) (core done)

# Задача Защо Статус
A.1 Array: Pop, Clear, IsEmpty, First, Last, Cap, Reserve Без това колекциите са неудобни (тази сесия)
A.2 String: IsEmpty, ReplaceAll Чести операции; само first-replace досега (тази сесия)
A.3 Os_Exit + Test_AssertEqString / richer asserts Тестове и CLI без raw bux_exit (тази сесия)
A.4 Map_Remove / Set polish Completeness на колекциите (тази сесия)
A.5 Iter: map/filter/fold върху closures Higher-order без boilerplate generic Iter_Map/Filter/Fold + Int aliases
A.6 Result helpers: Expect, UnwrapErr, Or По-малко match boilerplate (тази сесия)

B — Compiler Correctness (P0)

# Задача Защо Статус
B.1 Proper tuple types в C backend (T,U)Tuple_T_U struct + .0/.1 bootstrap + selfhost
B.2 Function pointer types func(T)->U fat ABI bootstrap + selfhost
B.3 Match expression lowering (literals, ranges, enums) Expression-context match → if-else bootstrap + selfhost
B.3b Pattern bindings (Some(value) => value) Payload idents bound in arm body bootstrap + selfhost
B.3c Match arm guards (p if cond => …) Bindings visible in guard; sequential found-flag lower bootstrap + selfhost
B.4 Closures multi-instance Fat BuxFn + heap env bootstrap + selfhost
B.4b Closures: || empty params + loop/return body Lexer || vs empty closure; while/break/return bootstrap + selfhost
B.5 По-добри diagnostics (snippet + hint) DX #1 за нови потребители
B.6 Bootstrap ↔ selfhost feature parity empty ||, match-as-expr, string interp f"..." bootstrap+selfhost

C — Gradual Ownership 2.0 (P1)

# Задача Защо Статус
C.1 Lifetime elision за common cases Без 'a в 90% от API-тата bootstrap + selfhost
C.2 Exclusive &mut vs shared & data-flow По-малко false negatives let-bound + use-while + call conflict
C.3 Auto-drop edge cases (early return, branches) RAII да е надежден bootstrap + selfhost
C.4 @[Release] zero-cost path документация + golden tests Killer story: safe default, free hot path partial (unchecked path + goldens)

D — Tooling (P1)

# Задача Защо Статус
D.1 LSP: hover, go-to-def, diagnostics IDE = adoption v0.6.0: refs/rename + workspace/symbol
D.2 bux fmt стабилен + CI check Единен style full-tree format + make fmt-check enforce
D.3 bux test с --filter, exit codes, summary table CI-friendly --filter / summary / exit 0|1
D.4 bux doc от /// comments Самодокументиращ се stdlib bootstrap+selfhost + make docs
D.5 Golden tests за stdlib modules Регресии без изненади tests/stdlib_golden/ + make test-stdlib

E — Ecosystem & v1.0 (P2)

# Задача Защо Статус
E.1 Package registry protocol (git/HTTP) bux add foo без path hacks local index + HTTP(S) URL cache + file/git + search
E.2 35 production-quality apps в apps/ Showcase 4 apps + make test-apps smoke (build + CLI)
E.3 Language freeze + semver policy Trust draft docs/SEMVER.md
E.4 Debugger/DWARF basics Systems audience #line.bux + -g / --release; make test-dwarf
E.5 Benchmarks vs C/Zig/Nim (micro + nexus) Marketing + regression micro + C/Nim/Zig twins + make bench-nexus (wrk)

Препоръчан ред на работа

A (stdlib ergonomics)  →  B (compiler holes)  →  C (ownership depth)
        ↓                        ↓
   D (tooling)  ←──────────  dogfood apps
        ↓
   E (v1.0 ecosystem)

Правило: всяка сесия ship-ва нещо runnable (stdlib API, fix, example), не само docs.


Acceptance criteria за „добър v1.0“

  • Всички examples + selfhost-loop + 3 apps минават на CI (apps: make test-apps ready)
  • Array/Map/String/Test API покрива 90% от ежедневните нужди
  • @[Checked] хваща use-after-move + double &mut + dangling return / elision fail
  • bux test + bux fmt + bux check са default developer loop (--filter / --check shipped)
  • LanguageRef синхронизиран с компилатора (incl. C.1 elision)
  • Поне един външен/temp проект build-ва с registry dep (tools/smoke_registry.sh + HTTP)

Сесия 1 (stdlib ergonomics)

  1. Array_Pop, Array_Clear, Array_IsEmpty, Array_First, Array_Last, Array_Cap, Array_Reserve
  2. String_IsEmpty, String_ReplaceAll
  3. Os_Exit
  4. Test_AssertEqString, Test_AssertNeqInt, Test_AssertEqBool
  5. Example + docs update

Сесия 2 (collections + tuples)

  1. Map_Remove / Map_Clear / Map_IsEmpty (+ StringMap)
  2. Set_Remove / Set_Clear / Set_IsEmpty
  3. Result_Expect / Result_UnwrapErr / Result_Or
  4. Option_Expect / Option_Or
  5. Tuples: (T, U)typedef struct { T _0; U _1; } Tuple_T_U + field access .0/.1
  6. Examples: tuples, func_ptr, map_remove

Сесия 3 (diagnostics + collections)

  1. Rust-style errors in bootstrap CLI: --> file:line:col, source snippet, ^ caret, = help: hints
  2. SourceLocation.file propagated from lexer
  3. Better caret for type-mismatch on let (points at initializer)
  4. Array_Contains / Array_IndexOf / Array_Extend
  5. Iter_AnyEq / Iter_AllEq / Iter_Collect
  6. Selfhost Diagnostic_Hint for common messages

Сесия 4 (diagnostics depth + LSP + strings)

  1. Multi-char underlines (^^^^^^^ under tokens/strings/idents)
  2. Quoted-name highlighting for undeclared identifier 'x'
  3. Golden error tests (tests/error_golden/, make test-errors)
  4. LSP runs buxc check and publishes real diagnostics
  5. String_IsBlank / String_Repeat

Сесия 5 (multi-instance closures)

  1. Fat function pointers for all func(...) types: BuxFn { code(env, args...), env }
  2. Capturing closures: heap-allocate env per creation site (independent instances)
  3. Capture-less closures + named funcs: adapters with env = NULL
  4. Calls through func values: f.code(f.env, args...)
  5. Example multi_closure.bux — MakeAdder(10)/MakeAdder(20) yield 11 and 21
  6. Selfhost parity: same fat ABI in src/hir_lower.bux + src/c_backend.bux (makers, adapters)

Сесия 6 (selfhost tuples)

  1. Parser: (T, U) types, (a, b) exprs, field access .0/.1
  2. Sema: tekTuple / ekTuple
  3. HIR lower → hStructInit of Tuple_int_int
  4. C backend: typedef struct Tuple_int_int { int _0; int _1; }
  5. Verified with buxc2 on examples/tuples.bux

Сесия 7 (Iter HOF)

  1. Iter_MapInt / FilterInt / FoldInt / ForEachInt / AnyInt / AllInt / SumInt
  2. Named funcs + capturing closures (fat ABI)
  3. Example iter_hof.bux (sum=15, product=120)
  4. Selfhost fix: pointer -> field access; no bogus bounds check on arr.data[len] (Push)

Сесия 8 (match expression + error goldens)

  1. Match expression lowering (B.3): pkLiteral, pkRange (a..b / a..=b), enum tags, wildcard
    • Bugfix: literal arms were always-true (else branch) → match 1 { 1=>10, 2=>20 } returned 10 for all
    • skMatch now also lowers via lowerMatch (void result)
  2. Expanded examples/pattern_matching.bux (enums + classify ranges + string arms)
  3. Error golden cases (tests/error_golden/):
    • parse_error — incomplete let x = ;
    • use_after_move@[Checked] + own String
    • double_mut_borrow — two &mut of same var
  4. run.sh normalize: absolute paths in "parse errors in …" lines

Сесия 9 (selfhost match — B.6 parity)

  1. AST: MatchArm linked list; Pattern.patChild1/2 for ranges; Expr.matchArms
  2. Parser: full parserParsePattern / parserParseMatchExpr (was skip-arms stub)
    • literals, _, ident, Enum::Variant / Enum::Variant(...), ranges
    • statement matchskExpr + ekMatch
  3. Sema: type-check subject + arms; propagate first-arm type to expr.refType
  4. HIR lower: Lcx_LowerMatch → alloca result + if-else stores (enum .tag / simple / literal / range)
  5. Last-expr return: Lcx_LowerBlock converts final skExpr into return (needed for func F() -> T { match ... })
  6. Verified: pattern_matching via buxc2; simple enum + ranges; selfhost-loop IDENTICAL ✓

Сесия 10 (pattern bindings — B.3b)

  1. Bootstrap sema: extractPatternBindings resolves enum payload field types from variant decl
  2. Bootstrap HIR: matchPatternBindingsalloca name; name = subject.data.Variant_i before arm body
  3. Match result type: fall back to currentFuncRetType when arm bodies only use bindings
  4. Multi-field enum layout: positional fields.len > 1 → nested struct in _Data union (no overlay)
  5. Selfhost: parse patArgs linked list; Sema_BindPattern; Lcx_PatternBindings in match arms
  6. Example: pattern_matching.bux uses Option::Some(value) => value (real binding, not opt.data.Some_0)
  7. Verified: bootstrap + buxc2 + all examples + error goldens + selfhost-loop IDENTICAL ✓

Сесия 11 (empty || closures + match-as-expr — B.4b)

  1. Bug: || -> int { ... } lexed as tkPipePipe (logical-or), not two tkPipe → empty-param closures failed to parse
  2. Fix bootstrap: primary of tkPipePipe: → zero-param ekClosure
  3. Fix selfhost: parserParseEmptyClosure for tkPipePipe
  4. Match-as-expr: expression-form match now skips newlines (same as statement form) → let x = match n { ... } works
  5. Verified control-flow in closure body: while/break, early return from loop, multi-return if-chain
  6. Pattern bind reuse: one alloca per binding name per function (patternBoundNames) so two matches can both use v
  7. Selfhost match-as-expr: let-init + binary operands expand yield blocks (Lcx_IsMatchYield / __binop_N)
  8. Examples: closure_control.bux, match_let.bux
  9. Verified: bootstrap + buxc2 + all examples + error goldens + selfhost-loop IDENTICAL ✓

Сесия 12 (string interpolation selfhost — B.6)

  1. Selfhost parser: parserParseStringInterp — interleaved lit/expr parts in callArgs, nested fragment parse via Lexer_Tokenize + sub-parser
  2. Selfhost sema/HIR: ekStringInterpString_Concat + String_FromInt/FromBool/FromFloat
  3. Bootstrap fix: f"plain" no longer keeps the f prefix in the literal; \{/\} preserved by lexer and unescaped by interp parser
  4. Lexer: \{ / \} allowed (bootstrap + selfhost) so LanguageRef escape rules work
  5. Compiler hygiene: original dense &&/|| in the large interp loop caused bootstrap OOM (~27GB) when compiling parser.bux — rewrite with simpler control flow
  6. Example: examples/string_interp.bux (name/int/bool/plain/escaped braces)
  7. Verified: bootstrap + buxc2 + all 41 examples + error goldens + selfhost-loop IDENTICAL ✓

Сесия 13 (LSP hover / go-to-def / outline — D.1)

  1. Richer symbol index: func signatures (params + -> Ret), let/var/const with types, struct/enum/union/interface/type/module
  2. Skip comments/strings during scan (no false func hits)
  3. Hover: markdown bux signature + kind; accurate word range
  4. Go-to-definition: current file + workspace index (scan .bux under rootUri)
  5. Document symbols (outline) via textDocument/documentSymbol
  6. didChange refreshes symbols immediately; didSave/didOpen still run buxc check diagnostics
  7. Fix: responses write to stdout (was writing to stdin stream → broken pipe)
  8. Version bux-lsp 0.2.0; smoke-tested via JSON-RPC

Сесия 14 (generic Iter map/filter/fold)

  1. Iter_Map<T,U> / Filter<T> / Fold<T,Acc> / Any / All / ForEach — fat func params + monomorphization
  2. Int aliases keep working: Iter_MapIntIter_Map<int,int>, …
  3. Bootstrap fixes:
    • call return type for local fat-func (f: func(T)->U) after mono (was always int → String map truncated pointers)
    • generic call Foo<T>(…) now type-checks args (closures get capture analysis)
  4. Selfhost fixes:
    • Lcx_SubstituteType recurses into tekFunc (was leaving BuxFn_U_T)
    • fat typedef emit covers cstr shapes + #ifndef guards
  5. Example: examples/iter_generic.bux (int↔String map, filter, fold, closures)
  6. Verified: bootstrap + buxc2 + selfhost-loop IDENTICAL ✓

Сесия 15 (struct/tuple patterns)

  1. Tuple patterns: match t { (a, b) => a + b } — bind subject._0 / ._1
  2. Struct patterns: Point { x: px, y: py } + shorthand Point { x, y }
  3. Bootstrap: matchPatternBindings for pkTuple/pkStruct; field types from struct decl; range registration of local tuple typedefs
  4. Selfhost: parse () / Name { … } patterns; Sema_BindPattern + Lcx_PatternBindings with Scope_Define
  5. Fix: operator-overload path treated String_Eq(null, "") as non-empty → crash on a + b after pattern bind
  6. Example: examples/struct_tuple_pat.bux
  7. Verified: bootstrap + buxc2 + selfhost-loop IDENTICAL ✓

Сесия 16 (match block arms + block expressions)

  1. Block-as-expression: let r = { let x = 1; x + 2 } — last skExpr is the value
  2. Multi-stmt match arms: 1 => { PrintLine("…"); let a = 10; a + 1 }
  3. Bootstrap: lowerBlock(..., asExpr) promotes last expression; nested enum/struct pattern bindings
  4. Selfhost: parse { … } as ekBlock; yield temps __blk_N; fix IsMatchYield null-strValue false positive; retTypeKind -2 for expr blocks
  5. Nested: Shape::Dot(Point { x, y }) works on bootstrap; selfhost covers struct/tuple/enum + block arms
  6. Example: examples/match_block.bux
  7. Verified: bootstrap + buxc2 + selfhost-loop IDENTICAL ✓

Сесия 17 (deeper nested patterns + multi-field enum layout)

  1. Multi-field payload type: nested struct Enum_Variant_Payload (suffix avoids clash with tag Enum_Variant)
  2. Bootstrap: full resolveTypeExpr for enum field types (tuples/pointers); sema synthetic field lookup on payload types; data.Two.Two_0 construction
  3. LIR C backend: emit tuple typedefs before enums that embed them; topo deps for *_Payload
  4. Selfhost: enum variants parse full types (parserParseType) — fixes Val((int,int)); store fieldTypeName*; structs before enums (e.g. Dot(Point))
  5. Patterns: Pair::Two(a, b), Box::Val((a, c)), Shape::Dot(Point { x, y }) + block arms
  6. Example: examples/nested_patterns.bux
  7. Verified: bootstrap + buxc2 + selfhost-loop IDENTICAL ✓

Сесия 18 (match arm guards — B.3c)

  1. Syntax: p if cond => body (also after ranges: 1..10 if x % 2 == 0)
  2. Parser fix (critical): isTypeArgListAhead treated x < 0 as generic when a later x > 0 existed in another arm → infinite parse. Stop lookahead on =>, keywords, literals, arithmetic, comparisons.
  3. Sema: bind inner pattern first; type-check guard as bool in arm scope
  4. HIR lower: sequential found flag (no shared if-else DAG):
    if (!found) { if (inner_cond) { binds; if (guard) { result = body; found = true; } } }
    
    Bindings are in scope for the guard expression.
  5. Selfhost: pkGuarded + patGuardExpr; same lower strategy; fix return match {…} to expand yield block before return
  6. Example: examples/match_guards.bux (ident/literal/range/enum payload guards)
  7. Verified: bootstrap + buxc2 + all examples + error goldens + selfhost-loop IDENTICAL ✓

Сесия 19 (generic HOF type inference)

  1. Bootstrap inferTypeArgs: structural unify of param TypeExpr vs arg Type
    • *Iter<T> / *Array<T> → extract T from pointee type args or mangled Array_int
    • func(T)->U → bind T/U from function-value type (not whole func as T)
    • bare Acc from init; multi-param Iter_Fold<T,Acc>
  2. Selfhost: improved Sema_InferGenericArgs + return-type subst after inference
    • Fix: ekCast must type-check operand (was skipping → no inference under as)
    • HIR fallback mono from first *Array/*Iter arg when count=0
  3. Works without explicit type args:
    • Array_Push(&nums, 1), Array_Get, Array_Len, Array_Iter
    • Iter_Map(&it, f), Iter_Filter, Iter_Fold, Iter_Any (int↔String)
  4. Example: examples/generic_infer_hof.bux
  5. Verified: bootstrap + buxc2 + all examples + selfhost-loop IDENTICAL ✓

Сесия 20 (LSP hover from real sema — D.1)

  1. bux-lsp 0.3.0 links bootstrap (--path:../bootstrap) and runs analyzeFull on open/save
  2. typeIndex: global scope (stdlib + file) → hover signatures with real types
  3. File-local priority: user decls override stdlib name collisions (Max<T> vs Math.Max)
  4. Locals: walk function bodies for let/var with explicit type annotations
  5. didChange: fast lightweight rescan; keeps previous typeIndex until save/hover refresh
  6. Hover shows bux signature + _kind_ · sema
  7. Smoke: Main() -> int, PrintLine(String) -> void, Max<T>(a: T, b: T) -> T

Сесия 21 (pattern binding shadowing)

  1. Problem: C/LIR function-scoped locals — nested Some(n) => match … Some(n) emitted store before int n, and let v + pattern v caused redeclaration.
  2. Bootstrap: every pattern binding → unique C name __pN_src via patternRenames map; body/guard idents rewritten; binds before body lower inside lowerMatch.
  3. Selfhost: same model — Lcx_BindPatIdent + patMapFrom/To rename table; arm-scoped push/pop of map.
  4. Semantics: nested pattern name shadows correctly; outer let v survives after match that binds v.
  5. Example: examples/pattern_shadow.bux
  6. Verified: bootstrap + buxc2 + all examples + selfhost-loop IDENTICAL ✓

Сесия 22 (Ownership 2.0 — C.2 + C.4 + *p= fix)

  1. C.2 Exclusive &mut data-flow (@[Checked]):
    • Track long-lived let-bound borrows (activeMutBorrows / activeSharedBorrows)
    • Reject: second &mut x, use/assign of x while mutably borrowed, &x while &mut live
    • Call-site temps conflict with existing let-bound borrows
  2. C.4 Golden tests:
    • tests/error_golden/exclusive_mut_let/
    • tests/error_golden/use_while_mut_borrow/
  3. Bugfix: *p = expr now stores through the pointer (was assigning to a temp) — ownership examples finally mutate correctly
  4. Example: examples/ownership_checked.bux (unchecked zero-cost + checked OK path)
  5. Verified: 7 error goldens + borrow_test + all examples + selfhost-loop

Сесия 23 (Ownership 2.0 — C.3 auto-drop early return / branches)

  1. Bootstrap auto-drop for @[Drop] + collections:
    • Parser: @[Drop] / @[Release] on structs and funcs (declAttrs)
    • autoDropFuncName + monomorphize Array_Drop/Free (etc.) so stdlib links
    • Inject Type_Drop(&x) on let via deferStmts
  2. Early return / multi-path:
    • Every return snapshots the full live defer stack (no clear-after-first-return)
    • Materialize return value before Drop (return a.id is not use-after-drop)
    • Move-on-return: skip Drop for a local returned by value (return out)
  3. Branch / loop scopes:
    • Bootstrap: lowerBlock scopes deferStmts — branch-local drops at block exit; siblings do not see each other
    • Selfhost C backend: CBE_EmitDefers keeps stack for multi-return; CBE_EmitAndPopDefersFrom pops branch/loop locals after if/while/loop
  4. Selfhost fixes: null-safe ret type; temp name counter; use function retTypeName for __retdrop_N
  5. Example: examples/drop_early_return.bux (Early + Branched + Scoped → 5 drops)
  6. Verified: bootstrap + buxc2 drop tests, 7 error goldens, key examples, selfhost-loop IDENTICAL ✓

Сесия 24 (tooling — D.2 fmt --check + D.3 test --filter)

  1. Bootstrap bux fmt (bootstrap/fmt.nim):
    • Indent-by-brace-depth formatter (parity with src/fmt.bux)
    • bux fmt [path...] writes; bux fmt --check exits 1 if any file would change
    • Collects single file or recursive .bux under directories
  2. Bootstrap bux test --filter:
    • --filter <s> / --filter=<s> — only run tests/*.bux whose name contains s
    • Summary table (PASS / FAIL[:code]) + Results: N passed, M failed, T total
    • Exit 0 all pass, 1 failures or no match
  3. Selfhost parity (src/cli.bux, src/fmt.bux):
    • Fmt_WouldChange / Fmt_CheckFile; Cli_Fmt(dir, checkOnly)
    • Cli_Test(dir, filter) with summary + skip count; filter skips Main package run
  4. CI hooks: make fmt-check smoke (clean→0, dirty→1); full-tree enforce deferred until a one-shot format pass on lib//examples/
  5. Idempotence fix: drop trailing split-empty so re-format is a no-op
  6. Verified: unit suite + ./buxc test --filter first _test_runner + selfhost buxc2 fmt/test parity

Сесия 25 (Ownership 2.0 — C.1 lifetime elision)

  1. Elision rules in @[Checked] (bootstrap/sema.nim):
    • Each elided input &/&mut → distinct #elidedN
    • One input lifetime → assigned to elided return
    • First param self/Self preferred when multiple inputs
    • Multiple inputs + elided return → lifetime elision failed (need 'a)
  2. Return checks:
    • cannot return reference to local variable (return &local / let-bound local ref)
    • no input reference to borrow from (return ref with zero input refs)
    • Explicit 'a mismatch between return and value
  3. Body check for lifetime-only generics (func F<'a>(...)) — no longer skipped
  4. Diagnostics hints for elision / dangling / mismatch
  5. Tests: 8 new borrow_test cases; goldens return_local_ref, elision_multi_input
  6. Example: examples/lifetime_elision.bux (Identity / explicit / ViaLet / self)
  7. LanguageRef + QUALITY_PLAN updated
  8. Verified: borrow_test 24/24, 9 error goldens, example runs

Сесия 26 (C.1 selfhost parity)

  1. Lexer (src/lexer.bux + tkLifetime=111): 'a vs char 'x' (same heuristic as bootstrap)
  2. Parser:
    • &'a T / &'a mut TTypeExpr.refLifetime
    • func F<'a, T>(…) — lifetime params accepted and skipped for mono slots
  3. Sema lifetime elision (fixed 8-slot maps, same rules as bootstrap):
    • single-input elision, self preference, multi-input fail
    • return-local / no-input-ref / explicit mismatch
    • let-bound ref lifetime propagation
  4. Fixed checkFunc else-branch that wiped checkedFunc when retType was void
  5. Verified: buxc2 run lifetime_elision PASS; goldens on buxc2 show same errors; bootstrap still green; selfhost-loop expected IDENTICAL

Сесия 27 (tooling — D.4 bux doc + D.5 stdlib goldens)

  1. D.5 Stdlib goldens (tests/stdlib_golden/):
    • Packages: array, string, collections (Map/Set/Result/Option)
    • run.sh builds via buxc run and matches expected PASS lines
    • make test-stdlib wired into make test
  2. D.4 bux doc:
    • Bootstrap: bootstrap/docgen.nim/// + adjacent /* */
    • Selfhost: Cli_Doc line scanner for ///
    • bux doc [--out file] [path] (default path lib/)
    • make docsdocs/api/stdlib.md
  3. Stdlib docs: /// on Array / String / Test public helpers
  4. Verified: make test-stdlib, ./buxc doc lib/Array.bux | head, selfhost build

Сесия 28 (LSP v0.4.0 — position-sensitive locals + inferred lets)

  1. LocalBinding with scope range (scopeStartLinescopeEndLine) per let/param
  2. Sema-backed inference (checkExprForLsp / resolveType):
    • let x = 42 → hover let x: int · inferred
    • let s: String = "…" → annotated, not inferred
    • params: param a: int visible for whole function
  3. Position-sensitive hover / go-to-def / completion (innermost scope wins on shadowing)
  4. Nested scopes: if/while/for/match/block arms
  5. Version bux-lsp 0.4.0; tests: tools/test_lsp_locals.nim, tools/smoke_lsp_hover.sh
  6. Verified: hover shows let sum: int · inferred, param a: int, let n: int · inferred

Сесия 29 (full-tree bux fmt + CI enforce)

  1. One-shot format of lib/ (33), examples/ (23), src/ (15), tests/ (8), apps/ (12)
  2. Idempotent: second --check → 0 would reformat on all trees
  3. CI: make fmt-check enforces full tree + dirty-path smoke (exit 1)
  4. make fmt helper to reformat the same roots
  5. Verified: test-stdlib, key examples, selfhost + selfhost-loop IDENTICAL ✓

Сесия 30 (E.1 package registry + E.3 semver draft)

  1. Registry index (config/registry.toml, $BUX_REGISTRY, ~/.bux/registry.toml)
    • [[package]] with name / version / source / description
    • file: / path: (relative to index) or git URL
  2. CLI: bux search [q], bux add <name> resolves registry, bux install locks path/git
  3. Demo package: registry/packages/greet (Greet_Hello, Greet_Version)
  4. Smoke: tools/smoke_registry.sh / make test-registry — temp app outside tree
  5. Semver policy: docs/SEMVER.md (0.x vs 1.0, registry version match)
  6. Packages.md updated

Сесия 31 (E.1b HTTP registry + E.2 apps smoke + E.5 micro-bench)

  1. HTTP registry index (bootstrap/registry.nim):
    • $BUX_REGISTRY accepts http:// / https:// URLs
    • Fetch via curl (fallback wget) → ~/.bux/cache/registry_http.toml
    • BUX_REGISTRY_REFRESH=1 forces re-download; cache keyed by URL meta file
    • bux search shows URL + cached path; clear errors on fetch failure
  2. Smoke: tools/smoke_registry.sh — local path flow + python http.server HTTP search
  3. E.2 apps smoke (tools/smoke_apps.sh / make test-apps):
    • Build simpledb, jwt-pitbul, nexus, boko-framework
    • simpledb set/get/has/count/del; jwt-pitbul sign/verify/decode
    • Removed obsolete JWT-disable workaround from simpledb README
  4. E.5 micro-benchmarks (benches/micro, benches/c, make bench):
    • Bux: int_loop, fib30, string_concat, array_push
    • C refs (gcc -O2): fib30, int_loop for relative comparison
  5. Docs: Packages.md, config/registry.toml, benches/README.md
  6. Verified: make test-registry, tools/smoke_apps.sh, tools/bench.sh

Сесия 32 (E.5 nexus throughput + language twins)

  1. Nexus env config (apps/nexus/src/Main.bux):
    • NEXUS_PORT, NEXUS_WORKERS, NEXUS_BIND, NEXUS_PUBLIC
  2. Throughput harness (tools/bench_nexus.sh / make bench-nexus):
    • Start nexus on :18080, wrk -t4 -c64 -d5s/api/health
    • Sample (this machine): ~44.5k req/s, p50 ~0.94ms (Connection: close)
  3. Language twins for micro kernels:
    • benches/nim/ fib + int_loop (nim c -d:release)
    • benches/zig/ sources (built when zig is on PATH)
    • make bench runs Bux + C + Nim (+ Zig if present)
  4. Docs: benches/README.md, nexus README, BuildAndTest
  5. Verified: tools/bench.sh, tools/bench_nexus.sh

Сесия 33 (E.4 Debugger / DWARF basics)

  1. Source map: HIR loc → LIR locLine/locFile on every stmt/expr (setSourceLoc)
  2. C backend: emit #line N "path.bux" when location changes; force map at each func entry
  3. Build modes:
    • default: cc -O0 -g + #line maps
    • --release / build --release: -O2 -DNDEBUG, no #line, no -g
    • BUX_CFLAGS appended for custom flags
  4. Smoke: tools/smoke_dwarf.sh / make test-dwarf
    • #line for stdlib + user Main.bux
    • .debug_info present in debug binary
    • gdb list Main shows real Bux source
  5. Verified: smoke PASS; gdb list Main → hello.bux body

Сесия 34 (LSP refs/rename + CI wiring)

  1. bux-lsp 0.5.0 (tools/lsp_server.nim):
    • textDocument/references — scoped locals (same binding via lookupLocalAt) + workspace globals
    • textDocument/prepareRename + renameWorkspaceEdit.changes
    • Ident scan skips strings/comments; keyword rename rejected
  2. Smoke: tools/smoke_lsp_rename.sh (sum→total ≥2 edits); wired into make test-lsp
  3. CI: make test now runs test-registry + test-dwarf + test-apps after stdlib goldens
  4. Verified: rename/hover smokes PASS

Сесия 35 (Nexus HTTP/1.1 keep-alive)

  1. Server loop (apps/nexus/src/Server.bux):
    • HandleConnection serves up to 1000 requests per TCP fd
    • BuildResponse(..., keepAlive)Connection: keep-alive + Keep-Alive: or close
  2. Policy (RawRequest_WantsKeepAlive on raw bytes):
    • HTTP/1.1 default keep-alive; Connection: close forces close
    • HTTP/1.0 needs explicit keep-alive
    • (Avoided fragile Array<HeaderEntry> walk — keys corrupted under for-in/Get)
  3. Bench: ~81k req/s vs ~45k with close-only (wrk -t4 -c64 -d5s /api/health)
  4. Version banner 0.3.0; README / benches notes updated
  5. Verified: curl headers + make bench-nexus

Сесия 36 (header ownership + selfhost flags + LSP workspace/symbol)

  1. Root cause (Nexus headers UAF): auto-drop of local headers after shallow-copy into HttpRequest / ParseResult freed the buffer while still referenced. Not Array ABI — move-out-of-field not tracked.
  2. Fix (apps/nexus/src/Parser.bux): after embedding, zero headers.data/len/cap so auto-drop is a no-op; Request_WantsKeepAlive again uses RequestHeader_Get safely. Bench still ~7680k RPS.
  3. Selfhost compile flags (src/cli.bux):
    • default -O0 -g (was always -O2)
    • --release-O2 -DNDEBUG (was -O3 -flto)
    • BUX_CFLAGS appended via bux_getenv
    • #line maps remain bootstrap-only (selfhost C backend has no LIR #line yet)
  4. LSP 0.6.0: workspace/symbol over open docs + workspace index (cap 200); tools/smoke_lsp_workspace.sh + make test-lsp
  5. Verified: nexus keep-alive + header Get; make lsp + workspace smoke

Следващи стъпки

  1. Compiler: skip Drop when local is moved into a struct field / return payload
  2. Selfhost #line maps (parity with bootstrap LIR backend)
  3. Deeper rename (type members / qualified paths)
  4. LSP call hierarchy (optional)