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 common simple lowerings.
11
12const
13  genPrefix* = ":tmp"         # prefix for generated names
14
15import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
16  lineinfos
17
18proc newDeref*(n: PNode): PNode {.inline.} =
19  result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
20  result.add n
21
22proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
23  if tup.kind == nkHiddenAddr:
24    result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent}))
25    result.add newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent})[i])
26    result[0].add tup[0]
27    var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
28    lit.intVal = i
29    result[0].add lit
30  else:
31    result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
32                       abstractInst)[i])
33    result.add copyTree(tup)
34    var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
35    lit.intVal = i
36    result.add lit
37
38proc addVar*(father, v: PNode) =
39  var vpart = newNodeI(nkIdentDefs, v.info, 3)
40  vpart[0] = v
41  vpart[1] = newNodeI(nkEmpty, v.info)
42  vpart[2] = vpart[1]
43  father.add vpart
44
45proc addVar*(father, v, value: PNode) =
46  var vpart = newNodeI(nkIdentDefs, v.info, 3)
47  vpart[0] = v
48  vpart[1] = newNodeI(nkEmpty, v.info)
49  vpart[2] = value
50  father.add vpart
51
52proc newAsgnStmt*(le, ri: PNode): PNode =
53  result = newNodeI(nkAsgn, le.info, 2)
54  result[0] = le
55  result[1] = ri
56
57proc newFastAsgnStmt*(le, ri: PNode): PNode =
58  result = newNodeI(nkFastAsgn, le.info, 2)
59  result[0] = le
60  result[1] = ri
61
62proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
63  result = newNodeI(nkFastAsgn, le.info, 2)
64  result[0] = le
65  result[1] = newNodeIT(nkCall, ri.info, ri.typ)
66  result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove))
67  result[1].add ri
68
69proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
70  assert n.kind == nkVarTuple
71  let value = n.lastSon
72  result = newNodeI(nkStmtList, n.info)
73
74  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
75                    owner, value.info, g.config.options)
76  temp.typ = skipTypes(value.typ, abstractInst)
77  incl(temp.flags, sfFromGeneric)
78
79  var v = newNodeI(nkVarSection, value.info)
80  let tempAsNode = newSymNode(temp)
81  v.addVar(tempAsNode, value)
82  result.add(v)
83
84  for i in 0..<n.len-2:
85    let val = newTupleAccess(g, tempAsNode, i)
86    if n[i].kind == nkSym: v.addVar(n[i], val)
87    else: result.add newAsgnStmt(n[i], val)
88
89proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): PNode =
90  ## Turns (value) into (let tmp = value; tmp) so that 'value' can be re-used
91  ## freely, multiple times. This is frequently required and such a builtin would also be
92  ## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
93  result = newNodeIT(nkStmtListExpr, value.info, value.typ)
94  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
95                    owner, value.info, g.config.options)
96  temp.typ = skipTypes(value.typ, abstractInst)
97  incl(temp.flags, sfFromGeneric)
98
99  var v = newNodeI(nkLetSection, value.info)
100  let tempAsNode = newSymNode(temp)
101  v.addVar(tempAsNode)
102  result.add(v)
103  result.add newAsgnStmt(tempAsNode, value)
104  result.add tempAsNode
105
106proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
107  result = newNodeI(nkBracketExpr, tup.info)
108  result.add copyTree(tup)
109  var lit = newNodeI(nkIntLit, tup.info)
110  lit.intVal = i
111  result.add lit
112
113proc newTryFinally*(body, final: PNode): PNode =
114  result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
115
116proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
117  let value = n.lastSon
118  result = newNodeI(nkStmtList, n.info)
119
120  var temp = newSym(skTemp, getIdent(g.cache, "_"), nextSymId(idgen), owner, value.info, owner.options)
121  var v = newNodeI(nkLetSection, value.info)
122  let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
123
124  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
125  vpart[0] = tempAsNode
126  vpart[1] = newNodeI(nkEmpty, value.info)
127  vpart[2] = value
128  v.add vpart
129  result.add(v)
130
131  let lhs = n[0]
132  for i in 0..<lhs.len:
133    result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
134
135proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
136  result = newNodeI(nkStmtList, n.info)
137  # note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
138  var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextSymId(idgen), owner, n.info, owner.options)
139  temp.typ = n[1].typ
140  incl(temp.flags, sfFromGeneric)
141  incl(temp.flags, sfGenSym)
142
143  var v = newNodeI(nkVarSection, n.info)
144  let tempAsNode = newSymNode(temp)
145
146  var vpart = newNodeI(nkIdentDefs, v.info, 3)
147  vpart[0] = tempAsNode
148  vpart[1] = newNodeI(nkEmpty, v.info)
149  vpart[2] = n[1]
150  v.add vpart
151
152  result.add(v)
153  result.add newFastAsgnStmt(n[1], n[2])
154  result.add newFastAsgnStmt(n[2], tempAsNode)
155
156proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
157  result = newType(tyObject, nextTypeId(idgen), owner)
158  if final:
159    rawAddSon(result, nil)
160    incl result.flags, tfFinal
161  else:
162    rawAddSon(result, getCompilerProc(g, "RootObj").typ)
163  result.n = newNodeI(nkRecList, info)
164  let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
165                  nextSymId(idgen),
166                  owner, info, owner.options)
167  incl s.flags, sfAnon
168  s.typ = result
169  result.sym = s
170
171template fieldCheck {.dirty.} =
172  when false:
173    if tfCheckedForDestructor in obj.flags:
174      echo "missed field ", field.name.s
175      writeStackTrace()
176
177proc rawAddField*(obj: PType; field: PSym) =
178  assert field.kind == skField
179  field.position = obj.n.len
180  obj.n.add newSymNode(field)
181  propagateToOwner(obj, field.typ)
182  fieldCheck()
183
184proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
185  # returns a[].field as a node
186  assert field.kind == skField
187  var deref = newNodeI(nkHiddenDeref, info)
188  deref.typ = a.typ.skipTypes(abstractInst)[0]
189  deref.add a
190  result = newNodeI(nkDotExpr, info)
191  result.add deref
192  result.add newSymNode(field)
193  result.typ = field.typ
194
195proc rawDirectAccess*(obj, field: PSym): PNode =
196  # returns a.field as a node
197  assert field.kind == skField
198  result = newNodeI(nkDotExpr, field.info)
199  result.add newSymNode(obj)
200  result.add newSymNode(field)
201  result.typ = field.typ
202
203proc lookupInRecord(n: PNode, id: ItemId): PSym =
204  result = nil
205  case n.kind
206  of nkRecList:
207    for i in 0..<n.len:
208      result = lookupInRecord(n[i], id)
209      if result != nil: return
210  of nkRecCase:
211    if n[0].kind != nkSym: return
212    result = lookupInRecord(n[0], id)
213    if result != nil: return
214    for i in 1..<n.len:
215      case n[i].kind
216      of nkOfBranch, nkElse:
217        result = lookupInRecord(lastSon(n[i]), id)
218        if result != nil: return
219      else: discard
220  of nkSym:
221    if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
222  else: discard
223
224proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) =
225  # because of 'gensym' support, we have to mangle the name with its ID.
226  # This is hacky but the clean solution is much more complex than it looks.
227  var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
228                     nextSymId(idgen), s.owner, s.info, s.options)
229  field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
230  let t = skipIntLit(s.typ, idgen)
231  field.typ = t
232  assert t.kind != tyTyped
233  propagateToOwner(obj, t)
234  field.position = obj.n.len
235  field.flags = s.flags * {sfCursor}
236  obj.n.add newSymNode(field)
237  fieldCheck()
238
239proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
240  result = lookupInRecord(obj.n, s.itemId)
241  if result == nil:
242    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextSymId(idgen),
243                       s.owner, s.info, s.options)
244    field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
245    let t = skipIntLit(s.typ, idgen)
246    field.typ = t
247    assert t.kind != tyTyped
248    propagateToOwner(obj, t)
249    field.position = obj.n.len
250    obj.n.add newSymNode(field)
251    result = field
252
253proc newDotExpr*(obj, b: PSym): PNode =
254  result = newNodeI(nkDotExpr, obj.info)
255  let field = lookupInRecord(obj.typ.n, b.itemId)
256  assert field != nil, b.name.s
257  result.add newSymNode(obj)
258  result.add newSymNode(field)
259  result.typ = field.typ
260
261proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
262  # returns a[].b as a node
263  var deref = newNodeI(nkHiddenDeref, info)
264  deref.typ = a.typ.skipTypes(abstractInst)[0]
265  var t = deref.typ.skipTypes(abstractInst)
266  var field: PSym
267  while true:
268    assert t.kind == tyObject
269    field = lookupInRecord(t.n, b)
270    if field != nil: break
271    t = t[0]
272    if t == nil: break
273    t = t.skipTypes(skipPtrs)
274  #if field == nil:
275  #  echo "FIELD ", b
276  #  debug deref.typ
277  assert field != nil
278  deref.add a
279  result = newNodeI(nkDotExpr, info)
280  result.add deref
281  result.add newSymNode(field)
282  result.typ = field.typ
283
284proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
285  # returns a[].b as a node
286  var deref = newNodeI(nkHiddenDeref, info)
287  deref.typ = a.typ.skipTypes(abstractInst)[0]
288  var t = deref.typ.skipTypes(abstractInst)
289  var field: PSym
290  let bb = getIdent(cache, b)
291  while true:
292    assert t.kind == tyObject
293    field = getSymFromList(t.n, bb)
294    if field != nil: break
295    t = t[0]
296    if t == nil: break
297    t = t.skipTypes(skipPtrs)
298  #if field == nil:
299  #  echo "FIELD ", b
300  #  debug deref.typ
301  assert field != nil
302  deref.add a
303  result = newNodeI(nkDotExpr, info)
304  result.add deref
305  result.add newSymNode(field)
306  result.typ = field.typ
307
308proc getFieldFromObj*(t: PType; v: PSym): PSym =
309  assert v.kind != skField
310  var t = t
311  while true:
312    assert t.kind == tyObject
313    result = lookupInRecord(t.n, v.itemId)
314    if result != nil: break
315    t = t[0]
316    if t == nil: break
317    t = t.skipTypes(skipPtrs)
318
319proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
320  # returns a[].b as a node
321  result = indirectAccess(a, b.itemId, info)
322
323proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
324  result = indirectAccess(newSymNode(a), b, info)
325
326proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
327  result = newNodeI(nkAddr, n.info, 1)
328  result[0] = n
329  result.typ = newType(typeKind, nextTypeId(idgen), n.typ.owner)
330  result.typ.rawAddSon(n.typ)
331
332proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
333  result = newNodeIT(k, n.info,
334                     n.typ.skipTypes(abstractInst)[0])
335  result.add n
336
337proc callCodegenProc*(g: ModuleGraph; name: string;
338                      info: TLineInfo = unknownLineInfo;
339                      arg1, arg2, arg3, optionalArgs: PNode = nil): PNode =
340  result = newNodeI(nkCall, info)
341  let sym = magicsys.getCompilerProc(g, name)
342  if sym == nil:
343    localError(g.config, info, "system module needs: " & name)
344  else:
345    result.add newSymNode(sym)
346    if arg1 != nil: result.add arg1
347    if arg2 != nil: result.add arg2
348    if arg3 != nil: result.add arg3
349    if optionalArgs != nil:
350      for i in 1..<optionalArgs.len-2:
351        result.add optionalArgs[i]
352    result.typ = sym.typ[0]
353
354proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
355  result = nkIntLit.newIntNode(value)
356  result.typ = getSysType(g, info, tyInt)
357
358proc genHigh*(g: ModuleGraph; n: PNode): PNode =
359  if skipTypes(n.typ, abstractVar).kind == tyArray:
360    result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
361  else:
362    result = newNodeI(nkCall, n.info, 2)
363    result.typ = getSysType(g, n.info, tyInt)
364    result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
365    result[1] = n
366
367proc genLen*(g: ModuleGraph; n: PNode): PNode =
368  if skipTypes(n.typ, abstractVar).kind == tyArray:
369    result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
370  else:
371    result = newNodeI(nkCall, n.info, 2)
372    result.typ = getSysType(g, n.info, tyInt)
373    result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
374    result[1] = n
375
376