about summary refs log tree commit diff stats
path: root/html/tangle.mu.html
Commit message (Expand)AuthorAgeFilesLines
* 4539Kartik Agaram2018-09-071-4/+4
* 4447Kartik Agaram2018-07-271-2/+2
* 4239Kartik Agaram2018-05-081-4/+4
* 4200Kartik K. Agaram2018-01-271-7/+7
* 4199Kartik K. Agaram2018-01-251-16/+15
* 4165Kartik K. Agaram2017-12-271-1/+1
* 4161Kartik K. Agaram2017-12-151-5/+5
* 4134 - 'input' = 'ingredient'Kartik K. Agaram2017-12-031-1/+1
* 4003Kartik K. Agaram2017-09-231-28/+27
* 3764 - better colors for cross-linksKartik K. Agaram2017-03-081-3/+4
* 3761Kartik K. Agaram2017-03-071-3/+4
* 3725Kartik K. Agaram2016-12-271-8/+8
* 3716Kartik K. Agaram2016-12-261-0/+2
* 3710Kartik K. Agaram2016-12-261-37/+37
* 3709 - line numbers in htmlKartik K. Agaram2016-12-261-40/+64
* 3569Kartik K. Agaram2016-10-231-7/+7
* 3491Kartik K. Agaram2016-10-091-1/+1
* 3431Kartik K. Agaram2016-09-301-2/+2
* 3395Kartik K. Agaram2016-09-171-5/+5
* 3315Kartik K. Agaram2016-09-101-2/+3
* 2812Kartik K. Agaram2016-03-271-9/+18
* 2746Kartik K. Agaram2016-03-091-1/+1
* 2745Kartik K. Agaram2016-03-091-1/+1
* 2744Kartik K. Agaram2016-03-091-2/+2
* 2743Kartik K. Agaram2016-03-091-20/+11
* 2430 - make room for more transformsKartik K. Agaram2015-11-131-4/+3
* 2423 - describe shape-shifting in html docsKartik K. Agaram2015-11-101-5/+5
* 2177Kartik K. Agaram2015-09-071-22/+24
* 2175Kartik K. Agaram2015-09-061-24/+22
* 2062Kartik K. Agaram2015-08-231-2/+2
* 1949Kartik K. Agaram2015-08-061-1/+1
* 1925Kartik K. Agaram2015-08-031-2/+2
* 1885Kartik K. Agaram2015-07-291-10/+10
* 1853Kartik K. Agaram2015-07-251-1/+1
* 1818Kartik K. Agaram2015-07-181-1/+1
* 1778Kartik K. Agaram2015-07-131-2/+2
* 1631 - update html versionsKartik K. Agaram2015-06-231-5/+5
* 1556Kartik K. Agaram2015-06-121-11/+11
* 1549Kartik K. Agaram2015-06-091-2/+2
* 1517Kartik K. Agaram2015-05-301-3/+3
* 1471Kartik K. Agaram2015-05-261-1/+1
* 1459Kartik K. Agaram2015-05-251-0/+73
b301a8483e463772b785b0aee79cf2a68c'>^
e25474154 ^

































































3b7ef2288 ^

e25474154 ^



































ade67f1ab ^
e25474154 ^





























































3b7ef2288 ^


e25474154 ^








3b7ef2288 ^



e25474154 ^













e25474154 ^



































































5635fde06 ^





e25474154 ^























































3b7ef2288 ^

e25474154 ^





























































































5b28d0820 ^
e25474154 ^





























































5b28d0820 ^
e25474154 ^





















5b28d0820 ^
e25474154 ^














































































































































6deda5a97 ^



e25474154 ^


















































2f066395b ^
e25474154 ^





























2f066395b ^
e25474154 ^























93b3c03db ^
e25474154 ^


93b3c03db ^
e25474154 ^








93b3c03db ^







e25474154 ^





























cb21fd4f8 ^

e25474154 ^




































cb21fd4f8 ^
e25474154 ^











5b28d0820 ^
e25474154 ^




































6deda5a97 ^
e25474154 ^
6deda5a97 ^
e25474154 ^



6deda5a97 ^
e25474154 ^


6deda5a97 ^
e25474154 ^





6deda5a97 ^
e25474154 ^




6deda5a97 ^


e25474154 ^
6deda5a97 ^




e25474154 ^
6deda5a97 ^



























e25474154 ^






6deda5a97 ^

e25474154 ^
6deda5a97 ^
36accda8a ^


6deda5a97 ^
e25474154 ^


6deda5a97 ^
e25474154 ^



6deda5a97 ^





e25474154 ^





6deda5a97 ^


e25474154 ^



6deda5a97 ^
e25474154 ^





6deda5a97 ^
e25474154 ^
6deda5a97 ^

e25474154 ^
6deda5a97 ^
e25474154 ^
6deda5a97 ^
e25474154 ^

6deda5a97 ^
e25474154 ^













6deda5a97 ^
5b28d0820 ^
93b3c03db ^

6deda5a97 ^

e25474154 ^


























































































5b28d0820 ^
e25474154 ^
































































5b28d0820 ^
e25474154 ^

























5b28d0820 ^
e25474154 ^
5b28d0820 ^
e25474154 ^




















5131b3cea ^

e25474154 ^
5131b3cea ^
e25474154 ^







5131b3cea ^

e25474154 ^
5131b3cea ^

064417fc5 ^
e25474154 ^






















e25474154 ^
5b28d0820 ^
e25474154 ^











93b3c03db ^
e25474154 ^











5b28d0820 ^
e25474154 ^











5b28d0820 ^
e25474154 ^




















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#
#
#           The Nim Compiler
#        (c) Copyright 2013 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# included from cgen.nim

# -------------------------- constant expressions ------------------------

proc int64Literal(i: BiggestInt): Rope =
  if i > low(int64):
    result = rfmt(nil, "IL64($1)", rope(i))
  else:
    result = ~"(IL64(-9223372036854775807) - IL64(1))"

proc uint64Literal(i: uint64): Rope = rope($i & "ULL")

proc intLiteral(i: BiggestInt): Rope =
  if i > low(int32) and i <= high(int32):
    result = rope(i)
  elif i == low(int32):
    # Nim has the same bug for the same reasons :-)
    result = ~"(-2147483647 -1)"
  elif i > low(int64):
    result = rfmt(nil, "IL64($1)", rope(i))
  else:
    result = ~"(IL64(-9223372036854775807) - IL64(1))"

proc getStrLit(m: BModule, s: string): Rope =
  discard cgsym(m, "TGenericSeq")
  result = getTempName(m)
  addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n",
       [result, makeCString(s), rope(len(s))])

proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
  if ty == nil: internalError(n.info, "genLiteral: ty is nil")
  case n.kind
  of nkCharLit..nkUInt64Lit:
    case skipTypes(ty, abstractVarRange).kind
    of tyChar, tyNil:
      result = intLiteral(n.intVal)
    of tyBool:
      if n.intVal != 0: result = ~"NIM_TRUE"
      else: result = ~"NIM_FALSE"
    of tyInt64: result = int64Literal(n.intVal)
    of tyUInt64: result = uint64Literal(uint64(n.intVal))
    else:
      result = "(($1) $2)" % [getTypeDesc(p.module,
          ty), intLiteral(n.intVal)]
  of nkNilLit:
    let t = skipTypes(ty, abstractVarRange)
    if t.kind == tyProc and t.callConv == ccClosure:
      let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
      result = p.module.tmpBase & rope(id)
      if id == p.module.labels:
        # not found in cache:
        inc(p.module.labels)
        addf(p.module.s[cfsData],
             "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n",
             [getTypeDesc(p.module, ty), result])
    else:
      result = rope("NIM_NIL")
  of nkStrLit..nkTripleStrLit:
    if n.strVal.isNil:
      result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", [])
    elif skipTypes(ty, abstractVarRange).kind == tyString:
      let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
      if id == p.module.labels:
        # string literal not found in the cache:
        result = ropecg(p.module, "((#NimStringDesc*) &$1)",
                        [getStrLit(p.module, n.strVal)])
      else:
        result = ropecg(p.module, "((#NimStringDesc*) &$1$2)",
                        [p.module.tmpBase, rope(id)])
    else:
      result = makeCString(n.strVal)
  of nkFloatLit, nkFloat64Lit:
    result = rope(n.floatVal.toStrMaxPrecision)
  of nkFloat32Lit:
    result = rope(n.floatVal.toStrMaxPrecision("f"))
  else:
    internalError(n.info, "genLiteral(" & $n.kind & ')')
    result = nil

proc genLiteral(p: BProc, n: PNode): Rope =
  result = genLiteral(p, n, n.typ)

proc bitSetToWord(s: TBitSet, size: int): BiggestInt =
  result = 0
  when true:
    for j in countup(0, size - 1):
      if j < len(s): result = result or `shl`(ze64(s[j]), j * 8)
  else:
    # not needed, too complex thinking:
    if CPU[platform.hostCPU].endian == CPU[targetCPU].endian:
      for j in countup(0, size - 1):
        if j < len(s): result = result or `shl`(Ze64(s[j]), j * 8)
    else:
      for j in countup(0, size - 1):
        if j < len(s): result = result or `shl`(Ze64(s[j]), (Size - 1 - j) * 8)

proc genRawSetData(cs: TBitSet, size: int): Rope =
  var frmt: FormatStr
  if size > 8:
    result = "{$n" % []
    for i in countup(0, size - 1):
      if i < size - 1:
        # not last iteration?
        if (i + 1) mod 8 == 0: frmt = "0x$1,$n"
        else: frmt = "0x$1, "
      else:
        frmt = "0x$1}$n"
      addf(result, frmt, [rope(toHex(ze64(cs[i]), 2))])
  else:
    result = intLiteral(bitSetToWord(cs, size))
    #  result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2))

proc genSetNode(p: BProc, n: PNode): Rope =
  var cs: TBitSet
  var size = int(getSize(n.typ))
  toBitSet(n, cs)
  if size > 8:
    let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels)
    result = p.module.tmpBase & rope(id)
    if id == p.module.labels:
      # not found in cache:
      inc(p.module.labels)
      addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
           [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)])
  else:
    result = genRawSetData(cs, size)

proc getStorageLoc(n: PNode): TStorageLoc =
  case n.kind
  of nkSym:
    case n.sym.kind
    of skParam, skTemp:
      result = OnStack
    of skVar, skForVar, skResult, skLet:
      if sfGlobal in n.sym.flags: result = OnHeap
      else: result = OnStack
    of skConst:
      if sfGlobal in n.sym.flags: result = OnHeap
      else: result = OnUnknown
    else: result = OnUnknown
  of nkDerefExpr, nkHiddenDeref:
    case n.sons[0].typ.kind
    of tyVar, tyLent: result = OnUnknown
    of tyPtr: result = OnStack
    of tyRef: result = OnHeap
    else: internalError(n.info, "getStorageLoc")
  of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv:
    result = getStorageLoc(n.sons[0])
  else: result = OnUnknown

proc canMove(n: PNode): bool =
  # for now we're conservative here:
  if n.kind == nkBracket:
    # This needs to be kept consistent with 'const' seq code
    # generation!
    if not isDeepConstExpr(n) or n.len == 0:
      if skipTypes(n.typ, abstractVarRange).kind == tySequence:
        return true
  result = n.kind in nkCallKinds
  #if result:
  #  echo n.info, " optimized ", n
  #  result = false

proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
  if dest.storage == OnStack or not usesNativeGC():
    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
  elif dest.storage == OnHeap:
    # location is on heap
    # now the writer barrier is inlined for performance:
    #
    #    if afSrcIsNotNil in flags:
    #      UseMagic(p.module, 'nimGCref')
    #      lineF(p, cpsStmts, 'nimGCref($1);$n', [rdLoc(src)])
    #    elif afSrcIsNil notin flags:
    #      UseMagic(p.module, 'nimGCref')
    #      lineF(p, cpsStmts, 'if ($1) nimGCref($1);$n', [rdLoc(src)])
    #    if afDestIsNotNil in flags:
    #      UseMagic(p.module, 'nimGCunref')
    #      lineF(p, cpsStmts, 'nimGCunref($1);$n', [rdLoc(dest)])
    #    elif afDestIsNil notin flags:
    #      UseMagic(p.module, 'nimGCunref')
    #      lineF(p, cpsStmts, 'if ($1) nimGCunref($1);$n', [rdLoc(dest)])
    #    lineF(p, cpsStmts, '$1 = $2;$n', [rdLoc(dest), rdLoc(src)])
    if canFormAcycle(dest.t):
      linefmt(p, cpsStmts, "#asgnRef((void**) $1, $2);$n",
              addrLoc(dest), rdLoc(src))
    else:
      linefmt(p, cpsStmts, "#asgnRefNoCycle((void**) $1, $2);$n",
              addrLoc(dest), rdLoc(src))
  else:
    linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
            addrLoc(dest), rdLoc(src))

proc asgnComplexity(n: PNode): int =
  if n != nil:
    case n.kind
    of nkSym: result = 1
    of nkRecCase:
      # 'case objects' are too difficult to inline their assignment operation:
      result = 100
    of nkRecList:
      for t in items(n):
        result += asgnComplexity(t)
    else: discard

proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
  assert field != nil
  result.k = locField
  result.storage = a.storage
  result.lode = lodeTyp t
  result.r = rdLoc(a) & "." & field

proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
  let newflags =
    if src.storage == OnStatic:
      flags + {needToCopy}
    elif tfShallow in dest.t.flags:
      flags - {needToCopy}
    else:
      flags
  let t = skipTypes(dest.t, abstractInst).getUniqueType()
  for i in 0 ..< t.len:
    let t = t.sons[i]
    let field = "Field$1" % [i.rope]
    genAssignment(p, optAsgnLoc(dest, t, field),
                     optAsgnLoc(src, t, field), newflags)

proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
                      t: PNode, typ: PType) =
  if t == nil: return
  let newflags =
    if src.storage == OnStatic:
      flags + {needToCopy}
    elif tfShallow in dest.t.flags:
      flags - {needToCopy}
    else:
      flags
  case t.kind
  of nkSym:
    let field = t.sym
    if field.loc.r == nil: fillObjectFields(p.module, typ)
    genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
                     optAsgnLoc(src, field.typ, field.loc.r), newflags)
  of nkRecList:
    for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ)
  else: discard

proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
  # Consider:
  # type TMyFastString {.shallow.} = string
  # Due to the implementation of pragmas this would end up to set the
  # tfShallow flag for the built-in string type too! So we check only
  # here for this flag, where it is reasonably safe to do so
  # (for objects, etc.):
  if needToCopy notin flags or
      tfShallow in skipTypes(dest.t, abstractVarRange).flags:
    if dest.storage == OnStack or not usesNativeGC():
      useStringh(p.module)
      linefmt(p, cpsStmts,
           "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
           addrLoc(dest), addrLoc(src), rdLoc(dest))
    else:
      linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n",
              addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info))
  else:
    linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
            addrLoc(dest), addrLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info))

proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
  # This function replaces all other methods for generating
  # the assignment operation in C.
  if src.t != nil and src.t.kind == tyPtr:
    # little HACK to support the new 'var T' as return type:
    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
    return
  let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses)
  case ty.kind
  of tyRef:
    genRefAssign(p, dest, src, flags)
  of tySequence:
    if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
      genRefAssign(p, dest, src, flags)
    else:
      linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n",
              addrLoc(dest), rdLoc(src),
              genTypeInfo(p.module, dest.t, dest.lode.info))
  of tyString:
    if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode):
      genRefAssign(p, dest, src, flags)
    else:
      if dest.storage == OnStack or not usesNativeGC():
        linefmt(p, cpsStmts, "$1 = #copyString($2);$n", dest.rdLoc, src.rdLoc)
      elif dest.storage == OnHeap:
        # we use a temporary to care for the dreaded self assignment:
        var tmp: TLoc
        getTemp(p, ty, tmp)
        linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n",
                dest.rdLoc, src.rdLoc, tmp.rdLoc)
        linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", tmp.rdLoc)
      else:
        linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, #copyString($2));$n",
               addrLoc(dest), rdLoc(src))
  of tyProc:
    if needsComplexAssignment(dest.t):
      # optimize closure assignment:
      let a = optAsgnLoc(dest, dest.t, "ClE_0".rope)
      let b = optAsgnLoc(src, dest.t, "ClE_0".rope)
      genRefAssign(p, a, b, flags)
      linefmt(p, cpsStmts, "$1.ClP_0 = $2.ClP_0;$n", rdLoc(dest), rdLoc(src))
    else:
      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
  of tyTuple:
    if needsComplexAssignment(dest.t):
      if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags)
      else: genGenericAsgn(p, dest, src, flags)
    else:
      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
  of tyObject:
    # XXX: check for subtyping?
    if ty.isImportedCppType:
      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
    elif not isObjLackingTypeField(ty):
      genGenericAsgn(p, dest, src, flags)
    elif needsComplexAssignment(ty):
      if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4:
        discard getTypeDesc(p.module, ty)
        internalAssert ty.n != nil
        genOptAsgnObject(p, dest, src, flags, ty.n, ty)
      else:
        genGenericAsgn(p, dest, src, flags)
    else:
      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
  of tyArray:
    if needsComplexAssignment(dest.t):
      genGenericAsgn(p, dest, src, flags)
    else:
      useStringh(p.module)
      linefmt(p, cpsStmts,
           "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
           rdLoc(dest), rdLoc(src), getTypeDesc(p.module, dest.t))
  of tyOpenArray, tyVarargs:
    # open arrays are always on the stack - really? What if a sequence is
    # passed to an open array?
    if needsComplexAssignment(dest.t):
      linefmt(p, cpsStmts,     # XXX: is this correct for arrays?
           "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
           addrLoc(dest), addrLoc(src),
           genTypeInfo(p.module, dest.t, dest.lode.info))
    else:
      useStringh(p.module)
      linefmt(p, cpsStmts,
           "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
           rdLoc(dest), rdLoc(src))
  of tySet:
    if mapType(ty) == ctArray:
      useStringh(p.module)
      linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
              rdLoc(dest), rdLoc(src), rope(getSize(dest.t)))
    else:
      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
  of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
     tyInt..tyUInt64, tyRange, tyVar, tyLent:
    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
  else: internalError("genAssignment: " & $ty.kind)

  if optMemTracker in p.options and dest.storage in {OnHeap, OnUnknown}:
    #writeStackTrace()
    #echo p.currLineInfo, " requesting"
    linefmt(p, cpsStmts, "#memTrackerWrite((void*)$1, $2, $3, $4);$n",
            addrLoc(dest), rope getSize(dest.t),
            makeCString(p.currLineInfo.toFullPath),
            rope p.currLineInfo.safeLineNm)

proc genDeepCopy(p: BProc; dest, src: TLoc) =
  template addrLocOrTemp(a: TLoc): Rope =
    if a.k == locExpr:
      var tmp: TLoc
      getTemp(p, a.t, tmp)
      genAssignment(p, tmp, a, {})
      addrLoc(tmp)
    else:
      addrLoc(a)

  var ty = skipTypes(dest.t, abstractVarRange)
  case ty.kind
  of tyPtr, tyRef, tyProc, tyTuple, tyObject, tyArray:
    # XXX optimize this
    linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n",
            addrLoc(dest), addrLocOrTemp(src),
            genTypeInfo(p.module, dest.t, dest.lode.info))
  of tySequence, tyString:
    linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n",
            addrLoc(dest), rdLoc(src),
            genTypeInfo(p.module, dest.t, dest.lode.info))
  of tyOpenArray, tyVarargs:
    linefmt(p, cpsStmts,
         "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
         addrLoc(dest), addrLocOrTemp(src),
         genTypeInfo(p.module, dest.t, dest.lode.info))
  of tySet:
    if mapType(ty) == ctArray:
      useStringh(p.module)
      linefmt(p, cpsStmts, "memcpy((void*)$1, (NIM_CONST void*)$2, $3);$n",
              rdLoc(dest), rdLoc(src), rope(getSize(dest.t)))
    else:
      linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
  of tyPointer, tyChar, tyBool, tyEnum, tyCString,
     tyInt..tyUInt64, tyRange, tyVar, tyLent:
    linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
  else: internalError("genDeepCopy: " & $ty.kind)

proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
  if d.k != locNone:
    if lfNoDeepCopy in d.flags: genAssignment(p, d, s, {})
    else: genAssignment(p, d, s, {needToCopy})
  else:
    d = s # ``d`` is free, so fill it with ``s``

proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
  var a: TLoc
  if d.k != locNone:
    # need to generate an assignment here
    initLoc(a, locData, n, OnStatic)
    a.r = r
    if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
    else: genAssignment(p, d, a, {needToCopy})
  else:
    # we cannot call initLoc() here as that would overwrite
    # the flags field!
    d.k = locData
    d.lode = n
    d.r = r

proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
  var a: TLoc
  if d.k != locNone:
    # need to generate an assignment here
    initLoc(a, locExpr, n, s)
    a.r = r
    if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
    else: genAssignment(p, d, a, {needToCopy})
  else:
    # we cannot call initLoc() here as that would overwrite
    # the flags field!
    d.k = locExpr
    d.lode = n
    d.r = r

proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
  var a, b: TLoc
  if d.k != locNone: internalError(e.info, "binaryStmt")
  initLocExpr(p, e.sons[1], a)
  initLocExpr(p, e.sons[2], b)
  lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b))

proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
  var a: TLoc
  if d.k != locNone: internalError(e.info, "unaryStmt")
  initLocExpr(p, e.sons[1], a)
  lineCg(p, cpsStmts, frmt, [rdLoc(a)])

proc binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
  var a, b: TLoc
  assert(e.sons[1].typ != nil)
  assert(e.sons[2].typ != nil)
  initLocExpr(p, e.sons[1], a)
  initLocExpr(p, e.sons[2], b)
  putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)]))

proc binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
  var a, b: TLoc
  assert(e.sons[1].typ != nil)
  assert(e.sons[2].typ != nil)
  initLocExpr(p, e.sons[1], a)
  initLocExpr(p, e.sons[2], b)
  putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc]))

proc unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
  var a: TLoc
  initLocExpr(p, e.sons[1], a)
  putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)]))

proc unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
  var a: TLoc
  initLocExpr(p, e.sons[1], a)
  putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)]))

proc binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
                            frmt: string): Rope =
  var size = getSize(t)
  let storage = if size < platform.intSize: rope("NI")
                else: getTypeDesc(p.module, t)
  result = getTempName(p.module)
  linefmt(p, cpsLocals, "$1 $2;$n", storage, result)
  lineCg(p, cpsStmts, frmt, result, rdCharLoc(a), rdCharLoc(b))
  if size < platform.intSize or t.kind in {tyRange, tyEnum}:
    linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseOverflow();$n",
            result, intLiteral(firstOrd(t)), intLiteral(lastOrd(t)))

proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
  const
    prc: array[mAddI..mPred, string] = [
      "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n",
      "$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n",
      "$# = #modInt($#, $#);$n",
      "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"]
    prc64: array[mAddI..mPred, string] = [
      "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n",
      "$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n",
      "$# = #modInt64($#, $#);$n",
      "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"]
    opr: array[mAddI..mPred, string] = [
      "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)",
      "($#)($# / $#)", "($#)($# % $#)",
      "($#)($# + $#)", "($#)($# - $#)"]
  var a, b: TLoc
  assert(e.sons[1].typ != nil)
  assert(e.sons[2].typ != nil)
  initLocExpr(p, e.sons[1], a)
  initLocExpr(p, e.sons[2], b)
  # skipping 'range' is correct here as we'll generate a proper range check
  # later via 'chckRange'
  let t = e.typ.skipTypes(abstractRange)
  if optOverflowCheck notin p.options:
    let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)]
    putIntoDest(p, d, e, res)
  else:
    let res = binaryArithOverflowRaw(p, t, a, b,
                                   if t.kind == tyInt64: prc64[m] else: prc[m])
    putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])

proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
  const
    opr: array[mUnaryMinusI..mAbsI, string] = [
      mUnaryMinusI: "((NI$2)-($1))",
      mUnaryMinusI64: "-($1)",
      mAbsI: "($1 > 0? ($1) : -($1))"]
  var
    a: TLoc
    t: PType
  assert(e.sons[1].typ != nil)
  initLocExpr(p, e.sons[1], a)
  t = skipTypes(e.typ, abstractRange)
  if optOverflowCheck in p.options:
    linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n",
            rdLoc(a), intLiteral(firstOrd(t)))
  putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(t) * 8)])

proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
  const
    binArithTab: array[mAddF64..mXor, string] = [
      "(($4)($1) + ($4)($2))", # AddF64
      "(($4)($1) - ($4)($2))", # SubF64
      "(($4)($1) * ($4)($2))", # MulF64
      "(($4)($1) / ($4)($2))", # DivF64

      "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI
      "($4)((NU$3)($1) << (NU$3)($2))", # ShlI
      "($4)($1 & $2)",      # BitandI
      "($4)($1 | $2)",      # BitorI
      "($4)($1 ^ $2)",      # BitxorI
      "(($1 <= $2) ? $1 : $2)", # MinI
      "(($1 >= $2) ? $1 : $2)", # MaxI
      "(($1 <= $2) ? $1 : $2)", # MinF64
      "(($1 >= $2) ? $1 : $2)", # MaxF64
      "($4)((NU$3)($1) + (NU$3)($2))", # AddU
      "($4)((NU$3)($1) - (NU$3)($2))", # SubU
      "($4)((NU$3)($1) * (NU$3)($2))", # MulU
      "($4)((NU$3)($1) / (NU$3)($2))", # DivU
      "($4)((NU$3)($1) % (NU$3)($2))", # ModU
      "($1 == $2)",           # EqI
      "($1 <= $2)",           # LeI
      "($1 < $2)",            # LtI
      "($1 == $2)",           # EqF64
      "($1 <= $2)",           # LeF64
      "($1 < $2)",            # LtF64
      "((NU$3)($1) <= (NU$3)($2))", # LeU
      "((NU$3)($1) < (NU$3)($2))", # LtU
      "((NU64)($1) <= (NU64)($2))", # LeU64
      "((NU64)($1) < (NU64)($2))", # LtU64
      "($1 == $2)",           # EqEnum
      "($1 <= $2)",           # LeEnum
      "($1 < $2)",            # LtEnum
      "((NU8)($1) == (NU8)($2))", # EqCh
      "((NU8)($1) <= (NU8)($2))", # LeCh
      "((NU8)($1) < (NU8)($2))", # LtCh
      "($1 == $2)",           # EqB
      "($1 <= $2)",           # LeB
      "($1 < $2)",            # LtB
      "($1 == $2)",           # EqRef
      "($1 == $2)",           # EqPtr
      "($1 <= $2)",           # LePtr
      "($1 < $2)",            # LtPtr
      "($1 != $2)"]           # Xor
  var
    a, b: TLoc
    s, k: BiggestInt
  assert(e.sons[1].typ != nil)
  assert(e.sons[2].typ != nil)
  initLocExpr(p, e.sons[1], a)
  initLocExpr(p, e.sons[2], b)
  # BUGFIX: cannot use result-type here, as it may be a boolean
  s = max(getSize(a.t), getSize(b.t)) * 8
  k = getSize(a.t) * 8
  putIntoDest(p, d, e,
              binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
                                      getSimpleTypeDesc(p.module, e.typ), rope(k)])

proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
  var a, b: TLoc
  assert(e.sons[1].typ != nil)
  assert(e.sons[2].typ != nil)
  initLocExpr(p, e.sons[1], a)
  initLocExpr(p, e.sons[2], b)
  if a.t.skipTypes(abstractInst).callConv == ccClosure:
    putIntoDest(p, d, e,
      "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)])
  else:
    putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)])

proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
  let t = skipTypes(e.sons[1].typ, abstractRange)
  if t.kind == tyProc and t.callConv == ccClosure:
    unaryExpr(p, e, d, "($1.ClP_0 == 0)")
  else:
    unaryExpr(p, e, d, "($1 == 0)")

proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
  const
    unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not
      "$1",                   # UnaryPlusI
      "($3)((NU$2) ~($1))",   # BitnotI
      "$1",                   # UnaryPlusF64
      "-($1)",                # UnaryMinusF64
      "($1 < 0? -($1) : ($1))", # AbsF64; BUGFIX: fabs() makes problems
                                # for Tiny C, so we don't use it
      "(($3)(NU)(NU8)($1))",  # mZe8ToI
      "(($3)(NU64)(NU8)($1))", # mZe8ToI64
      "(($3)(NU)(NU16)($1))", # mZe16ToI
      "(($3)(NU64)(NU16)($1))", # mZe16ToI64
      "(($3)(NU64)(NU32)($1))", # mZe32ToI64
      "(($3)(NU64)(NU)($1))", # mZeIToI64
      "(($3)(NU8)(NU)($1))", # ToU8
      "(($3)(NU16)(NU)($1))", # ToU16
      "(($3)(NU32)(NU64)($1))", # ToU32
      "((double) ($1))",      # ToFloat
      "((double) ($1))",      # ToBiggestFloat
      "float64ToInt32($1)",   # ToInt
      "float64ToInt64($1)"]   # ToBiggestInt
  var
    a: TLoc
    t: PType
  assert(e.sons[1].typ != nil)
  initLocExpr(p, e.sons[1], a)
  t = skipTypes(e.typ, abstractRange)
  putIntoDest(p, d, e,
              unArithTab[op] % [rdLoc(a), rope(getSize(t) * 8),
                getSimpleTypeDesc(p.module, e.typ)])

proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
  result = p.module.compileToCpp and
      skipTypes(typ, abstractInst).kind == tyVar and
      tfVarIsPtr notin skipTypes(typ, abstractInst).flags

proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
  let mt = mapType(e.sons[0].typ)
  if mt in {ctArray, ctPtrToArray} and not enforceDeref:
    # XXX the amount of hacks for C's arrays is incredible, maybe we should
    # simply wrap them in a struct? --> Losing auto vectorization then?
    #if e[0].kind != nkBracketExpr:
    #  message(e.info, warnUser, "CAME HERE " & renderTree(e))
    expr(p, e.sons[0], d)
    if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef:
      d.storage = OnHeap
  else:
    var a: TLoc
    var typ = e.sons[0].typ
    if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
      typ = typ.lastSon
    typ = typ.skipTypes(abstractInst)
    if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr:
      initLocExprSingleUse(p, e[0][0], d)
      return
    else:
      initLocExprSingleUse(p, e.sons[0], a)
    if d.k == locNone:
      # dest = *a;  <-- We do not know that 'dest' is on the heap!
      # It is completely wrong to set 'd.storage' here, unless it's not yet
      # been assigned to.
      case typ.kind
      of tyRef:
        d.storage = OnHeap
      of tyVar, tyLent:
        d.storage = OnUnknown
        if tfVarIsPtr notin typ.flags and p.module.compileToCpp and
            e.kind == nkHiddenDeref:
          putIntoDest(p, d, e, rdLoc(a), a.storage)
          return
      of tyPtr:
        d.storage = OnUnknown         # BUGFIX!
      else:
        internalError(e.info, "genDeref " & $typ.kind)
    elif p.module.compileToCpp:
      if typ.kind == tyVar and tfVarIsPtr notin typ.flags and
           e.kind == nkHiddenDeref:
        putIntoDest(p, d, e, rdLoc(a), a.storage)
        return
    if enforceDeref and mt == ctPtrToArray:
      # we lie about the type for better C interop: 'ptr array[3,T]' is
      # translated to 'ptr T', but for deref'ing this produces wrong code.
      # See tmissingderef. So we get rid of the deref instead. The codegen
      # ends up using 'memcpy' for the array assignment,
      # so the '&' and '*' cancel out:
      putIntoDest(p, d, lodeTyp(a.t.sons[0]), rdLoc(a), a.storage)
    else:
      putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage)

proc genAddr(p: BProc, e: PNode, d: var TLoc) =
  # careful  'addr(myptrToArray)' needs to get the ampersand:
  if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
    var a: TLoc
    initLocExpr(p, e.sons[0], a)
    putIntoDest(p, d, e, "&" & a.r, a.storage)
    #Message(e.info, warnUser, "HERE NEW &")
  elif mapType(e.sons[0].typ) == ctArray or isCppRef(p, e.sons[0].typ):
    expr(p, e.sons[0], d)
  else:
    var a: TLoc
    initLocExpr(p, e.sons[0], a)
    putIntoDest(p, d, e, addrLoc(a), a.storage)

template inheritLocation(d: var TLoc, a: TLoc) =
  if d.k == locNone: d.storage = a.storage

proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
  initLocExpr(p, e.sons[0], a)
  if e.sons[1].kind != nkSym: internalError(e.info, "genRecordFieldAux")
  d.inheritLocation(a)
  discard getTypeDesc(p.module, a.t) # fill the record's fields.loc

proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
  var
    a: TLoc
    i: int
  initLocExpr(p, e.sons[0], a)
  let tupType = a.t.skipTypes(abstractInst)
  assert tupType.kind == tyTuple
  d.inheritLocation(a)
  discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
  var r = rdLoc(a)
  case e.sons[1].kind
  of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal)
  else: internalError(e.info, "genTupleElem")
  addf(r, ".Field$1", [rope(i)])
  putIntoDest(p, d, e, r, a.storage)

proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
                      resTyp: ptr PType = nil): PSym =
  var ty = ty
  assert r != nil
  while ty != nil:
    ty = ty.skipTypes(skipPtrs)
    assert(ty.kind in {tyTuple, tyObject})
    result = lookupInRecord(ty.n, field.name)
    if result != nil:
      if resTyp != nil: resTyp[] = ty
      break
    if not p.module.compileToCpp: add(r, ".Sup")
    ty = ty.sons[0]
  if result == nil: internalError(field.info, "genCheckedRecordField")

proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
  var a: TLoc
  genRecordFieldAux(p, e, d, a)
  var r = rdLoc(a)
  var f = e.sons[1].sym
  let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
  if ty.kind == tyTuple:
    # we found a unique tuple type which lacks field information
    # so we use Field$i
    addf(r, ".Field$1", [rope(f.position)])
    putIntoDest(p, d, e, r, a.storage)
  else:
    var rtyp: PType
    let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
    if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp)
    if field.loc.r == nil: internalError(e.info, "genRecordField 3 " & typeToString(ty))
    addf(r, ".$1", [field.loc.r])
    putIntoDest(p, d, e, r, a.storage)

proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc)

proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
  var test, u, v: TLoc
  for i in countup(1, sonsLen(e) - 1):
    var it = e.sons[i]
    assert(it.kind in nkCallKinds)
    assert(it.sons[0].kind == nkSym)
    let op = it.sons[0].sym
    if op.magic == mNot: it = it.sons[1]
    let disc = it.sons[2].skipConv
    assert(disc.kind == nkSym)
    initLoc(test, locNone, it, OnStack)
    initLocExpr(p, it.sons[1], u)
    initLoc(v, locExpr, disc, OnUnknown)
    v.r = obj
    v.r.add(".")
    v.r.add(disc.sym.loc.r)
    genInExprAux(p, it, u, v, test)
    let id = nodeTableTestOrSet(p.module.dataCache,
                               newStrNode(nkStrLit, field.name.s), p.module.labels)
    let strLit = if id == p.module.labels: getStrLit(p.module, field.name.s)
                 else: p.module.tmpBase & rope(id)
    if op.magic == mNot:
      linefmt(p, cpsStmts,
              "if ($1) #raiseFieldError(((#NimStringDesc*) &$2));$n",
              rdLoc(test), strLit)
    else:
      linefmt(p, cpsStmts,
              "if (!($1)) #raiseFieldError(((#NimStringDesc*) &$2));$n",
              rdLoc(test), strLit)

proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
  if optFieldCheck in p.options:
    var a: TLoc
    genRecordFieldAux(p, e.sons[0], d, a)
    let ty = skipTypes(a.t, abstractInst)
    var r = rdLoc(a)
    let f = e.sons[0].sons[1].sym
    let field = lookupFieldAgain(p, ty, f, r)
    if field.loc.r == nil: fillObjectFields(p.module, ty)
    if field.loc.r == nil:
      internalError(e.info, "genCheckedRecordField") # generate the checks:
    genFieldCheck(p, e, r, field)
    add(r, rfmt(nil, ".$1", field.loc.r))
    putIntoDest(p, d, e.sons[0], r, a.storage)
  else:
    genRecordField(p, e.sons[0], d)

proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
  var a, b: TLoc
  initLocExpr(p, x, a)
  initLocExpr(p, y, b)
  var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses)
  var first = intLiteral(firstOrd(ty))
  # emit range check:
  if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
    if not isConstExpr(y):
      # semantic pass has already checked for const index expressions
      if firstOrd(ty) == 0:
        if (firstOrd(b.t) < firstOrd(ty)) or (lastOrd(b.t) > lastOrd(ty)):
          linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError();$n",
                  rdCharLoc(b), intLiteral(lastOrd(ty)))
      else:
        linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n",
                rdCharLoc(b), first, intLiteral(lastOrd(ty)))
    else:
      let idx = getOrdValue(y)
      if idx < firstOrd(ty) or idx > lastOrd(ty):
        localError(x.info, errIndexOutOfBounds)
  d.inheritLocation(a)
  putIntoDest(p, d, n,
              rfmt(nil, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage)

proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
  var a, b: TLoc
  initLocExpr(p, x, a)
  initLocExpr(p, y, b)
  var ty = skipTypes(a.t, abstractVarRange)
  inheritLocation(d, a)
  putIntoDest(p, d, n,
              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)

proc genIndexCheck(p: BProc; arr, idx: TLoc) =
  let ty = skipTypes(arr.t, abstractVarRange)
  case ty.kind
  of tyOpenArray, tyVarargs:
    linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n",
            rdLoc(idx), rdLoc(arr))
  of tyArray:
    let first = intLiteral(firstOrd(ty))
    if tfUncheckedArray notin ty.flags:
      linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n",
              rdCharLoc(idx), first, intLiteral(lastOrd(ty)))
  of tySequence, tyString:
    linefmt(p, cpsStmts,
          "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
          rdLoc(idx), rdLoc(arr), lenField(p))
  else: discard

proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
  var a, b: TLoc
  initLocExpr(p, x, a)
  initLocExpr(p, y, b) # emit range check:
  if optBoundsCheck in p.options:
    linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n",
            rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``!
  inheritLocation(d, a)
  putIntoDest(p, d, n,
              rfmt(nil, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)

proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
  var a, b: TLoc
  initLocExpr(p, x, a)
  initLocExpr(p, y, b)
  var ty = skipTypes(a.t, abstractVarRange)
  if ty.kind in {tyRef, tyPtr}:
    ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
  if optBoundsCheck in p.options:
    if ty.kind == tyString:
      linefmt(p, cpsStmts,
           "if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
           rdLoc(b), rdLoc(a), lenField(p))
    else:
      linefmt(p, cpsStmts,
           "if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
           rdLoc(b), rdLoc(a), lenField(p))
  if d.k == locNone: d.storage = OnHeap
  if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
    a.r = rfmt(nil, "(*$1)", a.r)
  putIntoDest(p, d, n,
              rfmt(nil, "$1->data[$2]", rdLoc(a), rdCharLoc(b)), a.storage)

proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
  var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses)
  if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
  case ty.kind
  of tyArray: genArrayElem(p, n, n.sons[0], n.sons[1], d)
  of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n.sons[0], n.sons[1], d)
  of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d)
  of tyCString: genCStringElem(p, n, n.sons[0], n.sons[1], d)
  of tyTuple: genTupleElem(p, n, d)
  else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')')

proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
  # how to generate code?
  #  'expr1 and expr2' becomes:
  #     result = expr1
  #     fjmp result, end
  #     result = expr2
  #  end:
  #  ... (result computed)
  # BUGFIX:
  #   a = b or a
  # used to generate:
  # a = b
  # if a: goto end
  # a = a
  # end:
  # now it generates:
  # tmp = b
  # if tmp: goto end
  # tmp = a
  # end:
  # a = tmp
  var
    L: TLabel
    tmp: TLoc
  getTemp(p, e.typ, tmp)      # force it into a temp!
  inc p.splitDecls
  expr(p, e.sons[1], tmp)
  L = getLabel(p)
  if m == mOr:
    lineF(p, cpsStmts, "if ($1) goto $2;$n", [rdLoc(tmp), L])
  else:
    lineF(p, cpsStmts, "if (!($1)) goto $2;$n", [rdLoc(tmp), L])
  expr(p, e.sons[2], tmp)
  fixLabel(p, L)
  if d.k == locNone:
    d = tmp
  else:
    genAssignment(p, d, tmp, {}) # no need for deep copying
  dec p.splitDecls

proc genEcho(p: BProc, n: PNode) =
  # this unusal way of implementing it ensures that e.g. ``echo("hallo", 45)``
  # is threadsafe.
  internalAssert n.kind == nkBracket
  if platform.targetOS == osGenode:
    # bypass libc and print directly to the Genode LOG session
    var args: Rope = nil
    var a: TLoc
    for i in countup(0, n.len-1):
      if n.sons[i].skipConv.kind == nkNilLit:
        add(args, ", \"nil\"")
      else:
        initLocExpr(p, n.sons[i], a)
        addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)])
    p.module.includeHeader("<base/log.h>")
    linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
  else:
    if n.len == 0:
      linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", n.len.rope)
    else:
      var a: TLoc
      initLocExpr(p, n, a)
      linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", a.rdLoc, n.len.rope)
    when false:
      p.module.includeHeader("<stdio.h>")
      linefmt(p, cpsStmts, "printf($1$2);$n",
              makeCString(repeat("%s", n.len) & tnl), args)
      linefmt(p, cpsStmts, "fflush(stdout);$n")

proc gcUsage(n: PNode) =
  if gSelectedGC == gcNone: message(n.info, warnGcMem, n.renderTree)

proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
  #   <Nim code>
  #   s = 'Hello ' & name & ', how do you feel?' & 'z'
  #
  #   <generated C code>
  #  {
  #    string tmp0;
  #    ...
  #    tmp0 = rawNewString(6 + 17 + 1 + s2->len);
  #    // we cannot generate s = rawNewString(...) here, because
  #    // ``s`` may be used on the right side of the expression
  #    appendString(tmp0, strlit_1);
  #    appendString(tmp0, name);
  #    appendString(tmp0, strlit_2);
  #    appendChar(tmp0, 'z');
  #    asgn(s, tmp0);
  #  }
  var a, tmp: TLoc
  getTemp(p, e.typ, tmp)
  var L = 0
  var appends: Rope = nil
  var lens: Rope = nil
  for i in countup(0, sonsLen(e) - 2):
    # compute the length expression:
    initLocExpr(p, e.sons[i + 1], a)
    if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar:
      inc(L)
      add(appends, rfmt(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a)))
    else:
      if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}:
        inc(L, len(e.sons[i + 1].strVal))
      else:
        addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)])
      add(appends, rfmt(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a)))
  linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
  add(p.s(cpsStmts), appends)
  if d.k == locNone:
    d = tmp
  else:
    genAssignment(p, d, tmp, {}) # no need for deep copying
  gcUsage(e)

proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
  #  <Nim code>
  #  s &= 'Hello ' & name & ', how do you feel?' & 'z'
  #  // BUG: what if s is on the left side too?
  #  <generated C code>
  #  {
  #    s = resizeString(s, 6 + 17 + 1 + name->len);
  #    appendString(s, strlit_1);
  #    appendString(s, name);
  #    appendString(s, strlit_2);
  #    appendChar(s, 'z');
  #  }
  var
    a, dest: TLoc
    appends, lens: Rope
  assert(d.k == locNone)
  var L = 0
  initLocExpr(p, e.sons[1], dest)
  for i in countup(0, sonsLen(e) - 3):
    # compute the length expression:
    initLocExpr(p, e.sons[i + 2], a)
    if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar:
      inc(L)
      add(appends, rfmt(p.module, "#appendChar($1, $2);$n",
                        rdLoc(dest), rdLoc(a)))
    else:
      if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}:
        inc(L, len(e.sons[i + 2].strVal))
      else:
        addf(lens, "$1->$2 + ", [rdLoc(a), lenField(p)])
      add(appends, rfmt(p.module, "#appendString($1, $2);$n",
                        rdLoc(dest), rdLoc(a)))
  linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n",
          rdLoc(dest), lens, rope(L))
  add(p.s(cpsStmts), appends)
  gcUsage(e)

proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
  # seq &= x  -->
  #    seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
  #    seq->data[seq->len-1] = x;
  let seqAppendPattern = if not p.module.compileToCpp:
                           "$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n"
                         else:
                           "$1 = ($2) #incrSeqV2($1, sizeof($3));$n"
  var a, b, dest, tmpL: TLoc
  initLocExpr(p, e.sons[1], a)
  initLocExpr(p, e.sons[2], b)
  let bt = skipTypes(er == nil: f.loc.r = mangleName(f)
  r.res = makeCString(ropeToStr(f.loc.r))
  r.com = mergeExpr(a)

proc genFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = 
  r.kind = etyNone
  gen(p, n.sons[0], r)
  if n.sons[1].kind != nkSym: InternalError(n.sons[1].info, "genFieldAddr")
  var f = n.sons[1].sym
  if f.loc.r == nil: f.loc.r = mangleName(f)
  r.res = ropef("$1.$2", [r.res, f.loc.r])

proc genCheckedFieldAddr(p: var TProc, n: PNode, r: var TCompRes) = 
  genFieldAddr(p, n.sons[0], r) # XXX
  
proc genCheckedFieldAccess(p: var TProc, n: PNode, r: var TCompRes) = 
  genFieldAccess(p, n.sons[0], r) # XXX
  
proc genArrayAddr(p: var TProc, n: PNode, r: var TCompRes) = 
  var 
    a, b: TCompRes
    first: biggestInt
  r.kind = etyBaseIndex
  gen(p, n.sons[0], a)
  gen(p, n.sons[1], b)
  r.com = mergeExpr(a)
  var typ = skipTypes(n.sons[0].typ, abstractPtrs)
  if typ.kind in {tyArray, tyArrayConstr}: first = FirstOrd(typ.sons[0])
  else: first = 0
  if (optBoundsCheck in p.options) and not isConstExpr(n.sons[1]): 
    useMagic(p, "chckIndx")
    b.res = ropef("chckIndx($1, $2, $3.length)-$2", 
                  [b.res, toRope(first), a.res]) 
    # XXX: BUG: a.res evaluated twice!
  elif first != 0: 
    b.res = ropef("($1)-$2", [b.res, toRope(first)])
  r.res = mergeExpr(b)

proc genArrayAccess(p: var TProc, n: PNode, r: var TCompRes) = 
  genArrayAddr(p, n, r)
  r.kind = etyNone
  r.res = ropef("$1[$2]", [r.com, r.res])
  r.com = nil

proc genAddr(p: var TProc, n: PNode, r: var TCompRes) = 
  var s: PSym
  case n.sons[0].kind
  of nkSym: 
    s = n.sons[0].sym
    if s.loc.r == nil: InternalError(n.info, "genAddr: 3")
    case s.kind
    of skVar, skResult: 
      if mapType(n.typ) == etyObject: 
        # make addr() a no-op:
        r.kind = etyNone
        r.res = s.loc.r
        r.com = nil
      elif sfGlobal in s.flags: 
        # globals are always indirect accessible
        r.kind = etyBaseIndex
        r.com = toRope("Globals")
        r.res = makeCString(ropeToStr(s.loc.r))
      elif sfAddrTaken in s.flags: 
        r.kind = etyBaseIndex
        r.com = s.loc.r
        r.res = toRope("0")
      else: 
        InternalError(n.info, "genAddr: 4")
    else: InternalError(n.info, "genAddr: 2")
  of nkCheckedFieldExpr: 
    genCheckedFieldAddr(p, n, r)
  of nkDotExpr: 
    genFieldAddr(p, n, r)
  of nkBracketExpr: 
    genArrayAddr(p, n, r)
  else: InternalError(n.info, "genAddr")
  
proc genSym(p: var TProc, n: PNode, r: var TCompRes) = 
  var s = n.sym
  if s.loc.r == nil: 
    InternalError(n.info, "symbol has no generated name: " & s.name.s)
  case s.kind
  of skVar, skParam, skTemp, skResult: 
    var k = mapType(s.typ)
    if k == etyBaseIndex: 
      r.kind = etyBaseIndex
      if {sfAddrTaken, sfGlobal} * s.flags != {}: 
        r.com = ropef("$1[0]", [s.loc.r])
        r.res = ropef("$1[1]", [s.loc.r])
      else: 
        r.com = s.loc.r
        r.res = con(s.loc.r, "_Idx")
    elif (k != etyObject) and (sfAddrTaken in s.flags): 
      r.res = ropef("$1[0]", [s.loc.r])
    else: 
      r.res = s.loc.r
  else: r.res = s.loc.r
  
proc genDeref(p: var TProc, n: PNode, r: var TCompRes) = 
  var a: TCompRes
  if mapType(n.sons[0].typ) == etyObject: 
    gen(p, n.sons[0], r)
  else: 
    gen(p, n.sons[0], a)
    if a.kind != etyBaseIndex: InternalError(n.info, "genDeref")
    r.res = ropef("$1[$2]", [a.com, a.res])

proc genArgs(p: var TProc, n: PNode, r: var TCompRes) =
  app(r.res, "(")
  for i in countup(1, sonsLen(n) - 1): 
    if i > 1: app(r.res, ", ")
    var a: TCompRes
    gen(p, n.sons[i], a)
    if a.kind == etyBaseIndex: 
      app(r.res, a.com)
      app(r.res, ", ")
      app(r.res, a.res)
    else: 
      app(r.res, mergeExpr(a))
  app(r.res, ")")

proc genCall(p: var TProc, n: PNode, r: var TCompRes) = 
  gen(p, n.sons[0], r)
  genArgs(p, n, r)

proc genEcho(p: var TProc, n: PNode, r: var TCompRes) =
  app(r.res, "rawEcho")
  genArgs(p, n, r)

proc putToSeq(s: string, indirect: bool): PRope = 
  result = toRope(s)
  if indirect: result = ropef("[$1]", [result])
  
proc createVar(p: var TProc, typ: PType, indirect: bool): PRope
proc createRecordVarAux(p: var TProc, rec: PNode, c: var int): PRope = 
  result = nil
  case rec.kind
  of nkRecList: 
    for i in countup(0, sonsLen(rec) - 1): 
      app(result, createRecordVarAux(p, rec.sons[i], c))
  of nkRecCase: 
    app(result, createRecordVarAux(p, rec.sons[0], c))
    for i in countup(1, sonsLen(rec) - 1): 
      app(result, createRecordVarAux(p, lastSon(rec.sons[i]), c))
  of nkSym: 
    if c > 0: app(result, ", ")
    app(result, mangleName(rec.sym))
    app(result, ": ")
    app(result, createVar(p, rec.sym.typ, false))
    inc(c)
  else: InternalError(rec.info, "createRecordVarAux")
  
proc createVar(p: var TProc, typ: PType, indirect: bool): PRope = 
  var t = skipTypes(typ, abstractInst)
  case t.kind
  of tyInt..tyInt64, tyEnum, tyChar: 
    result = putToSeq("0", indirect)
  of tyFloat..tyFloat128: 
    result = putToSeq("0.0", indirect)
  of tyRange, tyGenericInst: 
    result = createVar(p, lastSon(typ), indirect)
  of tySet: 
    result = toRope("{}")
  of tyBool: 
    result = putToSeq("false", indirect)
  of tyArray, tyArrayConstr: 
    var length = int(lengthOrd(t))
    var e = elemType(t)
    if length > 32: 
      useMagic(p, "ArrayConstr")
      result = ropef("ArrayConstr($1, $2, $3)", [toRope(length), 
          createVar(p, e, false), genTypeInfo(p, e)])
    else: 
      result = toRope("[")
      var i = 0
      while i < length: 
        if i > 0: app(result, ", ")
        app(result, createVar(p, e, false))
        inc(i)
      app(result, "]")
  of tyTuple: 
    result = toRope("{")
    var c = 0
    app(result, createRecordVarAux(p, t.n, c))
    app(result, "}")
  of tyObject: 
    result = toRope("{")
    var c = 0
    if not (tfFinal in t.flags) or (t.sons[0] != nil): 
      inc(c)
      appf(result, "m_type: $1", [genTypeInfo(p, t)])
    while t != nil: 
      app(result, createRecordVarAux(p, t.n, c))
      t = t.sons[0]
    app(result, "}")
  of tyVar, tyPtr, tyRef: 
    if mapType(t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect)
    else: result = putToSeq("null", indirect)
  of tySequence, tyString, tyCString, tyPointer, tyProc: 
    result = putToSeq("null", indirect)
  else: 
    internalError("createVar: " & $t.kind)
    result = nil

proc isIndirect(v: PSym): bool = 
  result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject)

proc genVarInit(p: var TProc, v: PSym, n: PNode, r: var TCompRes) = 
  var 
    a: TCompRes
    s: PRope
  if n.kind == nkEmpty: 
    appf(r.com, "var $1 = $2;$n", 
         [mangleName(v), createVar(p, v.typ, isIndirect(v))])
  else: 
    discard mangleName(v)
    gen(p, n, a)
    case mapType(v.typ)
    of etyObject: 
      if a.com != nil: appf(r.com, "$1;$n", [a.com])
      if needsNoCopy(n): 
        s = a.res
      else: 
        useMagic(p, "NimCopy")
        s = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)])
    of etyBaseIndex: 
      if (a.kind != etyBaseIndex): InternalError(n.info, "genVarInit")
      if {sfAddrTaken, sfGlobal} * v.flags != {}: 
        appf(r.com, "var $1 = [$2, $3];$n", [v.loc.r, a.com, a.res])
      else: 
        appf(r.com, "var $1 = $2; var $1_Idx = $3;$n", [v.loc.r, a.com, a.res])
      return 
    else: 
      if a.com != nil: appf(r.com, "$1;$n", [a.com])
      s = a.res
    if isIndirect(v): appf(r.com, "var $1 = [$2];$n", [v.loc.r, s])
    else: appf(r.com, "var $1 = $2;$n", [v.loc.r, s])
  
proc genVarStmt(p: var TProc, n: PNode, r: var TCompRes) = 
  for i in countup(0, sonsLen(n) - 1): 
    var a = n.sons[i]
    if a.kind == nkCommentStmt: continue 
    assert(a.kind == nkIdentDefs)
    assert(a.sons[0].kind == nkSym)
    var v = a.sons[0].sym
    if lfNoDecl in v.loc.flags: continue 
    genLineDir(p, a, r)
    genVarInit(p, v, a.sons[2], r)

proc genConstStmt(p: var TProc, n: PNode, r: var TCompRes) =
  genLineDir(p, n, r)
  for i in countup(0, sonsLen(n) - 1):
    if n.sons[i].kind == nkCommentStmt: continue 
    assert(n.sons[i].kind == nkConstDef)
    var c = n.sons[i].sons[0].sym
    if (c.ast != nil) and (c.typ.kind in ConstantDataTypes) and
        not (lfNoDecl in c.loc.flags):
      genLineDir(p, n.sons[i], r)
      genVarInit(p, c, c.ast, r)

proc genNew(p: var TProc, n: PNode, r: var TCompRes) =
  var a: TCompRes
  gen(p, n.sons[1], a)
  var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
  if a.com != nil: appf(r.com, "$1;$n", [a.com])
  appf(r.com, "$1 = $2;$n", [a.res, createVar(p, t, true)])

proc genOrd(p: var TProc, n: PNode, r: var TCompRes) =
  case skipTypes(n.sons[1].typ, abstractVar).kind
  of tyEnum, tyInt..tyInt64, tyChar: gen(p, n.sons[1], r)
  of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)")
  else: InternalError(n.info, "genOrd")
  
proc genConStrStr(p: var TProc, n: PNode, r: var TCompRes) =
  var a: TCompRes

  gen(p, n.sons[1], a)
  r.com = mergeExpr(r.com, a.com)
  if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar:
    r.res.app(ropef("[$1].concat(", [a.res]))
  else:
    r.res.app(ropef("($1.slice(0,-1)).concat(", [a.res]))

  for i in countup(2, sonsLen(n) - 2):
    gen(p, n.sons[i], a)
    r.com = mergeExpr(r.com, a.com)

    if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar:
      r.res.app(ropef("[$1],", [a.res]))
    else:
      r.res.app(ropef("$1.slice(0,-1),", [a.res]))

  gen(p, n.sons[sonsLen(n) - 1], a)
  r.com = mergeExpr(r.com, a.com)
  if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar:
    r.res.app(ropef("[$1, 0])", [a.res]))
  else:
    r.res.app(ropef("$1)", [a.res]))

proc genRepr(p: var TProc, n: PNode, r: var TCompRes) =
  var t = skipTypes(n.sons[1].typ, abstractVarRange)
  case t.kind
  of tyInt..tyInt64:
    unaryExpr(p, n, r, "", "reprInt($1)")
  of tyEnum, tyOrdinal:
    binaryExpr(p, n, r, "", "reprEnum($1, $2)")
  else:
    # XXX:
    internalError(n.info, "genRepr: Not implemented")

proc genMagic(p: var TProc, n: PNode, r: var TCompRes) =
  var 
    a: TCompRes
    line, filen: PRope
  var op = n.sons[0].sym.magic
  case op
  of mOr: genOr(p, n.sons[1], n.sons[2], r)
  of mAnd: genAnd(p, n.sons[1], n.sons[2], r)
  of mAddi..mStrToStr: arith(p, n, r, op)
  of mRepr: genRepr(p, n, r)
  of mSwap: genSwap(p, n, r)
  of mUnaryLt:
    # XXX: range checking?
    if not (optOverflowCheck in p.Options): unaryExpr(p, n, r, "", "$1 - 1")
    else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)")
  of mPred:
    # XXX: range checking?
    if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 - $2")
    else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)")
  of mSucc:
    # XXX: range checking?
    if not (optOverflowCheck in p.Options): binaryExpr(p, n, r, "", "$1 - $2")
    else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)")
  of mAppendStrCh: binaryStmt(p, n, r, "addChar", "$1 = addChar($1, $2)")
  of mAppendStrStr:
    if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
        binaryStmt(p, n, r, "", "$1 += $2")
    else:
      binaryStmt(p, n, r, "", "$1 = ($1.slice(0,-1)).concat($2)")
    # XXX: make a copy of $2, because of ECMAScript's sucking semantics
  of mAppendSeqElem: binaryStmt(p, n, r, "", "$1.push($2)")
  of mConStrStr: genConStrStr(p, n, r)
  of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)")
  of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)")
  of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)")
  of mIsNil: unaryExpr(p, n, r, "", "$1 == null")
  of mEnumToStr: genRepr(p, n, r)
  of mAssert:
    if (optAssert in p.Options):
      useMagic(p, "internalAssert")
      gen(p, n.sons[1], a)
      line = toRope(toLinenumber(n.info))
      filen = makeCString(ToFilename(n.info))
      appf(r.com, "if (!($3)) internalAssert($1, $2)",
           [filen, line, mergeExpr(a)])
  of mNew, mNewFinalize: genNew(p, n, r)
  of mSizeOf: r.res = toRope(getSize(n.sons[1].typ))
  of mChr: gen(p, n.sons[1], r)      # nothing to do
  of mOrd: genOrd(p, n, r)
  of mLengthStr: unaryExpr(p, n, r, "", "($1.length-1)")
  of mLengthSeq, mLengthOpenArray, mLengthArray:
    unaryExpr(p, n, r, "", "$1.length")
  of mHigh:
    if skipTypes(n.sons[0].typ, abstractVar).kind == tyString:
      unaryExpr(p, n, r, "", "($1.length-2)")
    else:
      unaryExpr(p, n, r, "", "($1.length-1)")
  of mInc:
    if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 += $2")
    else: binaryStmt(p, n, r, "addInt", "$1 = addInt($1, $2)")
  of ast.mDec:
    if not (optOverflowCheck in p.Options): binaryStmt(p, n, r, "", "$1 -= $2")
    else: binaryStmt(p, n, r, "subInt", "$1 = subInt($1, $2)")
  of mSetLengthStr: binaryStmt(p, n, r, "", "$1.length = ($2)-1")
  of mSetLengthSeq: binaryStmt(p, n, r, "", "$1.length = $2")
  of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
  of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
  of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)")
  of mEqSet: binaryExpr(p, n, r, "SetEq", "SetEq($1, $2)")
  of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)")
  of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)")
  of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)")
  of mIncl: binaryStmt(p, n, r, "", "$1[$2] = true")
  of mExcl: binaryStmt(p, n, r, "", "delete $1[$2]")
  of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)")
  of mNLen..mNError:
    localError(n.info, errCannotGenerateCodeForX, n.sons[0].sym.name.s)
  of mNewSeq: binaryStmt(p, n, r, "", "$1 = new Array($2)")
  of mEcho: genEcho(p, n, r)
  else:
    genCall(p, n, r)
    #else internalError(e.info, 'genMagic: ' + magicToStr[op]);
  
proc genSetConstr(p: var TProc, n: PNode, r: var TCompRes) = 
  var 
    a, b: TCompRes
  useMagic(p, "SetConstr")
  r.res = toRope("SetConstr(")
  for i in countup(0, sonsLen(n) - 1): 
    if i > 0: app(r.res, ", ")
    var it = n.sons[i]
    if it.kind == nkRange: 
      gen(p, it.sons[0], a)
      gen(p, it.sons[1], b)
      r.com = mergeExpr(r.com, mergeExpr(a.com, b.com))
      appf(r.res, "[$1, $2]", [a.res, b.res])
    else: 
      gen(p, it, a)
      r.com = mergeExpr(r.com, a.com)
      app(r.res, a.res)
  app(r.res, ")")

proc genArrayConstr(p: var TProc, n: PNode, r: var TCompRes) = 
  var a: TCompRes
  r.res = toRope("[")
  for i in countup(0, sonsLen(n) - 1): 
    if i > 0: app(r.res, ", ")
    gen(p, n.sons[i], a)
    r.com = mergeExpr(r.com, a.com)
    app(r.res, a.res)
  app(r.res, "]")

proc genRecordConstr(p: var TProc, n: PNode, r: var TCompRes) = 
  var a: TCompRes
  var i = 0
  var length = sonsLen(n)
  r.res = toRope("{")
  while i < length: 
    if i > 0: app(r.res, ", ")
    if (n.sons[i].kind != nkSym): 
      internalError(n.sons[i].info, "genRecordConstr")
    gen(p, n.sons[i + 1], a)
    r.com = mergeExpr(r.com, a.com)
    appf(r.res, "$1: $2", [mangleName(n.sons[i].sym), a.res])
    inc(i, 2)

proc genConv(p: var TProc, n: PNode, r: var TCompRes) = 
  var dest = skipTypes(n.typ, abstractVarRange)
  var src = skipTypes(n.sons[1].typ, abstractVarRange)
  gen(p, n.sons[1], r)
  if (dest.kind != src.kind) and (src.kind == tyBool): 
    r.res = ropef("(($1)? 1:0)", [r.res])
  
proc upConv(p: var TProc, n: PNode, r: var TCompRes) = 
  gen(p, n.sons[0], r)        # XXX
  
proc genRangeChck(p: var TProc, n: PNode, r: var TCompRes, magic: string) = 
  var a, b: TCompRes
  gen(p, n.sons[0], r)
  if optRangeCheck in p.options: 
    gen(p, n.sons[1], a)
    gen(p, n.sons[2], b)
    r.com = mergeExpr(r.com, mergeExpr(a.com, b.com))
    useMagic(p, "chckRange")
    r.res = ropef("chckRange($1, $2, $3)", [r.res, a.res, b.res])

proc convStrToCStr(p: var TProc, n: PNode, r: var TCompRes) = 
  # we do an optimization here as this is likely to slow down
  # much of the code otherwise:
  if n.sons[0].kind == nkCStringToString: 
    gen(p, n.sons[0].sons[0], r)
  else: 
    gen(p, n.sons[0], r)
    if r.res == nil: InternalError(n.info, "convStrToCStr")
    useMagic(p, "toEcmaStr")
    r.res = ropef("toEcmaStr($1)", [r.res])

proc convCStrToStr(p: var TProc, n: PNode, r: var TCompRes) = 
  # we do an optimization here as this is likely to slow down
  # much of the code otherwise:
  if n.sons[0].kind == nkStringToCString: 
    gen(p, n.sons[0].sons[0], r)
  else: 
    gen(p, n.sons[0], r)
    if r.res == nil: InternalError(n.info, "convCStrToStr")
    useMagic(p, "cstrToNimstr")
    r.res = ropef("cstrToNimstr($1)", [r.res])

proc genReturnStmt(p: var TProc, n: PNode, r: var TCompRes) = 
  var a: TCompRes
  if p.procDef == nil: InternalError(n.info, "genReturnStmt")
  p.BeforeRetNeeded = true
  if (n.sons[0].kind != nkEmpty): 
    genStmt(p, n.sons[0], a)
    if a.com != nil: appf(r.com, "$1;$n", mergeStmt(a))
  else: 
    genLineDir(p, n, r)
  finishTryStmt(p, r, p.nestedTryStmts)
  app(r.com, "break BeforeRet;" & tnl)

proc genProcBody(p: var TProc, prc: PSym, r: TCompRes): PRope = 
  if optStackTrace in prc.options: 
    result = ropef("var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" &
        "framePtr = F;$n", [makeCString(prc.owner.name.s & '.' & prc.name.s), 
                            makeCString(toFilename(prc.info))])
  else: 
    result = nil
  if p.beforeRetNeeded: 
    appf(result, "BeforeRet: do {$n$1} while (false); $n", [mergeStmt(r)])
  else: 
    app(result, mergeStmt(r))
  if prc.typ.callConv == ccSysCall: 
    result = ropef("try {$n$1} catch (e) {$n" &
        " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}", [result])
  if optStackTrace in prc.options: 
    app(result, "framePtr = framePtr.prev;" & tnl)
  
proc genProc(oldProc: var TProc, n: PNode, r: var TCompRes) = 
  var 
    p: TProc
    resultSym: PSym
    name, returnStmt, resultAsgn, header: PRope
    a: TCompRes
  var prc = n.sons[namePos].sym
  initProc(p, oldProc.globals, oldProc.module, n, prc.options)
  returnStmt = nil
  resultAsgn = nil
  name = mangleName(prc)
  header = generateHeader(p, prc.typ)
  if (prc.typ.sons[0] != nil) and not (sfPure in prc.flags): 
    resultSym = n.sons[resultPos].sym
    resultAsgn = ropef("var $1 = $2;$n", [mangleName(resultSym), 
        createVar(p, resultSym.typ, isIndirect(resultSym))])
    gen(p, n.sons[resultPos], a)
    if a.com != nil: appf(returnStmt, "$1;$n", [a.com])
    returnStmt = ropef("return $1;$n", [a.res])
  genStmt(p, n.sons[codePos], r)
  r.com = ropef("function $1($2) {$n$3$4$5}$n", 
                [name, header, resultAsgn, genProcBody(p, prc, r), returnStmt])
  r.res = nil

proc genStmtListExpr(p: var TProc, n: PNode, r: var TCompRes) = 
  var a: TCompRes
  # watch out this trick: ``function () { stmtList; return expr; }()``
  r.res = toRope("function () {")
  for i in countup(0, sonsLen(n) - 2): 
    genStmt(p, n.sons[i], a)
    app(r.res, mergeStmt(a))
  gen(p, lastSon(n), a)
  if a.com != nil: appf(r.res, "$1;$n", [a.com])
  appf(r.res, "return $1; }()", [a.res])

proc genStmt(p: var TProc, n: PNode, r: var TCompRes) = 
  var a: TCompRes
  r.kind = etyNone
  r.com = nil
  r.res = nil
  case n.kind
  of nkNilLit, nkEmpty: nil
  of nkStmtList: 
    for i in countup(0, sonsLen(n) - 1): 
      genStmt(p, n.sons[i], a)
      app(r.com, mergeStmt(a))
  of nkBlockStmt: genBlock(p, n, r)
  of nkIfStmt: genIfStmt(p, n, r)
  of nkWhileStmt: genWhileStmt(p, n, r)
  of nkVarSection: genVarStmt(p, n, r)
  of nkConstSection: genConstStmt(p, n, r)
  of nkForStmt: internalError(n.info, "for statement not eliminated")
  of nkCaseStmt: genCaseStmt(p, n, r)
  of nkReturnStmt: genReturnStmt(p, n, r)
  of nkBreakStmt: genBreakStmt(p, n, r)
  of nkAsgn: genAsgn(p, n, r)
  of nkFastAsgn: genFastAsgn(p, n, r)
  of nkDiscardStmt: 
    genLineDir(p, n, r)
    gen(p, n.sons[0], r)
    app(r.res, ';' & tnl)
  of nkAsmStmt: genAsmStmt(p, n, r)
  of nkTryStmt: genTryStmt(p, n, r)
  of nkRaiseStmt: genRaiseStmt(p, n, r)
  of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, 
     nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: 
    nil
  of nkProcDef, nkMethodDef, nkConverterDef: 
    if (n.sons[genericParamsPos].kind == nkEmpty): 
      var prc = n.sons[namePos].sym
      if (n.sons[codePos].kind != nkEmpty) and not (lfNoDecl in prc.loc.flags): 
        genProc(p, n, r)
      else: 
        discard mangleName(prc)
  else: 
    genLineDir(p, n, r)
    gen(p, n, r)
    app(r.res, ';' & tnl)

proc gen(p: var TProc, n: PNode, r: var TCompRes) = 
  var f: BiggestFloat
  r.kind = etyNone
  r.com = nil
  r.res = nil
  case n.kind
  of nkSym: 
    genSym(p, n, r)
  of nkCharLit..nkInt64Lit: 
    r.res = toRope(n.intVal)
  of nkNilLit: 
    if mapType(n.typ) == etyBaseIndex: 
      r.kind = etyBaseIndex
      r.com = toRope"null"
      r.res = toRope"0"
    else: 
      r.res = toRope"null"
  of nkStrLit..nkTripleStrLit: 
    if skipTypes(n.typ, abstractVarRange).kind == tyString: 
      useMagic(p, "cstrToNimstr")
      r.res = ropef("cstrToNimstr($1)", [makeCString(n.strVal)])
    else: 
      r.res = makeCString(n.strVal)
  of nkFloatLit..nkFloat64Lit: 
    f = n.floatVal
    if f != f: r.res = toRope"NaN"
    elif f == 0.0: r.res = toRope"0.0"
    elif f == 0.5 * f: 
      if f > 0.0: r.res = toRope"Infinity"
      else: r.res = toRope"-Infinity"
    else: r.res = toRope(f.ToStrMaxPrecision)
  of nkBlockExpr: genBlock(p, n, r)
  of nkIfExpr: genIfExpr(p, n, r)
  of nkCall, nkHiddenCallConv, nkCommand, nkCallStrLit: 
    if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): 
      genMagic(p, n, r)
    else: 
      genCall(p, n, r)
  of nkCurly: genSetConstr(p, n, r)
  of nkBracket: genArrayConstr(p, n, r)
  of nkPar: genRecordConstr(p, n, r)
  of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r)
  of nkAddr, nkHiddenAddr: genAddr(p, n, r)
  of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r)
  of nkBracketExpr: genArrayAccess(p, n, r)
  of nkDotExpr: genFieldAccess(p, n, r)
  of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r)
  of nkObjDownConv: gen(p, n.sons[0], r)
  of nkObjUpConv: upConv(p, n, r)
  of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF")
  of nkChckRange64: genRangeChck(p, n, r, "chckRange64")
  of nkChckRange: genRangeChck(p, n, r, "chckRange")
  of nkStringToCString: convStrToCStr(p, n, r)
  of nkCStringToString: convCStrToStr(p, n, r)
  of nkStmtListExpr: genStmtListExpr(p, n, r)
  of nkEmpty: nil
  else: InternalError(n.info, "gen: unknown node type: " & $n.kind)
  
var globals: PGlobals

proc newModule(module: PSym, filename: string): BModule = 
  new(result)
  result.filename = filename
  result.module = module
  if globals == nil: globals = newGlobals()
  
proc genHeader(): PRope = 
  result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" &
      "/*   (c) 2010 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" &
      "var framePtr = null;$n" & "var excHandler = null;$n", 
                 [toRope(versionAsString)])

proc genModule(p: var TProc, n: PNode, r: var TCompRes) = 
  genStmt(p, n, r)
  if optStackTrace in p.options: 
    r.com = ropef("var F = {procname:$1,prev:framePtr,filename:$2,line:0};$n" &
        "framePtr = F;$n" & "$3" & "framePtr = framePtr.prev;$n", [
        makeCString("module " & p.module.module.name.s), 
        makeCString(toFilename(p.module.module.info)), r.com])

proc myProcess(b: PPassContext, n: PNode): PNode = 
  if passes.skipCodegen(n): return n
  var 
    p: TProc
    r: TCompRes
  result = n
  var m = BModule(b)
  if m.module == nil: InternalError(n.info, "myProcess")
  initProc(p, globals, m, nil, m.module.options)
  genModule(p, n, r)
  app(p.globals.code, p.data)
  app(p.globals.code, mergeStmt(r))

proc myClose(b: PPassContext, n: PNode): PNode = 
  if passes.skipCodegen(n): return n
  result = myProcess(b, n)
  var m = BModule(b)
  if sfMainModule in m.module.flags: 
    # write the file:
    var code = con(globals.typeInfo, globals.code)
    var outfile = changeFileExt(completeCFilePath(m.filename), "js")
    discard writeRopeIfNotEqual(con(genHeader(), code), outfile)

proc myOpenCached(s: PSym, filename: string, rd: PRodReader): PPassContext = 
  InternalError("symbol files are not possible with the Ecmas code generator")
  result = nil

proc myOpen(s: PSym, filename: string): PPassContext = 
  result = newModule(s, filename)

proc ecmasgenPass(): TPass = 
  InitPass(result)
  result.open = myOpen
  result.close = myClose
  result.openCached = myOpenCached
  result.process = myProcess
an class="w"> = case op of mOr, mAnd: genAndOr(p, e, d, op) of mNot..mToBiggestInt: unaryArith(p, e, d, op) of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op) of mAddF64..mDivF64: binaryFloatArith(p, e, d, op) of mShrI..mXor: binaryArith(p, e, d, op) of mEqProc: genEqProc(p, e, d) of mAddI..mPred: binaryArithOverflow(p, e, d, op) of mRepr: genRepr(p, e, d) of mGetTypeInfo: genGetTypeInfo(p, e, d) of mSwap: genSwap(p, e, d) of mUnaryLt: if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)") else: unaryExpr(p, e, d, "#subInt($1, 1)") of mInc, mDec: const opr: array[mInc..mDec, string] = ["$1 += $2;$n", "$1 -= $2;$n"] const fun64: array[mInc..mDec, string] = ["$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"] const fun: array[mInc..mDec, string] = ["$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange}) if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: binaryStmt(p, e, d, opr[op]) else: var a, b: TLoc assert(e.sons[1].typ != nil) assert(e.sons[2].typ != nil) initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent}) let res = binaryArithOverflowRaw(p, ranged, a, b, if underlying.kind == tyInt64: fun64[op] else: fun[op]) putIntoDest(p, a, e.sons[1], "($#)($#)" % [ getTypeDesc(p.module, ranged), res]) of mConStrStr: genStrConcat(p, e, d) of mAppendStrCh: binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n") of mAppendStrStr: genStrAppend(p, e, d) of mAppendSeqElem: genSeqElemAppend(p, e, d) of mEqStr: genStrEquals(p, e, d) of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)") of mIsNil: genIsNil(p, e, d) of mIntToStr: genDollar(p, e, d, "#nimIntToStr($1)") of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)") of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)") of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") of mStrToStr: expr(p, e.sons[1], d) of mEnumToStr: genRepr(p, e, d) of mOf: genOf(p, e, d) of mNew: genNew(p, e) of mNewFinalize: genNewFinalize(p, e) of mNewSeq: genNewSeq(p, e) of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: let t = e.sons[1].typ.skipTypes({tyTypeDesc}) putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: genArrayLen(p, e, d, op) of mXLenStr: if not p.module.compileToCpp: unaryExpr(p, e, d, "($1->Sup.len)") else: unaryExpr(p, e, d, "$1->len") of mXLenSeq: # see 'taddhigh.nim' for why we need to use a temporary here: var a, tmp: TLoc initLocExpr(p, e[1], a) getIntTemp(p, tmp) var frmt: FormatStr if not p.module.compileToCpp: frmt = "$1 = $2->Sup.len;$n" else: frmt = "$1 = $2->len;$n" lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) putIntoDest(p, d, e, tmp.r) of mGCref: unaryStmt(p, e, d, "#nimGCref($1);$n") of mGCunref: unaryStmt(p, e, d, "#nimGCunref($1);$n") of mSetLengthStr: genSetLengthStr(p, e, d) of mSetLengthSeq: genSetLengthSeq(p, e, d) of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, mInSet: genSetOp(p, e, d, op) of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, mParseBiggestFloat: var opr = e.sons[0].sym if lfNoDecl notin opr.loc.flags: discard cgsym(p.module, $opr.loc.r) genCall(p, e, d) of mReset: genReset(p, e) of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s) of mSpawn: let n = lowerings.wrapProcForSpawn(p.module.module, e, e.typ, nil, nil) expr(p, n, d) of mParallel: let n = semparallel.liftParallel(p.module.module, e) expr(p, n, d) of mDeepCopy: var a, b: TLoc let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] initLocExpr(p, x, a) initLocExpr(p, e.sons[2], b) genDeepCopy(p, a, b) of mDotDot, mEqCString: genCall(p, e, d) else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind internalError(e.info, "genMagicExpr: " & $op) proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # example: { a..b, c, d, e, f..g } # we have to emit an expression of the form: # memset(tmp, 0, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); var a, b, idx: TLoc if nfAllConst in e.flags: putIntoDest(p, d, e, genSetNode(p, e)) else: if d.k == locNone: getTemp(p, e.typ, d) if getSize(e.typ) > 8: # big set: useStringh(p.module) lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n", [rdLoc(d), getTypeDesc(p.module, e.typ)]) for i in countup(0, sonsLen(e) - 1): if e.sons[i].kind == nkRange: getTemp(p, getSysType(tyInt), idx) # our counter initLocExpr(p, e.sons[i].sons[0], a) initLocExpr(p, e.sons[i].sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)]) else: initLocExpr(p, e.sons[i], a) lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n", [rdLoc(d), rdSetElemLoc(a, e.typ)]) else: # small set var ts = "NU" & $(getSize(e.typ) * 8) lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) for i in countup(0, sonsLen(e) - 1): if e.sons[i].kind == nkRange: getTemp(p, getSysType(tyInt), idx) # our counter initLocExpr(p, e.sons[i].sons[0], a) initLocExpr(p, e.sons[i].sons[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [ rdLoc(idx), rdLoc(d), rdSetElemLoc(a, e.typ), rdSetElemLoc(b, e.typ)]) else: initLocExpr(p, e.sons[i], a) lineF(p, cpsStmts, "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n", [rdLoc(d), rdSetElemLoc(a, e.typ)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var rec: TLoc if not handleConstExpr(p, n, d): let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized if d.k == locNone: getTemp(p, t, d) for i in countup(0, sonsLen(n) - 1): var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] initLoc(rec, locExpr, it, d.storage) rec.r = "$1.Field$2" % [rdLoc(d), rope(i)] expr(p, it, rec) proc isConstClosure(n: PNode): bool {.inline.} = result = n.sons[0].kind == nkSym and isRoutine(n.sons[0].sym) and n.sons[1].kind == nkNilLit proc genClosure(p: BProc, n: PNode, d: var TLoc) = assert n.kind == nkClosure if isConstClosure(n): inc(p.module.labels) var tmp = "CNSTCLOSURE" & rope(p.module.labels) addf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, n.typ), tmp, genConstExpr(p, n)]) putIntoDest(p, d, n, tmp, OnStatic) else: var tmp, a, b: TLoc initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[1], b) if n.sons[0].skipConv.kind == nkClosure: internalError(n.info, "closure to closure created") # tasyncawait.nim breaks with this optimization: when false: if d.k != locNone: linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n", d.rdLoc, a.rdLoc, b.rdLoc) else: getTemp(p, n.typ, tmp) linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n", tmp.rdLoc, a.rdLoc, b.rdLoc) putLocIntoDest(p, d, tmp) proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) = var arr: TLoc if not handleConstExpr(p, n, d): if d.k == locNone: getTemp(p, n.typ, d) for i in countup(0, sonsLen(n) - 1): initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage) arr.r = "$1[$2]" % [rdLoc(d), intLiteral(i)] expr(p, n.sons[i], arr) proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) = requestConstImpl(p, sym) assert((sym.loc.r != nil) and (sym.loc.t != nil)) putLocIntoDest(p, d, sym.loc) template genStmtListExprImpl(exprOrStmt) {.dirty.} = #let hasNimFrame = magicsys.getCompilerProc("nimFrame") != nil let hasNimFrame = p.prc != nil and sfSystemModule notin p.module.module.flags and optStackTrace in p.prc.options var frameName: Rope = nil for i in 0 .. n.len - 2: let it = n[i] if it.kind == nkComesFrom: if hasNimFrame and frameName == nil: inc p.labels frameName = "FR" & rope(p.labels) & "_" let theMacro = it[0].sym add p.s(cpsStmts), initFrameNoDebug(p, frameName, makeCString theMacro.name.s, theMacro.info.quotedFilename, it.info.line) else: genStmts(p, it) if n.len > 0: exprOrStmt if frameName != nil: add p.s(cpsStmts), deinitFrameNoDebug(p, frameName) proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) = genStmtListExprImpl: expr(p, n[n.len - 1], d) proc genStmtList(p: BProc, n: PNode) = genStmtListExprImpl: genStmts(p, n[n.len - 1]) proc upConv(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLocExpr(p, n.sons[0], a) let dest = skipTypes(n.typ, abstractPtrs) if optObjCheck in p.options and not isObjLackingTypeField(dest): var r = rdLoc(a) var nilCheck: Rope = nil var t = skipTypes(a.t, abstractInst) while t.kind in {tyVar, tyLent, tyPtr, tyRef}: if t.kind notin {tyVar, tyLent}: nilCheck = r if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: r = "(*$1)" % [r] t = skipTypes(t.lastSon, abstractInst) discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: add(r, ".Sup") t = skipTypes(t.sons[0], skipPtrs) if nilCheck != nil: linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n", nilCheck, r, genTypeInfo(p.module, dest, n.info)) else: linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n", r, genTypeInfo(p.module, dest, n.info)) if n.sons[0].typ.kind != tyObject: putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) else: putIntoDest(p, d, n, "(*($1*) ($2))" % [getTypeDesc(p.module, dest), addrLoc(a)], a.storage) proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs)) expr(p, n.sons[0], d) # downcast does C++ for us else: var dest = skipTypes(n.typ, abstractPtrs) var arg = n.sons[0] while arg.kind == nkObjDownConv: arg = arg.sons[0] var src = skipTypes(arg.typ, abstractPtrs) discard getTypeDesc(p.module, src) var a: TLoc initLocExpr(p, arg, a) var r = rdLoc(a) let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent} if isRef: add(r, "->Sup") else: add(r, ".Sup") for i in countup(2, abs(inheritanceDiff(dest, src))): add(r, ".Sup") if isRef: # it can happen that we end up generating '&&x->Sup' here, so we pack # the '&x->Sup' into a temporary and then those address is taken # (see bug #837). However sometimes using a temporary is not correct: # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test # this by ensuring the destination is also a pointer: if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}: getTemp(p, n.typ, d) linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) else: r = "&" & r putIntoDest(p, d, n, r, a.storage) else: putIntoDest(p, d, n, r, a.storage) proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) let tmp = p.module.tmpBase & rope(id) if id == p.module.labels: # expression not found in the cache: inc(p.module.labels) addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) if d.k == locNone: fillLoc(d, locData, n, tmp, OnStatic) else: putDataIntoDest(p, d, n, tmp) # This fixes bug #4551, but we really need better dataflow # analysis to make this 100% safe. if t.kind notin {tySequence, tyString}: d.storage = OnStatic proc expr(p: BProc, n: PNode, d: var TLoc) = p.currLineInfo = n.info case n.kind of nkSym: var sym = n.sym case sym.kind of skMethod: if {sfDispatcher, sfForward} * sym.flags != {}: # we cannot produce code for the dispatcher yet: fillProcLoc(p.module, n) genProcPrototype(p.module, sym) else: genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) of skProc, skConverter, skIterator, skFunc: #if sym.kind == skIterator: # echo renderTree(sym.getBody, {renderIds}) if sfCompileTime in sym.flags: localError(n.info, "request to generate code for .compileTime proc: " & sym.name.s) genProc(p.module, sym) if sym.loc.r == nil or sym.loc.lode == nil: internalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of skConst: if isSimpleConst(sym.typ): putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic) else: genComplexConst(p, sym, d) of skEnumField: putIntoDest(p, d, n, rope(sym.position)) of skVar, skForVar, skResult, skLet: if {sfGlobal, sfThread} * sym.flags != {}: genVarPrototype(p.module, n) if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError n.info, "expr: var not init " & sym.name.s & "_" & $sym.id if sfThread in sym.flags: accessThreadLocalVar(p, sym) if emulatedThreadVars(): putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r) else: putLocIntoDest(p, d, sym.loc) else: putLocIntoDest(p, d, sym.loc) of skTemp: if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) internalError(n.info, "expr: temp not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) of skParam: if sym.loc.r == nil or sym.loc.t == nil: # echo "FAILED FOR PRCO ", p.prc.name.s # debug p.prc.typ.n # echo renderTree(p.prc.ast, {renderIds}) internalError(n.info, "expr: param not init " & sym.name.s & "_" & $sym.id) putLocIntoDest(p, d, sym.loc) else: internalError(n.info, "expr(" & $sym.kind & "); unknown symbol") of nkNilLit: if not isEmptyType(n.typ): putIntoDest(p, d, n, genLiteral(p, n)) of nkStrLit..nkTripleStrLit: putDataIntoDest(p, d, n, genLiteral(p, n)) of nkIntLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit, nkCharLit: putIntoDest(p, d, n, genLiteral(p, n)) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: genLineDir(p, n) let op = n.sons[0] if n.typ.isNil: # discard the value: var a: TLoc if op.kind == nkSym and op.sym.magic != mNone: genMagicExpr(p, n, a, op.sym.magic) else: genCall(p, n, a) else: # load it into 'd': if op.kind == nkSym and op.sym.magic != mNone: genMagicExpr(p, n, d, op.sym.magic) else: genCall(p, n, d) of nkCurly: if isDeepConstExpr(n) and n.len != 0: putIntoDest(p, d, n, genSetNode(p, n)) else: genSetConstr(p, n, d) of nkBracket: if isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) elif skipTypes(n.typ, abstractVarRange).kind == tySequence: genSeqConstr(p, n, d) else: genArrayConstr(p, n, d) of nkPar: if isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) else: genTupleConstr(p, n, d) of nkObjConstr: genObjConstr(p, n, d) of nkCast: genCast(p, n, d) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d) of nkHiddenAddr, nkAddr: genAddr(p, n, d) of nkBracketExpr: genBracketExpr(p, n, d) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d) of nkDotExpr: genRecordField(p, n, d) of nkCheckedFieldExpr: genCheckedRecordField(p, n, d) of nkBlockExpr, nkBlockStmt: genBlock(p, n, d) of nkStmtListExpr: genStmtListExpr(p, n, d) of nkStmtList: genStmtList(p, n) of nkIfExpr, nkIfStmt: genIf(p, n, d) of nkWhen: # This should be a "when nimvm" node. expr(p, n.sons[1].sons[0], d) of nkObjDownConv: downConv(p, n, d) of nkObjUpConv: upConv(p, n, d) of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF") of nkChckRange64: genRangeChck(p, n, d, "chckRange64") of nkChckRange: genRangeChck(p, n, d, "chckRange") of nkStringToCString: convStrToCStr(p, n, d) of nkCStringToString: convCStrToStr(p, n, d) of nkLambdaKinds: var sym = n.sons[namePos].sym genProc(p.module, sym) if sym.loc.r == nil or sym.loc.lode == nil: internalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of nkClosure: genClosure(p, n, d) of nkEmpty: discard of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) of nkConstSection: genConstStmt(p, n) of nkForStmt: internalError(n.info, "for statement not eliminated") of nkCaseStmt: genCase(p, n, d) of nkReturnStmt: genReturnStmt(p, n) of nkBreakStmt: genBreakStmt(p, n) of nkAsgn: if nfPreventCg notin n.flags: genAsgn(p, n, fastAsgn=false) of nkFastAsgn: if nfPreventCg notin n.flags: # transf is overly aggressive with 'nkFastAsgn', so we work around here. # See tests/run/tcnstseq3 for an example that would fail otherwise. genAsgn(p, n, fastAsgn=p.prc != nil) of nkDiscardStmt: let ex = n[0] if ex.kind != nkEmpty: genLineDir(p, n) var a: TLoc if ex.kind in nkCallKinds and (ex[0].kind != nkSym or ex[0].sym.magic == mNone): # bug #6037: do not assign to a temp in C++ mode: incl a.flags, lfSingleUse genCall(p, ex, a) if lfSingleUse notin a.flags: line(p, cpsStmts, a.r & ";" & tnl) else: initLocExpr(p, ex, a) of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt: if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions: genTryCpp(p, n, d) else: genTry(p, n, d) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection: # we have to emit the type information for object types here to support # separate compilation: genTypeSection(p.module, n) of nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef: discard of nkPragma: genPragma(p, n) of nkPragmaBlock: expr(p, n.lastSon, d) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: if n.sons[genericParamsPos].kind == nkEmpty: var prc = n.sons[namePos].sym # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if (not emitLazily(prc)) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (prc.kind == skMethod): # we have not only the header: if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: genProc(p.module, prc) of nkParForStmt: genParForStmt(p, n) of nkState: genState(p, n) of nkGotoState: genGotoState(p, n) of nkBreakState: genBreakState(p, n) else: internalError(n.info, "expr(" & $n.kind & "); unknown node kind") proc genNamedConstExpr(p: BProc, n: PNode): Rope = if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1]) else: result = genConstExpr(p, n) proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = var t = skipTypes(typ, abstractRange-{tyTypeDesc}) case t.kind of tyBool: result = rope"NIM_FALSE" of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0" of tyFloat..tyFloat128: result = rope"0.0" of tyCString, tyString, tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: result = rope"NIM_NIL" of tyProc: if t.callConv != ccClosure: result = rope"NIM_NIL" else: result = rope"{NIM_NIL, NIM_NIL}" of tyObject: if not isObjLackingTypeField(t) and not p.module.compileToCpp: result = "{{$1}}" % [genTypeInfo(p.module, t, info)] else: result = rope"{}" of tyTuple: result = rope"{" for i in 0 ..< typ.len: if i > 0: result.add ", " result.add getDefaultValue(p, typ.sons[i], info) result.add "}" of tyArray: result = rope"{}" of tySet: if mapType(t) == ctArray: result = rope"{}" else: result = rope"0" else: globalError(info, "cannot create null element for: " & $t.kind) proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) = case obj.kind of nkRecList: for i in countup(0, sonsLen(obj) - 1): getNullValueAux(p, t, obj.sons[i], cons, result, count) of nkRecCase: getNullValueAux(p, t, obj.sons[0], cons, result, count) for i in countup(1, sonsLen(obj) - 1): getNullValueAux(p, t, lastSon(obj.sons[i]), cons, result, count) of nkSym: if count > 0: result.add ", " inc count let field = obj.sym for i in 1..<cons.len: if cons[i].kind == nkExprColonExpr: if cons[i][0].sym.name.id == field.name.id: result.add genConstExpr(p, cons[i][1]) return elif i == field.position: result.add genConstExpr(p, cons[i]) return # not found, produce default value: result.add getDefaultValue(p, field.typ, cons.info) else: localError(cons.info, "cannot create null element for: " & $obj) proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Rope; count: var int) = var base = t.sons[0] let oldRes = result if not p.module.compileToCpp: result.add "{" let oldcount = count if base != nil: base = skipTypes(base, skipPtrs) getNullValueAuxT(p, orig, base, base.n, cons, result, count) elif not isObjLackingTypeField(t) and not p.module.compileToCpp: addf(result, "$1", [genTypeInfo(p.module, orig, obj.info)]) inc count getNullValueAux(p, t, obj, cons, result, count) # do not emit '{}' as that is not valid C: if oldcount == count: result = oldres elif not p.module.compileToCpp: result.add "}" proc genConstObjConstr(p: BProc; n: PNode): Rope = result = nil let t = n.typ.skipTypes(abstractInst) var count = 0 #if not isObjLackingTypeField(t) and not p.module.compileToCpp: # addf(result, "{$1}", [genTypeInfo(p.module, t)]) # inc count getNullValueAuxT(p, t, t, t.n, n, result, count) if p.module.compileToCpp: result = "{$1}$n" % [result] proc genConstSimpleList(p: BProc, n: PNode): Rope = var length = sonsLen(n) result = rope("{") let t = n.typ.skipTypes(abstractInst) for i in countup(0, length - 2): addf(result, "$1,$n", [genNamedConstExpr(p, n.sons[i])]) if length > 0: add(result, genNamedConstExpr(p, n.sons[length - 1])) addf(result, "}$n", []) proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = var data = "{{$1, $1}" % [n.len.rope] if n.len > 0: # array part needs extra curlies: data.add(", {") for i in countup(0, n.len - 1): if i > 0: data.addf(",$n", []) data.add genConstExpr(p, n.sons[i]) data.add("}") data.add("}") result = getTempName(p.module) let base = t.skipTypes(abstractInst).sons[0] appcg(p.module, cfsData, "NIM_CONST struct {$n" & " #TGenericSeq Sup;$n" & " $1 data[$2];$n" & "} $3 = $4;$n", [ getTypeDesc(p.module, base), n.len.rope, result, data]) result = "(($1)&$2)" % [getTypeDesc(p.module, t), result] proc genConstExpr(p: BProc, n: PNode): Rope = case n.kind of nkHiddenStdConv, nkHiddenSubConv: result = genConstExpr(p, n.sons[1]) of nkCurly: var cs: TBitSet toBitSet(n, cs) result = genRawSetData(cs, int(getSize(n.typ))) of nkBracket, nkPar, nkClosure: var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, n.typ) elif t.kind == tyProc and t.callConv == ccClosure and not n.sons.isNil and n.sons[0].kind == nkNilLit and n.sons[1].kind == nkNilLit: # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} # this behaviour is needed since closure_var = nil must be # expanded to {NIM_NIL,NIM_NIL} # in VM closures are initialized with nkPar(nkNilLit, nkNilLit) # leading to duplicate code like this: # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}" result = ~"{NIM_NIL,NIM_NIL}" else: result = genConstSimpleList(p, n) of nkObjConstr: result = genConstObjConstr(p, n) else: var d: TLoc initLocExpr(p, n, d) result = rdLoc(d)