From 0c41c7bb252c7ae599808eff8496c5a2c3d38d4f Mon Sep 17 00:00:00 2001 From: dimgigov Date: Mon, 8 Jun 2026 23:39:15 +0300 Subject: [PATCH] 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 --- src/ast.bux | 19 ++++-- src/c_backend.bux | 81 ++++++++++++++--------- src/hir_lower.bux | 162 +++++++++++++++++++++++++++++++++++----------- src/parser.bux | 39 +++++++++++ src/scope.bux | 5 +- src/sema.bux | 76 ++++++++++++++++++++-- src/types.bux | 1 + 7 files changed, 305 insertions(+), 78 deletions(-) diff --git a/src/ast.bux b/src/ast.bux index a976e13..99f26ea 100644 --- a/src/ast.bux +++ b/src/ast.bux @@ -28,19 +28,28 @@ const tekSlice: int = 2; const tekPointer: int = 3; const tekTuple: int = 4; const tekSelf: int = 5; +const tekFunc: int = 6; + +struct TypeExprList { + te: *TypeExpr, + next: *TypeExprList, +} struct TypeExpr { kind: int, line: uint32, column: uint32, - typeName: String, // for tekNamed - pathStr: String, // for tekPath (segments joined with ::) - pathCount: int, // number of path segments - typeArgName0: String, // up to 2 type args + typeName: String, // for tekNamed / diagnostic name for tekFunc + pathStr: String, // for tekPath (segments joined with ::) + pathCount: int, // number of path segments + typeArgName0: String, // up to 2 type args typeArgName1: String, typeArgCount: int, - sliceElement: *TypeExpr, // for tekSlice + sliceElement: *TypeExpr, // for tekSlice pointerPointee: *TypeExpr, // for tekPointer + funcParams: *TypeExprList, // for tekFunc + funcRet: *TypeExpr, // for tekFunc + funcParamCount: int, // for tekFunc } // --------------------------------------------------------------------------- diff --git a/src/c_backend.bux b/src/c_backend.bux index ea6f759..c89433b 100644 --- a/src/c_backend.bux +++ b/src/c_backend.bux @@ -31,9 +31,19 @@ func CBackend_TypeToC(kind: int) -> String { if kind == tyFloat64 { return "float64"; } if kind == tyPointer { return "void*"; } if kind == tyNamed { return "int"; } + if kind == tyFunc { return "void (*)(void)"; } return "int"; } +// Emit a C parameter/variable declaration, embedding the name inside function-pointer syntax. +func CBE_CParamDecl(typeStr: String, name: String) -> String { + if String_Contains(typeStr, "(*)") { + let replacement: String = String_Concat("(*", String_Concat(name, ")")); + return String_Replace(typeStr, "(*)", replacement); + } + return String_Concat(typeStr, String_Concat(" ", name)); +} + func CBackend_OpToC(op: int) -> String { if op == tkPlus { return "+"; } if op == tkMinus { return "-"; } @@ -263,6 +273,38 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) { return; } + // Indirect call through function pointer + if kind == hCallIndirect { + CBE_EmitExpr(cbe, node.child1); + StringBuilder_Append(&cbe.sb, "("); + var needsComma: bool = false; + if node.child2 != null as *HirNode { + CBE_EmitExpr(cbe, node.child2); + needsComma = true; + } + if node.child3 != null as *HirNode { + if needsComma { + 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, ", "); + } + CBE_EmitExpr(cbe, curExtra.node); + needsComma = true; + curExtra = curExtra.next; + ai = ai + 1; + } + StringBuilder_Append(&cbe.sb, ")"); + return; + } + // spawn Callee(args) if kind == hSpawn { StringBuilder_Append(&cbe.sb, "bux_async_spawn("); @@ -310,13 +352,11 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) { // Alloca if kind == hAlloca { + var ct: String = "int"; if !String_Eq(node.typeName, "") { - StringBuilder_Append(&cbe.sb, node.typeName); - } else { - StringBuilder_Append(&cbe.sb, "int"); + ct = node.typeName; } - StringBuilder_Append(&cbe.sb, " "); - StringBuilder_Append(&cbe.sb, node.strValue); + StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ct, node.strValue)); return; } @@ -324,13 +364,11 @@ func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) { if kind == hStore { if node.child1 != null as *HirNode && node.child1.kind == hAlloca { // Declaration with initializer: Type x = value; + var ct: String = CBackend_TypeToC(node.child1.intValue); if !String_Eq(node.child1.typeName, "") { - StringBuilder_Append(&cbe.sb, node.child1.typeName); - } else { - StringBuilder_Append(&cbe.sb, CBackend_TypeToC(node.child1.intValue)); + ct = node.child1.typeName; } - StringBuilder_Append(&cbe.sb, " "); - StringBuilder_Append(&cbe.sb, node.child1.strValue); + StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ct, node.child1.strValue)); if node.child2 != null as *HirNode { StringBuilder_Append(&cbe.sb, " = "); CBE_EmitExpr(cbe, node.child2); @@ -624,26 +662,11 @@ func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) { if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; } if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; } if i == 8 { pname = f.param8.name; ptype = f.param8.typeName; } - // Emit type - if String_Eq(ptype, "String") || String_Eq(ptype, "str") { - StringBuilder_Append(&cbe.sb, "String "); - } else if String_Eq(ptype, "bool") { - StringBuilder_Append(&cbe.sb, "bool "); - } else if String_Eq(ptype, "int") { - StringBuilder_Append(&cbe.sb, "int "); - } else if String_Eq(ptype, "int64") { - StringBuilder_Append(&cbe.sb, "int64 "); - } else if String_Eq(ptype, "uint") { - StringBuilder_Append(&cbe.sb, "uint "); - } else if String_Eq(ptype, "float64") { - StringBuilder_Append(&cbe.sb, "float64 "); - } else if !String_Eq(ptype, "") { - StringBuilder_Append(&cbe.sb, ptype); - StringBuilder_Append(&cbe.sb, " "); - } else { - StringBuilder_Append(&cbe.sb, "int "); // fallback + // Emit type + name + if String_Eq(ptype, "") { + ptype = "int"; } - StringBuilder_Append(&cbe.sb, pname); + StringBuilder_Append(&cbe.sb, CBE_CParamDecl(ptype, pname)); i = i + 1; } diff --git a/src/hir_lower.bux b/src/hir_lower.bux index 5ef1567..faed40e 100644 --- a/src/hir_lower.bux +++ b/src/hir_lower.bux @@ -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, "*"); diff --git a/src/parser.bux b/src/parser.bux index fc7e340..2bb4b78 100644 --- a/src/parser.bux +++ b/src/parser.bux @@ -173,6 +173,45 @@ func parserParseType(p: *Parser) -> *TypeExpr { return te; } + // func(Params) -> Ret + if kindTok == tkFunc { + discard parserAdvance(p); + discard parserExpect(p, tkLParen, "expected '(' after 'func'"); + var params: *TypeExprList = null as *TypeExprList; + var paramsTail: *TypeExprList = null as *TypeExprList; + var count: int = 0; + while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { + let paramTe: *TypeExpr = parserParseType(p); + let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList; + node.te = paramTe; + node.next = null as *TypeExprList; + if params == null as *TypeExprList { + params = node; + } else { + paramsTail.next = node; + } + paramsTail = node; + count = count + 1; + if parserCheck(p, tkComma) { + discard parserAdvance(p); + } + } + discard parserExpect(p, tkRParen, "expected ')' after func params"); + var ret: *TypeExpr = null as *TypeExpr; + if parserCheck(p, tkArrow) { + discard parserAdvance(p); + ret = parserParseType(p); + } + let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; + te.kind = tekFunc; + te.line = line; + te.column = col; + te.funcParams = params; + te.funcRet = ret; + te.funcParamCount = count; + return te; + } + // name let nameTok: LexToken = parserExpect(p, tkIdent, "expected type name"); let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; diff --git a/src/scope.bux b/src/scope.bux index d08d90d..d9a02b1 100644 --- a/src/scope.bux +++ b/src/scope.bux @@ -16,6 +16,7 @@ struct Symbol { name: String; typeKind: int; typeName: String; + refType: *TypeExpr; // original type expression (for func types, etc.) isMutable: bool; isPublic: bool; decl: *Decl; // associated declaration (for funcs, structs, enums) @@ -72,7 +73,7 @@ func Scope_Lookup(scope: *Scope, name: String) -> Symbol { } cur = cur.parent; } - var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl }; + var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl }; return empty; } @@ -84,7 +85,7 @@ func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol { } i = i + 1; } - var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl }; + var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl }; return empty; } diff --git a/src/sema.bux b/src/sema.bux index 3c65b0e..1b3adbb 100644 --- a/src/sema.bux +++ b/src/sema.bux @@ -46,11 +46,54 @@ func Sema_ZeroInitSymbol(sym: *Symbol) { sym.name = ""; sym.typeKind = 0; sym.typeName = ""; + sym.refType = null as *TypeExpr; sym.isMutable = false; sym.isPublic = false; sym.decl = null as *Decl; } +// --------------------------------------------------------------------------- +// Build a tekFunc TypeExpr from a function declaration +// --------------------------------------------------------------------------- + +func Sema_BuildFuncTypeExprFromDecl(decl: *Decl) -> *TypeExpr { + let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; + te.kind = tekFunc; + te.line = decl.line; + te.column = decl.column; + te.funcRet = decl.retType; + te.funcParamCount = decl.paramCount; + var head: *TypeExprList = null as *TypeExprList; + var tail: *TypeExprList = null as *TypeExprList; + var i: int = 0; + while i < decl.paramCount && i < 9 { + var p: Param; + if i == 0 { p = decl.param0; } + else if i == 1 { p = decl.param1; } + else if i == 2 { p = decl.param2; } + else if i == 3 { p = decl.param3; } + else if i == 4 { p = decl.param4; } + else if i == 5 { p = decl.param5; } + else if i == 6 { p = decl.param6; } + else if i == 7 { p = decl.param7; } + else { p = decl.param8; } + if p.refParamType != null as *TypeExpr { + let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList; + node.te = p.refParamType; + node.next = null as *TypeExprList; + if head == null as *TypeExprList { + head = node; + } else { + tail.next = node; + } + tail = node; + } + i = i + 1; + } + te.funcParams = head; + return te; +} + // --------------------------------------------------------------------------- // Type resolution from TypeExpr → Type constants // --------------------------------------------------------------------------- @@ -63,6 +106,10 @@ func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int { return tyPointer; } + if te.kind == tekFunc { + return tyFunc; + } + if String_Eq(name, "void") { return tyVoid; } if String_Eq(name, "bool") { return tyBool; } if String_Eq(name, "bool8") { return tyBool8; } @@ -271,7 +318,9 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { Sema_EmitError(sema, expr.line, expr.column, errMsg2); return tyUnknown; } - if sym.typeName != null as String && !String_Eq(sym.typeName, "") { + if sym.refType != null as *TypeExpr { + expr.refType = sym.refType; + } else if sym.typeName != null as String && !String_Eq(sym.typeName, "") { let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; te.kind = tekNamed; te.typeName = sym.typeName; @@ -318,7 +367,12 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { let op: int = expr.intValue; if op == tkBang { return tyBool; } if op == tkStar { return tyUnknown; } // dereference — resolve pointee - if op == tkAmp { return tyPointer; } + if op == tkAmp { + if expr.child1.refType != null as *TypeExpr { + expr.refType = expr.child1.refType; + } + return tyPointer; + } return operand; } @@ -332,14 +386,23 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { // Call if kind == ekCall { - discard Sema_CheckExpr(sema, expr.child1); + let calleeType: int = Sema_CheckExpr(sema, expr.child1); Sema_ResolveCallArgs(sema, expr); var arg: *ExprList = expr.callArgs; while arg != null as *ExprList { discard Sema_CheckExpr(sema, arg.expr); arg = arg.next; } - // Try to resolve return type from function declaration + // Indirect call through function-typed value + if calleeType == tyFunc { + if expr.child1.refType != null as *TypeExpr && expr.child1.refType.kind == tekFunc { + if expr.child1.refType.funcRet != null as *TypeExpr { + return Sema_ResolveType(sema, expr.child1.refType.funcRet); + } + return tyVoid; + } + } + // Direct call to named function if expr.child1.kind == ekIdent { let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue); if sym.kind == skFunc { @@ -421,7 +484,9 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) { sym.name = stmt.strValue; sym.typeKind = initType; sym.typeName = ""; + sym.refType = null as *TypeExpr; if stmt.refStmtType != null as *TypeExpr { + sym.refType = stmt.refStmtType; if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr { sym.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*"); } else { @@ -560,6 +625,7 @@ func Sema_CollectGlobals(sema: *Sema) { sym.kind = skFunc; sym.name = decl.strValue; sym.typeKind = tyFunc; + sym.refType = Sema_BuildFuncTypeExprFromDecl(decl); sym.isPublic = decl.isPublic; sym.decl = decl; discard Scope_Define(sema.scope, sym); @@ -629,6 +695,7 @@ func Sema_CollectGlobals(sema: *Sema) { if decl.constType != null as *TypeExpr { sym.typeKind = Sema_ResolveType(sema, decl.constType); sym.typeName = decl.constType.typeName; + sym.refType = decl.constType; } else { sym.typeKind = tyInt; } @@ -810,6 +877,7 @@ func Sema_Analyze(mod: *Module) -> *Sema { pSym.kind = skVar; if p != null as *Param && p.refParamType != null as *TypeExpr { pSym.typeKind = Sema_ResolveType(s, p.refParamType); + pSym.refType = p.refParamType; if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr { pSym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*"); } else { diff --git a/src/types.bux b/src/types.bux index f3bc6de..281fdc7 100644 --- a/src/types.bux +++ b/src/types.bux @@ -183,6 +183,7 @@ func Type_ToString(t: Type) -> String { if t.kind == tyNamed { return t.name; } if t.kind == tyTypeParam { return t.name; } if t.kind == tyPointer { return String_Concat("*", t.innerName1); } + if t.kind == tyFunc { return t.name; } return "?"; } }