1# 2# 3# The Nim Compiler 4# (c) Copyright 2015 Andreas Rumpf 5# 6# See the file "copying.txt", included in this 7# distribution, for details about the copyright. 8# 9 10## This module implements the '.liftLocals' pragma. 11 12import 13 strutils, options, ast, msgs, 14 idents, renderer, types, lowerings, lineinfos 15 16from pragmas import getPragmaVal 17from wordrecg import wLiftLocals 18 19type 20 Ctx = object 21 partialParam: PSym 22 objType: PType 23 cache: IdentCache 24 idgen: IdGenerator 25 26proc interestingVar(s: PSym): bool {.inline.} = 27 result = s.kind in {skVar, skLet, skTemp, skForVar, skResult} and 28 sfGlobal notin s.flags 29 30proc lookupOrAdd(c: var Ctx; s: PSym; info: TLineInfo): PNode = 31 let field = addUniqueField(c.objType, s, c.cache, c.idgen) 32 var deref = newNodeI(nkHiddenDeref, info) 33 deref.typ = c.objType 34 deref.add(newSymNode(c.partialParam, info)) 35 result = newNodeI(nkDotExpr, info) 36 result.add(deref) 37 result.add(newSymNode(field)) 38 result.typ = field.typ 39 40proc liftLocals(n: PNode; i: int; c: var Ctx) = 41 let it = n[i] 42 case it.kind 43 of nkSym: 44 if interestingVar(it.sym): 45 n[i] = lookupOrAdd(c, it.sym, it.info) 46 of procDefs, nkTypeSection, nkMixinStmt, nkBindStmt: discard 47 else: 48 for i in 0..<it.safeLen: 49 liftLocals(it, i, c) 50 51proc lookupParam(params, dest: PNode): PSym = 52 if dest.kind != nkIdent: return nil 53 for i in 1..<params.len: 54 if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id: 55 return params[i].sym 56 57proc liftLocalsIfRequested*(prc: PSym; n: PNode; cache: IdentCache; conf: ConfigRef; 58 idgen: IdGenerator): PNode = 59 let liftDest = getPragmaVal(prc.ast, wLiftLocals) 60 if liftDest == nil: return n 61 let partialParam = lookupParam(prc.typ.n, liftDest) 62 if partialParam.isNil: 63 localError(conf, liftDest.info, "'$1' is not a parameter of '$2'" % 64 [$liftDest, prc.name.s]) 65 return n 66 let objType = partialParam.typ.skipTypes(abstractPtrs) 67 if objType.kind != tyObject or tfPartial notin objType.flags: 68 localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest) 69 return n 70 var c = Ctx(partialParam: partialParam, objType: objType, cache: cache, idgen: idgen) 71 let w = newTree(nkStmtList, n) 72 liftLocals(w, 0, c) 73 result = w[0] 74