first steps towards term rewriting macros

This commit is contained in:
Araq 2012-08-30 10:55:40 +02:00
commit 1786e30991
17 changed files with 264 additions and 58 deletions

View file

@ -176,6 +176,7 @@ type
nkFromStmt, # a from * import statement
nkIncludeStmt, # an include statement
nkBindStmt, # a bind statement
nkPatternStmt, # a pattern statement ('as' statement)
nkCommentStmt, # a comment statement
nkStmtListExpr, # a statement list followed by an expr; this is used
# to allow powerful multi-line templates
@ -246,8 +247,8 @@ type
# for interfacing with C++, JS
sfNamedParamCall, # symbol needs named parameter call syntax in target
# language; for interfacing with Objective C
sfDiscardable # returned value may be discarded implicitely
sfDestructor # proc is destructor
sfDiscardable, # returned value may be discarded implicitely
sfDestructor, # proc is destructor
sfGenSym # symbol is 'gensym'ed; do not add to symbol table
TSymFlags* = set[TSymFlag]
@ -689,9 +690,10 @@ const
genericParamsPos* = 1
paramsPos* = 2
pragmasPos* = 3
bodyPos* = 4 # position of body; use rodread.getBody() instead!
resultPos* = 5
dispatcherPos* = 6 # caution: if method has no 'result' it can be position 5!
patternPos* = 4 # empty except for term rewriting macros
bodyPos* = 5 # position of body; use rodread.getBody() instead!
resultPos* = 6
dispatcherPos* = 7 # caution: if method has no 'result' it can be position 5!
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
nkCommand, nkCallStrLit}
@ -1175,6 +1177,9 @@ proc isRoutine*(s: PSym): bool {.inline.} =
result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod,
skConverter}
proc hasPattern*(s: PSym): bool {.inline.} =
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
iterator items*(n: PNode): PNode =
for i in 0.. <n.len: yield n.sons[i]

View file

@ -834,8 +834,8 @@ proc writeIdNodeTable(t: TIdNodeTable) =
proc IdNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
var h: THash
h = key.id and high(t.data) # start with real hash value
while t.data[h].key != nil:
if (t.data[h].key.id == key.id):
while t.data[h].key != nil:
if t.data[h].key.id == key.id:
return h
h = nextTry(h, high(t.data))
result = - 1
@ -845,6 +845,10 @@ proc IdNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
index = IdNodeTableRawGet(t, key)
if index >= 0: result = t.data[index].val
else: result = nil
proc IdNodeTableGetLazy*(t: TIdNodeTable, key: PIdObj): PNode =
if not isNil(t.data):
result = IdNodeTableGet(t, key)
proc IdNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
var h: THash
@ -872,6 +876,10 @@ proc IdNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
IdNodeTableRawInsert(t.data, key, val)
inc(t.counter)
proc IdNodeTablePutLazy*(t: var TIdNodeTable, key: PIdObj, val: PNode) =
if isNil(t.data): initIdNodeTable(t)
IdNodeTablePut(t, key, val)
iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
for i in 0 .. high(t.data):
if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)

View file

@ -45,8 +45,8 @@ proc rawImportSymbol(c: PContext, s: PSym) =
var copy = s # do not copy symbols when importing!
# check if we have already a symbol of the same name:
var check = StrTableGet(c.tab.stack[importTablePos], s.name)
if (check != nil) and (check.id != copy.id):
if not (s.kind in OverloadableSyms):
if check != nil and check.id != copy.id:
if s.kind notin OverloadableSyms:
# s and check need to be qualified:
Incl(c.AmbiguousSymbols, copy.id)
Incl(c.AmbiguousSymbols, check.id)
@ -70,8 +70,10 @@ proc rawImportSymbol(c: PContext, s: PSym) =
check = NextIdentIter(it, c.tab.stack[importTablePos])
if e != nil:
rawImportSymbol(c, e)
elif s.kind == skConverter:
addConverter(c, s) # rodgen assures that converters are no stubs
else:
# rodgen assures that converters and patterns are no stubs
if s.kind == skConverter: addConverter(c, s)
if hasPattern(s): addPattern(c, s)
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerAcc(n)

View file

@ -183,7 +183,7 @@ proc getPrecedence(tok: TToken): int =
of '?': result = 2
else: considerAsgn(2)
of tkDiv, tkMod, tkShl, tkShr: result = 9
of tkIn, tkNotIn, tkIs, tkIsNot, tkNot, tkOf: result = 5
of tkIn, tkNotIn, tkIs, tkIsNot, tkNot, tkOf, tkAs: result = 5
of tkDotDot: result = 6
of tkAnd: result = 4
of tkOr, tkXor: result = 3
@ -969,9 +969,18 @@ proc parseBreakOrContinue(p: var TParser, kind: TNodeKind): PNode =
of tkEof, tkSad, tkDed: addSon(result, ast.emptyNode)
else: addSon(result, parseSymbol(p))
proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
proc parseAs(p: var TParser): PNode =
result = newNodeP(nkPatternStmt, p)
getTok(p) # skip `as`
if p.tok.tokType == tkColon:
eat(p, tkColon)
addSon(result, parseStmt(p))
else:
addSon(result, parseExpr(p))
proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
result = newNodeP(kind, p)
while true:
while true:
getTok(p) # skip `if`, `when`, `elif`
var branch = newNodeP(nkElifBranch, p)
optInd(p, branch)
@ -981,8 +990,8 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
addSon(branch, parseStmt(p))
skipComment(p, branch)
addSon(result, branch)
if p.tok.tokType != tkElif: break
if p.tok.tokType == tkElse:
if p.tok.tokType != tkElif: break
if p.tok.tokType == tkElse:
var branch = newNodeP(nkElse, p)
eat(p, tkElse)
eat(p, tkColon)
@ -1176,6 +1185,8 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
addSon(result, parseParamList(p))
if p.tok.tokType == tkCurlyDotLe: addSon(result, parsePragma(p))
else: addSon(result, ast.emptyNode)
# empty pattern:
addSon(result, ast.emptyNode)
if p.tok.tokType == tkEquals:
getTok(p)
skipComment(p, result)
@ -1511,6 +1522,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode =
of tkWhen: result = parseIfOrWhen(p, nkWhenStmt)
of tkVar: result = parseSection(p, nkVarSection, parseVariable)
of tkBind: result = parseBind(p)
of tkAs: result = parseAs(p)
else: result = simpleStmt(p)
proc parseStmt(p: var TParser): PNode =

View file

@ -15,13 +15,8 @@ import
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, rodread, semthreads, idgen
type
TPassComm* = object {.pure.} ## communication object between passes
optimizers*: TSymSeq ## filled by semantic pass; used in HLO
PPassComm* = ref TPassComm
type
TPassContext* = object of TObject # the pass's context
comm*: PPassComm
fromCache*: bool # true if created by "openCached"
PPassContext* = ref TPassContext
@ -85,26 +80,18 @@ proc registerPass(p: TPass) =
gPasses[gPassesLen] = p
inc(gPassesLen)
proc newPassComm(): PPassComm =
new(result)
result.optimizers = @[]
proc openPasses(a: var TPassContextArray, module: PSym, filename: string) =
var comm = newPassComm()
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].open):
a[i] = gPasses[i].open(module, filename)
if a[i] != nil: a[i].comm = comm
else: a[i] = nil
proc openPassesCached(a: var TPassContextArray, module: PSym, filename: string,
rd: PRodReader) =
var comm = newPassComm()
for i in countup(0, gPassesLen - 1):
if not isNil(gPasses[i].openCached):
a[i] = gPasses[i].openCached(module, filename, rd)
if a[i] != nil:
a[i].comm = comm
a[i].fromCache = true
else:
a[i] = nil

128
compiler/patterns.nim Normal file
View file

@ -0,0 +1,128 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the pattern matching features for term rewriting
## macro support.
import ast, astalgo, types, semdata, sigmatch, msgs, idents
type
TPatternContext = object
owner: PSym
mapping: TIdNodeTable # maps formal parameters to nodes
c: PContext
PPatternContext = var TPatternContext
proc matches(c: PPatternContext, p, n: PNode): bool
proc checkConstraints(c: PPatternContext, p, n: PNode): bool =
# XXX create a new mapping here? --> need use cases
result = matches(c, p, n)
proc canonKind(n: PNode): TNodeKind =
## nodekind canonilization for pattern matching
result = n.kind
case result
of nkCallKinds: result = nkCall
of nkStrLit..nkTripleStrLit: result = nkStrLit
of nkFastAsgn: result = nkAsgn
else: nil
proc sameKinds(a, b: PNode): bool {.inline.} =
result = a.kind == b.kind or a.canonKind == b.canonKind
proc sameTrees(a, b: PNode): bool =
if sameKinds(a, b):
case a.kind
of nkSym: result = a.sym == b.sym
of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkEmpty, nkNilLit: result = true
of nkType: result = sameTypeOrNil(a.typ, b.typ)
else:
if sonsLen(a) == sonsLen(b):
for i in countup(0, sonsLen(a) - 1):
if not sameTrees(a.sons[i], b.sons[i]): return
result = true
proc inSymChoice(sc, x: PNode): bool =
if sc.kind in {nkOpenSymChoice, nkClosedSymChoice}:
for i in 0.. <sc.len:
if sc.sons[i].sym == x.sym: return true
proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
# XXX tyVarargs is special here; lots of other special cases
if isNil(n.typ):
result = p.typ.kind == tyStmt
else:
result = sigmatch.argtypeMatches(c.c, p.typ, n.typ)
proc matches(c: PPatternContext, p, n: PNode): bool =
# XXX special treatment: statement list,
# ignore comments, nkPar, hidden conversions
# f(..X) ~> how can 'X' stand for all remaining parameters? -> introduce
# a new local node kind (alias of nkReturnToken or something)
if p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner:
var pp = IdNodeTableGetLazy(c.mapping, p.sym)
if pp != nil:
# check if we got the same pattern (already unified):
result = matches(c, pp, n)
elif checkTypes(c, p.sym, n) and
(p.sym.ast == nil or checkConstraints(c, p.sym.ast, n)):
IdNodeTablePutLazy(c.mapping, p.sym, n)
result = true
elif n.kind == nkSym and inSymChoice(p, n):
result = true
elif n.kind == nkSym and n.sym.kind == skConst:
# try both:
if sameTrees(p, n): result = true
elif matches(c, p, n.sym.ast):
result = true
elif sameKinds(p, n):
case p.kind
of nkSym: result = p.sym == n.sym
of nkIdent: result = p.ident.id == n.ident.id
of nkCharLit..nkInt64Lit: result = p.intVal == n.intVal
of nkFloatLit..nkFloat64Lit: result = p.floatVal == n.floatVal
of nkStrLit..nkTripleStrLit: result = p.strVal == n.strVal
of nkEmpty, nkNilLit, nkType: result = true
else:
if sonsLen(p) == sonsLen(n):
for i in countup(0, sonsLen(p) - 1):
if not matches(c, p.sons[i], n.sons[i]): return
result = true
# writeln(X, a); writeln(X, b); --> writeln(X, a, b)
proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
## returns a tree to semcheck if the rule triggered; nil otherwise
var ctx: TPatternContext
ctx.owner = s
ctx.c = c
# we perform 'initIdNodeTable' lazily for performance
if matches(ctx, s.ast.sons[patternPos], n):
# each parameter should have been bound; we simply setup a call and
# let semantic checking deal with the rest :-)
# this also saves type checking if we allow for type checking errors
# as in 'system.compiles' and simply discard the results. But an error
# may have been desired in the first place! Meh, it's good enough for
# a first implementation:
result = newNodeI(nkCall, n.info)
result.add(newSymNode(s, n.info))
let params = s.typ.n
for i in 1 .. < params.len:
let param = params.sons[i].sym
let x = IdNodeTableGetLazy(ctx.mapping, param)
# couldn't bind parameter:
if isNil(x):
echo "couldn't bind ", param.name.s
return nil
result.add(x)

View file

@ -586,6 +586,16 @@ proc gwhile(g: var TSrcGen, n: PNode) =
gcoms(g) # a good place for comments
gstmts(g, n.sons[1], c)
proc gpattern(g: var TSrcGen, n: PNode) =
var c: TContext
put(g, tkAs, "as")
putWithSpace(g, tkColon, ":")
initContext(c)
if longMode(n) or (lsub(n.sons[0]) + g.lineLen > maxLineLen):
incl(c.flags, rfLongMode)
gcoms(g) # a good place for comments
gstmts(g, n.sons[0], c)
proc gpragmaBlock(g: var TSrcGen, n: PNode) =
var c: TContext
gsub(g, n.sons[0])
@ -656,20 +666,20 @@ proc gproc(g: var TSrcGen, n: PNode) =
put(g, tkSymbol, renderDefinitionName(n.sons[namePos].sym))
else:
gsub(g, n.sons[namePos])
gsub(g, n.sons[1])
gsub(g, n.sons[2])
gsub(g, n.sons[3])
gsub(g, n.sons[genericParamsPos])
gsub(g, n.sons[paramsPos])
gsub(g, n.sons[pragmasPos])
if renderNoBody notin g.flags:
if n.sons[4].kind != nkEmpty:
if n.sons[bodyPos].kind != nkEmpty:
put(g, tkSpaces, Space)
putWithSpace(g, tkEquals, "=")
indentNL(g)
gcoms(g)
dedent(g)
initContext(c)
gstmts(g, n.sons[4], c)
gstmts(g, n.sons[bodyPos], c)
putNL(g)
else:
else:
indentNL(g)
gcoms(g)
dedent(g)
@ -1035,6 +1045,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
of nkCaseStmt, nkRecCase: gcase(g, n)
of nkMacroStmt: gmacro(g, n)
of nkTryStmt: gtry(g, n)
of nkPatternStmt: gpattern(g, n)
of nkForStmt, nkParForStmt: gfor(g, n)
of nkBlockStmt, nkBlockExpr: gblock(g, n)
of nkStaticStmt: gstaticStmt(g, n)

View file

@ -11,7 +11,7 @@
#
# Reading and writing binary files are really hard to debug. Therefore we use
# a "creative" text/binary hybrid format. ROD-files are more efficient
# to process because symbols are can be loaded on demand.
# to process because symbols can be loaded on demand.
#
# A ROD file consists of:
#
@ -65,10 +65,12 @@
# semantic checking:
# CONVERTERS:id id\n # symbol ID
#
# This is a misnomer now; it's really a "load unconditionally" section as
# it is also used for pattern templates.
#
# - a list of all (private or exported) methods because they are needed for
# correct dispatcher generation:
# METHODS: id id\n # symbol ID
#
# - an AST section that contains the module's AST:
# INIT(
# idx\n # position of the node in the DATA section

View file

@ -357,10 +357,10 @@ proc symStack(w: PRodWriter): int =
add(w.compilerProcs, ' ')
encodeVInt(s.id, w.compilerProcs)
add(w.compilerProcs, rodNL)
if s.kind == skConverter:
if s.kind == skConverter or hasPattern(s):
if w.converters.len != 0: add(w.converters, ' ')
encodeVInt(s.id, w.converters)
elif s.kind == skMethod and sfDispatcher notin s.flags:
if s.kind == skMethod and sfDispatcher notin s.flags:
if w.methods.len != 0: add(w.methods, ' ')
encodeVInt(s.id, w.methods)
elif IiTableGet(w.imports.tab, s.id) == invalidKey:

View file

@ -15,7 +15,7 @@ import
magicsys, parser, nversion, nimsets, semfold, importer,
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
semthreads, intsets, transf, evals, idgen, aliases, cgmeth, lambdalifting,
evaltempl
evaltempl, patterns
proc semPass*(): TPass
# implementation
@ -102,6 +102,19 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
return n
result = evalTypedExpr(c, e)
proc applyPatterns(c: PContext, n: PNode): PNode =
# fast exit:
if c.patterns.len == 0: return n
result = n
# we apply the last pattern first, so that pattern overriding is possible;
# however the resulting AST would better not trigger the old rule then
# anymore ;-)
for i in countdown(<c.patterns.len, 0):
let x = applyRule(c, c.patterns[i], result)
if not isNil(x):
assert x.kind == nkCall
result = semExpr(c, x)
include seminst, semcall
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym): PNode =

View file

@ -67,6 +67,7 @@ type
InUnrolledContext*: int # > 0 if we are unrolling a loop
InCompilesContext*: int # > 0 if we are in a ``compiles`` magic
converters*: TSymSeq # sequence of converters
patterns*: TSymSeq # sequence of pattern matchers
optionStack*: TLinkedList
libs*: TLinkedList # all libs used by this module
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
@ -93,7 +94,6 @@ proc newContext*(module: PSym, nimfile: string): PContext
proc lastOptionEntry*(c: PContext): POptionEntry
proc newOptionEntry*(): POptionEntry
proc addConverter*(c: PContext, conv: PSym)
proc newLib*(kind: TLibKind): PLib
proc addToLib*(lib: PLib, sym: PSym)
proc makePtrType*(c: PContext, baseType: PType): PType
@ -158,6 +158,7 @@ proc newContext(module: PSym, nimfile: string): PContext =
result.friendModule = module
result.threadEntries = @[]
result.converters = @[]
result.patterns = @[]
result.filename = nimfile
result.includedFiles = initIntSet()
initStrTable(result.userPragmas)
@ -172,12 +173,18 @@ proc newContext(module: PSym, nimfile: string): PContext =
assert gGenericsCache == nil
result.UnknownIdents = initIntSet()
proc addConverter(c: PContext, conv: PSym) =
var L = len(c.converters)
proc inclSym(sq: var TSymSeq, s: PSym) =
var L = len(sq)
for i in countup(0, L - 1):
if c.converters[i].id == conv.id: return
setlen(c.converters, L + 1)
c.converters[L] = conv
if sq[i].id == s.id: return
setlen(sq, L + 1)
sq[L] = s
proc addConverter*(c: PContext, conv: PSym) =
inclSym(c.converters, conv)
proc addPattern*(c: PContext, p: PSym) =
inclSym(c.patterns, p)
proc newLib(kind: TLibKind): PLib =
new(result)

View file

@ -1591,3 +1591,4 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
LocalError(n.info, errInvalidExpressionX,
renderTree(n, {renderNoComments}))
incl(result.flags, nfSem)
result = applyPatterns(c, result)

View file

@ -1202,6 +1202,16 @@ proc SemStmt(c: PContext, n: PNode): PNode =
result = semPragmaBlock(c, n)
of nkStaticStmt:
result = semStaticStmt(c, n)
of nkPatternStmt:
let pat = semPatternStmt(c, n)
let s = getCurrOwner()
if s.kind in routineKinds and s.ast.sons[patternPos].kind == nkEmpty:
s.ast.sons[patternPos] = pat
c.patterns.add(s)
else:
LocalError(n.info, errXNotAllowedHere, "'as'")
# replace by an empty statement:
result = newNodeI(nkNilLit, n.info)
else:
# in interactive mode, we embed the expression in an 'echo':
if gCmd == cmdInteractive:
@ -1214,10 +1224,11 @@ proc SemStmt(c: PContext, n: PNode): PNode =
InternalError(n.info, "SemStmt: result = nil")
# error correction:
result = emptyNode
else:
else:
incl(result.flags, nfSem)
result = applyPatterns(c, result)
proc semStmtScope(c: PContext, n: PNode): PNode =
proc semStmtScope(c: PContext, n: PNode): PNode =
openScope(c.tab)
result = semStmt(c, n)
closeScope(c.tab)

View file

@ -98,10 +98,16 @@ proc replaceIdentBySym(n: var PNode, s: PNode) =
of nkIdent, nkAccQuoted, nkSym: n = s
else: illFormedAst(n)
# This code here is the first pass over a template's body. The same code also
# implements the first pass over a pattern's body:
type
TBodyKind = enum
bkTemplate, bkPattern
TemplCtx {.pure, final.} = object
c: PContext
toBind: TIntSet
bodyKind: TBodyKind
owner: PSym
proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
@ -166,10 +172,8 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
else:
n.sons[namePos] = semTemplBody(c, n.sons[namePos])
openScope(c)
n.sons[genericParamsPos] = semTemplBody(c, n.sons[genericParamsPos])
n.sons[paramsPos] = semTemplBody(c, n.sons[paramsPos])
n.sons[pragmasPos] = semTemplBody(c, n.sons[pragmasPos])
n.sons[bodyPos] = semTemplBodyScope(c, n.sons[bodyPos])
for i in genericParamsPos..bodyPos:
n.sons[i] = semTemplBody(c, n.sons[i])
closeScope(c)
proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
@ -183,6 +187,8 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
result = newSymNode(s, n.info)
elif Contains(c.toBind, s.id):
result = symChoice(c.c, n, s, scClosed)
elif c.bodyKind == bkPattern:
result = symChoice(c.c, n, s, scOpen)
elif s.owner == c.owner and sfGenSym in s.flags:
# template tmp[T](x: var seq[T]) =
# var yz: T
@ -395,6 +401,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
ctx.toBind = initIntSet()
ctx.c = c
ctx.owner = s
ctx.bodyKind = bkTemplate
if sfDirty in s.flags:
n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos])
else:
@ -415,3 +422,15 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
else:
SymTabReplace(c.tab.stack[curScope], proto, s)
proc semPatternStmt(c: PContext, n: PNode): PNode =
# not much to do here: We don't replace operators ``$``, ``*``, ``+``,
# ``|``, ``~`` as meta operators and strip the leading ``\`` of all
# operators.
openScope(c.tab)
var ctx: TemplCtx
ctx.toBind = initIntSet()
ctx.c = c
ctx.owner = getCurrOwner()
ctx.bodyKind = bkPattern
result = semTemplBody(ctx, n.sons[0])
closeScope(c.tab)

View file

@ -647,7 +647,7 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
inc(m.convMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
of isIntConv:
# too lazy to introduce another ``*matches`` field, so we conflate
# I'm too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here:
inc(m.intConvMatches)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)