Backtick strings were emitted directly into C code without
stripping the backticks or escaping the content, causing C syntax errors.
Fix: detect backtick-wrapped literals in CBE_EmitExpr, strip
the backticks, escape inner content, and wrap in double quotes.
Simple enums were emitted as plain C enums, but the rest of the codegen
(and source code) expected them to have a .tag field like algebraic enums.
This caused C compilation errors:
- 'request for member tag in something not a structure or union'
- 'field name not in record or union initializer'
Fix: always emit the _Tag typedef + struct wrapper for every enum.
For simple enums, the struct only contains the tag (no _Data union).
For algebraic enums, keep the existing _Data union + struct behavior.
This matches the bootstrap compiler's enum codegen.
The selfhost parser did not skip tkNewLine tokens inside expression parsing,
causing multi-line expressions (e.g. if conditions spanning lines with ||)
to be truncated. When parserParseBinaryPrec hit a newline it treated it as
an invalid primary, returning a malformed literal and leaving p.pos at the
newline. parserParseBlock then failed to find '{' and its depth-counter
fallback consumed far too many tokens, causing subsequent function bodies
to be parsed into the wrong AST nodes.
Fix: add newline skipping in parserParsePrimary, parserParseUnary, and
parserParseBinaryPrec, matching the bootstrap parser's skipNewlines() calls
in parseAnd/parseOr.
Also complete the variant cap raise from 8->9 in src/sema.bux (was already
done in ast.bux, parser.bux, hir_lower.bux).
parserParseImportDecl now skips tkNewLine tokens inside {..} blocks,
matching the bootstrap parser behavior. This fixes parsing of imports
with names on separate lines, e.g.:
import Std::String::{
String_Eq,
String_Len
};
Previously the parser failed because it expected tkIdent immediately
after { but encountered tkNewLine instead.
hir_lower.resolveExprType for ekField now correctly resolves variant
positional/named fields (e.g., r.data.Ok_0, r.data.Err_0) on _Data
union types, matching the existing logic in sema.checkExpr.
This fixes the runtime segfault in examples/algebraic_enums.bux where
the C backend emitted an int temporary for a String field, causing
PrintLine to dereference a garbage pointer.
- Fix bootstrap sema to type simple enum variants as the enum itself
(not _Tag), and .tag access returns the enum type.
- Fix hir_lower to lower simple enum .tag as no-op and Enum{tag:X}
init as X directly.
- Fix sema collectGlobals to register imports AFTER real declarations,
preventing duplicate-symbol errors when stdlib files import symbols
defined in later files (e.g. jwt.bux importing rsa.bux).
- Rewrite lib/crypto/*.bux as single-line to work around bootstrap
parser limitations with multi-line function calls.
- Rename 'pub' -> 'pubBuf' in ed25519.bux and jwt-pitbul/Main.bux
to avoid reserved keyword collision.
- Remove algTag:int workaround from jwt-pitbul/Main.bux; enum
comparisons now type-check without manual casts.
- Update .gitignore to exclude _test_*/ and backup files.
- Delete accidentally-committed _test_array/ directory.
Parser would hang (infinite loop) when encountering newline tokens
inside certain constructs because expect() returns without advancing
position when the expected token doesn't match.
Fixed 5 places in parseBaseType, parsePrimaryPattern, parseTypeParams,
parseParamList, and parseUseDecl by adding newline-skipping at the
start of each iteration.
Verified: compiler no longer hangs/crashes on all 3 real-world apps
from apps/ (boko-framework, jwt-pitbul, nexus).
- README: bump version to v0.4.0, add selfhost loop & borrow/shared
- Stdlib.md: update sections 1-4, add String_Chars, FromBool, Fmt, Iter
- BuildAndTest.md: add make selfhost-loop and make test-golden targets
- Standard library list updated to all 23 modules
- examples/borrow.bux: working borrow expression example
- tests/borrow_test.nim: add 4 new tests for borrow & @[Shared]
- parser.nim: fix borrow parsing order (& before mut)
- Build verified: example compiles and runs
This workaround consumes a stray } that parserParseBlock sometimes leaves
behind, which was causing the module handler to exit early. With this fix,
the selfhost compiler (buxc2) can now compile the entire src/ project.
- buxc2 project build: 491 user + 310 stdlib = 801 decls, no sema errors
- make selfhost-loop: builds and runs both passes
- C compilation succeeds, resulting binary works
- Cli_BuildProject now appends declarations (preserving original file order)
instead of prepending (which reversed order)
- Added per-file declaration count debug output
- This makes merge order match Nim bootstrap behavior
- Makefile: new targets `test-golden` and `selfhost-loop`
- selfhost-loop verifies C codegen determinism (2-pass bootstrap)
- test-golden compares C output against expected.c golden files
- Updated clean-all to clean new build dirs
- tests/golden/hello/: first golden test (hello world)
- src/sema.bux:
- Fix: import stubs only registered if symbol doesn't already exist
- Improve: error message now includes identifier name
- ast.bux: Decl expanded with field0..field255, variant0..7, param0..8
- parser.bux: add parserParseUnary for correct unary precedence; handle tkDiscard statements; allow var without initializer (matching bootstrap); add diagCount bounds check in parserEmitDiag
- hir.bux: HirParam changed to pointers
- hir_lower.bux: full field chains for struct lowering
- c_backend.bux: hardened -> vs . field access; null checks in CBE_GetExprTypeName
- cli.bux: add -lcrypto to linker
- Fix lirValToC crash on lvkInt in hStore raw C emission
- Add hAlloca handling to lowerExpr (returns &name, avoids unhandled fallback)
- Fix nested comment syntax in unhandled expr fallback
- Rewrite buildLval to handle hIndexPtr and nested hFieldPtr/hArrowField
so assignments like arr[i].field = val emit direct C lvalues
- Make &&/|| temporaries unique (__and_tmp_N/__or_tmp_N) to avoid
duplicate declarations in the same function
- Selfhost null-deref fix in sema.bux (unchained nested conditions)
- hir_lower: add isScope=true to lowerBlock; fix resolveExprType for ekIndex,
ekField on monomorphized structs, and ekTry/ekUnwrap tmpVar typing
- lir_lower: add loop label stack for break/continue; pass null arg to
bux_task_spawn when spawn has no arguments
- lir_c_backend: multi-pass type inference for temps; include params in
varTypes; handle lvkTemp in lirLoad; use inferred type in lirAlloca
- 42 LIR instructions in bootstrap/lir.nim
- HIR→LIR lowering in bootstrap/lir_lower.nim
- LIR→C emission with full struct/enum/vtable support
- All 22 examples + 5 Nim unit tests passing
- Updated README status and backend description
The old findGenericCalls second-pass only looked in non-generic functions
and generated unresolved instances like Array_Get_T when generic funcs
called other generic funcs. Removed it entirely.
lowerExpr for ekCall now invokes generateMethodInstance directly for
both explicit (ekGenericCall) and inferred generic calls. This ensures
concrete instantiations are generated on-demand with correct typeSubst.
Also added guard in resolveTypeExpr/substituteType to skip emitting C
struct definitions for unresolved type parameters (e.g. Array<T> inside
a generic function body before monomorphization).
feat(std): add Std::Iter module
- Array_Iter, Iter_Next, Iter_HasNext, Iter_Peek, Iter_Reset
- Iter_Pos, Iter_Len, Iter_Count, Iter_Skip, Iter_Take
docs: document Std::Iter in Stdlib.md
examples: add iter.bux example
tests: add _test_array regression test
- Fix sema.nim: use objType.name instead of obj.name for algebraic enum
tag/data field types (was generating _Tag instead of EnumName_Tag)
- Fix c_backend.nim: emit enum definitions before struct definitions
to allow structs to reference algebraic enum types
- Add bux_mutex_* and bux_rwlock_* C runtime functions
- Add library/std/Sync.bux with Mutex and RwLock wrappers
- Add examples/sync.bux — threaded counter with Mutex
- Update docs/Stdlib.md with Sync section and example
- Update README.md feature table
- Initialize result to 0 in Channel_RecvInt/RecvFloat64 to handle closed channel
- Add producer/consumer channel example in examples/channel.bux
- Update README.md with Channels syntax preview
- Add isOwn field to Symbol for tracking own T declarations
- Reinitialization after move: assigning to a moved variable removes it from movedVars
- Move tracking in let/var initialization: var b: own Box = a moves a
- Move tracking in assignment: x = y moves y if y is own T
- Move tracking in return: return x moves x if x is own T
- Expand borrow_test.nim to 10 tests (all passing)
- C runtime (OpenSSL): bux_sha256, bux_hmac_sha256, bux_random_bytes,
bux_base64_encode, bux_base64_decode, bux_bytes_to_hex
- library/std/Crypto.bux: Crypto_Sha256, Crypto_HmacSha256,
Crypto_HmacSha256Raw, Crypto_RandomBytes, Crypto_Base64Encode,
Crypto_Base64Decode
- examples/jwt.bux: full JWT creation with HS256 signature
- Link with -lcrypto in C backend
- Lexer (Nim + Bux): resolve escape sequences (\n, \t, \r, etc) instead of
keeping raw source text in token. This was the root cause of \n being
double-escaped to \\n in generated C code.
- Manifest parser (src_bux/manifest.bux): use bux_strlen instead of broken
String_SplitCount(s, "") hack for string length; fix quote stripping.
- CLI (src_bux/cli.bux): --version/--help now recognized alongside version/help.
- Remove ~30+ debug Print statements from cli, parser, lexer, sema, main.
- Clean up unneeded Print/PrintInt extern declarations.
- _selfhost/src/: synced from src_bux/.
- Fix segfault from uninitialized Symbol structs in scope/hir_lower/sema
- Fix null ReadFile crash in Cli_MergeFileInto (missing stdlib files)
- Extend function params from 6 to 9 (bux_str_format needs 9 args)
- Add ExprList/HirArgList linked lists for >2 call arguments
- Add banner image to README
- Update docs: C backend is now primary (not QBE)
Major changes:
- Cli_BuildProject now uses CBackend_Generate instead of QBE SSA generation
- Replaced QBE invocation (vendor/qbe/qbe) with direct cc compilation
- Cli_Build (single-file) now compiles C output with cc including runtime.c/io.c
- Added import-based stdlib merging: only imported Std::* modules are merged,
eliminating unused generic code from Array/Map/Set/Channel in simple programs
C backend fixes:
- Fixed struct typedef redefinition: struct definitions now use 'struct Name {}'
instead of 'typedef struct {} Name' to avoid conflicting with forward typedefs
- Added enum support: HirEnum struct, enum emission in C backend, variant
constants emitted as #define Name_Value
- Added char8 typedef
- Skip generic structs/functions (T/K/V type parameters) in C emission
Sema fixes:
- Fixed Sema_IsNumeric: now correctly recognizes uint, float32, float64
- Fixed return type check: allow pointer compatibility (String <-> *T)
Tested: hello, fibonacci, factorial, enums examples build and run via buxc2
- Iterate function body statements and call Sema_CheckStmt on each
- Add Sema_CheckBlock helper for child scope creation in if/while/for/loop blocks
- Extend Sema_CheckStmt to handle all statement kinds: skIf, skWhile, skDoWhile,
skLoop, skFor, skMatch, skBreak, skContinue, skDecl, skReturn
- Extend Sema_CheckExpr with ekAssign, ekField, ekIndex, ekBlock, ekSelf
- Fix ekCall to resolve return type from function declaration
- Fix ekBinary to handle assignment operators (tkAssign, +=, -=, etc.)
- Add Sema_CollectGlobals handling for dkUse (imports), dkConst, dkEnum variants
- Add tkColonColon path parsing in parser (Color::Red)
- Add decl field to Symbol struct for function/type resolution
- Add Sema_ZeroInitSymbol helper to avoid garbage from uninitialized struct fields
- Fix concurrency.bux missing Task_Join import
All examples now pass buxc2 check. make selfhost succeeds.
- QBE_IsTerminator(hBlock) checks ALL children, not just last
- Removed legacy c_backend.bux and nim_backend.bux from _selfhost/src/
- QBE self-hosting QBE phase: PASSES (SSA valid, assembled to .s)
- Remaining: linker errors from missing String_Eq/StringBuilder in runtime
The self-hosted compiler crashed with SIGSEGV when processing files
that generated >=256 parser diagnostics (e.g. truncated source files).
The parser allocated a fixed-size diag buffer of 256 entries, but
parserExpect, parserExpectIdentOrKeyword, and parserEmitDiag wrote
into it without checking bounds. Once diagCount exceeded 256, writes
overflowed into adjacent heap objects (Decl structs), corrupting
their kind/childDecl2 fields and causing crashes during AST traversal.
This fix adds bounds checks to all three sites so excess diagnostics
are silently dropped rather than corrupting memory. The file size
threshold (7037 vs 7062 bytes) was a red herring — it determined
how many parser errors were generated before EOF.
Closes: selfhost segfault on 250+ line files
- Fix lexer unterminated char error test (was treating 'a as lifetime)
- Fix parser test sample.bux path for root-dir execution
- Add bux_system() to runtime.c and run command to selfhost CLI
- Switch bux_alloc from malloc to calloc for zero-init
- Fix module flattening in Cli_Check/Cli_Compile to scan full decl list
- Make test passes fully, selfhost builds successfully
- README.md: drop 'inspired by Rux' and 'improves on Rux' language
- PLAN.md: remove Rux from comparison table, milestones, and appendix
- PLAN.md: delete entire Appendix A (Rux Language Reference)
- docs/PHASE8_STRATEGY.md: rephrase without Rux
- docs/Packages.md: drop 'Compatible with Rux.toml spec'
- Parser_Parse: add beforePos safeguard to skip unrecognized tokens
instead of looping forever when parserParseDecl returns null
- Lexer: increase maxTokens 4096 -> 32768; add explicit break on limit
- CLI: null-check ReadFile result before String_Eq
- Nim CLI: add -O2 to cc invocation for faster self-hosted builds
- Fixed module flattening in self-hosted compiler (Cli_Compile now unwraps module wrappers)
- buxc2 check now finds functions in module-wrapped files (token.bux: funcCount=6, Main.bux: funcCount=2)
- Added debug prints to trace lexer.bux timeout and check x segfault
- Remaining issues: lexer.bux hangs in self-hosted pipeline, check x segfaults in runtime
- Added exprSpawnAsync flag to AST ekSpawn
- Sema checks if callee is async and sets the flag
- HIR hSpawn carries spawnAsync flag
- C backend uses bux_task_spawn for non-async (OS threads) and bux_async_spawn for async (coroutines)
- Added concurrency example to test suite
- T[] slices are now fat pointers {data: T*, len: size_t} in C backend
- Added hSliceIndex HIR node for slice indexing (distinct from hIndexPtr for raw pointers)
- Added exprIndexBoundsCheck flag to AST ekIndex
- Sema sets bounds-check flag in @[Checked] functions
- C backend emits Slice_<T> typedefs and compound literals for slice init
- bux_bounds_check() called automatically on slice indexing in @[Checked] funcs
- Unchecked functions allow raw access (C behavior) for performance
- Lexer tokenizes 'a as tkLifetime
- Parser accepts lifetimes in type params <'a> and reference types &'a T / &'a mut T
- AST TypeExpr has refLifetime field for tekRef/tekMutRef
- TypeParam gains isLifetime flag for generic monomorphization
- Sema infers lifetime params as no-op dummy values; unwraps pointee types for params inside refs
- HIR lower skips lifetime params when generating mangled names and substitution tables
- &mut expr syntax supported in parser (consumed as part of unary &)
Self-hosted compiler (buxc2):
- Add async/await/spawn tokens, parsing, HIR, lowering, C emission
- Fix pointer type emission (*T -> T*) in C backend
- Fix sizeof(Type) parsing with parentheses
- Fix import ::{...} infinite loop guard
- Fix int64 emission by avoiding else-if chain workaround
- Add debug-free cli and hir_lower
Bootstrap compiler (Nim):
- Fix critical else-if lowering bug in hir_lower.nim:
when both elseIfs and else block exist, elseIfs were dropped
causing all else-if chains to collapse to if+else only
Runtime:
- Fix bux_async_await memory corruption (don't free in run)
- Add bux_remove_from_ready for safe cleanup
- Fix forward declaration and deduplicate bux_now_ms
Docs & examples:
- Update async.bux example with proper int64 params
- Update README, LanguageRef, Stdlib, PLAN for async features
- Mark Phase 7.10 bootstrap loop as completed
All 27 examples pass. buxc2 check/build work on all examples.
- runtime: bux_async_return(value, size) copies result to task->result
- runtime: bux_async_await returns void* result pointer
- runtime: bux_async_result(handle) to retrieve result
- sema: ekAwait returns *void
- HIR: await lowered to bux_async_await returning *void
- Example: async Compute() -> int with interleaved execution and result await