feat: generic struct monomorphization + tkUnknown fix

- Add generic struct instantiation (Box<int>, Pair<T,U>) in HIR lowering
- Fix tkUnknown/tkNamed/tkTypeParam ambiguity in sema.nim (qualify with TypeKind)
- Add generics_struct example
- Update Makefile with new example
This commit is contained in:
2026-05-31 10:33:08 +03:00
parent 7ee6a73ea4
commit 9f733aca7d
9 changed files with 136 additions and 15 deletions
+90 -8
View File
@@ -13,6 +13,9 @@ type
pendingStmts*: seq[HirNode]
typeSubst*: Table[string, Type] # Type parameter substitution for generics
importTable*: Table[string, string] # Local name → fully qualified name for imports
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]]]]
proc freshName(ctx: var LowerCtx): string =
inc ctx.varCounter
@@ -118,11 +121,71 @@ proc initLowerCtx*(module: Module, sema: Sema): LowerCtx =
result.pendingStmts = @[]
result.typeSubst = initTable[string, Type]()
result.importTable = initTable[string, string]()
result.genericStructs = initTable[string, Decl]()
result.generatedStructInsts = initTable[string, bool]()
result.extraStructs = @[]
proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type
proc substituteType(ctx: var LowerCtx, te: TypeExpr, subst: Table[string, Type]): Type =
if te == nil: return makeUnknown()
case te.kind
of tekNamed:
if subst.hasKey(te.typeName):
return subst[te.typeName]
if te.typeArgs.len > 0 and ctx.genericStructs.hasKey(te.typeName):
var suffix = ""
for i, arg in te.typeArgs:
if i > 0: suffix.add("_")
let argType = substituteType(ctx, arg, subst)
suffix.add(argType.toString)
let mangledName = te.typeName & "_" & suffix
if not ctx.generatedStructInsts.hasKey(mangledName):
let genericDecl = ctx.genericStructs[te.typeName]
var fields: seq[tuple[name: string, typ: Type]] = @[]
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
return makeNamed(mangledName)
return ctx.resolveTypeExpr(te)
of tekPointer:
return makePointer(substituteType(ctx, te.pointerPointee, subst))
of tekSlice:
return makeSlice(substituteType(ctx, te.sliceElement, subst))
of tekTuple:
var elems: seq[Type] = @[]
for e in te.tupleElements:
elems.add(substituteType(ctx, e, subst))
return makeTuple(elems)
else:
return ctx.resolveTypeExpr(te)
proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type =
if te == nil: return makeUnknown()
case te.kind
of tekNamed:
if te.typeArgs.len > 0 and ctx.genericStructs.hasKey(te.typeName):
var suffix = ""
for i, arg in te.typeArgs:
if i > 0: suffix.add("_")
let argType = ctx.resolveTypeExpr(arg)
suffix.add(argType.toString)
let mangledName = te.typeName & "_" & suffix
if not ctx.generatedStructInsts.hasKey(mangledName):
let genericDecl = ctx.genericStructs[te.typeName]
var fields: seq[tuple[name: string, typ: Type]] = @[]
var subst = initTable[string, Type]()
for j, tp in genericDecl.declStructTypeParams:
if j < te.typeArgs.len:
subst[tp] = ctx.resolveTypeExpr(te.typeArgs[j])
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
return makeNamed(mangledName)
case te.typeName
of "void": return makeVoid()
of "bool": return makeBool()
@@ -252,7 +315,11 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
return ctx.resolveTypeExpr(f.ftype)
else: return makeUnknown()
return makeUnknown()
of ekStructInit: return makeNamed(expr.exprStructInitName)
of ekStructInit:
if expr.exprStructInitTypeArgs.len > 0:
let te = TypeExpr(kind: tekNamed, loc: expr.loc, typeName: expr.exprStructInitName, typeArgs: expr.exprStructInitTypeArgs)
return ctx.resolveTypeExpr(te)
return makeNamed(expr.exprStructInitName)
of ekSlice:
if expr.exprSliceElements.len > 0:
return makeSlice(ctx.resolveExprType(expr.exprSliceElements[0]))
@@ -405,7 +472,15 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
var fields: seq[tuple[name: string, value: HirNode]] = @[]
for f in expr.exprStructInitFields:
fields.add((f.name, ctx.lowerExpr(f.value)))
return HirNode(kind: hStructInit, structInitName: expr.exprStructInitName,
var structName = expr.exprStructInitName
if expr.exprStructInitTypeArgs.len > 0:
var suffix = ""
for i, targ in expr.exprStructInitTypeArgs:
if i > 0: suffix.add("_")
let argType = ctx.resolveTypeExpr(targ)
suffix.add(argType.toString)
structName = structName & "_" & suffix
return HirNode(kind: hStructInit, structInitName: structName,
structInitFields: fields, typ: typ, loc: loc)
of ekSlice:
@@ -801,11 +876,13 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
discard
# First pass: collect generic functions
# 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
if decl.kind == dkStruct and decl.declStructTypeParams.len > 0:
ctx.genericStructs[decl.declStructName] = decl
# Second pass: find all generic calls and monomorphize
proc findGenericCalls(expr: Expr): seq[tuple[name: string, typeArgs: seq[TypeExpr]]] =
@@ -932,11 +1009,12 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
hf.name = decl.declImplTypeName & "_" & hf.name
funcs.add(hf)
of dkStruct:
var fields: seq[tuple[name: string, typ: Type]] = @[]
for f in decl.declStructFields:
let fType = if f.ftype != nil: ctx.resolveTypeExpr(f.ftype) else: makeUnknown()
fields.add((f.name, fType))
structs.add((decl.declStructName, fields))
if decl.declStructTypeParams.len == 0: # Skip generic structs — monomorphized separately
var fields: seq[tuple[name: string, typ: Type]] = @[]
for f in decl.declStructFields:
let fType = if f.ftype != nil: ctx.resolveTypeExpr(f.ftype) else: makeUnknown()
fields.add((f.name, fType))
structs.add((decl.declStructName, fields))
of dkEnum:
var variants: seq[HirEnumVariant] = @[]
for v in decl.declEnumVariants:
@@ -980,4 +1058,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
consts.add((decl.declConstName, typ, value))
else: discard
# Add monomorphized generic structs
for s in ctx.extraStructs:
structs.add(s)
result = HirModule(funcs: funcs, externFuncs: externFuncs, structs: structs, enums: enums, consts: consts)