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:
+425
-88
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+77
-2
@@ -55,12 +55,44 @@ func Diagnostic_GetLine(path: String, lineNum: uint32) -> String {
|
||||
return bux_str_split_part(content, "\n", lineNum - 1);
|
||||
}
|
||||
|
||||
/* Simple substring check for help hints */
|
||||
func Diagnostic_MsgContains(msg: String, needle: String) -> bool {
|
||||
return bux_str_contains(msg, needle) != 0;
|
||||
}
|
||||
|
||||
/* Actionable help for common error messages */
|
||||
func Diagnostic_Hint(msg: String) -> String {
|
||||
if Diagnostic_MsgContains(msg, "cannot assign") {
|
||||
return "ensure the right-hand side type matches the left-hand side";
|
||||
}
|
||||
if Diagnostic_MsgContains(msg, "undeclared identifier") {
|
||||
return "check the spelling, or import the symbol from the right module";
|
||||
}
|
||||
if Diagnostic_MsgContains(msg, "too few arguments") {
|
||||
return "compare the call with the function's parameter list";
|
||||
}
|
||||
if Diagnostic_MsgContains(msg, "too many arguments") {
|
||||
return "compare the call with the function's parameter list";
|
||||
}
|
||||
if Diagnostic_MsgContains(msg, "use of moved value") {
|
||||
return "the value was moved; clone it or restructure ownership";
|
||||
}
|
||||
if Diagnostic_MsgContains(msg, "expected expression") {
|
||||
return "the previous statement may be incomplete (missing value or ';')";
|
||||
}
|
||||
if Diagnostic_MsgContains(msg, "duplicate symbol") {
|
||||
return "rename one of the definitions or remove the duplicate";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Print a diagnostic in Rust-style format:
|
||||
* error: <message>
|
||||
* --> <path>:<line>:<col>
|
||||
* |
|
||||
* 42 | <source_line>
|
||||
* | <spaces>^
|
||||
* = help: <hint>
|
||||
*/
|
||||
func Diagnostic_Print(diag: *Diagnostic, sourcePath: String) {
|
||||
/* Severity prefix */
|
||||
@@ -94,14 +126,57 @@ func Diagnostic_Print(diag: *Diagnostic, sourcePath: String) {
|
||||
Print(" | ");
|
||||
PrintLine(lineText);
|
||||
|
||||
/* Underline */
|
||||
/* Underline (multi-char for identifiers/string tokens) */
|
||||
Print(" | ");
|
||||
var i: uint32 = 0;
|
||||
while i < diag.column - 1 && i < 120 {
|
||||
Print(" ");
|
||||
i = i + 1;
|
||||
}
|
||||
PrintLine("^");
|
||||
/* Estimate token length from the source line */
|
||||
var ulen: uint = 1;
|
||||
let col0: uint = diag.column - 1;
|
||||
let lineLen: uint = String_Len(lineText);
|
||||
if col0 < lineLen {
|
||||
let first: String = String_Chars(lineText, col0);
|
||||
if String_Eq(first, "\"") || String_Eq(first, "`") || String_Eq(first, "'") {
|
||||
var j: uint = col0 + 1;
|
||||
while j < lineLen {
|
||||
let cj: String = String_Chars(lineText, j);
|
||||
if String_Eq(cj, first) {
|
||||
ulen = j - col0 + 1;
|
||||
break;
|
||||
}
|
||||
j = j + 1;
|
||||
}
|
||||
} else {
|
||||
var j: uint = col0;
|
||||
while j < lineLen {
|
||||
let cj: String = String_Chars(lineText, j);
|
||||
if String_Contains("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_", cj) {
|
||||
j = j + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if j > col0 {
|
||||
ulen = j - col0;
|
||||
}
|
||||
}
|
||||
}
|
||||
var k: uint = 0;
|
||||
while k < ulen {
|
||||
Print("^");
|
||||
k = k + 1;
|
||||
}
|
||||
PrintLine("");
|
||||
}
|
||||
|
||||
/* Helpful hint when we recognize the error */
|
||||
let hint: String = Diagnostic_Hint(diag.message);
|
||||
if !String_Eq(hint, "") {
|
||||
Print(" = help: ");
|
||||
PrintLine(hint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+256
-146
@@ -43,30 +43,7 @@ struct LowerCtx {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Lcx_ResolveTypeKindFromName(name: String) -> int {
|
||||
if String_Eq(name, "void") { return tyVoid; }
|
||||
if String_Eq(name, "bool") { return tyBool; }
|
||||
if String_Eq(name, "bool8") { return tyBool8; }
|
||||
if String_Eq(name, "bool16") { return tyBool16; }
|
||||
if String_Eq(name, "bool32") { return tyBool32; }
|
||||
if String_Eq(name, "char8") { return tyChar8; }
|
||||
if String_Eq(name, "char16") { return tyChar16; }
|
||||
if String_Eq(name, "char32") { return tyChar32; }
|
||||
if String_Eq(name, "String") { return tyStr; }
|
||||
if String_Eq(name, "str") { return tyStr; }
|
||||
if String_Eq(name, "int8") { return tyInt8; }
|
||||
if String_Eq(name, "int16") { return tyInt16; }
|
||||
if String_Eq(name, "int32") { return tyInt32; }
|
||||
if String_Eq(name, "int64") { return tyInt64; }
|
||||
if String_Eq(name, "int") { return tyInt; }
|
||||
if String_Eq(name, "uint8") { return tyUInt8; }
|
||||
if String_Eq(name, "uint16") { return tyUInt16; }
|
||||
if String_Eq(name, "uint32") { return tyUInt32; }
|
||||
if String_Eq(name, "uint64") { return tyUInt64; }
|
||||
if String_Eq(name, "uint") { return tyUInt; }
|
||||
if String_Eq(name, "float32") { return tyFloat32; }
|
||||
if String_Eq(name, "float64") { return tyFloat64; }
|
||||
if String_Eq(name, "float") { return tyFloat64; }
|
||||
return tyNamed;
|
||||
return Type_FromName(name);
|
||||
}
|
||||
|
||||
func Lcx_TypeKindToName(kind: int) -> String {
|
||||
@@ -178,38 +155,58 @@ func Lcx_SubstituteType(ctx: *LowerCtx, te: *TypeExpr) -> *TypeExpr {
|
||||
return te;
|
||||
}
|
||||
|
||||
// Build C function-pointer type string from a tekFunc TypeExpr, e.g. "int (*)(int)"
|
||||
func Lcx_BuildFuncTypeName(te: *TypeExpr) -> String {
|
||||
if te == null as *TypeExpr || te.kind != tekFunc { return "void (*)(void)"; }
|
||||
var retName: String = "void";
|
||||
if te.funcRet != null as *TypeExpr {
|
||||
if te.funcRet.kind == tekPointer && te.funcRet.pointerPointee != null as *TypeExpr {
|
||||
retName = String_Concat(te.funcRet.pointerPointee.typeName, "*");
|
||||
} else {
|
||||
retName = te.funcRet.typeName;
|
||||
}
|
||||
if String_Eq(retName, "") { retName = "int"; }
|
||||
// Sanitize a C type fragment for use inside BuxFn_* mangled names
|
||||
func Lcx_SanitizeFatPart(s: String) -> String {
|
||||
var r: String = s;
|
||||
if String_Eq(r, "String") || String_Eq(r, "str") || String_Eq(r, "const char*") {
|
||||
return "cstr";
|
||||
}
|
||||
var result: String = retName;
|
||||
result = String_Concat(result, " (*)(");
|
||||
if String_Eq(r, "unsigned int") { return "uint"; }
|
||||
// crude replacements for * and spaces
|
||||
r = String_ReplaceAll(r, "*", "Ptr");
|
||||
r = String_ReplaceAll(r, " ", "_");
|
||||
r = String_ReplaceAll(r, "(", "");
|
||||
r = String_ReplaceAll(r, ")", "");
|
||||
r = String_ReplaceAll(r, ",", "_");
|
||||
if String_Eq(r, "") { return "int"; }
|
||||
return r;
|
||||
}
|
||||
|
||||
func Lcx_TypeExprFatPart(te: *TypeExpr) -> String {
|
||||
if te == null as *TypeExpr { return "void"; }
|
||||
if te.kind == tekPointer && te.pointerPointee != null as *TypeExpr {
|
||||
return Lcx_SanitizeFatPart(String_Concat(te.pointerPointee.typeName, "Ptr"));
|
||||
}
|
||||
if te.kind == tekFunc {
|
||||
return Lcx_SanitizeFatPart(Lcx_BuildFuncTypeName(te));
|
||||
}
|
||||
var n: String = te.typeName;
|
||||
if String_Eq(n, "") { n = "int"; }
|
||||
return Lcx_SanitizeFatPart(n);
|
||||
}
|
||||
|
||||
// Fat function-pointer type name: BuxFn_<ret>_<p0>_<p1>...
|
||||
// Enables multi-instance closures (code + env).
|
||||
func Lcx_BuildFuncTypeName(te: *TypeExpr) -> String {
|
||||
if te == null as *TypeExpr || te.kind != tekFunc {
|
||||
return "BuxFn_void_void";
|
||||
}
|
||||
var retPart: String = "void";
|
||||
if te.funcRet != null as *TypeExpr {
|
||||
retPart = Lcx_TypeExprFatPart(te.funcRet);
|
||||
}
|
||||
var result: String = String_Concat("BuxFn_", retPart);
|
||||
var cur: *TypeExprList = te.funcParams;
|
||||
var first: bool = true;
|
||||
var anyParam: bool = false;
|
||||
while cur != null as *TypeExprList {
|
||||
if !first {
|
||||
result = String_Concat(result, ", ");
|
||||
}
|
||||
var pName: String = "int";
|
||||
if cur.te.kind == tekPointer && cur.te.pointerPointee != null as *TypeExpr {
|
||||
pName = String_Concat(cur.te.pointerPointee.typeName, "*");
|
||||
} else {
|
||||
pName = cur.te.typeName;
|
||||
}
|
||||
if String_Eq(pName, "") { pName = "int"; }
|
||||
result = String_Concat(result, pName);
|
||||
first = false;
|
||||
result = String_Concat(result, "_");
|
||||
result = String_Concat(result, Lcx_TypeExprFatPart(cur.te));
|
||||
anyParam = true;
|
||||
cur = cur.next;
|
||||
}
|
||||
result = String_Concat(result, ")");
|
||||
if !anyParam {
|
||||
result = String_Concat(result, "_void");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -469,9 +466,44 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
|
||||
// Named function used as a value → fat pointer via __adapt_ wrapper
|
||||
if sym.kind == skFunc {
|
||||
var fatName: String = "BuxFn_int_int";
|
||||
if sym.refType != null as *TypeExpr && sym.refType.kind == tekFunc {
|
||||
fatName = Lcx_BuildFuncTypeName(sym.refType);
|
||||
} else if !String_Eq(sym.typeName, "") && String_StartsWith(sym.typeName, "BuxFn_") {
|
||||
fatName = sym.typeName;
|
||||
}
|
||||
n.kind = hStructInit;
|
||||
n.strValue = fatName;
|
||||
n.typeKind = tyFunc;
|
||||
n.typeName = fatName;
|
||||
let codeField: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
codeField.kind = hBlock;
|
||||
codeField.strValue = "code";
|
||||
let codeVal: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
codeVal.kind = hVar;
|
||||
codeVal.strValue = String_Concat("__adapt_", expr.strValue);
|
||||
codeField.child1 = codeVal;
|
||||
let envField: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
envField.kind = hBlock;
|
||||
envField.strValue = "env";
|
||||
let nullEnv: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
nullEnv.kind = hCast;
|
||||
nullEnv.typeName = "void*";
|
||||
let zero: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
zero.kind = hLit;
|
||||
zero.intValue = tkIntLiteral;
|
||||
zero.strValue = "0";
|
||||
nullEnv.child1 = zero;
|
||||
envField.child1 = nullEnv;
|
||||
codeField.child3 = envField;
|
||||
n.child1 = codeField;
|
||||
return n;
|
||||
}
|
||||
n.kind = hVar;
|
||||
n.strValue = expr.strValue;
|
||||
let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue);
|
||||
n.typeKind = sym.typeKind;
|
||||
|
||||
if expr.refType != null as *TypeExpr {
|
||||
@@ -566,6 +598,30 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
||||
}
|
||||
}
|
||||
|
||||
// Overflow checking: in @[Checked] mode, lower +, -, * on signed integers to checked calls
|
||||
if ctx.checkedFunc && !ctx.releaseFunc {
|
||||
var opKind: int = expr.intValue;
|
||||
var isArithOp: bool = opKind == tkPlus || opKind == tkMinus || opKind == tkStar;
|
||||
var isSignedInt: bool = false;
|
||||
if expr.child1 != null as *Expr && expr.child1.refType != null as *TypeExpr {
|
||||
let lhsKind: int = Lcx_ResolveTypeKind(expr.child1.refType);
|
||||
isSignedInt = Type_IsSigned(lhsKind);
|
||||
}
|
||||
if isArithOp && isSignedInt {
|
||||
var checkedFunc: String = "";
|
||||
if opKind == tkPlus { checkedFunc = "bux_add_i64_checked"; }
|
||||
else if opKind == tkMinus { checkedFunc = "bux_sub_i64_checked"; }
|
||||
else if opKind == tkStar { checkedFunc = "bux_mul_i64_checked"; }
|
||||
if !String_Eq(checkedFunc, "") {
|
||||
n.kind = hCall;
|
||||
n.strValue = checkedFunc;
|
||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||
n.child2 = Lcx_LowerExpr(ctx, expr.child2);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n.kind = hBinary;
|
||||
n.intValue = expr.intValue; // operator
|
||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||
@@ -575,6 +631,20 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
||||
|
||||
// Unary
|
||||
if kind == ekUnary {
|
||||
// Overflow checking: in @[Checked] mode, lower negation on signed integers to checked call
|
||||
if ctx.checkedFunc && !ctx.releaseFunc && expr.intValue == tkMinus {
|
||||
var isSignedInt: bool = false;
|
||||
if expr.child1 != null as *Expr && expr.child1.refType != null as *TypeExpr {
|
||||
let operandKind: int = Lcx_ResolveTypeKind(expr.child1.refType);
|
||||
isSignedInt = Type_IsSigned(operandKind);
|
||||
}
|
||||
if isSignedInt {
|
||||
n.kind = hCall;
|
||||
n.strValue = "bux_neg_i64_checked";
|
||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
n.kind = hUnary;
|
||||
n.intValue = expr.intValue;
|
||||
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
|
||||
@@ -1132,20 +1202,85 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
||||
return n;
|
||||
}
|
||||
|
||||
// Closure: generate function and return address-of
|
||||
// Closure: fat function pointer (multi-instance via heap env + maker)
|
||||
if kind == ekClosure {
|
||||
let f: *HirFunc = Lcx_LowerClosureFunc(ctx, expr);
|
||||
n.kind = hUnary;
|
||||
n.intValue = tkAmp;
|
||||
let varNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
varNode.kind = hVar;
|
||||
varNode.strValue = f.name;
|
||||
varNode.typeKind = tyFunc;
|
||||
n.child1 = varNode;
|
||||
n.typeKind = tyFunc;
|
||||
if expr.refType != null as *TypeExpr {
|
||||
n.typeName = Lcx_BuildFuncTypeName(expr.refType);
|
||||
var fatName: String = "BuxFn_int_int";
|
||||
if expr.refType != null as *TypeExpr && expr.refType.kind == tekFunc {
|
||||
fatName = Lcx_BuildFuncTypeName(expr.refType);
|
||||
} else if f.paramCount >= 2 {
|
||||
// thunk has __env + user params; approximate from ret + user arity
|
||||
fatName = "BuxFn_int_int";
|
||||
if f.paramCount == 3 { fatName = "BuxFn_int_int_int"; }
|
||||
if f.paramCount == 1 { fatName = "BuxFn_int_void"; }
|
||||
}
|
||||
if f.captureCount > 0 {
|
||||
// Call __make_<closure>(captures...) which heap-allocs env
|
||||
n.kind = hCall;
|
||||
n.strValue = String_Concat("__make_", f.name);
|
||||
n.typeKind = tyFunc;
|
||||
n.typeName = fatName;
|
||||
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; }
|
||||
let capVar: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
capVar.kind = hVar;
|
||||
capVar.strValue = capName;
|
||||
if ci == 0 { n.child1 = capVar; }
|
||||
else if ci == 1 { n.child2 = capVar; }
|
||||
else if ci == 2 {
|
||||
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
|
||||
firstExtra.node = capVar;
|
||||
firstExtra.next = null as *HirArgList;
|
||||
n.extraData = firstExtra as *void;
|
||||
n.extraCount = 1;
|
||||
} else {
|
||||
var cur: *HirArgList = n.extraData as *HirArgList;
|
||||
while cur.next != null as *HirArgList { cur = cur.next; }
|
||||
let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
|
||||
newNode.node = capVar;
|
||||
newNode.next = null as *HirArgList;
|
||||
cur.next = newNode;
|
||||
n.extraCount = n.extraCount + 1;
|
||||
}
|
||||
ci = ci + 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
// Capture-less: compound literal fat pointer with NULL env
|
||||
n.kind = hStructInit;
|
||||
n.strValue = fatName;
|
||||
n.typeKind = tyFunc;
|
||||
n.typeName = fatName;
|
||||
let codeField: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
codeField.kind = hBlock;
|
||||
codeField.strValue = "code";
|
||||
let codeVal: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
codeVal.kind = hVar;
|
||||
codeVal.strValue = f.name;
|
||||
codeField.child1 = codeVal;
|
||||
let envField: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
envField.kind = hBlock;
|
||||
envField.strValue = "env";
|
||||
let nullEnv: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
nullEnv.kind = hCast;
|
||||
nullEnv.typeName = "void*";
|
||||
let zero: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
zero.kind = hLit;
|
||||
zero.intValue = tkIntLiteral;
|
||||
zero.strValue = "0";
|
||||
nullEnv.child1 = zero;
|
||||
envField.child1 = nullEnv;
|
||||
codeField.child3 = envField;
|
||||
n.child1 = codeField;
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -1227,6 +1362,25 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
||||
return n;
|
||||
}
|
||||
|
||||
// Block expression (boolValue = true means unsafe block)
|
||||
if kind == ekBlock {
|
||||
if expr.refBlock != null as *Block {
|
||||
if expr.boolValue {
|
||||
let oldChecked: bool = ctx.checkedFunc;
|
||||
let oldRelease: bool = ctx.releaseFunc;
|
||||
ctx.checkedFunc = false;
|
||||
ctx.releaseFunc = false;
|
||||
let blockNode: *HirNode = Lcx_LowerBlock(ctx, expr.refBlock, -1);
|
||||
ctx.checkedFunc = oldChecked;
|
||||
ctx.releaseFunc = oldRelease;
|
||||
return blockNode;
|
||||
} else {
|
||||
return Lcx_LowerBlock(ctx, expr.refBlock, -1);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -1461,71 +1615,8 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
|
||||
}
|
||||
}
|
||||
|
||||
// If init is a closure with captures, emit capture assignments before the let
|
||||
if stmt.child1 != null as *Expr && stmt.child1.kind == ekClosure && stmt.child1.captureCount > 0 {
|
||||
let closureIdx: int = ctx.funcCount - 1;
|
||||
let envInst: String = String_Concat("__closure_env_instance_", String_FromInt(closureIdx));
|
||||
// Build a block: capture assignments + let store
|
||||
let blockNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
blockNode.kind = hBlock;
|
||||
blockNode.line = line;
|
||||
blockNode.column = col;
|
||||
var firstStmt: *HirNode = null as *HirNode;
|
||||
var lastStmt: *HirNode = null as *HirNode;
|
||||
var ci: int = 0;
|
||||
while ci < stmt.child1.captureCount {
|
||||
var capName: String = "";
|
||||
if ci == 0 { capName = stmt.child1.captureName0; }
|
||||
else if ci == 1 { capName = stmt.child1.captureName1; }
|
||||
else if ci == 2 { capName = stmt.child1.captureName2; }
|
||||
else if ci == 3 { capName = stmt.child1.captureName3; }
|
||||
else if ci == 4 { capName = stmt.child1.captureName4; }
|
||||
else if ci == 5 { capName = stmt.child1.captureName5; }
|
||||
else if ci == 6 { capName = stmt.child1.captureName6; }
|
||||
else if ci == 7 { capName = stmt.child1.captureName7; }
|
||||
// Build: envInst.capName = capName;
|
||||
let assignNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
assignNode.kind = hAssign;
|
||||
assignNode.line = line;
|
||||
assignNode.column = col;
|
||||
let fieldNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
fieldNode.kind = hFieldAccess;
|
||||
fieldNode.strValue = capName;
|
||||
let baseNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
baseNode.kind = hVar;
|
||||
baseNode.strValue = envInst;
|
||||
fieldNode.child1 = baseNode;
|
||||
let valNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
valNode.kind = hVar;
|
||||
valNode.strValue = capName;
|
||||
assignNode.child1 = fieldNode;
|
||||
assignNode.child2 = valNode;
|
||||
if firstStmt == null as *HirNode {
|
||||
firstStmt = assignNode;
|
||||
lastStmt = assignNode;
|
||||
} else {
|
||||
lastStmt.child3 = assignNode;
|
||||
lastStmt = assignNode;
|
||||
}
|
||||
ci = ci + 1;
|
||||
}
|
||||
// Append the let store
|
||||
if firstStmt == null as *HirNode {
|
||||
firstStmt = storeNode;
|
||||
lastStmt = storeNode;
|
||||
} else {
|
||||
lastStmt.child3 = storeNode;
|
||||
lastStmt = storeNode;
|
||||
}
|
||||
// Append auto-Drop defer if present
|
||||
if deferNode != null as *HirNode {
|
||||
lastStmt.child3 = deferNode;
|
||||
lastStmt = deferNode;
|
||||
}
|
||||
blockNode.child1 = firstStmt;
|
||||
return blockNode;
|
||||
}
|
||||
// Non-closure: wrap with defer if present
|
||||
// Capturing closures allocate env via __make_* at ekClosure site.
|
||||
// Wrap with defer if present
|
||||
if deferNode != null as *HirNode {
|
||||
storeNode.child3 = deferNode;
|
||||
let blockNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||
@@ -2244,8 +2335,16 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
|
||||
|
||||
let retTe: *TypeExpr = Lcx_SubstituteType(ctx, decl.retType);
|
||||
if retTe != null as *TypeExpr {
|
||||
f.retTypeName = retTe.typeName;
|
||||
f.retTypeKind = Lcx_ResolveTypeKind(retTe);
|
||||
if retTe.kind == tekFunc {
|
||||
f.retTypeName = Lcx_BuildFuncTypeName(retTe);
|
||||
} else if !String_Eq(retTe.typeName, "") {
|
||||
f.retTypeName = retTe.typeName;
|
||||
} else if retTe.kind == tekPointer && retTe.pointerPointee != null as *TypeExpr {
|
||||
f.retTypeName = String_Concat(retTe.pointerPointee.typeName, "*");
|
||||
} else {
|
||||
f.retTypeName = "";
|
||||
}
|
||||
} else {
|
||||
f.retTypeName = "";
|
||||
f.retTypeKind = 0;
|
||||
@@ -2320,25 +2419,36 @@ func Lcx_LowerClosureFunc(ctx: *LowerCtx, expr: *Expr) -> *HirFunc {
|
||||
f.isPublic = false;
|
||||
|
||||
let params: *Decl = expr.closureParams;
|
||||
if params != null as *Decl {
|
||||
f.paramCount = params.paramCount;
|
||||
if params.paramCount > 0 { f.param0 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param0, ¶ms.param0, ctx); }
|
||||
if params.paramCount > 1 { f.param1 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param1, ¶ms.param1, ctx); }
|
||||
if params.paramCount > 2 { f.param2 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param2, ¶ms.param2, ctx); }
|
||||
if params.paramCount > 3 { f.param3 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param3, ¶ms.param3, ctx); }
|
||||
if params.paramCount > 4 { f.param4 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param4, ¶ms.param4, ctx); }
|
||||
if params.paramCount > 5 { f.param5 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param5, ¶ms.param5, ctx); }
|
||||
if params.paramCount > 6 { f.param6 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param6, ¶ms.param6, ctx); }
|
||||
if params.paramCount > 7 { f.param7 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param7, ¶ms.param7, ctx); }
|
||||
if params.paramCount > 8 { f.param8 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param8, ¶ms.param8, ctx); }
|
||||
}
|
||||
// Fat-func ABI: leading void* __env, then user params
|
||||
var userCount: int = 0;
|
||||
if params != null as *Decl { userCount = params.paramCount; }
|
||||
f.paramCount = userCount + 1;
|
||||
f.param0 = bux_alloc(sizeof(HirParam)) as *HirParam;
|
||||
f.param0.name = "__env";
|
||||
f.param0.typeKind = tyPointer;
|
||||
f.param0.typeName = "void*";
|
||||
if userCount > 0 { f.param1 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param1, ¶ms.param0, ctx); }
|
||||
if userCount > 1 { f.param2 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param2, ¶ms.param1, ctx); }
|
||||
if userCount > 2 { f.param3 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param3, ¶ms.param2, ctx); }
|
||||
if userCount > 3 { f.param4 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param4, ¶ms.param3, ctx); }
|
||||
if userCount > 4 { f.param5 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param5, ¶ms.param4, ctx); }
|
||||
if userCount > 5 { f.param6 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param6, ¶ms.param5, ctx); }
|
||||
if userCount > 6 { f.param7 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param7, ¶ms.param6, ctx); }
|
||||
if userCount > 7 { f.param8 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param8, ¶ms.param7, ctx); }
|
||||
|
||||
if expr.refType != null as *TypeExpr && expr.refType.kind == tekFunc && expr.refType.funcRet != null as *TypeExpr {
|
||||
f.retTypeName = expr.refType.funcRet.typeName;
|
||||
f.retTypeKind = Lcx_ResolveTypeKind(expr.refType.funcRet);
|
||||
if expr.refType != null as *TypeExpr && expr.refType.kind == tekFunc {
|
||||
// Return type of the *thunk* is the closure's return type (not the fat type)
|
||||
if expr.refType.funcRet != null as *TypeExpr {
|
||||
f.retTypeName = expr.refType.funcRet.typeName;
|
||||
if String_Eq(f.retTypeName, "") { f.retTypeName = "int"; }
|
||||
f.retTypeKind = Lcx_ResolveTypeKind(expr.refType.funcRet);
|
||||
} else {
|
||||
f.retTypeName = "void";
|
||||
f.retTypeKind = tyVoid;
|
||||
}
|
||||
} else {
|
||||
f.retTypeName = "";
|
||||
f.retTypeKind = 0;
|
||||
f.retTypeName = "int";
|
||||
f.retTypeKind = tyInt;
|
||||
}
|
||||
|
||||
// Copy capture metadata from AST
|
||||
|
||||
@@ -277,6 +277,7 @@ func lexKeywordKind(text: String) -> int {
|
||||
if String_Eq(text, "switch") { return tkSwitch; }
|
||||
if String_Eq(text, "case") { return tkCase; }
|
||||
if String_Eq(text, "default") { return tkDefault; }
|
||||
if String_Eq(text, "unsafe") { return tkUnsafe; }
|
||||
if String_Eq(text, "async") { return tkAsync; }
|
||||
if String_Eq(text, "await") { return tkAwait; }
|
||||
if String_Eq(text, "spawn") { return tkSpawn; }
|
||||
|
||||
@@ -399,6 +399,15 @@ func parserParsePrimary(p: *Parser) -> *Expr {
|
||||
return parserParseClosure(p);
|
||||
}
|
||||
|
||||
// unsafe { ... } — unsafe block expression
|
||||
if kind == tkUnsafe {
|
||||
discard parserAdvance(p);
|
||||
let e: *Expr = parserMakeExpr(ekBlock, line, col);
|
||||
e.boolValue = true; // marks this block as unsafe
|
||||
e.refBlock = parserParseBlock(p);
|
||||
return e;
|
||||
}
|
||||
|
||||
parserEmitDiag(p, line, col, "expected expression");
|
||||
return parserMakeExpr(ekLiteral, line, col);
|
||||
}
|
||||
|
||||
+10
-30
@@ -136,7 +136,6 @@ func Sema_BuildFuncTypeExprFromDecl(decl: *Decl) -> *TypeExpr {
|
||||
|
||||
func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int {
|
||||
if te == null as *TypeExpr { return tyUnknown; }
|
||||
let name: String = te.typeName;
|
||||
|
||||
if te.kind == tekPointer {
|
||||
return tyPointer;
|
||||
@@ -146,33 +145,7 @@ func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int {
|
||||
return tyFunc;
|
||||
}
|
||||
|
||||
if String_Eq(name, "void") { return tyVoid; }
|
||||
if String_Eq(name, "bool") { return tyBool; }
|
||||
if String_Eq(name, "bool8") { return tyBool8; }
|
||||
if String_Eq(name, "bool16") { return tyBool16; }
|
||||
if String_Eq(name, "bool32") { return tyBool32; }
|
||||
if String_Eq(name, "char8") { return tyChar8; }
|
||||
if String_Eq(name, "char16") { return tyChar16; }
|
||||
if String_Eq(name, "char32") { return tyChar32; }
|
||||
if String_Eq(name, "String") { return tyStr; }
|
||||
if String_Eq(name, "str") { return tyStr; }
|
||||
if String_Eq(name, "int8") { return tyInt8; }
|
||||
if String_Eq(name, "int16") { return tyInt16; }
|
||||
if String_Eq(name, "int32") { return tyInt32; }
|
||||
if String_Eq(name, "int64") { return tyInt64; }
|
||||
if String_Eq(name, "int") { return tyInt; }
|
||||
if String_Eq(name, "uint8") { return tyUInt8; }
|
||||
if String_Eq(name, "uint16") { return tyUInt16; }
|
||||
if String_Eq(name, "uint32") { return tyUInt32; }
|
||||
if String_Eq(name, "uint64") { return tyUInt64; }
|
||||
if String_Eq(name, "uint") { return tyUInt; }
|
||||
if String_Eq(name, "float32") { return tyFloat32; }
|
||||
if String_Eq(name, "float64") { return tyFloat64; }
|
||||
if String_Eq(name, "float") { return tyFloat64; }
|
||||
|
||||
// Type resolution uses scope-based lookup via Sema_CollectGlobals
|
||||
// TODO: add StringMap-based type table for faster named-type validation
|
||||
return tyNamed;
|
||||
return Type_FromName(te.typeName);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -758,10 +731,17 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
||||
return tyUnknown;
|
||||
}
|
||||
|
||||
// Block expression
|
||||
// Block expression (boolValue = true means unsafe block)
|
||||
if kind == ekBlock {
|
||||
if expr.refBlock != null as *Block {
|
||||
Sema_CheckBlock(sema, expr.refBlock);
|
||||
if expr.boolValue {
|
||||
let prevChecked: bool = sema.checkedFunc;
|
||||
sema.checkedFunc = false;
|
||||
Sema_CheckBlock(sema, expr.refBlock);
|
||||
sema.checkedFunc = prevChecked;
|
||||
} else {
|
||||
Sema_CheckBlock(sema, expr.refBlock);
|
||||
}
|
||||
}
|
||||
return tyVoid;
|
||||
}
|
||||
|
||||
@@ -142,6 +142,7 @@ const tkDefer: int = 106;
|
||||
const tkSwitch: int = 107;
|
||||
const tkCase: int = 108;
|
||||
const tkDefault: int = 109;
|
||||
const tkUnsafe: int = 110;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Token struct
|
||||
@@ -165,6 +166,7 @@ func Token_IsKeyword(kind: int) -> bool {
|
||||
if kind >= tkFunc && kind <= tkExtern { return true; }
|
||||
if kind >= tkAs && kind <= tkSuper { return true; }
|
||||
if kind == tkSizeOf { return true; }
|
||||
if kind >= tkDefer && kind <= tkUnsafe { return true; }
|
||||
if kind >= tkAsync && kind <= tkSpawn { return true; }
|
||||
return false;
|
||||
}
|
||||
@@ -215,6 +217,15 @@ func Token_KeywordKind(text: String) -> int {
|
||||
if String_Eq(text, "self") { return tkSelf; }
|
||||
if String_Eq(text, "super") { return tkSuper; }
|
||||
if String_Eq(text, "sizeof") { return tkSizeOf; }
|
||||
if String_Eq(text, "defer") { return tkDefer; }
|
||||
if String_Eq(text, "switch") { return tkSwitch; }
|
||||
if String_Eq(text, "case") { return tkCase; }
|
||||
if String_Eq(text, "default") { return tkDefault; }
|
||||
if String_Eq(text, "unsafe") { return tkUnsafe; }
|
||||
if String_Eq(text, "discard") { return tkDiscard; }
|
||||
if String_Eq(text, "async") { return tkAsync; }
|
||||
if String_Eq(text, "await") { return tkAwait; }
|
||||
if String_Eq(text, "spawn") { return tkSpawn; }
|
||||
if String_Eq(text, "true") { return tkBoolLiteral; }
|
||||
if String_Eq(text, "false") { return tkBoolLiteral; }
|
||||
return tkIdent;
|
||||
@@ -259,6 +270,15 @@ func Token_KindName(kind: int) -> String {
|
||||
if kind == tkNull { return "null"; }
|
||||
if kind == tkSelf { return "self"; }
|
||||
if kind == tkSuper { return "super"; }
|
||||
if kind == tkUnsafe { return "unsafe"; }
|
||||
if kind == tkDefer { return "defer"; }
|
||||
if kind == tkSwitch { return "switch"; }
|
||||
if kind == tkCase { return "case"; }
|
||||
if kind == tkDefault { return "default"; }
|
||||
if kind == tkAsync { return "async"; }
|
||||
if kind == tkAwait { return "await"; }
|
||||
if kind == tkSpawn { return "spawn"; }
|
||||
if kind == tkDiscard { return "discard"; }
|
||||
if kind == tkLParen { return "("; }
|
||||
if kind == tkRParen { return ")"; }
|
||||
if kind == tkLBrace { return "{"; }
|
||||
|
||||
@@ -186,4 +186,94 @@ func Type_ToString(t: Type) -> String {
|
||||
if t.kind == tyFunc { return t.name; }
|
||||
return "?";
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type_FromName — central type-name → kind mapping (used by sema, hir_lower)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Type_FromName(name: String) -> int {
|
||||
if String_Eq(name, "void") { return tyVoid; }
|
||||
if String_Eq(name, "bool") { return tyBool; }
|
||||
if String_Eq(name, "bool8") { return tyBool8; }
|
||||
if String_Eq(name, "bool16") { return tyBool16; }
|
||||
if String_Eq(name, "bool32") { return tyBool32; }
|
||||
if String_Eq(name, "char8") { return tyChar8; }
|
||||
if String_Eq(name, "char16") { return tyChar16; }
|
||||
if String_Eq(name, "char32") { return tyChar32; }
|
||||
if String_Eq(name, "String") { return tyStr; }
|
||||
if String_Eq(name, "str") { return tyStr; }
|
||||
if String_Eq(name, "int8") { return tyInt8; }
|
||||
if String_Eq(name, "int16") { return tyInt16; }
|
||||
if String_Eq(name, "int32") { return tyInt32; }
|
||||
if String_Eq(name, "int64") { return tyInt64; }
|
||||
if String_Eq(name, "int") { return tyInt; }
|
||||
if String_Eq(name, "uint8") { return tyUInt8; }
|
||||
if String_Eq(name, "uint16") { return tyUInt16; }
|
||||
if String_Eq(name, "uint32") { return tyUInt32; }
|
||||
if String_Eq(name, "uint64") { return tyUInt64; }
|
||||
if String_Eq(name, "uint") { return tyUInt; }
|
||||
if String_Eq(name, "float32") { return tyFloat32; }
|
||||
if String_Eq(name, "float64") { return tyFloat64; }
|
||||
if String_Eq(name, "float") { return tyFloat64; }
|
||||
return tyNamed;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type_ToCName — type kind → C type name (used by C backend)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Type_ToCName(kind: int) -> String {
|
||||
if kind == tyVoid { return "void"; }
|
||||
if kind == tyBool || kind == tyBool8 || kind == tyBool16 || 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*"; }
|
||||
// Fat function pointer — concrete BuxFn_* name comes from typeName field
|
||||
if kind == tyFunc { return "BuxFn"; }
|
||||
return "";
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Signed / Unsigned / Float predicates
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Type_IsSigned(kind: int) -> bool {
|
||||
return kind == tyInt8 || kind == tyInt16 || kind == tyInt32 || kind == tyInt64 || kind == tyInt;
|
||||
}
|
||||
|
||||
func Type_IsUnsigned(kind: int) -> bool {
|
||||
return kind == tyUInt8 || kind == tyUInt16 || kind == tyUInt32 || kind == tyUInt64 || kind == tyUInt;
|
||||
}
|
||||
|
||||
func Type_IsFloat(kind: int) -> bool {
|
||||
return kind == tyFloat32 || kind == tyFloat64;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type_SizeOf — byte size of a primitive type (0 for non-primitive)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Type_SizeOf(kind: int) -> int {
|
||||
if kind == tyBool || kind == tyBool8 || kind == tyChar8 || kind == tyInt8 || kind == tyUInt8 { return 1; }
|
||||
if kind == tyBool16 || kind == tyChar16 || kind == tyInt16 || kind == tyUInt16 { return 2; }
|
||||
if kind == tyBool32 || kind == tyChar32 || kind == tyInt32 || kind == tyUInt32 || kind == tyFloat32 { return 4; }
|
||||
if kind == tyInt64 || kind == tyUInt64 || kind == tyFloat64 { return 8; }
|
||||
if kind == tyInt || kind == tyUInt { return 8; }
|
||||
if kind == tyPointer { return 8; }
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user