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