diff options
Diffstat (limited to 'compiler/transf.nim')
-rw-r--r-- | compiler/transf.nim | 463 |
1 files changed, 291 insertions, 172 deletions
diff --git a/compiler/transf.nim b/compiler/transf.nim index 10a2680ae..8dd24e090 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -18,20 +18,29 @@ # * performs lambda lifting for closure support # * transforms 'defer' into a 'try finally' statement +import std / tables + import options, ast, astalgo, trees, msgs, idents, renderer, types, semfold, magicsys, cgmeth, lowerings, liftlocals, modulegraphs, lineinfos -proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode +when defined(nimPreviewSlimSystem): + import std/assertions + +type + TransformFlag* = enum + useCache, keepOpenArrayConversions, force + TransformFlags* = set[TransformFlag] + +proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode import closureiters, lambdalifting type - PTransCon = ref TTransCon - TTransCon{.final.} = object # part of TContext; stackable - mapping: TIdNodeTable # mapping from symbols to nodes + PTransCon = ref object # part of TContext; stackable + mapping: Table[ItemId, PNode] # mapping from symbols to nodes owner: PSym # current owner forStmt: PNode # current for stmt forLoopBody: PNode # transformed for loop body @@ -40,15 +49,17 @@ type # if we encounter the 2nd yield statement next: PTransCon # for stacking - TTransfContext = object of TPassContext + PTransf = ref object module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) - nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool + isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) + inAddr: bool + flags: TransformFlags graph: ModuleGraph - PTransf = ref TTransfContext + idgen: IdGenerator proc newTransNode(a: PNode): PNode {.inline.} = result = shallowCopy(a) @@ -63,15 +74,12 @@ proc newTransNode(kind: TNodeKind, n: PNode, sons: int): PNode {.inline.} = var x = newNodeIT(kind, n.info, n.typ) newSeq(x.sons, sons) - x.typ = n.typ # x.flags = n.flags result = x proc newTransCon(owner: PSym): PTransCon = assert owner != nil - new(result) - initIdNodeTable(result.mapping) - result.owner = owner + result = PTransCon(mapping: initTable[ItemId, PNode](), owner: owner) proc pushTransCon(c: PTransf, t: PTransCon) = t.next = c.transCon @@ -86,12 +94,12 @@ proc getCurrOwner(c: PTransf): PSym = else: result = c.module proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = - let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), getCurrOwner(c), info) + let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), c.idgen, getCurrOwner(c), info) r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink}) incl(r.flags, sfFromGeneric) let owner = getCurrOwner(c) - if owner.isIterator and not c.tooEarly: - result = freshVarForClosureIter(c.graph, r, owner) + if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"): + result = freshVarForClosureIter(c.graph, r, c.idgen, owner) else: result = newSymNode(r) @@ -102,22 +110,24 @@ proc transformSons(c: PTransf, n: PNode): PNode = for i in 0..<n.len: result[i] = transform(c, n[i]) -proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode): PNode = +proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode = result = newTransNode(kind, ri.info, 2) result[0] = le + if isFirstWrite: + le.flags.incl nfFirstWrite result[1] = ri proc transformSymAux(c: PTransf, n: PNode): PNode = let s = n.sym if s.typ != nil and s.typ.callConv == ccClosure: if s.kind in routineKinds: - discard transformBody(c.graph, s, true) + discard transformBody(c.graph, c.idgen, s, {useCache}+c.flags) if s.kind == skIterator: if c.tooEarly: return n - else: return liftIterSym(c.graph, n, getCurrOwner(c)) + else: return liftIterSym(c.graph, n, c.idgen, getCurrOwner(c)) elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly: # top level .closure procs are still somewhat supported for 'Nake': - return makeClosure(c.graph, s, nil, n.info) + return makeClosure(c.graph, c.idgen, s, nil, n.info) #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure: # echo n.info, " come heer for ", c.tooEarly # if not c.tooEarly: @@ -125,7 +135,15 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = var tc = c.transCon if sfBorrow in s.flags and s.kind in routineKinds: # simply exchange the symbol: - b = s.getBody + var s = s + while true: + # Skips over all borrowed procs getting the last proc symbol without an implementation + let body = getBody(c.graph, s) + if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym: + s = body.sym + else: + break + b = getBody(c.graph, s) if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol") b = newSymNode(b.sym, n.info) elif c.inlining > 0: @@ -144,7 +162,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = else: b = n while tc != nil: - result = idNodeTableGet(tc.mapping, b.sym) + result = getOrDefault(tc.mapping, b.sym.itemId) if result != nil: # this slightly convoluted way ensures the line info stays correct: if result.kind == nkSym: @@ -159,10 +177,10 @@ proc transformSym(c: PTransf, n: PNode): PNode = proc freshVar(c: PTransf; v: PSym): PNode = let owner = getCurrOwner(c) - if owner.isIterator and not c.tooEarly: - result = freshVarForClosureIter(c.graph, v, owner) + if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"): + result = freshVarForClosureIter(c.graph, v, c.idgen, owner) else: - var newVar = copySym(v) + var newVar = copySym(v, c.idgen) incl(newVar.flags, sfFromGeneric) newVar.owner = owner result = newSymNode(newVar) @@ -174,10 +192,12 @@ proc transformVarSection(c: PTransf, v: PNode): PNode = if it.kind == nkCommentStmt: result[i] = it elif it.kind == nkIdentDefs: - if it[0].kind == nkSym: + var vn = it[0] + if vn.kind == nkPragmaExpr: vn = vn[0] + if vn.kind == nkSym: internalAssert(c.graph.config, it.len == 3) - let x = freshVar(c, it[0].sym) - idNodeTablePut(c.transCon.mapping, it[0].sym, x) + let x = freshVar(c, vn.sym) + c.transCon.mapping[vn.sym.itemId] = x var defs = newTransNode(nkIdentDefs, it.info, 3) if importantComments(c.graph.config): # keep documentation information: @@ -198,7 +218,7 @@ proc transformVarSection(c: PTransf, v: PNode): PNode = for j in 0..<it.len-2: if it[j].kind == nkSym: let x = freshVar(c, it[j].sym) - idNodeTablePut(c.transCon.mapping, it[j].sym, x) + c.transCon.mapping[it[j].sym.itemId] = x defs[j] = x else: defs[j] = transform(c, it[j]) @@ -225,21 +245,21 @@ proc transformConstSection(c: PTransf, v: PNode): PNode = proc hasContinue(n: PNode): bool = case n.kind - of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard + of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: result = false of nkContinueStmt: result = true else: + result = false for i in 0..<n.len: if hasContinue(n[i]): return true proc newLabel(c: PTransf, n: PNode): PSym = - result = newSym(skLabel, nil, getCurrOwner(c), n.info) - result.name = getIdent(c.graph.cache, genPrefix & $result.id) + result = newSym(skLabel, getIdent(c.graph.cache, genPrefix), c.idgen, getCurrOwner(c), n.info) proc transformBlock(c: PTransf, n: PNode): PNode = var labl: PSym if c.inlining > 0: labl = newLabel(c, n[0]) - idNodeTablePut(c.transCon.mapping, n[0].sym, newSymNode(labl)) + c.transCon.mapping[n[0].sym.itemId] = newSymNode(labl) else: labl = if n[0].kind != nkEmpty: @@ -308,6 +328,14 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode = if a.kind == nkSym: n[1] = transformSymAux(c, a) return n + of nkProcDef: # todo optimize nosideeffects? + result = newTransNode(n) + let x = newSymNode(copySym(n[namePos].sym, c.idgen)) + c.transCon.mapping[n[namePos].sym.itemId] = x + result[namePos] = x # we have to copy proc definitions for iters + for i in 1..<n.len: + result[i] = introduceNewLocalVars(c, n[i]) + result[namePos].sym.ast = result else: result = newTransNode(n) for i in 0..<n.len: @@ -350,13 +378,14 @@ proc transformYield(c: PTransf, n: PNode): PNode = # Choose the right assignment instruction according to the given ``lhs`` # node since it may not be a nkSym (a stack-allocated skForVar) but a # nkDotExpr (a heap-allocated slot into the envP block) - case lhs.kind: + case lhs.kind of nkSym: internalAssert c.graph.config, lhs.sym.kind == skForVar - result = newAsgnStmt(c, nkFastAsgn, lhs, rhs) + result = newAsgnStmt(c, nkFastAsgn, lhs, rhs, false) of nkDotExpr: - result = newAsgnStmt(c, nkAsgn, lhs, rhs) + result = newAsgnStmt(c, nkAsgn, lhs, rhs, false) else: + result = nil internalAssert c.graph.config, false result = newTransNode(nkStmtList, n.info, 0) var e = n[0] @@ -365,7 +394,7 @@ proc transformYield(c: PTransf, n: PNode): PNode = if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons if c.transCon.forStmt.len != 3: e = skipConv(e) - if e.kind in {nkPar, nkTupleConstr}: + if e.kind == nkTupleConstr: for i in 0..<e.len: var v = e[i] if v.kind == nkExprColonExpr: v = v[1] @@ -378,64 +407,116 @@ proc transformYield(c: PTransf, n: PNode): PNode = let lhs = c.transCon.forStmt[i] let rhs = transform(c, v) result.add(asgnTo(lhs, rhs)) + elif e.kind notin {nkAddr, nkHiddenAddr}: # no need to generate temp for address operation + # TODO do not use temp for nodes which cannot have side-effects + var tmp = newTemp(c, e.typ, e.info) + let v = newNodeI(nkVarSection, e.info) + v.addVar(tmp, e) + + result.add transform(c, v) + + for i in 0..<c.transCon.forStmt.len - 2: + if c.transCon.forStmt[i].kind == nkVarTuple: + for j in 0..<c.transCon.forStmt[i].len-1: + let lhs = c.transCon.forStmt[i][j] + let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j)) + result.add(asgnTo(lhs, rhs)) + else: + let lhs = c.transCon.forStmt[i] + let rhs = transform(c, newTupleAccess(c.graph, tmp, i)) + result.add(asgnTo(lhs, rhs)) else: - # Unpack the tuple into the loop variables - # XXX: BUG: what if `n` is an expression with side-effects? for i in 0..<c.transCon.forStmt.len - 2: let lhs = c.transCon.forStmt[i] let rhs = transform(c, newTupleAccess(c.graph, e, i)) result.add(asgnTo(lhs, rhs)) else: if c.transCon.forStmt[0].kind == nkVarTuple: - for i in 0..<c.transCon.forStmt[0].len-1: - let lhs = c.transCon.forStmt[0][i] - let rhs = transform(c, newTupleAccess(c.graph, e, i)) - result.add(asgnTo(lhs, rhs)) + var notLiteralTuple = false # we don't generate temp for tuples with const value: (1, 2, 3) + let ev = e.skipConv + if ev.kind == nkTupleConstr: + for i in ev: + if not isConstExpr(i): + notLiteralTuple = true + break + else: + notLiteralTuple = true + + if e.kind notin {nkAddr, nkHiddenAddr} and notLiteralTuple: + # TODO do not use temp for nodes which cannot have side-effects + var tmp = newTemp(c, e.typ, e.info) + let v = newNodeI(nkVarSection, e.info) + v.addVar(tmp, e) + + result.add transform(c, v) + for i in 0..<c.transCon.forStmt[0].len-1: + let lhs = c.transCon.forStmt[0][i] + let rhs = transform(c, newTupleAccess(c.graph, tmp, i)) + result.add(asgnTo(lhs, rhs)) + else: + for i in 0..<c.transCon.forStmt[0].len-1: + let lhs = c.transCon.forStmt[0][i] + let rhs = transform(c, newTupleAccess(c.graph, e, i)) + result.add(asgnTo(lhs, rhs)) else: let lhs = c.transCon.forStmt[0] let rhs = transform(c, e) result.add(asgnTo(lhs, rhs)) + + # bug #23536; note that the info of forLoopBody should't change + for idx in 0 ..< result.len: + var changeNode = result[idx] + changeNode.info = c.transCon.forStmt.info + for i, child in changeNode: + child.info = changeNode.info + inc(c.transCon.yieldStmts) if c.transCon.yieldStmts <= 1: # common case result.add(c.transCon.forLoopBody) else: # we need to introduce new local variables: + c.isIntroducingNewLocalVars = true # don't transform yields when introducing new local vars result.add(introduceNewLocalVars(c, c.transCon.forLoopBody)) - if result.len > 0: - var changeNode = result[0] - changeNode.info = c.transCon.forStmt.info - for i, child in changeNode: - child.info = changeNode.info + c.isIntroducingNewLocalVars = false -proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = +proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode = result = transformSons(c, n) - if c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags: return + # inlining of 'var openarray' iterators; bug #19977 + if n.typ.kind != tyOpenArray and (c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags): return var n = result case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: var m = n[0][0] - if m.kind == a or m.kind == b: + if m.kind in kinds: # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) n[0][0] = m[0] result = n[0] if n.typ.skipTypes(abstractVar).kind != tyOpenArray: result.typ = n.typ elif n.typ.skipTypes(abstractInst).kind in {tyVar}: - result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind) + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen) of nkHiddenStdConv, nkHiddenSubConv, nkConv: var m = n[0][1] - if m.kind == a or m.kind == b: + if m.kind in kinds: # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) n[0][1] = m[0] result = n[0] if n.typ.skipTypes(abstractVar).kind != tyOpenArray: result.typ = n.typ elif n.typ.skipTypes(abstractInst).kind in {tyVar}: - result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind) + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen) else: - if n[0].kind == a or n[0].kind == b: + if n[0].kind in kinds and + not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and + n[0][0].typ.skipTypes(abstractVar).kind == tyTuple + ) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and + n.typ.kind == tyVar and + n.typ.skipTypes(abstractVar).kind == tyOpenArray and + n[0][0].typ.skipTypes(abstractVar).kind == tyString) + : # elimination is harmful to `for tuple unpack` because of newTupleAccess + # it is also harmful to openArrayLoc (var openArray) for strings # addr ( deref ( x )) --> x result = n[0][0] if n.typ.skipTypes(abstractVar).kind != tyOpenArray: @@ -447,7 +528,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode = # we cannot generate a proper thunk here for GC-safety reasons # (see internal documentation): - if c.graph.config.backend == backendJs: return prc + if jsNoLambdaLifting in c.graph.config.legacyFeatures and c.graph.config.backend == backendJs: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenSubConv, prc.info, dest) conv.add(newNodeI(nkEmpty, prc.info)) @@ -493,18 +574,22 @@ proc transformConv(c: PTransf, n: PNode): PNode = else: result = transformSons(c, n) of tyOpenArray, tyVarargs: - result = transform(c, n[1]) - result.typ = takeType(n.typ, n[1].typ) - #echo n.info, " came here and produced ", typeToString(result.typ), - # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) - of tyCString: + if keepOpenArrayConversions in c.flags: + result = transformSons(c, n) + else: + result = transform(c, n[1]) + #result = transformSons(c, n) + result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen) + #echo n.info, " came here and produced ", typeToString(result.typ), + # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) + of tyCstring: if source.kind == tyString: result = newTransNode(nkStringToCString, n, 1) result[0] = transform(c, n[1]) else: result = transformSons(c, n) of tyString: - if source.kind == tyCString: + if source.kind == tyCstring: result = newTransNode(nkCStringToString, n, 1) result[0] = transform(c, n[1]) else: @@ -522,6 +607,7 @@ proc transformConv(c: PTransf, n: PNode): PNode = result[0] = transform(c, n[1]) else: result = transform(c, n[1]) + result.typ = n.typ else: result = transformSons(c, n) of tyObject: @@ -534,12 +620,13 @@ proc transformConv(c: PTransf, n: PNode): PNode = result[0] = transform(c, n[1]) else: result = transform(c, n[1]) + result.typ = n.typ of tyGenericParam, tyOrdinal: result = transform(c, n[1]) # happens sometimes for generated assignments, etc. of tyProc: result = transformSons(c, n) - if dest.callConv == ccClosure and source.callConv == ccDefault: + if dest.callConv == ccClosure and source.callConv == ccNimCall: result = generateThunk(c, result[1], dest) else: result = transformSons(c, n) @@ -547,7 +634,7 @@ proc transformConv(c: PTransf, n: PNode): PNode = type TPutArgInto = enum paDirectMapping, paFastAsgn, paFastAsgnTakeTypeFromArg - paVarAsgn, paComplexOpenarray + paVarAsgn, paComplexOpenarray, paViaIndirection proc putArgInto(arg: PNode, formal: PType): TPutArgInto = # This analyses how to treat the mapping "formal <-> arg" in an @@ -565,8 +652,11 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = case arg.kind of nkEmpty..nkNilLit: result = paDirectMapping - of nkDotExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr: + of nkDotExpr, nkDerefExpr, nkHiddenDeref: result = putArgInto(arg[0], formal) + of nkAddr, nkHiddenAddr: + result = putArgInto(arg[0], formal) + if result == paViaIndirection: result = paFastAsgn of nkCurly, nkBracket: for i in 0..<arg.len: if putArgInto(arg[i], formal) != paDirectMapping: @@ -579,6 +669,9 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = if putArgInto(a, formal) != paDirectMapping: return paFastAsgn result = paDirectMapping + of nkBracketExpr: + if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn + else: result = paViaIndirection else: if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn else: result = paFastAsgn @@ -592,6 +685,37 @@ proc findWrongOwners(c: PTransf, n: PNode) = else: for i in 0..<n.safeLen: findWrongOwners(c, n[i]) +proc isSimpleIteratorVar(c: PTransf; iter: PSym; call: PNode; owner: PSym): bool = + proc rec(n: PNode; owner: PSym; dangerousYields: var int) = + case n.kind + of nkEmpty..nkNilLit: discard + of nkYieldStmt: + if n[0].kind == nkSym and n[0].sym.owner == owner: + discard "good: yield a single variable that we own" + else: + inc dangerousYields + else: + for c in n: rec(c, owner, dangerousYields) + + proc recSym(n: PNode; owner: PSym; sameOwner: var bool) = + case n.kind + of {nkEmpty..nkNilLit} - {nkSym}: discard + of nkSym: + if n.sym.owner != owner: + sameOwner = false + else: + for c in n: recSym(c, owner, sameOwner) + + var dangerousYields = 0 + rec(getBody(c.graph, iter), iter, dangerousYields) + result = dangerousYields == 0 + # the parameters should be owned by the owner + # bug #22237 + for i in 1..<call.len: + recSym(call[i], owner, result) + +template destructor(t: PType): PSym = getAttachedOp(c.graph, t, attachedDestructor) + proc transformFor(c: PTransf, n: PNode): PNode = # generate access statements for the parameters (unless they are constant) # put mapping from formal parameters to actual parameters @@ -612,7 +736,7 @@ proc transformFor(c: PTransf, n: PNode): PNode = result[1] = n result[1][^1] = transformLoopBody(c, n[^1]) result[1][^2] = transform(c, n[^2]) - result[1] = lambdalifting.liftForLoop(c.graph, result[1], getCurrOwner(c)) + result[1] = lambdalifting.liftForLoop(c.graph, result[1], c.idgen, getCurrOwner(c)) discard c.breakSyms.pop return result @@ -624,18 +748,22 @@ proc transformFor(c: PTransf, n: PNode): PNode = discard c.breakSyms.pop + let iter = call[0].sym + var v = newNodeI(nkVarSection, n.info) for i in 0..<n.len - 2: if n[i].kind == nkVarTuple: for j in 0..<n[i].len-1: addVar(v, copyTree(n[i][j])) # declare new vars else: + if n[i].kind == nkSym and isSimpleIteratorVar(c, iter, call, n[i].sym.owner): + incl n[i].sym.flags, sfCursor addVar(v, copyTree(n[i])) # declare new vars stmtList.add(v) + # Bugfix: inlined locals belong to the invoking routine, not to the invoked # iterator! - let iter = call[0].sym var newC = newTransCon(getCurrOwner(c)) newC.forStmt = n newC.forLoopBody = loopBody @@ -652,7 +780,7 @@ proc transformFor(c: PTransf, n: PNode): PNode = let pa = putArgInto(arg, formal.typ) case pa of paDirectMapping: - idNodeTablePut(newC.mapping, formal, arg) + newC.mapping[formal.itemId] = arg of paFastAsgn, paFastAsgnTakeTypeFromArg: var t = formal.typ if pa == paFastAsgnTakeTypeFromArg: @@ -663,25 +791,36 @@ proc transformFor(c: PTransf, n: PNode): PNode = t = arg.typ # generate a temporary and produce an assignment statement: var temp = newTemp(c, t, formal.info) + #incl(temp.sym.flags, sfCursor) addVar(v, temp) - stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg)) - idNodeTablePut(newC.mapping, formal, temp) + stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true)) + newC.mapping[formal.itemId] = temp of paVarAsgn: - assert(skipTypes(formal.typ, abstractInst).kind in {tyVar}) - idNodeTablePut(newC.mapping, formal, arg) + assert(skipTypes(formal.typ, abstractInst).kind in {tyVar, tyLent}) + newC.mapping[formal.itemId] = arg # XXX BUG still not correct if the arg has a side effect! + of paViaIndirection: + let t = formal.typ + let vt = makeVarType(t.owner, t, c.idgen) + vt.flags.incl tfVarIsPtr + var temp = newTemp(c, vt, formal.info) + addVar(v, temp) + var addrExp = newNodeIT(nkHiddenAddr, formal.info, makeVarType(t.owner, t, c.idgen, tyPtr)) + addrExp.add(arg) + stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, addrExp, true)) + newC.mapping[formal.itemId] = newDeref(temp) of paComplexOpenarray: # arrays will deep copy here (pretty bad). var temp = newTemp(c, arg.typ, formal.info) addVar(v, temp) - stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg)) - idNodeTablePut(newC.mapping, formal, temp) + stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true)) + newC.mapping[formal.itemId] = temp - let body = transformBody(c.graph, iter, true) + let body = transformBody(c.graph, c.idgen, iter, {useCache}+c.flags) pushInfoContext(c.graph.config, n.info) inc(c.inlining) stmtList.add(transform(c, body)) - #findWrongOwners(c, stmtList.pnode) + #findWrongOwners(c, stmtList.PNode) dec(c.inlining) popInfoContext(c.graph.config) popTransCon(c) @@ -735,9 +874,13 @@ proc getMergeOp(n: PNode): PSym = nkCallStrLit: if n[0].kind == nkSym and n[0].sym.magic == mConStrStr: result = n[0].sym - else: discard + else: + result = nil + else: result = nil proc flattenTreeAux(d, a: PNode, op: PSym) = + ## Optimizes away the `&` calls in the children nodes and + ## lifts the leaf nodes to the same level as `op2`. let op2 = getMergeOp(a) if op2 != nil and (op2.id == op.id or op.magic != mNone and op2.magic == op.magic): @@ -769,14 +912,10 @@ proc transformCall(c: PTransf, n: PNode): PNode = while (j < n.len): let b = transform(c, n[j]) if not isConstExpr(b): break - a = evalOp(op.magic, n, a, b, nil, c.graph) + a = evalOp(op.magic, n, a, b, nil, c.idgen, c.graph) inc(j) result.add(a) if result.len == 2: result = result[1] - elif magic == mAddr: - result = newTransNode(nkAddr, n, 1) - result[0] = n[1] - result = transformAddrDeref(c, result, nkDerefExpr, nkHiddenDeref) elif magic in {mNBindSym, mTypeOf, mRunnableExamples}: # for bindSym(myconst) we MUST NOT perform constant folding: result = n @@ -809,7 +948,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode = let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2) convNode[0] = newNodeI(nkEmpty, n.info) convNode[1] = excCall - convNode.typ = excTypeNode.typ.toRef() + convNode.typ = excTypeNode.typ.toRef(c.idgen) # -> let exc = ... let identDefs = newTransNode(nkIdentDefs, n[1].info, 3) identDefs[0] = n[0][2] @@ -829,17 +968,13 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode = else: result = transformSons(c, n) -proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = - # symbols that expand to a complex constant (array, etc.) should not be - # inlined, unless it's the empty array: - result = orig.kind == nkSym and - cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and - cnst.len != 0 - -proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode = +proc commonOptimizations*(g: ModuleGraph; idgen: IdGenerator; c: PSym, n: PNode): PNode = + ## Merges adjacent constant expressions of the children of the `&` call into + ## a single constant expression. It also inlines constant expressions which are not + ## complex. result = n for i in 0..<n.safeLen: - result[i] = commonOptimizations(g, c, n[i]) + result[i] = commonOptimizations(g, idgen, c, n[i]) var op = getMergeOp(n) if (op != nil) and (op.magic != mNone) and (n.len >= 3): result = newNodeIT(nkCall, n.info, n.typ) @@ -854,54 +989,26 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode = while j < args.len: let b = args[j] if not isConstExpr(b): break - a = evalOp(op.magic, result, a, b, nil, g) + a = evalOp(op.magic, result, a, b, nil, idgen, g) inc(j) result.add(a) if result.len == 2: result = result[1] else: - var cnst = getConstExpr(c, n, g) + var cnst = getConstExpr(c, n, idgen, g) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): result = cnst else: 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 transformDerefBlock(c: PTransf, n: PNode): PNode = + # We transform (block: x)[] to (block: x[]) + let e0 = n[0] + result = shallowCopy(e0) + result.typ = n.typ + for i in 0 ..< e0.len - 1: + result[i] = e0[i] + result[e0.len-1] = newTreeIT(nkHiddenDeref, n.info, n.typ, e0[e0.len-1]) proc transform(c: PTransf, n: PNode): PNode = when false: @@ -969,20 +1076,22 @@ proc transform(c: PTransf, n: PNode): PNode = of nkBreakStmt: result = transformBreak(c, n) of nkCallKinds: 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: - result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref) - of nkDerefExpr, nkHiddenDeref: - result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr) + of nkHiddenAddr: + result = transformAddrDeref(c, n, {nkHiddenDeref}) + of nkAddr: + let oldInAddr = c.inAddr + c.inAddr = true + result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref}) + c.inAddr = oldInAddr + of nkDerefExpr: + result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr}) + of nkHiddenDeref: + if n[0].kind in {nkBlockExpr, nkBlockStmt}: + # bug #20107 bug #21540. Watch out to not deref the pointer too late. + let e = transformDerefBlock(c, n) + result = transformBlock(c, e) + else: + result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr}) of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = transformConv(c, n) of nkDiscardStmt: @@ -999,7 +1108,7 @@ proc transform(c: PTransf, n: PNode): PNode = of nkConstSection: # do not replace ``const c = 3`` with ``const 3 = 3`` return transformConstSection(c, n) - of nkTypeSection, nkTypeOfExpr: + of nkTypeSection, nkTypeOfExpr, nkMixinStmt, nkBindStmt: # no need to transform type sections: return n of nkVarSection, nkLetSection: @@ -1009,18 +1118,20 @@ proc transform(c: PTransf, n: PNode): PNode = else: result = transformSons(c, n) of nkYieldStmt: - if c.inlining > 0: + if c.inlining > 0 and not c.isIntroducingNewLocalVars: result = transformYield(c, n) else: result = transformSons(c, n) of nkAsgn: result = transformAsgn(c, n) of nkIdentDefs, nkConstDef: - result = n - result[0] = transform(c, n[0]) + result = newTransNode(n) + result[0] = transform(c, skipPragmaExpr(n[0])) # Skip the second son since it only contains an unsemanticized copy of the # variable type used by docgen - result[2] = transform(c, n[2]) + let last = n.len-1 + for i in 1..<last: result[i] = n[i] + result[last] = transform(c, n[last]) # XXX comment handling really sucks: if importantComments(c.graph.config): result.comment = n.comment @@ -1030,10 +1141,17 @@ proc transform(c: PTransf, n: PNode): PNode = # (bug #2604). We need to patch this environment here too: let a = n[1] if a.kind == nkSym: - n[1] = transformSymAux(c, a) - return n + result = copyTree(n) + result[1] = transformSymAux(c, a) + else: + result = n of nkExceptBranch: result = transformExceptBranch(c, n) + of nkCheckedFieldExpr: + result = transformSons(c, n) + if result[0].kind != nkDotExpr: + # simplfied beyond a dot expression --> simplify further. + result = result[0] else: result = transformSons(c, n) when false: @@ -1042,9 +1160,10 @@ proc transform(c: PTransf, n: PNode): PNode = # Constants can be inlined here, but only if they cannot result in a cast # in the back-end (e.g. var p: pointer = someProc) let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and + n.typ != nil and n.typ.kind == tyPointer - if not exprIsPointerCast: - var cnst = getConstExpr(c.module, result, c.graph) + if not exprIsPointerCast and not c.inAddr: + var cnst = getConstExpr(c.module, result, c.idgen, c.graph) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): result = cnst # do not miss an optimization @@ -1059,12 +1178,8 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = popTransCon(c) incl(result.flags, nfTransf) -proc openTransf(g: ModuleGraph; module: PSym, filename: string): PTransf = - new(result) - result.contSyms = @[] - result.breakSyms = @[] - result.module = module - result.graph = g +proc openTransf(g: ModuleGraph; module: PSym, filename: string; idgen: IdGenerator; flags: TransformFlags): PTransf = + result = PTransf(module: module, graph: g, idgen: idgen, flags: flags) proc flattenStmts(n: PNode) = var goOn = true @@ -1089,15 +1204,15 @@ proc liftDeferAux(n: PNode) = if n[i].kind == nkDefer: let deferPart = newNodeI(nkFinally, n[i].info) deferPart.add n[i][0] - var tryStmt = newNodeI(nkTryStmt, n[i].info) - var body = newNodeI(n.kind, n[i].info) + var tryStmt = newNodeIT(nkTryStmt, n[i].info, n.typ) + var body = newNodeIT(n.kind, n[i].info, n.typ) if i < last: body.sons = n.sons[(i+1)..last] tryStmt.add body tryStmt.add deferPart n[i] = tryStmt n.sons.setLen(i+1) - n.typ = n[i].typ + n.typ = tryStmt.typ goOn = true break for i in 0..n.safeLen-1: @@ -1107,48 +1222,52 @@ template liftDefer(c, root) = if c.deferDetected: liftDeferAux(root) -proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode = +proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode = assert prc.kind in routineKinds if prc.transformedBody != nil: result = prc.transformedBody - elif nfTransf in prc.ast[bodyPos].flags or prc.kind in {skTemplate}: - result = prc.ast[bodyPos] + elif nfTransf in getBody(g, prc).flags or prc.kind in {skTemplate}: + result = getBody(g, prc) else: prc.transformedBody = newNode(nkEmpty) # protects from recursion - var c = openTransf(g, prc.getModule, "") - result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly) + var c = openTransf(g, prc.getModule, "", idgen, flags) + result = liftLambdas(g, prc, getBody(g, prc), c.tooEarly, c.idgen, flags) result = processTransf(c, result, prc) liftDefer(c, result) - result = liftLocalsIfRequested(prc, result, g.cache, g.config) + result = liftLocalsIfRequested(prc, result, g.cache, g.config, c.idgen) if prc.isIterator: - result = g.transformClosureIterator(prc, result) + result = g.transformClosureIterator(c.idgen, prc, result) incl(result.flags, nfTransf) - if cache or prc.typ.callConv == ccInline: + if useCache in flags or prc.typ.callConv == ccInline: # genProc for inline procs will be called multiple times from different modules, # it is important to transform exactly once to get sym ids and locations right prc.transformedBody = result else: prc.transformedBody = nil + # XXX Rodfile support for transformedBody! + + #if prc.name.s == "main": + # echo "transformed into ", renderTree(result, {renderIds}) -proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = +proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode = if nfTransf in n.flags: result = n else: - var c = openTransf(g, module, "") + var c = openTransf(g, module, "", idgen, flags) result = processTransf(c, n, module) liftDefer(c, result) #result = liftLambdasForTopLevel(module, result) incl(result.flags, nfTransf) -proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = +proc transformExpr*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode = if nfTransf in n.flags: result = n else: - var c = openTransf(g, module, "") + var c = openTransf(g, module, "", idgen, flags) result = processTransf(c, n, module) liftDefer(c, result) # expressions are not to be injected with destructor calls as that |