Phase 7.9: Self-hosted compiler buxc2 builds and runs! 🎉

buxc (Nim bootstrap) successfully compiles buxc2 (Bux self-hosted compiler)
into a working 88KB ELF x86-64 binary.

Compiler fixes (Nim):
- duplicate symbol: user funcs shadow stdlib funcs via mergeDecls()
- forward declarations: func without body + definition (both orderings)
- extern func dedup: same extern in multiple files
- discard keyword: new language keyword, lowered to expr stmt or no-op
- parser: keywords as field names + advance-on-error safeguard
- parser: var without initializer (zero-init)
- parser: multi-line || && continuation expressions
- parser: else-if chain newline handling
- C backend: const declarations emitted as #define
- C backend: load(field_ptr) → base.field optimization (fixes lvalue errors)

Source fixes (src_bux/*.bux):
- types.bux: tk* → ty* prefix for type kind constants (avoid token conflict)
- sema.bux: remaining tk* → ty* references, StringMap → *void workaround
- hir_lower.bux: ekReturn removed, Lcx_LowerParam helper, *f dereference
- c_backend.bux: &mod.funcs[i] for pointer passing
- cli.bux: ReadFile/WriteFile → bux_read_file/bux_write_file wrappers
- parser.bux: pathStr.len → String_Len(pathStr)
- types.bux: "*" + x → String_Concat("*", x)

Build system:
- Makefile: added 4 missing examples + selfhost target
- PLAN.md: updated with actual project state, Phase 7.9 marked complete

All 18 examples pass, all unit tests pass.
This commit is contained in:
2026-05-31 16:34:36 +03:00
parent 166954204c
commit cb256397bd
31 changed files with 4603 additions and 284 deletions
+23 -4
View File
@@ -209,7 +209,19 @@ proc collectGlobals*(sema: var Sema) =
let retType = if decl.declFuncReturnType != nil: sema.resolveType(decl.declFuncReturnType) else: makeVoid()
sym.typ = makeFunc(params, retType)
if not sema.globalScope.define(sym):
sema.emitError(decl.loc, &"duplicate symbol '{decl.declFuncName}'")
let existing = sema.globalScope.lookup(decl.declFuncName)
if existing != nil and existing.kind == skFunc:
if existing.decl != nil and existing.decl.declFuncBody == nil and decl.declFuncBody != nil:
# First was forward declaration, update with definition
existing.decl = decl
existing.typ = sym.typ
elif decl.declFuncBody == nil:
# New one is a forward declaration, existing already has it — skip
discard
else:
sema.emitError(decl.loc, &"duplicate symbol '{decl.declFuncName}'")
else:
sema.emitError(decl.loc, &"duplicate symbol '{decl.declFuncName}'")
# Auto-register func Type_Method(self: Type, ...) as a method
if decl.declFuncParams.len > 0 and decl.declFuncParams[0].name == "self":
var typeName = ""
@@ -243,7 +255,10 @@ proc collectGlobals*(sema: var Sema) =
let retType = if decl.declExtFuncReturnType != nil: sema.resolveType(decl.declExtFuncReturnType) else: makeVoid()
sym.typ = makeFunc(params, retType)
if not sema.globalScope.define(sym):
sema.emitError(decl.loc, &"duplicate symbol '{decl.declExtFuncName}'")
# Allow duplicate extern func declarations (same func declared in multiple files)
let existing = sema.globalScope.lookup(decl.declExtFuncName)
if existing == nil or existing.kind != skFunc:
sema.emitError(decl.loc, &"duplicate symbol '{decl.declExtFuncName}'")
of dkStruct:
let t = makeNamed(decl.declStructName)
let sym = Symbol(kind: skType, name: decl.declStructName, typ: t,
@@ -776,10 +791,14 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
of skExpr:
return sema.checkExpr(stmt.stmtExpr, scope)
of skLet:
let initType = sema.checkExpr(stmt.stmtLetInit, scope)
var initType: Type = makeVoid()
if stmt.stmtLetInit != nil:
initType = sema.checkExpr(stmt.stmtLetInit, scope)
let declaredType = if stmt.stmtLetType != nil: sema.resolveType(stmt.stmtLetType) else: initType
if stmt.stmtLetType != nil and not initType.isAssignableTo(declaredType) and not (initType.kind in {TypeKind.tkUnknown, TypeKind.tkNamed, TypeKind.tkTypeParam}):
if stmt.stmtLetInit != nil and stmt.stmtLetType != nil and not initType.isAssignableTo(declaredType) and not (initType.kind in {TypeKind.tkUnknown, TypeKind.tkNamed, TypeKind.tkTypeParam}):
sema.emitError(stmt.loc, &"cannot assign {initType.toString} to {declaredType.toString}")
if stmt.stmtLetInit == nil and stmt.stmtLetType == nil:
sema.emitError(stmt.loc, "variable must have either type annotation or initializer")
let sym = Symbol(kind: skVar, name: stmt.stmtLetName, typ: declaredType,
isMutable: stmt.stmtLetMut)
if not scope.define(sym):