From c7d4c87fcfa742b01824475e6419c18a04948a22 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Wed, 10 Jun 2026 11:11:52 +0300 Subject: [PATCH] =?UTF-8?q?selfhost:=20basic=20CTFE=20=E2=80=94=20compile-?= =?UTF-8?q?time=20const=20expression=20evaluator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Evaluate const declarations at compile time: - Arithmetic: +, -, *, /, % - Comparisons: <, <=, >, >=, ==, != - Bitwise: &, |, ^, <<, >> - Logical: &&, || - Unary: -, !, ~ - Ternary: cond ? a : b - Cast: (T)expr (evaluates operand) - Cross-const references: const C = A + B Uses multi-pass evaluation to handle forward references (caused by parser push-front ordering). --- src/hir_lower.bux | 120 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 7 deletions(-) diff --git a/src/hir_lower.bux b/src/hir_lower.bux index 9ab41b3..1512a4e 100644 --- a/src/hir_lower.bux +++ b/src/hir_lower.bux @@ -1837,6 +1837,93 @@ func Lcx_LowerClosureFunc(ctx: *LowerCtx, expr: *Expr) -> *HirFunc { return f; } +// --------------------------------------------------------------------------- +// Compile-Time Function Execution (CTFE) — constant expression evaluator +// --------------------------------------------------------------------------- + +func Lcx_EvalConstExpr(ctx: *LowerCtx, expr: *Expr) -> int { + if expr == null as *Expr { + return 0; + } + + // Literal integer + if expr.kind == ekLiteral { + return expr.intValue; + } + + // Reference to another constant + if expr.kind == ekIdent { + let name: String = expr.strValue; + let i: int = 0; + while i < ctx.hm.constCount { + if String_Eq(ctx.hm.consts[i].name, name) { + return ctx.hm.consts[i].value; + } + i = i + 1; + } + return 0; + } + + // Unary operators + if expr.kind == ekUnary { + let operand: int = Lcx_EvalConstExpr(ctx, expr.child1); + let op: int = expr.intValue; + if op == tkMinus { return -operand; } + if op == tkBang { return (operand == 0) as int; } + if op == tkTilde { return ~operand; } + return operand; + } + + // Binary operators + if expr.kind == ekBinary { + let left: int = Lcx_EvalConstExpr(ctx, expr.child1); + let right: int = Lcx_EvalConstExpr(ctx, expr.child2); + let op: int = expr.intValue; + if op == tkPlus { return left + right; } + if op == tkMinus { return left - right; } + if op == tkStar { return left * right; } + if op == tkSlash { + if right == 0 { return 0; } + return left / right; + } + if op == tkPercent { + if right == 0 { return 0; } + return left % right; + } + if op == tkLt { return (left < right) as int; } + if op == tkLe { return (left <= right) as int; } + if op == tkGt { return (left > right) as int; } + if op == tkGe { return (left >= right) as int; } + if op == tkEq { return (left == right) as int; } + if op == tkNe { return (left != right) as int; } + if op == tkAmp { return left & right; } + if op == tkPipe { return left | right; } + if op == tkCaret { return left ^ right; } + if op == tkShl { return left << right; } + if op == tkShr { return left >> right; } + if op == tkAmpAmp { return (left != 0 && right != 0) as int; } + if op == tkPipePipe { return (left != 0 || right != 0) as int; } + return 0; + } + + // Ternary operator + if expr.kind == ekTernary { + let cond: int = Lcx_EvalConstExpr(ctx, expr.child1); + if cond != 0 { + return Lcx_EvalConstExpr(ctx, expr.child2); + } else { + return Lcx_EvalConstExpr(ctx, expr.child3); + } + } + + // Cast — evaluate operand (types don't affect integer values) + if expr.kind == ekCast { + return Lcx_EvalConstExpr(ctx, expr.child1); + } + + return 0; +} + // --------------------------------------------------------------------------- // Module lowering — main entry point // --------------------------------------------------------------------------- @@ -1947,16 +2034,11 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { ctx.externFuncs[ctx.externCount] = *f; ctx.externCount = ctx.externCount + 1; } + // Pass 1: collect const names (expressions evaluated in Pass 2) if decl.kind == dkConst && hm.constCount < 512 { let ci: int = hm.constCount; hm.consts[ci].name = decl.strValue; - var val: int = 0; - if decl.constValue != null as *Expr { - if decl.constValue.kind == ekLiteral { - val = decl.constValue.intValue; - } - } - hm.consts[ci].value = val; + hm.consts[ci].value = 0; hm.constCount = hm.constCount + 1; } if decl.kind == dkEnum { @@ -1999,6 +2081,30 @@ func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { } + // Pass 2: evaluate all const expressions (multiple passes for forward refs) + var changed: bool = true; + var maxPasses: int = 10; + var pass: int = 0; + while changed && pass < maxPasses { + changed = false; + decl = mod.firstItem; + var ci2: int = 0; + while decl != null as *Decl && ci2 < hm.constCount { + if decl.kind == dkConst { + if decl.constValue != null as *Expr { + let newVal: int = Lcx_EvalConstExpr(ctx, decl.constValue); + if newVal != hm.consts[ci2].value { + hm.consts[ci2].value = newVal; + changed = true; + } + } + ci2 = ci2 + 1; + } + decl = decl.childDecl2; + } + pass = pass + 1; + } + hm.funcCount = ctx.funcCount; hm.funcs = ctx.funcs; hm.externCount = ctx.externCount;