mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-30 18:02:05 +00:00
fix #1858; Add support for generic templates and macros
Implementation notes: Just after overload resolution, the resolved generic params will be added to the call expression to be later processed in evalTemplate and evalMacroCall. These procs have been modified to handle the increased number of parameters, but one remaining issue is that immediate templates and macros don't go through the same process. The next commit will outlaw the use of generic parameters with such macros.
This commit is contained in:
@@ -1341,6 +1341,10 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
|||||||
result = t
|
result = t
|
||||||
while result.kind in kinds: result = lastSon(result)
|
while result.kind in kinds: result = lastSon(result)
|
||||||
|
|
||||||
|
proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||||
|
result = if t != nil: t.skipTypes(kinds)
|
||||||
|
else: nil
|
||||||
|
|
||||||
proc isGCedMem*(t: PType): bool {.inline.} =
|
proc isGCedMem*(t: PType): bool {.inline.} =
|
||||||
result = t.kind in {tyString, tyRef, tySequence} or
|
result = t.kind in {tyString, tyRef, tySequence} or
|
||||||
t.kind == tyProc and t.callConv == ccClosure
|
t.kind == tyProc and t.callConv == ccClosure
|
||||||
|
|||||||
@@ -25,16 +25,22 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
|
|||||||
if ctx.instLines: result.info = b.info
|
if ctx.instLines: result.info = b.info
|
||||||
|
|
||||||
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||||
|
template handleParam(param) =
|
||||||
|
let x = param
|
||||||
|
if x.kind == nkArgList:
|
||||||
|
for y in items(x): result.add(y)
|
||||||
|
else:
|
||||||
|
result.add copyTree(x)
|
||||||
|
|
||||||
case templ.kind
|
case templ.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var s = templ.sym
|
var s = templ.sym
|
||||||
if s.owner.id == c.owner.id:
|
if s.owner.id == c.owner.id:
|
||||||
if s.kind == skParam:
|
case s.kind
|
||||||
let x = actual.sons[s.position]
|
of skParam:
|
||||||
if x.kind == nkArgList:
|
handleParam actual.sons[s.position]
|
||||||
for y in items(x): result.add(y)
|
of skGenericParam:
|
||||||
else:
|
handleParam actual.sons[s.owner.typ.len + s.position - 1]
|
||||||
result.add copyTree(x)
|
|
||||||
else:
|
else:
|
||||||
internalAssert sfGenSym in s.flags
|
internalAssert sfGenSym in s.flags
|
||||||
var x = PSym(idTableGet(c.mapping, s))
|
var x = PSym(idTableGet(c.mapping, s))
|
||||||
@@ -56,20 +62,30 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
|||||||
proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||||
# if the template has zero arguments, it can be called without ``()``
|
# if the template has zero arguments, it can be called without ``()``
|
||||||
# `n` is then a nkSym or something similar
|
# `n` is then a nkSym or something similar
|
||||||
var a: int
|
var totalParams = case n.kind
|
||||||
case n.kind
|
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: <n.len
|
||||||
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
else: 0
|
||||||
a = sonsLen(n)
|
|
||||||
else: a = 0
|
var
|
||||||
var f = s.typ.sonsLen
|
genericParams = s.ast[genericParamsPos].len
|
||||||
if a > f: globalError(n.info, errWrongNumberOfArguments)
|
expectedRegularParams = <s.typ.len
|
||||||
|
givenRegularParams = totalParams - genericParams
|
||||||
|
|
||||||
|
if totalParams > expectedRegularParams + genericParams:
|
||||||
|
globalError(n.info, errWrongNumberOfArguments)
|
||||||
|
|
||||||
result = newNodeI(nkArgList, n.info)
|
result = newNodeI(nkArgList, n.info)
|
||||||
for i in countup(1, f - 1):
|
for i in 1 .. givenRegularParams:
|
||||||
var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast)
|
result.addSon n.sons[i]
|
||||||
if arg == nil or arg.kind == nkEmpty:
|
|
||||||
|
for i in givenRegularParams+1 .. expectedRegularParams:
|
||||||
|
let default = s.typ.n.sons[i].sym.ast
|
||||||
|
if default.kind == nkEmpty:
|
||||||
localError(n.info, errWrongNumberOfArguments)
|
localError(n.info, errWrongNumberOfArguments)
|
||||||
addSon(result, arg)
|
result.addSon default.copyTree
|
||||||
|
|
||||||
|
for i in 1 .. genericParams:
|
||||||
|
result.addSon n.sons[givenRegularParams + i]
|
||||||
|
|
||||||
var evalTemplateCounter* = 0
|
var evalTemplateCounter* = 0
|
||||||
# to prevent endless recursion in templates instantiation
|
# to prevent endless recursion in templates instantiation
|
||||||
|
|||||||
@@ -283,8 +283,21 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
|||||||
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
|
if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
|
||||||
result.typ = newTypeS(x.fauxMatch, c)
|
result.typ = newTypeS(x.fauxMatch, c)
|
||||||
return
|
return
|
||||||
if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
|
let gp = finalCallee.ast.sons[genericParamsPos]
|
||||||
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
|
if gp.kind != nkEmpty:
|
||||||
|
if x.calleeSym.kind notin {skMacro, skTemplate}:
|
||||||
|
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
|
||||||
|
else:
|
||||||
|
# For macros and templates, the resolved generic params
|
||||||
|
# are added as normal params.
|
||||||
|
for s in instantiateGenericParamList(c, gp, x.bindings):
|
||||||
|
case s.kind
|
||||||
|
of skConst:
|
||||||
|
x.call.add s.ast
|
||||||
|
of skType:
|
||||||
|
x.call.add newSymNode(s, n.info)
|
||||||
|
else:
|
||||||
|
internalAssert false
|
||||||
result = x.call
|
result = x.call
|
||||||
instGenericConvertersSons(c, result, x)
|
instGenericConvertersSons(c, result, x)
|
||||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||||
|
|||||||
@@ -10,14 +10,10 @@
|
|||||||
# This module implements the instantiation of generic procs.
|
# This module implements the instantiation of generic procs.
|
||||||
# included from sem.nim
|
# included from sem.nim
|
||||||
|
|
||||||
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
|
||||||
entry: var TInstantiation) =
|
internalAssert n.kind == nkGenericParams
|
||||||
if n.kind != nkGenericParams:
|
|
||||||
internalError(n.info, "instantiateGenericParamList; no generic params")
|
|
||||||
newSeq(entry.concreteTypes, n.len)
|
|
||||||
for i, a in n.pairs:
|
for i, a in n.pairs:
|
||||||
if a.kind != nkSym:
|
internalAssert a.kind == nkSym
|
||||||
internalError(a.info, "instantiateGenericParamList; no symbol")
|
|
||||||
var q = a.sym
|
var q = a.sym
|
||||||
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
|
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
|
||||||
continue
|
continue
|
||||||
@@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
|||||||
#t = ReplaceTypeVarsT(cl, t)
|
#t = ReplaceTypeVarsT(cl, t)
|
||||||
s.typ = t
|
s.typ = t
|
||||||
if t.kind == tyStatic: s.ast = t.n
|
if t.kind == tyStatic: s.ast = t.n
|
||||||
addDecl(c, s)
|
yield s
|
||||||
entry.concreteTypes[i] = t
|
|
||||||
|
|
||||||
proc sameInstantiation(a, b: TInstantiation): bool =
|
proc sameInstantiation(a, b: TInstantiation): bool =
|
||||||
if a.concreteTypes.len == b.concreteTypes.len:
|
if a.concreteTypes.len == b.concreteTypes.len:
|
||||||
@@ -196,7 +191,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
|||||||
## The `pt` parameter is a type-unsafe mapping table used to link generic
|
## The `pt` parameter is a type-unsafe mapping table used to link generic
|
||||||
## parameters to their concrete types within the generic instance.
|
## parameters to their concrete types within the generic instance.
|
||||||
# no need to instantiate generic templates/macros:
|
# no need to instantiate generic templates/macros:
|
||||||
if fn.kind in {skTemplate, skMacro}: return fn
|
internalAssert fn.kind notin {skMacro, skTemplate}
|
||||||
# generates an instantiated proc
|
# generates an instantiated proc
|
||||||
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
|
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
|
||||||
inc(c.instCounter)
|
inc(c.instCounter)
|
||||||
@@ -213,12 +208,18 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
|||||||
result.ast = n
|
result.ast = n
|
||||||
pushOwner(result)
|
pushOwner(result)
|
||||||
openScope(c)
|
openScope(c)
|
||||||
internalAssert n.sons[genericParamsPos].kind != nkEmpty
|
let gp = n.sons[genericParamsPos]
|
||||||
|
internalAssert gp.kind != nkEmpty
|
||||||
n.sons[namePos] = newSymNode(result)
|
n.sons[namePos] = newSymNode(result)
|
||||||
pushInfoContext(info)
|
pushInfoContext(info)
|
||||||
var entry = TInstantiation.new
|
var entry = TInstantiation.new
|
||||||
entry.sym = result
|
entry.sym = result
|
||||||
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
|
newSeq(entry.concreteTypes, gp.len)
|
||||||
|
var i = 0
|
||||||
|
for s in instantiateGenericParamList(c, gp, pt):
|
||||||
|
addDecl(c, s)
|
||||||
|
entry.concreteTypes[i] = s.typ
|
||||||
|
inc i
|
||||||
pushProcCon(c, result)
|
pushProcCon(c, result)
|
||||||
instantiateProcType(c, pt, result, info)
|
instantiateProcType(c, pt, result, info)
|
||||||
n.sons[genericParamsPos] = ast.emptyNode
|
n.sons[genericParamsPos] = ast.emptyNode
|
||||||
|
|||||||
@@ -1417,12 +1417,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) =
|
|||||||
proc setupCompileTimeVar*(module: PSym, n: PNode) =
|
proc setupCompileTimeVar*(module: PSym, n: PNode) =
|
||||||
discard evalConstExprAux(module, nil, n, emStaticStmt)
|
discard evalConstExprAux(module, nil, n, emStaticStmt)
|
||||||
|
|
||||||
proc setupMacroParam(x: PNode): PNode =
|
proc setupMacroParam(x: PNode, typ: PType): TFullReg =
|
||||||
result = x
|
case typ.kind
|
||||||
if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1]
|
of tyStatic:
|
||||||
result = canonValue(result)
|
putIntoReg(result, x)
|
||||||
result.flags.incl nfIsRef
|
of tyTypeDesc:
|
||||||
result.typ = x.typ
|
putIntoReg(result, x)
|
||||||
|
else:
|
||||||
|
result.kind = rkNode
|
||||||
|
var n = x
|
||||||
|
if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1]
|
||||||
|
n = n.canonValue
|
||||||
|
n.flags.incl nfIsRef
|
||||||
|
n.typ = x.typ
|
||||||
|
result.node = n
|
||||||
|
|
||||||
var evalMacroCounter: int
|
var evalMacroCounter: int
|
||||||
|
|
||||||
@@ -1442,6 +1450,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
|
|||||||
|
|
||||||
c.callsite = nOrig
|
c.callsite = nOrig
|
||||||
let start = genProc(c, sym)
|
let start = genProc(c, sym)
|
||||||
|
# c.echoCode start
|
||||||
|
|
||||||
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
|
var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
|
||||||
let maxSlots = sym.offset
|
let maxSlots = sym.offset
|
||||||
@@ -1457,9 +1466,14 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
|
|||||||
tos.slots[0].kind = rkNode
|
tos.slots[0].kind = rkNode
|
||||||
tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
|
tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
|
||||||
# setup parameters:
|
# setup parameters:
|
||||||
for i in 1 .. < min(tos.slots.len, L):
|
for i in 1.. <sym.typ.len:
|
||||||
tos.slots[i].kind = rkNode
|
tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i])
|
||||||
tos.slots[i].node = setupMacroParam(n.sons[i])
|
|
||||||
|
let gp = sym.ast[genericParamsPos]
|
||||||
|
for i in 0 .. <gp.len:
|
||||||
|
let idx = sym.typ.len + i
|
||||||
|
tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
|
||||||
|
|
||||||
# temporary storage:
|
# temporary storage:
|
||||||
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
|
#for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
|
||||||
result = rawExecute(c, start, tos).regToNode
|
result = rawExecute(c, start, tos).regToNode
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ proc getSlotKind(t: PType): TSlotKind =
|
|||||||
const
|
const
|
||||||
HighRegisterPressure = 40
|
HighRegisterPressure = 40
|
||||||
|
|
||||||
proc getTemp(c: PCtx; typ: PType): TRegister =
|
proc getTemp(c: PCtx; tt: PType): TRegister =
|
||||||
|
let typ = tt.skipTypes({tyStatic})
|
||||||
let c = c.prc
|
let c = c.prc
|
||||||
# we prefer the same slot kind here for efficiency. Unfortunately for
|
# we prefer the same slot kind here for efficiency. Unfortunately for
|
||||||
# discardable return types we may not know the desired type. This can happen
|
# discardable return types we may not know the desired type. This can happen
|
||||||
@@ -685,7 +686,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
|
|||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
c.gABC(n, opc, dest, tmp)
|
c.gABC(n, opc, dest, tmp)
|
||||||
c.gABx(n, opc, 0, genType(c, n.typ))
|
c.gABx(n, opc, 0, genType(c, n.typ))
|
||||||
c.gABx(n, opc, 0, genType(c, arg.typ))
|
c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic})))
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
||||||
@@ -1085,7 +1086,8 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
|||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
||||||
proc whichAsgnOpc(n: PNode): TOpcode =
|
proc whichAsgnOpc(n: PNode): TOpcode =
|
||||||
case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind
|
let toSkip = abstractRange-{tyTypeDesc}
|
||||||
|
case n.typ.skipTypes(toSkip).kind
|
||||||
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
|
of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
|
||||||
opcAsgnInt
|
opcAsgnInt
|
||||||
of tyString, tyCString:
|
of tyString, tyCString:
|
||||||
@@ -1559,6 +1561,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
|
|||||||
c.gABx(n, opcLdConst, dest, lit)
|
c.gABx(n, opcLdConst, dest, lit)
|
||||||
of skType:
|
of skType:
|
||||||
genTypeLit(c, s.typ, dest)
|
genTypeLit(c, s.typ, dest)
|
||||||
|
of skGenericParam:
|
||||||
|
if c.prc.sym.kind == skMacro:
|
||||||
|
genRdVar(c, n, dest, flags)
|
||||||
|
else:
|
||||||
|
internalError(n.info, "cannot generate code for: " & s.name.s)
|
||||||
else:
|
else:
|
||||||
internalError(n.info, "cannot generate code for: " & s.name.s)
|
internalError(n.info, "cannot generate code for: " & s.name.s)
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
@@ -1690,6 +1697,14 @@ proc genParams(c: PCtx; params: PNode) =
|
|||||||
c.prc.slots[i] = (inUse: true, kind: slotFixedLet)
|
c.prc.slots[i] = (inUse: true, kind: slotFixedLet)
|
||||||
c.prc.maxSlots = max(params.len, 1)
|
c.prc.maxSlots = max(params.len, 1)
|
||||||
|
|
||||||
|
proc genGenericParams(c: PCtx; gp: PNode) =
|
||||||
|
var base = c.prc.maxSlots
|
||||||
|
for i in 0.. <gp.len:
|
||||||
|
var param = gp.sons[i].sym
|
||||||
|
param.position = base + i # XXX: fix this earlier; make it consistent with templates
|
||||||
|
c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet)
|
||||||
|
c.prc.maxSlots = base + gp.len
|
||||||
|
|
||||||
proc finalJumpTarget(c: PCtx; pc, diff: int) =
|
proc finalJumpTarget(c: PCtx; pc, diff: int) =
|
||||||
internalAssert(-0x7fff < diff and diff < 0x7fff)
|
internalAssert(-0x7fff < diff and diff < 0x7fff)
|
||||||
let oldInstr = c.code[pc]
|
let oldInstr = c.code[pc]
|
||||||
@@ -1761,6 +1776,8 @@ proc genProc(c: PCtx; s: PSym): int =
|
|||||||
c.prc = p
|
c.prc = p
|
||||||
# iterate over the parameters and allocate space for them:
|
# iterate over the parameters and allocate space for them:
|
||||||
genParams(c, s.typ.n)
|
genParams(c, s.typ.n)
|
||||||
|
if s.kind == skMacro and s.ast[genericParamsPos].kind != nkEmpty:
|
||||||
|
genGenericParams(c, s.ast[genericParamsPos])
|
||||||
if tfCapturesEnv in s.typ.flags:
|
if tfCapturesEnv in s.typ.flags:
|
||||||
#let env = s.ast.sons[paramsPos].lastSon.sym
|
#let env = s.ast.sons[paramsPos].lastSon.sym
|
||||||
#assert env.position == 2
|
#assert env.position == 2
|
||||||
|
|||||||
Reference in New Issue
Block a user