mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-04 04:40:27 +00:00
The same treatment the `PNode` side just got, for the reason that applies to types: `t[0]` is the return type, the base class, the index type or the generic head depending on the kind, and the subscript says none of that. Every child access in the cgen files that has a named accessor now uses it — `baseClass` for the eleven object-hierarchy walks, `elementType` for the seq/openArray element, `returnType`, `genericHead`, `firstGenericParam` — and the two loops that walked a type's children become `paramTypes` and `kids`. Left indexed on purpose: a parameter reached by ARGUMENT position (`typ[i]` in ccgcalls/ccgstmts), a tuple field, and a generic parameter at an explicit index. There the index is the clearest thing to write. Every substitution is exact rather than merely close. `[]` with index 0 is unconditionally `sonsImpl[0]`, so `baseClass`/`returnType`/`genericHead` cannot diverge; `elementType` is `sonsImpl[^1]` and is used only where the type has a single son; `paramTypes` and `kids` are literally the loops they replace. `ast.sons(t: PType)` gets the warning it has been missing. Despite the name it is not the counterpart of the `sons` ITERATOR over a `PNode`: it returns the raw seq, and a `tyProc` keeps its parameter types in `n`, so that seq holds only the return type while `[]`/`len`/`kids` route parameters through `n[i].sym.typ`. `for x in t.sons` therefore compiles, reads exactly like the `PNode` idiom, and visits a different set of types — which is what `ccgutils.encodeType` would have started doing had it been converted to `sons` rather than `kids`. Marking the proc deprecated and rebuilding shows one call site in the whole compiler (`previouslyInferred`), so the trap is latent, not active. `bnode.nim` also records that there is deliberately no `BType` beside `BNode`: types stay `PType`s under `newIcBackend` — `BNode.typ` returns one — because the backend asks them questions (`skipTypes`, `getSize`, `lengthOrd`, the record walk over `t.n`) that a raw cursor cannot answer. Pure refactor: all 216 generated `.c` files byte-identical to the parent commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FMyRHByv7hhaQJ4Pa1bHbE
1804 lines
58 KiB
Nim
1804 lines
58 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, 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)
|
|
|
|
proc setIcMainModule*(fileIdx: FileIndex) =
|
|
## Tells the IC loader which module is being compiled fresh, so that
|
|
## re-exports of that module's symbols by dependencies are not loaded as
|
|
## duplicate stubs.
|
|
when not defined(nimKochBootstrap):
|
|
ast2nif.setMainModule(program, fileIdx)
|
|
|
|
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 unsealForTransform*(t: PType) {.inline.} =
|
|
## The transformer/lambda lifting also run inside `nim m` when the VM
|
|
## compiles a LOADED routine (macro evaluation, `getImpl`). Their mutations
|
|
## are process-local — transformed bodies are never written back to a NIF —
|
|
## so downgrade the loaded type to mutable, mirroring the `cmdNifC` loader
|
|
## which loads everything `Complete` for exactly this reason (see
|
|
## `ast2nif.loadedState`).
|
|
if t.state == Partial: loadType(t)
|
|
if t.state == Sealed: t.state = Complete
|
|
|
|
proc owner*(s: PSym): lent PSym {.inline.} =
|
|
if s.state == Partial: loadSym(s)
|
|
result = s.ownerFieldImpl
|
|
|
|
proc owner*(s: PType): lent 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): lent 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): lent 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): lent 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): lent 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): lent 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.} =
|
|
# No `Sealed` guard: the VM reuses `position` as a register slot while compiling
|
|
# a macro for execution (see `vmgen.genGenericParams`), which under IC may be a
|
|
# macro loaded from a NIF file. The macro is run, not code-generated, so this
|
|
# scratch mutation is harmless.
|
|
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): lent 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): lent 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): lent 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.} =
|
|
# No `assert s.state != Sealed`: `allUsagesImpl` is nimsuggest-only usage
|
|
# tracking, NOT part of the NIF-serialized symbol. nimsuggest loads symbols
|
|
# as `Sealed` (ast2nif.loadedState under cmdM) yet `suggestSym` legitimately
|
|
# records usages on them; the getter likewise doesn't assert.
|
|
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.} =
|
|
## The RAW child seq. Despite the name this is NOT the counterpart of the
|
|
## `sons` ITERATOR over a `PNode`, and it is not the way to walk a type's
|
|
## children — use `kids` / `ikids` / `paramTypes` / `signature`, or the named
|
|
## accessors (`returnType`, `baseClass`, `elementType`, `indexType`,
|
|
## `genericHead`, ...), which say WHICH child they mean.
|
|
##
|
|
## The difference is not cosmetic. A `tyProc` keeps its parameter types in
|
|
## `n`, not here — `setSons` asserts `sonsImpl.len <= 1` for one — so `[]`,
|
|
## `len` and every iterator built on them route parameters through
|
|
## `n[i].sym.typ`, while this seq holds only the return type. `for x in
|
|
## t.sons` therefore compiles, looks like the `PNode` idiom, and silently
|
|
## visits a different set of types.
|
|
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): lent 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): lent 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): lent 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): lent 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:
|
|
# NIF-based IC doesn't serialize comments, but the comment table is keyed by
|
|
# the node's address (`nodeId`), which is unique among live nodes; a loaded
|
|
# node that carries `nfHasComment` simply has no entry here (its comment was
|
|
# set in another process), so `getOrDefault` safely returns "" for it while
|
|
# in-process VM macro nodes (e.g. newCommentStmtNode) still round-trip.
|
|
result = gconfig.comments.getOrDefault(n.nodeId)
|
|
else:
|
|
result = ""
|
|
|
|
nodeCommentReader = proc(n: PNode): string {.nimcall.} = comment(n)
|
|
|
|
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)
|
|
|
|
nodeCommentWriter = proc(n: PNode; s: string) {.nimcall.} = n.comment = s
|
|
|
|
# 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
|
|
|
|
template id*(a: PSym): int = toId(a.itemId)
|
|
template id*(a: PType): int = toId(a.bindingId)
|
|
|
|
type
|
|
IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
|
|
module*: int32
|
|
symId*: int32
|
|
typeId*: int32
|
|
sealed*: bool
|
|
backendMinted*: bool
|
|
disambTable*: CountTable[PIdent]
|
|
|
|
proc idGeneratorFromModule*(m: PSym): IdGenerator =
|
|
assert m.kind == skModule
|
|
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0, disambTable: initCountTable[PIdent]())
|
|
result.disambTable.inc m.name
|
|
|
|
proc idGeneratorForBackend*(m: PSym): IdGenerator =
|
|
## Like `idGeneratorFromModule`, but for IC codegen (`nim nifc`): symbols and
|
|
## types minted fresh during codegen (transf labels/temps, lifted hooks, type
|
|
## copies) must not collide with the itemIds the NIF loader synthesizes for
|
|
## lazily-loaded symbols/types of the same module — those come from a
|
|
## per-module load-order counter that keeps running while codegen mints its
|
|
## own ids. A collision corrupts itemId-keyed tables, e.g. `transf`'s inline
|
|
## iterator mapping then substitutes a random loaded sym (a call's callee)
|
|
## with a `:tmp` block label. Backend-minted ids carry a marker bit in the
|
|
## module half (see `itemids.backendItemId`), so the two id spaces are
|
|
## disjoint by construction.
|
|
assert m.kind == skModule
|
|
result = IdGenerator(module: m.itemId.module, symId: 0, typeId: 0,
|
|
backendMinted: true, disambTable: initCountTable[PIdent]())
|
|
result.disambTable.inc m.name
|
|
|
|
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)
|
|
when not defined(nimKochBootstrap):
|
|
if x.backendMinted:
|
|
# Share the loader's per-module backend counter so a freshly-minted
|
|
# backend sym never collides with an `@bk` sym loaded from the module's
|
|
# `.t.bif` (see ast2nif.nextBackendSymItem).
|
|
let it = nextBackendSymItem(program, x.module)
|
|
if it >= 0'i32:
|
|
return backendItemId(x.module, it)
|
|
inc x.symId
|
|
result = if x.backendMinted: backendItemId(x.module, x.symId)
|
|
else: itemId(x.module, x.symId)
|
|
|
|
proc nextTypeId*(x: IdGenerator): ItemId {.inline.} =
|
|
assert(not x.sealed)
|
|
when not defined(nimKochBootstrap):
|
|
if x.backendMinted:
|
|
# Share the loader's per-module backend TYPE counter (seeded from the
|
|
# module's `(unusedid)`) so a freshly-minted backend type sits ABOVE every
|
|
# loaded type — never colliding with a frontend type's `toId` (the bug that
|
|
# crashed cgen's `getTypeDescAux` cycle check on `AsyncBufferRef`). Mirrors
|
|
# `nextSymId` (see ast2nif.nextBackendTypeItem).
|
|
let it = nextBackendTypeItem(program, x.module)
|
|
if it >= 0'i32:
|
|
return backendItemId(x.module, it)
|
|
inc x.typeId
|
|
result = if x.backendMinted: backendItemId(x.module, x.typeId)
|
|
else: itemId(x.module, 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) =
|
|
ensureMutable father
|
|
assert father.kind != tyProc or father.sonsImpl.len == 0
|
|
assert son != nil
|
|
father.sonsImpl.add son
|
|
|
|
proc addAllowNil*(father, son: PType) {.inline.} =
|
|
ensureMutable father
|
|
assert father.kind != tyProc or father.sonsImpl.len == 0
|
|
father.sonsImpl.add son
|
|
|
|
proc `[]`*(n: PType, i: int): PType {.inline.} =
|
|
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]
|
|
|
|
proc `[]=`*(n: PType, i: int; x: PType) {.inline.} =
|
|
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
|
|
|
|
proc `[]`*(n: PType, i: BackwardsIndex): PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
n[n.sonsImpl.len - i.int]
|
|
|
|
proc `[]=`*(n: PType, i: BackwardsIndex; x: PType) {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
n[n.sonsImpl.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
|
|
if result.typField == nil and nifcBackendActive:
|
|
# See the two-arg overload in astdef: in the NIF backend cg stage a sym node
|
|
# built from a not-yet-typed stub must track the symbol's type lazily.
|
|
result.flags.incl nfLazyType
|
|
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): lent 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): lent PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
result = n.sonsImpl[^1]
|
|
|
|
proc skipModifier*(n: PType): lent PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
result = n.sonsImpl[^1]
|
|
|
|
proc indexType*(n: PType): lent PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
result = n.sonsImpl[0]
|
|
|
|
proc baseClass*(n: PType): lent PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
result = n.sonsImpl[0]
|
|
|
|
proc base*(t: PType): lent PType {.inline.} =
|
|
if t.state == Partial: loadType(t)
|
|
result = t.sonsImpl[0]
|
|
|
|
proc returnType*(n: PType): lent PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
result = 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): lent PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
result = n.sonsImpl[1]
|
|
|
|
proc typeBodyImpl*(n: PType): lent PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
result = n.sonsImpl[^1]
|
|
|
|
proc genericHead*(n: PType): lent PType {.inline.} =
|
|
if n.state == Partial: loadType(n)
|
|
result = 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.state == Partial: loadType(n)
|
|
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,
|
|
bindingId: 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()
|
|
when defined(icDbg):
|
|
if kind == tyOpenArray:
|
|
echo "NEWTYPE openArray id=", id.module, ".", id.item,
|
|
" owner=", (if owner != nil: owner.name.s else: "nil")
|
|
echo getStackTrace()
|
|
|
|
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; idgen: IdGenerator): PType =
|
|
## Copy that INHERITS `bindingId` — the generic-param binding tables
|
|
## (`LayeredIdTable`) key on it, so the copy must keep matching its original
|
|
## there — while getting its own `itemId`, like every other type. The two
|
|
## remaining callers are `semtypinst.instCopyType` (a partially instantiated
|
|
## meta type must still bind in the next instantiation round) and the
|
|
## `tfUnresolved` typedesc replica in `semtypes.semTypeIdent`; everything
|
|
## else that used to come through here is a plain `copyType`.
|
|
##
|
|
## Do not "simplify" this to share `itemId` as well: `itemId` is the
|
|
## serialization identity, and replicas sharing it serialized as duplicate
|
|
## defs under one NIF name, which the loader collapsed into a single type —
|
|
## losing their flag differences (use-site `tfUnresolved` typedescs) or
|
|
## their structure (meta instance bodies shadowing a generic's canonical
|
|
## body).
|
|
result = PType(kind: t.kind, ownerFieldImpl: t.owner, sizeImpl: defaultSize,
|
|
alignImpl: defaultAlignment, itemId: nextTypeId(idgen),
|
|
bindingId: t.bindingId)
|
|
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.state == Partial: loadType(result)
|
|
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}:
|
|
if o2.state == Sealed:
|
|
# During the original compilation, propagateToOwner set tfHasAsgn/tfHasOwned on the type before it was sealed
|
|
# On IC reload, the sealed type already has those flags
|
|
assert mask <= o2.flags, "IC bug: sealed type missing propagated flags"
|
|
else:
|
|
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) =
|
|
# Under IC the symbol may still be an unloaded stub (`skStub`); materialise it
|
|
# first so its kind-specific fields (read below as `obj.*`) actually exist.
|
|
if s.state == Partial: loadSym(s)
|
|
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,
|
|
disamb: obj.disamb, locImpl: obj.locImpl, annexImpl: obj.annexImpl, constraintImpl: obj.constraintImpl,
|
|
instantiatedFromImpl: obj.instantiatedFromImpl)
|
|
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:
|
|
# A proc-typed value with no explicit raises slot still has
|
|
# unspecified effects, which sempass2 treats conservatively.
|
|
# Codegen needs to do the same in order to keep goto-exception
|
|
# checks after indirect/closure calls.
|
|
result = ((fn.typ.n[0].len < effectListLen) or
|
|
fn.typ.n[0][exceptionEffects] == nil or
|
|
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]()
|
|
|
|
proc sameModules*(a, b: PSym): bool {.inline.} =
|
|
assert a.kind == skModule and b.kind == skModule
|
|
result = a.position == b.position
|
|
|
|
proc sameOwners*(a, b: PSym): bool =
|
|
result = a == b or (a.kind == skModule and b.kind == skModule and a.position == b.position) or a.id == b.id
|