feat(bootstrap): function pointer types + C emission
- Add tekFunc AST node and parser support for func(Params) -> Ret - Add tkFunc type resolution in sema and hir_lower - Add &FuncName address-taking yielding tkMutRef(tkFunc) - Fix C emission: function pointer params use Ret (*name)(Params) syntax via cParamDecl helper that embeds the name inside (*) - Fix forward declarations and temp declarations for function pointers - Add funcPtrTypes table in C backend so &Fn temps get proper type - Add _test_funcptr integration test
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
name = "test_funcptr"
|
||||
version = "0.1.0"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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 <stdio.h>")
|
||||
@@ -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
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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: "")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user