feat: generic methods on generic structs + method call auto-addressing
- Auto-register func Type_Method<T>(self: *Type<T>) as methods in sema - Add type param awareness to sema resolveType for generic function bodies - Add lazy monomorphization for generic struct method calls in HIR lowering - Track varTypeExprs in LowerCtx for local variable type inference - Fix ekSelf in both sema and hir_lower to resolve actual parameter type - Fix lowerFunc to use substituteType for param/return types (handles pointers to generic structs) - Auto-address value receivers when method expects pointer (e.g., b.Get() where self: *Box<T>) - Add C forward declarations for all functions to fix ordering issues - Relax type checks for tkTypeParam in assignments and arguments - Update generics_struct example with Box_Get, Box_Set, Pair_GetFirst/Second
This commit is contained in:
@@ -10,15 +10,41 @@ struct Pair<T, U> {
|
|||||||
second: U,
|
second: U,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Box_Get<T>(self: *Box<T>) -> T {
|
||||||
|
return self.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Box_Set<T>(self: *Box<T>, value: T) {
|
||||||
|
self.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Pair_GetFirst<T, U>(self: *Pair<T, U>) -> T {
|
||||||
|
return self.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Pair_GetSecond<T, U>(self: *Pair<T, U>) -> U {
|
||||||
|
return self.second;
|
||||||
|
}
|
||||||
|
|
||||||
func Main() -> int {
|
func Main() -> int {
|
||||||
let b: Box<int> = Box<int> { value: 42 };
|
let b: Box<int> = Box<int> { value: 42 };
|
||||||
PrintLine("Box value:");
|
PrintLine("Box value:");
|
||||||
PrintInt(b.value);
|
PrintInt(b.Get());
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
b.Set(100);
|
||||||
|
PrintLine("Box after Set(100):");
|
||||||
|
PrintInt(b.Get());
|
||||||
PrintLine("");
|
PrintLine("");
|
||||||
|
|
||||||
let p: Pair<int, String> = Pair<int, String> { first: 10, second: "hello" };
|
let p: Pair<int, String> = Pair<int, String> { first: 10, second: "hello" };
|
||||||
PrintLine("Pair first:");
|
PrintLine("Pair first:");
|
||||||
PrintInt(p.first);
|
PrintInt(p.GetFirst());
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
let bp: *Box<int> = &b;
|
||||||
|
PrintLine("Box via pointer:");
|
||||||
|
PrintInt(bp.Get());
|
||||||
PrintLine("");
|
PrintLine("");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -490,6 +490,17 @@ proc emitModule*(be: var CBackend, module: HirModule): string =
|
|||||||
for e in module.enums:
|
for e in module.enums:
|
||||||
be.emitEnum(e.name, e.variants)
|
be.emitEnum(e.name, e.variants)
|
||||||
|
|
||||||
|
# Forward declarations for all functions
|
||||||
|
for f in module.funcs:
|
||||||
|
let retType = typeToC(f.retType)
|
||||||
|
var params: seq[string] = @[]
|
||||||
|
for p in f.params:
|
||||||
|
params.add(typeToC(p.typ) & " " & p.name)
|
||||||
|
if params.len == 0:
|
||||||
|
params.add("void")
|
||||||
|
be.emitLine(retType & " " & f.name & "(" & params.join(", ") & ");")
|
||||||
|
be.emitLine("")
|
||||||
|
|
||||||
# Function definitions
|
# Function definitions
|
||||||
var hasMain = false
|
var hasMain = false
|
||||||
for f in module.funcs:
|
for f in module.funcs:
|
||||||
|
|||||||
+139
-32
@@ -16,6 +16,11 @@ type
|
|||||||
genericStructs*: Table[string, Decl] # Generic struct declarations
|
genericStructs*: Table[string, Decl] # Generic struct declarations
|
||||||
generatedStructInsts*: Table[string, bool] # Track generated struct instantiations
|
generatedStructInsts*: Table[string, bool] # Track generated struct instantiations
|
||||||
extraStructs*: seq[tuple[name: string, fields: seq[tuple[name: string, typ: Type]]]]
|
extraStructs*: seq[tuple[name: string, fields: seq[tuple[name: string, typ: Type]]]]
|
||||||
|
structInstMap*: Table[string, tuple[baseName: string, typeArgs: seq[Type]]] # Mangled name -> base + args
|
||||||
|
genericFuncs*: Table[string, Decl] # Generic function declarations
|
||||||
|
generatedFuncInsts*: Table[string, bool] # Track generated function instantiations
|
||||||
|
extraFuncs*: seq[HirFunc] # Monomorphized generic methods
|
||||||
|
varTypeExprs*: Table[string, TypeExpr] # Track variable names -> type expr for generic method inference
|
||||||
|
|
||||||
proc freshName(ctx: var LowerCtx): string =
|
proc freshName(ctx: var LowerCtx): string =
|
||||||
inc ctx.varCounter
|
inc ctx.varCounter
|
||||||
@@ -124,6 +129,11 @@ proc initLowerCtx*(module: Module, sema: Sema): LowerCtx =
|
|||||||
result.genericStructs = initTable[string, Decl]()
|
result.genericStructs = initTable[string, Decl]()
|
||||||
result.generatedStructInsts = initTable[string, bool]()
|
result.generatedStructInsts = initTable[string, bool]()
|
||||||
result.extraStructs = @[]
|
result.extraStructs = @[]
|
||||||
|
result.structInstMap = initTable[string, tuple[baseName: string, typeArgs: seq[Type]]]()
|
||||||
|
result.genericFuncs = initTable[string, Decl]()
|
||||||
|
result.generatedFuncInsts = initTable[string, bool]()
|
||||||
|
result.extraFuncs = @[]
|
||||||
|
result.varTypeExprs = initTable[string, TypeExpr]()
|
||||||
|
|
||||||
proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type
|
proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type
|
||||||
|
|
||||||
@@ -143,11 +153,15 @@ proc substituteType(ctx: var LowerCtx, te: TypeExpr, subst: Table[string, Type])
|
|||||||
if not ctx.generatedStructInsts.hasKey(mangledName):
|
if not ctx.generatedStructInsts.hasKey(mangledName):
|
||||||
let genericDecl = ctx.genericStructs[te.typeName]
|
let genericDecl = ctx.genericStructs[te.typeName]
|
||||||
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
||||||
|
var concreteArgs: seq[Type] = @[]
|
||||||
for f in genericDecl.declStructFields:
|
for f in genericDecl.declStructFields:
|
||||||
let resolvedType = substituteType(ctx, f.ftype, subst)
|
let resolvedType = substituteType(ctx, f.ftype, subst)
|
||||||
fields.add((f.name, resolvedType))
|
fields.add((f.name, resolvedType))
|
||||||
|
for arg in te.typeArgs:
|
||||||
|
concreteArgs.add(substituteType(ctx, arg, subst))
|
||||||
ctx.extraStructs.add((mangledName, fields))
|
ctx.extraStructs.add((mangledName, fields))
|
||||||
ctx.generatedStructInsts[mangledName] = true
|
ctx.generatedStructInsts[mangledName] = true
|
||||||
|
ctx.structInstMap[mangledName] = (te.typeName, concreteArgs)
|
||||||
return makeNamed(mangledName)
|
return makeNamed(mangledName)
|
||||||
return ctx.resolveTypeExpr(te)
|
return ctx.resolveTypeExpr(te)
|
||||||
of tekPointer:
|
of tekPointer:
|
||||||
@@ -177,14 +191,18 @@ proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type =
|
|||||||
let genericDecl = ctx.genericStructs[te.typeName]
|
let genericDecl = ctx.genericStructs[te.typeName]
|
||||||
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
||||||
var subst = initTable[string, Type]()
|
var subst = initTable[string, Type]()
|
||||||
|
var concreteArgs: seq[Type] = @[]
|
||||||
for j, tp in genericDecl.declStructTypeParams:
|
for j, tp in genericDecl.declStructTypeParams:
|
||||||
if j < te.typeArgs.len:
|
if j < te.typeArgs.len:
|
||||||
subst[tp] = ctx.resolveTypeExpr(te.typeArgs[j])
|
subst[tp] = ctx.resolveTypeExpr(te.typeArgs[j])
|
||||||
|
for arg in te.typeArgs:
|
||||||
|
concreteArgs.add(ctx.resolveTypeExpr(arg))
|
||||||
for f in genericDecl.declStructFields:
|
for f in genericDecl.declStructFields:
|
||||||
let resolvedType = substituteType(ctx, f.ftype, subst)
|
let resolvedType = substituteType(ctx, f.ftype, subst)
|
||||||
fields.add((f.name, resolvedType))
|
fields.add((f.name, resolvedType))
|
||||||
ctx.extraStructs.add((mangledName, fields))
|
ctx.extraStructs.add((mangledName, fields))
|
||||||
ctx.generatedStructInsts[mangledName] = true
|
ctx.generatedStructInsts[mangledName] = true
|
||||||
|
ctx.structInstMap[mangledName] = (te.typeName, concreteArgs)
|
||||||
return makeNamed(mangledName)
|
return makeNamed(mangledName)
|
||||||
case te.typeName
|
case te.typeName
|
||||||
of "void": return makeVoid()
|
of "void": return makeVoid()
|
||||||
@@ -234,7 +252,10 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
# Check global scope first
|
# Check global scope first
|
||||||
let sym = ctx.globalScope.lookup(expr.exprIdent)
|
let sym = ctx.globalScope.lookup(expr.exprIdent)
|
||||||
if sym != nil and sym.typ != nil: return sym.typ
|
if sym != nil and sym.typ != nil: return sym.typ
|
||||||
# Check current function parameters
|
# Check local variables and parameters tracked in varTypeExprs
|
||||||
|
if ctx.varTypeExprs.hasKey(expr.exprIdent):
|
||||||
|
return ctx.resolveTypeExpr(ctx.varTypeExprs[expr.exprIdent])
|
||||||
|
# Check current function parameters (fallback for untracked params)
|
||||||
if ctx.currentFuncDecl != nil:
|
if ctx.currentFuncDecl != nil:
|
||||||
var params: seq[Param] = @[]
|
var params: seq[Param] = @[]
|
||||||
case ctx.currentFuncDecl.kind
|
case ctx.currentFuncDecl.kind
|
||||||
@@ -259,7 +280,17 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
return makePointer(pointeeType)
|
return makePointer(pointeeType)
|
||||||
else: discard
|
else: discard
|
||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
of ekSelf: return makeNamed("self")
|
of ekSelf:
|
||||||
|
# Look up self parameter type from current function
|
||||||
|
if ctx.currentFuncDecl != nil:
|
||||||
|
var params: seq[Param] = @[]
|
||||||
|
case ctx.currentFuncDecl.kind
|
||||||
|
of dkFunc: params = ctx.currentFuncDecl.declFuncParams
|
||||||
|
of dkExternFunc: params = ctx.currentFuncDecl.declExtFuncParams
|
||||||
|
else: discard
|
||||||
|
if params.len > 0 and params[0].name == "self" and params[0].ptype != nil:
|
||||||
|
return substituteType(ctx, params[0].ptype, ctx.typeSubst)
|
||||||
|
return makeNamed("self")
|
||||||
of ekBinary:
|
of ekBinary:
|
||||||
let left = ctx.resolveExprType(expr.exprBinaryLeft)
|
let left = ctx.resolveExprType(expr.exprBinaryLeft)
|
||||||
case expr.exprBinaryOp
|
case expr.exprBinaryOp
|
||||||
@@ -347,6 +378,32 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
return makeVoid()
|
return makeVoid()
|
||||||
else: return makeUnknown()
|
else: return makeUnknown()
|
||||||
|
|
||||||
|
proc extractGenericStructInfo(ctx: LowerCtx, te: TypeExpr): tuple[baseName: string, typeArgs: seq[TypeExpr]] =
|
||||||
|
if te == nil: return ("", @[])
|
||||||
|
var baseTe = te
|
||||||
|
if baseTe.kind == tekPointer:
|
||||||
|
baseTe = baseTe.pointerPointee
|
||||||
|
if baseTe.kind == tekNamed and baseTe.typeArgs.len > 0 and ctx.genericStructs.hasKey(baseTe.typeName):
|
||||||
|
return (baseTe.typeName, baseTe.typeArgs)
|
||||||
|
return ("", @[])
|
||||||
|
|
||||||
|
proc getReceiverTypeExpr(ctx: LowerCtx, expr: Expr): TypeExpr =
|
||||||
|
case expr.kind
|
||||||
|
of ekIdent:
|
||||||
|
if ctx.varTypeExprs.hasKey(expr.exprIdent):
|
||||||
|
return ctx.varTypeExprs[expr.exprIdent]
|
||||||
|
of ekField:
|
||||||
|
# For chained field access, try to resolve from the outer object
|
||||||
|
# This is limited but covers common cases
|
||||||
|
discard
|
||||||
|
of ekStructInit:
|
||||||
|
return TypeExpr(kind: tekNamed, loc: expr.loc, typeName: expr.exprStructInitName,
|
||||||
|
typeArgs: expr.exprStructInitTypeArgs)
|
||||||
|
else: discard
|
||||||
|
return nil
|
||||||
|
|
||||||
|
proc generateMethodInstance(ctx: var LowerCtx, baseMethodName: string, typeArgs: seq[TypeExpr]): string
|
||||||
|
|
||||||
proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
||||||
if expr == nil: return nil
|
if expr == nil: return nil
|
||||||
let loc = expr.loc
|
let loc = expr.loc
|
||||||
@@ -384,18 +441,29 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
# Method call desugaring: obj.method(args) → Type_method(obj, args)
|
# Method call desugaring: obj.method(args) → Type_method(obj, args)
|
||||||
if expr.exprCallCallee.kind == ekField:
|
if expr.exprCallCallee.kind == ekField:
|
||||||
let methodName = expr.exprCallCallee.exprFieldName
|
let methodName = expr.exprCallCallee.exprFieldName
|
||||||
|
let receiverExpr = expr.exprCallCallee.exprFieldObj
|
||||||
|
|
||||||
# Try to find the method in methodTable
|
# Try to find the method in methodTable
|
||||||
for typeName, methods in ctx.methodTable:
|
for typeName, methods in ctx.methodTable:
|
||||||
for minfo in methods:
|
for minfo in methods:
|
||||||
if minfo.name == methodName:
|
if minfo.name == methodName:
|
||||||
# Found the method - desugar to Type_method(receiver, args)
|
var calleeName = typeName & "_" & methodName
|
||||||
let mangledName = typeName & "_" & methodName
|
# Check if this is a generic method on a generic struct instance
|
||||||
|
let recvTypeExpr = ctx.getReceiverTypeExpr(receiverExpr)
|
||||||
|
let (baseName, typeArgs) = ctx.extractGenericStructInfo(recvTypeExpr)
|
||||||
|
if baseName != "" and baseName == typeName and minfo.decl.declFuncTypeParams.len > 0:
|
||||||
|
calleeName = ctx.generateMethodInstance(calleeName, typeArgs)
|
||||||
var args: seq[HirNode] = @[]
|
var args: seq[HirNode] = @[]
|
||||||
args.add(ctx.lowerExpr(expr.exprCallCallee.exprFieldObj))
|
let loweredReceiver = ctx.lowerExpr(receiverExpr)
|
||||||
|
let receiverType = ctx.resolveExprType(receiverExpr)
|
||||||
|
# Auto-address if method expects pointer but receiver is value
|
||||||
|
if minfo.params.len > 0 and minfo.params[0].kind == tkPointer and receiverType.kind != tkPointer:
|
||||||
|
args.add(hirUnary(tkAmp, loweredReceiver, makePointer(receiverType), loc))
|
||||||
|
else:
|
||||||
|
args.add(loweredReceiver)
|
||||||
for arg in expr.exprCallArgs:
|
for arg in expr.exprCallArgs:
|
||||||
args.add(ctx.lowerExpr(arg))
|
args.add(ctx.lowerExpr(arg))
|
||||||
return hirCall(mangledName, args, typ, loc)
|
return hirCall(calleeName, args, typ, loc)
|
||||||
|
|
||||||
# Not a method call - treat as field access + call (function pointer)
|
# Not a method call - treat as field access + call (function pointer)
|
||||||
let callee = ctx.lowerExpr(expr.exprCallCallee)
|
let callee = ctx.lowerExpr(expr.exprCallCallee)
|
||||||
@@ -632,6 +700,16 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
|||||||
let alloca = hirAlloca(stmt.stmtLetName, allocaType, loc)
|
let alloca = hirAlloca(stmt.stmtLetName, allocaType, loc)
|
||||||
let varNode = hirVar(stmt.stmtLetName, makePointer(allocaType), loc)
|
let varNode = hirVar(stmt.stmtLetName, makePointer(allocaType), loc)
|
||||||
let store = hirStore(varNode, initHir, loc)
|
let store = hirStore(varNode, initHir, loc)
|
||||||
|
# Track type expr for generic method inference
|
||||||
|
if stmt.stmtLetType != nil:
|
||||||
|
ctx.varTypeExprs[stmt.stmtLetName] = stmt.stmtLetType
|
||||||
|
elif stmt.stmtLetInit.kind == ekStructInit and stmt.stmtLetInit.exprStructInitTypeArgs.len > 0:
|
||||||
|
ctx.varTypeExprs[stmt.stmtLetName] = TypeExpr(
|
||||||
|
kind: tekNamed,
|
||||||
|
loc: stmt.stmtLetInit.loc,
|
||||||
|
typeName: stmt.stmtLetInit.exprStructInitName,
|
||||||
|
typeArgs: stmt.stmtLetInit.exprStructInitTypeArgs
|
||||||
|
)
|
||||||
var stmts = ctx.pendingStmts
|
var stmts = ctx.pendingStmts
|
||||||
ctx.pendingStmts = @[]
|
ctx.pendingStmts = @[]
|
||||||
stmts.add(alloca)
|
stmts.add(alloca)
|
||||||
@@ -798,36 +876,25 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
|||||||
for p in funcParams:
|
for p in funcParams:
|
||||||
var pType = makeUnknown()
|
var pType = makeUnknown()
|
||||||
if p.ptype != nil:
|
if p.ptype != nil:
|
||||||
case p.ptype.kind
|
pType = substituteType(ctx, p.ptype, ctx.typeSubst)
|
||||||
of tekNamed:
|
|
||||||
# Check if this is a type parameter
|
|
||||||
if ctx.typeSubst.hasKey(p.ptype.typeName):
|
|
||||||
pType = ctx.typeSubst[p.ptype.typeName]
|
|
||||||
else:
|
|
||||||
pType = ctx.resolveTypeExpr(p.ptype)
|
|
||||||
of tekPointer:
|
|
||||||
let pointeeType = ctx.resolveTypeExpr(p.ptype.pointerPointee)
|
|
||||||
pType = makePointer(pointeeType)
|
|
||||||
else: discard
|
|
||||||
params.add((p.name, pType))
|
params.add((p.name, pType))
|
||||||
|
if p.ptype != nil:
|
||||||
|
ctx.varTypeExprs[p.name] = p.ptype
|
||||||
|
|
||||||
var retType = makeVoid()
|
var retType = makeVoid()
|
||||||
if funcReturnType != nil:
|
if funcReturnType != nil:
|
||||||
case funcReturnType.kind
|
retType = substituteType(ctx, funcReturnType, ctx.typeSubst)
|
||||||
of tekNamed:
|
|
||||||
# Check if this is a type parameter
|
|
||||||
if ctx.typeSubst.hasKey(funcReturnType.typeName):
|
|
||||||
retType = ctx.typeSubst[funcReturnType.typeName]
|
|
||||||
else:
|
|
||||||
retType = ctx.resolveTypeExpr(funcReturnType)
|
|
||||||
of tekPointer:
|
|
||||||
let pointeeType = ctx.resolveTypeExpr(funcReturnType.pointerPointee)
|
|
||||||
retType = makePointer(pointeeType)
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
|
let oldFuncDecl = ctx.currentFuncDecl
|
||||||
|
let oldFuncRetType = ctx.currentFuncRetType
|
||||||
|
let oldVarTypeExprs = ctx.varTypeExprs
|
||||||
ctx.currentFuncRetType = retType
|
ctx.currentFuncRetType = retType
|
||||||
ctx.currentFuncDecl = decl
|
ctx.currentFuncDecl = decl
|
||||||
|
ctx.varTypeExprs = initTable[string, TypeExpr]() # Clear local vars for new function
|
||||||
let body = if funcBody != nil: ctx.lowerBlock(funcBody) else: nil
|
let body = if funcBody != nil: ctx.lowerBlock(funcBody) else: nil
|
||||||
|
ctx.currentFuncDecl = oldFuncDecl
|
||||||
|
ctx.currentFuncRetType = oldFuncRetType
|
||||||
|
ctx.varTypeExprs = oldVarTypeExprs
|
||||||
|
|
||||||
result = HirFunc(name: funcName, params: params, retType: retType,
|
result = HirFunc(name: funcName, params: params, retType: retType,
|
||||||
body: body, isPublic: decl.isPublic)
|
body: body, isPublic: decl.isPublic)
|
||||||
@@ -835,6 +902,43 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
|||||||
# Restore old substitution
|
# Restore old substitution
|
||||||
ctx.typeSubst = oldSubst
|
ctx.typeSubst = oldSubst
|
||||||
|
|
||||||
|
proc generateMethodInstance(ctx: var LowerCtx, baseMethodName: string, typeArgs: seq[TypeExpr]): string =
|
||||||
|
if not ctx.genericFuncs.hasKey(baseMethodName):
|
||||||
|
return baseMethodName
|
||||||
|
let genericDecl = ctx.genericFuncs[baseMethodName]
|
||||||
|
if genericDecl.declFuncTypeParams.len == 0:
|
||||||
|
return baseMethodName
|
||||||
|
var subst = initTable[string, Type]()
|
||||||
|
var typeSuffix = ""
|
||||||
|
for i, tp in genericDecl.declFuncTypeParams:
|
||||||
|
if i > 0: typeSuffix.add("_")
|
||||||
|
if i < typeArgs.len:
|
||||||
|
let argType = ctx.resolveTypeExpr(typeArgs[i])
|
||||||
|
subst[tp] = argType
|
||||||
|
typeSuffix.add(argType.toString)
|
||||||
|
else:
|
||||||
|
typeSuffix.add("unknown")
|
||||||
|
let mangledName = baseMethodName & "_" & typeSuffix
|
||||||
|
if not ctx.generatedFuncInsts.hasKey(mangledName):
|
||||||
|
var specDecl = Decl(
|
||||||
|
kind: dkFunc,
|
||||||
|
loc: genericDecl.loc,
|
||||||
|
isPublic: genericDecl.isPublic,
|
||||||
|
declFuncAsm: genericDecl.declFuncAsm,
|
||||||
|
declFuncCallConv: genericDecl.declFuncCallConv,
|
||||||
|
declFuncName: mangledName,
|
||||||
|
declFuncTypeParams: @[],
|
||||||
|
declFuncParams: genericDecl.declFuncParams,
|
||||||
|
declFuncReturnType: genericDecl.declFuncReturnType,
|
||||||
|
declFuncBody: genericDecl.declFuncBody
|
||||||
|
)
|
||||||
|
let oldSubst = ctx.typeSubst
|
||||||
|
ctx.typeSubst = subst
|
||||||
|
ctx.extraFuncs.add(ctx.lowerFunc(specDecl))
|
||||||
|
ctx.typeSubst = oldSubst
|
||||||
|
ctx.generatedFuncInsts[mangledName] = true
|
||||||
|
return mangledName
|
||||||
|
|
||||||
proc lowerModule*(module: Module, sema: Sema): HirModule =
|
proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||||
var ctx = initLowerCtx(module, sema)
|
var ctx = initLowerCtx(module, sema)
|
||||||
var funcs: seq[HirFunc] = @[]
|
var funcs: seq[HirFunc] = @[]
|
||||||
@@ -877,10 +981,9 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
|
|
||||||
|
|
||||||
# First pass: collect generic functions and generic structs
|
# First pass: collect generic functions and generic structs
|
||||||
var genericFuncs = initTable[string, Decl]()
|
|
||||||
for decl in module.items:
|
for decl in module.items:
|
||||||
if decl.kind == dkFunc and decl.declFuncTypeParams.len > 0:
|
if decl.kind == dkFunc and decl.declFuncTypeParams.len > 0:
|
||||||
genericFuncs[decl.declFuncName] = decl
|
ctx.genericFuncs[decl.declFuncName] = decl
|
||||||
if decl.kind == dkStruct and decl.declStructTypeParams.len > 0:
|
if decl.kind == dkStruct and decl.declStructTypeParams.len > 0:
|
||||||
ctx.genericStructs[decl.declStructName] = decl
|
ctx.genericStructs[decl.declStructName] = decl
|
||||||
|
|
||||||
@@ -943,7 +1046,7 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
var generated = initTable[string, bool]()
|
var generated = initTable[string, bool]()
|
||||||
for inst in instantiations:
|
for inst in instantiations:
|
||||||
let baseName = inst.name
|
let baseName = inst.name
|
||||||
if genericFuncs.hasKey(baseName):
|
if ctx.genericFuncs.hasKey(baseName):
|
||||||
var typeSuffix = ""
|
var typeSuffix = ""
|
||||||
for i, targ in inst.typeArgs:
|
for i, targ in inst.typeArgs:
|
||||||
if i > 0: typeSuffix.add("_")
|
if i > 0: typeSuffix.add("_")
|
||||||
@@ -954,7 +1057,7 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
let mangledName = baseName & "_" & typeSuffix
|
let mangledName = baseName & "_" & typeSuffix
|
||||||
if not generated.hasKey(mangledName):
|
if not generated.hasKey(mangledName):
|
||||||
# Generate specialized version
|
# Generate specialized version
|
||||||
let genericDecl = genericFuncs[baseName]
|
let genericDecl = ctx.genericFuncs[baseName]
|
||||||
|
|
||||||
# Build type substitution table
|
# Build type substitution table
|
||||||
var subst = initTable[string, Type]()
|
var subst = initTable[string, Type]()
|
||||||
@@ -1062,4 +1165,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
for s in ctx.extraStructs:
|
for s in ctx.extraStructs:
|
||||||
structs.add(s)
|
structs.add(s)
|
||||||
|
|
||||||
|
# Add monomorphized generic methods
|
||||||
|
for f in ctx.extraFuncs:
|
||||||
|
funcs.add(f)
|
||||||
|
|
||||||
result = HirModule(funcs: funcs, externFuncs: externFuncs, structs: structs, enums: enums, consts: consts)
|
result = HirModule(funcs: funcs, externFuncs: externFuncs, structs: structs, enums: enums, consts: consts)
|
||||||
|
|||||||
+41
-1
@@ -110,6 +110,11 @@ proc collectGlobals*(sema: var Sema) =
|
|||||||
of dkFunc:
|
of dkFunc:
|
||||||
let sym = Symbol(kind: skFunc, name: decl.declFuncName, decl: decl,
|
let sym = Symbol(kind: skFunc, name: decl.declFuncName, decl: decl,
|
||||||
isPublic: decl.isPublic)
|
isPublic: decl.isPublic)
|
||||||
|
# Temporarily add type parameters to type table for resolution
|
||||||
|
var addedTypeParams: seq[string] = @[]
|
||||||
|
for tp in decl.declFuncTypeParams:
|
||||||
|
sema.typeTable[tp] = makeTypeParam(tp)
|
||||||
|
addedTypeParams.add(tp)
|
||||||
# Build function type from params and return
|
# Build function type from params and return
|
||||||
var params: seq[Type] = @[]
|
var params: seq[Type] = @[]
|
||||||
for p in decl.declFuncParams:
|
for p in decl.declFuncParams:
|
||||||
@@ -118,6 +123,30 @@ proc collectGlobals*(sema: var Sema) =
|
|||||||
sym.typ = makeFunc(params, retType)
|
sym.typ = makeFunc(params, retType)
|
||||||
if not sema.globalScope.define(sym):
|
if not sema.globalScope.define(sym):
|
||||||
sema.emitError(decl.loc, &"duplicate symbol '{decl.declFuncName}'")
|
sema.emitError(decl.loc, &"duplicate symbol '{decl.declFuncName}'")
|
||||||
|
# Auto-register func Type_Method(self: Type, ...) as a method
|
||||||
|
if decl.declFuncParams.len > 0 and decl.declFuncParams[0].name == "self":
|
||||||
|
var typeName = ""
|
||||||
|
for i in countdown(decl.declFuncName.len - 1, 1):
|
||||||
|
if decl.declFuncName[i] == '_':
|
||||||
|
let prefix = decl.declFuncName[0..<i]
|
||||||
|
let typeSym = sema.globalScope.lookup(prefix)
|
||||||
|
if typeSym != nil and typeSym.kind == skType and typeSym.decl != nil and typeSym.decl.kind == dkStruct:
|
||||||
|
typeName = prefix
|
||||||
|
break
|
||||||
|
if typeName != "":
|
||||||
|
let methodName = decl.declFuncName[typeName.len + 1 .. ^1]
|
||||||
|
if not sema.methodTable.hasKey(typeName):
|
||||||
|
sema.methodTable[typeName] = @[]
|
||||||
|
var minfo = MethodInfo(
|
||||||
|
name: methodName,
|
||||||
|
decl: decl,
|
||||||
|
params: params,
|
||||||
|
retType: retType
|
||||||
|
)
|
||||||
|
sema.methodTable[typeName].add(minfo)
|
||||||
|
# Clean up type parameters
|
||||||
|
for tp in addedTypeParams:
|
||||||
|
sema.typeTable.del(tp)
|
||||||
of dkExternFunc:
|
of dkExternFunc:
|
||||||
let sym = Symbol(kind: skFunc, name: decl.declExtFuncName, decl: decl,
|
let sym = Symbol(kind: skFunc, name: decl.declExtFuncName, decl: decl,
|
||||||
isPublic: decl.isPublic)
|
isPublic: decl.isPublic)
|
||||||
@@ -283,6 +312,9 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
return sym.typ
|
return sym.typ
|
||||||
of ekSelf:
|
of ekSelf:
|
||||||
|
let sym = scope.lookup("self")
|
||||||
|
if sym != nil and sym.typ != nil:
|
||||||
|
return sym.typ
|
||||||
return makeNamed("self")
|
return makeNamed("self")
|
||||||
of ekPath:
|
of ekPath:
|
||||||
let fullName = expr.exprPath.join("::")
|
let fullName = expr.exprPath.join("::")
|
||||||
@@ -607,7 +639,7 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
|
|||||||
of skLet:
|
of skLet:
|
||||||
let initType = sema.checkExpr(stmt.stmtLetInit, scope)
|
let initType = sema.checkExpr(stmt.stmtLetInit, scope)
|
||||||
let declaredType = if stmt.stmtLetType != nil: sema.resolveType(stmt.stmtLetType) else: initType
|
let declaredType = if stmt.stmtLetType != nil: sema.resolveType(stmt.stmtLetType) else: initType
|
||||||
if stmt.stmtLetType != nil and not initType.isAssignableTo(declaredType):
|
if stmt.stmtLetType != nil and not initType.isAssignableTo(declaredType) and not (initType.kind in {TypeKind.tkUnknown, TypeKind.tkNamed, TypeKind.tkTypeParam}):
|
||||||
sema.emitError(stmt.loc, &"cannot assign {initType.toString} to {declaredType.toString}")
|
sema.emitError(stmt.loc, &"cannot assign {initType.toString} to {declaredType.toString}")
|
||||||
let sym = Symbol(kind: skVar, name: stmt.stmtLetName, typ: declaredType,
|
let sym = Symbol(kind: skVar, name: stmt.stmtLetName, typ: declaredType,
|
||||||
isMutable: stmt.stmtLetMut)
|
isMutable: stmt.stmtLetMut)
|
||||||
@@ -677,6 +709,11 @@ proc checkFunc(sema: var Sema, decl: Decl) =
|
|||||||
if decl.declFuncBody == nil:
|
if decl.declFuncBody == nil:
|
||||||
return
|
return
|
||||||
var funcScope = newScope(sema.globalScope)
|
var funcScope = newScope(sema.globalScope)
|
||||||
|
# Add type parameters to type table for resolution
|
||||||
|
var addedTypeParams: seq[string] = @[]
|
||||||
|
for tp in decl.declFuncTypeParams:
|
||||||
|
sema.typeTable[tp] = makeTypeParam(tp)
|
||||||
|
addedTypeParams.add(tp)
|
||||||
# Add parameters
|
# Add parameters
|
||||||
for p in decl.declFuncParams:
|
for p in decl.declFuncParams:
|
||||||
let pType = sema.resolveType(p.ptype)
|
let pType = sema.resolveType(p.ptype)
|
||||||
@@ -685,6 +722,9 @@ proc checkFunc(sema: var Sema, decl: Decl) =
|
|||||||
# Check body statements
|
# Check body statements
|
||||||
for stmt in decl.declFuncBody.stmts:
|
for stmt in decl.declFuncBody.stmts:
|
||||||
discard sema.checkStmt(stmt, funcScope)
|
discard sema.checkStmt(stmt, funcScope)
|
||||||
|
# Clean up type parameters
|
||||||
|
for tp in addedTypeParams:
|
||||||
|
sema.typeTable.del(tp)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Second pass: check all function bodies
|
# Second pass: check all function bodies
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ proc `!=`*(a, b: Type): bool = not (a == b)
|
|||||||
# Assignment compatibility
|
# Assignment compatibility
|
||||||
proc isAssignableTo*(a, b: Type): bool =
|
proc isAssignableTo*(a, b: Type): bool =
|
||||||
if a.isUnknown or b.isUnknown: return true
|
if a.isUnknown or b.isUnknown: return true
|
||||||
|
if b.kind == tkTypeParam: return true
|
||||||
if a == b: return true
|
if a == b: return true
|
||||||
# float32 -> float64
|
# float32 -> float64
|
||||||
if a.kind == tkFloat32 and b.kind == tkFloat64: return true
|
if a.kind == tkFloat32 and b.kind == tkFloat64: return true
|
||||||
|
|||||||
Reference in New Issue
Block a user