mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
387 lines
13 KiB
Nim
387 lines
13 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2015 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## This module implements common simple lowerings.
|
|
|
|
const
|
|
genPrefix* = ":tmp" # prefix for generated names
|
|
|
|
import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
|
|
lineinfos
|
|
|
|
when defined(nimPreviewSlimSystem):
|
|
import std/assertions
|
|
|
|
proc newDeref*(n: PNode): PNode {.inline.} =
|
|
result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
|
|
result.add n
|
|
|
|
proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
|
|
if tup.kind == nkHiddenAddr:
|
|
result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent}))
|
|
result.add newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent})[i])
|
|
result[0].add tup[0]
|
|
var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
|
|
lit.intVal = i
|
|
result[0].add lit
|
|
else:
|
|
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
|
abstractInst)[i])
|
|
result.add copyTree(tup)
|
|
var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
|
|
lit.intVal = i
|
|
result.add lit
|
|
|
|
proc addVar*(father, v: PNode) =
|
|
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
|
vpart[0] = v
|
|
vpart[1] = newNodeI(nkEmpty, v.info)
|
|
vpart[2] = vpart[1]
|
|
father.add vpart
|
|
|
|
proc addVar*(father, v, value: PNode) =
|
|
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
|
vpart[0] = v
|
|
vpart[1] = newNodeI(nkEmpty, v.info)
|
|
vpart[2] = value
|
|
father.add vpart
|
|
|
|
proc newAsgnStmt*(le, ri: PNode): PNode =
|
|
result = newNodeI(nkAsgn, le.info, 2)
|
|
result[0] = le
|
|
result[1] = ri
|
|
|
|
proc newFastAsgnStmt*(le, ri: PNode): PNode =
|
|
result = newNodeI(nkFastAsgn, le.info, 2)
|
|
result[0] = le
|
|
result[1] = ri
|
|
|
|
proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode =
|
|
result = newNodeI(nkFastAsgn, le.info, 2)
|
|
result[0] = le
|
|
result[1] = newNodeIT(nkCall, ri.info, ri.typ)
|
|
result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove))
|
|
result[1].add ri
|
|
|
|
proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
|
assert n.kind == nkVarTuple
|
|
let value = n.lastSon
|
|
result = newNodeI(nkStmtList, n.info)
|
|
|
|
var tempAsNode: PNode
|
|
let avoidTemp = value.kind == nkSym
|
|
if avoidTemp:
|
|
tempAsNode = value
|
|
else:
|
|
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
|
|
owner, value.info, g.config.options)
|
|
temp.typ = skipTypes(value.typ, abstractInst)
|
|
incl(temp.flags, sfFromGeneric)
|
|
tempAsNode = newSymNode(temp)
|
|
|
|
var v = newNodeI(nkVarSection, value.info)
|
|
if not avoidTemp:
|
|
v.addVar(tempAsNode, value)
|
|
result.add(v)
|
|
|
|
for i in 0..<n.len-2:
|
|
let val = newTupleAccess(g, tempAsNode, i)
|
|
if n[i].kind == nkSym: v.addVar(n[i], val)
|
|
else: result.add newAsgnStmt(n[i], val)
|
|
|
|
proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
|
## Turns (value) into (let tmp = value; tmp) so that 'value' can be re-used
|
|
## freely, multiple times. This is frequently required and such a builtin would also be
|
|
## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
|
|
result = newNodeIT(nkStmtListExpr, value.info, value.typ)
|
|
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
|
|
owner, value.info, g.config.options)
|
|
temp.typ = skipTypes(value.typ, abstractInst)
|
|
incl(temp.flags, sfFromGeneric)
|
|
|
|
var v = newNodeI(nkLetSection, value.info)
|
|
let tempAsNode = newSymNode(temp)
|
|
v.addVar(tempAsNode)
|
|
result.add(v)
|
|
result.add newAsgnStmt(tempAsNode, value)
|
|
result.add tempAsNode
|
|
|
|
proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
|
|
result = newNodeI(nkBracketExpr, tup.info)
|
|
result.add copyTree(tup)
|
|
var lit = newNodeI(nkIntLit, tup.info)
|
|
lit.intVal = i
|
|
result.add lit
|
|
|
|
proc newTryFinally*(body, final: PNode): PNode =
|
|
result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
|
|
|
|
proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
|
let value = n.lastSon
|
|
result = newNodeI(nkStmtList, n.info)
|
|
|
|
var temp = newSym(skTemp, getIdent(g.cache, "_"), nextSymId(idgen), owner, value.info, owner.options)
|
|
var v = newNodeI(nkLetSection, value.info)
|
|
let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
|
|
|
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
|
vpart[0] = tempAsNode
|
|
vpart[1] = newNodeI(nkEmpty, value.info)
|
|
vpart[2] = value
|
|
v.add vpart
|
|
result.add(v)
|
|
|
|
let lhs = n[0]
|
|
for i in 0..<lhs.len:
|
|
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
|
|
|
|
proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
|
result = newNodeI(nkStmtList, n.info)
|
|
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
|
|
var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextSymId(idgen), owner, n.info, owner.options)
|
|
temp.typ = n[1].typ
|
|
incl(temp.flags, sfFromGeneric)
|
|
incl(temp.flags, sfGenSym)
|
|
|
|
var v = newNodeI(nkVarSection, n.info)
|
|
let tempAsNode = newSymNode(temp)
|
|
|
|
var vpart = newNodeI(nkIdentDefs, v.info, 3)
|
|
vpart[0] = tempAsNode
|
|
vpart[1] = newNodeI(nkEmpty, v.info)
|
|
vpart[2] = n[1]
|
|
v.add vpart
|
|
|
|
result.add(v)
|
|
result.add newFastAsgnStmt(n[1], n[2])
|
|
result.add newFastAsgnStmt(n[2], tempAsNode)
|
|
|
|
proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
|
|
result = newType(tyObject, nextTypeId(idgen), owner)
|
|
if final:
|
|
rawAddSon(result, nil)
|
|
incl result.flags, tfFinal
|
|
else:
|
|
rawAddSon(result, getCompilerProc(g, "RootObj").typ)
|
|
result.n = newNodeI(nkRecList, info)
|
|
let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
|
|
nextSymId(idgen),
|
|
owner, info, owner.options)
|
|
incl s.flags, sfAnon
|
|
s.typ = result
|
|
result.sym = s
|
|
|
|
template fieldCheck {.dirty.} =
|
|
when false:
|
|
if tfCheckedForDestructor in obj.flags:
|
|
echo "missed field ", field.name.s
|
|
writeStackTrace()
|
|
|
|
proc rawAddField*(obj: PType; field: PSym) =
|
|
assert field.kind == skField
|
|
field.position = obj.n.len
|
|
obj.n.add newSymNode(field)
|
|
propagateToOwner(obj, field.typ)
|
|
fieldCheck()
|
|
|
|
proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
|
|
# returns a[].field as a node
|
|
assert field.kind == skField
|
|
var deref = newNodeI(nkHiddenDeref, info)
|
|
deref.typ = a.typ.skipTypes(abstractInst)[0]
|
|
deref.add a
|
|
result = newNodeI(nkDotExpr, info)
|
|
result.add deref
|
|
result.add newSymNode(field)
|
|
result.typ = field.typ
|
|
|
|
proc rawDirectAccess*(obj, field: PSym): PNode =
|
|
# returns a.field as a node
|
|
assert field.kind == skField
|
|
result = newNodeI(nkDotExpr, field.info)
|
|
result.add newSymNode(obj)
|
|
result.add newSymNode(field)
|
|
result.typ = field.typ
|
|
|
|
proc lookupInRecord(n: PNode, id: ItemId): PSym =
|
|
result = nil
|
|
case n.kind
|
|
of nkRecList:
|
|
for i in 0..<n.len:
|
|
result = lookupInRecord(n[i], id)
|
|
if result != nil: return
|
|
of nkRecCase:
|
|
if n[0].kind != nkSym: return
|
|
result = lookupInRecord(n[0], id)
|
|
if result != nil: return
|
|
for i in 1..<n.len:
|
|
case n[i].kind
|
|
of nkOfBranch, nkElse:
|
|
result = lookupInRecord(lastSon(n[i]), id)
|
|
if result != nil: return
|
|
else: discard
|
|
of nkSym:
|
|
if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
|
|
else: discard
|
|
|
|
proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym =
|
|
# because of 'gensym' support, we have to mangle the name with its ID.
|
|
# This is hacky but the clean solution is much more complex than it looks.
|
|
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
|
|
nextSymId(idgen), s.owner, s.info, s.options)
|
|
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
|
let t = skipIntLit(s.typ, idgen)
|
|
field.typ = t
|
|
assert t.kind != tyTyped
|
|
propagateToOwner(obj, t)
|
|
field.position = obj.n.len
|
|
# sfNoInit flag for skField is used in closureiterator codegen
|
|
field.flags = s.flags * {sfCursor, sfNoInit}
|
|
obj.n.add newSymNode(field)
|
|
fieldCheck()
|
|
result = field
|
|
|
|
proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
|
|
result = lookupInRecord(obj.n, s.itemId)
|
|
if result == nil:
|
|
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextSymId(idgen),
|
|
s.owner, s.info, s.options)
|
|
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
|
let t = skipIntLit(s.typ, idgen)
|
|
field.typ = t
|
|
assert t.kind != tyTyped
|
|
propagateToOwner(obj, t)
|
|
field.position = obj.n.len
|
|
obj.n.add newSymNode(field)
|
|
result = field
|
|
|
|
proc newDotExpr*(obj, b: PSym): PNode =
|
|
result = newNodeI(nkDotExpr, obj.info)
|
|
let field = lookupInRecord(obj.typ.n, b.itemId)
|
|
assert field != nil, b.name.s
|
|
result.add newSymNode(obj)
|
|
result.add newSymNode(field)
|
|
result.typ = field.typ
|
|
|
|
proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
|
|
# returns a[].b as a node
|
|
var deref = newNodeI(nkHiddenDeref, info)
|
|
deref.typ = a.typ.skipTypes(abstractInst)[0]
|
|
var t = deref.typ.skipTypes(abstractInst)
|
|
var field: PSym
|
|
while true:
|
|
assert t.kind == tyObject
|
|
field = lookupInRecord(t.n, b)
|
|
if field != nil: break
|
|
t = t[0]
|
|
if t == nil: break
|
|
t = t.skipTypes(skipPtrs)
|
|
#if field == nil:
|
|
# echo "FIELD ", b
|
|
# debug deref.typ
|
|
assert field != nil
|
|
deref.add a
|
|
result = newNodeI(nkDotExpr, info)
|
|
result.add deref
|
|
result.add newSymNode(field)
|
|
result.typ = field.typ
|
|
|
|
proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
|
|
# returns a[].b as a node
|
|
var deref = newNodeI(nkHiddenDeref, info)
|
|
deref.typ = a.typ.skipTypes(abstractInst)[0]
|
|
var t = deref.typ.skipTypes(abstractInst)
|
|
var field: PSym
|
|
let bb = getIdent(cache, b)
|
|
while true:
|
|
assert t.kind == tyObject
|
|
field = getSymFromList(t.n, bb)
|
|
if field != nil: break
|
|
t = t[0]
|
|
if t == nil: break
|
|
t = t.skipTypes(skipPtrs)
|
|
#if field == nil:
|
|
# echo "FIELD ", b
|
|
# debug deref.typ
|
|
assert field != nil
|
|
deref.add a
|
|
result = newNodeI(nkDotExpr, info)
|
|
result.add deref
|
|
result.add newSymNode(field)
|
|
result.typ = field.typ
|
|
|
|
proc getFieldFromObj*(t: PType; v: PSym): PSym =
|
|
assert v.kind != skField
|
|
var t = t
|
|
while true:
|
|
assert t.kind == tyObject
|
|
result = lookupInRecord(t.n, v.itemId)
|
|
if result != nil: break
|
|
t = t[0]
|
|
if t == nil: break
|
|
t = t.skipTypes(skipPtrs)
|
|
|
|
proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
|
|
# returns a[].b as a node
|
|
result = indirectAccess(a, b.itemId, info)
|
|
|
|
proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
|
result = indirectAccess(newSymNode(a), b, info)
|
|
|
|
proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
|
|
result = newNodeI(nkAddr, n.info, 1)
|
|
result[0] = n
|
|
result.typ = newType(typeKind, nextTypeId(idgen), n.typ.owner)
|
|
result.typ.rawAddSon(n.typ)
|
|
|
|
proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
|
|
result = newNodeIT(k, n.info,
|
|
n.typ.skipTypes(abstractInst)[0])
|
|
result.add n
|
|
|
|
proc callCodegenProc*(g: ModuleGraph; name: string;
|
|
info: TLineInfo = unknownLineInfo;
|
|
arg1, arg2, arg3, optionalArgs: PNode = nil): PNode =
|
|
result = newNodeI(nkCall, info)
|
|
let sym = magicsys.getCompilerProc(g, name)
|
|
if sym == nil:
|
|
localError(g.config, info, "system module needs: " & name)
|
|
else:
|
|
result.add newSymNode(sym)
|
|
if arg1 != nil: result.add arg1
|
|
if arg2 != nil: result.add arg2
|
|
if arg3 != nil: result.add arg3
|
|
if optionalArgs != nil:
|
|
for i in 1..<optionalArgs.len-2:
|
|
result.add optionalArgs[i]
|
|
result.typ = sym.typ[0]
|
|
|
|
proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
|
|
result = nkIntLit.newIntNode(value)
|
|
result.typ = getSysType(g, info, tyInt)
|
|
|
|
proc genHigh*(g: ModuleGraph; n: PNode): PNode =
|
|
if skipTypes(n.typ, abstractVar).kind == tyArray:
|
|
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar))))
|
|
else:
|
|
result = newNodeI(nkCall, n.info, 2)
|
|
result.typ = getSysType(g, n.info, tyInt)
|
|
result[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
|
|
result[1] = n
|
|
|
|
proc genLen*(g: ModuleGraph; n: PNode): PNode =
|
|
if skipTypes(n.typ, abstractVar).kind == tyArray:
|
|
result = newIntLit(g, n.info, toInt64(lastOrd(g.config, skipTypes(n.typ, abstractVar)) + 1))
|
|
else:
|
|
result = newNodeI(nkCall, n.info, 2)
|
|
result.typ = getSysType(g, n.info, tyInt)
|
|
result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
|
|
result[1] = n
|
|
|