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,
|
||||
}
|
||||
|
||||
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 {
|
||||
let b: Box<int> = Box<int> { value: 42 };
|
||||
PrintLine("Box value:");
|
||||
PrintInt(b.value);
|
||||
PrintInt(b.Get());
|
||||
PrintLine("");
|
||||
|
||||
b.Set(100);
|
||||
PrintLine("Box after Set(100):");
|
||||
PrintInt(b.Get());
|
||||
PrintLine("");
|
||||
|
||||
let p: Pair<int, String> = Pair<int, String> { first: 10, second: "hello" };
|
||||
PrintLine("Pair first:");
|
||||
PrintInt(p.first);
|
||||
PrintInt(p.GetFirst());
|
||||
PrintLine("");
|
||||
|
||||
let bp: *Box<int> = &b;
|
||||
PrintLine("Box via pointer:");
|
||||
PrintInt(bp.Get());
|
||||
PrintLine("");
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -490,6 +490,17 @@ proc emitModule*(be: var CBackend, module: HirModule): string =
|
||||
for e in module.enums:
|
||||
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
|
||||
var hasMain = false
|
||||
for f in module.funcs:
|
||||
|
||||
+139
-32
@@ -16,6 +16,11 @@ type
|
||||
genericStructs*: Table[string, Decl] # Generic struct declarations
|
||||
generatedStructInsts*: Table[string, bool] # Track generated struct instantiations
|
||||
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 =
|
||||
inc ctx.varCounter
|
||||
@@ -124,6 +129,11 @@ proc initLowerCtx*(module: Module, sema: Sema): LowerCtx =
|
||||
result.genericStructs = initTable[string, Decl]()
|
||||
result.generatedStructInsts = initTable[string, bool]()
|
||||
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
|
||||
|
||||
@@ -143,11 +153,15 @@ proc substituteType(ctx: var LowerCtx, te: TypeExpr, subst: Table[string, Type])
|
||||
if not ctx.generatedStructInsts.hasKey(mangledName):
|
||||
let genericDecl = ctx.genericStructs[te.typeName]
|
||||
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
||||
var concreteArgs: seq[Type] = @[]
|
||||
for f in genericDecl.declStructFields:
|
||||
let resolvedType = substituteType(ctx, f.ftype, subst)
|
||||
fields.add((f.name, resolvedType))
|
||||
for arg in te.typeArgs:
|
||||
concreteArgs.add(substituteType(ctx, arg, subst))
|
||||
ctx.extraStructs.add((mangledName, fields))
|
||||
ctx.generatedStructInsts[mangledName] = true
|
||||
ctx.structInstMap[mangledName] = (te.typeName, concreteArgs)
|
||||
return makeNamed(mangledName)
|
||||
return ctx.resolveTypeExpr(te)
|
||||
of tekPointer:
|
||||
@@ -177,14 +191,18 @@ proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type =
|
||||
let genericDecl = ctx.genericStructs[te.typeName]
|
||||
var fields: seq[tuple[name: string, typ: Type]] = @[]
|
||||
var subst = initTable[string, Type]()
|
||||
var concreteArgs: seq[Type] = @[]
|
||||
for j, tp in genericDecl.declStructTypeParams:
|
||||
if j < te.typeArgs.len:
|
||||
subst[tp] = ctx.resolveTypeExpr(te.typeArgs[j])
|
||||
for arg in te.typeArgs:
|
||||
concreteArgs.add(ctx.resolveTypeExpr(arg))
|
||||
for f in genericDecl.declStructFields:
|
||||
let resolvedType = substituteType(ctx, f.ftype, subst)
|
||||
fields.add((f.name, resolvedType))
|
||||
ctx.extraStructs.add((mangledName, fields))
|
||||
ctx.generatedStructInsts[mangledName] = true
|
||||
ctx.structInstMap[mangledName] = (te.typeName, concreteArgs)
|
||||
return makeNamed(mangledName)
|
||||
case te.typeName
|
||||
of "void": return makeVoid()
|
||||
@@ -234,7 +252,10 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
||||
# Check global scope first
|
||||
let sym = ctx.globalScope.lookup(expr.exprIdent)
|
||||
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:
|
||||
var params: seq[Param] = @[]
|
||||
case ctx.currentFuncDecl.kind
|
||||
@@ -259,7 +280,17 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
||||
return makePointer(pointeeType)
|
||||
else: discard
|
||||
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:
|
||||
let left = ctx.resolveExprType(expr.exprBinaryLeft)
|
||||
case expr.exprBinaryOp
|
||||
@@ -347,6 +378,32 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
||||
return makeVoid()
|
||||
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 =
|
||||
if expr == nil: return nil
|
||||
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)
|
||||
if expr.exprCallCallee.kind == ekField:
|
||||
let methodName = expr.exprCallCallee.exprFieldName
|
||||
let receiverExpr = expr.exprCallCallee.exprFieldObj
|
||||
|
||||
# Try to find the method in methodTable
|
||||
for typeName, methods in ctx.methodTable:
|
||||
for minfo in methods:
|
||||
if minfo.name == methodName:
|
||||
# Found the method - desugar to Type_method(receiver, args)
|
||||
let mangledName = typeName & "_" & methodName
|
||||
var calleeName = 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] = @[]
|
||||
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:
|
||||
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)
|
||||
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 varNode = hirVar(stmt.stmtLetName, makePointer(allocaType), 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
|
||||
ctx.pendingStmts = @[]
|
||||
stmts.add(alloca)
|
||||
@@ -798,36 +876,25 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
||||
for p in funcParams:
|
||||
var pType = makeUnknown()
|
||||
if p.ptype != nil:
|
||||
case p.ptype.kind
|
||||
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
|
||||
pType = substituteType(ctx, p.ptype, ctx.typeSubst)
|
||||
params.add((p.name, pType))
|
||||
if p.ptype != nil:
|
||||
ctx.varTypeExprs[p.name] = p.ptype
|
||||
|
||||
var retType = makeVoid()
|
||||
if funcReturnType != nil:
|
||||
case funcReturnType.kind
|
||||
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
|
||||
retType = substituteType(ctx, funcReturnType, ctx.typeSubst)
|
||||
|
||||
let oldFuncDecl = ctx.currentFuncDecl
|
||||
let oldFuncRetType = ctx.currentFuncRetType
|
||||
let oldVarTypeExprs = ctx.varTypeExprs
|
||||
ctx.currentFuncRetType = retType
|
||||
ctx.currentFuncDecl = decl
|
||||
ctx.varTypeExprs = initTable[string, TypeExpr]() # Clear local vars for new function
|
||||
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,
|
||||
body: body, isPublic: decl.isPublic)
|
||||
@@ -835,6 +902,43 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
||||
# Restore old substitution
|
||||
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 =
|
||||
var ctx = initLowerCtx(module, sema)
|
||||
var funcs: seq[HirFunc] = @[]
|
||||
@@ -877,10 +981,9 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
|
||||
|
||||
# First pass: collect generic functions and generic structs
|
||||
var genericFuncs = initTable[string, Decl]()
|
||||
for decl in module.items:
|
||||
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:
|
||||
ctx.genericStructs[decl.declStructName] = decl
|
||||
|
||||
@@ -943,7 +1046,7 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
var generated = initTable[string, bool]()
|
||||
for inst in instantiations:
|
||||
let baseName = inst.name
|
||||
if genericFuncs.hasKey(baseName):
|
||||
if ctx.genericFuncs.hasKey(baseName):
|
||||
var typeSuffix = ""
|
||||
for i, targ in inst.typeArgs:
|
||||
if i > 0: typeSuffix.add("_")
|
||||
@@ -954,7 +1057,7 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
let mangledName = baseName & "_" & typeSuffix
|
||||
if not generated.hasKey(mangledName):
|
||||
# Generate specialized version
|
||||
let genericDecl = genericFuncs[baseName]
|
||||
let genericDecl = ctx.genericFuncs[baseName]
|
||||
|
||||
# Build type substitution table
|
||||
var subst = initTable[string, Type]()
|
||||
@@ -1062,4 +1165,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||
for s in ctx.extraStructs:
|
||||
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)
|
||||
|
||||
+41
-1
@@ -110,6 +110,11 @@ proc collectGlobals*(sema: var Sema) =
|
||||
of dkFunc:
|
||||
let sym = Symbol(kind: skFunc, name: decl.declFuncName, decl: decl,
|
||||
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
|
||||
var params: seq[Type] = @[]
|
||||
for p in decl.declFuncParams:
|
||||
@@ -118,6 +123,30 @@ proc collectGlobals*(sema: var Sema) =
|
||||
sym.typ = makeFunc(params, retType)
|
||||
if not sema.globalScope.define(sym):
|
||||
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:
|
||||
let sym = Symbol(kind: skFunc, name: decl.declExtFuncName, decl: decl,
|
||||
isPublic: decl.isPublic)
|
||||
@@ -283,6 +312,9 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
||||
return makeUnknown()
|
||||
return sym.typ
|
||||
of ekSelf:
|
||||
let sym = scope.lookup("self")
|
||||
if sym != nil and sym.typ != nil:
|
||||
return sym.typ
|
||||
return makeNamed("self")
|
||||
of ekPath:
|
||||
let fullName = expr.exprPath.join("::")
|
||||
@@ -607,7 +639,7 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
|
||||
of skLet:
|
||||
let initType = sema.checkExpr(stmt.stmtLetInit, scope)
|
||||
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}")
|
||||
let sym = Symbol(kind: skVar, name: stmt.stmtLetName, typ: declaredType,
|
||||
isMutable: stmt.stmtLetMut)
|
||||
@@ -677,6 +709,11 @@ proc checkFunc(sema: var Sema, decl: Decl) =
|
||||
if decl.declFuncBody == nil:
|
||||
return
|
||||
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
|
||||
for p in decl.declFuncParams:
|
||||
let pType = sema.resolveType(p.ptype)
|
||||
@@ -685,6 +722,9 @@ proc checkFunc(sema: var Sema, decl: Decl) =
|
||||
# Check body statements
|
||||
for stmt in decl.declFuncBody.stmts:
|
||||
discard sema.checkStmt(stmt, funcScope)
|
||||
# Clean up type parameters
|
||||
for tp in addedTypeParams:
|
||||
sema.typeTable.del(tp)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Second pass: check all function bodies
|
||||
|
||||
@@ -114,6 +114,7 @@ proc `!=`*(a, b: Type): bool = not (a == b)
|
||||
# Assignment compatibility
|
||||
proc isAssignableTo*(a, b: Type): bool =
|
||||
if a.isUnknown or b.isUnknown: return true
|
||||
if b.kind == tkTypeParam: return true
|
||||
if a == b: return true
|
||||
# float32 -> float64
|
||||
if a.kind == tkFloat32 and b.kind == tkFloat64: return true
|
||||
|
||||
Reference in New Issue
Block a user