diff options
Diffstat (limited to 'compiler/transf.nim')
-rw-r--r-- | compiler/transf.nim | 330 |
1 files changed, 233 insertions, 97 deletions
diff --git a/compiler/transf.nim b/compiler/transf.nim index d2d9156aa..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; idgen: IdGenerator, 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,16 +49,17 @@ type # if we encounter the 2nd yield statement next: PTransCon # for stacking - TTransfContext = object + 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 idgen: IdGenerator - PTransf = ref TTransfContext proc newTransNode(a: PNode): PNode {.inline.} = result = shallowCopy(a) @@ -64,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 @@ -87,11 +94,11 @@ 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), nextSymId(c.idgen), 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: + 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) @@ -103,16 +110,18 @@ 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, c.idgen, 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, c.idgen, getCurrOwner(c)) @@ -126,6 +135,14 @@ 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: + 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) @@ -145,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: @@ -160,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: + 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, nextSymId(c.idgen)) + var newVar = copySym(v, c.idgen) incl(newVar.flags, sfFromGeneric) newVar.owner = owner result = newSymNode(newVar) @@ -175,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: @@ -199,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]) @@ -226,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, nextSymId(c.idgen), getCurrOwner(c), n.info) - result.name = getIdent(c.graph.cache, genPrefix) + 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: @@ -309,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: @@ -354,10 +381,11 @@ proc transformYield(c: PTransf, n: PNode): PNode = 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] @@ -366,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] @@ -379,45 +407,89 @@ 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] @@ -427,7 +499,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = 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] @@ -436,7 +508,15 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = elif n.typ.skipTypes(abstractInst).kind in {tyVar}: 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: @@ -448,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)) @@ -494,19 +574,22 @@ proc transformConv(c: PTransf, n: PNode): PNode = else: result = transformSons(c, n) of tyOpenArray, tyVarargs: - 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 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: @@ -551,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 @@ -569,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: @@ -583,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 @@ -596,7 +685,7 @@ proc findWrongOwners(c: PTransf, n: PNode) = else: for i in 0..<n.safeLen: findWrongOwners(c, n[i]) -proc isSimpleIteratorVar(c: PTransf; iter: PSym): bool = +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 @@ -608,9 +697,22 @@ proc isSimpleIteratorVar(c: PTransf; iter: PSym): bool = 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) @@ -654,7 +756,7 @@ proc transformFor(c: PTransf, n: PNode): PNode = 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): + 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) @@ -678,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: @@ -689,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, c.idgen, 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) @@ -761,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): @@ -799,10 +916,6 @@ proc transformCall(c: PTransf, n: PNode): PNode = 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 @@ -856,6 +969,9 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode = result = transformSons(c, n) 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, idgen, c, n[i]) @@ -885,6 +1001,15 @@ proc commonOptimizations*(g: ModuleGraph; idgen: IdGenerator; c: PSym, n: PNode) else: result = n +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: var oldDeferAnchor: PNode @@ -951,10 +1076,22 @@ proc transform(c: PTransf, n: PNode): PNode = of nkBreakStmt: result = transformBreak(c, n) of nkCallKinds: result = transformCall(c, n) - 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: @@ -981,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 @@ -1002,8 +1141,10 @@ 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: @@ -1021,7 +1162,7 @@ proc transform(c: PTransf, n: PNode): PNode = let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and n.typ != nil and n.typ.kind == tyPointer - if not exprIsPointerCast: + 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): @@ -1037,13 +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; idgen: IdGenerator): PTransf = - new(result) - result.contSyms = @[] - result.breakSyms = @[] - result.module = module - result.graph = g - result.idgen = idgen +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 @@ -1086,7 +1222,7 @@ template liftDefer(c, root) = if c.deferDetected: liftDeferAux(root) -proc transformBody*(g: ModuleGraph; idgen: IdGenerator; 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: @@ -1095,8 +1231,8 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool): result = getBody(g, prc) else: prc.transformedBody = newNode(nkEmpty) # protects from recursion - var c = openTransf(g, prc.getModule, "", idgen) - result = liftLambdas(g, prc, getBody(g, prc), c.tooEarly, c.idgen) + 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, c.idgen) @@ -1106,7 +1242,7 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool): 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 @@ -1117,21 +1253,21 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool): #if prc.name.s == "main": # echo "transformed into ", renderTree(result, {renderIds}) -proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; 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, "", idgen) + 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; idgen: IdGenerator; 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, "", idgen) + 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 |