diff options
author | Zahary Karadjov <zahary@gmail.com> | 2015-07-21 10:21:14 +0300 |
---|---|---|
committer | Zahary Karadjov <zahary@gmail.com> | 2015-08-02 23:58:22 +0300 |
commit | 02f97489b795cd33d49966e254b46fcc3f8072ba (patch) | |
tree | c2be27abe57fe3b0f1118f21065409abc9784629 | |
parent | 7af92708af94164cd1373be85a06dd4cc4e4439e (diff) | |
download | Nim-02f97489b795cd33d49966e254b46fcc3f8072ba.tar.gz |
fix #1858 again; restores the support for static macro params
-rw-r--r-- | compiler/evaltempl.nim | 50 | ||||
-rw-r--r-- | compiler/semcall.nim | 18 | ||||
-rw-r--r-- | compiler/seminst.nim | 25 | ||||
-rw-r--r-- | compiler/vm.nim | 32 | ||||
-rw-r--r-- | compiler/vmgen.nim | 21 | ||||
-rw-r--r-- | tests/macros/tstaticparamsmacro.nim | 74 |
6 files changed, 179 insertions, 41 deletions
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 2b3112909..015c14ab5 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -25,16 +25,21 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = if ctx.instLines: result.info = b.info 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 of nkSym: var s = templ.sym if s.owner.id == c.owner.id: if s.kind == skParam and sfGenSym notin s.flags: - let x = actual.sons[s.position] - if x.kind == nkArgList: - for y in items(x): result.add(y) - else: - result.add copyTree(x) + handleParam actual.sons[s.position] + elif s.kind == skGenericParam: + handleParam actual.sons[s.owner.typ.len + s.position - 1] else: internalAssert sfGenSym in s.flags 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 = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar - var a: int - case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: - a = sonsLen(n) - else: a = 0 - var f = s.typ.sonsLen - if a > f: globalError(n.info, errWrongNumberOfArguments) + var totalParams = case n.kind + of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: <n.len + else: 0 + + var + genericParams = s.ast[genericParamsPos].len + expectedRegularParams = <s.typ.len + givenRegularParams = totalParams - genericParams + if totalParams > expectedRegularParams + genericParams: + globalError(n.info, errWrongNumberOfArguments) + result = newNodeI(nkArgList, n.info) - for i in countup(1, f - 1): - var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast) - if arg == nil or arg.kind == nkEmpty: + for i in 1 .. givenRegularParams: + result.addSon n.sons[i] + + # 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) addSon(result, ast.emptyNode) 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 # to prevent endless recursion in templates instantiation diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 571504c3a..e2ff16c6e 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -305,8 +305,22 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = if containsGenericType(result.typ) or x.fauxMatch == tyUnknown: result.typ = newTypeS(x.fauxMatch, c) return - if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty: - finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) + let gp = finalCallee.ast.sons[genericParamsPos] + 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 instGenericConvertersSons(c, result, x) result.sons[0] = newSymNode(finalCallee, result.sons[0].info) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b2aef63a8..6ba107916 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -10,14 +10,10 @@ # This module implements the instantiation of generic procs. # included from sem.nim -proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, - entry: var TInstantiation) = - if n.kind != nkGenericParams: - internalError(n.info, "instantiateGenericParamList; no generic params") - newSeq(entry.concreteTypes, n.len) +iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = + internalAssert n.kind == nkGenericParams for i, a in n.pairs: - if a.kind != nkSym: - internalError(a.info, "instantiateGenericParamList; no symbol") + internalAssert a.kind == nkSym var q = a.sym if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: continue @@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, #t = ReplaceTypeVarsT(cl, t) s.typ = t if t.kind == tyStatic: s.ast = t.n - addDecl(c, s) - entry.concreteTypes[i] = t + yield s proc sameInstantiation(a, b: TInstantiation): bool = 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 ## parameters to their concrete types within the generic instance. # 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 if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") inc(c.instCounter) @@ -234,12 +229,18 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, result.ast = n pushOwner(result) openScope(c) - internalAssert n.sons[genericParamsPos].kind != nkEmpty + let gp = n.sons[genericParamsPos] + internalAssert gp.kind != nkEmpty n.sons[namePos] = newSymNode(result) pushInfoContext(info) var entry = TInstantiation.new 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) instantiateProcType(c, pt, result, info) n.sons[genericParamsPos] = ast.emptyNode diff --git a/compiler/vm.nim b/compiler/vm.nim index d7495d77f..fc63da1d4 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1467,12 +1467,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) = proc setupCompileTimeVar*(module: PSym, n: PNode) = discard evalConstExprAux(module, nil, n, emStaticStmt) -proc setupMacroParam(x: PNode): PNode = - result = x - if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] - result = canonValue(result) - result.flags.incl nfIsRef - result.typ = x.typ +proc setupMacroParam(x: PNode, typ: PType): TFullReg = + case typ.kind + of tyStatic: + putIntoReg(result, x) + of tyTypeDesc: + 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 @@ -1508,10 +1516,16 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = # return value: tos.slots[0].kind = rkNode tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) + # setup parameters: - for i in 1 .. < min(tos.slots.len, L): - tos.slots[i].kind = rkNode - tos.slots[i].node = setupMacroParam(n.sons[i]) + for i in 1.. <sym.typ.len: + tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.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: #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 1bee9788a..3f4ee8000 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -173,7 +173,8 @@ const proc bestEffort(c: PCtx): TLineInfo = (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 # we prefer the same slot kind here for efficiency. Unfortunately for # 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) c.gABC(n, opc, dest, tmp) 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) 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) of skType: 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: globalError(n.info, errGenerated, "cannot generate code for: " & s.name.s) of nkCallKinds: @@ -1767,6 +1773,14 @@ proc finalJumpTarget(c: PCtx; pc, diff: int) = c.code[pc] = ((oldInstr.uint32 and 0xffff'u32).uint32 or 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) = const maxIterations = 10 for i in start .. <c.code.len: @@ -1831,6 +1845,9 @@ proc genProc(c: PCtx; s: PSym): int = c.prc = p # iterate over the parameters and allocate space for them: 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: #let env = s.ast.sons[paramsPos].lastSon.sym #assert env.position == 2 diff --git a/tests/macros/tstaticparamsmacro.nim b/tests/macros/tstaticparamsmacro.nim new file mode 100644 index 000000000..1ab638396 --- /dev/null +++ b/tests/macros/tstaticparamsmacro.nim @@ -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) + |