transf.nim 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This module implements the transformator. It transforms the syntax tree
  10. # to ease the work of the code generators. Does some transformations:
  11. #
  12. # * inlines iterators
  13. # * inlines constants
  14. # * performs constant folding
  15. # * converts "continue" to "break"; disambiguates "break"
  16. # * introduces method dispatchers
  17. # * performs lambda lifting for closure support
  18. # * transforms 'defer' into a 'try finally' statement
  19. import
  20. options, ast, astalgo, trees, msgs,
  21. idents, renderer, types, semfold, magicsys, cgmeth,
  22. lowerings, injectdestructors, liftlocals,
  23. modulegraphs, lineinfos
  24. proc transformBody*(g: ModuleGraph, prc: PSym, cache = true;
  25. noDestructors = false): PNode
  26. import closureiters, lambdalifting
  27. type
  28. PTransNode* = distinct PNode
  29. PTransCon = ref TTransCon
  30. TTransCon{.final.} = object # part of TContext; stackable
  31. mapping: TIdNodeTable # mapping from symbols to nodes
  32. owner: PSym # current owner
  33. forStmt: PNode # current for stmt
  34. forLoopBody: PTransNode # transformed for loop body
  35. yieldStmts: int # we count the number of yield statements,
  36. # because we need to introduce new variables
  37. # if we encounter the 2nd yield statement
  38. next: PTransCon # for stacking
  39. TTransfContext = object of TPassContext
  40. module: PSym
  41. transCon: PTransCon # top of a TransCon stack
  42. inlining: int # > 0 if we are in inlining context (copy vars)
  43. nestedProcs: int # > 0 if we are in a nested proc
  44. contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
  45. deferDetected, tooEarly, needsDestroyPass, noDestructors: bool
  46. graph: ModuleGraph
  47. PTransf = ref TTransfContext
  48. proc newTransNode(a: PNode): PTransNode {.inline.} =
  49. result = PTransNode(shallowCopy(a))
  50. proc newTransNode(kind: TNodeKind, info: TLineInfo,
  51. sons: int): PTransNode {.inline.} =
  52. var x = newNodeI(kind, info)
  53. newSeq(x.sons, sons)
  54. result = x.PTransNode
  55. proc newTransNode(kind: TNodeKind, n: PNode,
  56. sons: int): PTransNode {.inline.} =
  57. var x = newNodeIT(kind, n.info, n.typ)
  58. newSeq(x.sons, sons)
  59. x.typ = n.typ
  60. # x.flags = n.flags
  61. result = x.PTransNode
  62. proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
  63. proc len(a: PTransNode): int {.inline.} = len(a.PNode)
  64. proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
  65. var n = PNode(a)
  66. n.sons[i] = PNode(x)
  67. proc `[]=`(a: PTransNode, i: BackwardsIndex, x: PTransNode) {.inline.} =
  68. `[]=`(a, a.len - i.int, x)
  69. proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
  70. var n = PNode(a)
  71. result = n.sons[i].PTransNode
  72. proc `[]`(a: PTransNode, i: BackwardsIndex): PTransNode {.inline.} =
  73. `[]`(a, a.len - i.int)
  74. proc newTransCon(owner: PSym): PTransCon =
  75. assert owner != nil
  76. new(result)
  77. initIdNodeTable(result.mapping)
  78. result.owner = owner
  79. proc pushTransCon(c: PTransf, t: PTransCon) =
  80. t.next = c.transCon
  81. c.transCon = t
  82. proc popTransCon(c: PTransf) =
  83. if (c.transCon == nil): internalError(c.graph.config, "popTransCon")
  84. c.transCon = c.transCon.next
  85. proc getCurrOwner(c: PTransf): PSym =
  86. if c.transCon != nil: result = c.transCon.owner
  87. else: result = c.module
  88. proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
  89. let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), getCurrOwner(c), info)
  90. r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
  91. incl(r.flags, sfFromGeneric)
  92. let owner = getCurrOwner(c)
  93. if owner.isIterator and not c.tooEarly:
  94. result = freshVarForClosureIter(c.graph, r, owner)
  95. else:
  96. result = newSymNode(r)
  97. proc transform(c: PTransf, n: PNode): PTransNode
  98. proc transformSons(c: PTransf, n: PNode): PTransNode =
  99. result = newTransNode(n)
  100. for i in 0 ..< len(n):
  101. result[i] = transform(c, n.sons[i])
  102. proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PTransNode): PTransNode =
  103. result = newTransNode(kind, PNode(ri).info, 2)
  104. result[0] = PTransNode(le)
  105. result[1] = ri
  106. proc transformSymAux(c: PTransf, n: PNode): PNode =
  107. let s = n.sym
  108. if s.typ != nil and s.typ.callConv == ccClosure:
  109. if s.kind in routineKinds:
  110. discard transformBody(c.graph, s, true, c.noDestructors)
  111. if s.kind == skIterator:
  112. if c.tooEarly: return n
  113. else: return liftIterSym(c.graph, n, getCurrOwner(c))
  114. elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly:
  115. # top level .closure procs are still somewhat supported for 'Nake':
  116. return makeClosure(c.graph, s, nil, n.info)
  117. #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:
  118. # echo n.info, " come heer for ", c.tooEarly
  119. # if not c.tooEarly:
  120. var b: PNode
  121. var tc = c.transCon
  122. if sfBorrow in s.flags and s.kind in routineKinds:
  123. # simply exchange the symbol:
  124. b = s.getBody
  125. if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol")
  126. b = newSymNode(b.sym, n.info)
  127. else:
  128. b = n
  129. while tc != nil:
  130. result = idNodeTableGet(tc.mapping, b.sym)
  131. if result != nil:
  132. # this slightly convoluted way ensures the line info stays correct:
  133. if result.kind == nkSym:
  134. result = copyNode(result)
  135. result.info = n.info
  136. return
  137. tc = tc.next
  138. result = b
  139. proc transformSym(c: PTransf, n: PNode): PTransNode =
  140. result = PTransNode(transformSymAux(c, n))
  141. proc freshVar(c: PTransf; v: PSym): PNode =
  142. let owner = getCurrOwner(c)
  143. if owner.isIterator and not c.tooEarly:
  144. result = freshVarForClosureIter(c.graph, v, owner)
  145. else:
  146. var newVar = copySym(v)
  147. incl(newVar.flags, sfFromGeneric)
  148. newVar.owner = owner
  149. result = newSymNode(newVar)
  150. proc transformVarSection(c: PTransf, v: PNode): PTransNode =
  151. result = newTransNode(v)
  152. for i in 0 ..< len(v):
  153. var it = v.sons[i]
  154. if it.kind == nkCommentStmt:
  155. result[i] = PTransNode(it)
  156. elif it.kind == nkIdentDefs:
  157. if it.sons[0].kind == nkSym:
  158. internalAssert(c.graph.config, it.len == 3)
  159. let x = freshVar(c, it.sons[0].sym)
  160. idNodeTablePut(c.transCon.mapping, it.sons[0].sym, x)
  161. var defs = newTransNode(nkIdentDefs, it.info, 3)
  162. if importantComments(c.graph.config):
  163. # keep documentation information:
  164. PNode(defs).comment = it.comment
  165. defs[0] = x.PTransNode
  166. defs[1] = it.sons[1].PTransNode
  167. defs[2] = transform(c, it.sons[2])
  168. if x.kind == nkSym: x.sym.ast = defs[2].PNode
  169. result[i] = defs
  170. else:
  171. # has been transformed into 'param.x' for closure iterators, so just
  172. # transform it:
  173. result[i] = transform(c, it)
  174. else:
  175. if it.kind != nkVarTuple:
  176. internalError(c.graph.config, it.info, "transformVarSection: not nkVarTuple")
  177. var L = len(it)
  178. var defs = newTransNode(it.kind, it.info, L)
  179. for j in 0 .. L-3:
  180. if it[j].kind == nkSym:
  181. let x = freshVar(c, it.sons[j].sym)
  182. idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
  183. defs[j] = x.PTransNode
  184. else:
  185. defs[j] = transform(c, it[j])
  186. assert(it.sons[L-2].kind == nkEmpty)
  187. defs[L-2] = newNodeI(nkEmpty, it.info).PTransNode
  188. defs[L-1] = transform(c, it.sons[L-1])
  189. result[i] = defs
  190. proc transformConstSection(c: PTransf, v: PNode): PTransNode =
  191. result = PTransNode(v)
  192. when false:
  193. result = newTransNode(v)
  194. for i in 0 ..< len(v):
  195. var it = v.sons[i]
  196. if it.kind == nkCommentStmt:
  197. result[i] = PTransNode(it)
  198. else:
  199. if it.kind != nkConstDef: internalError(c.graph.config, it.info, "transformConstSection")
  200. if it.sons[0].kind != nkSym:
  201. debug it.sons[0]
  202. internalError(c.graph.config, it.info, "transformConstSection")
  203. result[i] = PTransNode(it)
  204. proc hasContinue(n: PNode): bool =
  205. case n.kind
  206. of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard
  207. of nkContinueStmt: result = true
  208. else:
  209. for i in 0 ..< len(n):
  210. if hasContinue(n.sons[i]): return true
  211. proc newLabel(c: PTransf, n: PNode): PSym =
  212. result = newSym(skLabel, nil, getCurrOwner(c), n.info)
  213. result.name = getIdent(c.graph.cache, genPrefix & $result.id)
  214. proc transformBlock(c: PTransf, n: PNode): PTransNode =
  215. var labl: PSym
  216. if c.inlining > 0:
  217. labl = newLabel(c, n[0])
  218. idNodeTablePut(c.transCon.mapping, n[0].sym, newSymNode(labl))
  219. else:
  220. labl =
  221. if n.sons[0].kind != nkEmpty:
  222. n.sons[0].sym # already named block? -> Push symbol on the stack
  223. else:
  224. newLabel(c, n)
  225. c.breakSyms.add(labl)
  226. result = transformSons(c, n)
  227. discard c.breakSyms.pop
  228. result[0] = newSymNode(labl).PTransNode
  229. proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
  230. # What if it contains "continue" and "break"? "break" needs
  231. # an explicit label too, but not the same!
  232. # We fix this here by making every 'break' belong to its enclosing loop
  233. # and changing all breaks that belong to a 'block' by annotating it with
  234. # a label (if it hasn't one already).
  235. if hasContinue(n):
  236. let labl = newLabel(c, n)
  237. c.contSyms.add(labl)
  238. result = newTransNode(nkBlockStmt, n.info, 2)
  239. result[0] = newSymNode(labl).PTransNode
  240. result[1] = transform(c, n)
  241. discard c.contSyms.pop()
  242. else:
  243. result = transform(c, n)
  244. proc transformWhile(c: PTransf; n: PNode): PTransNode =
  245. if c.inlining > 0:
  246. result = transformSons(c, n)
  247. else:
  248. let labl = newLabel(c, n)
  249. c.breakSyms.add(labl)
  250. result = newTransNode(nkBlockStmt, n.info, 2)
  251. result[0] = newSymNode(labl).PTransNode
  252. var body = newTransNode(n)
  253. for i in 0..n.len-2:
  254. body[i] = transform(c, n.sons[i])
  255. body[n.len-1] = transformLoopBody(c, n.sons[n.len-1])
  256. result[1] = body
  257. discard c.breakSyms.pop
  258. proc transformBreak(c: PTransf, n: PNode): PTransNode =
  259. result = transformSons(c, n)
  260. if n.sons[0].kind == nkEmpty and c.breakSyms.len > 0:
  261. let labl = c.breakSyms[c.breakSyms.high]
  262. result[0] = newSymNode(labl).PTransNode
  263. proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
  264. case n.kind
  265. of nkSym:
  266. result = transformSym(c, n)
  267. of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
  268. # nothing to be done for leaves:
  269. result = PTransNode(n)
  270. of nkVarSection, nkLetSection:
  271. result = transformVarSection(c, n)
  272. of nkClosure:
  273. # it can happen that for-loop-inlining produced a fresh
  274. # set of variables, including some computed environment
  275. # (bug #2604). We need to patch this environment here too:
  276. let a = n[1]
  277. if a.kind == nkSym:
  278. n.sons[1] = transformSymAux(c, a)
  279. return PTransNode(n)
  280. else:
  281. result = newTransNode(n)
  282. for i in 0 ..< len(n):
  283. result[i] = introduceNewLocalVars(c, n.sons[i])
  284. proc transformAsgn(c: PTransf, n: PNode): PTransNode =
  285. let rhs = n[1]
  286. if rhs.kind != nkTupleConstr:
  287. return transformSons(c, n)
  288. # Unpack the tuple assignment into N temporary variables and then pack them
  289. # into a tuple: this allows us to get the correct results even when the rhs
  290. # depends on the value of the lhs
  291. let letSection = newTransNode(nkLetSection, n.info, rhs.len)
  292. let newTupleConstr = newTransNode(nkTupleConstr, n.info, rhs.len)
  293. for i, field in rhs:
  294. let val = if field.kind == nkExprColonExpr: field[1] else: field
  295. let def = newTransNode(nkIdentDefs, field.info, 3)
  296. def[0] = PTransNode(newTemp(c, val.typ, field.info))
  297. def[1] = PTransNode(newNodeI(nkEmpty, field.info))
  298. def[2] = transform(c, val)
  299. letSection[i] = def
  300. # NOTE: We assume the constructor fields are in the correct order for the
  301. # given tuple type
  302. newTupleConstr[i] = def[0]
  303. PNode(newTupleConstr).typ = rhs.typ
  304. let asgnNode = newTransNode(nkAsgn, n.info, 2)
  305. asgnNode[0] = transform(c, n[0])
  306. asgnNode[1] = newTupleConstr
  307. result = newTransNode(nkStmtList, n.info, 2)
  308. result[0] = letSection
  309. result[1] = asgnNode
  310. proc transformYield(c: PTransf, n: PNode): PTransNode =
  311. proc asgnTo(lhs: PNode, rhs: PTransNode): PTransNode =
  312. # Choose the right assignment instruction according to the given ``lhs``
  313. # node since it may not be a nkSym (a stack-allocated skForVar) but a
  314. # nkDotExpr (a heap-allocated slot into the envP block)
  315. case lhs.kind:
  316. of nkSym:
  317. internalAssert c.graph.config, lhs.sym.kind == skForVar
  318. result = newAsgnStmt(c, nkFastAsgn, lhs, rhs)
  319. of nkDotExpr:
  320. result = newAsgnStmt(c, nkAsgn, lhs, rhs)
  321. else:
  322. internalAssert c.graph.config, false
  323. result = newTransNode(nkStmtList, n.info, 0)
  324. var e = n.sons[0]
  325. # c.transCon.forStmt.len == 3 means that there is one for loop variable
  326. # and thus no tuple unpacking:
  327. if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons
  328. if c.transCon.forStmt.len != 3:
  329. e = skipConv(e)
  330. if e.kind in {nkPar, nkTupleConstr}:
  331. for i in 0 ..< len(e):
  332. var v = e.sons[i]
  333. if v.kind == nkExprColonExpr: v = v.sons[1]
  334. if c.transCon.forStmt[i].kind == nkVarTuple:
  335. for j in 0 ..< len(c.transCon.forStmt[i])-1:
  336. let lhs = c.transCon.forStmt[i][j]
  337. let rhs = transform(c, newTupleAccess(c.graph, v, j))
  338. add(result, asgnTo(lhs, rhs))
  339. else:
  340. let lhs = c.transCon.forStmt.sons[i]
  341. let rhs = transform(c, v)
  342. add(result, asgnTo(lhs, rhs))
  343. else:
  344. # Unpack the tuple into the loop variables
  345. # XXX: BUG: what if `n` is an expression with side-effects?
  346. for i in 0 .. len(c.transCon.forStmt) - 3:
  347. let lhs = c.transCon.forStmt.sons[i]
  348. let rhs = transform(c, newTupleAccess(c.graph, e, i))
  349. add(result, asgnTo(lhs, rhs))
  350. else:
  351. if c.transCon.forStmt.sons[0].kind == nkVarTuple:
  352. for i in 0 ..< len(c.transCon.forStmt[0])-1:
  353. let lhs = c.transCon.forStmt[0][i]
  354. let rhs = transform(c, newTupleAccess(c.graph, e, i))
  355. add(result, asgnTo(lhs, rhs))
  356. else:
  357. let lhs = c.transCon.forStmt.sons[0]
  358. let rhs = transform(c, e)
  359. add(result, asgnTo(lhs, rhs))
  360. inc(c.transCon.yieldStmts)
  361. if c.transCon.yieldStmts <= 1:
  362. # common case
  363. add(result, c.transCon.forLoopBody)
  364. else:
  365. # we need to introduce new local variables:
  366. add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode))
  367. if result.len > 0:
  368. var changeNode = PNode(result[0])
  369. changeNode.info = c.transCon.forStmt.info
  370. for i, child in changeNode:
  371. child.info = changeNode.info
  372. proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
  373. result = transformSons(c, n)
  374. if c.graph.config.cmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return
  375. var n = result.PNode
  376. case n.sons[0].kind
  377. of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
  378. var m = n.sons[0].sons[0]
  379. if m.kind == a or m.kind == b:
  380. # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
  381. n.sons[0].sons[0] = m.sons[0]
  382. result = PTransNode(n.sons[0])
  383. if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
  384. PNode(result).typ = n.typ
  385. elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
  386. PNode(result).typ = toVar(PNode(result).typ)
  387. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  388. var m = n.sons[0].sons[1]
  389. if m.kind == a or m.kind == b:
  390. # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
  391. n.sons[0].sons[1] = m.sons[0]
  392. result = PTransNode(n.sons[0])
  393. if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
  394. PNode(result).typ = n.typ
  395. elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
  396. PNode(result).typ = toVar(PNode(result).typ)
  397. else:
  398. if n.sons[0].kind == a or n.sons[0].kind == b:
  399. # addr ( deref ( x )) --> x
  400. result = PTransNode(n.sons[0].sons[0])
  401. if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
  402. PNode(result).typ = n.typ
  403. proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
  404. ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
  405. ## a closure.
  406. # we cannot generate a proper thunk here for GC-safety reasons
  407. # (see internal documentation):
  408. if c.graph.config.cmd == cmdCompileToJS: return prc
  409. result = newNodeIT(nkClosure, prc.info, dest)
  410. var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
  411. conv.add(newNodeI(nkEmpty, prc.info))
  412. conv.add(prc)
  413. if prc.kind == nkClosure:
  414. internalError(c.graph.config, prc.info, "closure to closure created")
  415. result.add(conv)
  416. result.add(newNodeIT(nkNilLit, prc.info, getSysType(c.graph, prc.info, tyNil)))
  417. proc transformConv(c: PTransf, n: PNode): PTransNode =
  418. # numeric types need range checks:
  419. var dest = skipTypes(n.typ, abstractVarRange)
  420. var source = skipTypes(n.sons[1].typ, abstractVarRange)
  421. case dest.kind
  422. of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
  423. # we don't include uint and uint64 here as these are no ordinal types ;-)
  424. if not isOrdinalType(source):
  425. # float -> int conversions. ugh.
  426. result = transformSons(c, n)
  427. elif firstOrd(c.graph.config, n.typ) <= firstOrd(c.graph.config, n.sons[1].typ) and
  428. lastOrd(c.graph.config, n.sons[1].typ) <= lastOrd(c.graph.config, n.typ):
  429. # BUGFIX: simply leave n as it is; we need a nkConv node,
  430. # but no range check:
  431. result = transformSons(c, n)
  432. else:
  433. # generate a range check:
  434. if dest.kind == tyInt64 or source.kind == tyInt64:
  435. result = newTransNode(nkChckRange64, n, 3)
  436. else:
  437. result = newTransNode(nkChckRange, n, 3)
  438. dest = skipTypes(n.typ, abstractVar)
  439. result[0] = transform(c, n.sons[1])
  440. result[1] = newIntTypeNode(firstOrd(c.graph.config, dest), dest).PTransNode
  441. result[2] = newIntTypeNode(lastOrd(c.graph.config, dest), dest).PTransNode
  442. of tyFloat..tyFloat128:
  443. # XXX int64 -> float conversion?
  444. if skipTypes(n.typ, abstractVar).kind == tyRange:
  445. result = newTransNode(nkChckRangeF, n, 3)
  446. dest = skipTypes(n.typ, abstractVar)
  447. result[0] = transform(c, n.sons[1])
  448. result[1] = copyTree(dest.n.sons[0]).PTransNode
  449. result[2] = copyTree(dest.n.sons[1]).PTransNode
  450. else:
  451. result = transformSons(c, n)
  452. of tyOpenArray, tyVarargs:
  453. result = transform(c, n.sons[1])
  454. PNode(result).typ = takeType(n.typ, n.sons[1].typ)
  455. #echo n.info, " came here and produced ", typeToString(PNode(result).typ),
  456. # " from ", typeToString(n.typ), " and ", typeToString(n.sons[1].typ)
  457. of tyCString:
  458. if source.kind == tyString:
  459. result = newTransNode(nkStringToCString, n, 1)
  460. result[0] = transform(c, n.sons[1])
  461. else:
  462. result = transformSons(c, n)
  463. of tyString:
  464. if source.kind == tyCString:
  465. result = newTransNode(nkCStringToString, n, 1)
  466. result[0] = transform(c, n.sons[1])
  467. else:
  468. result = transformSons(c, n)
  469. of tyRef, tyPtr:
  470. dest = skipTypes(dest, abstractPtrs)
  471. source = skipTypes(source, abstractPtrs)
  472. if source.kind == tyObject:
  473. var diff = inheritanceDiff(dest, source)
  474. if diff < 0:
  475. result = newTransNode(nkObjUpConv, n, 1)
  476. result[0] = transform(c, n.sons[1])
  477. elif diff > 0 and diff != high(int):
  478. result = newTransNode(nkObjDownConv, n, 1)
  479. result[0] = transform(c, n.sons[1])
  480. else:
  481. result = transform(c, n.sons[1])
  482. else:
  483. result = transformSons(c, n)
  484. of tyObject:
  485. var diff = inheritanceDiff(dest, source)
  486. if diff < 0:
  487. result = newTransNode(nkObjUpConv, n, 1)
  488. result[0] = transform(c, n.sons[1])
  489. elif diff > 0 and diff != high(int):
  490. result = newTransNode(nkObjDownConv, n, 1)
  491. result[0] = transform(c, n.sons[1])
  492. else:
  493. result = transform(c, n.sons[1])
  494. of tyGenericParam, tyOrdinal:
  495. result = transform(c, n.sons[1])
  496. # happens sometimes for generated assignments, etc.
  497. of tyProc:
  498. result = transformSons(c, n)
  499. if dest.callConv == ccClosure and source.callConv == ccDefault:
  500. result = generateThunk(c, result[1].PNode, dest).PTransNode
  501. else:
  502. result = transformSons(c, n)
  503. type
  504. TPutArgInto = enum
  505. paDirectMapping, paFastAsgn, paFastAsgnTakeTypeFromArg
  506. paVarAsgn, paComplexOpenarray
  507. proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
  508. # This analyses how to treat the mapping "formal <-> arg" in an
  509. # inline context.
  510. if formal.kind == tyTypeDesc: return paDirectMapping
  511. if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
  512. case arg.kind
  513. of nkStmtListExpr:
  514. return paComplexOpenarray
  515. of nkBracket:
  516. return paFastAsgnTakeTypeFromArg
  517. else:
  518. return paDirectMapping # XXX really correct?
  519. # what if ``arg`` has side-effects?
  520. case arg.kind
  521. of nkEmpty..nkNilLit:
  522. result = paDirectMapping
  523. of nkDotExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr:
  524. result = putArgInto(arg[0], formal)
  525. of nkCurly, nkBracket:
  526. for i in 0..<arg.len:
  527. if putArgInto(arg[i], formal) != paDirectMapping:
  528. return paFastAsgn
  529. result = paDirectMapping
  530. of nkPar, nkTupleConstr, nkObjConstr:
  531. for i in 0..<arg.len:
  532. let a = if arg[i].kind == nkExprColonExpr: arg[i][1]
  533. else: arg[0]
  534. if putArgInto(a, formal) != paDirectMapping:
  535. return paFastAsgn
  536. result = paDirectMapping
  537. else:
  538. if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn
  539. else: result = paFastAsgn
  540. proc findWrongOwners(c: PTransf, n: PNode) =
  541. if n.kind == nkVarSection:
  542. let x = n.sons[0].sons[0]
  543. if x.kind == nkSym and x.sym.owner != getCurrOwner(c):
  544. internalError(c.graph.config, x.info, "bah " & x.sym.name.s & " " &
  545. x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
  546. else:
  547. for i in 0 ..< safeLen(n): findWrongOwners(c, n.sons[i])
  548. proc transformFor(c: PTransf, n: PNode): PTransNode =
  549. # generate access statements for the parameters (unless they are constant)
  550. # put mapping from formal parameters to actual parameters
  551. if n.kind != nkForStmt: internalError(c.graph.config, n.info, "transformFor")
  552. var length = len(n)
  553. var call = n.sons[length - 2]
  554. let labl = newLabel(c, n)
  555. result = newTransNode(nkBlockStmt, n.info, 2)
  556. result[0] = newSymNode(labl).PTransNode
  557. if call.typ.isNil:
  558. # see bug #3051
  559. result[1] = newNode(nkEmpty).PTransNode
  560. return result
  561. c.breakSyms.add(labl)
  562. if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
  563. call.sons[0].typ.callConv == ccClosure:
  564. result[1] = n.PTransNode
  565. result[1][^1] = transformLoopBody(c, n[^1])
  566. result[1][^2] = transform(c, n[^2])
  567. result[1] = lambdalifting.liftForLoop(c.graph, result[1].PNode, getCurrOwner(c)).PTransNode
  568. discard c.breakSyms.pop
  569. return result
  570. #echo "transforming: ", renderTree(n)
  571. var stmtList = newTransNode(nkStmtList, n.info, 0)
  572. result[1] = stmtList
  573. var loopBody = transformLoopBody(c, n.sons[length-1])
  574. discard c.breakSyms.pop
  575. var v = newNodeI(nkVarSection, n.info)
  576. for i in 0 .. length - 3:
  577. if n[i].kind == nkVarTuple:
  578. for j in 0 ..< len(n[i])-1:
  579. addVar(v, copyTree(n[i][j])) # declare new vars
  580. else:
  581. addVar(v, copyTree(n.sons[i])) # declare new vars
  582. add(stmtList, v.PTransNode)
  583. # Bugfix: inlined locals belong to the invoking routine, not to the invoked
  584. # iterator!
  585. let iter = call.sons[0].sym
  586. var newC = newTransCon(getCurrOwner(c))
  587. newC.forStmt = n
  588. newC.forLoopBody = loopBody
  589. # this can fail for 'nimsuggest' and 'check':
  590. if iter.kind != skIterator: return result
  591. # generate access statements for the parameters (unless they are constant)
  592. pushTransCon(c, newC)
  593. for i in 1 ..< len(call):
  594. var arg = transform(c, call.sons[i]).PNode
  595. let ff = skipTypes(iter.typ, abstractInst)
  596. # can happen for 'nim check':
  597. if i >= ff.n.len: return result
  598. var formal = ff.n.sons[i].sym
  599. let pa = putArgInto(arg, formal.typ)
  600. case pa
  601. of paDirectMapping:
  602. idNodeTablePut(newC.mapping, formal, arg)
  603. of paFastAsgn, paFastAsgnTakeTypeFromArg:
  604. var t = formal.typ
  605. if pa == paFastAsgnTakeTypeFromArg:
  606. t = arg.typ
  607. elif formal.ast != nil and formal.ast.typ.destructor != nil and t.destructor == nil:
  608. t = formal.ast.typ # better use the type that actually has a destructor.
  609. elif t.destructor == nil and arg.typ.destructor != nil:
  610. t = arg.typ
  611. # generate a temporary and produce an assignment statement:
  612. var temp = newTemp(c, t, formal.info)
  613. addVar(v, temp)
  614. add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode))
  615. idNodeTablePut(newC.mapping, formal, temp)
  616. of paVarAsgn:
  617. assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
  618. idNodeTablePut(newC.mapping, formal, arg)
  619. # XXX BUG still not correct if the arg has a side effect!
  620. of paComplexOpenarray:
  621. let typ = newType(tySequence, formal.owner)
  622. addSonSkipIntLit(typ, formal.typ.sons[0])
  623. var temp = newTemp(c, typ, formal.info)
  624. addVar(v, temp)
  625. add(stmtList, newAsgnStmt(c, nkFastAsgn, temp, arg.PTransNode))
  626. idNodeTablePut(newC.mapping, formal, temp)
  627. let body = transformBody(c.graph, iter, true, c.noDestructors)
  628. pushInfoContext(c.graph.config, n.info)
  629. inc(c.inlining)
  630. add(stmtList, transform(c, body))
  631. #findWrongOwners(c, stmtList.pnode)
  632. dec(c.inlining)
  633. popInfoContext(c.graph.config)
  634. popTransCon(c)
  635. # echo "transformed: ", stmtList.PNode.renderTree
  636. proc transformCase(c: PTransf, n: PNode): PTransNode =
  637. # removes `elif` branches of a case stmt
  638. # adds ``else: nil`` if needed for the code generator
  639. result = newTransNode(nkCaseStmt, n, 0)
  640. var ifs = PTransNode(nil)
  641. for i in 0 .. len(n)-1:
  642. var it = n.sons[i]
  643. var e = transform(c, it)
  644. case it.kind
  645. of nkElifBranch:
  646. if ifs.PNode == nil:
  647. # Generate the right node depending on whether `n` is used as a stmt or
  648. # as an expr
  649. let kind = if n.typ != nil: nkIfExpr else: nkIfStmt
  650. ifs = newTransNode(kind, it.info, 0)
  651. ifs.PNode.typ = n.typ
  652. ifs.add(e)
  653. of nkElse:
  654. if ifs.PNode == nil: result.add(e)
  655. else: ifs.add(e)
  656. else:
  657. result.add(e)
  658. if ifs.PNode != nil:
  659. var elseBranch = newTransNode(nkElse, n.info, 1)
  660. elseBranch[0] = ifs
  661. result.add(elseBranch)
  662. elif result.PNode.lastSon.kind != nkElse and not (
  663. skipTypes(n.sons[0].typ, abstractVarRange).kind in
  664. {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64}):
  665. # fix a stupid code gen bug by normalizing:
  666. var elseBranch = newTransNode(nkElse, n.info, 1)
  667. elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
  668. add(result, elseBranch)
  669. proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
  670. # XXX this is really bad; transf should use a proper AST visitor
  671. if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType:
  672. result = n.PTransNode
  673. else:
  674. result = newTransNode(n)
  675. for i in 0 ..< n.len:
  676. result[i] = transform(c, skipConv(n.sons[i]))
  677. proc getMergeOp(n: PNode): PSym =
  678. case n.kind
  679. of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
  680. nkCallStrLit:
  681. if n.sons[0].kind == nkSym and n.sons[0].sym.magic == mConStrStr:
  682. result = n.sons[0].sym
  683. else: discard
  684. proc flattenTreeAux(d, a: PNode, op: PSym) =
  685. let op2 = getMergeOp(a)
  686. if op2 != nil and
  687. (op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
  688. for i in 1 ..< len(a): flattenTreeAux(d, a.sons[i], op)
  689. else:
  690. addSon(d, copyTree(a))
  691. proc flattenTree(root: PNode): PNode =
  692. let op = getMergeOp(root)
  693. if op != nil:
  694. result = copyNode(root)
  695. addSon(result, copyTree(root.sons[0]))
  696. flattenTreeAux(result, root, op)
  697. else:
  698. result = root
  699. proc transformCall(c: PTransf, n: PNode): PTransNode =
  700. var n = flattenTree(n)
  701. let op = getMergeOp(n)
  702. let magic = getMagic(n)
  703. if op != nil and op.magic != mNone and n.len >= 3:
  704. result = newTransNode(nkCall, n, 0)
  705. add(result, transform(c, n.sons[0]))
  706. var j = 1
  707. while j < len(n):
  708. var a = transform(c, n.sons[j]).PNode
  709. inc(j)
  710. if isConstExpr(a):
  711. while (j < len(n)):
  712. let b = transform(c, n.sons[j]).PNode
  713. if not isConstExpr(b): break
  714. a = evalOp(op.magic, n, a, b, nil, c.graph)
  715. inc(j)
  716. add(result, a.PTransNode)
  717. if len(result) == 2: result = result[1]
  718. elif magic == mAddr:
  719. result = newTransNode(nkAddr, n, 1)
  720. result[0] = n[1].PTransNode
  721. result = transformAddrDeref(c, result.PNode, nkDerefExpr, nkHiddenDeref)
  722. elif magic in {mNBindSym, mTypeOf, mRunnableExamples}:
  723. # for bindSym(myconst) we MUST NOT perform constant folding:
  724. result = n.PTransNode
  725. elif magic == mProcCall:
  726. # but do not change to its dispatcher:
  727. result = transformSons(c, n[1])
  728. elif magic == mStrToStr:
  729. result = transform(c, n[1])
  730. else:
  731. let s = transformSons(c, n).PNode
  732. # bugfix: check after 'transformSons' if it's still a method call:
  733. # use the dispatcher for the call:
  734. if s.sons[0].kind == nkSym and s.sons[0].sym.kind == skMethod:
  735. when false:
  736. let t = lastSon(s.sons[0].sym.ast)
  737. if t.kind != nkSym or sfDispatcher notin t.sym.flags:
  738. methodDef(s.sons[0].sym, false)
  739. result = methodCall(s, c.graph.config).PTransNode
  740. else:
  741. result = s.PTransNode
  742. proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
  743. if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config):
  744. let excTypeNode = n[0][1]
  745. let actions = newTransNode(nkStmtListExpr, n[1], 2)
  746. # Generating `let exc = (excType)(getCurrentException())`
  747. # -> getCurrentException()
  748. let excCall = PTransNode(callCodegenProc(c.graph, "getCurrentException"))
  749. # -> (excType)
  750. let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
  751. convNode[0] = PTransNode(newNodeI(nkEmpty, n.info))
  752. convNode[1] = excCall
  753. PNode(convNode).typ = excTypeNode.typ.toRef()
  754. # -> let exc = ...
  755. let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
  756. identDefs[0] = PTransNode(n[0][2])
  757. identDefs[1] = PTransNode(newNodeI(nkEmpty, n.info))
  758. identDefs[2] = convNode
  759. let letSection = newTransNode(nkLetSection, n[1].info, 1)
  760. letSection[0] = identDefs
  761. # Place the let statement and body of the 'except' branch into new stmtList.
  762. actions[0] = letSection
  763. actions[1] = transform(c, n[1])
  764. # Overwrite 'except' branch body with our stmtList.
  765. result = newTransNode(nkExceptBranch, n[1].info, 2)
  766. # Replace the `Exception as foobar` with just `Exception`.
  767. result[0] = transform(c, n[0][1])
  768. result[1] = actions
  769. else:
  770. result = transformSons(c, n)
  771. proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
  772. # symbols that expand to a complex constant (array, etc.) should not be
  773. # inlined, unless it's the empty array:
  774. result = orig.kind == nkSym and
  775. cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and
  776. cnst.len != 0
  777. proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode =
  778. result = n
  779. for i in 0 ..< n.safeLen:
  780. result.sons[i] = commonOptimizations(g, c, n.sons[i])
  781. var op = getMergeOp(n)
  782. if (op != nil) and (op.magic != mNone) and (len(n) >= 3):
  783. result = newNodeIT(nkCall, n.info, n.typ)
  784. add(result, n.sons[0])
  785. var args = newNode(nkArgList)
  786. flattenTreeAux(args, n, op)
  787. var j = 0
  788. while j < len(args):
  789. var a = args.sons[j]
  790. inc(j)
  791. if isConstExpr(a):
  792. while j < len(args):
  793. let b = args.sons[j]
  794. if not isConstExpr(b): break
  795. a = evalOp(op.magic, result, a, b, nil, g)
  796. inc(j)
  797. add(result, a)
  798. if len(result) == 2: result = result[1]
  799. else:
  800. var cnst = getConstExpr(c, n, g)
  801. # we inline constants if they are not complex constants:
  802. if cnst != nil and not dontInlineConstant(n, cnst):
  803. result = cnst
  804. else:
  805. result = n
  806. proc hoistParamsUsedInDefault(c: PTransf, call, letSection, defExpr: PNode): PNode =
  807. # This takes care of complicated signatures such as:
  808. # proc foo(a: int, b = a)
  809. # proc bar(a: int, b: int, c = a + b)
  810. #
  811. # The recursion may confuse you. It performs two duties:
  812. #
  813. # 1) extracting all referenced params from default expressions
  814. # into a let section preceding the call
  815. #
  816. # 2) replacing the "references" within the default expression
  817. # with these extracted skLet symbols.
  818. #
  819. # The first duty is carried out directly in the code here, while the second
  820. # duty is activated by returning a non-nil value. The caller is responsible
  821. # for replacing the input to the function with the returned non-nil value.
  822. # (which is the hoisted symbol)
  823. if defExpr.kind == nkSym:
  824. if defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
  825. let paramPos = defExpr.sym.position + 1
  826. if call[paramPos].kind == nkSym and sfHoisted in call[paramPos].sym.flags:
  827. # Already hoisted, we still need to return it in order to replace the
  828. # placeholder expression in the default value.
  829. return call[paramPos]
  830. let hoistedVarSym = hoistExpr(letSection,
  831. call[paramPos],
  832. getIdent(c.graph.cache, genPrefix),
  833. c.transCon.owner).newSymNode
  834. call[paramPos] = hoistedVarSym
  835. return hoistedVarSym
  836. else:
  837. for i in 0..<defExpr.safeLen:
  838. let hoisted = hoistParamsUsedInDefault(c, call, letSection, defExpr[i])
  839. if hoisted != nil: defExpr[i] = hoisted
  840. proc transform(c: PTransf, n: PNode): PTransNode =
  841. when false:
  842. var oldDeferAnchor: PNode
  843. if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
  844. nkElseExpr, nkElse, nkForStmt, nkWhileStmt, nkFinally,
  845. nkBlockStmt, nkBlockExpr}:
  846. oldDeferAnchor = c.deferAnchor
  847. c.deferAnchor = n
  848. if (n.typ != nil and tfHasAsgn in n.typ.flags) or
  849. optSeqDestructors in c.graph.config.globalOptions:
  850. c.needsDestroyPass = true
  851. case n.kind
  852. of nkSym:
  853. result = transformSym(c, n)
  854. of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom:
  855. # nothing to be done for leaves:
  856. result = PTransNode(n)
  857. of nkBracketExpr: result = transformArrayAccess(c, n)
  858. of procDefs:
  859. var s = n.sons[namePos].sym
  860. if n.typ != nil and s.typ.callConv == ccClosure:
  861. result = transformSym(c, n.sons[namePos])
  862. # use the same node as before if still a symbol:
  863. if result.PNode.kind == nkSym: result = PTransNode(n)
  864. else:
  865. result = PTransNode(n)
  866. of nkMacroDef:
  867. # XXX no proper closure support yet:
  868. when false:
  869. if n.sons[genericParamsPos].kind == nkEmpty:
  870. var s = n.sons[namePos].sym
  871. n.sons[bodyPos] = PNode(transform(c, s.getBody))
  872. if n.kind == nkMethodDef: methodDef(s, false)
  873. result = PTransNode(n)
  874. of nkForStmt:
  875. result = transformFor(c, n)
  876. of nkParForStmt:
  877. result = transformSons(c, n)
  878. of nkCaseStmt:
  879. result = transformCase(c, n)
  880. of nkWhileStmt: result = transformWhile(c, n)
  881. of nkBlockStmt, nkBlockExpr:
  882. result = transformBlock(c, n)
  883. of nkDefer:
  884. c.deferDetected = true
  885. result = transformSons(c, n)
  886. when false:
  887. let deferPart = newNodeI(nkFinally, n.info)
  888. deferPart.add n.sons[0]
  889. let tryStmt = newNodeI(nkTryStmt, n.info)
  890. if c.deferAnchor.isNil:
  891. tryStmt.add c.root
  892. c.root = tryStmt
  893. result = PTransNode(tryStmt)
  894. else:
  895. # modify the corresponding *action*, don't rely on nkStmtList:
  896. let L = c.deferAnchor.len-1
  897. tryStmt.add c.deferAnchor.sons[L]
  898. c.deferAnchor.sons[L] = tryStmt
  899. result = newTransNode(nkCommentStmt, n.info, 0)
  900. tryStmt.addSon(deferPart)
  901. # disable the original 'defer' statement:
  902. n.kind = nkEmpty
  903. of nkContinueStmt:
  904. result = PTransNode(newNodeI(nkBreakStmt, n.info))
  905. var labl = c.contSyms[c.contSyms.high]
  906. add(result, PTransNode(newSymNode(labl)))
  907. of nkBreakStmt: result = transformBreak(c, n)
  908. of nkCallKinds:
  909. result = transformCall(c, n)
  910. var call = result.PNode
  911. if nfDefaultRefsParam in call.flags:
  912. # We've found a default value that references another param.
  913. # See the notes in `hoistParamsUsedInDefault` for more details.
  914. var hoistedParams = newNodeI(nkLetSection, call.info, 0)
  915. for i in 1 ..< call.len:
  916. let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i])
  917. if hoisted != nil: call[i] = hoisted
  918. result = newTree(nkStmtListExpr, hoistedParams, call).PTransNode
  919. PNode(result).typ = call.typ
  920. of nkAddr, nkHiddenAddr:
  921. result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
  922. of nkDerefExpr, nkHiddenDeref:
  923. result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
  924. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  925. result = transformConv(c, n)
  926. of nkDiscardStmt:
  927. result = PTransNode(n)
  928. if n.sons[0].kind != nkEmpty:
  929. result = transformSons(c, n)
  930. if isConstExpr(PNode(result).sons[0]):
  931. # ensure that e.g. discard "some comment" gets optimized away
  932. # completely:
  933. result = PTransNode(newNode(nkCommentStmt))
  934. of nkCommentStmt, nkTemplateDef, nkImportStmt, nkStaticStmt,
  935. nkExportStmt, nkExportExceptStmt:
  936. return n.PTransNode
  937. of nkConstSection:
  938. # do not replace ``const c = 3`` with ``const 3 = 3``
  939. return transformConstSection(c, n)
  940. of nkTypeSection, nkTypeOfExpr:
  941. # no need to transform type sections:
  942. return PTransNode(n)
  943. of nkVarSection, nkLetSection:
  944. if c.inlining > 0:
  945. # we need to copy the variables for multiple yield statements:
  946. result = transformVarSection(c, n)
  947. else:
  948. result = transformSons(c, n)
  949. of nkYieldStmt:
  950. if c.inlining > 0:
  951. result = transformYield(c, n)
  952. else:
  953. result = transformSons(c, n)
  954. of nkAsgn:
  955. result = transformAsgn(c, n)
  956. of nkIdentDefs, nkConstDef:
  957. result = PTransNode(n)
  958. result[0] = transform(c, n[0])
  959. # Skip the second son since it only contains an unsemanticized copy of the
  960. # variable type used by docgen
  961. result[2] = transform(c, n[2])
  962. # XXX comment handling really sucks:
  963. if importantComments(c.graph.config):
  964. PNode(result).comment = n.comment
  965. of nkClosure:
  966. # it can happen that for-loop-inlining produced a fresh
  967. # set of variables, including some computed environment
  968. # (bug #2604). We need to patch this environment here too:
  969. let a = n[1]
  970. if a.kind == nkSym:
  971. n.sons[1] = transformSymAux(c, a)
  972. return PTransNode(n)
  973. of nkExceptBranch:
  974. result = transformExceptBranch(c, n)
  975. else:
  976. result = transformSons(c, n)
  977. when false:
  978. if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor
  979. # Constants can be inlined here, but only if they cannot result in a cast
  980. # in the back-end (e.g. var p: pointer = someProc)
  981. let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
  982. n.typ.kind == tyPointer
  983. if not exprIsPointerCast:
  984. var cnst = getConstExpr(c.module, PNode(result), c.graph)
  985. # we inline constants if they are not complex constants:
  986. if cnst != nil and not dontInlineConstant(n, cnst):
  987. result = PTransNode(cnst) # do not miss an optimization
  988. proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
  989. # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
  990. # this step! We have to rely that the semantic pass transforms too errornous
  991. # nodes into an empty node.
  992. if nfTransf in n.flags: return n
  993. pushTransCon(c, newTransCon(owner))
  994. result = PNode(transform(c, n))
  995. popTransCon(c)
  996. incl(result.flags, nfTransf)
  997. proc openTransf(g: ModuleGraph; module: PSym, filename: string): PTransf =
  998. new(result)
  999. result.contSyms = @[]
  1000. result.breakSyms = @[]
  1001. result.module = module
  1002. result.graph = g
  1003. proc flattenStmts(n: PNode) =
  1004. var goOn = true
  1005. while goOn:
  1006. goOn = false
  1007. var i = 0
  1008. while i < n.len:
  1009. let it = n[i]
  1010. if it.kind in {nkStmtList, nkStmtListExpr}:
  1011. n.sons[i..i] = it.sons[0..<it.len]
  1012. goOn = true
  1013. inc i
  1014. proc liftDeferAux(n: PNode) =
  1015. if n.kind in {nkStmtList, nkStmtListExpr}:
  1016. flattenStmts(n)
  1017. var goOn = true
  1018. while goOn:
  1019. goOn = false
  1020. let last = n.len-1
  1021. for i in 0..last:
  1022. if n.sons[i].kind == nkDefer:
  1023. let deferPart = newNodeI(nkFinally, n.sons[i].info)
  1024. deferPart.add n.sons[i].sons[0]
  1025. var tryStmt = newNodeIT(nkTryStmt, n.sons[i].info, n.typ)
  1026. var body = newNodeIT(n.kind, n.sons[i].info, n.typ)
  1027. if i < last:
  1028. body.sons = n.sons[(i+1)..last]
  1029. tryStmt.addSon(body)
  1030. tryStmt.addSon(deferPart)
  1031. n.sons[i] = tryStmt
  1032. n.sons.setLen(i+1)
  1033. n.typ = tryStmt.typ
  1034. goOn = true
  1035. break
  1036. for i in 0..n.safeLen-1:
  1037. liftDeferAux(n.sons[i])
  1038. template liftDefer(c, root) =
  1039. if c.deferDetected:
  1040. liftDeferAux(root)
  1041. proc transformBody*(g: ModuleGraph, prc: PSym, cache = true;
  1042. noDestructors = false): PNode =
  1043. assert prc.kind in routineKinds
  1044. if prc.transformedBody != nil:
  1045. result = prc.transformedBody
  1046. elif nfTransf in prc.ast[bodyPos].flags or prc.kind in {skTemplate}:
  1047. result = prc.ast[bodyPos]
  1048. else:
  1049. prc.transformedBody = newNode(nkEmpty) # protects from recursion
  1050. var c = openTransf(g, prc.getModule, "")
  1051. c.noDestructors = noDestructors
  1052. result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly, noDestructors)
  1053. result = processTransf(c, result, prc)
  1054. liftDefer(c, result)
  1055. result = liftLocalsIfRequested(prc, result, g.cache, g.config)
  1056. if c.needsDestroyPass and not noDestructors:
  1057. result = injectDestructorCalls(g, prc, result)
  1058. if prc.isIterator:
  1059. result = g.transformClosureIterator(prc, result)
  1060. incl(result.flags, nfTransf)
  1061. let cache = cache or prc.typ.callConv == ccInline
  1062. if cache:
  1063. # genProc for inline procs will be called multiple times from different modules,
  1064. # it is important to transform exactly once to get sym ids and locations right
  1065. prc.transformedBody = result
  1066. else:
  1067. prc.transformedBody = nil
  1068. proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode =
  1069. if nfTransf in n.flags:
  1070. result = n
  1071. else:
  1072. var c = openTransf(g, module, "")
  1073. result = processTransf(c, n, module)
  1074. liftDefer(c, result)
  1075. #result = liftLambdasForTopLevel(module, result)
  1076. if c.needsDestroyPass:
  1077. result = injectDestructorCalls(g, module, result)
  1078. incl(result.flags, nfTransf)
  1079. proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode;
  1080. noDestructors = false): PNode =
  1081. if nfTransf in n.flags:
  1082. result = n
  1083. else:
  1084. var c = openTransf(g, module, "")
  1085. result = processTransf(c, n, module)
  1086. liftDefer(c, result)
  1087. # expressions are not to be injected with destructor calls as that
  1088. # the list of top level statements needs to be collected before.
  1089. if c.needsDestroyPass and not noDestructors:
  1090. result = injectDestructorCalls(g, module, result)
  1091. incl(result.flags, nfTransf)