diff --git a/_test_funcptr/bux.toml b/_test_funcptr/bux.toml new file mode 100644 index 0000000..d23b662 --- /dev/null +++ b/_test_funcptr/bux.toml @@ -0,0 +1,2 @@ +name = "test_funcptr" +version = "0.1.0" diff --git a/_test_funcptr/src/Main.bux b/_test_funcptr/src/Main.bux new file mode 100644 index 0000000..074156c --- /dev/null +++ b/_test_funcptr/src/Main.bux @@ -0,0 +1,17 @@ +module Main { + +func Apply(x: int, op: func(int) -> int) -> int { + return op(x); +} + +func Double(n: int) -> int { + return n * 2; +} + +func main() -> int { + let result: int = Apply(5, &Double); + if result != 10 { return 1; } + return 0; +} + +} diff --git a/bootstrap/ast.nim b/bootstrap/ast.nim index 5003fae..4fe20c5 100644 --- a/bootstrap/ast.nim +++ b/bootstrap/ast.nim @@ -33,6 +33,7 @@ type tekDynRef ## &dyn Trait — trait object (fat pointer) tekTuple tekSelf + tekFunc TypeExpr* = ref object loc*: SourceLocation @@ -54,6 +55,9 @@ type tupleElements*: seq[TypeExpr] of tekSelf: discard + of tekFunc: + funcParams*: seq[TypeExpr] + funcRet*: TypeExpr # --------------------------------------------------------------------------- # Patterns diff --git a/bootstrap/hir_lower.nim b/bootstrap/hir_lower.nim index ed4da6c..187641d 100644 --- a/bootstrap/hir_lower.nim +++ b/bootstrap/hir_lower.nim @@ -264,6 +264,12 @@ proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type = of tekDynRef: return makeDynRef(te.dynInterface) of tekPointer: return makePointer(ctx.resolveTypeExpr(te.pointerPointee)) of tekSlice: return makeSlice(ctx.resolveTypeExpr(te.sliceElement)) + of tekFunc: + var params: seq[Type] = @[] + for p in te.funcParams: + params.add(ctx.resolveTypeExpr(p)) + let ret = if te.funcRet != nil: ctx.resolveTypeExpr(te.funcRet) else: makeVoid() + return makeFunc(params, ret) else: return makeUnknown() # Forward declarations @@ -320,7 +326,7 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type = of ekUnary: case expr.exprUnaryOp of tkBang: return makeBool() - of tkAmp: return makePointer(ctx.resolveExprType(expr.exprUnaryOperand)) + of tkAmp: return makeMutRef(ctx.resolveExprType(expr.exprUnaryOperand)) of tkStar: let inner = ctx.resolveExprType(expr.exprUnaryOperand) if inner.isPointer: return inner.inner[0] diff --git a/bootstrap/lir_c_backend.nim b/bootstrap/lir_c_backend.nim index b4b6324..af16321 100644 --- a/bootstrap/lir_c_backend.nim +++ b/bootstrap/lir_c_backend.nim @@ -241,11 +241,18 @@ proc emitInstr(be: var LirCBackend, instr: LirInstr) = # ── Function emission ── -proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, string]) = +proc cParamDecl(cType, name: string): string = + ## Emit a C parameter declaration, handling function-pointer syntax. + if cType.contains("(*)"): + return cType.replace("(*)", "(*" & name & ")") + else: + return cType & " " & name + +proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, string], funcPtrTypes: Table[string, string]) = var paramsStr = "" for i, p in f.params: if i > 0: paramsStr.add(", ") - paramsStr.add(&"{p.cType} {p.name}") + paramsStr.add(cParamDecl(p.cType, p.name)) if f.params.len == 0: paramsStr = "void" @@ -318,7 +325,12 @@ proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, strin newType = be.tempTypes[instr.extra[0].strVal] elif instr.src2.kind == lvkVar and varTypes.hasKey(instr.src2.strVal): newType = varTypes[instr.src2.strVal] - of lirAddrOf, lirFieldPtr, lirArrowFieldPtr, lirIndexPtr, lirPtrAdd: + of lirAddrOf: + if funcPtrTypes.hasKey(instr.src.strVal): + newType = funcPtrTypes[instr.src.strVal] + else: + newType = "void*"; + of lirFieldPtr, lirArrowFieldPtr, lirIndexPtr, lirPtrAdd: newType = "void*" of lirAdd, lirSub, lirMul, lirDiv, lirMod, lirNeg, lirCmpEq, lirCmpNe, lirCmpLt, lirCmpLe, lirCmpGt, lirCmpGe, @@ -342,7 +354,7 @@ proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, strin let ct = be.tempTypes[instr.dst.strVal] if ct != "": declared.add(instr.dst.strVal) - be.emitLine(&"{ct} {instr.dst.strVal};") + be.emitLine(cParamDecl(ct, instr.dst.strVal) & ";") # ── Pass 4: emit instructions ── for instr in f.instrs: @@ -402,6 +414,11 @@ proc typeToCStr(typ: Type): string = of "float64": return "double" of "bool": return "bool" else: return typ.name + of tkFunc: + if typ.inner.len == 0: return "void (*)(void)" + let params = typ.inner[0..^2].mapIt(typeToCStr(it)).join(", ") + let ret = typeToCStr(typ.inner[^1]) + return ret & " (*)(" & params & ")" else: return "int" proc emitStructDef(be: var LirCBackend, name: string, fields: seq[tuple[name: string, typ: Type]]) = @@ -483,6 +500,17 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s for f in module.externFuncs: funcRetTypes[f.name] = typeToCStr(f.retType) + # Build function-pointer type lookup table (for address-of) + var funcPtrTypes = initTable[string, string]() + for f in module.funcs: + let params = f.params.mapIt(typeToCStr(it.typ)).join(", ") + let ret = typeToCStr(f.retType) + funcPtrTypes[f.name] = ret & " (*)(" & params & ")" + for f in module.externFuncs: + let params = f.params.mapIt(typeToCStr(it.typ)).join(", ") + let ret = typeToCStr(f.retType) + funcPtrTypes[f.name] = ret & " (*)(" & params & ")" + # Header be.emitLine("/* Generated by Bux Compiler (LIR backend) */") be.emitLine("#include ") @@ -512,7 +540,7 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s let rt = typeToCStr(ef.retType) var params: seq[string] = @[] for p in ef.params: - params.add(&"{typeToCStr(p.typ)} {p.name}") + params.add(cParamDecl(typeToCStr(p.typ), p.name)) if params.len == 0: params.add("void") be.emitLine(&"extern {rt} {ef.name}({params.join(\", \")});") be.emitLine("") @@ -559,7 +587,7 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s let rt = typeToCStr(f.retType) var params: seq[string] = @[] for p in f.params: - params.add(&"{typeToCStr(p.typ)} {p.name}") + params.add(cParamDecl(typeToCStr(p.typ), p.name)) if params.len == 0: params.add("void") be.emitLine(&"{rt} {f.name}({params.join(\", \")});") be.emitLine("") @@ -573,7 +601,7 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s for m in iface.methods: var paramCTypes: seq[string] = @["void* self"] for i in 1 ..< m.params.len: - paramCTypes.add(typeToCStr(m.params[i]) & " param") + paramCTypes.add(cParamDecl(typeToCStr(m.params[i]), "param")) let rt = typeToCStr(m.ret) be.emitLine(&"{rt} (*{m.name})({paramCTypes.join(\", \")});") be.indent -= 1 @@ -600,7 +628,7 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s # Emit all LIR functions for f in builder.funcs: - be.emitFunc(f, funcRetTypes) + be.emitFunc(f, funcRetTypes, funcPtrTypes) # C main wrapper var hasMain = false diff --git a/bootstrap/lir_lower.nim b/bootstrap/lir_lower.nim index 3428e99..515c355 100644 --- a/bootstrap/lir_lower.nim +++ b/bootstrap/lir_lower.nim @@ -105,6 +105,11 @@ proc typeToCStr(typ: Type): string = of "float64": return "double" of "bool": return "bool" else: return typ.name + of tkFunc: + if typ.inner.len == 0: return "void (*)(void)" + let params = typ.inner[0..^2].mapIt(typeToCStr(it)).join(", ") + let ret = typeToCStr(typ.inner[^1]) + return ret & " (*)(" & params & ")" else: return "int" proc hirTypeToC(ctx: var LowerToLirCtx, node: HirNode): string = diff --git a/bootstrap/parser.nim b/bootstrap/parser.nim index 223ffb7..ad34094 100644 --- a/bootstrap/parser.nim +++ b/bootstrap/parser.nim @@ -248,6 +248,20 @@ proc parseBaseType(p: var Parser): TypeExpr = discard p.advance() discard p.expect(tkRParen, "expected ')' to close tuple type") return TypeExpr(kind: tekTuple, loc: loc, tupleElements: elems) + of tkFunc: + discard p.advance() # func + discard p.expect(tkLParen, "expected '(' after 'func'") + var params: seq[TypeExpr] = @[] + while not p.check(tkRParen) and not p.isAtEnd: + params.add(p.parseType()) + if p.check(tkComma): + discard p.advance() + discard p.expect(tkRParen, "expected ')' to close func param types") + var ret: TypeExpr = nil + if p.check(tkArrow): + discard p.advance() + ret = p.parseType() + return TypeExpr(kind: tekFunc, loc: loc, funcParams: params, funcRet: ret) else: p.emitError(loc, "expected type expression") return TypeExpr(kind: tekNamed, loc: loc, typeName: "") diff --git a/bootstrap/sema.nim b/bootstrap/sema.nim index 8cec0fc..29825b5 100644 --- a/bootstrap/sema.nim +++ b/bootstrap/sema.nim @@ -126,6 +126,10 @@ proc typeExprReferencesTypeParam(te: TypeExpr, name: string): bool = of tekTuple: for elem in te.tupleElements: if typeExprReferencesTypeParam(elem, name): return true + of tekFunc: + for p in te.funcParams: + if typeExprReferencesTypeParam(p, name): return true + return typeExprReferencesTypeParam(te.funcRet, name) of tekSelf: return false @@ -278,6 +282,12 @@ proc resolveType(sema: var Sema, te: TypeExpr): Type = return makeTuple(elems) of tekSelf: return makeNamed("self") + of tekFunc: + var params: seq[Type] = @[] + for p in te.funcParams: + params.add(sema.resolveType(p)) + let ret = if te.funcRet != nil: sema.resolveType(te.funcRet) else: makeVoid() + return makeFunc(params, ret) # --------------------------------------------------------------------------- # First pass: collect global symbols diff --git a/bootstrap/types.nim b/bootstrap/types.nim index 4053e85..7ff978a 100644 --- a/bootstrap/types.nim +++ b/bootstrap/types.nim @@ -183,6 +183,10 @@ proc isAssignableTo*(a, b: Type): bool = return true if b.isDynRef and a.isMutRef: return true + # &mut func(...) -> func(...) (function pointer decay) + if a.isMutRef and b.kind == tkFunc: + if a.inner.len > 0 and a.inner[0].kind == tkFunc: + return true return false # String representation