From 02f97489b795cd33d49966e254b46fcc3f8072ba Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 21 Jul 2015 10:21:14 +0300 Subject: fix #1858 again; restores the support for static macro params --- compiler/evaltempl.nim | 50 ++++++++++++++++++++++++++++++++++---------------- compiler/semcall.nim | 18 ++++++++++++++++-- compiler/seminst.nim | 25 +++++++++++++------------ compiler/vm.nim | 32 +++++++++++++++++++++++--------- compiler/vmgen.nim | 21 +++++++++++++++++++-- 5 files changed, 105 insertions(+), 41 deletions(-) (limited to 'compiler') 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: 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.. Date: Sun, 2 Aug 2015 23:23:13 +0300 Subject: Disable the new generic params handling for immediate template and macros Since immediate templates are not subjected to the standard sigmatching algorithm, they will have a number of deficiencies when it comes to generic params: Type dependencies between the parameters won't be honoured and the bound generic symbols won't be resolvable within their bodies. We could try to fix this, but it may be wiser to just deprecate immediate templates and macros now that we have working untyped parameters. Disabling the new features is admittedly not the greatest way to handle this situations as it introduces inconsistency in the language, but at least it makes the code backwards-compatible with the previous version of the compiler instead of triggering more serious problems. --- compiler/evaltempl.nim | 12 +++++++++++- compiler/vm.nim | 9 +++++---- compiler/vmgen.nim | 6 +++++- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'compiler') diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 015c14ab5..82c4e8f57 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -66,7 +66,16 @@ proc evalTemplateArgs(n: PNode, s: PSym): PNode = else: 0 var - genericParams = s.ast[genericParamsPos].len + # XXX: Since immediate templates are not subjected to the + # standard sigmatching algorithm, they will have a number + # of deficiencies when it comes to generic params: + # Type dependencies between the parameters won't be honoured + # and the bound generic symbols won't be resolvable within + # their bodies. We could try to fix this, but it may be + # wiser to just deprecate immediate templates and macros + # now that we have working untyped parameters. + genericParams = if sfImmediate in s.flags: 0 + else: s.ast[genericParamsPos].len expectedRegularParams =