mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 05:50:30 +00:00
fixes #3496
The problem comes from the fact that macroOrTmpl[...] is transformed by
semSubscript which is trying to evaluate macroOrTmpl identifier in place. This
is okay for non-generic macros or templates, but wrong for generic ones, that
do not have a chance to receive their generic arguments explicitly specified in
brackets.
Solution:
1. macroOrTmpl[...] where macroOrTmpl is non-generic macro or template, then
macroOrTmpl is evaluated before applying brackets. (as before)
2. macroOrTmpl[...] where macroOrTmpl is generic macro or template, then if:
a. It comes from macroOrTmpl[...](...) call expr (efInCall), then macroOrTmpl
is turned into a symbol (efNoEvaluate) rather than evaluating it in place,
then whole bracket expr is returned to semIndirectOp which transforms it
to proper generic macro or template call with explicit generic arguments.
b. macroOrTmpl[...] does not come from call expr, as above macroOrTmpl is
transformed to symbol, then it is transformed into proper generic macro or
template call with explicit generic arguments and no normal arguments.
This commit is contained in:
@@ -46,7 +46,7 @@ type
|
||||
efLValue, efWantIterator, efInTypeof,
|
||||
efWantStmt, efAllowStmt, efDetermineType,
|
||||
efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
|
||||
efNoProcvarCheck
|
||||
efNoProcvarCheck, efNoEvaluateGeneric, efInCall
|
||||
TExprFlags* = set[TExprFlag]
|
||||
|
||||
TTypeAttachedOp* = enum
|
||||
|
||||
@@ -102,8 +102,20 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
else: result = newSymNode(s, n.info)
|
||||
else:
|
||||
result = newSymNode(s, n.info)
|
||||
of skMacro: result = semMacroExpr(c, n, n, s, flags)
|
||||
of skTemplate: result = semTemplateExpr(c, n, s, flags)
|
||||
of skMacro:
|
||||
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len>0:
|
||||
markUsed(n.info, s)
|
||||
styleCheckUse(n.info, s)
|
||||
result = newSymNode(s, n.info)
|
||||
else:
|
||||
result = semMacroExpr(c, n, n, s, flags)
|
||||
of skTemplate:
|
||||
if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len>0:
|
||||
markUsed(n.info, s)
|
||||
styleCheckUse(n.info, s)
|
||||
result = newSymNode(s, n.info)
|
||||
else:
|
||||
result = semTemplateExpr(c, n, s, flags)
|
||||
of skParam:
|
||||
markUsed(n.info, s)
|
||||
styleCheckUse(n.info, s)
|
||||
@@ -806,10 +818,34 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
|
||||
return semExpr(c, result, flags)
|
||||
else:
|
||||
n.sons[0] = semExpr(c, n.sons[0])
|
||||
n.sons[0] = semExpr(c, n.sons[0], {efInCall})
|
||||
let t = n.sons[0].typ
|
||||
if t != nil and t.kind == tyVar:
|
||||
n.sons[0] = newDeref(n.sons[0])
|
||||
elif n.sons[0].kind == nkBracketExpr:
|
||||
checkMinSonsLen(n.sons[0], 2)
|
||||
# We received untransformed bracket expression coming from macroOrTmpl[].
|
||||
# Transform it to macro or template call, where first come normal
|
||||
# arguments, next come generic template arguments.
|
||||
if n.sons[0].sons[0].kind == nkSym:
|
||||
let s = n.sons[0].sons[0].sym
|
||||
if s.kind in {skMacro, skTemplate}:
|
||||
var sons = newSeq[PNode]()
|
||||
sons.add n.sons[0].sons[0]
|
||||
# Normal arguments:
|
||||
for i in 1..<n.len:
|
||||
sons.add n.sons[i]
|
||||
# Generic template arguments from bracket expression:
|
||||
for i in 1..<n.sons[0].len:
|
||||
sons.add n.sons[0].sons[i]
|
||||
n.sons = sons
|
||||
# FIXME: Shouldn't we check sfImmediate and call semDirectOp?
|
||||
# However passing to semDirectOp doesn't work here.
|
||||
case s.kind
|
||||
of skMacro: result = semMacroExpr(c, n, n, s, flags)
|
||||
of skTemplate: result = semTemplateExpr(c, n, s, flags)
|
||||
else: discard
|
||||
return
|
||||
let nOrig = n.copyTree
|
||||
semOpAux(c, n)
|
||||
var t: PType = nil
|
||||
@@ -1157,7 +1193,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
result.add(x[0])
|
||||
return
|
||||
checkMinSonsLen(n, 2)
|
||||
n.sons[0] = semExprWithType(c, n.sons[0], {efNoProcvarCheck})
|
||||
# make sure we don't evaluate generic macros/templates
|
||||
n.sons[0] = semExprWithType(c, n.sons[0],
|
||||
{efNoProcvarCheck, efNoEvaluateGeneric})
|
||||
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyPtr, tyRef})
|
||||
case arr.kind
|
||||
of tyArray, tyOpenArray, tyVarargs, tyArrayConstr, tySequence, tyString,
|
||||
@@ -1204,6 +1242,20 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# type parameters: partial generic specialization
|
||||
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
|
||||
result = explicitGenericInstantiation(c, n, s)
|
||||
elif s != nil and s.kind in {skMacro, skTemplate}:
|
||||
if efInCall in flags:
|
||||
# We are processing macroOrTmpl[] in macroOrTmpl[](...) call.
|
||||
# Return as is, so it can be transformed into complete macro or
|
||||
# template call in semIndirectOp caller.
|
||||
result = n
|
||||
else:
|
||||
# We are processing macroOrTmpl[] not in call. Transform it to the
|
||||
# macro or template call with generic arguments here.
|
||||
n.kind = nkCall
|
||||
case s.kind
|
||||
of skMacro: result = semMacroExpr(c, n, n, s, flags)
|
||||
of skTemplate: result = semTemplateExpr(c, n, s, flags)
|
||||
else: discard
|
||||
elif s != nil and s.kind == skType:
|
||||
result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
|
||||
else:
|
||||
|
||||
31
tests/generics/tgenerictmpl2.nim
Normal file
31
tests/generics/tgenerictmpl2.nim
Normal file
@@ -0,0 +1,31 @@
|
||||
discard """
|
||||
output: '''1
|
||||
1
|
||||
1
|
||||
1
|
||||
999
|
||||
999
|
||||
999
|
||||
2'''
|
||||
"""
|
||||
|
||||
# test if we can pass explicit generic arguments to generic templates
|
||||
# based on bug report #3496
|
||||
|
||||
proc tproc[T](t: T = 999) = echo t
|
||||
template ttmpl[T](t: T = 999) = echo t
|
||||
|
||||
tproc(1)
|
||||
tproc[int](1)
|
||||
ttmpl(1)
|
||||
ttmpl[int](1) #<- crash case #1
|
||||
|
||||
tproc[int]()
|
||||
discard tproc[int]
|
||||
ttmpl[int]() #<- crash case #2
|
||||
ttmpl[int] #<- crash case #3
|
||||
|
||||
# but still allow normal use of [] on non-generic templates
|
||||
|
||||
template tarr: expr = [1, 2, 3, 4]
|
||||
echo tarr[1]
|
||||
Reference in New Issue
Block a user