diff options
Diffstat (limited to 'compiler/transf.nim')
-rw-r--r-- | compiler/transf.nim | 156 |
1 files changed, 95 insertions, 61 deletions
diff --git a/compiler/transf.nim b/compiler/transf.nim index 92d740276..8dd24e090 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -18,6 +18,8 @@ # * 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, @@ -28,16 +30,17 @@ when defined(nimPreviewSlimSystem): import std/assertions type - TransformBodyFlag* = enum - dontUseCache, useCache + TransformFlag* = enum + useCache, keepOpenArrayConversions, force + TransformFlags* = set[TransformFlag] -proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, flag: TransformBodyFlag, force = false): PNode +proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode import closureiters, lambdalifting type PTransCon = ref object # part of TContext; stackable - mapping: TIdNodeTable # mapping from symbols to nodes + mapping: Table[ItemId, PNode] # mapping from symbols to nodes owner: PSym # current owner forStmt: PNode # current for stmt forLoopBody: PNode # transformed for loop body @@ -50,9 +53,11 @@ type module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) - isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) 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 @@ -74,9 +79,7 @@ proc newTransNode(kind: TNodeKind, n: PNode, 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 @@ -95,7 +98,7 @@ proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = 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) @@ -118,7 +121,7 @@ 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, useCache) + 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)) @@ -159,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: @@ -174,7 +177,7 @@ 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, c.idgen) @@ -194,7 +197,7 @@ proc transformVarSection(c: PTransf, v: PNode): PNode = if vn.kind == nkSym: internalAssert(c.graph.config, it.len == 3) let x = freshVar(c, vn.sym) - idNodeTablePut(c.transCon.mapping, vn.sym, x) + c.transCon.mapping[vn.sym.itemId] = x var defs = newTransNode(nkIdentDefs, it.info, 3) if importantComments(c.graph.config): # keep documentation information: @@ -215,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]) @@ -256,7 +259,7 @@ 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: @@ -328,7 +331,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode = of nkProcDef: # todo optimize nosideeffects? result = newTransNode(n) let x = newSymNode(copySym(n[namePos].sym, c.idgen)) - idNodeTablePut(c.transCon.mapping, n[namePos].sym, x) + 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]) @@ -413,9 +416,15 @@ proc transformYield(c: PTransf, n: PNode): PNode = result.add transform(c, v) for i in 0..<c.transCon.forStmt.len - 2: - let lhs = c.transCon.forStmt[i] - let rhs = transform(c, newTupleAccess(c.graph, tmp, i)) - result.add(asgnTo(lhs, rhs)) + 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: for i in 0..<c.transCon.forStmt.len - 2: let lhs = c.transCon.forStmt[i] @@ -454,6 +463,14 @@ proc transformYield(c: PTransf, n: PNode): PNode = 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 @@ -464,15 +481,10 @@ proc transformYield(c: PTransf, n: PNode): PNode = result.add(introduceNewLocalVars(c, c.transCon.forLoopBody)) c.isIntroducingNewLocalVars = false - 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 - 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: @@ -496,7 +508,15 @@ proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): 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 in kinds: + 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: @@ -508,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)) @@ -554,11 +574,14 @@ 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) + 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) @@ -611,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 @@ -629,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: @@ -643,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 @@ -751,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: @@ -762,21 +791,32 @@ 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, true)) - idNodeTablePut(newC.mapping, formal, temp) + 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, true)) - idNodeTablePut(newC.mapping, formal, temp) + newC.mapping[formal.itemId] = temp - let body = transformBody(c.graph, c.idgen, iter, useCache) + 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)) @@ -876,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 @@ -1043,7 +1079,10 @@ proc transform(c: PTransf, n: PNode): PNode = 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: @@ -1123,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): @@ -1139,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 @@ -1188,7 +1222,7 @@ template liftDefer(c, root) = if c.deferDetected: liftDeferAux(root) -proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flag: TransformBodyFlag, force = false): PNode = +proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode = assert prc.kind in routineKinds if prc.transformedBody != nil: @@ -1197,8 +1231,8 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flag: Transfo 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, force) + 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) @@ -1208,7 +1242,7 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flag: Transfo incl(result.flags, nfTransf) - if flag == useCache 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 @@ -1219,21 +1253,21 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flag: Transfo #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 |