feat: multi-instance closures, richer stdlib, and Rust-style diagnostics

Introduce fat function pointers (BuxFn {code, env}) so capturing closures
are heap-allocated per value in both bootstrap and selfhost. Expand
Array/Map/Set/String/Test/Result APIs, add proper tuple codegen and
error snippets with multi-char underlines, golden diagnostic tests, and
LSP diagnostics via buxc check.
This commit is contained in:
2026-07-15 16:00:21 +03:00
parent 94e6806dda
commit 61ac06ab5f
48 changed files with 2789 additions and 362 deletions
+425 -88
View File
@@ -8,30 +8,9 @@ module CBackend {
// ---------------------------------------------------------------------------
func CBackend_TypeToC(kind: int) -> String {
if kind == tyVoid { return "void"; }
if kind == tyBool { return "bool"; }
if kind == tyBool8 { return "bool"; }
if kind == tyBool16 { return "bool"; }
if kind == tyBool32 { return "bool"; }
if kind == tyChar8 { return "char"; }
if kind == tyChar16 { return "uint16"; }
if kind == tyChar32 { return "uint32"; }
if kind == tyStr { return "String"; }
if kind == tyInt8 { return "int8"; }
if kind == tyInt16 { return "int16"; }
if kind == tyInt32 { return "int32"; }
if kind == tyInt64 { return "int64"; }
if kind == tyInt{ return "int"; }
if kind == tyUInt8 { return "uint8"; }
if kind == tyUInt16 { return "uint16"; }
if kind == tyUInt32 { return "uint32"; }
if kind == tyUInt64 { return "uint64"; }
if kind == tyUInt { return "uint"; }
if kind == tyFloat32 { return "float32"; }
if kind == tyFloat64 { return "float64"; }
if kind == tyPointer { return "void*"; }
let cName: String = Type_ToCName(kind);
if !String_Eq(cName, "") { return cName; }
if kind == tyNamed { return "int"; }
if kind == tyFunc { return "void (*)(void)"; }
return "int";
}
@@ -338,31 +317,27 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
return;
}
// Indirect call through function pointer
// Indirect call through fat function pointer: f.code(f.env, args...)
if kind == hCallIndirect {
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, "(");
var needsComma: bool = false;
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ".code)(");
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, ".env");
if node.child2 != null as *HirNode {
StringBuilder_Append(&cbe.sb, ", ");
CBE_EmitExpr(cbe, node.child2);
needsComma = true;
}
if node.child3 != null as *HirNode {
if needsComma {
StringBuilder_Append(&cbe.sb, ", ");
}
StringBuilder_Append(&cbe.sb, ", ");
CBE_EmitExpr(cbe, node.child3);
needsComma = true;
}
// Emit extra args from linked list
var ai: int = 0;
var curExtra: *HirArgList = node.extraData as *HirArgList;
while ai < node.extraCount {
if needsComma {
StringBuilder_Append(&cbe.sb, ", ");
}
StringBuilder_Append(&cbe.sb, ", ");
CBE_EmitExpr(cbe, curExtra.node);
needsComma = true;
curExtra = curExtra.next;
ai = ai + 1;
}
@@ -590,11 +565,27 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
}
// Index: arr[idx] — emit as arr[idx]
// For Array<T> desugar pattern (fieldPtr "data"), emit bounds-checked access
if kind == hIndexPtr {
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, "[");
CBE_EmitExpr(cbe, node.child2);
StringBuilder_Append(&cbe.sb, "]");
var isArrayAccess: bool = false;
if node.child1 != null as *HirNode {
if node.child1.kind == hFieldPtr && String_Eq(node.child1.strValue, "data") {
isArrayAccess = true;
}
}
if isArrayAccess {
CBE_EmitExpr(cbe, node.child1.child1);
StringBuilder_Append(&cbe.sb, ".data[bux_index_check(");
CBE_EmitExpr(cbe, node.child2);
StringBuilder_Append(&cbe.sb, ", ");
CBE_EmitExpr(cbe, node.child1.child1);
StringBuilder_Append(&cbe.sb, ".len)]");
} else {
CBE_EmitExpr(cbe, node.child1);
StringBuilder_Append(&cbe.sb, "[");
CBE_EmitExpr(cbe, node.child2);
StringBuilder_Append(&cbe.sb, "]");
}
return;
}
@@ -629,19 +620,28 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
StringBuilder_Append(&cbe.sb, ptrNode.strValue);
return;
}
// arrow field: load(arrow_field(base, field)) → base->field
if ptrKind == hArrowField {
CBE_EmitExpr(cbe, ptrNode.child1);
StringBuilder_Append(&cbe.sb, "->");
StringBuilder_Append(&cbe.sb, ptrNode.strValue);
return;
}
// index: load(index_ptr(base, idx)) → base[idx]
// For Array<T> desugar pattern, emit bounds-checked access
if ptrKind == hIndexPtr {
CBE_EmitExpr(cbe, ptrNode.child1);
StringBuilder_Append(&cbe.sb, "[");
CBE_EmitExpr(cbe, ptrNode.child2);
StringBuilder_Append(&cbe.sb, "]");
var isArrayAccess: bool = false;
if ptrNode.child1 != null as *HirNode {
if ptrNode.child1.kind == hFieldPtr && String_Eq(ptrNode.child1.strValue, "data") {
isArrayAccess = true;
}
}
if isArrayAccess {
CBE_EmitExpr(cbe, ptrNode.child1.child1);
StringBuilder_Append(&cbe.sb, ".data[bux_index_check(");
CBE_EmitExpr(cbe, ptrNode.child2);
StringBuilder_Append(&cbe.sb, ", ");
CBE_EmitExpr(cbe, ptrNode.child1.child1);
StringBuilder_Append(&cbe.sb, ".len)]");
} else {
CBE_EmitExpr(cbe, ptrNode.child1);
StringBuilder_Append(&cbe.sb, "[");
CBE_EmitExpr(cbe, ptrNode.child2);
StringBuilder_Append(&cbe.sb, "]");
}
return;
}
}
@@ -727,6 +727,312 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
// Emit function declaration
// ---------------------------------------------------------------------------
// Infer C type for a BuxFn mangled part (int, cstr, uint, void, ...)
func CBE_FatPartToC(part: String) -> String {
if String_Eq(part, "cstr") { return "const char*"; }
if String_Eq(part, "void") { return "void"; }
if String_Eq(part, "bool") { return "bool"; }
if String_Eq(part, "uint") { return "unsigned int"; }
if String_Eq(part, "float") { return "float"; }
if String_Eq(part, "double") || String_Eq(part, "float64") { return "double"; }
if String_EndsWith(part, "Ptr") {
let base: String = String_Slice(part, 0, String_Len(part) - 3);
return String_Concat(CBE_FatPartToC(base), "*");
}
return part; // int, etc.
}
// Emit typedefs for common BuxFn_* shapes (fat function pointers)
func CBE_EmitFatFuncTypedefs(cbe: *CEmitter, mod: *HirModule) {
StringBuilder_Append(&cbe.sb, "/* Fat function pointer types (code + env) */\n");
// (int)->int
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_int {\n");
StringBuilder_Append(&cbe.sb, " int (*code)(void* env, int a0);\n");
StringBuilder_Append(&cbe.sb, " void* env;\n");
StringBuilder_Append(&cbe.sb, "} BuxFn_int_int;\n");
// (int,int)->int
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_int_int {\n");
StringBuilder_Append(&cbe.sb, " int (*code)(void* env, int a0, int a1);\n");
StringBuilder_Append(&cbe.sb, " void* env;\n");
StringBuilder_Append(&cbe.sb, "} BuxFn_int_int_int;\n");
// ()->void
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_void_void {\n");
StringBuilder_Append(&cbe.sb, " void (*code)(void* env);\n");
StringBuilder_Append(&cbe.sb, " void* env;\n");
StringBuilder_Append(&cbe.sb, "} BuxFn_void_void;\n");
// ()->int
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_void {\n");
StringBuilder_Append(&cbe.sb, " int (*code)(void* env);\n");
StringBuilder_Append(&cbe.sb, " void* env;\n");
StringBuilder_Append(&cbe.sb, "} BuxFn_int_void;\n");
// Scan module for any other BuxFn_* names
var i: int = 0;
while i < mod.funcCount {
CBE_MaybeEmitExtraFat(cbe, mod.funcs[i].retTypeName);
var p: int = 0;
while p < mod.funcs[i].paramCount {
var ptype: String = "";
if p == 0 { ptype = mod.funcs[i].param0.typeName; }
else if p == 1 { ptype = mod.funcs[i].param1.typeName; }
else if p == 2 { ptype = mod.funcs[i].param2.typeName; }
else if p == 3 { ptype = mod.funcs[i].param3.typeName; }
else if p == 4 { ptype = mod.funcs[i].param4.typeName; }
else if p == 5 { ptype = mod.funcs[i].param5.typeName; }
else if p == 6 { ptype = mod.funcs[i].param6.typeName; }
else if p == 7 { ptype = mod.funcs[i].param7.typeName; }
else if p == 8 { ptype = mod.funcs[i].param8.typeName; }
CBE_MaybeEmitExtraFat(cbe, ptype);
p = p + 1;
}
i = i + 1;
}
StringBuilder_Append(&cbe.sb, "\n");
}
func CBE_MaybeEmitExtraFat(cbe: *CEmitter, name: String) {
if String_Eq(name, "") { return; }
if !String_StartsWith(name, "BuxFn_") { return; }
// Skip ones we already emit as built-ins
if String_Eq(name, "BuxFn_int_int") { return; }
if String_Eq(name, "BuxFn_int_int_int") { return; }
if String_Eq(name, "BuxFn_void_void") { return; }
if String_Eq(name, "BuxFn_int_void") { return; }
CBE_EmitOneFatTypedef(cbe, name);
}
func CBE_CollectBuxFn(names: *String, count: *int, name: String) {
if String_Eq(name, "") { return; }
if !String_StartsWith(name, "BuxFn_") { return; }
if *count >= 64 { return; }
var i: int = 0;
while i < *count {
if String_Eq(names[i], name) { return; }
i = i + 1;
}
names[*count] = name;
*count = *count + 1;
}
// BuxFn_ret_p0_p1 → typedef with code pointer
func CBE_EmitOneFatTypedef(cbe: *CEmitter, fatName: String) {
// Split fatName after "BuxFn_" into parts by '_'
let prefixLen: uint = 6; // "BuxFn_"
let rest: String = String_Slice(fatName, prefixLen, String_Len(fatName) - prefixLen);
// Parse parts: first = ret, rest = params (use simple split)
let partCount: uint = String_SplitCount(rest, "_");
if partCount == 0 { return; }
let retPart: String = String_SplitPart(rest, "_", 0);
let retC: String = CBE_FatPartToC(retPart);
StringBuilder_Append(&cbe.sb, "typedef struct ");
StringBuilder_Append(&cbe.sb, fatName);
StringBuilder_Append(&cbe.sb, " {\n ");
// code field: ret (*code)(void* env, params...)
StringBuilder_Append(&cbe.sb, retC);
StringBuilder_Append(&cbe.sb, " (*code)(void* env");
var pi: uint = 1;
while pi < partCount {
let pPart: String = String_SplitPart(rest, "_", pi);
if !(pi == 1 && String_Eq(pPart, "void") && partCount == 2) {
StringBuilder_Append(&cbe.sb, ", ");
StringBuilder_Append(&cbe.sb, CBE_FatPartToC(pPart));
}
pi = pi + 1;
}
StringBuilder_Append(&cbe.sb, ");\n void* env;\n} ");
StringBuilder_Append(&cbe.sb, fatName);
StringBuilder_Append(&cbe.sb, ";\n");
}
func CBE_EmitMakerDecl(cbe: *CEmitter, f: *HirFunc) {
// Infer fat type from thunk: skip __env, use user params + ret
var fatName: String = "BuxFn_";
var retC: String = f.retTypeName;
if String_Eq(retC, "") { retC = "int"; }
fatName = String_Concat(fatName, Lcx_SanitizeFatPart(retC));
var pi: int = 1; // skip __env
while pi < f.paramCount {
var ptype: String = "int";
if pi == 1 { ptype = f.param1.typeName; }
else if pi == 2 { ptype = f.param2.typeName; }
else if pi == 3 { ptype = f.param3.typeName; }
else if pi == 4 { ptype = f.param4.typeName; }
else if pi == 5 { ptype = f.param5.typeName; }
else if pi == 6 { ptype = f.param6.typeName; }
else if pi == 7 { ptype = f.param7.typeName; }
else if pi == 8 { ptype = f.param8.typeName; }
if String_Eq(ptype, "") { ptype = "int"; }
fatName = String_Concat(fatName, "_");
fatName = String_Concat(fatName, Lcx_SanitizeFatPart(ptype));
pi = pi + 1;
}
if f.paramCount <= 1 {
fatName = String_Concat(fatName, "_void");
}
StringBuilder_Append(&cbe.sb, fatName);
StringBuilder_Append(&cbe.sb, " __make_");
StringBuilder_Append(&cbe.sb, f.name);
StringBuilder_Append(&cbe.sb, "(");
var ci: int = 0;
while ci < f.captureCount {
if ci > 0 { StringBuilder_Append(&cbe.sb, ", "); }
var capType: String = "int";
var capName: String = "c";
if ci == 0 { capName = f.captureName0; capType = CBackend_TypeToC(f.captureType0); }
else if ci == 1 { capName = f.captureName1; capType = CBackend_TypeToC(f.captureType1); }
else if ci == 2 { capName = f.captureName2; capType = CBackend_TypeToC(f.captureType2); }
else if ci == 3 { capName = f.captureName3; capType = CBackend_TypeToC(f.captureType3); }
else if ci == 4 { capName = f.captureName4; capType = CBackend_TypeToC(f.captureType4); }
else if ci == 5 { capName = f.captureName5; capType = CBackend_TypeToC(f.captureType5); }
else if ci == 6 { capName = f.captureName6; capType = CBackend_TypeToC(f.captureType6); }
else if ci == 7 { capName = f.captureName7; capType = CBackend_TypeToC(f.captureType7); }
StringBuilder_Append(&cbe.sb, capType);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, capName);
ci = ci + 1;
}
StringBuilder_Append(&cbe.sb, ")");
}
func CBE_EmitMakerFunc(cbe: *CEmitter, f: *HirFunc) {
CBE_EmitMakerDecl(cbe, f);
StringBuilder_Append(&cbe.sb, " {\n");
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, f.envStructName);
StringBuilder_Append(&cbe.sb, "* __e = (");
StringBuilder_Append(&cbe.sb, f.envStructName);
StringBuilder_Append(&cbe.sb, "*)bux_alloc(sizeof(");
StringBuilder_Append(&cbe.sb, f.envStructName);
StringBuilder_Append(&cbe.sb, "));\n");
var ci: int = 0;
while ci < f.captureCount {
var capName: String = "";
if ci == 0 { capName = f.captureName0; }
else if ci == 1 { capName = f.captureName1; }
else if ci == 2 { capName = f.captureName2; }
else if ci == 3 { capName = f.captureName3; }
else if ci == 4 { capName = f.captureName4; }
else if ci == 5 { capName = f.captureName5; }
else if ci == 6 { capName = f.captureName6; }
else if ci == 7 { capName = f.captureName7; }
StringBuilder_Append(&cbe.sb, " __e->");
StringBuilder_Append(&cbe.sb, capName);
StringBuilder_Append(&cbe.sb, " = ");
StringBuilder_Append(&cbe.sb, capName);
StringBuilder_Append(&cbe.sb, ";\n");
ci = ci + 1;
}
// Build fat return type name same as decl
var fatName: String = "BuxFn_";
var retC: String = f.retTypeName;
if String_Eq(retC, "") { retC = "int"; }
fatName = String_Concat(fatName, Lcx_SanitizeFatPart(retC));
var pi: int = 1;
while pi < f.paramCount {
var ptype: String = "int";
if pi == 1 { ptype = f.param1.typeName; }
else if pi == 2 { ptype = f.param2.typeName; }
else if pi == 3 { ptype = f.param3.typeName; }
else if pi == 4 { ptype = f.param4.typeName; }
else if pi == 5 { ptype = f.param5.typeName; }
else if pi == 6 { ptype = f.param6.typeName; }
else if pi == 7 { ptype = f.param7.typeName; }
else if pi == 8 { ptype = f.param8.typeName; }
if String_Eq(ptype, "") { ptype = "int"; }
fatName = String_Concat(fatName, "_");
fatName = String_Concat(fatName, Lcx_SanitizeFatPart(ptype));
pi = pi + 1;
}
if f.paramCount <= 1 {
fatName = String_Concat(fatName, "_void");
}
StringBuilder_Append(&cbe.sb, " return (");
StringBuilder_Append(&cbe.sb, fatName);
StringBuilder_Append(&cbe.sb, "){ .code = ");
StringBuilder_Append(&cbe.sb, f.name);
StringBuilder_Append(&cbe.sb, ", .env = __e };\n}\n\n");
}
// Emit adapters for any non-closure function (used when taken as value)
func CBE_EmitAllAdapters(cbe: *CEmitter, mod: *HirModule) {
StringBuilder_Append(&cbe.sb, "/* Fat-func adapters for named functions */\n");
var i: int = 0;
while i < mod.funcCount {
let fname: String = mod.funcs[i].name;
// Skip closures, makers, adapters themselves
if String_StartsWith(fname, "__closure_") || String_StartsWith(fname, "__make_") || String_StartsWith(fname, "__adapt_") {
i = i + 1;
continue;
}
if CBE_FuncHasGeneric(&mod.funcs[i]) {
i = i + 1;
continue;
}
// Only emit adapter if function has body
if mod.funcs[i].body == null as *HirNode {
i = i + 1;
continue;
}
// Adapter signature: ret __adapt_F(void* env, params...) { return F(params); }
var retC: String = mod.funcs[i].retTypeName;
if String_Eq(retC, "") { retC = "void"; }
StringBuilder_Append(&cbe.sb, "static ");
StringBuilder_Append(&cbe.sb, retC);
StringBuilder_Append(&cbe.sb, " __adapt_");
StringBuilder_Append(&cbe.sb, fname);
StringBuilder_Append(&cbe.sb, "(void* env");
var p: int = 0;
while p < mod.funcs[i].paramCount {
// Skip if first param is already __env (shouldn't for named funcs)
var pname: String = "";
var ptype: String = "int";
if p == 0 { pname = mod.funcs[i].param0.name; ptype = mod.funcs[i].param0.typeName; }
else if p == 1 { pname = mod.funcs[i].param1.name; ptype = mod.funcs[i].param1.typeName; }
else if p == 2 { pname = mod.funcs[i].param2.name; ptype = mod.funcs[i].param2.typeName; }
else if p == 3 { pname = mod.funcs[i].param3.name; ptype = mod.funcs[i].param3.typeName; }
else if p == 4 { pname = mod.funcs[i].param4.name; ptype = mod.funcs[i].param4.typeName; }
else if p == 5 { pname = mod.funcs[i].param5.name; ptype = mod.funcs[i].param5.typeName; }
else if p == 6 { pname = mod.funcs[i].param6.name; ptype = mod.funcs[i].param6.typeName; }
else if p == 7 { pname = mod.funcs[i].param7.name; ptype = mod.funcs[i].param7.typeName; }
else if p == 8 { pname = mod.funcs[i].param8.name; ptype = mod.funcs[i].param8.typeName; }
if String_Eq(ptype, "") { ptype = "int"; }
StringBuilder_Append(&cbe.sb, ", ");
StringBuilder_Append(&cbe.sb, ptype);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, pname);
p = p + 1;
}
StringBuilder_Append(&cbe.sb, ") {\n (void)env;\n");
if String_Eq(retC, "void") {
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, fname);
StringBuilder_Append(&cbe.sb, "(");
} else {
StringBuilder_Append(&cbe.sb, " return ");
StringBuilder_Append(&cbe.sb, fname);
StringBuilder_Append(&cbe.sb, "(");
}
p = 0;
while p < mod.funcs[i].paramCount {
if p > 0 { StringBuilder_Append(&cbe.sb, ", "); }
var pname: String = "";
if p == 0 { pname = mod.funcs[i].param0.name; }
else if p == 1 { pname = mod.funcs[i].param1.name; }
else if p == 2 { pname = mod.funcs[i].param2.name; }
else if p == 3 { pname = mod.funcs[i].param3.name; }
else if p == 4 { pname = mod.funcs[i].param4.name; }
else if p == 5 { pname = mod.funcs[i].param5.name; }
else if p == 6 { pname = mod.funcs[i].param6.name; }
else if p == 7 { pname = mod.funcs[i].param7.name; }
else if p == 8 { pname = mod.funcs[i].param8.name; }
StringBuilder_Append(&cbe.sb, pname);
p = p + 1;
}
StringBuilder_Append(&cbe.sb, ");\n}\n\n");
i = i + 1;
}
}
func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
// Return type
if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
@@ -961,7 +1267,12 @@ func CBackend_Generate(mod: *HirModule) -> String {
StringBuilder_Append(&cbe.sb, "typedef char char8;\n\n");
// Runtime declarations
StringBuilder_Append(&cbe.sb, "void* bux_alloc(unsigned int size);\n");
StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n\n");
StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n");
StringBuilder_Append(&cbe.sb, "unsigned long long bux_index_check(unsigned long long index, unsigned long long len);\n");
StringBuilder_Append(&cbe.sb, "int64 bux_add_i64_checked(int64 a, int64 b);\n");
StringBuilder_Append(&cbe.sb, "int64 bux_sub_i64_checked(int64 a, int64 b);\n");
StringBuilder_Append(&cbe.sb, "int64 bux_mul_i64_checked(int64 a, int64 b);\n");
StringBuilder_Append(&cbe.sb, "int64 bux_neg_i64_checked(int64 a);\n\n");
// Forward declare all struct types (skip empty names)
var si: int = 0;
@@ -1121,12 +1432,53 @@ func CBackend_Generate(mod: *HirModule) -> String {
si = si + 1;
}
// Fat function-pointer typedefs (BuxFn_*) — before forward decls
CBE_EmitFatFuncTypedefs(cbe, mod);
// Env structs for capturing closures (no static instance — heap per value)
var ei2: int = 0;
while ei2 < mod.funcCount {
if mod.funcs[ei2].captureCount > 0 && !String_Eq(mod.funcs[ei2].envStructName, "") {
StringBuilder_Append(&cbe.sb, "typedef struct ");
StringBuilder_Append(&cbe.sb, mod.funcs[ei2].envStructName);
StringBuilder_Append(&cbe.sb, " {\n");
var ci2: int = 0;
while ci2 < mod.funcs[ei2].captureCount {
var capName: String = "";
var capType: String = "int";
if ci2 == 0 { capName = mod.funcs[ei2].captureName0; capType = CBackend_TypeToC(mod.funcs[ei2].captureType0); }
else if ci2 == 1 { capName = mod.funcs[ei2].captureName1; capType = CBackend_TypeToC(mod.funcs[ei2].captureType1); }
else if ci2 == 2 { capName = mod.funcs[ei2].captureName2; capType = CBackend_TypeToC(mod.funcs[ei2].captureType2); }
else if ci2 == 3 { capName = mod.funcs[ei2].captureName3; capType = CBackend_TypeToC(mod.funcs[ei2].captureType3); }
else if ci2 == 4 { capName = mod.funcs[ei2].captureName4; capType = CBackend_TypeToC(mod.funcs[ei2].captureType4); }
else if ci2 == 5 { capName = mod.funcs[ei2].captureName5; capType = CBackend_TypeToC(mod.funcs[ei2].captureType5); }
else if ci2 == 6 { capName = mod.funcs[ei2].captureName6; capType = CBackend_TypeToC(mod.funcs[ei2].captureType6); }
else if ci2 == 7 { capName = mod.funcs[ei2].captureName7; capType = CBackend_TypeToC(mod.funcs[ei2].captureType7); }
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, capType);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, capName);
StringBuilder_Append(&cbe.sb, ";\n");
ci2 = ci2 + 1;
}
StringBuilder_Append(&cbe.sb, "} ");
StringBuilder_Append(&cbe.sb, mod.funcs[ei2].envStructName);
StringBuilder_Append(&cbe.sb, ";\n\n");
}
ei2 = ei2 + 1;
}
// Forward declarations for all functions (skip generics)
var i: int = 0;
while i < mod.funcCount {
if !CBE_FuncHasGeneric(&mod.funcs[i]) {
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
StringBuilder_Append(&cbe.sb, ";\n");
// Maker for capturing closures
if mod.funcs[i].captureCount > 0 {
CBE_EmitMakerDecl(cbe, &mod.funcs[i]);
StringBuilder_Append(&cbe.sb, ";\n");
}
}
i = i + 1;
}
@@ -1141,6 +1493,9 @@ func CBackend_Generate(mod: *HirModule) -> String {
}
StringBuilder_Append(&cbe.sb, "\n");
// Adapters before function bodies (bodies may take funcs as values)
CBE_EmitAllAdapters(cbe, mod);
// Function definitions (skip generics)
var hasMain: bool = false;
i = 0;
@@ -1157,45 +1512,23 @@ func CBackend_Generate(mod: *HirModule) -> String {
i = i + 1;
continue;
}
// Emit env struct and global instance for closures with captures
if mod.funcs[i].captureCount > 0 {
let envName: String = mod.funcs[i].envStructName;
let instName: String = mod.funcs[i].envInstanceName;
if !String_Eq(envName, "") {
// Emit struct definition
StringBuilder_Append(&cbe.sb, "struct ");
StringBuilder_Append(&cbe.sb, envName);
StringBuilder_Append(&cbe.sb, " {\n");
var ci: int = 0;
while ci < mod.funcs[i].captureCount {
var capName: String = "";
var capType: String = "int";
if ci == 0 { capName = mod.funcs[i].captureName0; capType = CBackend_TypeToC(mod.funcs[i].captureType0); }
else if ci == 1 { capName = mod.funcs[i].captureName1; capType = CBackend_TypeToC(mod.funcs[i].captureType1); }
else if ci == 2 { capName = mod.funcs[i].captureName2; capType = CBackend_TypeToC(mod.funcs[i].captureType2); }
else if ci == 3 { capName = mod.funcs[i].captureName3; capType = CBackend_TypeToC(mod.funcs[i].captureType3); }
else if ci == 4 { capName = mod.funcs[i].captureName4; capType = CBackend_TypeToC(mod.funcs[i].captureType4); }
else if ci == 5 { capName = mod.funcs[i].captureName5; capType = CBackend_TypeToC(mod.funcs[i].captureType5); }
else if ci == 6 { capName = mod.funcs[i].captureName6; capType = CBackend_TypeToC(mod.funcs[i].captureType6); }
else if ci == 7 { capName = mod.funcs[i].captureName7; capType = CBackend_TypeToC(mod.funcs[i].captureType7); }
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, capType);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, capName);
StringBuilder_Append(&cbe.sb, ";\n");
ci = ci + 1;
}
StringBuilder_Append(&cbe.sb, "};\n");
// Emit global instance
StringBuilder_Append(&cbe.sb, "static struct ");
StringBuilder_Append(&cbe.sb, envName);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, instName);
StringBuilder_Append(&cbe.sb, ";\n\n");
}
}
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
StringBuilder_Append(&cbe.sb, " {\n");
// Capturing closure thunk: materialize env from fat-func env pointer
if mod.funcs[i].captureCount > 0 && !String_Eq(mod.funcs[i].envStructName, "") && !String_Eq(mod.funcs[i].envInstanceName, "") {
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, mod.funcs[i].envStructName);
StringBuilder_Append(&cbe.sb, " ");
StringBuilder_Append(&cbe.sb, mod.funcs[i].envInstanceName);
StringBuilder_Append(&cbe.sb, " = *((");
StringBuilder_Append(&cbe.sb, mod.funcs[i].envStructName);
StringBuilder_Append(&cbe.sb, "*)__env);\n");
} else if mod.funcs[i].paramCount > 0 {
// Capture-less closure still has __env
if String_Eq(mod.funcs[i].param0.name, "__env") {
StringBuilder_Append(&cbe.sb, " (void)__env;\n");
}
}
// Body
cbe.checkedFunc = mod.funcs[i].checkedFunc;
cbe.deferCount = 0;
@@ -1216,6 +1549,10 @@ func CBackend_Generate(mod: *HirModule) -> String {
StringBuilder_Append(&cbe.sb, " return 0;\n");
}
StringBuilder_Append(&cbe.sb, "\n}\n\n");
// After capturing closure thunk, emit heap-env maker
if mod.funcs[i].captureCount > 0 {
CBE_EmitMakerFunc(cbe, &mod.funcs[i]);
}
i = i + 1;
}