feat(selfhost): function types (func(Params) -> Ret)

- Add tekFunc AST node and TypeExprList linked list for params
- Parse func(T, U) -> R syntax in parser.bux
- Resolve tekFunc to tyFunc in sema.bux
- Track original TypeExpr via Symbol.refType for params/lets/consts/funcs
- Build C function-pointer type names in hir_lower.bux (Ret (*)(Params))
- Lower indirect calls through function-typed values as hCallIndirect
- Add CBE_CParamDecl helper in c_backend.bux to embed name inside (*)
- Emit hCallIndirect and function-pointer variable declarations
- _test_funcptr now builds and runs with selfhost buxc2
This commit is contained in:
2026-06-08 23:39:15 +03:00
parent 2e536488e6
commit 0c41c7bb25
7 changed files with 305 additions and 78 deletions
+124 -38
View File
@@ -56,10 +56,46 @@ func Lcx_ResolveTypeKind(te: *TypeExpr) -> int {
if te.kind == tekPointer { return tyPointer; }
if te.kind == tekSlice { return tySlice; }
if te.kind == tekTuple { return tyTuple; }
if te.kind == tekFunc { return tyFunc; }
return Lcx_ResolveTypeKindFromName(te.typeName);
}
// 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"; }
}
var result: String = retName;
result = String_Concat(result, " (*)(");
var cur: *TypeExprList = te.funcParams;
var first: bool = true;
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;
cur = cur.next;
}
result = String_Concat(result, ")");
return result;
}
// ---------------------------------------------------------------------------
// Fresh name generation
// ---------------------------------------------------------------------------
@@ -152,15 +188,18 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
if expr.intValue == tkAmp {
n.typeKind = tyPointer;
if expr.child1.refType != null as *TypeExpr && expr.child1.refType.kind == tekFunc {
n.typeName = Lcx_BuildFuncTypeName(expr.child1.refType);
}
}
return n;
}
// Call
if kind == ekCall {
n.kind = hCall;
// Method call desugaring: obj.method(args) → Type_method(obj, args)
if expr.child1 != null as *Expr && expr.child1.kind == ekField {
n.kind = hCall;
let methodName: String = expr.child1.strValue;
var receiverTypeName: String = "";
if expr.child1.child1 != null as *Expr && expr.child1.child1.kind == ekIdent {
@@ -217,40 +256,80 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
}
return n;
}
// Get callee name
// Decide direct vs indirect call
var isDirectFunc: bool = false;
if expr.child1 != null as *Expr && expr.child1.kind == ekIdent {
n.strValue = expr.child1.strValue;
}
// Lower arguments from linked list
var arg: *ExprList = expr.callArgs;
var argIdx: int = 0;
while arg != null as *ExprList {
let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr);
if argIdx == 0 {
n.child1 = lowered;
} else if argIdx == 1 {
n.child2 = lowered;
} else if argIdx == 2 {
// Third argument — start linked list
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
firstExtra.node = lowered;
firstExtra.next = null as *HirArgList;
n.extraData = firstExtra as *void;
n.extraCount = 1;
} else {
// Additional args — append to linked list
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 = lowered;
newNode.next = null as *HirArgList;
cur.next = newNode;
n.extraCount = n.extraCount + 1;
let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue);
if sym.kind == skFunc {
isDirectFunc = true;
}
}
if isDirectFunc {
n.kind = hCall;
n.strValue = expr.child1.strValue;
// Lower arguments into child1/child2/extraData
var arg: *ExprList = expr.callArgs;
var argIdx: int = 0;
while arg != null as *ExprList {
let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr);
if argIdx == 0 {
n.child1 = lowered;
} else if argIdx == 1 {
n.child2 = lowered;
} else if argIdx == 2 {
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
firstExtra.node = lowered;
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 = lowered;
newNode.next = null as *HirArgList;
cur.next = newNode;
n.extraCount = n.extraCount + 1;
}
arg = arg.next;
argIdx = argIdx + 1;
}
} else {
n.kind = hCallIndirect;
n.child1 = Lcx_LowerExpr(ctx, expr.child1);
// Lower arguments into child2/child3/extraData (child1 is callee)
var arg: *ExprList = expr.callArgs;
var argIdx: int = 0;
while arg != null as *ExprList {
let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr);
if argIdx == 0 {
n.child2 = lowered;
} else if argIdx == 1 {
n.child3 = lowered;
} else if argIdx == 2 {
let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList;
firstExtra.node = lowered;
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 = lowered;
newNode.next = null as *HirArgList;
cur.next = newNode;
n.extraCount = n.extraCount + 1;
}
arg = arg.next;
argIdx = argIdx + 1;
}
arg = arg.next;
argIdx = argIdx + 1;
}
return n;
}
@@ -400,8 +479,10 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
if stmt.refStmtType != null as *TypeExpr {
alloca.intValue = stmt.refStmtType.kind;
alloca.typeKind = Lcx_ResolveTypeKind(stmt.refStmtType);
// For pointer types, construct "PointeeType*"
if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr {
// For function types, build C function-pointer syntax
if stmt.refStmtType.kind == tekFunc {
alloca.typeName = Lcx_BuildFuncTypeName(stmt.refStmtType);
} else if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr {
alloca.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*");
} else if !String_Eq(stmt.refStmtType.typeName, "") {
alloca.typeName = stmt.refStmtType.typeName;
@@ -413,6 +494,7 @@ func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode {
sym.name = stmt.strValue;
sym.typeKind = alloca.typeKind;
sym.typeName = alloca.typeName;
sym.refType = stmt.refStmtType;
sym.isMutable = false;
sym.isPublic = false;
sym.decl = null as *Decl;
@@ -609,8 +691,10 @@ func Lcx_LowerParam(out: *HirParam, p: *Param) {
out.name = p.name;
if p.refParamType != null as *TypeExpr {
out.typeKind = Lcx_ResolveTypeKind(p.refParamType);
// Use typeName directly if set (parser may have constructed "String*")
if !String_Eq(p.refParamType.typeName, "") {
// Function type: build C function-pointer syntax
if p.refParamType.kind == tekFunc {
out.typeName = Lcx_BuildFuncTypeName(p.refParamType);
} else if !String_Eq(p.refParamType.typeName, "") {
out.typeName = p.refParamType.typeName;
} else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
out.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
@@ -681,7 +765,9 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
sym.name = p.name;
sym.typeKind = Lcx_ResolveTypeKind(p.refParamType);
// Build typeName same as Lcx_LowerParam
if !String_Eq(p.refParamType.typeName, "") {
if p.refParamType.kind == tekFunc {
sym.typeName = Lcx_BuildFuncTypeName(p.refParamType);
} else if !String_Eq(p.refParamType.typeName, "") {
sym.typeName = p.refParamType.typeName;
} else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
sym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");