transf.nim 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  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. intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
  21. idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread,
  22. lambdalifting, sempass2, lowerings, lookups, destroyer
  23. type
  24. PTransNode* = distinct PNode
  25. PTransCon = ref TTransCon
  26. TTransCon{.final.} = object # part of TContext; stackable
  27. mapping: TIdNodeTable # mapping from symbols to nodes
  28. owner: PSym # current owner
  29. forStmt: PNode # current for stmt
  30. forLoopBody: PTransNode # transformed for loop body
  31. yieldStmts: int # we count the number of yield statements,
  32. # because we need to introduce new variables
  33. # if we encounter the 2nd yield statement
  34. next: PTransCon # for stacking
  35. TTransfContext = object of passes.TPassContext
  36. module: PSym
  37. transCon: PTransCon # top of a TransCon stack
  38. inlining: int # > 0 if we are in inlining context (copy vars)
  39. nestedProcs: int # > 0 if we are in a nested proc
  40. contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
  41. deferDetected, tooEarly, needsDestroyPass: bool
  42. PTransf = ref TTransfContext
  43. proc newTransNode(a: PNode): PTransNode {.inline.} =
  44. result = PTransNode(shallowCopy(a))
  45. proc newTransNode(kind: TNodeKind, info: TLineInfo,
  46. sons: int): PTransNode {.inline.} =
  47. var x = newNodeI(kind, info)
  48. newSeq(x.sons, sons)
  49. result = x.PTransNode
  50. proc newTransNode(kind: TNodeKind, n: PNode,
  51. sons: int): PTransNode {.inline.} =
  52. var x = newNodeIT(kind, n.info, n.typ)
  53. newSeq(x.sons, sons)
  54. x.typ = n.typ
  55. result = x.PTransNode
  56. proc `[]=`(a: PTransNode, i: int, x: PTransNode) {.inline.} =
  57. var n = PNode(a)
  58. n.sons[i] = PNode(x)
  59. proc `[]`(a: PTransNode, i: int): PTransNode {.inline.} =
  60. var n = PNode(a)
  61. result = n.sons[i].PTransNode
  62. proc add(a, b: PTransNode) {.inline.} = addSon(PNode(a), PNode(b))
  63. proc len(a: PTransNode): int {.inline.} = result = sonsLen(a.PNode)
  64. proc newTransCon(owner: PSym): PTransCon =
  65. assert owner != nil
  66. new(result)
  67. initIdNodeTable(result.mapping)
  68. result.owner = owner
  69. proc pushTransCon(c: PTransf, t: PTransCon) =
  70. t.next = c.transCon
  71. c.transCon = t
  72. proc popTransCon(c: PTransf) =
  73. if (c.transCon == nil): internalError("popTransCon")
  74. c.transCon = c.transCon.next
  75. proc getCurrOwner(c: PTransf): PSym =
  76. if c.transCon != nil: result = c.transCon.owner
  77. else: result = c.module
  78. proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
  79. let r = newSym(skTemp, getIdent(genPrefix), getCurrOwner(c), info)
  80. r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias})
  81. incl(r.flags, sfFromGeneric)
  82. let owner = getCurrOwner(c)
  83. if owner.isIterator and not c.tooEarly:
  84. result = freshVarForClosureIter(r, owner)
  85. else:
  86. result = newSymNode(r)
  87. proc transform(c: PTransf, n: PNode): PTransNode
  88. proc transformSons(c: PTransf, n: PNode): PTransNode =
  89. result = newTransNode(n)
  90. for i in countup(0, sonsLen(n)-1):
  91. result[i] = transform(c, n.sons[i])
  92. proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
  93. result = newTransNode(nkFastAsgn, PNode(ri).info, 2)
  94. result[0] = PTransNode(le)
  95. result[1] = ri
  96. proc transformSymAux(c: PTransf, n: PNode): PNode =
  97. let s = n.sym
  98. if s.typ != nil and s.typ.callConv == ccClosure:
  99. if s.kind == skIterator:
  100. if c.tooEarly: return n
  101. else: return liftIterSym(n, getCurrOwner(c))
  102. elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly:
  103. # top level .closure procs are still somewhat supported for 'Nake':
  104. return makeClosure(s, nil, n.info)
  105. #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure:
  106. # echo n.info, " come heer for ", c.tooEarly
  107. # if not c.tooEarly:
  108. var b: PNode
  109. var tc = c.transCon
  110. if sfBorrow in s.flags and s.kind in routineKinds:
  111. # simply exchange the symbol:
  112. b = s.getBody
  113. if b.kind != nkSym: internalError(n.info, "wrong AST for borrowed symbol")
  114. b = newSymNode(b.sym, n.info)
  115. else:
  116. b = n
  117. while tc != nil:
  118. result = idNodeTableGet(tc.mapping, b.sym)
  119. if result != nil:
  120. # this slightly convoluted way ensures the line info stays correct:
  121. if result.kind == nkSym:
  122. result = copyNode(result)
  123. result.info = n.info
  124. return
  125. tc = tc.next
  126. result = b
  127. proc transformSym(c: PTransf, n: PNode): PTransNode =
  128. result = PTransNode(transformSymAux(c, n))
  129. proc freshVar(c: PTransf; v: PSym): PNode =
  130. let owner = getCurrOwner(c)
  131. if owner.isIterator and not c.tooEarly:
  132. result = freshVarForClosureIter(v, owner)
  133. else:
  134. var newVar = copySym(v)
  135. incl(newVar.flags, sfFromGeneric)
  136. newVar.owner = owner
  137. result = newSymNode(newVar)
  138. proc transformVarSection(c: PTransf, v: PNode): PTransNode =
  139. result = newTransNode(v)
  140. for i in countup(0, sonsLen(v)-1):
  141. var it = v.sons[i]
  142. if it.kind == nkCommentStmt:
  143. result[i] = PTransNode(it)
  144. elif it.kind == nkIdentDefs:
  145. if it.sons[0].kind == nkSym:
  146. internalAssert(it.len == 3)
  147. let x = freshVar(c, it.sons[0].sym)
  148. idNodeTablePut(c.transCon.mapping, it.sons[0].sym, x)
  149. var defs = newTransNode(nkIdentDefs, it.info, 3)
  150. if importantComments():
  151. # keep documentation information:
  152. PNode(defs).comment = it.comment
  153. defs[0] = x.PTransNode
  154. defs[1] = it.sons[1].PTransNode
  155. defs[2] = transform(c, it.sons[2])
  156. if x.kind == nkSym: x.sym.ast = defs[2].PNode
  157. result[i] = defs
  158. else:
  159. # has been transformed into 'param.x' for closure iterators, so just
  160. # transform it:
  161. result[i] = transform(c, it)
  162. else:
  163. if it.kind != nkVarTuple:
  164. internalError(it.info, "transformVarSection: not nkVarTuple")
  165. var L = sonsLen(it)
  166. var defs = newTransNode(it.kind, it.info, L)
  167. for j in countup(0, L-3):
  168. let x = freshVar(c, it.sons[j].sym)
  169. idNodeTablePut(c.transCon.mapping, it.sons[j].sym, x)
  170. defs[j] = x.PTransNode
  171. assert(it.sons[L-2].kind == nkEmpty)
  172. defs[L-2] = ast.emptyNode.PTransNode
  173. defs[L-1] = transform(c, it.sons[L-1])
  174. result[i] = defs
  175. proc transformConstSection(c: PTransf, v: PNode): PTransNode =
  176. result = newTransNode(v)
  177. for i in countup(0, sonsLen(v)-1):
  178. var it = v.sons[i]
  179. if it.kind == nkCommentStmt:
  180. result[i] = PTransNode(it)
  181. else:
  182. if it.kind != nkConstDef: internalError(it.info, "transformConstSection")
  183. if it.sons[0].kind != nkSym:
  184. internalError(it.info, "transformConstSection")
  185. result[i] = PTransNode(it)
  186. proc hasContinue(n: PNode): bool =
  187. case n.kind
  188. of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard
  189. of nkContinueStmt: result = true
  190. else:
  191. for i in countup(0, sonsLen(n) - 1):
  192. if hasContinue(n.sons[i]): return true
  193. proc newLabel(c: PTransf, n: PNode): PSym =
  194. result = newSym(skLabel, nil, getCurrOwner(c), n.info)
  195. result.name = getIdent(genPrefix & $result.id)
  196. proc freshLabels(c: PTransf, n: PNode; symMap: var TIdTable) =
  197. if n.kind in {nkBlockStmt, nkBlockExpr}:
  198. if n.sons[0].kind == nkSym:
  199. let x = newLabel(c, n[0])
  200. idTablePut(symMap, n[0].sym, x)
  201. n.sons[0].sym = x
  202. if n.kind == nkSym and n.sym.kind == skLabel:
  203. let x = PSym(idTableGet(symMap, n.sym))
  204. if x != nil: n.sym = x
  205. else:
  206. for i in 0 .. <safeLen(n): freshLabels(c, n.sons[i], symMap)
  207. proc transformBlock(c: PTransf, n: PNode): PTransNode =
  208. var labl: PSym
  209. if n.sons[0].kind != nkEmpty:
  210. # already named block? -> Push symbol on the stack:
  211. labl = n.sons[0].sym
  212. else:
  213. labl = newLabel(c, n)
  214. c.breakSyms.add(labl)
  215. result = transformSons(c, n)
  216. discard c.breakSyms.pop
  217. result[0] = newSymNode(labl).PTransNode
  218. proc transformLoopBody(c: PTransf, n: PNode): PTransNode =
  219. # What if it contains "continue" and "break"? "break" needs
  220. # an explicit label too, but not the same!
  221. # We fix this here by making every 'break' belong to its enclosing loop
  222. # and changing all breaks that belong to a 'block' by annotating it with
  223. # a label (if it hasn't one already).
  224. if hasContinue(n):
  225. let labl = newLabel(c, n)
  226. c.contSyms.add(labl)
  227. result = newTransNode(nkBlockStmt, n.info, 2)
  228. result[0] = newSymNode(labl).PTransNode
  229. result[1] = transform(c, n)
  230. discard c.contSyms.pop()
  231. else:
  232. result = transform(c, n)
  233. proc transformWhile(c: PTransf; n: PNode): PTransNode =
  234. if c.inlining > 0:
  235. result = transformSons(c, n)
  236. else:
  237. let labl = newLabel(c, n)
  238. c.breakSyms.add(labl)
  239. result = newTransNode(nkBlockStmt, n.info, 2)
  240. result[0] = newSymNode(labl).PTransNode
  241. var body = newTransNode(n)
  242. for i in 0..n.len-2:
  243. body[i] = transform(c, n.sons[i])
  244. body[<n.len] = transformLoopBody(c, n.sons[<n.len])
  245. result[1] = body
  246. discard c.breakSyms.pop
  247. proc transformBreak(c: PTransf, n: PNode): PTransNode =
  248. if n.sons[0].kind != nkEmpty or c.inlining > 0:
  249. result = n.PTransNode
  250. when false:
  251. let lablCopy = idNodeTableGet(c.transCon.mapping, n.sons[0].sym)
  252. if lablCopy.isNil:
  253. result = n.PTransNode
  254. else:
  255. result = newTransNode(n.kind, n.info, 1)
  256. result[0] = lablCopy.PTransNode
  257. elif c.breakSyms.len > 0:
  258. # this check can fail for 'nim check'
  259. let labl = c.breakSyms[c.breakSyms.high]
  260. result = transformSons(c, n)
  261. result[0] = newSymNode(labl).PTransNode
  262. else:
  263. result = n.PTransNode
  264. proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) =
  265. # XXX: BUG: what if `n` is an expression with side-effects?
  266. for i in countup(0, sonsLen(c.transCon.forStmt) - 3):
  267. add(father, newAsgnStmt(c, c.transCon.forStmt.sons[i],
  268. transform(c, newTupleAccess(n, i))))
  269. proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode =
  270. case n.kind
  271. of nkSym:
  272. result = transformSym(c, n)
  273. of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
  274. # nothing to be done for leaves:
  275. result = PTransNode(n)
  276. of nkVarSection, nkLetSection:
  277. result = transformVarSection(c, n)
  278. of nkClosure:
  279. # it can happen that for-loop-inlining produced a fresh
  280. # set of variables, including some computed environment
  281. # (bug #2604). We need to patch this environment here too:
  282. let a = n[1]
  283. if a.kind == nkSym:
  284. n.sons[1] = transformSymAux(c, a)
  285. return PTransNode(n)
  286. else:
  287. result = newTransNode(n)
  288. for i in countup(0, sonsLen(n)-1):
  289. result[i] = introduceNewLocalVars(c, n.sons[i])
  290. proc transformYield(c: PTransf, n: PNode): PTransNode =
  291. result = newTransNode(nkStmtList, n.info, 0)
  292. var e = n.sons[0]
  293. # c.transCon.forStmt.len == 3 means that there is one for loop variable
  294. # and thus no tuple unpacking:
  295. if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons
  296. if skipTypes(e.typ, {tyGenericInst, tyAlias}).kind == tyTuple and
  297. c.transCon.forStmt.len != 3:
  298. e = skipConv(e)
  299. if e.kind == nkPar:
  300. for i in countup(0, sonsLen(e) - 1):
  301. var v = e.sons[i]
  302. if v.kind == nkExprColonExpr: v = v.sons[1]
  303. add(result, newAsgnStmt(c, c.transCon.forStmt.sons[i],
  304. transform(c, v)))
  305. else:
  306. unpackTuple(c, e, result)
  307. else:
  308. var x = transform(c, e)
  309. add(result, newAsgnStmt(c, c.transCon.forStmt.sons[0], x))
  310. inc(c.transCon.yieldStmts)
  311. if c.transCon.yieldStmts <= 1:
  312. # common case
  313. add(result, c.transCon.forLoopBody)
  314. else:
  315. # we need to introduce new local variables:
  316. add(result, introduceNewLocalVars(c, c.transCon.forLoopBody.PNode))
  317. proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PTransNode =
  318. result = transformSons(c, n)
  319. if gCmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return
  320. var n = result.PNode
  321. case n.sons[0].kind
  322. of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
  323. var m = n.sons[0].sons[0]
  324. if m.kind == a or m.kind == b:
  325. # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
  326. n.sons[0].sons[0] = m.sons[0]
  327. result = PTransNode(n.sons[0])
  328. if n.typ.kind != tyOpenArray:
  329. PNode(result).typ = n.typ
  330. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  331. var m = n.sons[0].sons[1]
  332. if m.kind == a or m.kind == b:
  333. # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
  334. n.sons[0].sons[1] = m.sons[0]
  335. result = PTransNode(n.sons[0])
  336. if n.typ.kind != tyOpenArray:
  337. PNode(result).typ = n.typ
  338. else:
  339. if n.sons[0].kind == a or n.sons[0].kind == b:
  340. # addr ( deref ( x )) --> x
  341. result = PTransNode(n.sons[0].sons[0])
  342. if n.typ.kind != tyOpenArray:
  343. PNode(result).typ = n.typ
  344. proc generateThunk(prc: PNode, dest: PType): PNode =
  345. ## Converts 'prc' into '(thunk, nil)' so that it's compatible with
  346. ## a closure.
  347. # we cannot generate a proper thunk here for GC-safety reasons
  348. # (see internal documentation):
  349. if gCmd == cmdCompileToJS: return prc
  350. result = newNodeIT(nkClosure, prc.info, dest)
  351. var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
  352. conv.add(emptyNode)
  353. conv.add(prc)
  354. if prc.kind == nkClosure:
  355. internalError(prc.info, "closure to closure created")
  356. result.add(conv)
  357. result.add(newNodeIT(nkNilLit, prc.info, getSysType(tyNil)))
  358. proc transformConv(c: PTransf, n: PNode): PTransNode =
  359. # numeric types need range checks:
  360. var dest = skipTypes(n.typ, abstractVarRange)
  361. var source = skipTypes(n.sons[1].typ, abstractVarRange)
  362. case dest.kind
  363. of tyInt..tyInt64, tyEnum, tyChar, tyBool, tyUInt8..tyUInt32:
  364. # we don't include uint and uint64 here as these are no ordinal types ;-)
  365. if not isOrdinalType(source):
  366. # float -> int conversions. ugh.
  367. result = transformSons(c, n)
  368. elif firstOrd(n.typ) <= firstOrd(n.sons[1].typ) and
  369. lastOrd(n.sons[1].typ) <= lastOrd(n.typ):
  370. # BUGFIX: simply leave n as it is; we need a nkConv node,
  371. # but no range check:
  372. result = transformSons(c, n)
  373. else:
  374. # generate a range check:
  375. if dest.kind == tyInt64 or source.kind == tyInt64:
  376. result = newTransNode(nkChckRange64, n, 3)
  377. else:
  378. result = newTransNode(nkChckRange, n, 3)
  379. dest = skipTypes(n.typ, abstractVar)
  380. result[0] = transform(c, n.sons[1])
  381. result[1] = newIntTypeNode(nkIntLit, firstOrd(dest), dest).PTransNode
  382. result[2] = newIntTypeNode(nkIntLit, lastOrd(dest), dest).PTransNode
  383. of tyFloat..tyFloat128:
  384. # XXX int64 -> float conversion?
  385. if skipTypes(n.typ, abstractVar).kind == tyRange:
  386. result = newTransNode(nkChckRangeF, n, 3)
  387. dest = skipTypes(n.typ, abstractVar)
  388. result[0] = transform(c, n.sons[1])
  389. result[1] = copyTree(dest.n.sons[0]).PTransNode
  390. result[2] = copyTree(dest.n.sons[1]).PTransNode
  391. else:
  392. result = transformSons(c, n)
  393. of tyOpenArray, tyVarargs:
  394. result = transform(c, n.sons[1])
  395. PNode(result).typ = takeType(n.typ, n.sons[1].typ)
  396. #echo n.info, " came here and produced ", typeToString(PNode(result).typ),
  397. # " from ", typeToString(n.typ), " and ", typeToString(n.sons[1].typ)
  398. of tyCString:
  399. if source.kind == tyString:
  400. result = newTransNode(nkStringToCString, n, 1)
  401. result[0] = transform(c, n.sons[1])
  402. else:
  403. result = transformSons(c, n)
  404. of tyString:
  405. if source.kind == tyCString:
  406. result = newTransNode(nkCStringToString, n, 1)
  407. result[0] = transform(c, n.sons[1])
  408. else:
  409. result = transformSons(c, n)
  410. of tyRef, tyPtr:
  411. dest = skipTypes(dest, abstractPtrs)
  412. source = skipTypes(source, abstractPtrs)
  413. if source.kind == tyObject:
  414. var diff = inheritanceDiff(dest, source)
  415. if diff < 0:
  416. result = newTransNode(nkObjUpConv, n, 1)
  417. result[0] = transform(c, n.sons[1])
  418. elif diff > 0 and diff != high(int):
  419. result = newTransNode(nkObjDownConv, n, 1)
  420. result[0] = transform(c, n.sons[1])
  421. else:
  422. result = transform(c, n.sons[1])
  423. else:
  424. result = transformSons(c, n)
  425. of tyObject:
  426. var diff = inheritanceDiff(dest, source)
  427. if diff < 0:
  428. result = newTransNode(nkObjUpConv, n, 1)
  429. result[0] = transform(c, n.sons[1])
  430. elif diff > 0 and diff != high(int):
  431. result = newTransNode(nkObjDownConv, n, 1)
  432. result[0] = transform(c, n.sons[1])
  433. else:
  434. result = transform(c, n.sons[1])
  435. of tyGenericParam, tyOrdinal:
  436. result = transform(c, n.sons[1])
  437. # happens sometimes for generated assignments, etc.
  438. of tyProc:
  439. result = transformSons(c, n)
  440. if dest.callConv == ccClosure and source.callConv == ccDefault:
  441. result = generateThunk(result[1].PNode, dest).PTransNode
  442. else:
  443. result = transformSons(c, n)
  444. type
  445. TPutArgInto = enum
  446. paDirectMapping, paFastAsgn, paVarAsgn, paComplexOpenarray
  447. proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
  448. # This analyses how to treat the mapping "formal <-> arg" in an
  449. # inline context.
  450. if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
  451. if arg.kind == nkStmtListExpr:
  452. return paComplexOpenarray
  453. return paDirectMapping # XXX really correct?
  454. # what if ``arg`` has side-effects?
  455. case arg.kind
  456. of nkEmpty..nkNilLit:
  457. result = paDirectMapping
  458. of nkPar, nkCurly, nkBracket:
  459. result = paFastAsgn
  460. for i in countup(0, sonsLen(arg) - 1):
  461. if putArgInto(arg.sons[i], formal) != paDirectMapping: return
  462. result = paDirectMapping
  463. else:
  464. if skipTypes(formal, abstractInst).kind == tyVar: result = paVarAsgn
  465. else: result = paFastAsgn
  466. proc findWrongOwners(c: PTransf, n: PNode) =
  467. if n.kind == nkVarSection:
  468. let x = n.sons[0].sons[0]
  469. if x.kind == nkSym and x.sym.owner != getCurrOwner(c):
  470. internalError(x.info, "bah " & x.sym.name.s & " " &
  471. x.sym.owner.name.s & " " & getCurrOwner(c).name.s)
  472. else:
  473. for i in 0 .. <safeLen(n): findWrongOwners(c, n.sons[i])
  474. proc transformFor(c: PTransf, n: PNode): PTransNode =
  475. # generate access statements for the parameters (unless they are constant)
  476. # put mapping from formal parameters to actual parameters
  477. if n.kind != nkForStmt: internalError(n.info, "transformFor")
  478. var length = sonsLen(n)
  479. var call = n.sons[length - 2]
  480. let labl = newLabel(c, n)
  481. result = newTransNode(nkBlockStmt, n.info, 2)
  482. result[0] = newSymNode(labl).PTransNode
  483. if call.typ.isNil:
  484. # see bug #3051
  485. result[1] = newNode(nkEmpty).PTransNode
  486. return result
  487. c.breakSyms.add(labl)
  488. if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
  489. call.sons[0].typ.callConv == ccClosure:
  490. n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
  491. if not c.tooEarly:
  492. n.sons[length-2] = transform(c, n.sons[length-2]).PNode
  493. result[1] = lambdalifting.liftForLoop(n, getCurrOwner(c)).PTransNode
  494. else:
  495. result[1] = newNode(nkEmpty).PTransNode
  496. discard c.breakSyms.pop
  497. return result
  498. #echo "transforming: ", renderTree(n)
  499. var stmtList = newTransNode(nkStmtList, n.info, 0)
  500. result[1] = stmtList
  501. var loopBody = transformLoopBody(c, n.sons[length-1])
  502. discard c.breakSyms.pop
  503. var v = newNodeI(nkVarSection, n.info)
  504. for i in countup(0, length - 3):
  505. addVar(v, copyTree(n.sons[i])) # declare new vars
  506. add(stmtList, v.PTransNode)
  507. # Bugfix: inlined locals belong to the invoking routine, not to the invoked
  508. # iterator!
  509. let iter = call.sons[0].sym
  510. var newC = newTransCon(getCurrOwner(c))
  511. newC.forStmt = n
  512. newC.forLoopBody = loopBody
  513. # this can fail for 'nimsuggest' and 'check':
  514. if iter.kind != skIterator: return result
  515. # generate access statements for the parameters (unless they are constant)
  516. pushTransCon(c, newC)
  517. for i in countup(1, sonsLen(call) - 1):
  518. var arg = transform(c, call.sons[i]).PNode
  519. let ff = skipTypes(iter.typ, abstractInst)
  520. # can happen for 'nim check':
  521. if i >= ff.n.len: return result
  522. var formal = ff.n.sons[i].sym
  523. case putArgInto(arg, formal.typ)
  524. of paDirectMapping:
  525. idNodeTablePut(newC.mapping, formal, arg)
  526. of paFastAsgn:
  527. # generate a temporary and produce an assignment statement:
  528. var temp = newTemp(c, formal.typ, formal.info)
  529. addVar(v, temp)
  530. add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
  531. idNodeTablePut(newC.mapping, formal, temp)
  532. of paVarAsgn:
  533. assert(skipTypes(formal.typ, abstractInst).kind == tyVar)
  534. idNodeTablePut(newC.mapping, formal, arg)
  535. # XXX BUG still not correct if the arg has a side effect!
  536. of paComplexOpenarray:
  537. let typ = newType(tySequence, formal.owner)
  538. addSonSkipIntLit(typ, formal.typ.sons[0])
  539. var temp = newTemp(c, typ, formal.info)
  540. addVar(v, temp)
  541. add(stmtList, newAsgnStmt(c, temp, arg.PTransNode))
  542. idNodeTablePut(newC.mapping, formal, temp)
  543. var body = iter.getBody.copyTree
  544. pushInfoContext(n.info)
  545. # XXX optimize this somehow. But the check "c.inlining" is not correct:
  546. var symMap: TIdTable
  547. initIdTable symMap
  548. freshLabels(c, body, symMap)
  549. inc(c.inlining)
  550. add(stmtList, transform(c, body))
  551. #findWrongOwners(c, stmtList.pnode)
  552. dec(c.inlining)
  553. popInfoContext()
  554. popTransCon(c)
  555. # echo "transformed: ", stmtList.PNode.renderTree
  556. proc transformCase(c: PTransf, n: PNode): PTransNode =
  557. # removes `elif` branches of a case stmt
  558. # adds ``else: nil`` if needed for the code generator
  559. result = newTransNode(nkCaseStmt, n, 0)
  560. var ifs = PTransNode(nil)
  561. for i in 0 .. sonsLen(n)-1:
  562. var it = n.sons[i]
  563. var e = transform(c, it)
  564. case it.kind
  565. of nkElifBranch:
  566. if ifs.PNode == nil:
  567. ifs = newTransNode(nkIfStmt, it.info, 0)
  568. ifs.add(e)
  569. of nkElse:
  570. if ifs.PNode == nil: result.add(e)
  571. else: ifs.add(e)
  572. else:
  573. result.add(e)
  574. if ifs.PNode != nil:
  575. var elseBranch = newTransNode(nkElse, n.info, 1)
  576. elseBranch[0] = ifs
  577. result.add(elseBranch)
  578. elif result.PNode.lastSon.kind != nkElse and not (
  579. skipTypes(n.sons[0].typ, abstractVarRange).kind in
  580. {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32}):
  581. # fix a stupid code gen bug by normalizing:
  582. var elseBranch = newTransNode(nkElse, n.info, 1)
  583. elseBranch[0] = newTransNode(nkNilLit, n.info, 0)
  584. add(result, elseBranch)
  585. proc transformArrayAccess(c: PTransf, n: PNode): PTransNode =
  586. # XXX this is really bad; transf should use a proper AST visitor
  587. if n.sons[0].kind == nkSym and n.sons[0].sym.kind == skType:
  588. result = n.PTransNode
  589. else:
  590. result = newTransNode(n)
  591. for i in 0 .. < n.len:
  592. result[i] = transform(c, skipConv(n.sons[i]))
  593. proc getMergeOp(n: PNode): PSym =
  594. case n.kind
  595. of nkCall, nkHiddenCallConv, nkCommand, nkInfix, nkPrefix, nkPostfix,
  596. nkCallStrLit:
  597. if n.sons[0].kind == nkSym and n.sons[0].sym.magic == mConStrStr:
  598. result = n.sons[0].sym
  599. else: discard
  600. proc flattenTreeAux(d, a: PNode, op: PSym) =
  601. let op2 = getMergeOp(a)
  602. if op2 != nil and
  603. (op2.id == op.id or op.magic != mNone and op2.magic == op.magic):
  604. for i in countup(1, sonsLen(a)-1): flattenTreeAux(d, a.sons[i], op)
  605. else:
  606. addSon(d, copyTree(a))
  607. proc flattenTree(root: PNode): PNode =
  608. let op = getMergeOp(root)
  609. if op != nil:
  610. result = copyNode(root)
  611. addSon(result, copyTree(root.sons[0]))
  612. flattenTreeAux(result, root, op)
  613. else:
  614. result = root
  615. proc transformCall(c: PTransf, n: PNode): PTransNode =
  616. var n = flattenTree(n)
  617. let op = getMergeOp(n)
  618. let magic = getMagic(n)
  619. if op != nil and op.magic != mNone and n.len >= 3:
  620. result = newTransNode(nkCall, n, 0)
  621. add(result, transform(c, n.sons[0]))
  622. var j = 1
  623. while j < sonsLen(n):
  624. var a = transform(c, n.sons[j]).PNode
  625. inc(j)
  626. if isConstExpr(a):
  627. while (j < sonsLen(n)):
  628. let b = transform(c, n.sons[j]).PNode
  629. if not isConstExpr(b): break
  630. a = evalOp(op.magic, n, a, b, nil)
  631. inc(j)
  632. add(result, a.PTransNode)
  633. if len(result) == 2: result = result[1]
  634. elif magic in {mNBindSym, mTypeOf}:
  635. # for bindSym(myconst) we MUST NOT perform constant folding:
  636. result = n.PTransNode
  637. elif magic == mProcCall:
  638. # but do not change to its dispatcher:
  639. result = transformSons(c, n[1])
  640. else:
  641. let s = transformSons(c, n).PNode
  642. # bugfix: check after 'transformSons' if it's still a method call:
  643. # use the dispatcher for the call:
  644. if s.sons[0].kind == nkSym and s.sons[0].sym.kind == skMethod:
  645. when false:
  646. let t = lastSon(s.sons[0].sym.ast)
  647. if t.kind != nkSym or sfDispatcher notin t.sym.flags:
  648. methodDef(s.sons[0].sym, false)
  649. result = methodCall(s).PTransNode
  650. else:
  651. result = s.PTransNode
  652. proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
  653. result = transformSons(c, n)
  654. if n[0].isInfixAs():
  655. let excTypeNode = n[0][1]
  656. let actions = newTransNode(nkStmtList, n[1].info, 2)
  657. # Generating `let exc = (excType)(getCurrentException())`
  658. # -> getCurrentException()
  659. let excCall = PTransNode(callCodegenProc("getCurrentException", ast.emptyNode))
  660. # -> (excType)
  661. let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2)
  662. convNode[0] = PTransNode(ast.emptyNode)
  663. convNode[1] = excCall
  664. PNode(convNode).typ = excTypeNode.typ.toRef()
  665. # -> let exc = ...
  666. let identDefs = newTransNode(nkIdentDefs, n[1].info, 3)
  667. identDefs[0] = PTransNode(n[0][2])
  668. identDefs[1] = PTransNode(ast.emptyNode)
  669. identDefs[2] = convNode
  670. let letSection = newTransNode(nkLetSection, n[1].info, 1)
  671. letSection[0] = identDefs
  672. # Place the let statement and body of the 'except' branch into new stmtList.
  673. actions[0] = letSection
  674. actions[1] = transformSons(c, n[1])
  675. # Overwrite 'except' branch body with our stmtList.
  676. result[1] = actions
  677. # Replace the `Exception as foobar` with just `Exception`.
  678. result[0] = result[0][1]
  679. proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
  680. # symbols that expand to a complex constant (array, etc.) should not be
  681. # inlined, unless it's the empty array:
  682. result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and
  683. cnst.len != 0
  684. proc commonOptimizations*(c: PSym, n: PNode): PNode =
  685. result = n
  686. for i in 0 .. < n.safeLen:
  687. result.sons[i] = commonOptimizations(c, n.sons[i])
  688. var op = getMergeOp(n)
  689. if (op != nil) and (op.magic != mNone) and (sonsLen(n) >= 3):
  690. result = newNodeIT(nkCall, n.info, n.typ)
  691. add(result, n.sons[0])
  692. var args = newNode(nkArgList)
  693. flattenTreeAux(args, n, op)
  694. var j = 0
  695. while j < sonsLen(args):
  696. var a = args.sons[j]
  697. inc(j)
  698. if isConstExpr(a):
  699. while j < sonsLen(args):
  700. let b = args.sons[j]
  701. if not isConstExpr(b): break
  702. a = evalOp(op.magic, result, a, b, nil)
  703. inc(j)
  704. add(result, a)
  705. if len(result) == 2: result = result[1]
  706. else:
  707. var cnst = getConstExpr(c, n)
  708. # we inline constants if they are not complex constants:
  709. if cnst != nil and not dontInlineConstant(n, cnst):
  710. result = cnst
  711. else:
  712. result = n
  713. proc transform(c: PTransf, n: PNode): PTransNode =
  714. when false:
  715. var oldDeferAnchor: PNode
  716. if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr,
  717. nkElseExpr, nkElse, nkForStmt, nkWhileStmt, nkFinally,
  718. nkBlockStmt, nkBlockExpr}:
  719. oldDeferAnchor = c.deferAnchor
  720. c.deferAnchor = n
  721. if n.typ != nil and tfHasAsgn in n.typ.flags:
  722. c.needsDestroyPass = true
  723. case n.kind
  724. of nkSym:
  725. result = transformSym(c, n)
  726. of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit:
  727. # nothing to be done for leaves:
  728. result = PTransNode(n)
  729. of nkBracketExpr: result = transformArrayAccess(c, n)
  730. of procDefs:
  731. var s = n.sons[namePos].sym
  732. if n.typ != nil and s.typ.callConv == ccClosure:
  733. result = transformSym(c, n.sons[namePos])
  734. # use the same node as before if still a symbol:
  735. if result.PNode.kind == nkSym: result = PTransNode(n)
  736. else:
  737. result = PTransNode(n)
  738. of nkMacroDef:
  739. # XXX no proper closure support yet:
  740. when false:
  741. if n.sons[genericParamsPos].kind == nkEmpty:
  742. var s = n.sons[namePos].sym
  743. n.sons[bodyPos] = PNode(transform(c, s.getBody))
  744. if n.kind == nkMethodDef: methodDef(s, false)
  745. result = PTransNode(n)
  746. of nkForStmt:
  747. result = transformFor(c, n)
  748. of nkParForStmt:
  749. result = transformSons(c, n)
  750. of nkCaseStmt:
  751. result = transformCase(c, n)
  752. of nkWhileStmt: result = transformWhile(c, n)
  753. of nkBlockStmt, nkBlockExpr:
  754. result = transformBlock(c, n)
  755. of nkDefer:
  756. c.deferDetected = true
  757. result = transformSons(c, n)
  758. when false:
  759. let deferPart = newNodeI(nkFinally, n.info)
  760. deferPart.add n.sons[0]
  761. let tryStmt = newNodeI(nkTryStmt, n.info)
  762. if c.deferAnchor.isNil:
  763. tryStmt.add c.root
  764. c.root = tryStmt
  765. result = PTransNode(tryStmt)
  766. else:
  767. # modify the corresponding *action*, don't rely on nkStmtList:
  768. let L = c.deferAnchor.len-1
  769. tryStmt.add c.deferAnchor.sons[L]
  770. c.deferAnchor.sons[L] = tryStmt
  771. result = newTransNode(nkCommentStmt, n.info, 0)
  772. tryStmt.addSon(deferPart)
  773. # disable the original 'defer' statement:
  774. n.kind = nkEmpty
  775. of nkContinueStmt:
  776. result = PTransNode(newNodeI(nkBreakStmt, n.info))
  777. var labl = c.contSyms[c.contSyms.high]
  778. add(result, PTransNode(newSymNode(labl)))
  779. of nkBreakStmt: result = transformBreak(c, n)
  780. of nkCallKinds:
  781. result = transformCall(c, n)
  782. of nkAddr, nkHiddenAddr:
  783. result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
  784. of nkDerefExpr, nkHiddenDeref:
  785. result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
  786. of nkHiddenStdConv, nkHiddenSubConv, nkConv:
  787. result = transformConv(c, n)
  788. of nkDiscardStmt:
  789. result = PTransNode(n)
  790. if n.sons[0].kind != nkEmpty:
  791. result = transformSons(c, n)
  792. if isConstExpr(PNode(result).sons[0]):
  793. # ensure that e.g. discard "some comment" gets optimized away
  794. # completely:
  795. result = PTransNode(newNode(nkCommentStmt))
  796. of nkCommentStmt, nkTemplateDef:
  797. return n.PTransNode
  798. of nkConstSection:
  799. # do not replace ``const c = 3`` with ``const 3 = 3``
  800. return transformConstSection(c, n)
  801. of nkTypeSection, nkTypeOfExpr:
  802. # no need to transform type sections:
  803. return PTransNode(n)
  804. of nkVarSection, nkLetSection:
  805. if c.inlining > 0:
  806. # we need to copy the variables for multiple yield statements:
  807. result = transformVarSection(c, n)
  808. else:
  809. result = transformSons(c, n)
  810. of nkYieldStmt:
  811. if c.inlining > 0:
  812. result = transformYield(c, n)
  813. else:
  814. result = transformSons(c, n)
  815. of nkIdentDefs, nkConstDef:
  816. when true:
  817. result = transformSons(c, n)
  818. else:
  819. result = n.PTransNode
  820. let L = n.len-1
  821. result[L] = transform(c, n.sons[L])
  822. # XXX comment handling really sucks:
  823. if importantComments():
  824. PNode(result).comment = n.comment
  825. of nkClosure:
  826. # it can happen that for-loop-inlining produced a fresh
  827. # set of variables, including some computed environment
  828. # (bug #2604). We need to patch this environment here too:
  829. let a = n[1]
  830. if a.kind == nkSym:
  831. n.sons[1] = transformSymAux(c, a)
  832. return PTransNode(n)
  833. of nkExceptBranch:
  834. result = transformExceptBranch(c, n)
  835. else:
  836. result = transformSons(c, n)
  837. when false:
  838. if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor
  839. var cnst = getConstExpr(c.module, PNode(result))
  840. # we inline constants if they are not complex constants:
  841. if cnst != nil and not dontInlineConstant(n, cnst):
  842. result = PTransNode(cnst) # do not miss an optimization
  843. proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
  844. # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
  845. # this step! We have to rely that the semantic pass transforms too errornous
  846. # nodes into an empty node.
  847. if c.fromCache or nfTransf in n.flags: return n
  848. pushTransCon(c, newTransCon(owner))
  849. result = PNode(transform(c, n))
  850. popTransCon(c)
  851. incl(result.flags, nfTransf)
  852. proc openTransf(module: PSym, filename: string): PTransf =
  853. new(result)
  854. result.contSyms = @[]
  855. result.breakSyms = @[]
  856. result.module = module
  857. proc flattenStmts(n: PNode) =
  858. var goOn = true
  859. while goOn:
  860. goOn = false
  861. var i = 0
  862. while i < n.len:
  863. let it = n[i]
  864. if it.kind in {nkStmtList, nkStmtListExpr}:
  865. n.sons[i..i] = it.sons[0..<it.len]
  866. goOn = true
  867. inc i
  868. proc liftDeferAux(n: PNode) =
  869. if n.kind in {nkStmtList, nkStmtListExpr}:
  870. flattenStmts(n)
  871. var goOn = true
  872. while goOn:
  873. goOn = false
  874. let last = n.len-1
  875. for i in 0..last:
  876. if n.sons[i].kind == nkDefer:
  877. let deferPart = newNodeI(nkFinally, n.sons[i].info)
  878. deferPart.add n.sons[i].sons[0]
  879. var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
  880. var body = newNodeI(n.kind, n.sons[i].info)
  881. if i < last:
  882. body.sons = n.sons[(i+1)..last]
  883. tryStmt.addSon(body)
  884. tryStmt.addSon(deferPart)
  885. n.sons[i] = tryStmt
  886. n.sons.setLen(i+1)
  887. n.typ = n.sons[i].typ
  888. goOn = true
  889. break
  890. for i in 0..n.safeLen-1:
  891. liftDeferAux(n.sons[i])
  892. template liftDefer(c, root) =
  893. if c.deferDetected:
  894. liftDeferAux(root)
  895. proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
  896. if nfTransf in n.flags or prc.kind in {skTemplate}:
  897. result = n
  898. else:
  899. var c = openTransf(module, "")
  900. result = liftLambdas(prc, n, c.tooEarly)
  901. #result = n
  902. result = processTransf(c, result, prc)
  903. liftDefer(c, result)
  904. #result = liftLambdas(prc, result)
  905. when useEffectSystem: trackProc(prc, result)
  906. if c.needsDestroyPass and newDestructors:
  907. result = injectDestructorCalls(prc, result)
  908. incl(result.flags, nfTransf)
  909. #if prc.name.s == "testbody":
  910. # echo renderTree(result)
  911. proc transformStmt*(module: PSym, n: PNode): PNode =
  912. if nfTransf in n.flags:
  913. result = n
  914. else:
  915. var c = openTransf(module, "")
  916. result = processTransf(c, n, module)
  917. liftDefer(c, result)
  918. #result = liftLambdasForTopLevel(module, result)
  919. when useEffectSystem: trackTopLevelStmt(module, result)
  920. #if n.info ?? "temp.nim":
  921. # echo renderTree(result, {renderIds})
  922. if c.needsDestroyPass and newDestructors:
  923. result = injectDestructorCalls(module, result)
  924. incl(result.flags, nfTransf)
  925. proc transformExpr*(module: PSym, n: PNode): PNode =
  926. if nfTransf in n.flags:
  927. result = n
  928. else:
  929. var c = openTransf(module, "")
  930. result = processTransf(c, n, module)
  931. liftDefer(c, result)
  932. if c.needsDestroyPass and newDestructors:
  933. result = injectDestructorCalls(module, result)
  934. incl(result.flags, nfTransf)