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)
|
tekDynRef ## &dyn Trait — trait object (fat pointer)
|
||||||
tekTuple
|
tekTuple
|
||||||
tekSelf
|
tekSelf
|
||||||
|
tekFunc
|
||||||
|
|
||||||
TypeExpr* = ref object
|
TypeExpr* = ref object
|
||||||
loc*: SourceLocation
|
loc*: SourceLocation
|
||||||
@@ -54,6 +55,9 @@ type
|
|||||||
tupleElements*: seq[TypeExpr]
|
tupleElements*: seq[TypeExpr]
|
||||||
of tekSelf:
|
of tekSelf:
|
||||||
discard
|
discard
|
||||||
|
of tekFunc:
|
||||||
|
funcParams*: seq[TypeExpr]
|
||||||
|
funcRet*: TypeExpr
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Patterns
|
# Patterns
|
||||||
|
|||||||
@@ -264,6 +264,12 @@ proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type =
|
|||||||
of tekDynRef: return makeDynRef(te.dynInterface)
|
of tekDynRef: return makeDynRef(te.dynInterface)
|
||||||
of tekPointer: return makePointer(ctx.resolveTypeExpr(te.pointerPointee))
|
of tekPointer: return makePointer(ctx.resolveTypeExpr(te.pointerPointee))
|
||||||
of tekSlice: return makeSlice(ctx.resolveTypeExpr(te.sliceElement))
|
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()
|
else: return makeUnknown()
|
||||||
|
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
@@ -320,7 +326,7 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
of ekUnary:
|
of ekUnary:
|
||||||
case expr.exprUnaryOp
|
case expr.exprUnaryOp
|
||||||
of tkBang: return makeBool()
|
of tkBang: return makeBool()
|
||||||
of tkAmp: return makePointer(ctx.resolveExprType(expr.exprUnaryOperand))
|
of tkAmp: return makeMutRef(ctx.resolveExprType(expr.exprUnaryOperand))
|
||||||
of tkStar:
|
of tkStar:
|
||||||
let inner = ctx.resolveExprType(expr.exprUnaryOperand)
|
let inner = ctx.resolveExprType(expr.exprUnaryOperand)
|
||||||
if inner.isPointer: return inner.inner[0]
|
if inner.isPointer: return inner.inner[0]
|
||||||
|
|||||||
@@ -241,11 +241,18 @@ proc emitInstr(be: var LirCBackend, instr: LirInstr) =
|
|||||||
|
|
||||||
# ── Function emission ──
|
# ── 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 = ""
|
var paramsStr = ""
|
||||||
for i, p in f.params:
|
for i, p in f.params:
|
||||||
if i > 0: paramsStr.add(", ")
|
if i > 0: paramsStr.add(", ")
|
||||||
paramsStr.add(&"{p.cType} {p.name}")
|
paramsStr.add(cParamDecl(p.cType, p.name))
|
||||||
if f.params.len == 0:
|
if f.params.len == 0:
|
||||||
paramsStr = "void"
|
paramsStr = "void"
|
||||||
|
|
||||||
@@ -318,7 +325,12 @@ proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, strin
|
|||||||
newType = be.tempTypes[instr.extra[0].strVal]
|
newType = be.tempTypes[instr.extra[0].strVal]
|
||||||
elif instr.src2.kind == lvkVar and varTypes.hasKey(instr.src2.strVal):
|
elif instr.src2.kind == lvkVar and varTypes.hasKey(instr.src2.strVal):
|
||||||
newType = varTypes[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*"
|
newType = "void*"
|
||||||
of lirAdd, lirSub, lirMul, lirDiv, lirMod, lirNeg,
|
of lirAdd, lirSub, lirMul, lirDiv, lirMod, lirNeg,
|
||||||
lirCmpEq, lirCmpNe, lirCmpLt, lirCmpLe, lirCmpGt, lirCmpGe,
|
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]
|
let ct = be.tempTypes[instr.dst.strVal]
|
||||||
if ct != "":
|
if ct != "":
|
||||||
declared.add(instr.dst.strVal)
|
declared.add(instr.dst.strVal)
|
||||||
be.emitLine(&"{ct} {instr.dst.strVal};")
|
be.emitLine(cParamDecl(ct, instr.dst.strVal) & ";")
|
||||||
|
|
||||||
# ── Pass 4: emit instructions ──
|
# ── Pass 4: emit instructions ──
|
||||||
for instr in f.instrs:
|
for instr in f.instrs:
|
||||||
@@ -402,6 +414,11 @@ proc typeToCStr(typ: Type): string =
|
|||||||
of "float64": return "double"
|
of "float64": return "double"
|
||||||
of "bool": return "bool"
|
of "bool": return "bool"
|
||||||
else: return typ.name
|
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"
|
else: return "int"
|
||||||
|
|
||||||
proc emitStructDef(be: var LirCBackend, name: string, fields: seq[tuple[name: string, typ: Type]]) =
|
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:
|
for f in module.externFuncs:
|
||||||
funcRetTypes[f.name] = typeToCStr(f.retType)
|
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
|
# Header
|
||||||
be.emitLine("/* Generated by Bux Compiler (LIR backend) */")
|
be.emitLine("/* Generated by Bux Compiler (LIR backend) */")
|
||||||
be.emitLine("#include <stdio.h>")
|
be.emitLine("#include <stdio.h>")
|
||||||
@@ -512,7 +540,7 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s
|
|||||||
let rt = typeToCStr(ef.retType)
|
let rt = typeToCStr(ef.retType)
|
||||||
var params: seq[string] = @[]
|
var params: seq[string] = @[]
|
||||||
for p in ef.params:
|
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")
|
if params.len == 0: params.add("void")
|
||||||
be.emitLine(&"extern {rt} {ef.name}({params.join(\", \")});")
|
be.emitLine(&"extern {rt} {ef.name}({params.join(\", \")});")
|
||||||
be.emitLine("")
|
be.emitLine("")
|
||||||
@@ -559,7 +587,7 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s
|
|||||||
let rt = typeToCStr(f.retType)
|
let rt = typeToCStr(f.retType)
|
||||||
var params: seq[string] = @[]
|
var params: seq[string] = @[]
|
||||||
for p in f.params:
|
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")
|
if params.len == 0: params.add("void")
|
||||||
be.emitLine(&"{rt} {f.name}({params.join(\", \")});")
|
be.emitLine(&"{rt} {f.name}({params.join(\", \")});")
|
||||||
be.emitLine("")
|
be.emitLine("")
|
||||||
@@ -573,7 +601,7 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s
|
|||||||
for m in iface.methods:
|
for m in iface.methods:
|
||||||
var paramCTypes: seq[string] = @["void* self"]
|
var paramCTypes: seq[string] = @["void* self"]
|
||||||
for i in 1 ..< m.params.len:
|
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)
|
let rt = typeToCStr(m.ret)
|
||||||
be.emitLine(&"{rt} (*{m.name})({paramCTypes.join(\", \")});")
|
be.emitLine(&"{rt} (*{m.name})({paramCTypes.join(\", \")});")
|
||||||
be.indent -= 1
|
be.indent -= 1
|
||||||
@@ -600,7 +628,7 @@ proc emitModule*(be: var LirCBackend, builder: LirBuilder, module: HirModule): s
|
|||||||
|
|
||||||
# Emit all LIR functions
|
# Emit all LIR functions
|
||||||
for f in builder.funcs:
|
for f in builder.funcs:
|
||||||
be.emitFunc(f, funcRetTypes)
|
be.emitFunc(f, funcRetTypes, funcPtrTypes)
|
||||||
|
|
||||||
# C main wrapper
|
# C main wrapper
|
||||||
var hasMain = false
|
var hasMain = false
|
||||||
|
|||||||
@@ -105,6 +105,11 @@ proc typeToCStr(typ: Type): string =
|
|||||||
of "float64": return "double"
|
of "float64": return "double"
|
||||||
of "bool": return "bool"
|
of "bool": return "bool"
|
||||||
else: return typ.name
|
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"
|
else: return "int"
|
||||||
|
|
||||||
proc hirTypeToC(ctx: var LowerToLirCtx, node: HirNode): string =
|
proc hirTypeToC(ctx: var LowerToLirCtx, node: HirNode): string =
|
||||||
|
|||||||
@@ -248,6 +248,20 @@ proc parseBaseType(p: var Parser): TypeExpr =
|
|||||||
discard p.advance()
|
discard p.advance()
|
||||||
discard p.expect(tkRParen, "expected ')' to close tuple type")
|
discard p.expect(tkRParen, "expected ')' to close tuple type")
|
||||||
return TypeExpr(kind: tekTuple, loc: loc, tupleElements: elems)
|
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:
|
else:
|
||||||
p.emitError(loc, "expected type expression")
|
p.emitError(loc, "expected type expression")
|
||||||
return TypeExpr(kind: tekNamed, loc: loc, typeName: "")
|
return TypeExpr(kind: tekNamed, loc: loc, typeName: "")
|
||||||
|
|||||||
@@ -126,6 +126,10 @@ proc typeExprReferencesTypeParam(te: TypeExpr, name: string): bool =
|
|||||||
of tekTuple:
|
of tekTuple:
|
||||||
for elem in te.tupleElements:
|
for elem in te.tupleElements:
|
||||||
if typeExprReferencesTypeParam(elem, name): return true
|
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:
|
of tekSelf:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
@@ -278,6 +282,12 @@ proc resolveType(sema: var Sema, te: TypeExpr): Type =
|
|||||||
return makeTuple(elems)
|
return makeTuple(elems)
|
||||||
of tekSelf:
|
of tekSelf:
|
||||||
return makeNamed("self")
|
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
|
# First pass: collect global symbols
|
||||||
|
|||||||
@@ -183,6 +183,10 @@ proc isAssignableTo*(a, b: Type): bool =
|
|||||||
return true
|
return true
|
||||||
if b.isDynRef and a.isMutRef:
|
if b.isDynRef and a.isMutRef:
|
||||||
return true
|
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
|
return false
|
||||||
|
|
||||||
# String representation
|
# String representation
|
||||||
|
|||||||
Reference in New Issue
Block a user