Expand hoisted default params in sem (#15270)
* Expand hoisted default params in sem
Introduce ast.newTree{I,IT}
Add test for default params in procs
* Cleanup
* Simplify hoist transformation and expand test
This commit is contained in:
parent
70d6238756
commit
35ff17410f
12 changed files with 178 additions and 123 deletions
|
|
@ -312,10 +312,6 @@ const
|
||||||
# the compiler will avoid printing such names
|
# the compiler will avoid printing such names
|
||||||
# in user messages.
|
# in user messages.
|
||||||
|
|
||||||
sfHoisted* = sfForward
|
|
||||||
# an expression was hoised to an anonymous variable.
|
|
||||||
# the flag is applied to the var/let symbol
|
|
||||||
|
|
||||||
sfNoForward* = sfRegister
|
sfNoForward* = sfRegister
|
||||||
# forward declarations are not required (per module)
|
# forward declarations are not required (per module)
|
||||||
sfReorder* = sfForward
|
sfReorder* = sfForward
|
||||||
|
|
@ -1121,12 +1117,49 @@ proc newNode*(kind: TNodeKind): PNode =
|
||||||
writeStackTrace()
|
writeStackTrace()
|
||||||
inc gNodeId
|
inc gNodeId
|
||||||
|
|
||||||
|
proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
|
||||||
|
result = PNode(kind: kind, info: info)
|
||||||
|
when defined(useNodeIds):
|
||||||
|
result.id = gNodeId
|
||||||
|
if result.id == nodeIdToDebug:
|
||||||
|
echo "KIND ", result.kind
|
||||||
|
writeStackTrace()
|
||||||
|
inc gNodeId
|
||||||
|
|
||||||
|
proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
|
||||||
|
result = PNode(kind: kind, info: info)
|
||||||
|
if children > 0:
|
||||||
|
newSeq(result.sons, children)
|
||||||
|
when defined(useNodeIds):
|
||||||
|
result.id = gNodeId
|
||||||
|
if result.id == nodeIdToDebug:
|
||||||
|
echo "KIND ", result.kind
|
||||||
|
writeStackTrace()
|
||||||
|
inc gNodeId
|
||||||
|
|
||||||
|
proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
||||||
|
result = newNode(kind)
|
||||||
|
result.info = info
|
||||||
|
result.typ = typ
|
||||||
|
|
||||||
proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
|
proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
|
||||||
result = newNode(kind)
|
result = newNode(kind)
|
||||||
if children.len > 0:
|
if children.len > 0:
|
||||||
result.info = children[0].info
|
result.info = children[0].info
|
||||||
result.sons = @children
|
result.sons = @children
|
||||||
|
|
||||||
|
proc newTreeI*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode =
|
||||||
|
result = newNodeI(kind, info)
|
||||||
|
if children.len > 0:
|
||||||
|
result.info = children[0].info
|
||||||
|
result.sons = @children
|
||||||
|
|
||||||
|
proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[PNode]): PNode =
|
||||||
|
result = newNodeIT(kind, info, typ)
|
||||||
|
if children.len > 0:
|
||||||
|
result.info = children[0].info
|
||||||
|
result.sons = @children
|
||||||
|
|
||||||
template previouslyInferred*(t: PType): PType =
|
template previouslyInferred*(t: PType): PType =
|
||||||
if t.sons.len > 1: t.lastSon else: nil
|
if t.sons.len > 1: t.lastSon else: nil
|
||||||
|
|
||||||
|
|
@ -1227,43 +1260,6 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||||
result.typ = sym.typ
|
result.typ = sym.typ
|
||||||
result.info = info
|
result.info = info
|
||||||
|
|
||||||
proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode =
|
|
||||||
result = PNode(kind: kind, info: info)
|
|
||||||
when defined(useNodeIds):
|
|
||||||
result.id = gNodeId
|
|
||||||
if result.id == nodeIdToDebug:
|
|
||||||
echo "KIND ", result.kind
|
|
||||||
writeStackTrace()
|
|
||||||
inc gNodeId
|
|
||||||
|
|
||||||
proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode =
|
|
||||||
result = PNode(kind: kind, info: info)
|
|
||||||
if children > 0:
|
|
||||||
newSeq(result.sons, children)
|
|
||||||
when defined(useNodeIds):
|
|
||||||
result.id = gNodeId
|
|
||||||
if result.id == nodeIdToDebug:
|
|
||||||
echo "KIND ", result.kind
|
|
||||||
writeStackTrace()
|
|
||||||
inc gNodeId
|
|
||||||
|
|
||||||
proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[],
|
|
||||||
typ: PType = nil): PNode =
|
|
||||||
# XXX use shallowCopy here for ownership transfer:
|
|
||||||
result = PNode(kind: kind, info: info, typ: typ)
|
|
||||||
result.sons = sons
|
|
||||||
when defined(useNodeIds):
|
|
||||||
result.id = gNodeId
|
|
||||||
if result.id == nodeIdToDebug:
|
|
||||||
echo "KIND ", result.kind
|
|
||||||
writeStackTrace()
|
|
||||||
inc gNodeId
|
|
||||||
|
|
||||||
proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
|
|
||||||
result = newNode(kind)
|
|
||||||
result.info = info
|
|
||||||
result.typ = typ
|
|
||||||
|
|
||||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||||
result = newNode(kind)
|
result = newNode(kind)
|
||||||
result.intVal = intVal
|
result.intVal = intVal
|
||||||
|
|
|
||||||
|
|
@ -992,8 +992,8 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id
|
||||||
# set the type so that the following analysis doesn't screw up:
|
# set the type so that the following analysis doesn't screw up:
|
||||||
effects[i].typ = real[i].typ
|
effects[i].typ = real[i].typ
|
||||||
|
|
||||||
result = newNode(nkExprColonExpr, n.info, @[
|
result = newTreeI(nkExprColonExpr, n.info,
|
||||||
newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects])
|
newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects)
|
||||||
|
|
||||||
proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
|
proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
|
||||||
let s = n[namePos].sym
|
let s = n[namePos].sym
|
||||||
|
|
@ -1005,8 +1005,8 @@ proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName
|
||||||
effects.add params[i]
|
effects.add params[i]
|
||||||
|
|
||||||
if effects.len > 0:
|
if effects.len > 0:
|
||||||
result = newNode(nkExprColonExpr, n.info, @[
|
result = newTreeI(nkExprColonExpr, n.info,
|
||||||
newIdentNode(getIdent(cache, pragmaName), n.info), effects])
|
newIdentNode(getIdent(cache, pragmaName), n.info), effects)
|
||||||
|
|
||||||
proc documentRaises*(cache: IdentCache; n: PNode) =
|
proc documentRaises*(cache: IdentCache; n: PNode) =
|
||||||
if n[namePos].kind != nkSym: return
|
if n[namePos].kind != nkSym: return
|
||||||
|
|
|
||||||
|
|
@ -369,14 +369,3 @@ proc genLen*(g: ModuleGraph; n: PNode): PNode =
|
||||||
result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
|
result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
|
||||||
result[1] = n
|
result[1] = n
|
||||||
|
|
||||||
proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym =
|
|
||||||
result = newSym(skLet, name, owner, varSection.info, owner.options)
|
|
||||||
result.flags.incl sfHoisted
|
|
||||||
result.typ = expr.typ
|
|
||||||
|
|
||||||
var varDef = newNodeI(nkIdentDefs, varSection.info, 3)
|
|
||||||
varDef[0] = newSymNode(result)
|
|
||||||
varDef[1] = newNodeI(nkEmpty, varSection.info)
|
|
||||||
varDef[2] = expr
|
|
||||||
|
|
||||||
varSection.add varDef
|
|
||||||
|
|
|
||||||
|
|
@ -1444,7 +1444,7 @@ proc parseExprStmt(p: var Parser): PNode =
|
||||||
result.add(commandParam(p, isFirstParam, pmNormal))
|
result.add(commandParam(p, isFirstParam, pmNormal))
|
||||||
if p.tok.tokType != tkComma: break
|
if p.tok.tokType != tkComma: break
|
||||||
elif p.tok.indent < 0 and isExprStart(p):
|
elif p.tok.indent < 0 and isExprStart(p):
|
||||||
result = newNode(nkCommand, a.info, @[a])
|
result = newTreeI(nkCommand, a.info, a)
|
||||||
while true:
|
while true:
|
||||||
result.add(commandParam(p, isFirstParam, pmNormal))
|
result.add(commandParam(p, isFirstParam, pmNormal))
|
||||||
if p.tok.tokType != tkComma: break
|
if p.tok.tokType != tkComma: break
|
||||||
|
|
|
||||||
|
|
@ -532,7 +532,7 @@ proc processLink(c: PContext, n: PNode) =
|
||||||
proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||||
case n[1].kind
|
case n[1].kind
|
||||||
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
||||||
result = newNode(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
|
result = newNodeI(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
|
||||||
var str = n[1].strVal
|
var str = n[1].strVal
|
||||||
if str == "":
|
if str == "":
|
||||||
localError(con.config, n.info, "empty 'asm' statement")
|
localError(con.config, n.info, "empty 'asm' statement")
|
||||||
|
|
@ -563,7 +563,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
|
||||||
a = c + 1
|
a = c + 1
|
||||||
else:
|
else:
|
||||||
illFormedAstLocal(n, con.config)
|
illFormedAstLocal(n, con.config)
|
||||||
result = newNode(nkAsmStmt, n.info)
|
result = newNodeI(nkAsmStmt, n.info)
|
||||||
|
|
||||||
proc pragmaEmit(c: PContext, n: PNode) =
|
proc pragmaEmit(c: PContext, n: PNode) =
|
||||||
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
if n.kind notin nkPragmaCallKinds or n.len != 2:
|
||||||
|
|
@ -626,7 +626,7 @@ proc processPragma(c: PContext, n: PNode, i: int) =
|
||||||
invalidPragma(c, n)
|
invalidPragma(c, n)
|
||||||
|
|
||||||
var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options)
|
var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options)
|
||||||
userPragma.ast = newNode(nkPragma, n.info, n.sons[i+1..^1])
|
userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1])
|
||||||
strTableAdd(c.userPragmas, userPragma)
|
strTableAdd(c.userPragmas, userPragma)
|
||||||
|
|
||||||
proc pragmaRaisesOrTags(c: PContext, n: PNode) =
|
proc pragmaRaisesOrTags(c: PContext, n: PNode) =
|
||||||
|
|
|
||||||
|
|
@ -381,8 +381,7 @@ proc makeNotType*(c: PContext, t1: PType): PType =
|
||||||
result.flags.incl tfHasMeta
|
result.flags.incl tfHasMeta
|
||||||
|
|
||||||
proc nMinusOne(c: PContext; n: PNode): PNode =
|
proc nMinusOne(c: PContext; n: PNode): PNode =
|
||||||
result = newNode(nkCall, n.info, @[
|
result = newTreeI(nkCall, n.info, newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n)
|
||||||
newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n])
|
|
||||||
|
|
||||||
# Remember to fix the procs below this one when you make changes!
|
# Remember to fix the procs below this one when you make changes!
|
||||||
proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
||||||
|
|
@ -391,9 +390,8 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
||||||
result.sons = @[intType]
|
result.sons = @[intType]
|
||||||
if n.typ != nil and n.typ.n == nil:
|
if n.typ != nil and n.typ.n == nil:
|
||||||
result.flags.incl tfUnresolved
|
result.flags.incl tfUnresolved
|
||||||
result.n = newNode(nkRange, n.info, @[
|
result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType),
|
||||||
newIntTypeNode(0, intType),
|
makeStaticExpr(c, nMinusOne(c, n)))
|
||||||
makeStaticExpr(c, nMinusOne(c, n))])
|
|
||||||
|
|
||||||
template rangeHasUnresolvedStatic*(t: PType): bool =
|
template rangeHasUnresolvedStatic*(t: PType): bool =
|
||||||
tfUnresolved in t.flags
|
tfUnresolved in t.flags
|
||||||
|
|
|
||||||
|
|
@ -1556,10 +1556,9 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
|
||||||
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
|
||||||
# nodes?
|
# nodes?
|
||||||
let aOrig = nOrig[0]
|
let aOrig = nOrig[0]
|
||||||
result = newNode(nkCall, n.info, sons = @[setterId, a[0],
|
result = newTreeI(nkCall, n.info, setterId, a[0], semExprWithType(c, n[1]))
|
||||||
semExprWithType(c, n[1])])
|
|
||||||
result.flags.incl nfDotSetter
|
result.flags.incl nfDotSetter
|
||||||
let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
|
let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
|
||||||
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
|
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
|
||||||
|
|
||||||
if result != nil:
|
if result != nil:
|
||||||
|
|
@ -2041,7 +2040,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||||
dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type
|
dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type
|
||||||
ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type
|
ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type
|
||||||
ids.add c.graph.emptyNode # no default value
|
ids.add c.graph.emptyNode # no default value
|
||||||
dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids)
|
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids)
|
||||||
|
|
||||||
var tmpl = semTemplateDef(c, dummyTemplate)
|
var tmpl = semTemplateDef(c, dummyTemplate)
|
||||||
quotes[0] = tmpl[namePos]
|
quotes[0] = tmpl[namePos]
|
||||||
|
|
@ -2053,10 +2052,10 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||||
newIdentNode(getIdent(c.cache, "newIdentNode"), n.info)
|
newIdentNode(getIdent(c.cache, "newIdentNode"), n.info)
|
||||||
else:
|
else:
|
||||||
identNodeSym.newSymNode
|
identNodeSym.newSymNode
|
||||||
quotes[1] = newNode(nkCall, n.info, @[identNode, newStrNode(nkStrLit, "result")])
|
quotes[1] = newTreeI(nkCall, n.info, identNode, newStrNode(nkStrLit, "result"))
|
||||||
result = newNode(nkCall, n.info, @[
|
result = newTreeI(nkCall, n.info,
|
||||||
createMagic(c.graph, "getAst", mExpandToAst).newSymNode,
|
createMagic(c.graph, "getAst", mExpandToAst).newSymNode,
|
||||||
newNode(nkCall, n.info, quotes)])
|
newTreeI(nkCall, n.info, quotes))
|
||||||
result = semExpandToAst(c, result)
|
result = semExpandToAst(c, result)
|
||||||
|
|
||||||
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
|
|
@ -2579,6 +2578,43 @@ proc shouldBeBracketExpr(n: PNode): bool =
|
||||||
n[0] = be
|
n[0] = be
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
|
||||||
|
# This takes care of complicated signatures such as:
|
||||||
|
# proc foo(a: int, b = a)
|
||||||
|
# proc bar(a: int, b: int, c = a + b)
|
||||||
|
#
|
||||||
|
# The recursion may confuse you. It performs two duties:
|
||||||
|
#
|
||||||
|
# 1) extracting all referenced params from default expressions
|
||||||
|
# into a let section preceding the call
|
||||||
|
#
|
||||||
|
# 2) replacing the "references" within the default expression
|
||||||
|
# with these extracted skLet symbols.
|
||||||
|
#
|
||||||
|
# The first duty is carried out directly in the code here, while the second
|
||||||
|
# duty is activated by returning a non-nil value. The caller is responsible
|
||||||
|
# for replacing the input to the function with the returned non-nil value.
|
||||||
|
# (which is the hoisted symbol)
|
||||||
|
if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
|
||||||
|
let paramPos = defExpr.sym.position + 1
|
||||||
|
|
||||||
|
if call[paramPos].kind != nkSym:
|
||||||
|
let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.p.owner, letSection.info, c.p.owner.options)
|
||||||
|
hoistedVarSym.typ = call[paramPos].typ
|
||||||
|
|
||||||
|
letSection.add newTreeI(nkIdentDefs, letSection.info,
|
||||||
|
newSymNode(hoistedVarSym),
|
||||||
|
newNodeI(nkEmpty, letSection.info),
|
||||||
|
call[paramPos])
|
||||||
|
|
||||||
|
call[paramPos] = newSymNode(hoistedVarSym) # Refer the original arg to its hoisted sym
|
||||||
|
|
||||||
|
# arg we refer to is a sym, wether introduced by hoisting or not doesn't matter, we simply reuse it
|
||||||
|
defExpr = call[paramPos]
|
||||||
|
else:
|
||||||
|
for i in 0..<defExpr.safeLen:
|
||||||
|
hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
|
||||||
|
|
||||||
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
when defined(nimCompilerStackraceHints):
|
when defined(nimCompilerStackraceHints):
|
||||||
setFrameMsg c.config$n.info & " " & $n.kind
|
setFrameMsg c.config$n.info & " " & $n.kind
|
||||||
|
|
@ -2707,6 +2743,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
result = semDirectOp(c, n, flags)
|
result = semDirectOp(c, n, flags)
|
||||||
else:
|
else:
|
||||||
result = semIndirectOp(c, n, flags)
|
result = semIndirectOp(c, n, flags)
|
||||||
|
|
||||||
|
if nfDefaultRefsParam in result.flags:
|
||||||
|
result = result.copyTree #XXX: Figure out what causes default param nodes to be shared.. (sigmatch bug?)
|
||||||
|
# We've found a default value that references another param.
|
||||||
|
# See the notes in `hoistParamsUsedInDefault` for more details.
|
||||||
|
var hoistedParams = newNodeI(nkLetSection, result.info)
|
||||||
|
for i in 1..<result.len:
|
||||||
|
hoistParamsUsedInDefault(c, result, hoistedParams, result[i])
|
||||||
|
result = newTreeIT(nkStmtListExpr, result.info, result.typ, hoistedParams, result)
|
||||||
of nkWhen:
|
of nkWhen:
|
||||||
if efWantStmt in flags:
|
if efWantStmt in flags:
|
||||||
result = semWhen(c, n, true)
|
result = semWhen(c, n, true)
|
||||||
|
|
|
||||||
|
|
@ -1281,7 +1281,7 @@ proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) =
|
||||||
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
|
||||||
return
|
return
|
||||||
let g = c.graph
|
let g = c.graph
|
||||||
var effects = newNode(nkEffectList, n.info)
|
var effects = newNodeI(nkEffectList, n.info)
|
||||||
var t: TEffects
|
var t: TEffects
|
||||||
initEffects(g, effects, module, t, c)
|
initEffects(g, effects, module, t, c)
|
||||||
t.isTopLevel = isTopLevel
|
t.isTopLevel = isTopLevel
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
|
||||||
result.sym = replaceTypeVarsS(cl, n.sym)
|
result.sym = replaceTypeVarsS(cl, n.sym)
|
||||||
if result.sym.typ.kind == tyVoid:
|
if result.sym.typ.kind == tyVoid:
|
||||||
# don't add the 'void' field
|
# don't add the 'void' field
|
||||||
result = newNode(nkRecList, n.info)
|
result = newNodeI(nkRecList, n.info)
|
||||||
of nkRecWhen:
|
of nkRecWhen:
|
||||||
var branch: PNode = nil # the branch to take
|
var branch: PNode = nil # the branch to take
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
|
|
|
||||||
|
|
@ -886,43 +886,6 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc hoistParamsUsedInDefault(c: PTransf, call, letSection, defExpr: PNode): PNode =
|
|
||||||
# This takes care of complicated signatures such as:
|
|
||||||
# proc foo(a: int, b = a)
|
|
||||||
# proc bar(a: int, b: int, c = a + b)
|
|
||||||
#
|
|
||||||
# The recursion may confuse you. It performs two duties:
|
|
||||||
#
|
|
||||||
# 1) extracting all referenced params from default expressions
|
|
||||||
# into a let section preceding the call
|
|
||||||
#
|
|
||||||
# 2) replacing the "references" within the default expression
|
|
||||||
# with these extracted skLet symbols.
|
|
||||||
#
|
|
||||||
# The first duty is carried out directly in the code here, while the second
|
|
||||||
# duty is activated by returning a non-nil value. The caller is responsible
|
|
||||||
# for replacing the input to the function with the returned non-nil value.
|
|
||||||
# (which is the hoisted symbol)
|
|
||||||
if defExpr.kind == nkSym:
|
|
||||||
if defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
|
|
||||||
let paramPos = defExpr.sym.position + 1
|
|
||||||
|
|
||||||
if call[paramPos].kind == nkSym and sfHoisted in call[paramPos].sym.flags:
|
|
||||||
# Already hoisted, we still need to return it in order to replace the
|
|
||||||
# placeholder expression in the default value.
|
|
||||||
return call[paramPos]
|
|
||||||
|
|
||||||
let hoistedVarSym = hoistExpr(letSection,
|
|
||||||
call[paramPos],
|
|
||||||
getIdent(c.graph.cache, genPrefix),
|
|
||||||
c.transCon.owner).newSymNode
|
|
||||||
call[paramPos] = hoistedVarSym
|
|
||||||
return hoistedVarSym
|
|
||||||
else:
|
|
||||||
for i in 0..<defExpr.safeLen:
|
|
||||||
let hoisted = hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
|
|
||||||
if hoisted != nil: defExpr[i] = hoisted
|
|
||||||
|
|
||||||
proc transform(c: PTransf, n: PNode): PNode =
|
proc transform(c: PTransf, n: PNode): PNode =
|
||||||
when false:
|
when false:
|
||||||
var oldDeferAnchor: PNode
|
var oldDeferAnchor: PNode
|
||||||
|
|
@ -989,16 +952,6 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||||
of nkBreakStmt: result = transformBreak(c, n)
|
of nkBreakStmt: result = transformBreak(c, n)
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
result = transformCall(c, n)
|
result = transformCall(c, n)
|
||||||
var call = result
|
|
||||||
if nfDefaultRefsParam in call.flags:
|
|
||||||
# We've found a default value that references another param.
|
|
||||||
# See the notes in `hoistParamsUsedInDefault` for more details.
|
|
||||||
var hoistedParams = newNodeI(nkLetSection, call.info, 0)
|
|
||||||
for i in 1..<call.len:
|
|
||||||
let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i])
|
|
||||||
if hoisted != nil: call[i] = hoisted
|
|
||||||
result = newTree(nkStmtListExpr, hoistedParams, call)
|
|
||||||
result.typ = call.typ
|
|
||||||
of nkAddr, nkHiddenAddr:
|
of nkAddr, nkHiddenAddr:
|
||||||
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
|
||||||
of nkDerefExpr, nkHiddenDeref:
|
of nkDerefExpr, nkHiddenDeref:
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str
|
||||||
# we produce a fake include statement for every slurped filename, so that
|
# we produce a fake include statement for every slurped filename, so that
|
||||||
# the module dependencies are accurate:
|
# the module dependencies are accurate:
|
||||||
discard conf.fileInfoIdx(AbsoluteFile filename)
|
discard conf.fileInfoIdx(AbsoluteFile filename)
|
||||||
appendToModule(module, newNode(nkIncludeStmt, info, @[
|
appendToModule(module, newTreeI(nkIncludeStmt, info, newStrNode(nkStrLit, filename)))
|
||||||
newStrNode(nkStrLit, filename)]))
|
|
||||||
except IOError:
|
except IOError:
|
||||||
localError(conf, info, "cannot open file: " & file)
|
localError(conf, info, "cannot open file: " & file)
|
||||||
result = ""
|
result = ""
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,83 @@
|
||||||
discard """
|
discard """
|
||||||
output: '''
|
output: '''
|
||||||
hi
|
hi
|
||||||
|
hi
|
||||||
|
topLevel|topLevel|
|
||||||
|
topLevel2|topLevel2|
|
||||||
|
inProc|inProc|
|
||||||
|
inProc2|inProc2|
|
||||||
|
topLevel|9
|
||||||
|
topLevel2|10
|
||||||
|
inProc|7
|
||||||
|
inProc2|8
|
||||||
|
must have been the wind..
|
||||||
|
I'm there
|
||||||
|
must have been the wind..
|
||||||
|
I'm there
|
||||||
|
symbol'a'symbol'a'
|
||||||
|
symbol'b'symbol'b'
|
||||||
|
symbol'a'symbol'b'
|
||||||
|
symbol'a'9
|
||||||
|
symbol'b'9
|
||||||
|
symbol'a'0
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
import mdefaultprocparam
|
import mdefaultprocparam
|
||||||
|
|
||||||
p()
|
p()
|
||||||
|
|
||||||
|
proc testP =
|
||||||
|
p()
|
||||||
|
|
||||||
|
testP()
|
||||||
|
|
||||||
|
proc p2(s: string, count = s): string = s & count
|
||||||
|
|
||||||
|
proc testP2 =
|
||||||
|
echo p2 """inProc|"""
|
||||||
|
echo p2 """inProc2|"""
|
||||||
|
|
||||||
|
echo p2 """topLevel|"""
|
||||||
|
echo p2 """topLevel2|"""
|
||||||
|
|
||||||
|
testP2()
|
||||||
|
|
||||||
|
import macros
|
||||||
|
macro dTT(a: typed) = echo a.treeRepr
|
||||||
|
|
||||||
|
proc p3(s: string, count = len(s)): string = s & $count
|
||||||
|
|
||||||
|
proc testP3 =
|
||||||
|
echo p3 """inProc|"""
|
||||||
|
echo p3 """inProc2|"""
|
||||||
|
|
||||||
|
echo p3 """topLevel|"""
|
||||||
|
echo p3 """topLevel2|"""
|
||||||
|
|
||||||
|
testP3()
|
||||||
|
|
||||||
|
proc cut(s: string, c = len(s)): string =
|
||||||
|
s[0..<s.len-c]
|
||||||
|
|
||||||
|
echo "must have been the wind.." & cut "I'm gone"
|
||||||
|
echo cut("I'm gone", 4) & "there"
|
||||||
|
|
||||||
|
proc testCut =
|
||||||
|
echo "must have been the wind.." & cut "I'm gone"
|
||||||
|
echo cut("I'm gone", 4) & "there"
|
||||||
|
|
||||||
|
testCut()
|
||||||
|
|
||||||
|
var a = "symbol'a'"
|
||||||
|
var b = "symbol'b'"
|
||||||
|
|
||||||
|
block:
|
||||||
|
echo p2(a)
|
||||||
|
block:
|
||||||
|
echo p2(b)
|
||||||
|
block:
|
||||||
|
echo p2(a, b)
|
||||||
|
block:
|
||||||
|
echo p3(a)
|
||||||
|
echo p3(b)
|
||||||
|
echo p3(a, 0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue