fix #1858 again; restores the support for static macro params

This commit is contained in:
Zahary Karadjov 2015-07-21 10:21:14 +03:00
commit 02f97489b7
6 changed files with 179 additions and 41 deletions

View file

@ -25,16 +25,21 @@ 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 and sfGenSym notin s.flags: if s.kind == skParam and sfGenSym notin s.flags:
let x = actual.sons[s.position] handleParam actual.sons[s.position]
if x.kind == nkArgList: elif s.kind == skGenericParam:
for y in items(x): result.add(y) handleParam actual.sons[s.owner.typ.len + s.position - 1]
else:
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,22 +61,35 @@ 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:
# handle parameters with default values, which were
# not supplied by the user
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, ast.emptyNode) addSon(result, ast.emptyNode)
else: else:
addSon(result, arg) addSon(result, default.copyTree)
# add any generic paramaters
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

View file

@ -305,8 +305,22 @@ 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)

View file

@ -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:
@ -217,7 +212,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)
@ -234,12 +229,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

View file

@ -1467,12 +1467,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
@ -1508,10 +1516,16 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# return value: # return value:
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

View file

@ -173,7 +173,8 @@ const
proc bestEffort(c: PCtx): TLineInfo = proc bestEffort(c: PCtx): TLineInfo =
(if c.prc == nil: c.module.info else: c.prc.sym.info) (if c.prc == nil: c.module.info else: c.prc.sym.info)
proc getTemp(cc: PCtx; typ: PType): TRegister = proc getTemp(cc: PCtx; tt: PType): TRegister =
let typ = tt.skipTypesOrNil({tyStatic})
let c = cc.prc let c = cc.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
@ -707,7 +708,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) =
@ -1617,6 +1618,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:
globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s) globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s)
of nkCallKinds: of nkCallKinds:
@ -1767,6 +1773,14 @@ proc finalJumpTarget(c: PCtx; pc, diff: int) =
c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or
uint32(diff+wordExcess) shl 16'u32).TInstr uint32(diff+wordExcess) shl 16'u32).TInstr
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 optimizeJumps(c: PCtx; start: int) = proc optimizeJumps(c: PCtx; start: int) =
const maxIterations = 10 const maxIterations = 10
for i in start .. <c.code.len: for i in start .. <c.code.len:
@ -1831,6 +1845,9 @@ 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)
# allocate additional space for any generically bound parameters
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

View file

@ -0,0 +1,74 @@
discard """
msg: '''letters
aa
bb
numbers
11
22
AST a
[(11, 22), (33, 44)]
AST b
(e: [55, 66], f: [77, 88])
55
10
20Test
20
'''
"""
import macros
type
TConfig = tuple
letters: seq[string]
numbers:seq[int]
const data: Tconfig = (@["aa", "bb"], @[11, 22])
macro mymacro(data: static[TConfig]): stmt =
echo "letters"
for s in items(data.letters):
echo s
echo "numbers"
for n in items(data.numbers):
echo n
mymacro(data)
type
Ta = seq[tuple[c:int, d:int]]
Tb = tuple[e:seq[int], f:seq[int]]
const
a : Ta = @[(11, 22), (33, 44)]
b : Tb = (@[55,66], @[77, 88])
macro mA(data: static[Ta]): stmt =
echo "AST a \n", repr(data)
macro mB(data: static[Tb]): stmt =
echo "AST b \n", repr(data)
echo data.e[0]
mA(a)
mB(b)
type
Foo[N: static[int], Z: static[string]] = object
macro staticIntMacro(f: static[int]): stmt = echo f
staticIntMacro 10
var
x: Foo[20, "Test"]
macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): stmt =
echo N, Z
genericMacro x
template genericTemplate[N, Z](f: Foo[N, Z], ll = 3, zz = 12): int = N
static:
echo genericTemplate(x)