Files
Nim/compiler/ast.nim

1683 lines
51 KiB
Nim

#
#
# The Nim Compiler
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# abstract syntax tree + symbol table
import
lineinfos, options, ropes, idents, int128, wordrecg
import std/[tables, hashes]
from std/strutils import toLowerAscii
when defined(nimPreviewSlimSystem):
import std/assertions
export int128
import nodekinds
export nodekinds
import astdef
export astdef
when not defined(nimKochBootstrap):
import ast2nif
when not defined(nimKochBootstrap):
var program* {.threadvar.}: DecodeContext
proc setupProgram*(config: ConfigRef; cache: IdentCache) =
when not defined(nimKochBootstrap):
program = createDecodeContext(config, cache)
template loadSym(s: PSym) =
## Loads a symbol from NIF file if it's in Partial state.
when not defined(nimKochBootstrap):
ast2nif.loadSym(program, s)
template loadType(t: PType) =
## Loads a type from NIF file if it's in Partial state.
when not defined(nimKochBootstrap):
ast2nif.loadType(program, t)
proc loadSymCallback*(s: PSym) {.nimcall.} =
loadSym(s)
proc loadTypeCallback*(t: PType) {.nimcall.} =
loadType(t)
proc ensureMutable*(s: PSym) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
proc ensureMutable*(t: PType) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
proc backendEnsureMutable*(s: PSym) {.inline.} =
#assert s.state != Sealed
# ^ IC review this later
if s.state == Partial: loadSym(s)
proc backendEnsureMutable*(t: PType) {.inline.} =
#assert t.state != Sealed
# ^ IC review this later
if t.state == Partial: loadType(t)
proc owner*(s: PSym): PSym {.inline.} =
if s.state == Partial: loadSym(s)
result = s.ownerFieldImpl
proc owner*(s: PType): PSym {.inline.} =
if s.state == Partial: loadType(s)
result = s.ownerFieldImpl
proc setOwner*(s: PSym; owner: PSym) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.ownerFieldImpl = owner
proc setOwner*(s: PType; owner: PSym) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadType(s)
s.ownerFieldImpl = owner
proc kind*(s: PSym): TSymKind {.inline.} =
if s.state == Partial: loadSym(s)
result = s.kindImpl
proc `kind=`*(s: PSym, val: TSymKind) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.kindImpl = val
proc gcUnsafetyReason*(s: PSym): PSym {.inline.} =
if s.state == Partial: loadSym(s)
result = s.gcUnsafetyReasonImpl
proc `gcUnsafetyReason=`*(s: PSym, val: PSym) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.gcUnsafetyReasonImpl = val
proc transformedBody*(s: PSym): PNode {.inline.} =
if s.state == Partial: loadSym(s)
result = s.transformedBodyImpl
proc `transformedBody=`*(s: PSym, val: PNode) {.inline.} =
#assert s.state != Sealed
# Make an exception here for this misfeature...
if s.state == Partial: loadSym(s)
s.transformedBodyImpl = val
proc guard*(s: PSym): PSym {.inline.} =
if s.state == Partial: loadSym(s)
result = s.guardImpl
proc `guard=`*(s: PSym, val: PSym) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.guardImpl = val
proc bitsize*(s: PSym): int {.inline.} =
if s.state == Partial: loadSym(s)
result = s.bitsizeImpl
proc `bitsize=`*(s: PSym, val: int) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.bitsizeImpl = val
proc alignment*(s: PSym): int {.inline.} =
if s.state == Partial: loadSym(s)
result = s.alignmentImpl
proc `alignment=`*(s: PSym, val: int) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.alignmentImpl = val
proc magic*(s: PSym): TMagic {.inline.} =
if s.state == Partial: loadSym(s)
result = s.magicImpl
proc `magic=`*(s: PSym, val: TMagic) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.magicImpl = val
proc typ*(s: PSym): PType {.inline.} =
if s.state == Partial: loadSym(s)
result = s.typImpl
proc `typ=`*(s: PSym, val: PType) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.typImpl = val
proc info*(s: PSym): TLineInfo {.inline.} =
if s.state == Partial: loadSym(s)
result = s.infoImpl
proc `info=`*(s: PSym, val: TLineInfo) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.infoImpl = val
when defined(nimsuggest):
proc endInfo*(s: PSym): TLineInfo {.inline.} =
if s.state == Partial: loadSym(s)
result = s.endInfoImpl
proc `endInfo=`*(s: PSym, val: TLineInfo) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.endInfoImpl = val
proc hasUserSpecifiedType*(s: PSym): bool {.inline.} =
if s.state == Partial: loadSym(s)
result = s.hasUserSpecifiedTypeImpl
proc `hasUserSpecifiedType=`*(s: PSym, val: bool) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.hasUserSpecifiedTypeImpl = val
proc flags*(s: PSym): TSymFlags {.inline.} =
if s.state == Partial: loadSym(s)
result = s.flagsImpl
proc `flags=`*(s: PSym, val: TSymFlags) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.flagsImpl = val
proc ast*(s: PSym): PNode {.inline.} =
if s.state == Partial: loadSym(s)
result = s.astImpl
proc `ast=`*(s: PSym, val: PNode) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.astImpl = val
proc options*(s: PSym): TOptions {.inline.} =
if s.state == Partial: loadSym(s)
result = s.optionsImpl
proc `options=`*(s: PSym, val: TOptions) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.optionsImpl = val
proc position*(s: PSym): int {.inline.} =
if s.state == Partial: loadSym(s)
result = s.positionImpl
proc `position=`*(s: PSym, val: int) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.positionImpl = val
proc offset*(s: PSym): int32 {.inline.} =
if s.state == Partial: loadSym(s)
result = s.offsetImpl
proc `offset=`*(s: PSym, val: int32) {.inline.} =
#assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.offsetImpl = val
proc loc*(s: PSym): TLoc {.inline.} =
if s.state == Partial: loadSym(s)
result = s.locImpl
proc `loc=`*(s: PSym, val: TLoc) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.locImpl = val
proc annex*(s: PSym): PLib {.inline.} =
if s.state == Partial: loadSym(s)
result = s.annexImpl
proc `annex=`*(s: PSym, val: PLib) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.annexImpl = val
when hasFFI:
proc cname*(s: PSym): string {.inline.} =
if s.state == Partial: loadSym(s)
result = s.cnameImpl
proc `cname=`*(s: PSym, val: string) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.cnameImpl = val
proc constraint*(s: PSym): PNode {.inline.} =
if s.state == Partial: loadSym(s)
result = s.constraintImpl
proc `constraint=`*(s: PSym, val: PNode) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.constraintImpl = val
proc instantiatedFrom*(s: PSym): PSym {.inline.} =
if s.state == Partial: loadSym(s)
result = s.instantiatedFromImpl
proc `instantiatedFrom=`*(s: PSym, val: PSym) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.instantiatedFromImpl = val
proc setSnippet*(s: PSym; val: sink string) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.locImpl.snippet = val
proc incl*(s: PSym; flag: TSymFlag) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.flagsImpl.incl(flag)
proc incl*(s: PSym; flags: set[TSymFlag]) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.flagsImpl.incl(flags)
proc incl*(s: PSym; flag: TLocFlag) {.inline.} =
#assert s.state != Sealed
# locImpl is a backend field so do not protect it against mutations
if s.state == Partial: loadSym(s)
s.locImpl.flags.incl(flag)
proc excl*(s: PSym; flag: TSymFlag) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.flagsImpl.excl(flag)
when defined(nimsuggest):
proc allUsages*(s: PSym): var seq[TLineInfo] {.inline.} =
if s.state == Partial: loadSym(s)
result = s.allUsagesImpl
proc `allUsages=`*(s: PSym, val: sink seq[TLineInfo]) {.inline.} =
assert s.state != Sealed
if s.state == Partial: loadSym(s)
s.allUsagesImpl = val
# Accessor procs for TType fields
proc callConv*(t: PType): TCallingConvention {.inline.} =
if t.state == Partial: loadType(t)
result = t.callConvImpl
proc `callConv=`*(t: PType, val: TCallingConvention) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.callConvImpl = val
proc flags*(t: PType): TTypeFlags {.inline.} =
if t.state == Partial: loadType(t)
result = t.flagsImpl
proc `flags=`*(t: PType, val: TTypeFlags) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.flagsImpl = val
proc sons*(t: PType): var TTypeSeq {.inline.} =
if t.state == Partial: loadType(t)
result = t.sonsImpl
proc `sons=`*(t: PType, val: sink TTypeSeq) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.sonsImpl = val
proc n*(t: PType): PNode {.inline.} =
if t.state == Partial: loadType(t)
result = t.nImpl
proc `n=`*(t: PType, val: PNode) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.nImpl = val
proc sym*(t: PType): PSym {.inline.} =
if t.state == Partial: loadType(t)
result = t.symImpl
proc `sym=`*(t: PType, val: PSym) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.symImpl = val
proc size*(t: PType): BiggestInt {.inline.} =
if t.state == Partial: loadType(t)
result = t.sizeImpl
proc `size=`*(t: PType, val: BiggestInt) {.inline.} =
backendEnsureMutable t
t.sizeImpl = val
proc align*(t: PType): int16 {.inline.} =
if t.state == Partial: loadType(t)
result = t.alignImpl
proc `align=`*(t: PType, val: int16) {.inline.} =
backendEnsureMutable t
t.alignImpl = val
proc paddingAtEnd*(t: PType): int16 {.inline.} =
if t.state == Partial: loadType(t)
result = t.paddingAtEndImpl
proc `paddingAtEnd=`*(t: PType, val: int16) {.inline.} =
backendEnsureMutable t
t.paddingAtEndImpl = val
proc loc*(t: PType): TLoc {.inline.} =
if t.state == Partial: loadType(t)
result = t.locImpl
proc `loc=`*(t: PType, val: TLoc) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.locImpl = val
proc typeInst*(t: PType): PType {.inline.} =
if t.state == Partial: loadType(t)
result = t.typeInstImpl
proc `typeInst=`*(t: PType, val: PType) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.typeInstImpl = val
proc incl*(t: PType; flag: TTypeFlag) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.flagsImpl.incl(flag)
proc incl*(t: PType; flags: set[TTypeFlag]) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.flagsImpl.incl(flags)
proc excl*(t: PType; flag: TTypeFlag) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.flagsImpl.excl(flag)
proc excl*(t: PType; flags: set[TTypeFlag]) {.inline.} =
assert t.state != Sealed
if t.state == Partial: loadType(t)
t.flagsImpl.excl(flags)
proc typ*(n: PNode): PType {.inline.} =
result = n.typField
if result == nil and nfLazyType in n.flags:
result = n.sym.typ
proc `typ=`*(n: PNode, val: sink PType) {.inline.} =
n.typField = val
template nodeId(n: PNode): int = cast[int](n)
type Gconfig = object
# we put comments in a side channel to avoid increasing `sizeof(TNode)`, which
# reduces memory usage given that `PNode` is the most allocated type by far.
comments: Table[int, string] # nodeId => comment
useIc*: bool
var gconfig {.threadvar.}: Gconfig
proc setUseIc*(useIc: bool) = gconfig.useIc = useIc
proc comment*(n: PNode): string =
if nfHasComment in n.flags and not gconfig.useIc:
# IC doesn't track comments, see `packed_ast`, so this could fail
result = gconfig.comments[n.nodeId]
else:
result = ""
proc `comment=`*(n: PNode, a: string) =
let id = n.nodeId
if a.len > 0:
# if needed, we could periodically cleanup gconfig.comments when its size increases,
# to ensure only live nodes (and with nfHasComment) have an entry in gconfig.comments;
# for compiling compiler, the waste is very small:
# num calls to newNodeImpl: 14984160 (num of PNode allocations)
# size of gconfig.comments: 33585
# num of nodes with comments that were deleted and hence wasted: 3081
n.flags.incl nfHasComment
gconfig.comments[id] = a
elif nfHasComment in n.flags:
n.flags.excl nfHasComment
gconfig.comments.del(id)
# BUGFIX: a module is overloadable so that a proc can have the
# same name as an imported module. This is necessary because of
# the poor naming choices in the standard library.
proc getPIdent*(a: PNode): PIdent {.inline.} =
## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`.
case a.kind
of nkSym: a.sym.name
of nkIdent: a.ident
of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym: a.sons[0].sym.name
else: nil
const
moduleShift = when defined(cpu32): 20 else: 24
template toId*(a: ItemId): int =
let x = a
(x.module.int shl moduleShift) + x.item.int
template id*(a: PType | PSym): int = toId(a.itemId)
type
IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
module*: int32
symId*: int32
typeId*: int32
sealed*: bool
disambTable*: CountTable[PIdent]
const
PackageModuleId* = -3'i32
proc idGeneratorFromModule*(m: PSym): IdGenerator =
assert m.kind == skModule
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0, disambTable: initCountTable[PIdent]())
proc idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator =
result = IdGenerator(module: PackageModuleId, symId: nextIdWillBe - 1'i32, typeId: 0, disambTable: initCountTable[PIdent]())
proc nextSymId(x: IdGenerator): ItemId {.inline.} =
assert(not x.sealed)
inc x.symId
result = ItemId(module: x.module, item: x.symId)
proc nextTypeId*(x: IdGenerator): ItemId {.inline.} =
assert(not x.sealed)
inc x.typeId
result = ItemId(module: x.module, item: x.typeId)
when false:
proc nextId*(x: IdGenerator): ItemId {.inline.} =
inc x.item
result = x[]
when false:
proc storeBack*(dest: var IdGenerator; src: IdGenerator) {.inline.} =
assert dest.ItemId.module == src.ItemId.module
if dest.ItemId.item > src.ItemId.item:
echo dest.ItemId.item, " ", src.ItemId.item, " ", src.ItemId.module
assert dest.ItemId.item <= src.ItemId.item
dest = src
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
proc isCallExpr*(n: PNode): bool =
result = n.kind in nkCallKinds
proc discardSons*(father: PNode)
proc safeArrLen*(n: PNode): int {.inline.} =
## works for array-like objects (strings passed as openArray in VM).
if n.kind in {nkStrLit..nkTripleStrLit}: result = n.strVal.len
elif n.kind in {nkNone..nkFloat128Lit}: result = 0
else: result = n.len
proc add*(father, son: PNode) =
assert son != nil
father.sons.add(son)
proc addAllowNil*(father, son: PNode) {.inline.} =
father.sons.add(son)
proc add*(father, son: PType) =
assert father.kind != tyProc or father.sonsImpl.len == 0
assert son != nil
father.sonsImpl.add son
proc addAllowNil*(father, son: PType) {.inline.} =
assert father.kind != tyProc or father.sonsImpl.len == 0
father.sonsImpl.add son
template `[]`*(n: PType, i: int): PType =
if n.state == Partial: loadType(n)
if n.kind == tyProc and i > 0:
assert n.nImpl[i] != nil and n.nImpl[i].sym != nil
n.nImpl[i].sym.typ
else:
n.sonsImpl[i]
template `[]=`*(n: PType, i: int; x: PType) =
if n.state == Partial: loadType(n)
if n.kind == tyProc and i > 0:
assert n.nImpl[i] != nil and n.nImpl[i].sym != nil
n.nImpl[i].sym.typ = x
else:
n.sonsImpl[i] = x
template `[]`*(n: PType, i: BackwardsIndex): PType =
if n.state == Partial: loadType(n)
n[n.len - i.int]
template `[]=`*(n: PType, i: BackwardsIndex; x: PType) =
if n.state == Partial: loadType(n)
n[n.len - i.int] = x
proc getDeclPragma*(n: PNode): PNode =
## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found.
## Currently only supports routineDefs + {nkTypeDef}.
case n.kind
of routineDefs:
if n[pragmasPos].kind != nkEmpty: result = n[pragmasPos]
else: result = nil
of nkTypeDef:
#[
type F3*{.deprecated: "x3".} = int
TypeSection
TypeDef
PragmaExpr
Postfix
Ident "*"
Ident "F3"
Pragma
ExprColonExpr
Ident "deprecated"
StrLit "x3"
Empty
Ident "int"
]#
if n[0].kind == nkPragmaExpr:
result = n[0][1]
else:
result = nil
else:
# support as needed for `nkIdentDefs` etc.
result = nil
if result != nil:
assert result.kind == nkPragma, $(result.kind, n.kind)
proc extractPragma*(s: PSym): PNode =
## gets the pragma node of routine/type/var/let/const symbol `s`
if s.kind in routineKinds: # bug #24167
let astVal = s.ast
if astVal != nil and astVal[pragmasPos] != nil and astVal[pragmasPos].kind != nkEmpty:
result = astVal[pragmasPos]
else:
result = nil
elif s.kind in {skType, skVar, skLet, skConst}:
let astVal = s.ast
if astVal != nil and astVal.len > 0:
if astVal[0].kind == nkPragmaExpr and astVal[0].len > 1:
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
result = astVal[0][1]
else:
result = nil
else:
result = nil
else:
result = nil
assert result == nil or result.kind == nkPragma
proc skipPragmaExpr*(n: PNode): PNode =
## if pragma expr, give the node the pragmas are applied to,
## otherwise give node itself
if n.kind == nkPragmaExpr:
result = n[0]
else:
result = n
proc setInfoRecursive*(n: PNode, info: TLineInfo) =
## set line info recursively
if n != nil:
for i in 0..<n.safeLen: setInfoRecursive(n[i], info)
n.info = info
proc newAtom*(ident: PIdent, info: TLineInfo): PNode =
result = newNode(nkIdent, info)
result.ident = ident
proc newAtom*(kind: TNodeKind, intVal: BiggestInt, info: TLineInfo): PNode =
result = newNode(kind, info)
result.intVal = intVal
proc newAtom*(kind: TNodeKind, floatVal: BiggestFloat, info: TLineInfo): PNode =
result = newNode(kind, info)
result.floatVal = floatVal
proc newAtom*(kind: TNodeKind; strVal: sink string; info: TLineInfo): PNode =
result = newNode(kind, info)
result.strVal = strVal
proc newTree*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode =
result = newNodeI(kind, info)
if children.len > 0:
result.info = children[0].info
result.sons = @children
proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
result = newNode(kind)
if children.len > 0:
result.info = children[0].info
result.sons = @children
proc newTreeI*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode =
result = newNodeI(kind, info)
if children.len > 0:
result.info = children[0].info
result.sons = @children
proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[PNode]): PNode =
result = newNodeIT(kind, info, typ)
if children.len > 0:
result.info = children[0].info
result.sons = @children
template previouslyInferred*(t: PType): PType =
if t.sons.len > 1: t.last else: nil
when false:
import tables, strutils
var x: CountTable[string]
addQuitProc proc () {.noconv.} =
for k, v in pairs(x):
echo k
echo v
proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
info: TLineInfo; options: TOptions = {}): PSym =
# generates a symbol and initializes the hash field too
assert not name.isNil
let id = nextSymId idgen
result = PSym(name: name, kindImpl: symKind, flagsImpl: {}, infoImpl: info, itemId: id,
optionsImpl: options, ownerFieldImpl: owner, offsetImpl: defaultOffset,
disamb: getOrDefault(idgen.disambTable, name).int32)
idgen.disambTable.inc name
when false:
if id.module == 48 and id.item == 39:
writeStackTrace()
echo "kind ", symKind, " ", name.s
if owner != nil: echo owner.name.s
proc astdef*(s: PSym): PNode =
# get only the definition (initializer) portion of the ast
let astVal = s.ast
if astVal != nil and astVal.kind in {nkIdentDefs, nkConstDef}:
astVal[2]
else:
astVal
proc isMetaType*(t: PType): bool =
return t.kind in tyMetaTypes or
(t.kind == tyStatic and t.n == nil) or
tfHasMeta in t.flags
proc isUnresolvedStatic*(t: PType): bool =
return t.kind == tyStatic and t.n == nil
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
t.sym = s
s.typImpl = t
result = t
proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
t.sym = s
s.typImpl = t
result = s
template fileIdx*(c: PSym): FileIndex =
# XXX: this should be used only on module symbols
c.position().FileIndex
template filename*(c: PSym): string =
# XXX: this should be used only on module symbols
c.position().FileIndex.toFilename
proc appendToModule*(m: PSym, n: PNode) =
## The compiler will use this internally to add nodes that will be
## appended to the module after the sem pass
if m.astImpl == nil:
m.astImpl = newNode(nkStmtList)
else:
assert m.astImpl.kind == nkStmtList
m.astImpl.add(n)
proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
dest.counter = src.counter
setLen(dest.data, src.data.len)
for i in 0..high(src.data): dest.data[i] = src.data[i]
proc copyIdTable*[T](dest: var TIdTable[T], src: TIdTable[T]) =
dest.counter = src.counter
newSeq(dest.data, src.data.len)
for i in 0..high(src.data): dest.data[i] = src.data[i]
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
dest.counter = src.counter
setLen(dest.data, src.data.len)
for i in 0..high(src.data): dest.data[i] = src.data[i]
proc discardSons*(father: PNode) =
father.sons = @[]
proc withInfo*(n: PNode, info: TLineInfo): PNode =
# XXX Dead code. Remove
n.info = info
return n
proc newSymNode*(sym: PSym): PNode =
result = newNode(nkSym)
result.sym = sym
result.typField = sym.typ
result.info = sym.info
proc newOpenSym*(n: PNode): PNode {.inline.} =
result = newTreeI(nkOpenSym, n.info, n)
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
result = newNode(kind)
result.intVal = intVal
proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
result = newNode(kind)
result.intVal = castToInt64(intVal)
proc lastSon*(n: PNode): PNode {.inline.} = n.sons[^1]
template setLastSon*(n: PNode, s: PNode) = n.sons[^1] = s
template firstSon*(n: PNode): PNode = n.sons[0]
template secondSon*(n: PNode): PNode = n.sons[1]
template hasSon*(n: PNode): bool = n.len > 0
template has2Sons*(n: PNode): bool = n.len > 1
proc replaceFirstSon*(n, newson: PNode) {.inline.} =
n.sons[0] = newson
proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} =
n.sons[i] = newson
proc last*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
if n.kind == tyProc and n.nImpl.len > 1:
n.nImpl[^1].sym.typ
else:
n.sonsImpl[^1]
proc elementType*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[^1]
proc skipModifier*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[^1]
proc indexType*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[0]
proc baseClass*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[0]
proc base*(t: PType): PType {.inline.} =
if t.state == Partial: loadType(t)
result = t.sonsImpl[0]
proc returnType*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[0]
proc setReturnType*(n, r: PType) {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[0] = r
proc setIndexType*(n, idx: PType) {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[0] = idx
proc firstParamType*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
if n.kind == tyProc:
n.nImpl[1].sym.typ
else:
n.sonsImpl[1]
proc firstGenericParam*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[1]
proc typeBodyImpl*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[^1]
proc genericHead*(n: PType): PType {.inline.} =
if n.state == Partial: loadType(n)
n.sonsImpl[0]
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
## Used throughout the compiler code to test whether a type tree contains or
## doesn't contain a specific type/types - it is often the case that only the
## last child nodes of a type tree need to be searched. This is a really hot
## path within the compiler!
result = t
while result.kind in kinds: result = last(result)
proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
let kind = skipTypes(typ, abstractVarRange).kind
case kind
of tyInt: result = newNode(nkIntLit)
of tyInt8: result = newNode(nkInt8Lit)
of tyInt16: result = newNode(nkInt16Lit)
of tyInt32: result = newNode(nkInt32Lit)
of tyInt64: result = newNode(nkInt64Lit)
of tyChar: result = newNode(nkCharLit)
of tyUInt: result = newNode(nkUIntLit)
of tyUInt8: result = newNode(nkUInt8Lit)
of tyUInt16: result = newNode(nkUInt16Lit)
of tyUInt32: result = newNode(nkUInt32Lit)
of tyUInt64: result = newNode(nkUInt64Lit)
of tyBool, tyEnum:
# XXX: does this really need to be the kind nkIntLit?
result = newNode(nkIntLit)
of tyStatic: # that's a pre-existing bug, will fix in another PR
result = newNode(nkIntLit)
else: raiseAssert $kind
result.intVal = intVal
result.typField = typ
proc newIntTypeNode*(intVal: Int128, typ: PType): PNode =
# XXX: introduce range check
newIntTypeNode(castToInt64(intVal), typ)
proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
result = newNode(kind)
result.floatVal = floatVal
proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
params,
name, pattern, genericParams,
pragmas, exceptions: PNode): PNode =
result = newNodeI(kind, info)
result.sons = @[name, pattern, genericParams, params,
pragmas, exceptions, body]
const
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
"=wasMoved", "=destroy", "=copy", "=dup", "=sink", "=trace", "=deepcopy"]
proc `$`*(s: PSym): string =
if s != nil:
result = s.name.s & "@" & $s.id
else:
result = "<nil>"
proc len*(n: PType): int {.inline.} =
if n.kind == tyProc:
result = if n.nImpl == nil: 0 else: n.nImpl.len
else:
result = n.sonsImpl.len
proc sameTupleLengths*(a, b: PType): bool {.inline.} =
result = a.len == b.len
iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
for i in 0 ..< a.len:
yield (i, a[i], b[i])
iterator underspecifiedPairs*(a, b: PType; start = 0; without = 0): (PType, PType) =
# XXX Figure out with what typekinds this is called.
for i in start ..< min(a.len, b.len) + without:
yield (a[i], b[i])
proc signatureLen*(t: PType): int {.inline.} =
result = t.len
proc paramsLen*(t: PType): int {.inline.} =
result = t.len - 1
proc genericParamsLen*(t: PType): int {.inline.} =
assert t.kind == tyGenericInst
result = t.len - 2 # without 'head' and 'body'
proc genericInvocationParamsLen*(t: PType): int {.inline.} =
assert t.kind == tyGenericInvocation
result = t.len - 1 # without 'head'
proc kidsLen*(t: PType): int {.inline.} =
result = t.len
proc genericParamHasConstraints*(t: PType): bool {.inline.} = t.len > 0
proc hasElementType*(t: PType): bool {.inline.} = t.len > 0
proc isEmptyTupleType*(t: PType): bool {.inline.} = t.len == 0
proc isSingletonTupleType*(t: PType): bool {.inline.} = t.len == 1
proc genericConstraint*(t: PType): PType {.inline.} = t[0]
iterator genericInstParams*(t: PType): (bool, PType) =
for i in 1..<t.len-1:
yield (i!=1, t[i])
iterator genericInstParamPairs*(a, b: PType): (int, PType, PType) =
for i in 1..<min(a.len, b.len)-1:
yield (i-1, a[i], b[i])
iterator genericInvocationParams*(t: PType): (bool, PType) =
for i in 1..<t.len:
yield (i!=1, t[i])
iterator genericInvocationAndBodyElements*(a, b: PType): (PType, PType) =
for i in 1..<a.len:
yield (a[i], b[i-1])
iterator genericInvocationParamPairs*(a, b: PType): (bool, PType, PType) =
for i in 1..<a.len:
if i >= b.len:
yield (false, nil, nil)
else:
yield (true, a[i], b[i])
iterator genericBodyParams*(t: PType): (int, PType) =
for i in 0..<t.len-1:
yield (i, t[i])
iterator userTypeClassInstParams*(t: PType): (bool, PType) =
for i in 1..<t.len-1:
yield (i!=1, t[i])
iterator ikids*(t: PType): (int, PType) =
for i in 0..<t.len: yield (i, t[i])
const
FirstParamAt* = 1
FirstGenericParamAt* = 1
iterator paramTypes*(t: PType): (int, PType) =
for i in FirstParamAt..<t.len: yield (i, t[i])
iterator paramTypePairs*(a, b: PType): (PType, PType) =
for i in FirstParamAt..<a.len: yield (a[i], b[i])
template paramTypeToNodeIndex*(x: int): int = x
iterator kids*(t: PType): PType =
for i in 0..<t.len: yield t[i]
iterator signature*(t: PType): PType =
# yields return type + parameter types
for i in 0..<t.len: yield t[i]
proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
let id = nextTypeId idgen
result = PType(kind: kind, ownerFieldImpl: owner, sizeImpl: defaultSize,
alignImpl: defaultAlignment, itemId: id,
uniqueId: id, sonsImpl: @[])
if son != nil:
assert kind != tyProc
result.sonsImpl.add son
when false:
if result.itemId.module == 55 and result.itemId.item == 2:
echo "KNID ", kind
writeStackTrace()
proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} =
assert dest.kind != tyProc or sons.len <= 1
dest.sonsImpl = sons
proc setSon*(dest: PType; son: sink PType) {.inline.} =
dest.sonsImpl = @[son]
proc setSonsLen*(dest: PType; len: int) {.inline.} =
assert dest.kind != tyProc or len <= 1
setLen(dest.sonsImpl, len)
proc mergeLoc(a: var TLoc, b: TLoc) =
if a.k == low(typeof(a.k)): a.k = b.k
if a.storage == low(typeof(a.storage)): a.storage = b.storage
a.flags.incl b.flags
if a.lode == nil: a.lode = b.lode
if a.snippet == "": a.snippet = b.snippet
proc newSons*(father: PNode, length: int) =
setLen(father.sons, length)
proc newSons*(father: PType, length: int) =
assert father.kind != tyProc or length <= 1
setLen(father.sonsImpl, length)
proc truncateInferredTypeCandidates*(t: PType) {.inline.} =
assert t.kind == tyInferred
if t.len > 1:
setLen(t.sonsImpl, 1)
proc assignType*(dest, src: PType) =
dest.kind = src.kind
dest.flagsImpl = src.flags
dest.callConvImpl = src.callConv
dest.nImpl = src.n
dest.sizeImpl = src.size
dest.alignImpl = src.align
# this fixes 'type TLock = TSysLock':
if src.sym != nil:
if dest.sym != nil:
var destFlags = dest.sym.flags
var srcFlags = src.sym.flags
dest.sym.flagsImpl = destFlags + (srcFlags - {sfUsed, sfExported})
if dest.sym.annex == nil: dest.sym.annexImpl = src.sym.annex
mergeLoc(dest.sym.locImpl, src.sym.loc)
else:
dest.symImpl = src.sym
if src.kind == tyProc:
# `tyProc` uses only `sonsImpl[0]` to store return type.
# parameter symbols and types are stored in `nImpl`.
assert src.sonsImpl.len <= 1
if src.len > 0:
setLen(dest.sonsImpl, 1)
dest.sonsImpl[0] = src.sonsImpl[0]
else:
newSons(dest, src.len)
for i in 0..<src.len: dest[i] = src[i]
proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
result = newType(t.kind, idgen, owner)
assignType(result, t)
result.symImpl = t.sym # backend-info should not be copied
proc exactReplica*(t: PType): PType =
result = PType(kind: t.kind, ownerFieldImpl: t.owner, sizeImpl: defaultSize,
alignImpl: defaultAlignment, itemId: t.itemId,
uniqueId: t.uniqueId)
assignType(result, t)
result.symImpl = t.sym # backend-info should not be copied
proc copySym*(s: PSym; idgen: IdGenerator): PSym =
result = newSym(s.kind, s.name, idgen, s.owner, s.info, s.options)
#result.astImpl = nil # BUGFIX; was: s.ast which made problems
result.typImpl = s.typ
result.flagsImpl = s.flags
result.magicImpl = s.magic
result.optionsImpl = s.options
result.positionImpl = s.position
result.locImpl = s.loc
result.annexImpl = s.annex # BUGFIX
result.constraintImpl = s.constraint
if result.kind in {skVar, skLet, skField}:
result.guardImpl = s.guard
result.bitsizeImpl = s.bitsize
result.alignmentImpl = s.alignment
proc createModuleAlias*(s: PSym, idgen: IdGenerator, newIdent: PIdent, info: TLineInfo;
options: TOptions): PSym =
result = newSym(s.kind, newIdent, idgen, s.owner, info, options)
# keep ID!
result.astImpl = s.ast
#result.id = s.id # XXX figure out what to do with the ID.
result.flagsImpl = s.flags
result.optionsImpl = s.options
result.positionImpl = s.position
result.locImpl = s.loc
result.annexImpl = s.annex
proc initStrTable*(): TStrTable =
result = TStrTable(counter: 0)
newSeq(result.data, StartSize)
proc initIdTable*[T](): TIdTable[T] =
result = TIdTable[T](counter: 0)
newSeq(result.data, StartSize)
proc resetIdTable*[T](x: var TIdTable[T]) =
x.counter = 0
# clear and set to old initial size:
setLen(x.data, 0)
setLen(x.data, StartSize)
proc initObjectSet*(): TObjectSet =
result = TObjectSet(counter: 0)
newSeq(result.data, StartSize)
proc initNodeTable*(ignoreTypes=false): TNodeTable =
result = TNodeTable(counter: 0, ignoreTypes: ignoreTypes)
newSeq(result.data, StartSize)
proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
result = t
var i = maxIters
while result.kind in kinds:
result = last(result)
dec i
if i == 0: return nil
proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
## same as skipTypes but handles 'nil'
result = t
while result != nil and result.kind in kinds:
if result.sonsImpl.len == 0: return nil
result = last(result)
proc isGCedMem*(t: PType): bool {.inline.} =
result = t.kind in {tyString, tyRef, tySequence} or
t.kind == tyProc and t.callConv == ccClosure
proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
owner.incl elem.flags * {tfHasMeta, tfTriggersCompileTime}
if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
owner.incl tfNotNil
if elem.isMetaType:
owner.incl tfHasMeta
let mask = elem.flags * {tfHasAsgn, tfHasOwned}
if mask != {} and propagateHasAsgn:
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
if o2.kind in {tyTuple, tyObject, tyArray,
tySequence, tyString, tySet, tyDistinct}:
o2.incl mask
owner.incl mask
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
tyGenericInvocation, tyPtr}:
let elemB = elem.skipTypes({tyGenericInst, tyAlias, tySink})
if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
# for simplicity, we propagate this flag even to generics. We then
# ensure this doesn't bite us in sempass2.
owner.incl tfHasGCedMem
proc rawAddSon*(father, son: PType; propagateHasAsgn = true) =
ensureMutable father
if father.kind != tyProc or father.sonsImpl.len == 0:
father.sonsImpl.add(son)
if not son.isNil: propagateToOwner(father, son, propagateHasAsgn)
proc addSonNilAllowed*(father, son: PNode) =
father.sons.add(son)
proc delSon*(father: PNode, idx: int) =
if father.len == 0: return
for i in idx..<father.len - 1: father[i] = father[i + 1]
father.sons.setLen(father.len - 1)
proc copyNode*(src: PNode): PNode =
# does not copy its sons!
if src == nil:
return nil
result = newNode(src.kind)
result.info = src.info
result.typ = src.typ
result.flags = src.flags * PersistentNodeFlags
result.comment = src.comment
when defined(useNodeIds):
if result.id == nodeIdToDebug:
echo "COMES FROM ", src.id
case src.kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLiterals: result.floatVal = src.floatVal
of nkSym: result.sym = src.sym
of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
else: discard
when defined(nimsuggest):
result.endInfo = src.endInfo
template transitionNodeKindCommon(k: TNodeKind) {.dirty.} =
let obj {.inject.} = n[]
n[] = TNode(kind: k, typField: n.typ, info: obj.info, flags: obj.flags)
# n.comment = obj.comment # shouldn't be needed, the address doesnt' change
when defined(useNodeIds):
n.id = obj.id
proc transitionSonsKind*(n: PNode, kind: range[nkComesFrom..nkTupleConstr]) =
transitionNodeKindCommon(kind)
n.sons = obj.sons
proc transitionIntKind*(n: PNode, kind: range[nkCharLit..nkUInt64Lit]) =
transitionNodeKindCommon(kind)
n.intVal = obj.intVal
proc transitionIntToFloatKind*(n: PNode, kind: range[nkFloatLit..nkFloat128Lit]) =
transitionNodeKindCommon(kind)
n.floatVal = BiggestFloat(obj.intVal)
proc transitionNoneToSym*(n: PNode) =
transitionNodeKindCommon(nkSym)
template transitionSymKindCommon*(k: TSymKind) =
let obj {.inject.} = s[]
s[] = TSym(kindImpl: k, itemId: obj.itemId, magicImpl: obj.magicImpl, typImpl: obj.typImpl, name: obj.name,
infoImpl: obj.infoImpl, ownerFieldImpl: obj.ownerFieldImpl, flagsImpl: obj.flagsImpl, astImpl: obj.astImpl,
optionsImpl: obj.optionsImpl, positionImpl: obj.positionImpl, offsetImpl: obj.offsetImpl,
locImpl: obj.locImpl, annexImpl: obj.annexImpl, constraintImpl: obj.constraintImpl)
when hasFFI:
s.cnameImpl = obj.cnameImpl
when defined(nimsuggest):
s.allUsagesImpl = obj.allUsagesImpl
proc transitionGenericParamToType*(s: PSym) =
transitionSymKindCommon(skType)
proc transitionRoutineSymKind*(s: PSym, kind: range[skProc..skTemplate]) =
transitionSymKindCommon(kind)
s.gcUnsafetyReasonImpl = obj.gcUnsafetyReasonImpl
s.transformedBodyImpl = obj.transformedBodyImpl
proc transitionToLet*(s: PSym) =
transitionSymKindCommon(skLet)
s.guardImpl = obj.guardImpl
s.bitsizeImpl = obj.bitsizeImpl
s.alignmentImpl = obj.alignmentImpl
template copyNodeImpl(dst, src, processSonsStmt) =
if src == nil: return
dst = newNode(src.kind)
dst.info = src.info
when defined(nimsuggest):
result.endInfo = src.endInfo
dst.typ = src.typ
dst.flags = src.flags * PersistentNodeFlags
dst.comment = src.comment
when defined(useNodeIds):
if dst.id == nodeIdToDebug:
echo "COMES FROM ", src.id
case src.kind
of nkCharLit..nkUInt64Lit: dst.intVal = src.intVal
of nkFloatLiterals: dst.floatVal = src.floatVal
of nkSym: dst.sym = src.sym
of nkIdent: dst.ident = src.ident
of nkStrLit..nkTripleStrLit: dst.strVal = src.strVal
else: processSonsStmt
proc shallowCopy*(src: PNode): PNode =
# does not copy its sons, but provides space for them:
copyNodeImpl(result, src):
newSeq(result.sons, src.len)
proc copyTree*(src: PNode): PNode =
# copy a whole syntax tree; performs deep copying
copyNodeImpl(result, src):
newSeq(result.sons, src.len)
for i in 0..<src.len:
result[i] = copyTree(src[i])
proc copyTreeWithoutNode*(src, skippedNode: PNode): PNode =
copyNodeImpl(result, src):
result.sons = newSeqOfCap[PNode](src.len)
for n in src.sons:
if n != skippedNode:
result.sons.add copyTreeWithoutNode(n, skippedNode)
proc hasSonWith*(n: PNode, kind: TNodeKind): bool =
for i in 0..<n.len:
if n[i].kind == kind:
return true
result = false
proc hasNilSon*(n: PNode): bool =
for i in 0..<n.safeLen:
if n[i] == nil:
return true
elif hasNilSon(n[i]):
return true
result = false
proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
result = false
if n == nil: return
case n.kind
of nkEmpty..nkNilLit: result = n.kind in kinds
else:
for i in 0..<n.len:
if n.kind in kinds or containsNode(n[i], kinds): return true
proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool =
case n.kind
of nkEmpty..nkNilLit, nkFormalParams: result = n.kind == kind
else:
for i in 0..<n.len:
if (n[i].kind == kind) or hasSubnodeWith(n[i], kind):
return true
result = false
proc getInt*(a: PNode): Int128 =
case a.kind
of nkCharLit, nkUIntLit..nkUInt64Lit:
result = toInt128(cast[uint64](a.intVal))
of nkInt8Lit..nkInt64Lit:
result = toInt128(a.intVal)
of nkIntLit:
# XXX: enable this assert
# assert a.typ.kind notin {tyChar, tyUint..tyUInt64}
result = toInt128(a.intVal)
else:
raiseRecoverableError("cannot extract number from invalid AST node")
proc getInt64*(a: PNode): int64 {.deprecated: "use getInt".} =
case a.kind
of nkCharLit, nkUIntLit..nkUInt64Lit, nkIntLit..nkInt64Lit:
result = a.intVal
else:
raiseRecoverableError("cannot extract number from invalid AST node")
proc getFloat*(a: PNode): BiggestFloat =
case a.kind
of nkFloatLiterals: result = a.floatVal
of nkCharLit, nkUIntLit..nkUInt64Lit, nkIntLit..nkInt64Lit:
result = BiggestFloat a.intVal
else:
raiseRecoverableError("cannot extract number from invalid AST node")
#doAssert false, "getFloat"
#internalError(a.info, "getFloat")
#result = 0.0
proc getStr*(a: PNode): string =
case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkNilLit:
# let's hope this fixes more problems than it creates:
result = ""
else:
raiseRecoverableError("cannot extract string from invalid AST node")
#doAssert false, "getStr"
#internalError(a.info, "getStr")
#result = ""
proc getStrOrChar*(a: PNode): string =
case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
else:
raiseRecoverableError("cannot extract string from invalid AST node")
#doAssert false, "getStrOrChar"
#internalError(a.info, "getStrOrChar")
#result = ""
proc isGenericParams*(n: PNode): bool {.inline.} =
## used to judge whether a node is generic params.
n != nil and n.kind == nkGenericParams
proc isGenericRoutine*(n: PNode): bool {.inline.} =
n != nil and n.kind in callableDefs and n[genericParamsPos].isGenericParams
proc isGenericRoutineStrict*(s: PSym): bool {.inline.} =
## determines if this symbol represents a generic routine
## the unusual name is so it doesn't collide and eventually replaces
## `isGenericRoutine`
s.kind in skProcKinds and s.ast.isGenericRoutine
proc isGenericRoutine*(s: PSym): bool {.inline.} =
## determines if this symbol represents a generic routine or an instance of
## one. This should be renamed accordingly and `isGenericRoutineStrict`
## should take this name instead.
##
## Warning/XXX: Unfortunately, it considers a proc kind symbol flagged with
## sfFromGeneric as a generic routine. Instead this should likely not be the
## case and the concepts should be teased apart:
## - generic definition
## - generic instance
## - either generic definition or instance
s.kind in skProcKinds and (sfFromGeneric in s.flags or
s.ast.isGenericRoutine)
proc skipGenericOwner*(s: PSym): PSym =
## Generic instantiations are owned by their originating generic
## symbol. This proc skips such owners and goes straight to the owner
## of the generic itself (the module or the enclosing proc).
result = if s.kind == skModule:
s
elif s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule:
s.owner.owner
else:
s.owner
proc originatingModule*(s: PSym): PSym =
result = s
while result.kind != skModule: result = result.owner
proc isRoutine*(s: PSym): bool {.inline.} =
result = s.kind in skProcKinds
proc isCompileTimeProc*(s: PSym): bool {.inline.} =
result = s.kind == skMacro or
s.kind in {skProc, skFunc} and sfCompileTime in s.flags
proc hasPattern*(s: PSym): bool {.inline.} =
result = isRoutine(s) and s.ast[patternPos].kind != nkEmpty
iterator pairs*(n: PNode): tuple[i: int, n: PNode] =
for i in 0..<n.safeLen: yield (i, n[i])
proc isAtom*(n: PNode): bool {.inline.} =
result = n.kind >= nkNone and n.kind <= nkNilLit
proc isEmptyType*(t: PType): bool {.inline.} =
## 'void' and 'typed' types are often equivalent to 'nil' these days:
result = t == nil or t.kind in {tyVoid, tyTyped}
proc makeStmtList*(n: PNode): PNode =
if n.kind == nkStmtList:
result = n
else:
result = newNodeI(nkStmtList, n.info)
result.add n
proc skipStmtList*(n: PNode): PNode =
if n.kind in {nkStmtList, nkStmtListExpr}:
for i in 0..<n.len-1:
if n[i].kind notin {nkEmpty, nkCommentStmt}: return n
result = n.lastSon
else:
result = n
proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType =
## If ``typ`` is not a tyVar then it is converted into a `var <typ>` and
## returned. Otherwise ``typ`` is simply returned as-is.
result = typ
if typ.kind != kind:
result = newType(kind, idgen, typ.owner, typ)
proc toRef*(typ: PType; idgen: IdGenerator): PType =
## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
## returned. Otherwise ``typ`` is simply returned as-is.
result = typ
if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject:
result = newType(tyRef, idgen, typ.owner, typ)
proc toObject*(typ: PType): PType =
## If ``typ`` is a tyRef then its immediate son is returned (which in many
## cases should be a ``tyObject``).
## Otherwise ``typ`` is simply returned as-is.
let t = typ.skipTypes({tyAlias, tyGenericInst})
if t.kind == tyRef: t.elementType
else: typ
proc toObjectFromRefPtrGeneric*(typ: PType): PType =
#[
See also `toObject`.
Finds the underlying `object`, even in cases like these:
type
B[T] = object f0: int
A1[T] = ref B[T]
A2[T] = ref object f1: int
A3 = ref object f2: int
A4 = object f3: int
]#
result = typ
while true:
case result.kind
of tyGenericBody: result = result.last
of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
# automatic dereferencing is deep, refs #18298.
else: break
# result does not have to be object type
proc isImportedException*(t: PType; conf: ConfigRef): bool =
assert t != nil
if conf.exc != excCpp:
return false
let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
result = base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}
proc isInfixAs*(n: PNode): bool =
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs)
proc skipColon*(n: PNode): PNode =
result = n
if n.kind == nkExprColonExpr:
result = n[1]
proc findUnresolvedStatic*(n: PNode): PNode =
if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil:
return n
if n.typ != nil and n.typ.kind == tyTypeDesc:
let t = skipTypes(n.typ, {tyTypeDesc})
if t.kind == tyGenericParam and not t.genericParamHasConstraints:
return n
for son in n:
let n = son.findUnresolvedStatic
if n != nil: return n
return nil
when false:
proc containsNil*(n: PNode): bool =
# only for debugging
if n.isNil: return true
for i in 0..<n.safeLen:
if n[i].containsNil: return true
template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
template incompleteType*(t: PType): bool =
t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward}
template typeCompleted*(s: PSym) =
incl s, sfNoForward
template detailedInfo*(sym: PSym): string =
sym.name.s
proc isInlineIterator*(typ: PType): bool {.inline.} =
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
proc isIterator*(typ: PType): bool {.inline.} =
typ.kind == tyProc and tfIterator in typ.flags
proc isClosureIterator*(typ: PType): bool {.inline.} =
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
proc isClosure*(typ: PType): bool {.inline.} =
typ.kind == tyProc and typ.callConv == ccClosure
proc isNimcall*(s: PSym): bool {.inline.} =
s.typ.callConv == ccNimCall
proc isExplicitCallConv*(s: PSym): bool {.inline.} =
tfExplicitCallConv in s.typ.flags
proc isSinkParam*(s: PSym): bool {.inline.} =
s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
proc isSinkType*(t: PType): bool {.inline.} =
t.kind == tySink or tfHasOwned in t.flags
proc newProcType*(info: TLineInfo; idgen: IdGenerator; owner: PSym): PType =
result = newType(tyProc, idgen, owner)
result.n = newNodeI(nkFormalParams, info)
rawAddSon(result, nil) # return type
# result.n[0] used to be `nkType`, but now it's `nkEffectList` because
# the effects are now stored in there too ... this is a bit hacky, but as
# usual we desperately try to save memory:
result.n.add newNodeI(nkEffectList, info)
proc addParam*(procType: PType; param: PSym) =
param.position = procType.n.len - 1
procType.n.add newSymNode(param)
rawAddSon(procType, param.typ)
const magicsThatCanRaise = {
mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho}
proc canRaiseConservative*(fn: PNode): bool =
if fn.kind == nkSym and fn.sym.magic notin magicsThatCanRaise:
result = false
else:
result = true
proc canRaise*(fn: PNode): bool =
if fn.kind == nkSym and (fn.sym.magic notin magicsThatCanRaise or
{sfImportc, sfInfixCall} * fn.sym.flags == {sfImportc} or
sfGeneratedOp in fn.sym.flags):
result = false
elif fn.kind == nkSym and fn.sym.magic == mEcho:
result = true
elif fn.typ != nil and fn.typ.kind == tyProc and fn.typ.n != nil:
# TODO check for n having sons? or just return false for now if not
if fn.typ.n[0].kind == nkSym:
result = false
else:
result = ((fn.typ.n[0].len < effectListLen) or
(fn.typ.n[0][exceptionEffects] != nil and
fn.typ.n[0][exceptionEffects].safeLen > 0))
else:
result = false
proc toHumanStrImpl[T](kind: T, num: static int): string =
result = $kind
result = result[num..^1]
result[0] = result[0].toLowerAscii
proc toHumanStr*(kind: TSymKind): string =
## strips leading `sk`
result = toHumanStrImpl(kind, 2)
proc toHumanStr*(kind: TTypeKind): string =
## strips leading `tk`
result = toHumanStrImpl(kind, 2)
proc skipHiddenAddr*(n: PNode): PNode {.inline.} =
(if n.kind == nkHiddenAddr: n[0] else: n)
proc isNewStyleConcept*(n: PNode): bool {.inline.} =
assert n.kind == nkTypeClassTy
result = n[0].kind == nkEmpty
proc isOutParam*(t: PType): bool {.inline.} = tfIsOutParam in t.flags
const
nodesToIgnoreSet* = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit,
nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
nkTypeOfExpr, nkMixinStmt, nkBindStmt}
proc isTrue*(n: PNode): bool =
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
n.kind == nkIntLit and n.intVal != 0
type
TypeMapping* = TIdTable[PType]
SymMapping* = TIdTable[PSym]
template initSymMapping*(): SymMapping = initIdTable[PSym]()
template initTypeMapping*(): TypeMapping = initIdTable[PType]()