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