Compare commits

..

3 Commits

Author SHA1 Message Date
ringabout
a49f02a835 progress 2026-01-08 17:23:34 +08:00
ringabout
c73c88173f fixes 2026-01-08 17:21:06 +08:00
ringabout
f18b43098d enables dfa for refc strings 2026-01-07 23:04:26 +08:00
323 changed files with 5582 additions and 11852 deletions

View File

@@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

View File

@@ -15,7 +15,7 @@ jobs:
name: ${{ matrix.platform }}-bisects
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: Install OpenSSL (Windows)
if: |

View File

@@ -53,7 +53,7 @@ jobs:
steps:
- name: 'Checkout'
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 2
@@ -109,7 +109,7 @@ jobs:
if: |
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
matrix.target == 'linux'
uses: crazy-max/ghaction-github-pages@v5
uses: crazy-max/ghaction-github-pages@v4
with:
build_dir: doc/html
env:

View File

@@ -33,14 +33,14 @@ jobs:
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
steps:
- name: 'Checkout'
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: 'Install node.js'
uses: actions/setup-node@v6
- name: 'Install node.js 20.x'
uses: actions/setup-node@v4
with:
node-version: 24
node-version: '20.x'
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'

View File

@@ -17,14 +17,14 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: 'Checkout'
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: 'Install node.js'
uses: actions/setup-node@v6
uses: actions/setup-node@v4
with:
node-version: 24
node-version: ''
- name: 'Install dependencies (Linux amd64)'
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
@@ -60,7 +60,7 @@ jobs:
run: nim c -r -d:release ci/action.nim
- name: 'Comment'
uses: actions/github-script@v9
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');

View File

@@ -9,7 +9,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v10
- uses: actions/stale@v9
with:
days-before-pr-stale: 365
days-before-pr-close: 30

View File

@@ -33,12 +33,6 @@ errors.
- Bitshift operators (`shl`, `shr`, `ashr`) now apply bitmasking to the right operand in the C/C++/VM/JS backends.
- Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`.
- Procedure compatibility also checks the backend representation of the
parameter and result types, not just their source-level shape. Use
`--legacy:procParamTypeBackendAliases` to restore the older behavior.
## Standard library additions and changes
[//]: # "Additions:"
@@ -64,13 +58,6 @@ parameter and result types, not just their source-level shape. Use
- `copyDirWithPermissions` to recursively preserve attributes
- `system.setLenUninit` now supports refc, JS and VM backends.
- `system.setLenUninit` for the `string` type. Allows setting length without initializing new memory on growth.
- `std/parseopt` now supports multiple parser modes via a `CliMode` enum.
Modes include `Nim` (default, fully compatible) and two new experimental modes:
`Lax` and `Gnu` for different option parsing behaviors.
- `std/nre2` is added to replace deprecated NRE.
[//]: # "Changes:"
@@ -78,12 +65,6 @@ parameter and result types, not just their source-level shape. Use
- `min`, `max`, and `sequtils`' `minIndex`, `maxIndex` and `minmax` for `openArray`s now accept a comparison function.
- `system.substr` implementation now uses `copymem` (wrapped C `memcpy`) for copying data, if available at compilation.
- `system.newStringUninit` is now considered free of side-effects allowing it to be used with `--experimental:strictFuncs`.
- `std/re` and `std/nre` are deprecated as PCRE library is obsolete.
Use https://github.com/nitely/nim-regex or `std/nre2`.
See: https://github.com/nim-lang/Nim/issues/23668.
- `std/pegs` now correctly lexes UTF-8 bytes inside bare identifier-style
terminals, so case-insensitive matching of non-ASCII terms (e.g. ``\i café``)
works without single-quoting.
## Language changes
@@ -131,9 +112,7 @@ parameter and result types, not just their source-level shape. Use
## Tool changes
- Added `--raw` flag when generating JSON docs to not render markup.
- Added `--stdinfile` flag to name of the file used when running program from stdin (defaults to `stdinfile.nim`)
- Added `--styleCheck:warning` flag to treat style check violations as warnings.
## Documentation changes

View File

@@ -501,7 +501,6 @@ const
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 idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator =
result = IdGenerator(module: PackageModuleId, symId: nextIdWillBe - 1'i32, typeId: 0, disambTable: initCountTable[PIdent]())
@@ -550,25 +549,22 @@ 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.} =
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]
proc `[]=`*(n: PType, i: int; x: PType) {.inline.} =
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
@@ -576,13 +572,12 @@ proc `[]=`*(n: PType, i: int; x: PType) {.inline.} =
else:
n.sonsImpl[i] = x
proc `[]`*(n: PType, i: BackwardsIndex): PType {.inline.} =
template `[]`*(n: PType, i: BackwardsIndex): PType =
if n.state == Partial: loadType(n)
n[n.sonsImpl.len - i.int]
proc `[]=`*(n: PType, i: BackwardsIndex; x: PType) {.inline.} =
n[n.len - i.int]
template `[]=`*(n: PType, i: BackwardsIndex; x: PType) =
if n.state == Partial: loadType(n)
n[n.sonsImpl.len - i.int] = x
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.
@@ -935,7 +930,6 @@ proc `$`*(s: PSym): string =
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:
@@ -1174,7 +1168,6 @@ 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)
@@ -1196,12 +1189,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
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
o2.incl mask
owner.incl mask
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
@@ -1275,8 +1263,7 @@ template transitionSymKindCommon*(k: TSymKind) =
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)
locImpl: obj.locImpl, annexImpl: obj.annexImpl, constraintImpl: obj.constraintImpl)
when hasFFI:
s.cnameImpl = obj.cnameImpl
when defined(nimsuggest):

View File

@@ -56,12 +56,12 @@ proc toConverterIndexEntry*(config: ConfigRef; converterSym: PSym): (nifstreams.
# Fallback: return empty entry
result = (nifstreams.SymId(0), nifstreams.SymId(0))
proc toMethodIndexEntry*(config: ConfigRef; methodSym: PSym; signature: string): (nifstreams.SymId, nifstreams.StrId) =
## Converts a method symbol/signature to a method index entry.
proc toMethodIndexEntry*(config: ConfigRef; methodSym: PSym; signature: string): MethodIndexEntry =
## Converts a method symbol to a MethodIndexEntry.
let methodSymName = methodSym.name.s & "." & $methodSym.disamb & "." & cachedModuleSuffix(config, methodSym.itemId.module.FileIndex)
result = (
pool.syms.getOrIncl(methodSymName),
pool.strings.getOrIncl(signature)
result = MethodIndexEntry(
fn: pool.syms.getOrIncl(methodSymName),
signature: pool.strings.getOrIncl(signature)
)
proc toClassSymId*(config: ConfigRef; typeId: ItemId): nifstreams.SymId =
@@ -159,6 +159,7 @@ type
inProc: int
#writtenTypes: seq[PType] # types written in this module, to be unloaded later
#writtenSyms: seq[PSym] # symbols written in this module, to be unloaded later
exports: Table[FileIndex, HashSet[string]] # module -> specific symbol names (empty = all)
writtenPackages: HashSet[string]
const
@@ -167,8 +168,7 @@ const
proc isLocalSym(sym: PSym): bool {.inline.} =
sym.kindImpl in skLocalSymKinds or
(sym.kindImpl in {skVar, skLet} and {sfGlobal, sfThread} * sym.flagsImpl == {} and
(sym.ownerFieldImpl == nil or sym.ownerFieldImpl.kindImpl != skModule))
(sym.kindImpl in {skVar, skLet} and {sfGlobal, sfThread} * sym.flagsImpl == {})
proc toNifSymName(w: var Writer; sym: PSym): string =
## Generate NIF name for a symbol: local names are `ident.disamb`,
@@ -253,7 +253,6 @@ proc writeLoc(w: var Writer; dest: var TokenBuf; loc: TLoc) =
proc writeTypeDef(w: var Writer; dest: var TokenBuf; typ: PType) =
dest.buildTree tdefTag:
dest.addSymDef pool.syms.getOrIncl(typeToNifSym(typ, w.infos.config)), NoLineInfo
dest.addDotToken # always private for the index generator
#dest.addIdent toNifTag(typ.kind)
writeFlags(dest, typ.flagsImpl)
@@ -322,7 +321,7 @@ proc collectGenericParams(w: var Writer; n: PNode) =
proc writeSymDef(w: var Writer; dest: var TokenBuf; sym: PSym) =
dest.addParLe sdefTag, trLineInfo(w, sym.infoImpl)
dest.addSymDef pool.syms.getOrIncl(w.toNifSymName(sym)), NoLineInfo
if {sfExported, sfFromGeneric} * sym.flagsImpl == {sfExported}:
if sfExported in sym.flagsImpl:
dest.addIdent "x"
else:
dest.addDotToken
@@ -346,8 +345,6 @@ proc writeSymDef(w: var Writer; dest: var TokenBuf; sym: PSym) =
if sym.kindImpl == skModule:
dest.addDotToken() # position will be set by the loader!
elif sym.kindImpl in {skVar, skLet, skForVar, skResult}:
dest.addIntLit 0 # hack for the VM which uses this field to store information
else:
dest.addIntLit sym.positionImpl
@@ -477,40 +474,6 @@ proc trImport(w: var Writer; n: PNode) =
w.deps.addStrLit fp # raw string literal, no wrapper needed
w.deps.addParRi
proc trExport(w: var Writer; n: PNode) =
# Collect export information for the index
# nkExportStmt children are nkSym nodes
# When exporting a module (export dollars), the module symbol is a child
# followed by all symbols from that module - we use empty set to mean "export all"
# When exporting specific symbols (export foo, bar), we collect their names
w.deps.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), trLineInfo(w, n.info)
w.deps.addDotToken # flags
w.deps.addDotToken # type
for child in n:
if child.kind == nkSym:
let s = child.sym
if s.kindImpl == skModule:
discard "do not write module syms here"
else:
w.deps.addSymUse pool.syms.getOrIncl(w.toNifSymName(s)), NoLineInfo
w.deps.addParRi
let replayTag = registerTag("replay")
let repConverterTag = registerTag("repconverter")
let repDestroyTag = registerTag("repdestroy")
let repWasMovedTag = registerTag("repwasmoved")
let repCopyTag = registerTag("repcopy")
let repSinkTag = registerTag("repsink")
let repDupTag = registerTag("repdup")
let repTraceTag = registerTag("reptrace")
let repDeepCopyTag = registerTag("repdeepcopy")
let repEnumToStrTag = registerTag("repenumtostr")
let repMethodTag = registerTag("repmethod")
#let repClassTag = registerTag("repclass")
let includeTag = registerTag("include")
let importTag = registerTag("import")
let implTag = registerTag("implementation")
proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
if n == nil:
dest.addDotToken
@@ -524,7 +487,9 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
of nkEmpty:
if n.typField != nil:
w.withNode dest, n:
discard
let info = trLineInfo(w, n.info)
dest.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), info
dest.addParRi
else:
let info = trLineInfo(w, n.info)
dest.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), info
@@ -616,9 +581,37 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
of nkIncludeStmt:
trInclude w, n
of nkExportStmt, nkExportExceptStmt:
# Collect export information for the index
# nkExportStmt children are nkSym nodes
# When exporting a module (export dollars), the module symbol is a child
# followed by all symbols from that module - we use empty set to mean "export all"
# When exporting specific symbols (export foo, bar), we collect their names
# Note: nkExportExceptStmt is transformed to nkExportStmt by semExportExcept,
# but we handle both just in case
trExport w, n
var exportAllModules = initHashSet[FileIndex]()
for child in n:
if child.kind == nkSym:
let s = child.sym
if s.kindImpl == skModule:
# Export all from this module - use empty set
let modIdx = s.positionImpl.FileIndex
exportAllModules.incl modIdx
if modIdx notin w.exports:
w.exports[modIdx] = initHashSet[string]() # empty means "export all"
else:
# Export specific symbol, but only if we're not already exporting all from this module
let modIdx = s.itemId.module.FileIndex
if modIdx notin exportAllModules:
if modIdx notin w.exports:
w.exports[modIdx] = initHashSet[string]()
w.exports[modIdx].incl s.name.s
# Write the export statement as a regular node
w.withNode dest, n:
for i in 0 ..< n.len:
if n[i].kind == nkSym and n[i].sym.kindImpl == skModule:
discard "do not write module syms here"
else:
writeNode(w, dest, n[i], forAst)
else:
w.withNode dest, n:
for i in 0 ..< n.len:
@@ -670,6 +663,40 @@ proc createStmtList(buf: var TokenBuf; info: PackedLineInfo) {.inline.} =
buf.addDotToken # flags
buf.addDotToken # type
proc buildExportBuf(w: var Writer): TokenBuf =
## Build the export section for the NIF index from collected exports
result = createTokenBuf(32)
for modIdx, names in w.exports:
let path = toFullPath(w.infos.config, modIdx)
if names.len == 0:
# Export all from this module
result.addParLe(TagId(ExportIdx), NoLineInfo)
result.add strToken(pool.strings.getOrIncl(path), NoLineInfo)
result.addParRi()
else:
# Export specific symbols
result.addParLe(TagId(FromexportIdx), NoLineInfo)
result.add strToken(pool.strings.getOrIncl(path), NoLineInfo)
for name in names:
result.add identToken(pool.strings.getOrIncl(name), NoLineInfo)
result.addParRi()
let replayTag = registerTag("replay")
let repConverterTag = registerTag("repconverter")
let repDestroyTag = registerTag("repdestroy")
let repWasMovedTag = registerTag("repwasmoved")
let repCopyTag = registerTag("repcopy")
let repSinkTag = registerTag("repsink")
let repDupTag = registerTag("repdup")
let repTraceTag = registerTag("reptrace")
let repDeepCopyTag = registerTag("repdeepcopy")
let repEnumToStrTag = registerTag("repenumtostr")
let repMethodTag = registerTag("repmethod")
#let repClassTag = registerTag("repclass")
let includeTag = registerTag("include")
let importTag = registerTag("import")
let implTag = registerTag("implementation")
proc writeOp(w: var Writer; content: var TokenBuf; op: LogEntry) =
case op.kind
of HookEntry:
@@ -699,10 +726,7 @@ proc writeOp(w: var Writer; content: var TokenBuf; op: LogEntry) =
of MethodEntry:
discard "to implement"
of EnumToStrEntry:
content.addParLe repEnumToStrTag, NoLineInfo
content.add strToken(pool.strings.getOrIncl(op.key), NoLineInfo)
content.add symToken(pool.syms.getOrIncl(w.toNifSymName(op.sym)), NoLineInfo)
content.addParRi()
discard "to implement"
of GenericInstEntry:
discard "will only be written later to ensure it is materialized"
@@ -760,6 +784,10 @@ proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode;
writeFile(dest, d)
let exportBuf = buildExportBuf(w)
createIndex(d, dest[0].info, false,
IndexSections(exportBuf: exportBuf))
# --------------------------- Loader (lazy!) -----------------------------------------------
proc nodeKind(n: Cursor): TNodeKind {.inline.} =
@@ -817,7 +845,7 @@ type
NifModule = ref object
stream: nifstreams.Stream
symCounter: int32
index: Table[string, NifIndexEntry] # Simple embedded index for offsets
index: NifIndex
suffix: string
DecodeContext* = object
@@ -843,89 +871,46 @@ type
LoadFlag* = enum
LoadFullAst, AlwaysLoadInterface
proc readEmbeddedIndex(s: var Stream): Table[string, NifIndexEntry] =
## Reads the simple embedded index (index (kv sym offset)...) from indexStartsAt position.
result = initTable[string, NifIndexEntry]()
let indexPos = indexStartsAt(s.r)
if indexPos <= 0:
return
let contentPos = offset(s.r) # Save position
s.r.jumpTo(indexPos)
var previousOffset = 0
var t = next(s)
let exportedTagId = pool.tags.getOrIncl("x")
if t.kind == ParLe and pool.tags[t.tagId] == ".index":
t = next(s)
while t.kind != EofToken and t.kind != ParRi:
if t.kind == ParLe:
let vis = if t.tagId == exportedTagId: Exported else: Hidden
let info = t.info
t = next(s) # skip (kv
var key = ""
if t.kind == Symbol:
key = pool.syms[t.symId]
elif t.kind == Ident:
key = pool.strings[t.litId]
t = next(s) # skip symbol
if t.kind == IntLit:
let offset = int(pool.integers[t.intId]) + previousOffset
result[key] = NifIndexEntry(offset: offset, info: info, vis: vis)
previousOffset = offset
t = next(s) # skip offset
if t.kind == ParRi:
t = next(s) # skip )
else:
t = next(s)
s.r.jumpTo(contentPos) # Restore position
proc moduleId(c: var DecodeContext; suffix: string; flags: set[LoadFlag] = {}): FileIndex =
var isKnownFile = false
result = c.infos.config.registerNifSuffix(suffix, isKnownFile)
# Always load the module's index if it's not already in c.mods
# This is needed when resolving symbols from modules that were registered elsewhere
# but haven't had their NIF index loaded yet
let hasEntry = c.mods.hasKey(result)
if not hasEntry or AlwaysLoadInterface in flags:
if not isKnownFile or AlwaysLoadInterface in flags:
let modFile = (getNimcacheDir(c.infos.config) / RelativeFile(suffix & ".nif")).string
let idxFile = (getNimcacheDir(c.infos.config) / RelativeFile(suffix & ".s.idx.nif")).string
if not fileExists(modFile):
raiseAssert "NIF file not found for module suffix '" & suffix & "': " & modFile &
". This can happen when loading a module from NIF that references another module " &
"whose NIF file hasn't been written yet."
var stream = nifstreams.open(modFile)
let index = readEmbeddedIndex(stream)
c.mods[result] = NifModule(stream: stream, index: index, suffix: suffix)
c.mods[result] = NifModule(stream: nifstreams.open(modFile), index: readIndex(idxFile), suffix: suffix)
proc getOffset(c: var DecodeContext; module: FileIndex; nifName: string): NifIndexEntry =
let ii = addr c.mods[module].index
result = ii[].getOrDefault(nifName)
result = ii.public.getOrDefault(nifName)
if result.offset == 0:
raiseAssert "symbol has no offset: " & nifName
result = ii.private.getOrDefault(nifName)
if result.offset == 0:
raiseAssert "symbol has no offset: " & nifName
proc loadNode(c: var DecodeContext; n: var Cursor; thisModule: string;
localSyms: var Table[string, PSym]): PNode
proc loadSymFromCursor(c: var DecodeContext; s: PSym; n: var Cursor; thisModule: string;
localSyms: var Table[string, PSym])
proc createTypeStub(c: var DecodeContext; t: SymId): PType =
let name = pool.syms[t]
assert name.startsWith("`t")
var i = len("`t")
var k = 0
while i < name.len and name[i] in {'0'..'9'}:
k = k * 10 + name[i].ord - ord('0')
inc i
if i < name.len and name[i] == '.': inc i
var itemId = 0'i32
while i < name.len and name[i] in {'0'..'9'}:
itemId = itemId * 10'i32 + int32(name[i].ord - ord('0'))
inc i
if i < name.len and name[i] == '.': inc i
let suffix = name.substr(i)
result = c.types.getOrDefault(name)[0]
if result == nil:
var i = len("`t")
var k = 0
while i < name.len and name[i] in {'0'..'9'}:
k = k * 10 + name[i].ord - ord('0')
inc i
if i < name.len and name[i] == '.': inc i
var itemId = 0'i32
while i < name.len and name[i] in {'0'..'9'}:
itemId = itemId * 10'i32 + int32(name[i].ord - ord('0'))
inc i
if i < name.len and name[i] == '.': inc i
let suffix = name.substr(i)
let id = ItemId(module: moduleId(c, suffix).int32, item: itemId)
let offs = c.getOffset(id.module.FileIndex, name)
result = PType(itemId: id, uniqueId: id, kind: TTypeKind(k), state: Partial)
@@ -934,8 +919,8 @@ proc createTypeStub(c: var DecodeContext; t: SymId): PType =
proc extractLocalSymsFromTree(c: var DecodeContext; n: var Cursor; thisModule: string;
localSyms: var Table[string, PSym]) =
## Scan a tree for local symbol definitions (sdef tags) and add them to localSyms.
## For local symbols, fully load them immediately since they have no index offsets.
## After this proc returns, n is positioned AFTER the tree.
## This doesn't fully load the symbols, just pre-registers them so references
## can find them. After this proc returns, n is positioned AFTER the tree.
# Handle atoms (non-compound nodes) - just skip them
if n.kind != ParLe:
inc n
@@ -946,26 +931,18 @@ proc extractLocalSymsFromTree(c: var DecodeContext; n: var Cursor; thisModule: s
if n.tagId == sdefTag:
# Found an sdef - check if it's local
let name = n.firstSon
expect name, SymbolDef
let symName = pool.syms[name.symId]
let sn = parseSymName(symName)
if sn.module.len == 0 and symName notin localSyms:
# Local symbol - create stub and immediately load it fully
# since local symbols have no index offsets for lazy loading
let module = moduleId(c, thisModule)
let val = addr c.mods[module].symCounter
inc val[]
let id = ItemId(module: module.int32, item: val[])
let sym = PSym(itemId: id, kindImpl: skStub, name: c.cache.getIdent(sn.name),
disamb: sn.count.int32, state: Complete)
localSyms[symName] = sym
# Load the full symbol definition immediately
# We're currently at the `(sd` position, need to skip to SymbolDef
inc n # skip past `sd` tag to get to SymbolDef
loadSymFromCursor(c, sym, n, thisModule, localSyms)
sym.state = Sealed # mark as fully loaded
# Continue processing - loadSymFromCursor already advanced n past the closing `)`
continue
if name.kind == SymbolDef:
let symName = pool.syms[name.symId]
let sn = parseSymName(symName)
if sn.module.len == 0 and symName notin localSyms:
# Local symbol - create a stub entry in localSyms
let module = moduleId(c, thisModule)
let val = addr c.mods[module].symCounter
inc val[]
let id = ItemId(module: module.int32, item: val[])
let sym = PSym(itemId: id, kindImpl: skStub, name: c.cache.getIdent(sn.name),
disamb: sn.count.int32, state: Complete)
localSyms[symName] = sym
inc depth
elif n.kind == ParRi:
dec depth
@@ -1089,8 +1066,6 @@ proc loadTypeFromCursor(c: var DecodeContext; n: var Cursor; t: PType; localSyms
expect n, SymbolDef
# ignore the type's name, we have already used it to create this PType's itemId!
inc n
expect n, DotToken
inc n
#loadField t.kind
loadField t.flagsImpl
loadField t.callConvImpl
@@ -1405,33 +1380,83 @@ proc extractBasename(nifName: string): string =
proc populateInterfaceTablesFromIndex(c: var DecodeContext; module: FileIndex;
interf, interfHidden: var TStrTable; thisModule: string) =
## Populates interface tables from the NIF index structure.
## Uses the simple embedded index for offsets, exports passed from processTopLevel.
## Uses the index's public/private tables instead of traversing AST.
# Move the index table out to avoid iterator invalidation
# Move the public table and exports list out to avoid iterator invalidation
# (moduleId can add to c.mods which would invalidate Table iterators)
var indexTab = move c.mods[module].index
# We move them back after iteration.
var publicTab = move c.mods[module].index.public
var exportsList = move c.mods[module].index.exports
# Add all symbols to interf (exported interface) and interfHidden
for nifName, entry in indexTab:
if entry.vis == Exported:
let sym = loadSymFromIndexEntry(c, module, nifName, entry, thisModule)
if sym != nil:
strTableAdd(interf, sym)
strTableAdd(interfHidden, sym)
elif not nifName.startsWith("`t"):
# Add all public symbols to interf (exported interface) and interfHidden
for nifName, entry in publicTab:
if not nifName.startsWith("`t"):
# do not load types, they are not part of an interface but an implementation detail!
#echo "LOADING SYM ", nifName, " ", entry.offset
let sym = loadSymFromIndexEntry(c, module, nifName, entry, thisModule)
if sym != nil:
strTableAdd(interf, sym)
strTableAdd(interfHidden, sym)
# Move index table back
c.mods[module].index = move indexTab
# Move public table back
c.mods[module].index.public = move publicTab
# Process exports (re-exports from other modules)
for exp in exportsList:
let (path, kind, names) = exp
# Convert path to module suffix
let expSuffix = moduleSuffix(path, cast[seq[string]](c.infos.config.searchPaths))
# Load the exported module's index
let expModule = moduleId(c, expSuffix)
# Move the exported module's public table out to avoid iterator invalidation
var expPublicTab = move c.mods[expModule].index.public
# Build a set of names for filtering
var nameSet = initHashSet[string]()
for nameId in names:
nameSet.incl pool.strings[nameId]
# Add symbols based on export kind
for nifName, entry in expPublicTab:
if nifName.startsWith("`t"):
continue # skip types
let basename = extractBasename(nifName)
let shouldInclude =
case kind
of ExportIdx: true # export all
of FromexportIdx: basename in nameSet # only specific names
of ExportexceptIdx: basename notin nameSet # all except specific names
else: false
if shouldInclude:
let sym = loadSymFromIndexEntry(c, expModule, nifName, entry, expSuffix)
if sym != nil:
strTableAdd(interf, sym)
strTableAdd(interfHidden, sym)
# Move exported module's public table back
c.mods[expModule].index.public = move expPublicTab
# Move exports list back
c.mods[module].index.exports = move exportsList
when false:
# Add private symbols to interfHidden only
for nifName, entry in idx.private:
let sym = loadSymFromIndexEntry(c, module, nifName, entry, thisModule)
if sym != nil:
strTableAdd(interfHidden, sym)
proc toNifFilename*(conf: ConfigRef; f: FileIndex): string =
let suffix = moduleSuffix(conf, f)
result = toGeneratedFile(conf, AbsoluteFile(suffix), ".nif").string
proc toNifIndexFilename*(conf: ConfigRef; f: FileIndex): string =
let suffix = moduleSuffix(conf, f)
result = toGeneratedFile(conf, AbsoluteFile(suffix), ".s.idx.nif").string
proc resolveSym(c: var DecodeContext; symAsStr: string; alsoConsiderPrivate: bool): PSym =
result = c.syms.getOrDefault(symAsStr)[0]
if result != nil:
@@ -1442,17 +1467,14 @@ proc resolveSym(c: var DecodeContext; symAsStr: string; alsoConsiderPrivate: boo
return nil # Local symbols shouldn't be hooks
let module = moduleId(c, sn.module)
# Look up the symbol in the module's index
# Try both formats: with module suffix (e.g., "foo.0.modulename") and without (e.g., "foo.0.")
# NIF spec allows local symbols to be stored without module suffix
var offs = c.mods[module].index.getOrDefault(symAsStr)
var offs = c.mods[module].index.public.getOrDefault(symAsStr)
if offs.offset == 0:
# Try the format without module suffix
let localKey = sn.name & "." & $sn.count & "."
offs = c.mods[module].index.getOrDefault(localKey)
if offs.offset == 0:
return nil
if not alsoConsiderPrivate and offs.vis == Hidden:
return nil
if alsoConsiderPrivate:
offs = c.mods[module].index.private.getOrDefault(symAsStr)
if offs.offset == 0:
return nil
else:
return nil
# Create a stub symbol
let val = addr c.mods[module].symCounter
inc val[]
@@ -1544,14 +1566,12 @@ proc loadImport(c: var DecodeContext; s: var Stream; deps: var seq[ModuleSuffix]
else:
raiseAssert "expected ParRi but got " & $tok.kind
proc processTopLevel(c: var DecodeContext; s: var Stream; flags: set[LoadFlag];
interf: var TStrTable; suffix: string; module: int): PrecompiledModule =
proc processTopLevel(c: var DecodeContext; s: var Stream; flags: set[LoadFlag] = {}; suffix: string; module: int): PrecompiledModule =
result = PrecompiledModule(topLevel: newNode(nkStmtList))
var localSyms = initTable[string, PSym]()
var t = next(s) # skip dot
var cont = true
let exportTag = pool.tags.getOrIncl"export"
while cont and t.kind != EofToken:
if t.kind == ParLe:
if t.tagId == replayTag:
@@ -1592,24 +1612,6 @@ proc processTopLevel(c: var DecodeContext; s: var Stream; flags: set[LoadFlag];
t = loadLogOp(c, result.logOps, s, MethodEntry, attachedTrace, module)
#elif t.tagId == repClassTag:
# t = loadLogOp(c, logOps, s, ClassEntry, attachedTrace, module)
elif t.tagId == exportTag:
t = next(s) # skip (export
if t.kind == DotToken:
t = next(s) # skip dot
if t.kind == DotToken:
t = next(s) # skip dot
while true:
if t.kind == Symbol:
let symAsStr = pool.syms[t.symId]
let sym = resolveSym(c, symAsStr, false)
if sym != nil:
strTableAdd(interf, sym)
t = next(s)
elif t.kind == ParRi:
break
else:
raiseAssert "expected Symbol or ParRi but got " & $t.kind
t = next(s)
elif t.tagId == includeTag:
t = skipTree(s)
elif t.tagId == importTag:
@@ -1632,25 +1634,25 @@ proc processTopLevel(c: var DecodeContext; s: var Stream; flags: set[LoadFlag];
proc loadNifModule*(c: var DecodeContext; suffix: ModuleSuffix; interf, interfHidden: var TStrTable;
flags: set[LoadFlag] = {}): PrecompiledModule =
# Ensure module index is loaded - moduleId returns the FileIndex for this suffix
# Ensure module index is loaded - moduleId returns the FileIndex for this suffix
let module = moduleId(c, string(suffix), flags)
# Populate interface tables from the NIF index structure
# Symbols are created as stubs (Partial state) and will be loaded lazily via loadSym
populateInterfaceTablesFromIndex(c, module, interf, interfHidden, string(suffix))
# Load the module AST (or just replay actions if loadFullAst is false)
# processTopLevel also collects export instructions
let s = addr c.mods[module].stream
s.r.jumpTo 0 # Start from beginning
discard processDirectives(s.r)
var t = next(s[])
if t.kind == ParLe and pool.tags[t.tagId] == toNifTag(nkStmtList):
t = next(s[]) # skip (stmts
t = next(s[]) # skip flags
result = processTopLevel(c, s[], flags, interf, string(suffix), module.int)
result = processTopLevel(c, s[], flags, string(suffix), module.int)
else:
result = PrecompiledModule(topLevel: newNode(nkStmtList))
# Populate interface tables from the NIF index structure
# Symbols are created as stubs (Partial state) and will be loaded lazily via loadSym
# Use exports collected by processTopLevel
populateInterfaceTablesFromIndex(c, module, interf, interfHidden, string(suffix))
proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: var TStrTable;
flags: set[LoadFlag] = {}): PrecompiledModule =
let suffix = ModuleSuffix(moduleSuffix(c.infos.config, f))

View File

@@ -202,11 +202,7 @@ type
tySequence,
tyProc,
tyPointer, tyOpenArray,
tyString, tyCstring,
tyForward,
# a type not yet semchecked
# When semcheck a type section, all types defined in it are initialized to tyForward
tyString, tyCstring, tyForward,
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
@@ -701,7 +697,6 @@ type
PLib* = ref TLib
TSym* {.acyclic.} = object # Keep in sync with ast2nif.nim
# Check `transitionSymKindCommon` in ast.nim when add a new field.
itemId*: ItemId
# proc and type instantiations are cached in the generic symbol
state*: ItemState

View File

@@ -69,7 +69,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
result.links[j] = h.links[Mhalf + j]
else:
for j in 0..<Mhalf:
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
result.vals[j] = move h.vals[Mhalf + j]
else:
shallowCopy(result.vals[j], h.vals[Mhalf + j])
@@ -92,7 +92,7 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
if less(key, h.keys[j]): break
inc j
for i in countdown(h.entries, j+1):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
h.vals[i] = move h.vals[i-1]
else:
shallowCopy(h.vals[i], h.vals[i-1])

View File

@@ -230,29 +230,20 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
of tyString, tySequence:
let atyp = skipTypes(a.t, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and
optSeqDestructors in p.config.globalOptions and not p.config.usesSso():
optSeqDestructors in p.config.globalOptions:
let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
if p.config.usesSso() and
skipTypes(a.t, abstractVar + abstractInst).kind == tyString:
let strPtr = if atyp.kind in {tyVar} and not compileToCpp(p.module): ra
else: addrLoc(p.config, a)
result = (
cCast(ptrType(dest), cOp(Add, NimInt,
cCall(cgsymValue(p.module, "nimStrData"), strPtr), rb)),
lengthExpr)
var val: Snippet
if atyp.kind in {tyVar} and not compileToCpp(p.module):
val = cDeref(ra)
else:
var val: Snippet
if atyp.kind in {tyVar} and not compileToCpp(p.module):
val = cDeref(ra)
else:
val = ra
result = (
cIfExpr(dataFieldAccessor(p, val),
cCast(ptrType(dest), cOp(Add, NimInt, dataField(p, val), rb)),
NimNil),
lengthExpr)
val = ra
result = (
cIfExpr(dataFieldAccessor(p, val),
cCast(ptrType(dest), cOp(Add, NimInt, dataField(p, val), rb)),
NimNil),
lengthExpr)
else:
result = ("", "")
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -296,22 +287,11 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
of tyString, tySequence:
let ntyp = skipTypes(n.typ, abstractInst)
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
optSeqDestructors in p.config.globalOptions and not p.config.usesSso():
optSeqDestructors in p.config.globalOptions:
let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
if p.config.usesSso() and
skipTypes(n.typ, abstractVar + abstractInst).kind == tyString:
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
let ra = a.rdLoc
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
result.addArgumentSeparator()
result.add(cCall(cgsymValue(p.module, "nimStrLen"), cDeref(ra)))
else:
result.add(cCall(cgsymValue(p.module, "nimStrData"), addrLoc(p.config, a)))
result.addArgumentSeparator()
result.add(lenExpr(p, a))
elif ntyp.kind in {tyVar} and not compileToCpp(p.module):
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
let ra = a.rdLoc
var t = TLoc(snippet: cDeref(ra))
let lt = lenExpr(p, t)
@@ -335,14 +315,9 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Builder) =
let ra = a.rdLoc
var t = TLoc(snippet: cDeref(ra))
let lt = lenExpr(p, t)
if p.config.usesSso():
result.add(cCall(cgsymValue(p.module, "nimStrData"), ra))
result.addArgumentSeparator()
result.add(cCall(cgsymValue(p.module, "nimStrLen"), t.snippet))
else:
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
result.addArgumentSeparator()
result.add(lt)
result.add(cIfExpr(dataFieldAccessor(p, t.snippet), dataField(p, t.snippet), NimNil))
result.addArgumentSeparator()
result.add(lt)
of tyArray:
let ra = rdLoc(a)
result.add(ra)
@@ -356,7 +331,7 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
# Bug https://github.com/status-im/nimbus-eth2/issues/1549
# Aliasing is preferred over stack overflows.
# Also don't regress for non ARC-builds, too risky.
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc} and
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
getSize(p.config, a.lode.typ) < 1024:
result = getTemp(p, a.lode.typ, needsInit=false)
genAssignment(p, result, a, {})
@@ -369,8 +344,7 @@ proc expressionsNeedsTmp(p: BProc, a: TLoc): TLoc =
proc genArgStringToCString(p: BProc, n: PNode; result: var Builder; needsTmp: bool) {.inline.} =
var a = initLocExpr(p, n[0])
let tmp = withTmpIfNeeded(p, a, needsTmp)
let ra = if p.config.usesSso(): byRefLoc(p, tmp) else: tmp.rdLoc
let ra = withTmpIfNeeded(p, a, needsTmp).rdLoc
result.addCall(cgsymValue(p.module, "nimToCStringConv"), ra)
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Builder; needsTmp = false) =

View File

@@ -216,8 +216,6 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
flags
let t = skipTypes(dest.t, abstractInst).getUniqueType()
for i, t in t.ikids:
# Do not produce code for void types
if isEmptyType(t): continue
let field = "Field$1" % [i.rope]
genAssignment(p, optAsgnLoc(dest, t, field),
optAsgnLoc(src, t, field), newflags)
@@ -320,16 +318,12 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
p.s(cpsStmts).addCallStmt(
cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
let rd = d.rdLoc
let ra = a.rdLoc
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
let la = lenExpr(p, a)
if p.config.usesSso():
let bra = byRefLoc(p, a)
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
cCall(cgsymValue(p.module, "nimStrData"), bra))
else:
let ra = a.rdLoc
p.s(cpsStmts).addFieldAssignment(rd, "Field0",
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil))
p.s(cpsStmts).addFieldAssignment(rd, "Field1", la)
else:
internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind)
@@ -422,7 +416,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else:
simpleAsgn(p.s(cpsStmts), dest, src)
of tyArray:
if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcYrc, gcHooks}:
if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
genGenericAsgn(p, dest, src, flags)
else:
let rd = rdLoc(dest)
@@ -926,8 +920,8 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
else:
a = initLocExprSingleUse(p, e[0])
# bug #23453 #25265
if e.typ != nil and e.typ.skipTypes(abstractInst).kind == tyObject:
if e.typ != nil and e.typ.kind == tyObject:
# bug #23453 #25265
discard getTypeDesc(p.module, e.typ)
if d.k == locNone:
# dest = *a; <-- We do not know that 'dest' is on the heap!
@@ -962,8 +956,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
putIntoDest(p, d, e, cDeref(rdLoc(a)), a.storage)
proc cowBracket(p: BProc; n: PNode) =
if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions and
not p.config.usesSso():
if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions:
let strCandidate = n[0]
if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
var a: TLoc = initLocExpr(p, strCandidate)
@@ -974,7 +967,9 @@ proc cow(p: BProc; n: PNode) {.inline.} =
if n.kind == nkHiddenAddr: cowBracket(p, n[0])
template ignoreConv(e: PNode): bool =
sameBackendTypePickyAliases(e.typ, e[1].typ)
let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
let srcType = e[1].typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
sameBackendTypePickyAliases(destType, srcType)
proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# careful 'addr(myptrToArray)' needs to get the ampersand:
@@ -987,9 +982,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) =
# bug #19497
d.lode = e
else:
let ssoStrSub = p.config.usesSso() and e[0].kind == nkBracketExpr and
e[0][0].typ.skipTypes(abstractVar).kind == tyString
var a: TLoc = initLocExpr(p, e[0], if ssoStrSub: {lfEnforceDeref, lfPrepareForMutation} else: {})
var a: TLoc = initLocExpr(p, e[0])
if e[0].kind in {nkHiddenStdConv, nkHiddenSubConv, nkConv} and not ignoreConv(e[0]):
# addr (conv x) introduces a temp because `conv x` is not a rvalue
# transform addr ( conv ( x ) ) -> conv ( addr ( x ) )
@@ -1316,24 +1309,13 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
a.snippet = cDeref(a.snippet)
if p.config.usesSso() and ty.kind == tyString:
if lfPrepareForMutation in d.flags and ty.kind == tyString and
optSeqDestructors in p.config.globalOptions:
let bra = byRefLoc(p, a)
if lfPrepareForMutation in d.flags:
# Use nimStrAtMutV3 to get a mutable reference (char*) to the element.
# Only when mutation is requested: avoids calling nimPrepareStrMutationV2
# on const string literals (which would SIGSEGV on write to read-only memory).
putIntoDest(p, d, n,
cDeref(cCall(cgsymValue(p.module, "nimStrAtMutV3"), bra, rcb)), a.storage)
else:
putIntoDest(p, d, n,
cCall(cgsymValue(p.module, "nimStrAtV3"), bra, rcb), a.storage)
else:
if lfPrepareForMutation in d.flags and ty.kind == tyString and
optSeqDestructors in p.config.globalOptions:
let bra = byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"), bra)
let ra = rdLoc(a)
putIntoDest(p, d, n, subscript(dataField(p, ra), rcb), a.storage)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimPrepareStrMutationV2"),
bra)
let ra = rdLoc(a)
putIntoDest(p, d, n, subscript(dataField(p, ra), rcb), a.storage)
proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses)
@@ -1605,51 +1587,6 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
genAssignment(p, dest, b, {needToCopy})
gcUsage(p.config, e)
proc genSeqElemAppendV2(p: BProc, e: PNode, d: var TLoc) =
# s.add(x) with optSeqDestructors (arc/orc), inlined for direct slot construction:
# NI oldLen = s.len;
# if (s.p == NIM_NIL || (s.p->cap & ~NIM_STRLIT_FLAG) < oldLen + 1)
# s.p = (PayloadType*)prepareSeqAddUninit(oldLen, s.p, 1, sizeof(T), alignof(T));
# s.len = oldLen + 1;
# s.p->data[oldLen] = x; // direct assignment, no function call overhead
let seqtype = skipTypes(e[1].typ, abstractVarRange)
var a = initLocExpr(p, e[1])
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
# Capture a stable pointer to the seq BEFORE evaluating the element (e[2]).
# Evaluating e[2] may emit move semantics (eqwasMoved) that nil a variable
# through which e[1]'s snippet is accessed (e.g. a closure env pointer).
inc(p.labels)
let seqPtrName = "T" & rope(p.labels) & "_"
p.s(cpsLocals).addVar(kind = Local, name = seqPtrName,
typ = ptrType(getTypeDesc(p.module, seqtype)))
p.s(cpsStmts).addAssignment(seqPtrName, cAddr(rdLoc(a)))
var b = initLocExpr(p, e[2])
# All seq operations now go through the stable seqPtrName pointer.
let ra = wrapPar(cDeref(seqPtrName))
var tmpL = getIntTemp(p)
p.s(cpsStmts).addAssignment(tmpL.snippet, dotField(ra, "len"))
let pField = dotField(ra, "p")
p.s(cpsStmts).addSingleIfStmt(
cOp(Or,
cOp(Equal, pField, NimNil),
cOp(LessThan,
cOp(BitAnd, NimInt, derefField(pField, "cap"), cOp(BitNot, NimInt, NimStrlitFlag)),
cOp(Add, NimInt, tmpL.snippet, cIntValue(1))))):
p.s(cpsStmts).addFieldAssignmentWithValue(ra, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "prepareSeqAddUninit"),
tmpL.snippet,
pField,
cIntValue(1),
cSizeof(pe),
cAlignof(pe))
p.s(cpsStmts).addFieldAssignment(ra, "len",
cOp(Add, NimInt, tmpL.snippet, cIntValue(1)))
var dest = initLoc(locExpr, e[2], OnHeap)
dest.snippet = subscript(dataField(p, ra), tmpL.snippet)
genAssignment(p, dest, b, {})
proc genDefault(p: BProc; n: PNode; d: var TLoc) =
if d.k == locNone: d = getTemp(p, n.typ, needsInit=true)
else: resetLoc(p, d)
@@ -1785,15 +1722,15 @@ proc genNewSeq(p: BProc, e: PNode) =
let seqtype = skipTypes(e[1].typ, abstractVarRange)
let ra = a.rdLoc
let rb = b.rdLoc
let et = getTypeDesc(p.module, seqtype.elementType)
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
p.s(cpsStmts).addFieldAssignment(ra, "len", rb)
p.s(cpsStmts).addFieldAssignmentWithValue(ra, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "newSeqPayload"),
rb,
cSizeof(pe),
cAlignof(pe))
cSizeof(et),
cAlignof(et))
else:
let lenIsZero = e[2].kind == nkIntLit and e[2].intVal == 0
genNewSeqAux(p, a, b.rdLoc, lenIsZero)
@@ -1806,15 +1743,15 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
if d.k == locNone: d = getTemp(p, e.typ, needsInit=false)
let rd = d.rdLoc
let ra = a.rdLoc
let et = getTypeDesc(p.module, seqtype.elementType)
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
p.s(cpsStmts).addFieldAssignment(rd, "len", cIntValue(0))
p.s(cpsStmts).addFieldAssignmentWithValue(rd, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "newSeqPayloadUninit"),
ra,
cSizeof(pe),
cAlignof(pe))
cSizeof(et),
cAlignof(et))
else:
if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) # bug #22560
let ra = a.rdLoc
@@ -1887,22 +1824,15 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
var t = e.typ.skipTypes(abstractInstOwned)
let isRef = t.kind == tyRef
# check if we need to construct the object in a temporary.
# A temp is needed when:
# - the constructor produces a ref (isRef)
# - the destination is not a writable location (d.k == locNone)
# - the constructed type differs from the destination type (subtype
# assignments need the genAssignment path for ObjectAssignmentDefect)
# - the constructor's field values may alias the destination (isPartOf)
# check if we need to construct the object in a temporary
var useTemp =
isRef or
d.k == locNone or
(d.t != nil and not sameBackendType(t, d.t.skipTypes(abstractInstOwned))) or
(d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
(isPartOf(d.lode, e) != arNo)
var tmp: TLoc = default(TLoc)
var r: Rope
let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcYrc} or nfAllFieldsSet notin e.flags
let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc} or nfAllFieldsSet notin e.flags
if useTemp:
tmp = getTemp(p, t)
r = rdLoc(tmp)
@@ -1957,15 +1887,15 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
if optSeqDestructors in p.config.globalOptions:
let seqtype = n.typ
let rd = rdLoc dest[]
let et = getTypeDesc(p.module, seqtype.elementType)
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
p.s(cpsStmts).addFieldAssignment(rd, "len", lit)
p.s(cpsStmts).addFieldAssignmentWithValue(rd, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "newSeqPayload"),
lit,
cSizeof(pe),
cAlignof(pe))
cSizeof(et),
cAlignof(et))
else:
# generate call to newSeq before adding the elements per hand:
genNewSeqAux(p, dest[], lit, n.len == 0)
@@ -1998,15 +1928,15 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
let seqtype = n.typ
let rd = rdLoc d
let valL = cIntValue(L)
let et = getTypeDesc(p.module, seqtype.elementType)
let pt = getSeqPayloadType(p.module, seqtype)
let pe = seqPayloadElem(p.module, seqtype)
p.s(cpsStmts).addFieldAssignment(rd, "len", valL)
p.s(cpsStmts).addFieldAssignmentWithValue(rd, "p"):
p.s(cpsStmts).addCast(ptrType(pt)):
p.s(cpsStmts).addCall(cgsymValue(p.module, "newSeqPayload"),
valL,
cSizeof(pe),
cAlignof(pe))
cSizeof(et),
cAlignof(et))
else:
let lit = cIntLiteral(L)
genNewSeqAux(p, d, lit, L == 0)
@@ -2147,20 +2077,12 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
let ra = rdLoc(a)
putIntoDest(p, b, e, ra & cArgumentSeparator & ra & "Len_0", a.storage)
of tyString, tySequence:
let ra = rdLoc(a)
let la = lenExpr(p, a)
if p.config.usesSso() and
skipTypes(a.t, abstractVarRange).kind == tyString:
let bra = byRefLoc(p, a)
putIntoDest(p, b, e,
cCall(cgsymValue(p.module, "nimStrData"), bra) &
cArgumentSeparator & la,
a.storage)
else:
let ra = rdLoc(a)
putIntoDest(p, b, e,
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil) &
cArgumentSeparator & la,
a.storage)
putIntoDest(p, b, e,
cIfExpr(dataFieldAccessor(p, ra), dataField(p, ra), NimNil) &
cArgumentSeparator & la,
a.storage)
of tyArray:
let ra = rdLoc(a)
let la = cIntValue(lengthOrd(p.config, a.t))
@@ -2741,9 +2663,9 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) =
proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc = initLocExpr(p, n[0])
let arg = if p.config.usesSso(): byRefLoc(p, a) else: rdLoc(a)
putIntoDest(p, d, n,
cgCall(p, "nimToCStringConv", arg),
cgCall(p, "nimToCStringConv", rdLoc(a)),
# "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc],
a.storage)
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
@@ -2814,38 +2736,45 @@ proc genWasMoved(p: BProc; n: PNode) =
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
proc genMove(p: BProc; n: PNode; d: var TLoc) =
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref})
if n.len == 4:
# generated by liftdestructors:
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation})
var src: TLoc = initLocExpr(p, n[2])
let destVal = rdLoc(a)
let srcVal = rdLoc(src)
if p.config.usesSso() and
n[1].typ.skipTypes(abstractVar).kind == tyString:
# SmallString: destroy dst then struct-copy src; no .p field aliasing needed
p.s(cpsStmts).addSingleIfStmt(
cOp(NotEqual,
dotField(destVal, "p"),
dotField(srcVal, "p"))):
genStmts(p, n[3])
genAssignment(p, a, src, {})
else:
p.s(cpsStmts).addSingleIfStmt(
cOp(NotEqual,
dotField(destVal, "p"),
dotField(srcVal, "p"))):
genStmts(p, n[3])
p.s(cpsStmts).addFieldAssignment(destVal, "len", dotField(srcVal, "len"))
p.s(cpsStmts).addFieldAssignment(destVal, "p", dotField(srcVal, "p"))
p.s(cpsStmts).addFieldAssignment(destVal, "len", dotField(srcVal, "len"))
p.s(cpsStmts).addFieldAssignment(destVal, "p", dotField(srcVal, "p"))
else:
if d.k == locNone: d = getTemp(p, n.typ)
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}:
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
genAssignment(p, d, a, {})
var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved)
if op == nil or sfOverridden notin op.flags:
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation})
genAssignment(p, d, a, {})
if op == nil:
resetLoc(p, a)
else:
n[1] = makeAddr(n[1], p.module.idgen)
genCall(p, n, d)
var b = initLocExpr(p, newSymNode(op))
case skipTypes(a.t, abstractVar+{tyStatic}).kind
of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for
# openarrays, but it probably shouldn't?
let ra = rdLoc(a)
var s: string
if reifiedOpenArray(a.lode):
if a.t.kind in {tyVar, tyLent}:
s = derefField(ra, "Field0") & cArgumentSeparator & derefField(ra, "Field1")
else:
s = dotField(ra, "Field0") & cArgumentSeparator & dotField(ra, "Field1")
else:
s = ra & cArgumentSeparator & ra & "Len_0"
p.s(cpsStmts).addCallStmt(rdLoc(b), s)
else:
let val = if p.module.compileToCpp: rdLoc(a) else: byRefLoc(p, a)
p.s(cpsStmts).addCallStmt(rdLoc(b), val)
else:
var a: TLoc = initLocExpr(p, n[1].skipAddr, {lfEnforceDeref, lfPrepareForMutation})
genAssignment(p, d, a, {})
resetLoc(p, a)
@@ -2856,19 +2785,15 @@ proc genDestroy(p: BProc; n: PNode) =
case t.kind
of tyString:
var a: TLoc = initLocExpr(p, arg)
if p.config.usesSso():
# SmallString: delegate to nimDestroyStrV1 (rc-based, handles static strings)
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimDestroyStrV1"), rdLoc(a))
else:
let ra = rdLoc(a)
let rp = dotField(ra, "p")
p.s(cpsStmts).addSingleIfStmt(
cOp(And, rp,
cOp(Not, cOp(BitAnd, NimInt,
derefField(rp, "cap"),
NimStrlitFlag)))):
let fn = if optThreads in p.config.globalOptions: "deallocShared" else: "dealloc"
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, fn), rp)
let ra = rdLoc(a)
let rp = dotField(ra, "p")
p.s(cpsStmts).addSingleIfStmt(
cOp(And, rp,
cOp(Not, cOp(BitAnd, NimInt,
derefField(rp, "cap"),
NimStrlitFlag)))):
let fn = if optThreads in p.config.globalOptions: "deallocShared" else: "dealloc"
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, fn), rp)
of tySequence:
var a: TLoc = initLocExpr(p, arg)
let ra = rdLoc(a)
@@ -2910,7 +2835,7 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) =
let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.elementType,
prepareForMutation = e[1].kind == nkHiddenDeref and
e[1].typ.skipTypes(abstractInst).kind == tyString and
p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc})
p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc})
if d.k == locNone: d = getTemp(p, e.typ)
let dest = rdLoc(d)
p.s(cpsStmts).addFieldAssignment(dest, "Field0", x)
@@ -2973,15 +2898,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mAppendStrStr: genStrAppend(p, e, d)
of mAppendSeqElem:
if optSeqDestructors in p.config.globalOptions:
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
# Inline growth + direct slot assignment: avoids the add() call overhead
# and lets the C compiler see the construction expression at its final
# destination, enabling in-place construction for nkObjConstr etc.
# gcYrc is excluded because its add() acquires a striped reader lock.
genSeqElemAppendV2(p, e, d)
else:
e[1] = makeAddr(e[1], p.module.idgen)
genCall(p, e, d)
e[1] = makeAddr(e[1], p.module.idgen)
genCall(p, e, d)
else:
genSeqElemAppend(p, e, d)
of mEqStr: genStrEquals(p, e, d)
@@ -3121,7 +3039,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e)
expr(p, n, d)
of mDeepCopy:
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc} and optEnableDeepCopy notin p.config.globalOptions:
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
localError(p.config, e.info,
"for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
@@ -3237,8 +3155,6 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
for i in 0..<n.len:
var it = n[i]
if it.kind == nkExprColonExpr: it = it[1]
# Do not produce code for void types
if it.typ != nil and isEmptyType(it.typ): continue
rec = initLoc(locExpr, it, dest[].storage)
rec.snippet = dotField(rdLoc(dest[]), "Field" & rope(i))
rec.flags.incl(lfEnforceDeref)
@@ -3355,11 +3271,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) =
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "raiseObjectConversionError"))
raiseInstr(p, p.s(cpsStmts))
# skip cast when types map to the same C type
# this avoids invalid C code like `*(T*)&x` for types that can't have their address taken (e.g., WASM __externref_t)
if getTypeDesc(p.module, n.typ) == getTypeDesc(p.module, n[0].typ):
expr(p, n[0], d)
elif n[0].typ.kind != tyObject:
if n[0].typ.kind != tyObject:
let destTyp = getTypeDesc(p.module, n.typ)
let val = rdLoc(a)
if n.isLValue:
@@ -3405,7 +3317,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
cCast(ptrType(destType),
wrapPar(cAddr(wrapPar(val))))),
a.storage)
elif p.module.compileToCpp or isImportedType(src):
elif p.module.compileToCpp:
# C++ implicitly downcasts for us
expr(p, arg, d)
else:
@@ -3850,7 +3762,6 @@ proc containsOpaqueImportcField(typ: PType): bool =
return true
of tyTuple:
for i, a in t.ikids:
if isEmptyType(a): continue
if containsOpaqueImportcField(a):
return true
of tyArray:
@@ -3900,12 +3811,10 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder)
var tupleInit: StructInitializer
let initKind = if containsOpaqueImportcField(t): siNamedStruct else: siOrderedStruct
result.addStructInitializer(tupleInit, kind = initKind):
if p.vccAndC and validTupleTypeFields(t) == 0:
if p.vccAndC and t.isEmptyTupleType:
result.addField(tupleInit, name = "dummy"):
result.addIntValue(0)
for i, a in t.ikids:
# Do not produce code for void types
if isEmptyType(a): continue
let elemTyp = skipTypes(a, abstractRange+{tyOwned}-{tyTypeDesc})
if not isOpaqueImportcType(elemTyp):
result.addField(tupleInit, name = "Field" & $i):
@@ -4080,8 +3989,6 @@ proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Bu
var it = n[i]
if it.kind == nkExprColonExpr:
it = it[1]
# Do not produce code for void types
if isEmptyType(tup[i]): continue
result.addField(tupleInit, name = "Field" & $i):
genBracedInit(p, it, isConst, tup[i], result)
@@ -4228,10 +4135,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
genConstObjConstr(p, n, isConst, result)
of tyString, tyCstring:
if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
if p.config.usesSso():
genStringLiteralV3Const(p.module, n, isConst, result)
else:
genStringLiteralV2Const(p.module, n, isConst, result)
genStringLiteralV2Const(p.module, n, isConst, result)
else:
var d: TLoc = initLocExpr(p, n)
result.add rdLoc(d)

View File

@@ -16,17 +16,13 @@
## implementation.
template detectVersion(field, corename) =
if m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
if m.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcHooks}:
result = 2
else:
result = 1
proc detectStrVersion(m: BModule): int =
if m.g.config.usesSso() and
m.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc, gcHooks}:
result = 3
else:
detectVersion(strVersion, "nimStrVersion")
detectVersion(strVersion, "nimStrVersion")
proc detectSeqVersion(m: BModule): int =
detectVersion(seqVersion, "nimSeqVersion")
@@ -132,192 +128,6 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Bu
result.addField(strInit, name = "p"):
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
proc ssoCharLit(ch: char): string =
## Return a C char literal for ch, with proper escaping.
const hexDigits = "0123456789abcdef"
result = "'"
case ch
of '\'': result.add("\\'")
of '\\': result.add("\\\\")
of '\0': result.add("\\0")
of '\n': result.add("\\n")
of '\r': result.add("\\r")
of '\t': result.add("\\t")
elif ch.ord < 32 or ch.ord == 127:
result.add("\\x")
result.add(hexDigits[ch.ord shr 4])
result.add(hexDigits[ch.ord and 0xf])
else:
result.add(ch)
result.add('\'')
proc ssoBytesLit(m: BModule; s: string; slen: int): string =
## Compute the `bytes` field value for the new SmallString layout.
## byte 0 = slen, bytes 1-7 = inline chars 0-6 (zero-padded).
## On LE: slen in bits 0-7, char[i] in bits (i+1)*8..(i+1)*8+7.
## On BE: slen in bits 56-63, char[i] in bits (6-i)*8..(6-i)*8+7.
const AlwaysAvail = 7
var val: uint64
if CPU[m.g.config.target.targetCPU].endian == littleEndian:
val = uint64(slen)
for i in 0..<min(s.len, AlwaysAvail):
val = val or (uint64(s[i]) shl (uint(i + 1) * 8))
else:
val = uint64(slen) shl 56
for i in 0..<min(s.len, AlwaysAvail):
val = val or (uint64(s[i]) shl (uint(AlwaysAvail - 1 - i) * 8))
# Cast to NU (C name for Nim's uint, = NU64 on 64-bit). NU64 = uint64_t.
result = cCast("NU", $val & "ULL")
proc ssoMoreLit(m: BModule; s: string): string =
## For medium string literals (AlwaysAvail < len <= PayloadSize), encode
## chars[AlwaysAvail..ptrSize-1] in the 'more' pointer field bit-pattern.
## The last pointer byte is always '\0' (null terminator), guaranteed by
## PayloadSize = AlwaysAvail + ptrSize - 1. slen <= PayloadSize guards
## prevent any code from dereferencing this as an actual pointer.
const AlwaysAvail = 7
let ptrSize = m.g.config.target.ptrSize
var val: uint64 = 0
for i in 0..<ptrSize:
let ch: uint64 = if AlwaysAvail + i < s.len: uint64(s[AlwaysAvail + i]) else: 0
if CPU[m.g.config.target.targetCPU].endian == littleEndian:
val = val or (ch shl (uint(i) * 8))
else:
val = val or (ch shl (uint(ptrSize - 1 - i) * 8))
result = cCast(ptrType("LongString"), "(uintptr_t)" & $val)
proc genStringLiteralV3Const(m: BModule; n: PNode; isConst: bool; result: var Builder) =
# Inline SmallString struct initializer for use inside const aggregate types.
# Layout: {bytes: NimUint, more: ptr LongString}
# bytes = slen (low byte) | char[0]<<8 | char[1]<<16 | ... | char[6]<<56
const AlwaysAvail = 7
let s = n.strVal
cgsym(m, "SmallString")
cgsym(m, "LongString")
let payloadSize = AlwaysAvail + m.g.config.target.ptrSize - 1
var si: StructInitializer
result.addStructInitializer(si, kind = siOrderedStruct):
if s.len <= AlwaysAvail:
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, s.len))
result.addField(si, name = "more"):
result.add(NimNil)
elif s.len <= payloadSize:
# Medium string: bytes holds slen + chars 0-6; more holds chars 7..PayloadSize-1.
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, s.len))
result.addField(si, name = "more"):
result.add(ssoMoreLit(m, s))
else:
# Emit the LongString block into cfsStrData and reference it inline.
let dataName = getTempName(m)
var res = newBuilder("")
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = dataName):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "rc", typ = NimInt)
res.addField(name = "fullLen", typ = NimInt)
res.addField(name = "capImpl", typ = NimInt)
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
do:
var di: StructInitializer
res.addStructInitializer(di, kind = siOrderedStruct):
res.addField(di, name = "fullLen"):
res.addIntValue(s.len)
res.addField(di, name = "rc"):
res.addIntValue(1)
res.addField(di, name = "capImpl"):
res.addIntValue(0) # static, never freed
res.addField(di, name = "data"):
res.add(makeCString(s))
m.s[cfsStrData].add(extract(res))
# slen = StaticSlen (254): marks this as a static (never-freed) long string.
result.addField(si, name = "bytes"):
result.add(ssoBytesLit(m, s, 254))
result.addField(si, name = "more"):
result.add(cCast(ptrType("LongString"), cAddr(dataName)))
# ------ Version 3: SmallString (SSO) strings --------------------------------
proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Builder) =
# SmallString literal. Always generate a fresh SmallString variable (like v2
# always generates a fresh outer NimStringV2). For long strings, cache the
# LongString payload to avoid duplicates within a module.
const AlwaysAvail = 7 # must match strs_v3.nim
let s = n.strVal
let tmp = getTempName(m)
result.add tmp
cgsym(m, "SmallString")
cgsym(m, "LongString")
let payloadSize = AlwaysAvail + m.g.config.target.ptrSize - 1
var res = newBuilder("")
if s.len <= AlwaysAvail:
# Short: bytes holds slen + all chars (zero-padded), more = NULL.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, s.len))
res.addField(si, name = "more"):
res.add(NimNil)
elif s.len <= payloadSize:
# Medium: bytes holds slen + chars 0-6; more holds chars 7..PayloadSize-1 as raw bits.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, s.len))
res.addField(si, name = "more"):
res.add(ssoMoreLit(m, s))
else:
# Long: cache the LongString block to emit it only once per module per string.
# Always generate a fresh SmallString pointing at the (possibly cached) block.
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
var dataName: string
if id == m.labels:
dataName = getTempName(m)
res.addVarWithTypeAndInitializer(
if isConst: AlwaysConst else: Global,
name = dataName):
res.addSimpleStruct(m, name = "", baseType = ""):
res.addField(name = "rc", typ = NimInt)
res.addField(name = "fullLen", typ = NimInt)
res.addField(name = "capImpl", typ = NimInt)
res.addArrayField(name = "data", elementType = NimChar, len = s.len + 1)
do:
var di: StructInitializer
res.addStructInitializer(di, kind = siOrderedStruct):
res.addField(di, name = "fullLen"):
res.addIntValue(s.len)
res.addField(di, name = "rc"):
res.addIntValue(1)
res.addField(di, name = "capImpl"):
res.addIntValue(0) # bit 0 = 0: static, never freed
res.addField(di, name = "data"):
res.add(makeCString(s))
else:
dataName = m.tmpBase & $id
# slen = StaticSlen (254): marks this as a static (never-freed) long string.
res.addVarWithInitializer(
if isConst: AlwaysConst else: Global,
name = tmp, typ = "SmallString"):
var si: StructInitializer
res.addStructInitializer(si, kind = siOrderedStruct):
res.addField(si, name = "bytes"):
res.add(ssoBytesLit(m, s, 254))
res.addField(si, name = "more"):
res.add(cCast(ptrType("LongString"), cAddr(dataName)))
m.s[cfsStrData].add(extract(res))
# ------ Version selector ---------------------------------------------------
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
@@ -328,8 +138,6 @@ proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
let tmp = getTempName(m)
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
result.add tmp
of 3:
localError(m.config, info, "genStringLiteralDataOnly not supported for SmallString (nimsso)")
else:
localError(m.config, info, "cannot determine how to produce code for string literal")
@@ -340,6 +148,5 @@ proc genStringLiteral(m: BModule; n: PNode; result: var Builder) =
case detectStrVersion(m)
of 0, 1: genStringLiteralV1(m, n, result)
of 2: genStringLiteralV2(m, n, isConst = true, result)
of 3: genStringLiteralV3(m, n, isConst = true, result)
else:
localError(m.config, n.info, "cannot determine how to produce code for string literal")

View File

@@ -230,7 +230,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int, isReturnStmt
# Called by return and break stmts.
# Deals with issues faced when jumping out of try/except/finally stmts.
var stack = newSeq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]](0)
var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
inc p.withinBlockLeaveActions
for i in 1..howManyTrys:
@@ -341,9 +341,9 @@ proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): Snippet =
call[i][0]
else:
call[i]
if not param.typ.isCompileTimeOnly and (param.kind != nkBracketExpr or param.typ.kind in
if param.kind != nkBracketExpr or param.typ.kind in
{tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
tyVarargs, tySequence, tyString, tyCstring, tyTuple}):
tyVarargs, tySequence, tyString, tyCstring, tyTuple}:
let tempLoc = initLocExprSingleUse(p, param)
didGenTemp = didGenTemp or tempLoc.k == locTemp
genOtherArg(p, call, i, typ, res, argBuilder)
@@ -836,26 +836,12 @@ proc raiseExitCleanup(p: BProc, destroy: string) =
p.s(cpsStmts).addGoto("LA" & $p.nestedTryStmts[^1].label & "_")
proc finallyActions(p: BProc) =
if p.config.exc != excGoto:
# Walk past compiler-injected `nkHiddenTryStmt` wrappers (e.g. ARC's
# destructor try/finally that wraps `except T as e:` bodies) to reach
# the user's actual try. We must NOT walk past a real user try whose
# body we are currently in, because a raise from there will be caught
# by that try's own except branches rather than escaping outward.
#
# If after skipping wrappers the next entry is a user try in its
# except branch (inExcept=true), inline its finally body before the
# raise propagates — without this, the C++ sibling-catch rule would
# cause the user's catch(...)/finally pair to be bypassed and the
# finally would be silently dropped.
for i in countdown(p.nestedTryStmts.high, 0):
if p.nestedTryStmts[i].isHidden:
continue
if p.nestedTryStmts[i].inExcept:
let finallyBlock = p.nestedTryStmts[i].fin
if finallyBlock != nil:
genSimpleBlock(p, finallyBlock[0])
return
if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
# if the current try stmt have a finally block,
# we must execute it before reraising
let finallyBlock = p.nestedTryStmts[^1].fin
if finallyBlock != nil:
genSimpleBlock(p, finallyBlock[0])
proc raiseInstr(p: BProc; result: var Builder) =
if p.config.exc == excGoto:
@@ -1179,7 +1165,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
throw;
}
} catch(...) {
// C++ exception occurred, not under Nim's control.
// C++ exception occured, not under Nim's control.
}
{
/* finally: */
@@ -1199,7 +1185,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp])
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
p.nestedTryStmts.add((fin, false, 0.Natural))
if t.kind == nkHiddenTryStmt:
lineCg(p, cpsStmts, "try {$n", [])
@@ -1385,7 +1371,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
genLineDir(p, t)
cgsym(p.module, "popCurrentExceptionEx")
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
p.nestedTryStmts.add((fin, false, 0.Natural))
startBlockWith(p):
p.s(cpsStmts).add("try {\n")
expr(p, t[0], d)
@@ -1464,7 +1450,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
let lab = p.labels
let hasExcept = t[1].kind == nkExceptBranch
if hasExcept: inc p.withinTryWithExcept
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, Natural lab))
p.nestedTryStmts.add((fin, false, Natural lab))
p.flags.incl nimErrorFlagAccessed
@@ -1670,7 +1656,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
initElifBranch(p.s(cpsStmts), nonQuirkyIf, removeSinglePar(
cOp(Equal, dotField(safePoint, "status"), cIntValue(0))))
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
p.nestedTryStmts.add((fin, quirkyExceptions, t.kind == nkHiddenTryStmt, 0.Natural))
p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
expr(p, t[0], d)
var quirkyIf = default(IfBuilder)
var quirkyScope = default(ScopeBuilder)
@@ -1954,15 +1940,6 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
elif optFieldCheck in p.options and isDiscriminantField(e[0]):
genLineDir(p, e)
asgnFieldDiscriminant(p, e)
elif p.config.usesSso() and e[0].kind == nkBracketExpr and
e[0][0].typ.skipTypes(abstractVar).kind == tyString:
# nimsso: s[i] = c → nimStrPutV3(&s, i, c) (handles COW internally)
genLineDir(p, e)
var base = initLocExpr(p, e[0][0])
var idx = initLocExpr(p, e[0][1])
var rhs = initLocExpr(p, e[1])
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimStrPutV3"),
byRefLoc(p, base), rdLoc(idx), rdCharLoc(rhs))
else:
let le = e[0]
let ri = e[1]

View File

@@ -277,7 +277,7 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
of ctStruct:
let t = skipTypes(rettype, typedescInst)
if rettype.isImportedCppType or t.isImportedCppType or
(typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}):
(typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}):
# prevents nrvo for cdecl procs; # bug #23401
result = false
else:
@@ -294,12 +294,7 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
result = tab.getOrDefault(sig)
proc addAbiCheck(m: BModule; t: PType, name: Rope) =
if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize) and
not (t.kind == tyObject and searchTypeFor(t, proc (t: PType): bool {.nimcall.} = t.kind == tyUncheckedArray)):
# `UncheckedArray`, not `ptr UncheckedArray` type field in object types is a flexible array.
# `sizeof` in C and Nim doesn't always return the same value for object types containing it.
# making `getSize` in Nim always returns the same value as `sizeof` in C from flexible arrays seems hard.
# See `SEQ_DECL_SIZE` in lib/nimbase.h
if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize):
var msg = "backend & Nim disagree on size for: "
msg.addTypeHeader(m.config, t)
var msg2 = ""
@@ -309,7 +304,7 @@ proc addAbiCheck(m: BModule; t: PType, name: Rope) =
proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
backendEnsureMutable param.sym
ensureMutable param.sym
fillLoc(param.sym.locImpl, locParam, param, "Result",
OnStack)
let t = param.sym.typ
@@ -339,10 +334,6 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
cgsym(m, "NimStrPayload")
cgsym(m, "NimStringV2")
result = typeNameOrLiteral(m, typ, "NimStringV2")
of 3:
cgsym(m, "LongString")
cgsym(m, "SmallString")
result = typeNameOrLiteral(m, typ, "SmallString")
else:
cgsym(m, "NimStringDesc")
result = typeNameOrLiteral(m, typ, "NimStringDesc*")
@@ -457,14 +448,6 @@ proc getSeqPayloadType(m: BModule; t: PType): Rope =
result = getTypeDescWeak(m, t, check, dkParam) & "_Content"
#result = getTypeForward(m, t, hashType(t)) & "_Content"
proc seqPayloadElem(m: BModule; t: PType): Snippet =
## Returns the C type name for a seq's element as stored in the payload,
## suitable for sizeof()/alignof(). Must use dkVar, not the dkParam default,
## because reified openArrays (experimental views) differ: dkParam gives a
## bare pointer (T*) while dkVar gives the two-word struct actually stored.
var check = initIntSet()
result = getTypeDescAux(m, t.elementType, check, dkVar)
proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
let sig = hashType(t, m.config)
let result = cacheGetType(m.typeCache, sig)
@@ -546,7 +529,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
var types, names, args: seq[string] = @[]
if not isCtor:
var this = t.n[1].sym
backendEnsureMutable this
ensureMutable this
fillParamName(m, this)
fillLoc(this.locImpl, locParam, t.n[1],
this.paramStorageLoc)
@@ -568,7 +551,7 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params
else:
descKind = dkRefParam
var typ, name: string
backendEnsureMutable param
ensureMutable param
fillParamName(m, param)
fillLoc(param.locImpl, locParam, t.n[i],
param.paramStorageLoc)
@@ -812,8 +795,6 @@ proc getTupleDesc(m: BModule; typ: PType, name: Rope,
var res = newBuilder("")
res.addStruct(m, typ, name, ""):
for i, a in typ.ikids:
# Do not produce code for void types
if isEmptyType(a): continue
res.addField(
name = "Field" & $i,
typ = getTypeDescAux(m, a, check, dkField))
@@ -1086,7 +1067,6 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
else: getTupleDesc(m, t, result, check)
if not isImportedType(t):
m.s[cfsTypes].add(recdesc)
addAbiCheck(m, t, result)
elif tfIncompleteStruct notin t.flags:
discard # addAbiCheck(m, t, result) # already handled elsewhere
of tySet:
@@ -1187,7 +1167,7 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Builder; asPtr: bool
let isCtor = sfConstructor in prc.flags
var check = initIntSet()
fillBackendName(m, prc)
backendEnsureMutable prc
ensureMutable prc
fillLoc(prc.locImpl, locProc, prc.ast[namePos], OnUnknown)
var memberOp = "#." #only virtual
var typ: PType
@@ -1486,38 +1466,27 @@ proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo
t.incl tfObjHasKids
t = t.baseClass
proc validTupleTypeFields(t: PType): int =
# we want to treat tuples with only void fields as empty, so we need to exclude void types here:
result = 0
for a in t.kids:
if not isEmptyType(a): inc result
proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
genTypeInfoAuxBase(m, typ, typ, name, cIntValue(0), info)
var expr = getNimNode(m)
let nonVoidKids = validTupleTypeFields(typ)
if nonVoidKids > 0:
var tmp = getTempName(m) & "_" & $nonVoidKids
genTNimNodeArray(m, tmp, nonVoidKids)
var j = 0
if not typ.isEmptyTupleType:
var tmp = getTempName(m) & "_" & $typ.kidsLen
genTNimNodeArray(m, tmp, typ.kidsLen)
for i, a in typ.ikids:
# Do not produce code for void types
if isEmptyType(a): continue
var tmp2 = getNimNode(m)
let fieldTypInfo = genTypeInfoV1(m, a, info)
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(j), cAddr(tmp2))
m.s[cfsTypeInit3].addSubscriptAssignment(tmp, cIntValue(i), cAddr(tmp2))
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "kind", 1)
m.s[cfsTypeInit3].addFieldAssignmentWithValue(tmp2, "offset"):
m.s[cfsTypeInit3].addOffsetof(getTypeDesc(m, origType, dkVar), "Field" & $i)
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "typ", fieldTypInfo)
m.s[cfsTypeInit3].addFieldAssignment(tmp2, "name", "\"Field" & $i & "\"")
inc j
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", nonVoidKids)
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", typ.kidsLen)
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 2)
m.s[cfsTypeInit3].addFieldAssignment(expr, "sons",
cAddr(subscript(tmp, cIntValue(0))))
else:
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", cIntValue(0))
m.s[cfsTypeInit3].addFieldAssignment(expr, "len", typ.kidsLen)
m.s[cfsTypeInit3].addFieldAssignment(expr, "kind", 2)
m.s[cfsTypeInit3].addFieldAssignment(tiNameForHcr(m, name), "node", cAddr(expr))
@@ -1717,7 +1686,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
echo "ayclic but has this =trace ", t, " ", theProc.ast
else:
when false:
if op == attachedTrace and m.config.selectedGC in {gcOrc, gcYrc} and
if op == attachedTrace and m.config.selectedGC == gcOrc and
containsGarbageCollectedRef(t):
# unfortunately this check is wrong for an object type that only contains
# .cursor fields like 'Node' inside 'cycleleak'.
@@ -2027,7 +1996,7 @@ proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope =
owner = m.module.position.int32
m.g.typeInfoMarker[sig] = (str: result, owner: owner)
#rememberEmittedTypeInfo(m.g.graph, FileIndex(owner), $result)
rememberEmittedTypeInfo(m.g.graph, FileIndex(owner), $result)
case t.kind
of tyEmpty, tyVoid: result = cIntValue(0)

View File

@@ -30,6 +30,7 @@ when not defined(leanCompiler):
import std/strutils except `%`, addf # collides with ropes.`%`
from ic / ic import ModuleBackendFlag
import std/[dynlib, math, tables, sets, os, intsets, hashes]
const
@@ -389,11 +390,7 @@ proc lenField(p: BProc, val: Rope): Rope {.inline.} =
proc lenExpr(p: BProc; a: TLoc): Rope =
if optSeqDestructors in p.config.globalOptions:
if p.config.usesSso() and a.lode != nil and a.t != nil and
a.t.skipTypes(abstractInst).kind == tyString:
result = cCall(cgsymValue(p.module, "nimStrLen"), rdLoc(a))
else:
result = dotField(rdLoc(a), "len")
result = dotField(rdLoc(a), "len")
else:
let ra = rdLoc(a)
result = cIfExpr(ra, lenField(p, ra), cIntValue(0))
@@ -534,15 +531,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
let atyp = skipTypes(loc.t, abstractInst)
let rl = rdLoc(loc)
if typ.kind == tyString and p.config.usesSso():
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
if atyp.kind in {tyVar, tyLent}:
p.s(cpsStmts).addAssignment(derefField(rl, "bytes"), cIntValue(0))
p.s(cpsStmts).addAssignment(derefField(rl, "more"), NimNil)
else:
p.s(cpsStmts).addAssignment(dotField(rl, "bytes"), cIntValue(0))
p.s(cpsStmts).addAssignment(dotField(rl, "more"), NimNil)
elif atyp.kind in {tyVar, tyLent}:
if atyp.kind in {tyVar, tyLent}:
p.s(cpsStmts).addAssignment(derefField(rl, "len"), cIntValue(0))
p.s(cpsStmts).addAssignment(derefField(rl, "p"), NimNil)
else:
@@ -592,13 +581,8 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
let typ = loc.t
if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
let rl = rdLoc(loc)
if skipTypes(typ, abstractInst + {tyStatic}).kind == tyString and p.config.usesSso():
# SmallString zero state: bytes=0 (slen=0 in low byte, all inline chars zeroed)
p.s(cpsStmts).addFieldAssignment(rl, "bytes", cIntValue(0))
p.s(cpsStmts).addFieldAssignment(rl, "more", NimNil)
else:
p.s(cpsStmts).addFieldAssignment(rl, "len", cIntValue(0))
p.s(cpsStmts).addFieldAssignment(rl, "p", NimNil)
p.s(cpsStmts).addFieldAssignment(rl, "len", cIntValue(0))
p.s(cpsStmts).addFieldAssignment(rl, "p", NimNil)
elif not isComplexValueType(typ):
if containsGarbageCollectedRef(loc.t):
var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
@@ -1349,7 +1333,7 @@ proc genProcLvl3*(m: BModule, prc: PSym) =
# declare the result symbol:
assignLocalVar(p, resNode)
assert(res.loc.snippet != "")
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc} and
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
allPathsAsgnResult(p, procBody) == InitSkippable:
# In an ideal world the codegen could rely on injectdestructors doing its job properly
# and then the analysis step would not be required.
@@ -1704,7 +1688,7 @@ proc hcrGetProcLoadCode(builder: var Builder, m: BModule, sym, prefix, handle, g
# prevents inlining of the NimMainInner function and dependent
# functions, which might otherwise merge their stack frames.
proc isInnerMainVolatile(m: BModule): bool =
m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc, gcYrc}
m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}
proc genPreMain(m: BModule) =
m.s[cfsProcs].addDeclWithVisibility(Private):
@@ -1716,6 +1700,8 @@ proc genPreMain(m: BModule) =
m.s[cfsProcs].addVar(name = "cmdCount", typ = CInt)
m.s[cfsProcs].addDeclWithVisibility(Private):
m.s[cfsProcs].addVar(name = "cmdLine", typ = ptrType(ptrType(CChar)))
m.s[cfsProcs].addDeclWithVisibility(Private):
m.s[cfsProcs].addVar(name = "gEnv", typ = ptrType(ptrType(CChar)))
m.s[cfsProcs].addDeclWithVisibility(Private):
m.s[cfsProcs].addProcHeader(m.config.nimMainPrefix & "PreMain", CVoid, cProcParams())
m.s[cfsProcs].finishProcHeaderWithBody():
@@ -1749,7 +1735,7 @@ proc genNimMainInner(m: BModule) =
m.s[cfsProcs].addNewline()
proc initStackBottom(m: BModule): bool =
not (m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc, gcYrc})
not (m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc})
proc genNimMainProc(m: BModule, preMainCode: Snippet) =
m.s[cfsProcs].addProcHeader(ccCDecl, m.config.nimMainPrefix & "NimMain", CVoid, cProcParams())
@@ -1776,10 +1762,12 @@ proc genNimMainBody(m: BModule, preMainCode: Snippet) =
proc genPosixCMain(m: BModule) =
m.s[cfsProcs].addProcHeader("main", CInt, cProcParams(
(name: "argc", typ: CInt),
(name: "args", typ: ptrType(ptrType(CChar)))))
(name: "args", typ: ptrType(ptrType(CChar))),
(name: "env", typ: ptrType(ptrType(CChar)))))
m.s[cfsProcs].finishProcHeaderWithBody():
m.s[cfsProcs].addAssignment("cmdLine", "args")
m.s[cfsProcs].addAssignment("cmdCount", "argc")
m.s[cfsProcs].addAssignment("gEnv", "env")
genMainProcsWithResult(m)
m.s[cfsProcs].addNewline()
@@ -1877,7 +1865,7 @@ proc genMainProc(m: BModule) =
builder.addCallStmt(cgsymValue(m, "nimLoadLibraryError"), strLit)
loadLib(preMainBuilder, "hcr_handle", "hcrGetProc")
if m.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}:
if m.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
preMainBuilder.addCallStmt(m.config.nimMainPrefix & "PreMain")
else:
preMainBuilder.addVar(name = "rtl_handle", typ = CPointer)
@@ -1938,6 +1926,36 @@ proc genMainProc(m: BModule) =
if m.config.cppCustomNamespace.len > 0:
openNamespaceNim(m.config.cppCustomNamespace, m.s[cfsProcs])
proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag]) =
## Called from the IC backend.
if HasDatInitProc in flags:
let datInit = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "DatInit000"
g.mainModProcs.addDeclWithVisibility(Private):
g.mainModProcs.addProcHeader(ccNimCall, datInit, CVoid, cProcParams())
g.mainModProcs.finishProcHeaderAsProto()
g.mainDatInit.addCallStmt(datInit)
if HasModuleInitProc in flags:
let init = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "Init000"
g.mainModProcs.addDeclWithVisibility(Private):
g.mainModProcs.addProcHeader(ccNimCall, init, CVoid, cProcParams())
g.mainModProcs.finishProcHeaderAsProto()
if sfMainModule in m.flags:
g.mainModInit.addCallStmt(init)
elif sfSystemModule in m.flags:
g.mainDatInit.addCallStmt(init) # systemInit must called right after systemDatInit if any
else:
g.otherModsInit.addCallStmt(init)
proc whichInitProcs*(m: BModule): set[ModuleBackendFlag] =
# called from IC.
result = {}
if m.hcrOn or m.preInitProc.s(cpsInit).buf.len > 0 or m.preInitProc.s(cpsStmts).buf.len > 0:
result.incl HasModuleInitProc
for i in cfsTypeInit1..cfsDynLibInit:
if m.s[i].buf.len != 0:
result.incl HasDatInitProc
break
proc registerModuleToMain(g: BModuleList; m: BModule) =
let
init = m.getInitName
@@ -2047,7 +2065,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
if sfSystemModule in m.module.flags:
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
g.mainDatInit.addCallStmt(cgsymValue(m, "initThreadVarsEmulation"))
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc, gcYrc}:
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
g.mainDatInit.addCallStmt(cgsymValue(m, "initStackBottomWith"),
cCast(CPointer, cAddr("inner")))
@@ -2616,7 +2634,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
cgsym(m, "rawWrite")
# raise dependencies on behalf of genMainProc
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc, gcYrc}:
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
cgsym(m, "initStackBottomWith")
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
cgsym(m, "initThreadVarsEmulation")
@@ -2624,7 +2642,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
if m.g.forwardedProcs.len == 0:
incl m.flags, objHasKidsValid
if optMultiMethods in m.g.config.globalOptions or
m.g.config.selectedGC notin {gcArc, gcOrc, gcAtomicArc, gcYrc} or
m.g.config.selectedGC notin {gcArc, gcOrc, gcAtomicArc} or
vtables notin m.g.config.features:
generateIfMethodDispatchers(graph, m.idgen)

View File

@@ -75,13 +75,10 @@ type
flags*: set[TCProcFlag]
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
currLineInfo*: TLineInfo # AST codegen will make this superfluous
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, isHidden: bool, label: Natural]]
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
# in how many nested try statements we are
# (the vars must be volatile then)
# `inExcept` is true when we are in the except part of a try block.
# `isHidden` is true for compiler-injected `nkHiddenTryStmt` wrappers
# (e.g. ARC's destructor try/finally around `except T as e:` bodies);
# finallyActions walks past such wrappers to reach the user's try.
# bool is true when are in the except part of a try block
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
# using return in finally statements
labels*: Natural # for generating unique labels in the C proc

View File

@@ -139,7 +139,7 @@
import
ast, msgs, idents,
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos, trees
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
import std/tables
@@ -727,7 +727,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
n[0] = ex
result.add(n)
of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv, nkObjUpConv,
of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkObjDownConv,
nkDerefExpr, nkHiddenDeref:
var ns = false
for i in ord(n.kind == nkCast)..<n.len:
@@ -1390,34 +1390,18 @@ proc optimizeStates(ctx: var Ctx) =
for i in 0 .. ctx.states.high:
ctx.states[i].label.intVal = i
proc detectCapturedSym(c: var Ctx, s: PSym, stateIdx: int) =
if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym:
let vs = c.varStates.getOrDefault(s.itemId, localNotSeen)
if vs == localNotSeen: # First seing this variable
c.varStates[s.itemId] = stateIdx
elif vs == localRequiresLifting:
discard # Sym already marked
elif vs != stateIdx:
c.captureVar(s)
proc isClosureIterLocal(c: Ctx, s: PSym): bool =
s.kind in {skResult, skVar, skLet, skForVar, skTemp} and
sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym
proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) =
case n.kind
of nkSym:
let s = n.sym
detectCapturedSym(c, s, stateIdx)
of nkAddr, nkHiddenAddr:
let s = getRoot(n)
if s != nil and isClosureIterLocal(c, s):
detectCapturedSym(c, s, stateIdx)
# bug #25596; lifetime extension for `addr`-taken locals as
# we claim ARC/ORC do destruction based on scopes, not on last-usages.
c.captureVar(s)
for i in 0 ..< n.safeLen:
detectCapturedVars(c, n[i], stateIdx)
if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn and s != c.externExcSym:
let vs = c.varStates.getOrDefault(s.itemId, localNotSeen)
if vs == localNotSeen: # First seing this variable
c.varStates[s.itemId] = stateIdx
elif vs == localRequiresLifting:
discard # Sym already marked
elif vs != stateIdx:
c.captureVar(s)
of nkReturnStmt:
if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
# we have a `result = result` expression produced by the closure

View File

@@ -118,7 +118,7 @@ const
errInvalidCmdLineOption = "invalid command line option: '$1'"
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
errOffHintsError = "'off', 'hint', 'warning', 'error' or 'usages' expected, but '$1' found"
errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
@@ -245,12 +245,11 @@ proc processCompile(conf: ConfigRef; filename: string) =
extccomp.addExternalFileToCompile(conf, found)
const
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'yrc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
errDefaultOrSsoExpectedButXFound = "'default' or 'sso' expected, but '$1' found"
template warningOptionNoop(switch: string) =
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
@@ -267,7 +266,6 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
of "markandsweep": result = conf.selectedGC == gcMarkAndSweep
of "destructors", "arc": result = conf.selectedGC == gcArc
of "orc": result = conf.selectedGC == gcOrc
of "yrc": result = conf.selectedGC == gcYrc
of "hooks": result = conf.selectedGC == gcHooks
of "go": result = conf.selectedGC == gcGo
of "none": result = conf.selectedGC == gcNone
@@ -307,13 +305,6 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
else:
result = false
localError(conf, info, errInvalidExceptionSystem % arg)
of "strings":
case arg.normalize
of "default": result = conf.selectedStrings == stringDefault
of "sso": result = conf.selectedStrings == stringSso
else:
result = false
localError(conf, info, errDefaultOrSsoExpectedButXFound % arg)
of "experimental":
try:
result = conf.features.contains parseEnum[Feature](arg)
@@ -503,11 +494,12 @@ proc parseCommand*(command: string): Command =
of "gendepend": cmdGendepend
of "dump": cmdDump
of "parse": cmdParse
of "rod": cmdRod
of "secret": cmdInteractive
of "nop", "help": cmdNop
of "jsonscript": cmdJsonscript
of "nifc": cmdNifC # generate C from NIF files
of "ic": cmdIc # generate .build.nif for nifmake
of "deps": cmdDeps # generate .build.nif for nifmake
else: cmdUnknown
proc setCmd*(conf: ConfigRef, cmd: Command) =
@@ -579,7 +571,6 @@ proc unregisterArcOrc*(conf: ConfigRef) =
undefSymbol(conf.symbols, "gcdestructors")
undefSymbol(conf.symbols, "gcarc")
undefSymbol(conf.symbols, "gcorc")
undefSymbol(conf.symbols, "gcyrc")
undefSymbol(conf.symbols, "gcatomicarc")
undefSymbol(conf.symbols, "nimSeqsV2")
undefSymbol(conf.symbols, "nimV2")
@@ -613,10 +604,6 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
conf.selectedGC = gcOrc
defineSymbol(conf.symbols, "gcorc")
registerArcOrc(pass, conf)
of "yrc":
conf.selectedGC = gcYrc
defineSymbol(conf.symbols, "gcyrc")
registerArcOrc(pass, conf)
of "atomicarc":
conf.selectedGC = gcAtomicArc
defineSymbol(conf.symbols, "gcatomicarc")
@@ -758,17 +745,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
processMemoryManagementOption(switch, arg, pass, info, conf)
of "mm":
processMemoryManagementOption(switch, arg, pass, info, conf)
of "strings":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
case arg.normalize
of "default":
conf.selectedStrings = stringDefault
of "sso":
conf.selectedStrings = stringSso
defineSymbol(conf.symbols, "nimsso")
else:
localError(conf, info, errDefaultOrSsoExpectedButXFound % arg)
of "warnings", "w":
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
@@ -930,7 +906,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
if m.len == 0:
localError(conf, info, "Cannot resolve filename: " & arg)
else:
conf.implicitImports.add(if arg.startsWith(stdPrefix): arg else: m)
conf.implicitImports.add m
of "include":
expectArg(conf, switch, arg, pass, info)
if pass in {passCmd2, passPP}:
@@ -970,7 +946,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
expectArg(conf, switch, arg, pass, info)
var value: int = 10_000_000
discard parseSaturatedNatural(arg, value)
if value <= 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
conf.maxLoopIterationsVM = value
of "maxcalldepthvm":
expectArg(conf, switch, arg, pass, info)
@@ -1133,9 +1109,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
of "shownonexports":
expectNoArg(conf, switch, arg, pass, info)
showNonExportedFields(conf)
of "raw":
expectNoArg(conf, switch, arg, pass, info)
docRawOutput(conf)
of "exceptions":
case arg.normalize
of "cpp": conf.exc = excCpp
@@ -1167,10 +1140,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
defineSymbol(conf.symbols, "nimSeqsV2")
of "stylecheck":
case arg.normalize
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError, optStyleWarning}
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError, optStyleWarning}
of "warning": conf.globalOptions = conf.globalOptions + {optStyleWarning} - {optStyleHint, optStyleError}
of "error": conf.globalOptions = conf.globalOptions + {optStyleError} - {optStyleHint, optStyleWarning}
of "off": conf.globalOptions = conf.globalOptions - {optStyleHint, optStyleError}
of "hint": conf.globalOptions = conf.globalOptions + {optStyleHint} - {optStyleError}
of "error": conf.globalOptions = conf.globalOptions + {optStyleError}
of "usages": conf.globalOptions.incl optStyleUsages
else: localError(conf, info, errOffHintsError % arg)
of "showallmismatches":

View File

@@ -175,5 +175,3 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasSetLengthSeqUninitMagic")
defineSymbol("nimHasPreviewDuplicateModuleError")
defineSymbol("nimHasImplicitRangeConversion")

View File

@@ -10,10 +10,10 @@
## Generate a .build.nif file for nifmake from a Nim project.
## This enables incremental and parallel compilation using the `m` switch.
import std / [os, tables, sets, times, osproc]
import options, msgs, lineinfos, pathutils
import std / [os, tables, sets, times, osproc, strutils]
import options, msgs, lineinfos
import "../dist/nimony/src/lib" / [nifstreams, bitabs, nifreader, nifbuilder]
import "../dist/nimony/src/lib" / [nifstreams, nifcursors, bitabs, nifreader, nifbuilder]
import "../dist/nimony/src/gear2" / modnames
type
@@ -32,7 +32,6 @@ type
nodes: seq[Node]
processedModules: Table[string, int] # modname -> node index
includeStack: seq[string]
systemNodeId: int # ID of the system.nim node
proc toPair(c: DepContext; f: string): FilePair =
FilePair(nimFile: f, modname: moduleSuffix(f, cast[seq[string]](c.config.searchPaths)))
@@ -48,18 +47,15 @@ proc semmedFile(c: DepContext; f: FilePair): string =
proc findNifler(): string =
# Look for nifler in common locations
let nimDir = getAppDir()
result = nimDir / "nifler"
if not fileExists(result):
result = findExe("nifler")
proc findNifmake(): string =
# Look for nifmake in common locations
# Try relative to nim executable
let nimDir = getAppDir()
result = nimDir / "nifmake"
if not fileExists(result):
result = findExe("nifmake")
result = findExe("nifler")
if result.len == 0:
# Try relative to nim executable
let nimDir = getAppDir()
result = nimDir / "nifler"
if not fileExists(result):
result = nimDir / ".." / "nimony" / "bin" / "nifler"
if not fileExists(result):
result = ""
proc runNifler(c: DepContext; nimFile: string): bool =
## Run nifler deps on a file if needed. Returns true on success.
@@ -79,19 +75,22 @@ proc runNifler(c: DepContext; nimFile: string): bool =
let exitCode = execShellCmd(cmd)
result = exitCode == 0
proc resolveImport(c: DepContext; origin, toResolve: string): string =
## Resolve an import path using the compiler's normal module lookup rules.
result = findModule(c.config, toResolve, origin).string
proc resolveFile(c: DepContext; origin, toResolve: string): string =
## Resolve an import path relative to origin file
# Handle std/ prefix
var path = toResolve
if path.startsWith("std/"):
path = path.substr(4)
proc resolveInclude(c: DepContext; origin, toResolve: string): string =
## Resolve an include path relative to the including file or the search paths.
# Try relative to origin first
let originDir = parentDir(origin)
result = originDir / toResolve.addFileExt("nim")
result = originDir / path.addFileExt("nim")
if fileExists(result):
return result
# Try search paths
for searchPath in c.config.searchPaths:
result = searchPath.string / toResolve.addFileExt("nim")
result = searchPath.string / path.addFileExt("nim")
if fileExists(result):
return result
@@ -100,7 +99,7 @@ proc resolveInclude(c: DepContext; origin, toResolve: string): string =
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node)
proc processInclude(c: var DepContext; includePath: string; current: Node) =
let resolved = resolveInclude(c, current.files[current.files.len - 1].nimFile, includePath)
let resolved = resolveFile(c, current.files[current.files.len - 1].nimFile, includePath)
if resolved.len == 0 or not fileExists(resolved):
return
@@ -115,7 +114,7 @@ proc processInclude(c: var DepContext; includePath: string; current: Node) =
discard c.includeStack.pop()
proc processImport(c: var DepContext; importPath: string; current: Node) =
let resolved = resolveImport(c, current.files[0].nimFile, importPath)
let resolved = resolveFile(c, current.files[0].nimFile, importPath)
if resolved.len == 0 or not fileExists(resolved):
return
@@ -126,9 +125,6 @@ proc processImport(c: var DepContext; importPath: string; current: Node) =
# New module - create node and process it
let newNode = Node(files: @[pair], id: c.nodes.len)
current.deps.add newNode.id
# Every module depends on system.nim
if c.systemNodeId >= 0:
newNode.deps.add c.systemNodeId
c.processedModules[pair.modname] = newNode.id
c.nodes.add newNode
traverseDeps(c, pair, newNode)
@@ -137,171 +133,6 @@ proc processImport(c: var DepContext; importPath: string; current: Node) =
if existingIdx notin current.deps:
current.deps.add existingIdx
proc skipSubtree(s: var Stream; first: PackedToken) =
## Consume tokens until the ParLe at `first` is balanced. Caller has
## already obtained `first`.
if first.kind != ParLe: return
var depth = 1
while depth > 0:
let t = next(s)
if t.kind == ParLe: inc depth
elif t.kind == ParRi: dec depth
elif t.kind == EofToken: return
proc evalCondExpr(c: DepContext; s: var Stream): bool =
## Read exactly one condition expression from `s` and return its truth
## value. Consumes tokens whether the expression is recognised or not so
## the caller stays in sync. Recognises `defined(IDENT)`, the boolean
## operators `not`/`and`/`or`, and the literals `true`/`false`. Anything
## else (e.g. a call to an arbitrary proc) is treated as `true` — the
## conservative direction, since a false negative here drops a real
## dependency from the build graph.
let t = next(s)
case t.kind
of Ident:
case pool.strings[t.litId]
of "true": result = true
of "false": result = false
else: result = true
of ParLe:
let tag = pool.tags[t.tagId]
case tag
of "call", "cmd", "callstrlit", "infix", "prefix":
# First child is the head (function/operator name).
let head = next(s)
var name = ""
if head.kind == Ident: name = pool.strings[head.litId]
case name
of "defined":
let arg = next(s)
var sym = ""
if arg.kind == Ident: sym = pool.strings[arg.litId]
result = sym.len > 0 and isDefined(c.config, sym)
of "not":
result = not evalCondExpr(c, s)
of "and":
result = evalCondExpr(c, s)
if result: result = evalCondExpr(c, s)
else: skipSubtree(s, next(s))
of "or":
result = evalCondExpr(c, s)
if not result: result = evalCondExpr(c, s)
else: skipSubtree(s, next(s))
else:
result = true
# Drain whatever remains until the matching ParRi.
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
of "not":
result = not evalCondExpr(c, s)
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
of "and":
result = evalCondExpr(c, s)
if result: result = evalCondExpr(c, s)
else: skipSubtree(s, next(s))
# consume closing ParRi
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
of "or":
result = evalCondExpr(c, s)
if not result: result = evalCondExpr(c, s)
else: skipSubtree(s, next(s))
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
else:
skipSubtree(s, t)
result = true
else:
result = true
proc whenMarkerHolds(c: DepContext; s: var Stream): bool =
## Caller has just consumed the `(when` ParLe. Read children until the
## matching `)`, AND-ing each evaluated condition.
result = true
while true:
# peek by reading; if it's ParRi, we're done
let t = next(s)
if t.kind == ParRi: return
if t.kind == EofToken: return
if t.kind == ParLe:
# Re-feed by manually evaluating the subtree starting at `t`.
# evalCondExpr expects to read its own opener, so handle it directly.
let tag = pool.tags[t.tagId]
case tag
of "call", "cmd", "callstrlit", "infix", "prefix":
let head = next(s)
var name = ""
if head.kind == Ident: name = pool.strings[head.litId]
var ok = true
case name
of "defined":
let arg = next(s)
var sym = ""
if arg.kind == Ident: sym = pool.strings[arg.litId]
ok = sym.len > 0 and isDefined(c.config, sym)
of "not":
ok = not evalCondExpr(c, s)
of "and":
ok = evalCondExpr(c, s)
if ok: ok = evalCondExpr(c, s)
of "or":
ok = evalCondExpr(c, s)
if not ok: ok = evalCondExpr(c, s)
else:
ok = true
# finish the subtree
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
if not ok: result = false
of "not", "and", "or":
# Re-emit a synthetic dispatch: rewrap by descending.
var ok = true
case tag
of "not":
ok = not evalCondExpr(c, s)
of "and":
ok = evalCondExpr(c, s)
if ok: ok = evalCondExpr(c, s)
of "or":
ok = evalCondExpr(c, s)
if not ok: ok = evalCondExpr(c, s)
else: discard
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: return
if not ok: result = false
else:
# Unknown — treat as true and skip.
skipSubtree(s, t)
elif t.kind == Ident:
let v = pool.strings[t.litId]
if v == "false": result = false
# else (true / unknown ident): keep result
proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
## Read a .deps.nif file and process imports/includes
let depsPath = c.depsFile(pair)
@@ -323,27 +154,12 @@ proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
if t.kind == ParLe:
let tag = pool.tags[t.tagId]
case tag
of "import", "fromimport", "include":
# Read first child. May be a `(when COND...)` marker — parse and
# evaluate; if the condition is statically false, skip the import
# entirely. Otherwise advance past the marker and parse the path.
of "import", "fromimport":
# Read import path
t = next(s)
var live = true
if t.kind == ParLe and pool.tags[t.tagId] == "when":
# whenMarkerHolds consumes everything up to and including the
# closing `)` of the `(when ...)` subtree.
live = whenMarkerHolds(c, s)
t = next(s)
if not live:
# Drain the rest of this import/include node.
var depth = 1
while depth > 0:
let n = next(s)
if n.kind == ParLe: inc depth
elif n.kind == ParRi: dec depth
elif n.kind == EofToken: break
t = next(s)
continue
# Check for "when" marker (conditional import)
if t.kind == Ident and pool.strings[t.litId] == "when":
t = next(s) # skip it, still process the import
# Handle path expression (could be ident, string, or infix like std/foo)
var importPath = ""
if t.kind == Ident:
@@ -361,11 +177,26 @@ proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
if t.kind == Ident: # second part (foo)
importPath = importPath & "/" & pool.strings[t.litId]
if importPath.len > 0:
if tag == "include":
processInclude(c, importPath, current)
else:
processImport(c, importPath, current)
# Skip to end of node
processImport(c, importPath, current)
# Skip to end of import node
var depth = 1
while depth > 0:
t = next(s)
if t.kind == ParLe: inc depth
elif t.kind == ParRi: dec depth
of "include":
# Read include path
t = next(s)
if t.kind == Ident and pool.strings[t.litId] == "when":
t = next(s) # skip conditional marker
var includePath = ""
if t.kind == Ident:
includePath = pool.strings[t.litId]
elif t.kind == StringLit:
includePath = pool.strings[t.litId]
if includePath.len > 0:
processInclude(c, includePath, current)
# Skip to end
var depth = 1
while depth > 0:
t = next(s)
@@ -389,14 +220,12 @@ proc traverseDeps(c: var DepContext; pair: FilePair; current: Node) =
proc generateBuildFile(c: DepContext): string =
## Generate the .build.nif file for nifmake
let nimcache = getNimcacheDir(c.config).string
createDir(nimcache)
result = nimcache / c.nodes[0].files[0].modname & ".build.nif"
result = getNimcacheDir(c.config).string / c.nodes[0].files[0].modname & ".build.nif"
var b = nifbuilder.open(result)
defer: b.close()
b.addHeader("nim ic", "nifmake")
b.addHeader("nim deps", "nifmake")
b.addTree "stmts"
# Define nifler command
@@ -416,22 +245,6 @@ proc generateBuildFile(c: DepContext): string =
b.addSymbolDef "nim_m"
b.addStrLit getAppFilename()
b.addStrLit "m"
b.addStrLit "--nimcache:" & nimcache
# Add search paths
for p in c.config.searchPaths:
b.addStrLit "--path:" & p.string
b.addTree "args"
b.endTree()
b.withTree "input":
b.addIntLit 0 # main parsed file
b.endTree()
# Define nim nifc command
b.addTree "cmd"
b.addSymbolDef "nim_nifc"
b.addStrLit getAppFilename()
b.addStrLit "nifc"
b.addStrLit "--nimcache:" & nimcache
# Add search paths
for p in c.config.searchPaths:
b.addStrLit "--path:" & p.string
@@ -466,8 +279,6 @@ proc generateBuildFile(c: DepContext): string =
b.addTree "do"
b.addIdent "nim_m"
# Input: all parsed files for this module
b.withTree "input":
b.addStrLit node.files[0].nimFile
for f in node.files:
b.addTree "input"
b.addStrLit c.parsedFile(f)
@@ -481,35 +292,15 @@ proc generateBuildFile(c: DepContext): string =
b.addTree "output"
b.addStrLit c.semmedFile(pair)
b.endTree()
b.addTree "args"
b.addStrLit pair.nimFile
b.endTree()
# Final compilation step: generate executable from main module
let mainNif = c.nodes[0].files[0].nimFile
let exeFile = changeFileExt(c.nodes[0].files[0].nimFile, ExeExt)
b.addTree "do"
b.addIdent "nim_nifc"
# Input: .nim file (expanded as argument)
b.addTree "input"
b.addStrLit mainNif
b.endTree()
# Also depend on the semmed .nif files of the main module and all its
# dependencies. nifmake's topological sort orders nodes by depth; without
# these inputs the nim_nifc node sits at depth 1 (no recognized inputs)
# alongside the nifler nodes and runs *before* the nim_m steps that
# produce the .nif files it needs to read.
for node in c.nodes:
b.addTree "input"
b.addStrLit c.semmedFile(node.files[0])
b.endTree()
b.addTree "output"
b.addStrLit exeFile
b.endTree()
b.endTree()
b.endTree() # stmts
proc commandIc*(conf: ConfigRef) =
## Main entry point for `nim ic`
proc commandDeps*(conf: ConfigRef) =
## Main entry point for `nim deps`
when not defined(nimKochBootstrap):
let nifler = findNifler()
if nifler.len == 0:
@@ -529,8 +320,7 @@ proc commandIc*(conf: ConfigRef) =
nifler: nifler,
nodes: @[],
processedModules: initTable[string, int](),
includeStack: @[],
systemNodeId: -1
includeStack: @[]
)
# Create root node for main project file
@@ -539,28 +329,12 @@ proc commandIc*(conf: ConfigRef) =
c.nodes.add rootNode
c.processedModules[rootPair.modname] = 0
# model the system.nim dependency:
let sysNode = Node(files: @[toPair(c, (conf.libpath / RelativeFile"system.nim").string)], id: 1)
c.nodes.add sysNode
c.systemNodeId = sysNode.id
rootNode.deps.add sysNode.id
# Process dependencies
traverseDeps(c, rootPair, rootNode)
# Generate build file
let buildFile = generateBuildFile(c)
rawMessage(conf, hintSuccess, "generated: " & buildFile)
# Automatically run nifmake
let nifmake = findNifmake()
if nifmake.len == 0:
rawMessage(conf, hintSuccess, "run: nifmake run " & buildFile)
else:
let cmd = quoteShell(nifmake) & " run " & quoteShell(buildFile)
rawMessage(conf, hintExecuting, cmd)
let exitCode = execShellCmd(cmd)
if exitCode != 0:
rawMessage(conf, errGenerated, "nifmake failed with exit code: " & $exitCode)
rawMessage(conf, hintSuccess, "run: nifmake run " & buildFile)
else:
rawMessage(conf, errGenerated, "nim ic not available in bootstrap build")
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")

View File

@@ -483,7 +483,7 @@ proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
gen(c, body)
if root.kind == skResult:
genImplicitReturn(c)
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
result = c.code # will move
else:
shallowCopy(result, c.code)

View File

@@ -148,7 +148,7 @@ proc cmpDecimalsIgnoreCase(a, b: string): int =
limitB = iB
while limitA < aLen and isDigit(a[limitA]): inc limitA
while limitB < bLen and isDigit(b[limitB]): inc limitB
var pos = max(limitA-iA, limitB-iB)
var pos = max(limitA-iA, limitB-iA)
while pos > 0:
if limitA-pos < iA: # digit in `a` is 0 effectively
result = ord('0') - ord(b[limitB-pos])
@@ -433,9 +433,6 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
proc genComment(d: PDoc, n: PNode): PRstNode =
if n.comment.len > 0:
if optDocRaw in d.conf.globalOptions:
return newRstLeaf(n.comment)
d.sharedState.currFileIdx = addRstFileIndex(d, n.info)
try:
result = parseRst(n.comment,
@@ -540,11 +537,10 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
elif s != nil and s.kind in {skType, skVar, skLet, skConst} and
sfExported in s.flags and s.owner != nil and
belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml:
let href = (if d.module == s.owner: ""
else: externalDep(d, s.owner).changeFileExt("html")
) & "#" & literal
result.addf "<a href=\"$1\"><span class=\"Identifier\">$2</span></a>",
[href, escLit]
let external = externalDep(d, s.owner)
result.addf "<a href=\"$1#$2\"><span class=\"Identifier\">$3</span></a>",
[changeFileExt(external, "html"), literal,
escLit]
else:
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [escLit])
@@ -1180,12 +1176,8 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
"col": %n.info.col}
)
if comm != nil:
if optDocRaw in d.conf.globalOptions:
result.json["description"] = %comm.text
else:
result.rst = comm
result.rstField = "description"
result.rst = comm
result.rstField = "description"
if r.buf.len > 0:
result.json["code"] = %r.buf
if k in routineKinds:
@@ -1426,7 +1418,7 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags
of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
of nkCallKinds:
var comm = default(ItemPre)
var comm: ItemPre = default(ItemPre)
getAllRunnableExamples(d, n, comm)
if comm.len != 0: d.modDescPre.add(comm)
else: discard

178
compiler/ic/bitabs.nim Normal file
View File

@@ -0,0 +1,178 @@
## A BiTable is a table that can be seen as an optimized pair
## of `(Table[LitId, Val], Table[Val, LitId])`.
import std/hashes
import rodfiles
when defined(nimPreviewSlimSystem):
import std/assertions
type
LitId* = distinct uint32
BiTable*[T] = object
vals: seq[T] # indexed by LitId
keys: seq[LitId] # indexed by hash(val)
proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[])
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
result = (h + 1) and maxHash
template maxHash(t): untyped = high(t.keys)
template isFilled(x: LitId): bool = x.uint32 > 0'u32
proc `$`*(x: LitId): string {.borrow.}
proc `<`*(x, y: LitId): bool {.borrow.}
proc `<=`*(x, y: LitId): bool {.borrow.}
proc `==`*(x, y: LitId): bool {.borrow.}
proc hash*(x: LitId): Hash {.borrow.}
proc len*[T](t: BiTable[T]): int = t.vals.len
proc mustRehash(length, counter: int): bool {.inline.} =
assert(length > counter)
result = (length * 2 < counter * 3) or (length - counter < 4)
const
idStart = 1
template idToIdx(x: LitId): int = x.int - idStart
proc hasLitId*[T](t: BiTable[T]; x: LitId): bool =
let idx = idToIdx(x)
result = idx >= 0 and idx < t.vals.len
proc enlarge[T](t: var BiTable[T]) =
var n: seq[LitId]
newSeq(n, len(t.keys) * 2)
swap(t.keys, n)
for i in 0..high(n):
let eh = n[i]
if isFilled(eh):
var j = hash(t.vals[idToIdx eh]) and maxHash(t)
while isFilled(t.keys[j]):
j = nextTry(j, maxHash(t))
t.keys[j] = move n[i]
proc getKeyId*[T](t: BiTable[T]; v: T): LitId =
let origH = hash(v)
var h = origH and maxHash(t)
if t.keys.len != 0:
while true:
let litId = t.keys[h]
if not isFilled(litId): break
if t.vals[idToIdx t.keys[h]] == v: return litId
h = nextTry(h, maxHash(t))
return LitId(0)
proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId =
let origH = hash(v)
var h = origH and maxHash(t)
if t.keys.len != 0:
while true:
let litId = t.keys[h]
if not isFilled(litId): break
if t.vals[idToIdx t.keys[h]] == v: return litId
h = nextTry(h, maxHash(t))
# not found, we need to insert it:
if mustRehash(t.keys.len, t.vals.len):
enlarge(t)
# recompute where to insert:
h = origH and maxHash(t)
while true:
let litId = t.keys[h]
if not isFilled(litId): break
h = nextTry(h, maxHash(t))
else:
setLen(t.keys, 16)
h = origH and maxHash(t)
result = LitId(t.vals.len + idStart)
t.keys[h] = result
t.vals.add v
proc `[]`*[T](t: var BiTable[T]; litId: LitId): var T {.inline.} =
let idx = idToIdx litId
assert idx < t.vals.len
result = t.vals[idx]
proc `[]`*[T](t: BiTable[T]; litId: LitId): lent T {.inline.} =
let idx = idToIdx litId
assert idx < t.vals.len
result = t.vals[idx]
proc hash*[T](t: BiTable[T]): Hash =
## as the keys are hashes of the values, we simply use them instead
var h: Hash = 0
for i, n in pairs t.keys:
h = h !& hash((i, n))
result = !$h
proc store*[T](f: var RodFile; t: BiTable[T]) =
storeSeq(f, t.vals)
storeSeq(f, t.keys)
proc load*[T](f: var RodFile; t: var BiTable[T]) =
loadSeq(f, t.vals)
loadSeq(f, t.keys)
proc sizeOnDisc*(t: BiTable[string]): int =
result = 4
for x in t.vals:
result += x.len + 4
result += t.keys.len * sizeof(LitId)
when isMainModule:
var t: BiTable[string]
echo getOrIncl(t, "hello")
echo getOrIncl(t, "hello")
echo getOrIncl(t, "hello3")
echo getOrIncl(t, "hello4")
echo getOrIncl(t, "helloasfasdfdsa")
echo getOrIncl(t, "hello")
echo getKeyId(t, "hello")
echo getKeyId(t, "none")
for i in 0 ..< 100_000:
discard t.getOrIncl($i & "___" & $i)
for i in 0 ..< 100_000:
assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4
echo "begin"
echo t.vals.len
echo t.vals[0]
echo t.vals[1004]
echo "middle"
var tf: BiTable[float]
discard tf.getOrIncl(0.4)
discard tf.getOrIncl(16.4)
discard tf.getOrIncl(32.4)
echo getKeyId(tf, 32.4)
var f2 = open("testblah.bin", fmWrite)
echo store(f2, tf)
f2.close
var f1 = open("testblah.bin", fmRead)
var t2: BiTable[float]
echo f1.load(t2)
echo t2.vals.len
echo getKeyId(t2, 32.4)
echo "end"
f1.close

179
compiler/ic/cbackend.nim Normal file
View File

@@ -0,0 +1,179 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## New entry point into our C/C++ code generator. Ideally
## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
## to work on packed trees directly and produce the C code as an AST which can
## then be rendered to text in a very simple manner. Unfortunately nobody wrote
## this code. So instead we wrap the existing cgen.nim and its friends so that
## we call directly into the existing code generation logic but avoiding the
## naive, outdated `passes` design. Thus you will see some
## `useAliveDataFromDce in flags` checks in the old code -- the old code is
## also doing cross-module dependency tracking and DCE that we don't need
## anymore. DCE is now done as prepass over the entire packed module graph.
import std/[packedsets, algorithm, tables]
when defined(nimPreviewSlimSystem):
import std/assertions
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
pathutils, extccomp, msgs, modulepaths]
import packed_ast, ic, dce, rodfiles
proc unpackTree(g: ModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
var decoder = initPackedDecoder(g.config, g.cache)
result = loadNodes(decoder, g.packed, thisModule, tree, n)
proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
if g.backend == nil:
g.backend = cgendata.newModuleList(g)
assert g.backend != nil
var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config, idgenFromLoadedModule(m))
proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
var bmod = BModuleList(g.backend).mods[m.module.position]
assert bmod != nil
bmod.flags.incl useAliveDataFromDce
bmod.alive = move alive[m.module.position]
for p in allNodes(m.fromDisk.topLevel):
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
cgen.genTopLevelStmt(bmod, n)
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
for disp in getDispatchers(g):
genProcLvl3(bmod, disp)
m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
for x in mitems(m.fromDisk.emittedTypeInfo):
#echo "found type ", x, " for file ", int(origin)
g.emittedTypeInfo[x] = origin
proc addFileToLink(config: ConfigRef; m: PSym) =
let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
let ext =
if config.backend == backendCpp: ".nim.cpp"
elif config.backend == backendObjc: ".nim.m"
else: ".nim.c"
let cfile = changeFileExt(completeCfilePath(config,
mangleModuleName(config, filename).AbsoluteFile), ext)
let objFile = completeCfilePath(config, toObjFile(config, cfile))
if fileExists(objFile):
var cf = Cfile(nimname: m.name.s, cname: cfile,
obj: objFile,
flags: {CfileFlag.Cached})
addFileToCompile(config, cf)
when defined(debugDce):
import os, std/packedsets
proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) =
var f = rodfiles.create(asymFile.string)
f.storeHeader()
f.storeSection aliveSymsSection
f.storeSeq(s)
close f
template prepare {.dirty.} =
let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
var s = newSeqOfCap[int32](alive[position].len)
for a in items(alive[position]): s.add int32(a)
sort(s)
proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) =
prepare()
storeAliveSymsImpl(asymFile, s)
proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
prepare()
var f2 = rodfiles.open(asymFile.string)
f2.loadHeader()
f2.loadSection aliveSymsSection
var oldData: seq[int32] = @[]
f2.loadSeq(oldData)
f2.close
if f2.err == ok and oldData == s:
result = false
else:
when defined(debugDce):
let oldAsSet = toPackedSet[int32](oldData)
let newAsSet = toPackedSet[int32](s)
echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err
echo "in old but not in new ", oldAsSet.difference(newAsSet), " number of entries in old ", oldAsSet.len
echo "in new but not in old ", newAsSet.difference(oldAsSet), " number of entries in new ", newAsSet.len
#if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0:
# echo "command failed"
result = true
storeAliveSymsImpl(asymFile, s)
proc genPackedModule(g: ModuleGraph, i: int; alive: var AliveSyms) =
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading, stored:
assert false
of storing, outdated:
storeAliveSyms(g.config, g.packed[i].module.position, alive)
generateCodeForModule(g, g.packed[i], alive)
closeRodFile(g, g.packed[i].module)
of loaded:
if g.packed[i].loadedButAliveSetChanged:
generateCodeForModule(g, g.packed[i], alive)
else:
addFileToLink(g.config, g.packed[i].module)
replayTypeInfo(g, g.packed[i], FileIndex(i))
if g.backend == nil:
g.backend = cgendata.newModuleList(g)
registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags)
proc generateCode*(g: ModuleGraph) =
## The single entry point, generate C(++) code for the entire
## Nim program aka `ModuleGraph`.
resetForBackend(g)
var alive = computeAliveSyms(g.packed, g.config)
when false:
for i in 0..<len(g.packed):
echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i))
# First pass: Setup all the backend modules for all the modules that have
# changed:
for i in 0..<len(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading, stored:
assert false
of storing, outdated:
setupBackendModule(g, g.packed[i])
of loaded:
# Even though this module didn't change, DCE might trigger a change.
# Consider this case: Module A uses symbol S from B and B does not use
# S itself. A is then edited not to use S either. Thus we have to
# recompile B in order to remove S from the final result.
if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
g.packed[i].loadedButAliveSetChanged = true
setupBackendModule(g, g.packed[i])
# Second pass: Code generation.
let mainModuleIdx = g.config.projectMainIdx2.int
# We need to generate the main module last, because only then
# all init procs have been registered:
for i in 0..<len(g.packed):
if i != mainModuleIdx:
genPackedModule(g, i, alive)
if mainModuleIdx >= 0:
genPackedModule(g, mainModuleIdx, alive)

169
compiler/ic/dce.nim Normal file
View File

@@ -0,0 +1,169 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Dead code elimination (=DCE) for IC.
import std/[intsets, tables]
when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, options, lineinfos, types]
import packed_ast, ic, bitabs
type
AliveSyms* = seq[IntSet]
AliveContext* = object ## Purpose is to fill the 'alive' field.
stack: seq[(int, TOptions, NodePos)] ## A stack for marking symbols as alive.
decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations.
thisModule: int ## The module we're currently analysing for DCE.
alive: AliveSyms ## The final result of our computation.
options: TOptions
compilerProcs: Table[string, (int, int32)]
proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
## "Exported to C" procs are special (these are marked with '.exportc') because these
## must not be optimized away!
let symPtr = unsafeAddr g[c.thisModule].fromDisk.syms[symId]
let flags = symPtr.flags
# due to a bug/limitation in the lambda lifting, unused inner procs
# are not transformed correctly; issue (#411). However, the whole purpose here
# is to eliminate unused procs. So there is no special logic required for this case.
if sfCompileTime notin flags:
if ({sfExportc, sfCompilerProc} * flags != {}) or
(symPtr.kind == skMethod):
result = true
else:
result = false
# XXX: This used to be a condition to:
# (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
if sfCompilerProc in flags:
c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId)
else:
result = false
template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: int32) =
## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will
## be analysed.
if not c.alive[module].containsOrIncl(item):
var body = g[module].fromDisk.syms[item].ast
if body != emptyNodeId:
let opt = g[module].fromDisk.syms[item].options
if g[module].fromDisk.syms[item].kind in routineKinds:
body = NodeId ithSon(g[module].fromDisk.bodies, NodePos body, bodyPos)
c.stack.add((module, opt, NodePos(body)))
when false:
let nid = g[module].fromDisk.syms[item].name
if nid != LitId(0):
let name = g[module].fromDisk.strings[nid]
if name in ["nimFrame", "callDepthLimitReached"]:
echo "I was called! ", name, " body exists: ", body != emptyNodeId, " ", module, " ", item
proc requestCompilerProc(c: var AliveContext; g: PackedModuleGraph; name: string) =
let (module, item) = c.compilerProcs[name]
followLater(c, g, module, item)
proc loadTypeKind(t: PackedItemId; c: AliveContext; g: PackedModuleGraph; toSkip: set[TTypeKind]): TTypeKind =
template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.types[t.item].kind
var t2 = translateId(t, g, c.thisModule, c.decoder.config)
result = t2.kind
while result in toSkip:
t2 = translateId(g[t2.module].fromDisk.types[t2.item].types[^1], g, t2.module, c.decoder.config)
result = t2.kind
proc rangeCheckAnalysis(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
## Replicates the logic of `ccgexprs.genRangeChck`.
## XXX Refactor so that the duplicated logic is avoided. However, for now it's not clear
## the approach has enough merit.
var dest = loadTypeKind(n.typ, c, g, abstractVar)
if optRangeCheck notin c.options or dest in {tyUInt..tyUInt64}:
discard "no need to generate a check because it was disabled"
else:
let n0t = loadTypeKind(n.firstSon.typ, c, g, {})
if n0t in {tyUInt, tyUInt64}:
c.requestCompilerProc(g, "raiseRangeErrorNoArgs")
else:
let raiser =
case loadTypeKind(n.typ, c, g, abstractVarRange)
of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
of tyFloat..tyFloat128: "raiseRangeErrorF"
else: "raiseRangeErrorI"
c.requestCompilerProc(g, raiser)
proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
## Marks the symbols we encounter when we traverse the AST at `tree[n]` as alive, unless
## it is purely in a declarative context (type section etc.).
case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
discard "ignore non-sym atoms"
of nkSym:
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, tree[n].soperand)
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkNone
assert n2.kind == nkNone
let m = n1.litId
let item = tree[n2].soperand
let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
followLater(c, g, otherModule, item)
of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
nkCommentStmt, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkStaticStmt:
discard
of nkVarSection, nkLetSection, nkConstSection:
# XXX ignore the defining local variable name?
for son in sonsReadonly(tree, n):
aliveCode(c, g, tree, son)
of nkChckRangeF, nkChckRange64, nkChckRange:
rangeCheckAnalysis(c, g, tree, n)
of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef:
if n.firstSon.kind == nkSym and isNotGeneric(n):
let item = tree[n.firstSon].soperand
if isExportedToC(c, g, item):
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, item)
else:
for son in sonsReadonly(tree, n):
aliveCode(c, g, tree, son)
proc followNow(c: var AliveContext; g: PackedModuleGraph) =
## Mark all entries in the stack. Marking can add more entries
## to the stack but eventually we have looked at every alive symbol.
while c.stack.len > 0:
let (modId, opt, ast) = c.stack.pop()
c.thisModule = modId
c.options = opt
aliveCode(c, g, g[modId].fromDisk.bodies, ast)
proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms =
## Entry point for our DCE algorithm.
var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf),
thisModule: -1, alive: newSeq[IntSet](g.len),
options: conf.options)
for i in countdown(len(g)-1, 0):
if g[i].status != undefined:
c.thisModule = i
for p in allNodes(g[i].fromDisk.topLevel):
aliveCode(c, g, g[i].fromDisk.topLevel, p)
followNow(c, g)
result = move(c.alive)
proc isAlive*(a: AliveSyms; module: int, item: int32): bool =
## Backends use this to query if a symbol is `alive` which means
## we need to produce (C/C++/etc) code for it.
result = a[module].contains(item)

56
compiler/ic/design.rst Normal file
View File

@@ -0,0 +1,56 @@
====================================
Incremental Recompilations
====================================
We split the Nim compiler into a frontend and a backend.
The frontend produces a set of `.rod` files. Every `.nim` module
produces its own `.rod` file.
- The IR must be a faithful representation of the AST in memory.
- The backend can do its own caching but doesn't have to. In the
current implementation the backend also caches its results.
Advantage of the "set of files" vs the previous global database:
- By construction, we either read from the `.rod` file or from the
`.nim` file, there can be no inconsistency. There can also be no
partial updates.
- No dependency to external packages (SQLite). SQLite simply is too
slow and the old way of serialization was too slow too. We use a
format designed for Nim and expect to base further tools on this
file format.
References to external modules must be (moduleId, symId) pairs.
The symbol IDs are module specific. This way no global ID increment
mechanism needs to be implemented that we could get wrong. ModuleIds
are rod-file specific too.
Global state
------------
There is no global state.
Rod File Format
---------------
It's a simple binary file format. `rodfiles.nim` contains some details.
Backend
-------
Nim programmers have to come to enjoy whole-program dead code elimination,
by default. Since this is a "whole program" optimization, it does break
modularity. However, thanks to the packed AST representation we can perform
this global analysis without having to unpack anything. This is basically
a mark&sweep GC algorithm:
- Start with the top level statements. Every symbol that is referenced
from a top level statement is not "dead" and needs to be compiled by
the backend.
- Every symbol referenced from a referenced symbol also has to be
compiled.
Caching logic: Only if the set of alive symbols is different from the
last run, the module has to be regenerated.

1349
compiler/ic/ic.nim Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
#
#
# The Nim Compiler
# (c) Copyright 2024 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# For the line information we use 32 bits. They are used as follows:
# Bit 0 (AsideBit): If we have inline line information or not. If not, the
# remaining 31 bits are used as an index into a seq[(LitId, int, int)].
#
# We use 10 bits for the "file ID", this means a program can consist of as much
# as 1024 different files. (If it uses more files than that, the overflow bit
# would be set.)
# This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column
# so 128 is the limit and 14 bits for the line number.
# The packed representation supports files with up to 16384 lines.
# Keep in mind that whenever any limit is reached the AsideBit is set and the real line
# information is kept in a side channel.
import std / assertions
const
AsideBit = 1
FileBits = 10
LineBits = 14
ColBits = 7
FileMax = (1 shl FileBits) - 1
LineMax = (1 shl LineBits) - 1
ColMax = (1 shl ColBits) - 1
static:
assert AsideBit + FileBits + LineBits + ColBits == 32
import .. / ic / [bitabs, rodfiles] # for LitId
type
PackedLineInfo* = distinct uint32
LineInfoManager* = object
aside: seq[(LitId, int32, int32)]
const
NoLineInfo* = PackedLineInfo(0'u32)
proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo =
if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax:
let col = if col < 0'i32: 0'u32 else: col.uint32
let line = if line < 0'i32: 0'u32 else: line.uint32
# use inline representation:
result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or
(col shl uint32(AsideBit + FileBits + LineBits)))
else:
result = PackedLineInfo((m.aside.len shl 1) or AsideBit)
m.aside.add (file, line, col)
proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) =
let i = i.uint32
if (i and 1'u32) == 0'u32:
# inline representation:
result = (LitId((i shr 1'u32) and FileMax.uint32),
int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32),
int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32))
else:
result = m.aside[int(i shr 1'u32)]
proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId =
result = unpack(m, i)[0]
proc store*(r: var RodFile; m: LineInfoManager) = storeSeq(r, m.aside)
proc load*(r: var RodFile; m: var LineInfoManager) = loadSeq(r, m.aside)
when isMainModule:
var m = LineInfoManager(aside: @[])
for i in 0'i32..<16388'i32:
for col in 0'i32..<100'i32:
let packed = pack(m, LitId(1023), i, col)
let u = unpack(m, packed)
assert u[0] == LitId(1023)
assert u[1] == i
assert u[2] == col
echo m.aside.len

155
compiler/ic/integrity.nim Normal file
View File

@@ -0,0 +1,155 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Integrity checking for a set of .rod files.
## The set must cover a complete Nim project.
import std/[sets, tables]
when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, modulegraphs]
import packed_ast, bitabs, ic
type
CheckedContext = object
g: ModuleGraph
thisModule: int32
checkedSyms: HashSet[ItemId]
checkedTypes: HashSet[ItemId]
proc checkType(c: var CheckedContext; typeId: PackedItemId)
proc checkForeignSym(c: var CheckedContext; symId: PackedItemId)
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos)
proc checkTypeObj(c: var CheckedContext; typ: PackedType) =
for child in typ.types:
checkType(c, child)
if typ.n != emptyNodeId:
checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos typ.n)
if typ.sym != nilItemId:
checkForeignSym(c, typ.sym)
if typ.owner != nilItemId:
checkForeignSym(c, typ.owner)
checkType(c, typ.typeInst)
proc checkType(c: var CheckedContext; typeId: PackedItemId) =
if typeId == nilItemId: return
let itemId = translateId(typeId, c.g.packed, c.thisModule, c.g.config)
if not c.checkedTypes.containsOrIncl(itemId):
let oldThisModule = c.thisModule
c.thisModule = itemId.module
checkTypeObj c, c.g.packed[itemId.module].fromDisk.types[itemId.item]
c.thisModule = oldThisModule
proc checkSym(c: var CheckedContext; s: PackedSym) =
if s.name != LitId(0):
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId s.name
checkType c, s.typ
if s.ast != emptyNodeId:
checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos s.ast)
if s.owner != nilItemId:
checkForeignSym(c, s.owner)
proc checkLocalSym(c: var CheckedContext; item: int32) =
let itemId = ItemId(module: c.thisModule, item: item)
if not c.checkedSyms.containsOrIncl(itemId):
checkSym c, c.g.packed[c.thisModule].fromDisk.syms[item]
proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) =
let itemId = translateId(symId, c.g.packed, c.thisModule, c.g.config)
if not c.checkedSyms.containsOrIncl(itemId):
let oldThisModule = c.thisModule
c.thisModule = itemId.module
checkSym c, c.g.packed[itemId.module].fromDisk.syms[itemId.item]
c.thisModule = oldThisModule
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
let t = findType(tree, n)
if t != nilItemId:
checkType(c, t)
case n.kind
of nkEmpty, nkNilLit, nkType, nkNilRodNode:
discard
of nkIdent:
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
of nkSym:
checkLocalSym(c, tree[n].soperand)
of directIntLit:
discard
of externIntLit, nkFloatLit..nkFloat128Lit:
assert c.g.packed[c.thisModule].fromDisk.numbers.hasLitId n.litId
of nkStrLit..nkTripleStrLit:
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkNone
assert n2.kind == nkNone
checkForeignSym(c, PackedItemId(module: n1.litId, item: tree[n2].soperand))
else:
for n0 in sonsReadonly(tree, n):
checkNode(c, tree, n0)
proc checkTree(c: var CheckedContext; t: PackedTree) =
for p in allNodes(t): checkNode(c, t, p)
proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) =
for symId in symIds:
assert symId >= 0 and symId < m.syms.len, $symId & " " & $m.syms.len
proc checkModule(c: var CheckedContext; m: PackedModule) =
# We check that:
# - Every symbol references existing types and symbols.
# - Every tree node references existing types and symbols.
for _, v in pairs(m.syms):
checkLocalSym c, v.id
checkTree c, m.toReplay
checkTree c, m.topLevel
for e in m.exports:
#assert e[1] >= 0 and e[1] < m.syms.len
assert e[0] == m.syms[e[1]].name
for e in m.compilerProcs:
#assert e[1] >= 0 and e[1] < m.syms.len
assert e[0] == m.syms[e[1]].name
checkLocalSymIds c, m, m.converters
checkLocalSymIds c, m, m.methods
checkLocalSymIds c, m, m.trmacros
checkLocalSymIds c, m, m.pureEnums
#[
To do: Check all these fields:
reexports*: seq[(LitId, PackedItemId)]
macroUsages*: seq[(PackedItemId, PackedLineInfo)]
typeInstCache*: seq[(PackedItemId, PackedItemId)]
procInstCache*: seq[PackedInstantiation]
attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
methodsPerType*: seq[(PackedItemId, PackedItemId)]
dispatchers*: seq[PackedItemId]
]#
proc checkIntegrity*(g: ModuleGraph) =
var c = CheckedContext(g: g)
for i in 0..<len(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading:
assert false, "cannot check integrity: Module still loading"
of stored, storing, outdated, loaded:
c.thisModule = int32 i
checkModule(c, g.packed[i].fromDisk)

183
compiler/ic/navigator.nim Normal file
View File

@@ -0,0 +1,183 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Supports the "nim check --ic:legacy --defusages:FILE,LINE,COL"
## IDE-like features. It uses the set of .rod files to accomplish
## its task. The set must cover a complete Nim project.
import std/[sets, tables]
from std/os import nil
from std/private/miscdollars import toLocation
when defined(nimPreviewSlimSystem):
import std/assertions
import ".." / [ast, modulegraphs, msgs, options]
import iclineinfos
import packed_ast, bitabs, ic
type
UnpackedLineInfo = object
file: LitId
line, col: int
NavContext = object
g: ModuleGraph
thisModule: int32
trackPos: UnpackedLineInfo
alreadyEmitted: HashSet[string]
outputSep: char # for easier testing, use short filenames and spaces instead of tabs.
proc isTracked(man: LineInfoManager; current: PackedLineInfo, trackPos: UnpackedLineInfo, tokenLen: int): bool =
let (currentFile, currentLine, currentCol) = man.unpack(current)
if currentFile == trackPos.file and currentLine == trackPos.line:
let col = trackPos.col
if col >= currentCol and col < currentCol+tokenLen:
result = true
else:
result = false
else:
result = false
proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool =
result = s.name != LitId(0) and
isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool =
let name = c.g.packed[s.module].fromDisk.syms[s.item].name
result = name != LitId(0) and
isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
const
EmptyItemId = ItemId(module: -1'i32, item: -1'i32)
proc search(c: var NavContext; tree: PackedTree): ItemId =
# We use the linear representation here directly:
for i in 0..<len(tree):
let i = NodePos(i)
case tree[i].kind
of nkSym:
let item = tree[i].soperand
if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree[i].info):
return ItemId(module: c.thisModule, item: item)
of nkModuleRef:
let (currentFile, currentLine, currentCol) = c.g.packed[c.thisModule].fromDisk.man.unpack(tree[i].info)
if currentLine == c.trackPos.line and currentFile == c.trackPos.file:
let (n1, n2) = sons2(tree, i)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let pId = PackedItemId(module: n1.litId, item: tree[n2].soperand)
let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
if searchForeignSym(c, itemId, tree[i].info):
return itemId
else: discard
return EmptyItemId
proc isDecl(tree: PackedTree; n: NodePos): bool =
# XXX This is not correct yet.
const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef,
nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection,
nkIdentDefs, nkEnumTy, nkVarTuple}
result = n.int >= 0 and tree[n].kind in declarativeNodes
proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) =
let (fileId, line, col) = unpack(c.g.packed[c.thisModule].fromDisk.man, info)
var m = ""
var file = c.g.packed[c.thisModule].fromDisk.strings[fileId]
if c.outputSep == ' ':
file = os.extractFilename file
toLocation(m, file, line, col + ColOffset)
if not c.alreadyEmitted.containsOrIncl(m):
msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m
proc list(c: var NavContext; tree: PackedTree; sym: ItemId) =
for i in 0..<len(tree):
let i = NodePos(i)
case tree[i].kind
of nkSym:
let item = tree[i].soperand
if sym.item == item and sym.module == c.thisModule:
usage(c, tree[i].info, isDecl(tree, parent(i)))
of nkModuleRef:
let (n1, n2) = sons2(tree, i)
assert n1.kind == nkNone
assert n2.kind == nkNone
let pId = PackedItemId(module: n1.litId, item: tree[n2].soperand)
let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
if itemId.item == sym.item and sym.module == itemId.module:
usage(c, tree[i].info, isDecl(tree, parent(i)))
else: discard
proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int =
for i in 0..<len(g.packed):
for k in 1..high(g.packed[i].fromDisk.includes):
# we start from 1 because the first "include" file is
# the module's filename.
if os.cmpPaths(g.packed[i].fromDisk.strings[g.packed[i].fromDisk.includes[k][0]], fullPath) == 0:
return i
return -1
proc nav(g: ModuleGraph) =
# translate the track position to a packed position:
let unpacked = g.config.m.trackPos
var mid = unpacked.fileIndex.int
let fullPath = toFullPath(g.config, unpacked.fileIndex)
if g.packed[mid].status == undefined:
# check if 'mid' is an include file of some other module:
mid = searchForIncludeFile(g, fullPath)
if mid < 0:
localError(g.config, unpacked, "unknown file name: " & fullPath)
return
let fileId = g.packed[mid].fromDisk.strings.getKeyId(fullPath)
if fileId == LitId(0):
internalError(g.config, unpacked, "cannot find a valid file ID")
return
var c = NavContext(
g: g,
thisModule: int32 mid,
trackPos: UnpackedLineInfo(line: unpacked.line.int, col: unpacked.col.int, file: fileId),
outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t'
)
var symId = search(c, g.packed[mid].fromDisk.topLevel)
if symId == EmptyItemId:
symId = search(c, g.packed[mid].fromDisk.bodies)
if symId == EmptyItemId:
localError(g.config, unpacked, "no symbol at this position")
return
for i in 0..<len(g.packed):
# case statement here to enforce exhaustive checks.
case g.packed[i].status
of undefined:
discard "nothing to do"
of loading:
assert false, "cannot check integrity: Module still loading"
of stored, storing, outdated, loaded:
c.thisModule = int32 i
list(c, g.packed[i].fromDisk.topLevel, symId)
list(c, g.packed[i].fromDisk.bodies, symId)
proc navDefinition*(g: ModuleGraph) = nav(g)
proc navUsages*(g: ModuleGraph) = nav(g)
proc navDefusages*(g: ModuleGraph) = nav(g)
proc writeRodFiles*(g: ModuleGraph) =
for i in 0..<len(g.packed):
case g.packed[i].status
of undefined, loading, stored, loaded:
discard "nothing to do"
of storing, outdated:
closeRodFile(g, g.packed[i].module)

367
compiler/ic/packed_ast.nim Normal file
View File

@@ -0,0 +1,367 @@
#
#
# The Nim Compiler
# (c) Copyright 2020 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Packed AST representation, mostly based on a seq of nodes.
## For IC support. Far future: Rewrite the compiler passes to
## use this representation directly in all the transformations,
## it is superior.
import std/[hashes, tables, strtabs]
import bitabs, rodfiles
import ".." / [ast, options]
import iclineinfos
when defined(nimPreviewSlimSystem):
import std/assertions
type
SymId* = distinct int32
ModuleId* = distinct int32
NodePos* = distinct int
NodeId* = distinct int32
PackedItemId* = object
module*: LitId # 0 if it's this module
item*: int32 # same as the in-memory representation
const
nilItemId* = PackedItemId(module: LitId(0), item: 0.int32)
const
emptyNodeId* = NodeId(-1)
type
PackedLib* = object
kind*: TLibKind
generated*: bool
isOverridden*: bool
name*: LitId
path*: NodeId
PackedSym* = object
id*: int32
kind*: TSymKind
name*: LitId
typ*: PackedItemId
flags*: TSymFlags
magic*: TMagic
info*: PackedLineInfo
ast*: NodeId
owner*: PackedItemId
guard*: PackedItemId
bitsize*: int
alignment*: int # for alignment
options*: TOptions
position*: int
offset*: int32
disamb*: int32
externalName*: LitId # instead of TLoc
locFlags*: TLocFlags
annex*: PackedLib
when hasFFI:
cname*: LitId
constraint*: NodeId
instantiatedFrom*: PackedItemId
PackedType* = object
id*: int32
kind*: TTypeKind
callConv*: TCallingConvention
#nodekind*: TNodeKind
flags*: TTypeFlags
types*: seq[PackedItemId]
n*: NodeId
#nodeflags*: TNodeFlags
sym*: PackedItemId
owner*: PackedItemId
size*: BiggestInt
align*: int16
paddingAtEnd*: int16
# not serialized: loc*: TLoc because it is backend-specific
typeInst*: PackedItemId
nonUniqueId*: int32
PackedNode* = object # 8 bytes
x: uint32
info*: PackedLineInfo
PackedTree* = object ## usually represents a full Nim module
nodes: seq[PackedNode]
withFlags: seq[(int32, TNodeFlags)]
withTypes: seq[(int32, PackedItemId)]
PackedInstantiation* = object
key*, sym*: PackedItemId
concreteTypes*: seq[PackedItemId]
const
NodeKindBits = 8'u32
NodeKindMask = (1'u32 shl NodeKindBits) - 1'u32
template kind*(n: PackedNode): TNodeKind = TNodeKind(n.x and NodeKindMask)
template uoperand*(n: PackedNode): uint32 = (n.x shr NodeKindBits)
template soperand*(n: PackedNode): int32 = int32(uoperand(n))
template toX(k: TNodeKind; operand: uint32): uint32 =
uint32(k) or (operand shl NodeKindBits)
template toX(k: TNodeKind; operand: LitId): uint32 =
uint32(k) or (operand.uint32 shl NodeKindBits)
template typeId*(n: PackedNode): PackedItemId = n.typ
proc `==`*(a, b: SymId): bool {.borrow.}
proc hash*(a: SymId): Hash {.borrow.}
proc `==`*(a, b: NodePos): bool {.borrow.}
#proc `==`*(a, b: PackedItemId): bool {.borrow.}
proc `==`*(a, b: NodeId): bool {.borrow.}
proc newTreeFrom*(old: PackedTree): PackedTree =
result = PackedTree(nodes: @[])
when false: result.sh = old.sh
proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
tree.nodes.add PackedNode(x: toX(nkIdent, uint32(s)), info: info)
proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
tree.nodes.add PackedNode(x: toX(nkSym, cast[uint32](s)), info: info)
proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
tree.nodes.add PackedNode(x: toX(nkSym, cast[uint32](s)), info: info)
proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
type
PatchPos = distinct int
proc addNode*(t: var PackedTree; kind: TNodeKind; operand: int32;
typeId: PackedItemId = nilItemId; info: PackedLineInfo;
flags: TNodeFlags = {}) =
t.nodes.add PackedNode(x: toX(kind, cast[uint32](operand)), info: info)
if flags != {}:
t.withFlags.add (t.nodes.len.int32 - 1, flags)
if typeId != nilItemId:
t.withTypes.add (t.nodes.len.int32 - 1, typeId)
proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos =
result = PatchPos tree.nodes.len
tree.addNode(kind = kind, flags = flags, operand = 0, info = info, typeId = typeId)
proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
result = PatchPos dest.nodes.len
dest.nodes.add source.nodes[sourcePos.int]
proc patch*(tree: var PackedTree; pos: PatchPos) =
let pos = pos.int
let k = tree.nodes[pos].kind
assert k > nkNilLit
let distance = int32(tree.nodes.len - pos)
assert distance > 0
tree.nodes[pos].x = toX(k, cast[uint32](distance))
proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
proc `[]`*(tree: PackedTree; i: NodePos): lent PackedNode {.inline.} =
tree.nodes[i.int]
template rawSpan(n: PackedNode): int = int(uoperand(n))
proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
if tree.nodes[pos].kind > nkNilLit:
assert tree.nodes[pos].uoperand > 0
inc pos, tree.nodes[pos].rawSpan
else:
inc pos
iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].rawSpan
inc pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos =
let patchPos = prepare(dest, tree, n)
for x in sonsReadonly(tree, n): yield x
patch dest, patchPos
iterator isons*(dest: var PackedTree; tree: PackedTree;
n: NodePos): (int, NodePos) =
var i = 0
for ch0 in sons(dest, tree, n):
yield (i, ch0)
inc i
iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos =
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].rawSpan
inc pos
if pos < last:
nextChild tree, pos
while pos < last:
yield NodePos pos
nextChild tree, pos
iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
var count = 0
for child in sonsReadonly(tree, n):
inc count
var pos = n.int
assert tree.nodes[pos].kind > nkNilLit
let last = pos + tree.nodes[pos].rawSpan
inc pos
while pos < last and count > 2:
yield NodePos pos
dec count
nextChild tree, pos
proc parentImpl(tree: PackedTree; n: NodePos): NodePos =
# finding the parent of a node is rather easy:
var pos = n.int - 1
while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].rawSpan - 1 < n.int)):
dec pos
#assert pos >= 0, "node has no parent"
result = NodePos(pos)
template parent*(n: NodePos): NodePos = parentImpl(tree, n)
proc hasXsons*(tree: PackedTree; n: NodePos; x: int): bool =
var count = 0
if tree.nodes[n.int].kind > nkNilLit:
for child in sonsReadonly(tree, n): inc count
result = count == x
proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool =
if tree.nodes[n.int].kind > nkNilLit:
var count = 0
for child in sonsReadonly(tree, n):
inc count
if count >= x: return true
return false
proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} =
NodePos(n.int+1)
proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} =
tree.nodes[n.int].kind
proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} =
LitId tree.nodes[n.int].uoperand
proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} =
tree.nodes[n.int].info
proc findType*(tree: PackedTree; n: NodePos): PackedItemId =
for x in tree.withTypes:
if x[0] == int32(n): return x[1]
if x[0] > int32(n): return nilItemId
return nilItemId
proc findFlags*(tree: PackedTree; n: NodePos): TNodeFlags =
for x in tree.withFlags:
if x[0] == int32(n): return x[1]
if x[0] > int32(n): return {}
return {}
template typ*(n: NodePos): PackedItemId =
tree.findType(n)
template flags*(n: NodePos): TNodeFlags =
tree.findFlags(n)
template uoperand*(n: NodePos): uint32 =
tree.nodes[n.int].uoperand
proc span*(tree: PackedTree; pos: int): int {.inline.} =
if isAtom(tree, pos): 1 else: tree.nodes[pos].rawSpan
proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
result = (NodePos a, NodePos b)
proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) =
assert(not isAtom(tree, n.int))
let a = n.int+1
let b = a + span(tree, a)
let c = b + span(tree, b)
result = (NodePos a, NodePos b, NodePos c)
proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
result = default(NodePos)
if tree.nodes[n.int].kind > nkNilLit:
var count = 0
for child in sonsReadonly(tree, n):
if count == i: return child
inc count
assert false, "node has no i-th child"
when false:
proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} =
tree.sh.strings[lit]
template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].uoperand
template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].soperand
proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
const
externIntLit* = {nkCharLit,
nkIntLit,
nkInt8Lit,
nkInt16Lit,
nkInt32Lit,
nkInt64Lit,
nkUIntLit,
nkUInt8Lit,
nkUInt16Lit,
nkUInt32Lit,
nkUInt64Lit}
externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit}
externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
directIntLit* = nkNone
template copyInto*(dest, n, body) =
let patchPos = prepare(dest, tree, n)
body
patch dest, patchPos
template copyIntoKind*(dest, kind, info, body) =
let patchPos = prepare(dest, kind, info)
body
patch dest, patchPos
proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len
iterator allNodes*(tree: PackedTree): NodePos =
var p = 0
while p < tree.len:
yield NodePos(p)
let s = span(tree, p)
inc p, s
proc toPackedItemId*(item: int32): PackedItemId {.inline.} =
PackedItemId(module: LitId(0), item: item)
proc load*(f: var RodFile; t: var PackedTree) =
loadSeq f, t.nodes
loadSeq f, t.withFlags
loadSeq f, t.withTypes
proc store*(f: var RodFile; t: PackedTree) =
storeSeq f, t.nodes
storeSeq f, t.withFlags
storeSeq f, t.withTypes

View File

@@ -19,6 +19,8 @@ import std/tables
when defined(nimPreviewSlimSystem):
import std/assertions
import packed_ast, ic, bitabs
proc replayStateChanges*(module: PSym; g: ModuleGraph) =
let list = module.ast
assert list != nil
@@ -86,3 +88,84 @@ proc replayStateChanges*(module: PSym; g: ModuleGraph) =
g.cacheSeqs[destKey].add val
else:
internalAssert g.config, false
proc replayBackendProcs*(g: ModuleGraph; module: int) =
for it in mitems(g.packed[module].fromDisk.attachedOps):
let key = translateId(it[0], g.packed, module, g.config)
let op = it[1]
let tmp = translateId(it[2], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[2])
g.attachedOps[op][key] = LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
let key = translateId(it[0], g.packed, module, g.config)
let tmp = translateId(it[1], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[1])
g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.methodsPerType):
let key = translateId(it[0], g.packed, module, g.config)
let tmp = translateId(it[1], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[1])
g.methodsPerType.mgetOrPut(key, @[]).add LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.dispatchers):
let tmp = translateId(it, g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it)
g.dispatchers.add LazySym(id: symId, sym: nil)
proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
## We remember the generic instantiations a module performed
## in order to to avoid the code bloat that generic code tends
## to imply. This is cheaper than deduplication of identical
## generic instantiations. However, deduplication is more
## powerful and general and I hope to implement it soon too
## (famous last words).
assert g.packed[module].status == loaded
for it in g.packed[module].fromDisk.typeInstCache:
let key = translateId(it[0], g.packed, module, g.config)
g.typeInstCache.mgetOrPut(key, @[]).add LazyType(id: FullId(module: module, packed: it[1]), typ: nil)
for it in mitems(g.packed[module].fromDisk.procInstCache):
let key = translateId(it.key, g.packed, module, g.config)
let sym = translateId(it.sym, g.packed, module, g.config)
var concreteTypes = newSeq[FullId](it.concreteTypes.len)
for i in 0..high(it.concreteTypes):
let tmp = translateId(it.concreteTypes[i], g.packed, module, g.config)
concreteTypes[i] = FullId(module: tmp.module, packed: it.concreteTypes[i])
g.procInstCache.mgetOrPut(key, @[]).add LazyInstantiation(
module: module, sym: FullId(module: sym.module, packed: it.sym),
concreteTypes: concreteTypes, inst: nil)
for it in mitems(g.packed[module].fromDisk.methodsPerGenericType):
let key = translateId(it[0], g.packed, module, g.config)
let col = it[1]
let tmp = translateId(it[2], g.packed, module, g.config)
let symId = FullId(module: tmp.module, packed: it[2])
g.methodsPerGenericType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
replayBackendProcs(g, module)
for it in mitems(g.packed[module].fromDisk.methods):
let sym = loadSymFromId(g.config, g.cache, g.packed, module,
PackedItemId(module: LitId(0), item: it))
methodDef(g, g.idgen, sym)
when false:
# not used anymore:
for it in mitems(g.packed[module].fromDisk.compilerProcs):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it[1]))
g.lazyCompilerprocs[g.packed[module].fromDisk.sh.strings[it[0]]] = symId
for it in mitems(g.packed[module].fromDisk.converters):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].converters.add LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.trmacros):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].patterns.add LazySym(id: symId, sym: nil)
for it in mitems(g.packed[module].fromDisk.pureEnums):
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
g.ifaces[module].pureEnums.add LazySym(id: symId, sym: nil)

283
compiler/ic/rodfiles.nim Normal file
View File

@@ -0,0 +1,283 @@
#
#
# The Nim Compiler
# (c) Copyright 2020 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Low level binary format used by the compiler to store and load various AST
## and related data.
##
## NB: this is incredibly low level and if you're interested in how the
## compiler works and less a storage format, you're probably looking for
## the `ic` or `packed_ast` modules to understand the logical format.
from std/typetraits import supportsCopyMem
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
import std / tables
## Overview
## ========
## `RodFile` represents a Rod File (versioned binary format), and the
## associated data for common interactions such as IO and error tracking
## (`RodFileError`). The file format broken up into sections (`RodSection`)
## and preceded by a header (see: `cookie`). The precise layout, section
## ordering and data following the section are determined by the user. See
## `ic.loadRodFile`.
##
## A basic but "wrong" example of the lifecycle:
## ---------------------------------------------
## 1. `create` or `open` - create a new one or open an existing
## 2. `storeHeader` - header info
## 3. `storePrim` or `storeSeq` - save your stuff
## 4. `close` - and we're done
##
## Now read the bits below to understand what's missing.
##
## ### Issues with the Example
## Missing Sections:
## This is a low level API, so headers and sections need to be stored and
## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` &
## `loadSection`, respectively.
##
## No Error Handling:
## The API is centered around IO and prone to error, each operation checks or
## sets the `RodFile.err` field. A user of this API needs to handle these
## appropriately.
##
## API Notes
## =========
##
## Valid inputs for Rod files
## --------------------------
## ASTs, hopes, dreams, and anything as long as it and any children it may have
## support `copyMem`. This means anything that is not a pointer and that does not contain a pointer. At a glance these are:
## * string
## * objects & tuples (fields are recursed)
## * sequences AKA `seq[T]`
##
## Note on error handling style
## ----------------------------
## A flag based approach is used where operations no-op in case of a
## preexisting error and set the flag if they encounter one.
##
## Misc
## ----
## * 'Prim' is short for 'primitive', as in a non-sequence type
type
RodSection* = enum
versionSection
configSection
stringsSection
checkSumsSection
depsSection
numbersSection
exportsSection
hiddenSection
reexportsSection
compilerProcsSection
trmacrosSection
convertersSection
methodsSection
pureEnumsSection
toReplaySection
topLevelSection
bodiesSection
symsSection
typesSection
typeInstCacheSection
procInstCacheSection
attachedOpsSection
methodsPerGenericTypeSection
enumToStringProcsSection
methodsPerTypeSection
dispatchersSection
typeInfoSection # required by the backend
backendFlagsSection
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
sideChannelSection
namespaceSection
symnamesSection
RodFileError* = enum
ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
includeFileChanged
RodFile* = object
f*: File
currentSection*: RodSection # for error checking
err*: RodFileError # little experiment to see if this works
# better than exceptions.
const
RodVersion = 2
defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'),
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
proc setError(f: var RodFile; err: RodFileError) {.inline.} =
f.err = err
#raise newException(IOError, "IO error")
proc storePrim*(f: var RodFile; s: string) =
## Stores a string.
## The len is prefixed to allow for later retreival.
if f.err != ok: return
if s.len >= high(int32):
setError f, tooBig
return
var lenPrefix = int32(s.len)
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
if s.len != 0:
if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
setError f, ioFailure
proc storePrim*[T](f: var RodFile; x: T) =
## Stores a non-sequence/string `T`.
## If `T` doesn't support `copyMem` and is an object or tuple then the fields
## are written -- the user from context will need to know which `T` to load.
if f.err != ok: return
when supportsCopyMem(T):
if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
setError f, ioFailure
elif T is tuple:
for y in fields(x):
storePrim(f, y)
elif T is object:
for y in fields(x):
when y is seq:
storeSeq(f, y)
else:
storePrim(f, y)
else:
{.error: "unsupported type for 'storePrim'".}
proc storeSeq*[T](f: var RodFile; s: seq[T]) =
## Stores a sequence of `T`s, with the len as a prefix for later retrieval.
if f.err != ok: return
if s.len >= high(int32):
setError f, tooBig
return
var lenPrefix = int32(s.len)
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
for i in 0..<s.len:
storePrim(f, s[i])
proc storeOrderedTable*[K, T](f: var RodFile; s: OrderedTable[K, T]) =
if f.err != ok: return
if s.len >= high(int32):
setError f, tooBig
return
var lenPrefix = int32(s.len)
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
for _, v in s:
storePrim(f, v)
proc loadPrim*(f: var RodFile; s: var string) =
## Read a string, the length was stored as a prefix
if f.err != ok: return
var lenPrefix = int32(0)
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
s = newString(lenPrefix)
if lenPrefix > 0:
if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
setError f, ioFailure
proc loadPrim*[T](f: var RodFile; x: var T) =
## Load a non-sequence/string `T`.
if f.err != ok: return
when supportsCopyMem(T):
if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
setError f, ioFailure
elif T is tuple:
for y in fields(x):
loadPrim(f, y)
elif T is object:
for y in fields(x):
when y is seq:
loadSeq(f, y)
else:
loadPrim(f, y)
else:
{.error: "unsupported type for 'loadPrim'".}
proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
## `T` must be compatible with `copyMem`, see `loadPrim`
if f.err != ok: return
var lenPrefix = int32(0)
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
s = newSeq[T](lenPrefix)
for i in 0..<lenPrefix:
loadPrim(f, s[i])
proc loadOrderedTable*[K, T](f: var RodFile; s: var OrderedTable[K, T]) =
## `T` must be compatible with `copyMem`, see `loadPrim`
if f.err != ok: return
var lenPrefix = int32(0)
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
setError f, ioFailure
else:
s = initOrderedTable[K, T](lenPrefix)
for i in 0..<lenPrefix:
var x = default T
loadPrim(f, x)
s[x.id] = x
proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
## stores the header which is described by `cookie`.
if f.err != ok: return
if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
setError f, ioFailure
proc loadHeader*(f: var RodFile; cookie = defaultCookie) =
## Loads the header which is described by `cookie`.
if f.err != ok: return
var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte])
if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
setError f, ioFailure
elif thisCookie != cookie:
setError f, wrongHeader
proc storeSection*(f: var RodFile; s: RodSection) =
## update `currentSection` and writes the bytes value of s.
if f.err != ok: return
assert f.currentSection < s
f.currentSection = s
storePrim(f, s)
proc loadSection*(f: var RodFile; expected: RodSection) =
## read the bytes value of s, sets and error if the section is incorrect.
if f.err != ok: return
var s: RodSection = default(RodSection)
loadPrim(f, s)
if expected != s and f.err == ok:
setError f, wrongSection
proc create*(filename: string): RodFile =
## create the file and open it for writing
result = default(RodFile)
if not open(result.f, filename, fmWrite):
setError result, cannotOpen
proc close*(f: var RodFile) = close(f.f)
proc open*(filename: string): RodFile =
## open the file for reading
result = default(RodFile)
if not open(result.f, filename, fmRead):
setError result, cannotOpen

View File

@@ -13,7 +13,7 @@ import
ast, msgs, options, idents, lookups,
semdata, modulepaths, sigmatch, lineinfos,
modulegraphs, wordrecg
from std/strutils import `%`, startsWith, replace
from std/strutils import `%`, startsWith
from std/sequtils import addUnique
import std/[sets, tables, intsets]
@@ -108,8 +108,8 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
else:
importPureEnumField(c, e)
else:
if s.kind == skConverter: addConverter(c, s)
if hasPattern(s): addPattern(c, s)
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
if hasPattern(s): addPattern(c, LazySym(sym: s))
if s.owner != origin:
c.exportIndirections.incl((origin.id, s.id))
@@ -190,19 +190,22 @@ proc addImport(c: PContext; im: sink ImportedModule) =
template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
for it in mitems c.graph.ifaces[fromMod.position].converters:
if filter:
if sfExported in it.flags:
loadPackedSym(c.graph, it)
if sfExported in it.sym.flags:
addConverter(c, it)
for it in mitems c.graph.ifaces[fromMod.position].patterns:
if filter:
if sfExported in it.flags:
loadPackedSym(c.graph, it)
if sfExported in it.sym.flags:
addPattern(c, it)
for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
if filter:
importPureEnumFields(c, it, it.typ)
loadPackedSym(c.graph, it)
importPureEnumFields(c, it.sym, it.sym.typ)
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
addUnnamedIt(c, fromMod, it.name.id notin exceptSet)
addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
proc importAllSymbols*(c: PContext, fromMod: PSym) =
c.addImport ImportedModule(m: fromMod, mode: importAll)
@@ -289,8 +292,9 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
c.recursiveDep = err
let trackUnusedImport = warnUnusedImportX in c.config.notes
var realModule: PSym
discard pushOptionEntry(c)
let realModule = c.graph.importModuleCallback(c.graph, c.module, f)
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
result = importModuleAs(c, n, realModule, transf.importHidden, trackUnusedImport)
popOptionEntry(c)
@@ -304,9 +308,9 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
var prefix = ""
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
let moduleNameNorm = getModuleName(c.config, n).replace("\\", "/")
if belongsToStdlib(c.graph, result) and not startsWith(moduleNameNorm, stdPrefix) and
not startsWith(moduleNameNorm, "system/") and not startsWith(moduleNameNorm, "packages/"):
let moduleName = getModuleName(c.config, n)
if belongsToStdlib(c.graph, result) and not startsWith(moduleName, stdPrefix) and
not startsWith(moduleName, "system/") and not startsWith(moduleName, "packages/"):
message(c.config, n.info, warnStdPrefix, realModule.name.s)
proc suggestMod(n: PNode; s: PSym) =

View File

@@ -69,14 +69,12 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} =
result = ast.hasDestructor(t)
when toDebug.len > 0:
# for more effective debugging
if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
assert(not containsGarbageCollectedRef(t))
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo; needsInit: bool): PNode =
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info)
sym.typ = typ
if not needsInit:
sym.incl sfNoInit
s.vars.add(sym)
result = newSymNode(sym)
@@ -167,11 +165,11 @@ proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
template hasDestructorOrAsgn(c: var Con, typ: PType): bool =
# bug #23354; an object type could have a non-trivial assignements when it is passed to a sink parameter
hasDestructor(c, typ) or (c.graph.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc} and
hasDestructor(c, typ) or (c.graph.config.selectedGC == gcRefc and typ.kind == tyString) or (c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
typ.kind == tyObject and not isTrivial(getAttachedOp(c.graph, typ, attachedAsgn)))
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
if not hasDestructorOrAsgn(c, n.typ): return true
if not hasDestructorOrAsgn(c, n.typ.skipTypes({tyGenericInst, tyAlias, tySink})): return true
let m = skipConvDfa(n)
result = isLastReadImpl(n, c, s)
@@ -288,7 +286,9 @@ proc deepAliases(dest, ri: PNode): bool =
return aliases(dest, ri) != no
proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
if (c.inLoopCond == 0 and (isFullyUnpackedTuple(dest) or IsDecl in flags or
if c.graph.config.selectedGC == gcRefc and dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyString:
result = newFastAsgnStmt(dest, callCodegenProc(c.graph, "moveString", dest.info, ri))
elif (c.inLoopCond == 0 and (isFullyUnpackedTuple(dest) or IsDecl in flags or
(isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
isNoInit(dest) or IsReturn in flags:
# optimize sink call into a bitwise memcopy
@@ -304,7 +304,7 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFla
if deepAliases(dest, ri):
# consider: x = x + y, it is wrong to destroy the destination first!
# tmp to support self assignments
let tmp = c.getTemp(s, dest.typ, dest.info, needsInit = false)
let tmp = c.getTemp(s, dest.typ, dest.info)
result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
c.genDestroy(tmp))
else:
@@ -331,14 +331,14 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
result = dest.kind != nkSym
proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
if c.graph.config.selectedGC in {gcOrc, gcYrc} and IsExplicitSink notin flags:
if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
# add cyclic flag, but not to sink calls, which IsExplicitSink generates
let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
if cyclicType(c.graph, t):
result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
proc genMarkCyclic(c: var Con; result, dest: PNode) =
if c.graph.config.selectedGC in {gcOrc, gcYrc}:
if c.graph.config.selectedGC == gcOrc:
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
if cyclicType(c.graph, t):
if t.kind == tyRef:
@@ -354,6 +354,9 @@ proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
assert ri.typ != nil
proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode =
if c.graph.config.selectedGC == gcRefc and dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyString:
result = newAsgnStmt(dest, ri)
return
if c.inEnsureMove > 0:
localError(c.graph.config, ri.info, errFailedMove, "cannot move '" & $ri &
"', which introduces an implicit copy")
@@ -373,7 +376,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
# but fields within active case branch might need destruction
# tmp to support self assignments
let tmp = c.getTemp(s, n[1].typ, n.info, needsInit = false)
let tmp = c.getTemp(s, n[1].typ, n.info)
result = newTree(nkStmtList)
result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed))
@@ -459,50 +462,49 @@ proc isCapturedVar(n: PNode): bool =
else: result = false
proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let nTyp = n.typ.skipTypes(tyUserTypeClasses)
if not hasDestructorOrAsgn(c, nTyp):
# Non-managed (plain-old-data) type: no ownership transfer is needed.
# Return the expression directly — no temp required.
if c.graph.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
let tmp = c.getTemp(s, nTyp, n.info)
if hasDestructorOrAsgn(c, nTyp):
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
if sfError in op.flags:
c.checkForErrorPragma(nTyp, n, "=dup")
else:
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
if copyOp != nil and sfError in copyOp.flags and
sfOverridden notin op.flags:
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
let src = p(n, c, s, normal)
var newCall = newTreeIT(nkCall, src.info, src.typ,
newSymNode(op),
src)
c.finishCopy(newCall, n, {}, isFromSink = true)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newCall
)
else:
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, {}, isFromSink = true)
result.add m
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
message(c.graph.config, n.info, hintPerformance,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
if c.inEnsureMove > 0:
localError(c.graph.config, n.info, errFailedMove,
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
else:
if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
assert(not containsManagedMemory(nTyp))
if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
return p(n, c, s, normal)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let tmp = c.getTemp(s, nTyp, n.info, needsInit = false)
let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil and tfHasOwned notin typ.flags:
if sfError in op.flags:
c.checkForErrorPragma(nTyp, n, "=dup")
else:
let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
if copyOp != nil and sfError in copyOp.flags and
sfOverridden notin op.flags:
c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
let src = p(n, c, s, normal)
var newCall = newTreeIT(nkCall, src.info, src.typ,
newSymNode(op),
src)
c.finishCopy(newCall, n, {}, isFromSink = true)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newCall
)
else:
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, {}, isFromSink = true)
result.add m
if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
message(c.graph.config, n.info, hintPerformance,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
if c.inEnsureMove > 0:
localError(c.graph.config, n.info, errFailedMove,
("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
# Since we know somebody will take over the produced copy, there is
# no need to destroy it.
result.add tmp
@@ -533,7 +535,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
# produce temp creation for (fn, env). But we need to move 'env'?
# This was already done in the sink parameter handling logic.
result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
let tmp = c.getTemp(s, arg.typ, arg.info, true)
let tmp = c.getTemp(s, arg.typ, arg.info)
result.add c.genSink(s, tmp, arg, {IsDecl})
result.add tmp
s.final.add c.genDestroy(tmp)
@@ -612,7 +614,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
# There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
# later and use it to eliminate the temporary when theres no need for it, but its
# tricky because you would have to intercept moveOrCopy at a certain point
let tmp = c.getTemp(s.parent[], ret.typ, ret.info, needsInit = true)
let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
tmp.sym.flags = tmpFlags
let cpy = if hasDestructor(c, ret.typ) and
ret.typ.kind notin {tyOpenArray, tyVarargs}:
@@ -773,7 +775,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
result = copyNode(n)
result.add call
else:
let tmp = c.getTemp(s, n[0].typ, n.info, needsInit = true)
let tmp = c.getTemp(s, n[0].typ, n.info)
var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
m.add p(n[0], c, s, normal)
c.finishCopy(m, n[0], {}, isFromSink = false)
@@ -929,7 +931,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
result[0] = copyTree(n[0])
if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc, gcYrc}:
if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc}:
let destroyOld = c.genDestroy(result[1])
result = newTree(nkStmtList, destroyOld, result)
else:
@@ -1004,13 +1006,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
elif isDiscriminantField(n[0]):
result = c.genDiscriminantAsgn(s, n)
elif n[1].kind in {nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt, nkPragmaBlock}:
# Distribute the assignment into each branch to avoid
# creating pointless temporaries for expression-based control flow.
let dest = p(n[0], c, s, mode)
template process(child, s): untyped =
newTree(n.kind, dest, p(child, c, s, consumed))
handleNestedTempl(n[1], process, willProduceStmt = true)
else:
result = copyNode(n)
result.add p(n[0], c, s, mode)
@@ -1164,7 +1159,7 @@ proc ownsData(c: var Con; s: var Scope; orig: PNode; flags: set[MoveOrCopyFlag])
break
if n.kind in nkCallKinds and n.typ != nil and hasDestructor(c, n.typ):
result = newNodeIT(nkStmtListExpr, orig.info, orig.typ)
let tmp = c.getTemp(s, n.typ, n.info, needsInit = true)
let tmp = c.getTemp(s, n.typ, n.info)
tmp.sym.flagsImpl.incl sfSingleUsedTemp
result.add newTree(nkFastAsgn, tmp, copyTree(n))
s.final.add c.genDestroy(tmp)

View File

@@ -460,9 +460,7 @@ proc addInt128*(result: var string; value: Int128) =
var i = initialSize
var j = high(result)
while i < j:
let tmp = result[i]
result[i] = result[j]
result[j] = tmp
swap(result[i], result[j])
i += 1
j -= 1

View File

@@ -1544,7 +1544,7 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
r.res = s.loc.snippet
r.address = ""
r.typ = etyNone
of skVar, skLet, skResult, skTemp, skForVar:
of skVar, skLet, skResult:
r.kind = resExpr
let jsType = mapType(p):
if typ.isNil:
@@ -2018,12 +2018,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
if indirect: result = "[$1]" % [result]
of tyTuple:
result = rope("{")
var first = true
for i in 0..<t.len:
# Do not produce code for void types
if isEmptyType(t[i]): continue
if not first: result.add(", ")
first = false
if i > 0: result.add(", ")
result.addf("Field$1: $2", [i.rope,
createVar(p, t[i], false)])
result.add("}")

View File

@@ -216,10 +216,6 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
result[0] = le
result[1] = ri
proc markInjectDestructors(s: PSym) {.inline.} =
backendEnsureMutable s
s.flagsImpl.incl sfInjectDestructors
proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; info: TLineInfo): PNode =
result = newNodeIT(nkClosure, info, prc.typ)
result.add(newSymNode(prc))
@@ -232,7 +228,7 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf
#if isClosureIterator(result.typ):
createTypeBoundOps(g, nil, result.typ, info, idgen)
if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions:
markInjectDestructors(prc)
prc.incl sfInjectDestructors
template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
## lambda lifting can be harmful for JS-like code generators.
@@ -244,7 +240,7 @@ proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen
createTypeBoundOps(g, nil, refType.elementType, info, idgen)
createTypeBoundOps(g, nil, refType, info, idgen)
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
markInjectDestructors(owner)
owner.incl sfInjectDestructors
proc genCreateEnv(env: PNode): PNode =
var c = newNodeIT(nkObjConstr, env.info, env.typ)
@@ -412,12 +408,6 @@ Consider:
proc isTypeOf(n: PNode): bool =
n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
proc isEnvTypeForRoutine(envTyp: PType; routine: PSym): bool =
## True if `envTyp` is (maybe wrapped) env object type owned by `routine`, as
## created by `getEnvTypeForOwner` / `createEnvObj`.
let obj = envTyp.skipTypes({tyOwned, tyRef, tyPtr})
result = obj.kind == tyObject and obj.owner.id == routine.id
proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
var cp = getEnvParam(fn)
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
@@ -428,13 +418,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
cp.typ = t
addHiddenParam(fn, cp)
elif cp.typ != t and fn.kind != skIterator:
# Nested `liftLambdas` uses a fresh `DetectionPass`, so `getEnvTypeForOwner`
# can allocate another PType for the same logical env; the hidden param from
# the inner pass is authoritative (bug #21242).
if isEnvTypeForRoutine(cp.typ, owner) and isEnvTypeForRoutine(t, owner):
c.ownerToType[owner.id] = cp.typ
else:
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
#echo "adding closure to ", fn.name.s
proc iterEnvHasUpField(g: ModuleGraph, iter: PSym): bool =
@@ -640,7 +624,7 @@ proc rawClosureCreation(owner: PSym;
if owner.kind != skMacro:
createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen)
if tfHasAsgn in fieldAccess.typ.flags or optSeqDestructors in d.graph.config.globalOptions:
markInjectDestructors(owner)
owner.incl sfInjectDestructors
let upField = lookupInRecord(env.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
if upField != nil:

View File

@@ -46,11 +46,12 @@ proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
when useRef:
pt = pt.nextLayer
else:
# Must read nextLayer into a temp before destroying pt:
# `pt = pt.nextLayer[]` would call eqcopy(&pt, &(*pt.nextLayer)) which
# decrements pt.nextLayer's rc (freeing it) before reading pt.nextLayer.nextLayer.
let tmp = pt.nextLayer[]
pt = tmp
when defined(gcDestructors):
pt = pt.nextLayer[]
else:
# workaround refc
let tmp = pt.nextLayer[]
pt = tmp
iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
var tm = pt

View File

@@ -316,28 +316,6 @@ proc getNumber(L: var Lexer, result: var Token) =
L.bufpos = msgPos
lexMessage(L, msgKind, msg % t.literal)
proc checkBitWidth(L: var Lexer, base: NumericalBase, tokType: TokType,
numDigits: int, startpos: int) =
# Check bit width for non-base-10 literals
# Warn if the digit count exceeds what can fit in the target type
let bitsPerDigit = case base
of base2: 1
of base8: 3
of base16: 4
else: raiseAssert "unreachable"
let bitWidth = case tokType
of tkInt8Lit, tkUInt8Lit: 8
of tkInt16Lit, tkUInt16Lit: 16
of tkInt32Lit, tkUInt32Lit: 32
of tkInt64Lit, tkUIntLit, tkIntLit, tkUInt64Lit: 64
else: raiseAssert "unreachable"
# Maximum digits = ceil(bitWidth / bitsPerDigit) = (bitWidth + bitsPerDigit - 1) div bitsPerDigit
let maxDigits = (bitWidth + bitsPerDigit - 1) div bitsPerDigit
if numDigits > maxDigits:
lexMessageLitNum(L,
"number has " & $numDigits & " digits but type only supports " &
$maxDigits & " digits: '$1'", startpos, warnLongLiterals)
var
xi: BiggestInt
isBase10 = true
@@ -513,11 +491,6 @@ proc getNumber(L: var Lexer, result: var Token) =
setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
else: internalError(L.config, getLineInfo(L), "getNumber")
# Check bit width for non-base-10 literals
# Warn if the digit count exceeds what can fit in the target type
if result.base != base10 and result.tokType in {tkIntLit..tkUInt64Lit} and numDigits > 0:
checkBitWidth(L, result.base, result.tokType, numDigits, startpos)
# Bounds checks. Non decimal literals are allowed to overflow the range of
# the datatype as long as their pattern don't overflow _bitwise_, hence
# below checks of signed sizes against uint*.high is deliberate:
@@ -923,7 +896,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
tok.tokType = tkSymbol
else:
tok.tokType = TokType(tok.ident.id + ord(tkSymbol))
if suspicious and {optStyleHint, optStyleError, optStyleWarning} * L.config.globalOptions != {}:
if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}:
lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s)
L.bufpos = pos
@@ -1349,7 +1322,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
of '-':
if L.buf[L.bufpos+1] in {'0'..'9'} and
(L.bufpos == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
(L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist):
# x)-23 # binary minus
# ,-23 # unary minus
# \n-78 # unary minus? Yes.

View File

@@ -163,7 +163,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
if c.filterDiscriminator != nil: return
let f = n.sym
let b = if c.kind == attachedTrace: y else: y.dotField(f)
if (sfCursor in f.flags and c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc, gcHooks}) or
if (sfCursor in f.flags and c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or
enforceDefaultOp:
defaultOp(c, f.typ, body, x.dotField(f), b)
else:
@@ -558,22 +558,6 @@ proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
v.addVar(result, value)
body.add v
proc considerInferDupFromCopy(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
## For `=dup`, if no explicit hook exists, try to infer from `=copy` hook
## to maintain backward compatibility. Returns true if inference was applied.
if c.kind == attachedDup:
var op2 = getAttachedOp(c.g, t, attachedAsgn)
if op2 != nil and sfOverridden in op2.flags:
#markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op2)
body.add genBuiltin(c, mWasMoved, "wasMoved", x)
body.add newHookCall(c, op2, x, y)
result = true
else:
result = false
else:
result = false
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
let incCall = genBuiltin(c, mInc, "inc", i)
incCall.add lowerings.newIntLit(c.g, c.info, 1)
@@ -592,12 +576,10 @@ proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
result.add lenCall
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode; noinit = false): PNode =
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
lenCall.typ = getSysType(c.g, x.info, tyInt)
let name = if noinit: "setLenUninit" else: "setLen"
let magic = if noinit: mSetLengthSeqUninit else: mSetLengthSeq
var op = getSysMagic(c.g, x.info, name, magic)
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
op = instantiateGeneric(c, op, t, t)
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
@@ -622,35 +604,11 @@ proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
cond.typ = getSysType(c.g, c.info, tyBool)
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
proc genBulkCopySeq(c: var TLiftCtx; t: PType; body, x, y: PNode) =
## Generates a call to nimCopySeqPayload for bulk memcpy of seq data.
let elemType = t.elementType
let sym = magicsys.getCompilerProc(c.g, "nimCopySeqPayload")
if sym == nil:
localError(c.g.config, c.info, "system module needs: nimCopySeqPayload")
return
var sizeOf = genBuiltin(c, mSizeOf, "sizeof", newNodeIT(nkType, c.info, elemType))
sizeOf.typ = getSysType(c.g, c.info, tyInt)
var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType))
alignOf.typ = getSysType(c.g, c.info, tyInt)
let call = newNodeI(nkCall, c.info)
call.add newSymNode(sym)
call.add newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x)
call.add newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
call.add sizeOf
call.add alignOf
call.typ = sym.typ.returnType
body.add call
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
of attachedDup:
let bulkCopy = supportsCopyMem(t.elementType)
body.add setLenSeqCall(c, t, x, y, noinit = bulkCopy)
if bulkCopy:
genBulkCopySeq(c, t, body, x, y)
else:
forallElements(c, t, body, x, y)
body.add setLenSeqCall(c, t, x, y)
forallElements(c, t, body, x, y)
of attachedAsgn, attachedDeepCopy:
# we generate:
# if x.p == y.p:
@@ -659,14 +617,9 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# var i = 0
# while i < y.len: dest[i] = y[i]; inc(i)
# This is usually more efficient than a destroy/create pair.
# For trivially copyable types, use bulk copyMem instead of element loop.
checkSelfAssignment(c, t, body, x, y)
let bulkCopy = supportsCopyMem(t.elementType)
body.add setLenSeqCall(c, t, x, y, noinit = bulkCopy)
if bulkCopy:
genBulkCopySeq(c, t, body, x, y)
else:
forallElements(c, t, body, x, y)
body.add setLenSeqCall(c, t, x, y)
forallElements(c, t, body, x, y)
of attachedSink:
let moveCall = genBuiltin(c, mMove, "move", x)
moveCall.add y
@@ -732,18 +685,11 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedAsgn, attachedDeepCopy, attachedDup:
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
of attachedSink:
if c.g.config.usesSso():
# SmallString: destroy old dst, then bit-copy src (no rc increment — this is a move).
# No .p aliasing check needed; rc-based destroy handles COW sharing correctly.
doAssert t.destructor != nil
body.add destructorCall(c, t.destructor, x)
body.add newAsgnStmt(x, y)
else:
let moveCall = genBuiltin(c, mMove, "move", x)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall
let moveCall = genBuiltin(c, mMove, "move", x)
moveCall.add y
doAssert t.destructor != nil
moveCall.add destructorCall(c, t.destructor, x)
body.add moveCall
of attachedDestructor:
body.add genBuiltin(c, mDestroy, "destroy", x)
of attachedTrace:
@@ -775,43 +721,14 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
dest[] = source
decRef tmp
For YRC the write barrier is more complicated still and must be:
let tmp = dest
# assignment must come first so that the collector sees the most-recent graph:
atomic: dest[] = source
# Then teach the cycle collector about the changes edge (these use locks, see yrc.nim):
incRef source
decRef tmp
This is implemented as a single runtime call (nimAsgnYrc / nimSinkYrc).
]#
var actions = newNodeI(nkStmtList, c.info)
let elemType = t.elementType
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType)
# YRC uses dedicated runtime procs for the entire write barrier:
if c.g.config.selectedGC == gcYrc:
let desc =
if isFinal(elemType):
let ti = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType))
ti.typ = getSysType(c.g, c.info, tyPointer)
ti
else:
newNodeIT(nkNilLit, c.info, getSysType(c.g, c.info, tyPointer))
case c.kind
of attachedAsgn, attachedDup:
body.add callCodegenProc(c.g, "nimAsgnYrc", c.info, genAddr(c, x), y, desc)
return
of attachedSink:
body.add callCodegenProc(c.g, "nimSinkYrc", c.info, genAddr(c, x), y, desc)
return
else: discard # fall through for destructor, trace, wasMoved
let isCyclic = c.g.config.selectedGC in {gcOrc, gcYrc} and types.canFormAcycle(c.g, elemType)
let isInheritableAcyclicRef = c.g.config.selectedGC in {gcOrc, gcYrc} and
let isInheritableAcyclicRef = c.g.config.selectedGC == gcOrc and
(not isPureObject(elemType)) and
tfAcyclic in skipTypes(elemType, abstractInst+{tyOwned}-{tyTypeDesc}).flags
# dynamic Acyclic refs need to use dyn decRef
@@ -893,26 +810,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
xenv.typ = getSysType(c.g, c.info, tyPointer)
# Closures are (fnPtr, env) pairs. nimAsgnYrc/nimSinkYrc handle the env pointer
# (atomic store + buffered inc/dec). We also need newAsgnStmt to copy the fnPtr.
if c.g.config.selectedGC == gcYrc:
let nilDesc = newNodeIT(nkNilLit, c.info, getSysType(c.g, c.info, tyPointer))
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
yenv.typ = getSysType(c.g, c.info, tyPointer)
case c.kind
of attachedAsgn, attachedDup:
# nimAsgnYrc: save old env, atomic store new env, inc new env, dec old env
body.add callCodegenProc(c.g, "nimAsgnYrc", c.info, genAddr(c, xenv), yenv, nilDesc)
# Raw struct copy to also update the function pointer (env write is redundant but benign)
body.add newAsgnStmt(x, y)
return
of attachedSink:
body.add callCodegenProc(c.g, "nimSinkYrc", c.info, genAddr(c, xenv), yenv, nilDesc)
body.add newAsgnStmt(x, y)
return
else: discard # fall through for destructor, trace, wasMoved
let isCyclic = c.g.config.selectedGC in {gcOrc, gcYrc}
let isCyclic = c.g.config.selectedGC == gcOrc
let tmp =
if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
declareTempOf(c, body, xenv)
@@ -945,6 +843,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add genIf(c, cond, actions)
else:
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
body.add genIf(c, cond, actions)
body.add newAsgnStmt(x, y)
of attachedDup:
@@ -1029,7 +928,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
call[1] = y
body.add newAsgnStmt(x, call)
elif (optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}:
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
xx.typ = getSysType(c.g, c.info, tyPointer)
case c.kind
@@ -1084,7 +983,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyPtr, tyUncheckedArray, tyVar, tyLent:
defaultOp(c, t, body, x, y)
of tyRef:
if c.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
atomicRefOp(c, t, body, x, y)
elif (optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options):
@@ -1093,7 +992,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
defaultOp(c, t, body, x, y)
of tyProc:
if t.callConv == ccClosure:
if c.g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
atomicClosureOp(c, t, body, x, y)
else:
closureOp(c, t, body, x, y)
@@ -1137,6 +1036,8 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of tyString:
if useNoGc(c, t):
useSeqOrStrOp(c, t, body, x, y)
elif c.g.config.selectedGC == gcRefc:
defaultOp(c, t, body, x, y)
elif tfHasAsgn in t.flags:
discard considerUserDefinedOp(c, t, body, x, y)
else:
@@ -1154,12 +1055,19 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
elif tfUnion in t.flags: # bug #25236
defaultOp(c, t, body, x, y)
else:
if not considerInferDupFromCopy(c, t, body, x, y):
if c.kind == attachedDup:
var op2 = getAttachedOp(c.g, t, attachedAsgn)
if op2 != nil and sfOverridden in op2.flags:
#markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op2)
body.add newHookCall(c, t.assignment, x, y)
else:
fillBodyObjT(c, t, body, x, y)
else:
fillBodyObjT(c, t, body, x, y)
of tyDistinct:
if not considerUserDefinedOp(c, t, body, x, y):
if not considerInferDupFromCopy(c, t, body, x, y):
fillBody(c, t.elementType, body, x, y)
fillBody(c, t.elementType, body, x, y)
of tyTuple:
fillBodyTup(c, t, body, x, y)
of tyVarargs, tyOpenArray:
@@ -1206,7 +1114,7 @@ proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttache
result.typ.addParam src
if g.config.selectedGC in {gcOrc, gcYrc} and
if g.config.selectedGC == gcOrc and
cyclicType(g, typ.skipTypes(abstractInst)):
let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
idgen, result, info)
@@ -1233,7 +1141,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
idgen, result, info)
if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc} and
if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
dest.typ = typ
else:
@@ -1249,7 +1157,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
if kind notin {attachedDestructor, attachedWasMoved}:
result.typ.addParam src
if kind == attachedAsgn and g.config.selectedGC in {gcOrc, gcYrc} and
if kind == attachedAsgn and g.config.selectedGC == gcOrc and
cyclicType(g, typ.skipTypes(abstractInst)):
let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
idgen, result, info)
@@ -1278,17 +1186,7 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator): PSym =
if typ.kind == tyDistinct:
# For =dup, if the distinct type has a user-defined =copy, don't delegate
# to the base type. Instead fall through to the normal produceSym logic
# so that fillBody -> considerInferDupFromCopy can synthesize =dup from =copy.
if kind == attachedDup:
let copyOp = getAttachedOp(g, typ, attachedAsgn)
if copyOp != nil and sfOverridden in copyOp.flags:
discard "fall through to normal produceSym logic"
else:
return produceSymDistinctType(g, c, typ, kind, info, idgen)
else:
return produceSymDistinctType(g, c, typ, kind, info, idgen)
return produceSymDistinctType(g, c, typ, kind, info, idgen)
result = getAttachedOp(g, typ, kind)
if result == nil:
@@ -1317,22 +1215,14 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
else:
var tk: TTypeKind
var skipped: PType = nil
if g.config.selectedGC in {gcArc, gcOrc, gcYrc, gcHooks, gcAtomicArc}:
if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
skipped = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink})
tk = skipped.kind
else:
tk = tyNone # no special casing for strings and seqs
case tk
of tySequence:
let needsYrcLock = g.config.selectedGC == gcYrc and
kind in {attachedDestructor, attachedSink, attachedAsgn, attachedDeepCopy, attachedDup} and
types.canFormAcycle(g, skipped.elementType)
# YRC: topology-changing seq ops must hold the mutator (read) lock
if needsYrcLock:
result.ast[bodyPos].add callCodegenProc(g, "acquireMutatorLock", info)
fillSeqOp(a, typ, result.ast[bodyPos], d, src)
if needsYrcLock:
result.ast[bodyPos].add callCodegenProc(g, "releaseMutatorLock", info)
of tyString:
fillStrOp(a, typ, result.ast[bodyPos], d, src)
else:
@@ -1447,7 +1337,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
# we do not generate '=trace' procs if we
# have the cycle detection disabled, saves code size.
let lastAttached = if g.config.selectedGC in {gcOrc, gcYrc}: attachedTrace
let lastAttached = if g.config.selectedGC == gcOrc: attachedTrace
else: attachedSink
# bug #15122: We need to produce all prototypes before entering the

View File

@@ -93,13 +93,10 @@ type
warnBareExcept = "BareExcept",
warnImplicitDefaultValue = "ImplicitDefaultValue",
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
warnStdPrefix = "StdPrefix",
warnUnknownNotes = "UnknownNotes",
warnLongLiterals = "LongLiterals",
warnStdPrefix = "StdPrefix"
warnUnknownNotes = "UnknownNotes"
warnUser = "User",
warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
warnImplicitRangeConversion = "ImplicitRangeConversion",
warnSystemRangeConversion = "SystemRangeConversion",
# hints
hintSuccess = "Success", hintSuccessX = "SuccessX",
hintCC = "CC",
@@ -205,11 +202,8 @@ const
warnIgnoredSymbolInjection: "$1",
warnStdPrefix: "$1 needs the 'std' prefix",
warnUnknownNotes: "$1",
warnLongLiterals: "$1",
warnUser: "$1",
warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
warnImplicitRangeConversion: "implicit range conversion $1",
warnSystemRangeConversion: "implicit range conversion $1",
hintSuccess: "operation successful: $#",
# keep in sync with `testament.isSuccess`
hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
@@ -264,9 +258,9 @@ type
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
result = default(array[0..3, TNoteKinds])
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix, warnSystemRangeConversion}
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix}
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
result[1] = result[2] - {warnImplicitRangeConversion, warnProveField, warnProveIndex,
result[1] = result[2] - {warnProveField, warnProveIndex,
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf,

View File

@@ -95,7 +95,7 @@ proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) =
## Check symbol definitions adhere to NEP1 style rules.
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
{optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # check only if hint/error/warning is enabled
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
@@ -136,7 +136,7 @@ proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
## Check symbol uses match their definition's style.
if {optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
sym.kind != skTemp and # ignore temporary variables created by the compiler
@@ -152,7 +152,7 @@ proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragm
template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
## Check builtin pragma uses match their definition's style.
## Note: This only applies to builtin pragmas, not user pragmas.
if {optStyleHint, optStyleError, optStyleWarning} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
checkPragmaUseImpl(ctx.config, info, w, pragmaName)

View File

@@ -163,7 +163,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
inc(s.lineOffset)
result = min(bufLen, s.s.len - s.rd)
if result > 0:
copyMem(buf, readRawData(s.s, s.rd), result)
copyMem(buf, addr(s.s[s.rd]), result)
inc(s.rd, result)
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
@@ -173,7 +173,7 @@ proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
of llsString:
result = min(bufLen, s.s.len - s.rd)
if result > 0:
copyMem(buf, readRawData(s.s, s.rd), result)
copyMem(buf, addr(s.s[0 + s.rd]), result)
inc(s.rd, result)
of llsFile:
result = readBuffer(s.f, buf, bufLen)

View File

@@ -412,6 +412,8 @@ proc addDecl*(c: PContext, sym: PSym) {.inline.} =
proc addPrelimDecl*(c: PContext, sym: PSym) =
discard c.currentScope.addUniqueSym(sym)
from ic / ic import addHidden
proc addInterfaceDeclAux(c: PContext, sym: PSym) =
## adds symbol to the module for either private or public access.
if sfExported in sym.flags:
@@ -420,6 +422,8 @@ proc addInterfaceDeclAux(c: PContext, sym: PSym) =
else: internalError(c.config, sym.info, "addInterfaceDeclAux")
elif sym.kind in ExportableSymKinds and c.module != nil and isTopLevelInsideDeclaration(c, sym):
strTableAdd(semtabAll(c.graph, c.module), sym)
if c.config.symbolFiles != disabledSf:
addHidden(c.encoder, c.packedRepr, sym)
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
## adds a symbol on the scope and the interface if appropriate

View File

@@ -11,7 +11,7 @@
import
ast, msgs, platform, idents,
modulegraphs, lineinfos, types
modulegraphs, lineinfos
export createMagic
@@ -134,7 +134,7 @@ proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
proc resetNimScriptSymbols*(g: ModuleGraph) = g.exposed = initStrTable()
proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
case t.skipTypes(abstractRange).kind
case t.kind
of tyInt, tyInt8, tyInt16, tyInt32, tyInt64,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64:
result = getSysMagic(g, info, "==", mEqI)

View File

@@ -26,6 +26,8 @@ import
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
import ic / [cbackend, integrity, navigator, ic]
import ../dist/checksums/src/checksums/sha1
import pipelines
@@ -97,6 +99,14 @@ proc commandCheck(graph: ModuleGraph) =
setPipeLinePass(graph, SemPass)
compilePipelineProject(graph)
if conf.symbolFiles != disabledSf:
case conf.ideCmd
of ideDef: navDefinition(graph)
of ideUse: navUsages(graph)
of ideDus: navDefusages(graph)
else: discard
writeRodFiles(graph)
when not defined(leanCompiler):
proc commandDoc2(graph: ModuleGraph; ext: string) =
handleDocOutputOptions graph.config
@@ -163,7 +173,15 @@ proc commandCompileToC(graph: ModuleGraph) =
compilePipelineProject(graph)
if graph.config.errorCounter > 0:
return # issue #9933
cgenWriteModules(graph.backend, conf)
if conf.symbolFiles == disabledSf:
cgenWriteModules(graph.backend, conf)
else:
if isDefined(conf, "nimIcIntegrityChecks"):
checkIntegrity(graph)
generateCode(graph)
# graph.backend can be nil under IC when nothing changed at all:
if graph.backend != nil:
cgenWriteModules(graph.backend, conf)
if conf.cmd != cmdTcc and graph.backend != nil:
extccomp.callCCompiler(conf)
# for now we do not support writing out a .json file with the build instructions when HCR is on
@@ -223,6 +241,10 @@ proc commandScan(cache: IdentCache, config: ConfigRef) =
else:
rawMessage(config, errGenerated, "cannot open file: " & f.string)
proc commandView(graph: ModuleGraph) =
let f = toAbsolute(mainCommandArg(graph.config), AbsoluteDir getCurrentDir()).addFileExt(RodExt)
rodViewer(f, graph.config, graph.cache)
const
PrintRopeCacheStats = false
@@ -320,6 +342,8 @@ proc mainCommand*(graph: ModuleGraph) =
case conf.cmd
of cmdBackends:
compileToBackend()
when BenchIC:
echoTimes graph.packed
of cmdTcc:
when hasTinyCBackend:
extccomp.setCC(conf, "tcc", unknownLineInfo)
@@ -418,25 +442,27 @@ proc mainCommand*(graph: ModuleGraph) =
of cmdM:
# cmdM uses NIF files, not ROD files
graph.config.symbolFiles = disabledSf
setUseIc(true)
setUseIc(false)
commandCheck(graph)
of cmdNifC:
setUseIc(true)
# Generate C code from NIF files
wantMainModule(conf)
setOutFile(conf)
commandNifC(graph)
of cmdIc:
of cmdDeps:
# Generate .build.nif for nifmake
setUseIc(true)
wantMainModule(conf)
when not defined(nimKochBootstrap):
commandIc(conf)
commandDeps(conf)
else:
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")
of cmdParse:
wantMainModule(conf)
discard parseFile(conf.projectMainIdx, cache, conf)
of cmdRod:
wantMainModule(conf)
commandView(graph)
#msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
of cmdInteractive: commandInteractive(graph)
of cmdNimscript:
if conf.projectIsCmd or conf.projectIsStdin: discard

View File

@@ -14,6 +14,7 @@
import std/[intsets, tables, hashes, strtabs, os, strutils, parseutils]
import ../dist/checksums/src/checksums/md5
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
import ic / [packed_ast, ic]
when not defined(nimKochBootstrap):
import ast2nif
@@ -27,12 +28,16 @@ when defined(nimPreviewSlimSystem):
type
SigHash* = distinct MD5Digest
LazySym* = object
id*: FullId
sym*: PSym
Iface* = object ## data we don't want to store directly in the
## ast.PSym type for s.kind == skModule
module*: PSym ## module this "Iface" belongs to
converters*: seq[PSym]
patterns*: seq[PSym]
pureEnums*: seq[PSym]
converters*: seq[LazySym]
patterns*: seq[LazySym]
pureEnums*: seq[LazySym]
interf: TStrTable
interfHidden: TStrTable
uniqueName*: Rope
@@ -41,6 +46,20 @@ type
opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
FullId* = object
module*: int
packed*: PackedItemId
LazyType* = object
id*: FullId
typ*: PType
LazyInstantiation* = object
module*: int
sym*: FullId
concreteTypes*: seq[FullId]
inst*: PInstantiation
PipelinePass* = enum
NonePass
SemPass
@@ -56,19 +75,21 @@ type
ModuleGraph* {.acyclic.} = ref object
ifaces*: seq[Iface] ## indexed by int32 fileIdx
packed*: PackedModuleGraph
encoders*: seq[PackedEncoder]
typeInstCache*: Table[ItemId, seq[PType]] # A symbol's ItemId.
procInstCache*: Table[ItemId, seq[PInstantiation]] # A symbol's ItemId.
attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc.
typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
loadedOps: array[TTypeAttachedOp, Table[string, PSym]] # This can later by unified with `attachedOps` once it's stable
opsLog*: seq[LogEntry]
methodsPerGenericType*: Table[ItemId, seq[(int, PSym)]] # Type ID, attached methods
methodsPerGenericType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far).
initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only)
enumToStringProcs*: Table[ItemId, PSym]
loadedEnumToStringProcs: Table[string, PSym]
enumToStringProcs*: Table[ItemId, LazySym]
emittedTypeInfo*: Table[string, FileIndex]
startupPackedConfig*: PackedConfig
packageSyms*: TStrTable
deps*: IntSet # the dependency graph or potentially its transitive closure.
importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
@@ -94,8 +115,8 @@ type
methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
bucketTable*: CountTable[ItemId]
objectTree*: Table[ItemId, seq[tuple[depth: int, value: PType]]]
methodsPerType*: Table[ItemId, seq[PSym]]
dispatchers*: seq[PSym]
methodsPerType*: Table[ItemId, seq[LazySym]]
dispatchers*: seq[LazySym]
systemModule*: PSym
sysTypes*: array[TTypeKind, PType]
@@ -125,7 +146,6 @@ type
procGlobals*: seq[PNode]
nifReplayActions*: Table[int32, seq[PNode]] # module position -> replay actions for NIF
cachedMods: IntSet
TPassContext* = object of RootObj # the pass's context
idgen*: IdGenerator
@@ -148,7 +168,6 @@ proc resetForBackend*(g: ModuleGraph) =
a.clear()
g.methodsPerGenericType.clear()
g.enumToStringProcs.clear()
g.loadedEnumToStringProcs.clear()
g.dispatchers.setLen(0)
g.methodsPerType.clear()
for a in mitems(g.loadedOps):
@@ -209,43 +228,85 @@ proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) =
strTableAdd(semtabAll(g, m), s)
proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} =
result = module in g.cachedMods
result = module < g.packed.len and g.packed[module].status == loaded
proc isCachedModule*(g: ModuleGraph; m: PSym): bool {.inline.} =
isCachedModule(g, m.position)
proc simulateCachedModule(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
when false:
echo "simulating ", moduleSym.name.s, " ", moduleSym.position
simulateLoadedModule(g.packed, g.config, g.cache, moduleSym, m)
proc initEncoder*(g: ModuleGraph; module: PSym) =
let id = module.position
if id >= g.encoders.len:
setLen g.encoders, id+1
ic.initEncoder(g.encoders[id],
g.packed[id].fromDisk, module, g.config, g.startupPackedConfig)
type
ModuleIter* = object
fromRod: bool
modIndex: int
ti: TIdentIter
rodIt: RodIter
importHidden: bool
proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym; name: PIdent): PSym =
assert m.kind == skModule
mi.modIndex = m.position
mi.fromRod = isCachedModule(g, mi.modIndex)
mi.importHidden = optImportHidden in m.options
result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden), name)
if mi.fromRod:
result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name, mi.importHidden)
else:
result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden), name)
proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden))
if mi.fromRod:
result = nextRodIter(mi.rodIt, g.packed)
else:
result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden))
iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
let importHidden = optImportHidden in m.options
for s in g.ifaces[m.position].interfSelect(importHidden).data:
if s != nil:
yield s
if isCachedModule(g, m):
var rodIt: RodIter = default(RodIter)
var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden)
while r != nil:
yield r
r = nextRodIter(rodIt, g.packed)
else:
for s in g.ifaces[m.position].interfSelect(importHidden).data:
if s != nil:
yield s
proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
let importHidden = optImportHidden in m.options
result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
if isCachedModule(g, m):
result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden)
else:
result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
let importHidden = optImportHidden in m.options
var ti: TIdentIter = default(TIdentIter)
result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
# another symbol exists with same name
amb = true
if isCachedModule(g, m):
result = nil
for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
if result == nil:
# set result to the first symbol
result = s
else:
# another symbol found
amb = true
break
else:
var ti: TIdentIter = default(TIdentIter)
result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
# another symbol exists with same name
amb = true
proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
result = someSym(g, g.systemModule, name)
@@ -257,24 +318,56 @@ iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
yield r
r = nextModuleIter(mi, g)
proc resolveType(g: ModuleGraph; t: var LazyType): PType =
result = t.typ
if result == nil and isCachedModule(g, t.id.module):
result = loadTypeFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
t.typ = result
assert result != nil
proc resolveSym(g: ModuleGraph; t: var LazySym): PSym =
result = t.sym
if result == nil and isCachedModule(g, t.id.module):
result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
t.sym = result
assert result != nil
proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
result = t.inst
if result == nil and isCachedModule(g, t.module):
result = PInstantiation(sym: loadSymFromId(g.config, g.cache, g.packed, t.sym.module, t.sym.packed))
result.concreteTypes = newSeq[PType](t.concreteTypes.len)
for i in 0..high(result.concreteTypes):
result.concreteTypes[i] = loadTypeFromId(g.config, g.cache, g.packed,
t.concreteTypes[i].module, t.concreteTypes[i].packed)
t.inst = result
assert result != nil
proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym =
result = t.sym
if result == nil:
result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
t.sym = result
assert result != nil
iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
if g.typeInstCache.contains(s.itemId):
let x = addr(g.typeInstCache[s.itemId])
for t in mitems(x[]):
yield t
yield resolveType(g, t)
iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
if g.procInstCache.contains(s.itemId):
let x = addr(g.procInstCache[s.itemId])
for t in mitems(x[]):
yield t
yield resolveInst(g, t)
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
## returns the requested attached operation for type `t`. Can return nil
## if no such operation exists.
if g.attachedOps[op].contains(t.itemId):
result = g.attachedOps[op][t.itemId]
result = resolveAttachedOp(g, g.attachedOps[op][t.itemId])
elif g.config.cmd in {cmdNifC, cmdM}:
# Fall back to key-based lookup for NIF-loaded hooks
let key = typeKey(t, g.config, loadTypeCallback, loadSymCallback)
@@ -295,32 +388,36 @@ proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp;
let ownerModule = if t.sym != nil: t.sym.itemId.module.int else: module
g.opsLog.add LogEntry(kind: HookEntry, op: op, module: ownerModule, key: key, sym: value)
g.loadedOps[op][key] = value
g.attachedOps[op][t.itemId] = value
g.attachedOps[op][t.itemId] = LazySym(sym: value)
proc setAttachedOp*(g: ModuleGraph; module: int; typeId: ItemId; op: TTypeAttachedOp; value: PSym) =
## Overload that takes ItemId directly, useful for registering hooks from NIF index.
g.attachedOps[op][typeId] = value
g.attachedOps[op][typeId] = LazySym(sym: value)
proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
## we also need to record this to the packed module.
g.attachedOps[op][t.itemId] = value
g.attachedOps[op][t.itemId] = LazySym(sym: value)
proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) {.inline.} =
discard
proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
if g.config.symbolFiles != disabledSf:
assert module < g.encoders.len
assert isActive(g.encoders[module])
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
#storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
iterator getDispatchers*(g: ModuleGraph): PSym =
for i in g.dispatchers.mitems:
yield i
yield resolveSym(g, i)
proc addDispatchers*(g: ModuleGraph, value: PSym) =
# TODO: add it for packed modules
g.dispatchers.add value
g.dispatchers.add LazySym(sym: value)
iterator resolveLazySymSeq(g: ModuleGraph, list: var seq[PSym]): PSym =
iterator resolveLazySymSeq(g: ModuleGraph, list: var seq[LazySym]): PSym =
for it in list.mitems:
yield it
yield resolveSym(g, it)
proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[PSym]) =
proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[LazySym]) =
# TODO: add it for packed modules
g.methodsPerType[id] = methods
@@ -331,17 +428,14 @@ proc addNifReplayAction*(g: ModuleGraph; module: int32; n: PNode) =
iterator getMethodsPerType*(g: ModuleGraph; t: PType): PSym =
if g.methodsPerType.contains(t.itemId):
for it in mitems g.methodsPerType[t.itemId]:
yield it
yield resolveSym(g, it)
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
result = g.enumToStringProcs.getOrDefault(t.itemId)
if result == nil and g.config.cmd in {cmdNifC, cmdM}:
let key = typeKey(t, g.config, loadTypeCallback, loadSymCallback)
result = g.loadedEnumToStringProcs.getOrDefault(key)
result = resolveSym(g, g.enumToStringProcs[t.itemId])
assert result != nil
proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
g.enumToStringProcs[t.itemId] = value
g.enumToStringProcs[t.itemId] = LazySym(sym: value)
let key = typeKey(t, g.config, loadTypeCallback, loadSymCallback)
let ownerModule = if t.sym != nil: t.sym.itemId.module.int else: value.itemId.module.int
g.opsLog.add LogEntry(kind: EnumToStrEntry, module: ownerModule, key: key, sym: value)
@@ -349,10 +443,10 @@ proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
if g.methodsPerGenericType.contains(t.itemId):
for it in mitems g.methodsPerGenericType[t.itemId]:
yield (it[0], it[1])
yield (it[0], resolveSym(g, it[1]))
proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
g.methodsPerGenericType.mgetOrPut(t.itemId, @[]).add (col, m)
g.methodsPerGenericType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
let key = typeKey(t, g.config, loadTypeCallback, loadSymCallback)
let ownerModule = if t.sym != nil: t.sym.itemId.module.int else: module
g.opsLog.add LogEntry(kind: MethodEntry, module: ownerModule, key: key, sym: m)
@@ -380,12 +474,11 @@ proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
if g.config.symbolFiles == disabledSf and optWithinConfigSystem notin g.config.globalOptions:
# For NIF-based compilation, search in loaded NIF modules
when not defined(nimKochBootstrap):
# Try to resolve from NIF for both cmdNifC and cmdM (which uses NIF files)
if g.config.cmd in {cmdNifC, cmdM}:
# Only try to resolve from NIF if we're actually using NIF files (cmdNifC)
if g.config.cmd == cmdNifC:
# First try system module (most compilerprocs are there)
let systemFileIdx = g.config.m.systemFileIdx
if systemFileIdx != InvalidFileIdx and not g.withinSystem:
# Only try to load from NIF if the file exists (it may not during initial ic build)
if systemFileIdx != InvalidFileIdx:
result = tryResolveCompilerProc(ast.program, name, systemFileIdx)
if result != nil:
strTableAdd(g.compilerprocs, result)
@@ -403,6 +496,20 @@ proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
return result
return nil
# slow, linear search, but the results are cached:
for module in 0..<len(g.packed):
#if isCachedModule(g, module):
let x = searchForCompilerproc(g.packed[module], name)
if x >= 0:
result = loadSymFromId(g.config, g.cache, g.packed, module, toPackedItemId(x))
if result != nil:
strTableAdd(g.compilerprocs, result)
return result
proc loadPackedSym*(g: ModuleGraph; s: var LazySym) =
if s.sym == nil:
s.sym = loadSymFromId(g.config, g.cache, g.packed, s.id.module, s.id.packed)
proc `$`*(u: SigHash): string =
toBase64a(cast[cstring](unsafeAddr u), sizeof(u))
@@ -489,13 +596,16 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
if m.position >= g.ifaces.len:
setLen(g.ifaces, m.position + 1)
if m.position >= g.packed.len:
setLen(g.packed.pm, m.position + 1)
if g.ifaces[m.position].module == nil:
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
uniqueName: rope(uniqueModuleName(g.config, m)))
initStrTables(g, m)
proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
registerModule(g, g.ifaces[int m].module)
registerModule(g, g.packed[int m].module)
proc initOperators*(g: ModuleGraph): Operators =
# These are safe for IC.
@@ -542,7 +652,6 @@ proc initModuleGraphFields(result: ModuleGraph) =
result.operators = initOperators(result)
result.emittedTypeInfo = initTable[string, FileIndex]()
result.cachedFiles = newStringTable()
result.cachedMods = initIntSet()
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
result = ModuleGraph()
@@ -565,13 +674,49 @@ proc resetAllModules*(g: ModuleGraph) =
initModuleGraphFields(g)
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len:
result = g.ifaces[fileIdx.int32].module
else:
result = nil
result = nil
if fileIdx.int32 >= 0:
if isCachedModule(g, fileIdx.int32):
result = g.packed[fileIdx.int32].module
elif fileIdx.int32 < g.ifaces.len:
result = g.ifaces[fileIdx.int32].module
proc moduleOpenForCodegen*(g: ModuleGraph; m: FileIndex): bool {.inline.} =
result = true
if g.config.symbolFiles == disabledSf:
result = true
else:
result = g.packed[m.int32].status notin {undefined, stored, loaded}
proc rememberEmittedTypeInfo*(g: ModuleGraph; m: FileIndex; ti: string) =
#assert(not isCachedModule(g, m.int32))
if g.config.symbolFiles != disabledSf:
#assert g.encoders[m.int32].isActive
assert g.packed[m.int32].status != stored
g.packed[m.int32].fromDisk.emittedTypeInfo.add ti
#echo "added typeinfo ", m.int32, " ", ti, " suspicious ", not g.encoders[m.int32].isActive
proc rememberFlag*(g: ModuleGraph; m: PSym; flag: ModuleBackendFlag) =
if g.config.symbolFiles != disabledSf:
#assert g.encoders[m.int32].isActive
assert g.packed[m.position].status != stored
g.packed[m.position].fromDisk.backendFlags.incl flag
proc closeRodFile*(g: ModuleGraph; m: PSym) =
if g.config.symbolFiles in {readOnlySf, v2Sf}:
# For stress testing we seek to reload the symbols from memory. This
# way much of the logic is tested but the test is reproducible as it does
# not depend on the hard disk contents!
let mint = m.position
saveRodFile(toRodFile(g.config, AbsoluteFile toFullPath(g.config, FileIndex(mint))),
g.encoders[mint], g.packed[mint].fromDisk)
g.packed[mint].status = stored
elif g.config.symbolFiles == stressTest:
# debug code, but maybe a good idea for production? Could reduce the compiler's
# memory consumption considerably at the cost of more loads from disk.
let mint = m.position
simulateCachedModule(g, m, g.packed[mint].fromDisk)
g.packed[mint].status = loaded
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
@@ -655,8 +800,19 @@ proc needsCompilation*(g: ModuleGraph, fileIdx: FileIndex): bool =
proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} =
result = s.ast[bodyPos]
if result == nil and g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
result = loadProcBody(g.config, g.cache, g.packed, s)
s.ast[bodyPos] = result
assert result != nil
proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
cachedModules: var seq[FileIndex]): PSym =
## Returns 'nil' if the module needs to be recompiled.
if g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
result = moduleFromRodFile(g.packed, g.config, g.cache, fileIdx, cachedModules)
else:
result = nil
when not defined(nimKochBootstrap):
proc moduleFromNifFile*(g: ModuleGraph; fileIdx: FileIndex;
flags: set[LoadFlag] = {}): PrecompiledModule =
@@ -684,30 +840,26 @@ when not defined(nimKochBootstrap):
g.ifaces[fileIdx.int].interfHidden, flags)
result.module = m
# Mark module as cached
g.cachedMods.incl fileIdx.int
# Register hooks from NIF index with the module graph
for x in result.logOps:
case x.kind
of HookEntry:
g.loadedOps[x.op][x.key] = x.sym
of ConverterEntry:
g.ifaces[fileIdx.int].converters.add x.sym
g.ifaces[fileIdx.int].converters.add LazySym(sym: x.sym)
of MethodEntry:
discard "todo"
of EnumToStrEntry:
g.loadedEnumToStringProcs[x.key] = x.sym
discard "todo"
of GenericInstEntry:
raiseAssert "GenericInstEntry should not be in the NIF index"
# Register methods per type from NIF index
discard "todo"
proc configComplete*(g: ModuleGraph) =
#rememberStartupConfig(g.startupPackedConfig, g.config)
discard
rememberStartupConfig(g.startupPackedConfig, g.config)
proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym) =
proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
let conf = graph.config
let isNimscript = conf.isDefined("nimscript")
if (not isNimscript) or hintProcessing in conf.cmdlineNotes:

View File

@@ -109,11 +109,9 @@ proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
of FromSearchPath: "@p"
of FromNimblePath: "@n"
# Note: We encode ".." specially as "@d" to avoid issues with changeFileExt
# which would misinterpret ".." as "name.ext" and strip the second part.
prefix & best.multiReplace(
{"..": "@d", $os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
proc demangleModuleName*(path: string): string =
## Demangle a relative module path.
result = path.multiReplace({"@@": "@", "@d": "..", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})

View File

@@ -240,7 +240,7 @@ proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile)
proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
assert fileIdx.int32 >= 0
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
conf.m.fileInfos[fileIdx.int32].hash = hash
else:
shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
@@ -248,7 +248,7 @@ proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
assert fileIdx.int32 >= 0
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
result = conf.m.fileInfos[fileIdx.int32].hash
else:
shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
@@ -664,9 +664,7 @@ template internalAssert*(conf: ConfigRef, e: bool) =
template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") =
let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg]
let msg = if optStyleError in conf.globalOptions: errGenerated
elif optStyleWarning in conf.globalOptions: warnUser
else: hintName
let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
liMessage(conf, info, msg, m, doNothing, instLoc())
proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =

View File

@@ -44,16 +44,14 @@ proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[Precomp
let suffix = stack.pop()
if not visited.containsOrIncl(suffix.string):
var isKnownFile = false
let fileIdx = g.config.registerNifSuffix(suffix.string, isKnownFile)
let nifFile = toGeneratedFile(g.config, AbsoluteFile(suffix.string), ".nif")
let fileIdx = msgs.fileInfoIdx(g.config, nifFile)
let precomp = moduleFromNifFile(g, fileIdx, {LoadFullAst})
if precomp.module != nil:
result.add precomp
for dep in precomp.deps:
if not visited.contains(dep.string):
stack.add dep
else:
assert false, "Recompiling module is not implemented."
if mainModule.module != nil:
result.add mainModule

View File

@@ -983,7 +983,7 @@ proc genericParamToNif(n: PNode; parent: PNode; c: var TranslationContext) =
toNif n, parent, c
proc addExternName(sym: PSym; c: var TranslationContext) =
if sym.loc.snippet != "":
if sym.loc.snippet != nil:
c.b.addStrLit sym.loc.snippet
else:
c.b.addStrLit sym.name.s

View File

@@ -12,8 +12,7 @@ define:nimPreviewNonVarDestructor
define:nimPreviewCheckedClose
define:nimPreviewAsmSemSymbol
define:nimPreviewCStringComparisons
#define:nimPreviewDuplicateModuleError
# Incompatible with Nimony's compat2.nim for now
define:nimPreviewDuplicateModuleError
threads:off
@@ -66,7 +65,3 @@ define:useStdoutAsStdmsg
@if nimHasVtables:
experimental:vtables
@end
@if nimHasImplicitRangeConversion:
warning[ImplicitRangeConversion]:off
@end

View File

@@ -118,14 +118,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
if conf.selectedGC == gcUnselected:
if conf.backend in {backendC, backendCpp, backendObjc} or
(conf.cmd in cmdDocLike and conf.backend != backendJs) or
conf.cmd in {cmdGendepend, cmdNifC, cmdIc, cmdM}:
conf.cmd in {cmdGendepend, cmdNifC, cmdDeps, cmdM}:
initOrcDefines(conf)
if conf.selectedStrings == stringSso and
conf.selectedGC notin {gcArc, gcOrc, gcYrc, gcAtomicArc}:
rawMessage(conf, errGenerated,
"--strings:sso requires --mm:arc, --mm:orc, --mm:yrc, or --mm:atomicArc")
mainCommand(graph)
if conf.hasHint(hintGCStats): echo(GC_getStatistics())
#echo(GC_getStatistics())

View File

@@ -68,7 +68,6 @@ type # please make sure we have under 32 options
optUseNimcache, # save artifacts (including binary) in $nimcache
optStyleHint, # check that the names adhere to NEP-1
optStyleError, # enforce that the names adhere to NEP-1
optStyleWarning, # emit style checks as warnings
optStyleUsages, # only enforce consistent **usages** of the symbol
optSkipSystemConfigFile, # skip the system's cfg/nims config file
optSkipProjConfigFile, # skip the project's cfg/nims config file
@@ -111,7 +110,6 @@ type # please make sure we have under 32 options
optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types.
optShowNonExportedFields # for documentation: show fields that are not exported
optJsBigInt64 # use bigints for 64-bit integers in JS
optDocRaw # for documentation: Don't render markdown for JSON output
optItaniumMangle # mangling follows the Itanium spec
optCompress # turn on AST compression by converting it to NIF
optWithinConfigSystem # we still compile within the configuration system
@@ -157,6 +155,7 @@ type
cmdCheck # semantic checking for whole project
cmdM # only compile a single
cmdParse # parse a single file (for debugging)
cmdRod # .rod to some text representation (for debugging)
cmdIdeTools # ide tools (e.g. nimsuggest)
cmdNimscript # evaluate nimscript
cmdDoc0
@@ -178,7 +177,7 @@ type
# old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
cmdCompileToNif
cmdNifC # generate C code from NIF files
cmdIc # generate .build.nif for nifmake
cmdDeps # generate .build.nif for nifmake
const
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
@@ -195,7 +194,6 @@ type
gcRegions = "regions"
gcArc = "arc"
gcOrc = "orc"
gcYrc = "yrc" # thread-safe ORC (concurrent cycle collector)
gcAtomicArc = "atomicArc"
gcMarkAndSweep = "markAndSweep"
gcHooks = "hooks"
@@ -259,9 +257,6 @@ type
## Old transformation for closures in JS backend
noPanicOnExcept
## don't panic on bare except
procParamTypeBackendAliases
## Keep the old proc type compatibility rules that ignore backend
## c type aliases.
SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
@@ -270,10 +265,6 @@ type
ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
StringsMode* = enum
stringDefault = "default"
stringSso = "sso"
ExceptionSystem* = enum
excNone, # no exception system selected yet
excSetjmp, # setjmp based exception handling
@@ -373,7 +364,6 @@ type
implicitCmd*: bool # whether some flag triggered an implicit `command`
selectedGC*: TGCMode # the selected GC (+)
exc*: ExceptionSystem
selectedStrings*: StringsMode
hintProcessingDots*: bool # true for dots, false for filenames
verbosity*: int # how verbose the compiler is
numberOfProcessors*: int # number of processors
@@ -706,7 +696,6 @@ template quitOrRaise*(conf: ConfigRef, msg = "") =
proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools}
proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
proc usesSso*(conf: ConfigRef): bool {.inline.} = conf.selectedStrings == stringSso
template compilationCachePresent*(conf: ConfigRef): untyped =
false
@@ -1057,9 +1046,6 @@ proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
proc showNonExportedFields*(conf: ConfigRef) =
incl(conf.globalOptions, optShowNonExportedFields)
proc docRawOutput*(conf: ConfigRef) =
incl(conf.globalOptions, optDocRaw)
proc expandDone*(conf: ConfigRef): bool =
result = conf.ideCmd == ideExpand and conf.expandLevels == 0 and conf.expandProgress

View File

@@ -148,6 +148,11 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
closeParser(p)
if s.kind != llsStdIn: break
closePasses(graph, a)
if graph.config.backend notin {backendC, backendCpp, backendObjc}:
# We only write rod files here if no C-like backend is active.
# The C-like backends have been patched to support the IC mechanism.
# They are responsible for closing the rod files. See `cbackend.nim`.
closeRodFile(graph, module)
result = true
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
@@ -163,10 +168,22 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fr
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
discard processModule(graph, result, idGeneratorFromModule(result), s)
if result == nil:
result = newModule(graph, fileIdx)
result.incl flags
registerModule(graph, result)
processModuleAux("import")
var cachedModules: seq[FileIndex] = @[]
result = moduleFromRodFile(graph, fileIdx, cachedModules)
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
if result == nil:
result = newModule(graph, fileIdx)
result.incl flags
registerModule(graph, result)
processModuleAux("import")
else:
if sfSystemModule in flags:
graph.systemModule = result
partialInitModule(result, graph, fileIdx, filename)
for m in cachedModules:
registerModuleById(graph, m)
replayStateChanges(graph.packed.pm[m.int].module, graph)
replayGenericCacheInformation(graph, m.int)
elif graph.isDirty(result):
result.excl sfDirty
# reset module fields:

View File

@@ -242,11 +242,8 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
when not defined(nimKochBootstrap):
# For cmdM: only write NIF for the main module, not for imported modules
# (imported modules should be loaded from existing NIF files)
let shouldWriteNif = (optCompress in graph.config.globalOptions) or
(graph.config.cmd == cmdM and sfMainModule in module.flags)
if shouldWriteNif and not graph.config.isDefined("nimscript"):
if (optCompress in graph.config.globalOptions or graph.config.cmd == cmdM) and
not graph.config.isDefined("nimscript"):
topLevelStmts.add finalNode
# Collect replay actions from both pragma computations and VM state diff
var replayActions: seq[PNode] = @[]
@@ -261,6 +258,12 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
writeNifModule(graph.config, module.position.int32, topLevelStmts, graph.opsLog, replayActions)
if graph.config.backend notin {backendC, backendCpp, backendObjc} and graph.config.cmd != cmdM:
# We only write rod files here if no C-like backend is active.
# The C-like backends have been patched to support the IC mechanism.
# They are responsible for closing the rod files. See `cbackend.nim`.
# cmdM uses NIF files only, not ROD files.
closeRodFile(graph, module)
result = true
proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags; fromModule: PSym = nil): PSym =
@@ -276,6 +279,7 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
discard processPipelineModule(graph, result, idGeneratorFromModule(result), s)
if result == nil:
var cachedModules: seq[FileIndex] = @[]
when not defined(nimKochBootstrap):
# For cmdM: load imports from NIF files (but compile the main module from source)
# Skip when withinSystem is true (compiling system.nim itself)
@@ -286,20 +290,13 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
let precomp = moduleFromNifFile(graph, fileIdx)
if precomp.module == nil:
let nifPath = toNifFilename(graph.config, fileIdx)
globalError(graph.config, unknownLineInfo,
localError(graph.config, unknownLineInfo,
"nim m requires precompiled NIF for import: " & toFullPath(graph.config, fileIdx) &
" (expected: " & nifPath & ")")
return nil # Don't fall through to compile from source
else:
# Module successfully loaded from NIF file - use it and skip processing
result = precomp.module
if sfSystemModule in flags:
graph.systemModule = result
partialInitModule(result, graph, fileIdx, AbsoluteFile(toFullPath(graph.config, fileIdx)))
# Replay state changes from the loaded NIF module
if result.ast != nil:
replayStateChanges(result, graph)
return result # Return early, don't process from source
if result == nil and graph.config.cmd != cmdM:
# Fall back to ROD file loading (not used for cmdM which uses NIF only)
result = moduleFromRodFile(graph, fileIdx, cachedModules)
let path = toFullPath(graph.config, fileIdx)
let filename = AbsoluteFile path
# it could be a stdinfile/cmdfile
@@ -318,6 +315,16 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
registerModule(graph, result)
processModuleAux("import")
partialInitModule(result, graph, fileIdx, filename)
for m in cachedModules:
registerModuleById(graph, m)
if graph.config.cmd == cmdM:
# cmdM uses NIF files - replay from module AST loaded by loadNifModule
let module = graph.getModule(m)
if module != nil and module.ast != nil:
replayStateChanges(module, graph)
else:
replayStateChanges(graph.packed.pm[m.int].module, graph)
replayGenericCacheInformation(graph, m.int)
elif graph.isDirty(result):
result.excl sfDirty
# reset module fields:
@@ -377,6 +384,7 @@ proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx
connectPipelineCallbacks(graph)
graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
graph.config.libpath / RelativeFile"system.nim")
var cachedModules: seq[FileIndex] = @[]
when not defined(nimKochBootstrap):
let precomp = moduleFromNifFile(graph, graph.config.m.systemFileIdx)
graph.systemModule = precomp.module

View File

@@ -21,6 +21,8 @@ import std/[os, math, strutils]
when defined(nimPreviewSlimSystem):
import std/assertions
from ic / ic import addCompilerProc
const
FirstCallConv* = wNimcall
LastCallConv* = wNoconv
@@ -567,7 +569,7 @@ proc processCompile(c: PContext, n: PNode) =
n[i] = c.semConstExpr(c, n[i])
case n[i].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
result = n[i].strVal
else:
shallowCopy(result, n[i].strVal)
@@ -765,6 +767,8 @@ proc markCompilerProc(c: PContext; s: PSym) =
incl(s, sfCompilerProc)
incl(s.flagsImpl, sfUsed)
registerCompilerProc(c.graph, s)
if c.config.symbolFiles != disabledSf:
addCompilerProc(c.encoder, c.packedRepr, s)
proc deprecatedStmt(c: PContext; outerPragma: PNode) =
let pragma = outerPragma[1]

View File

@@ -14,7 +14,7 @@
{.used.}
import
lexer, options, idents, ast, msgs, lineinfos, wordrecg, trees
lexer, options, idents, ast, msgs, lineinfos, wordrecg
import std/[strutils]
@@ -66,359 +66,6 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string
# determines how long the subtree will likely be, the second
# phase appends to a buffer that will be the output.
type
TPreferedDesc* = enum
preferName, # default
preferDesc, # probably should become what preferResolved is
preferExported,
preferModuleInfo, # fully qualified
preferGenericArg,
preferTypeName,
preferResolved, # fully resolved symbols
preferMixed,
# most useful, shows: symbol + resolved symbols if it differs, e.g.:
# tuple[a: MyInt{int}, b: float]
preferInlayHint,
preferInferredEffects,
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
template `$`*(typ: PType): string = typeToString(typ)
proc valueToString(a: PNode): string =
case a.kind
of nkCharLit, nkUIntLit..nkUInt64Lit:
result = $cast[uint64](a.intVal)
of nkIntLit..nkInt64Lit:
result = $a.intVal
of nkFloatLit..nkFloat128Lit: result = $a.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkStaticExpr: result = "static(" & a[0].renderTree & ")"
else: result = "<invalid value>"
proc rangeToStr(n: PNode): string =
assert(n.kind == nkRange)
result = valueToString(n[0]) & ".." & valueToString(n[1])
const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,
preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects}
const
typeToStr: array[TTypeKind, string] = ["None", "bool", "char", "empty",
"Alias", "typeof(nil)", "untyped", "typed", "typeDesc",
# xxx typeDesc=>typedesc: typedesc is declared as such, and is 10x more common.
"GenericInvocation", "GenericBody", "GenericInst", "GenericParam",
"distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
"set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
"pointer", "OpenArray[$1]", "string", "cstring", "Forward",
"int", "int8", "int16", "int32", "int64",
"float", "float32", "float64", "float128",
"uint", "uint8", "uint16", "uint32", "uint64",
"owned", "sink",
"lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type",
"BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "inferred",
"and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix
"void", "iterable"]
proc addTypeFlags(name: var string, typ: PType) {.inline.} =
if tfNotNil in typ.flags: name.add(" not nil")
proc isIntLit*(t: PType): bool {.inline.} =
result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit
proc isFloatLit*(t: PType): bool {.inline.} =
result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
# TODO: It would be a good idea to kill the special state of a resolved
# concept by switching to tyAlias within the instantiated procs.
# Currently, tyAlias is always skipped with skipModifier, which means that
# we can store information about the matched concept in another position.
# Then builtInFieldAccess can be modified to properly read the derived
# consts and types stored within the concept.
template isResolvedUserTypeClass*(t: PType): bool =
tfResolved in t.flags
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
let preferToplevel = prefer
proc getPrefer(prefer: TPreferedDesc): TPreferedDesc =
if preferToplevel in {preferResolved, preferMixed}:
preferToplevel # sticky option
else:
prefer
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
result = ""
let prefer = getPrefer(prefer)
let t = typ
if t == nil: return
if prefer in preferToResolveSymbols and t.sym != nil and
sfAnon notin t.sym.flags and t.kind notin {tySequence, tyInferred}:
if t.kind == tyInt and isIntLit(t):
if prefer == preferInlayHint:
result = t.sym.name.s
else:
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
elif t.kind == tyAlias and t.elementType.kind != tyAlias:
result = typeToString(t.elementType)
elif prefer in {preferResolved, preferMixed}:
case t.kind
of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}:
result = typeToStr[t.kind]
of tyGenericBody:
result = typeToString(t.last)
of tyCompositeTypeClass:
# avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]`
result = typeToString(t.last.last)
else:
result = t.sym.name.s
if prefer == preferMixed and result != t.sym.name.s:
result = t.sym.name.s & "{" & result & "}"
elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil:
# note: should probably be: {preferName, preferTypeName, preferGenericArg}
result = t.sym.name.s
if t.kind == tyGenericParam and t.genericParamHasConstraints:
result.add ": "
result.add t.elementType.typeToString
else:
result = t.sym.owner.name.s & '.' & t.sym.name.s
result.addTypeFlags(t)
return
case t.kind
of tyInt:
if not isIntLit(t) or prefer == preferExported:
result = typeToStr[t.kind]
else:
case prefer:
of preferGenericArg:
result = $t.n.intVal
of preferInlayHint:
result = "int"
else:
result = "int literal(" & $t.n.intVal & ")"
of tyGenericInst:
result = typeToString(t.genericHead) & '['
for needsComma, a in t.genericInstParams:
if needsComma: result.add(", ")
result.add(typeToString(a, preferGenericArg))
result.add(']')
of tyGenericInvocation:
result = typeToString(t.genericHead) & '['
for needsComma, a in t.genericInvocationParams:
if needsComma: result.add(", ")
result.add(typeToString(a, preferGenericArg))
result.add(']')
of tyGenericBody:
result = typeToString(t.typeBodyImpl) & '['
for i, a in t.genericBodyParams:
if i > 0: result.add(", ")
result.add(typeToString(a, preferTypeName))
result.add(']')
of tyTypeDesc:
if t.elementType.kind == tyNone: result = "typedesc"
else: result = "typedesc[" & typeToString(t.elementType) & "]"
of tyStatic:
if prefer == preferGenericArg and t.n != nil:
result = t.n.renderTree
else:
result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) else: "") & "]"
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
of tyUserTypeClass:
if t.sym != nil and t.sym.owner != nil:
if t.isResolvedUserTypeClass: return typeToString(t.last)
return t.sym.owner.name.s
else:
result = "<invalid tyUserTypeClass>"
of tyBuiltInTypeClass:
result =
case t.base.kind
of tyVar: "var"
of tyRef: "ref"
of tyPtr: "ptr"
of tySequence: "seq"
of tyArray: "array"
of tySet: "set"
of tyRange: "range"
of tyDistinct: "distinct"
of tyProc: "proc"
of tyObject: "object"
of tyTuple: "tuple"
of tyOpenArray: "openArray"
else: typeToStr[t.base.kind]
of tyInferred:
let concrete = t.previouslyInferred
if concrete != nil: result = typeToString(concrete)
else: result = "inferred[" & typeToString(t.base) & "]"
of tyUserTypeClassInst:
let body = t.base
result = body.sym.name.s & "["
for needsComma, a in t.userTypeClassInstParams:
if needsComma: result.add(", ")
result.add(typeToString(a))
result.add "]"
of tyAnd:
for i, son in t.ikids:
if i > 0: result.add(" and ")
result.add(typeToString(son))
of tyOr:
for i, son in t.ikids:
if i > 0: result.add(" or ")
result.add(typeToString(son))
of tyNot:
result = "not " & typeToString(t.elementType)
of tyUntyped:
#internalAssert t.len == 0
result = "untyped"
of tyFromExpr:
if t.n == nil:
result = "unknown"
else:
result = "typeof(" & renderTree(t.n) & ")"
of tyArray:
result = "array"
if t.hasElementType:
if t.indexType.kind == tyRange:
result &= "[" & rangeToStr(t.indexType.n) & ", " &
typeToString(t.elementType) & ']'
else:
result &= "[" & typeToString(t.indexType) & ", " &
typeToString(t.elementType) & ']'
of tyUncheckedArray:
result = "UncheckedArray"
if t.hasElementType:
result &= "[" & typeToString(t.elementType) & ']'
of tySequence:
if t.sym != nil and prefer != preferResolved:
result = t.sym.name.s
else:
result = "seq"
if t.hasElementType:
result &= "[" & typeToString(t.elementType) & ']'
of tyOrdinal:
result = "ordinal"
if t.hasElementType:
result &= "[" & typeToString(t.skipModifier) & ']'
of tySet:
result = "set"
if t.hasElementType:
result &= "[" & typeToString(t.elementType) & ']'
of tyOpenArray:
result = "openArray"
if t.hasElementType:
result &= "[" & typeToString(t.elementType) & ']'
of tyDistinct:
result = "distinct " & typeToString(t.elementType,
if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
of tyIterable:
# xxx factor this pattern
result = "iterable"
if t.hasElementType:
result &= "[" & typeToString(t.skipModifier) & ']'
of tyTuple:
# we iterate over t.sons here, because t.n may be nil
if t.n != nil:
result = "tuple["
for i in 0..<t.n.len:
assert(t.n[i].kind == nkSym)
result.add(t.n[i].sym.name.s & ": " & typeToString(t.n[i].sym.typ))
if i < t.n.len - 1: result.add(", ")
result.add(']')
elif t.isEmptyTupleType:
result = "tuple[]"
elif t.isSingletonTupleType:
result = "("
for son in t.kids:
result.add(typeToString(son))
result.add(",)")
else:
result = "("
for i, son in t.ikids:
if i > 0: result.add ", "
result.add(typeToString(son))
result.add(')')
of tyPtr, tyRef, tyVar, tyLent:
result = if isOutParam(t): "out " else: typeToStr[t.kind]
result.add typeToString(t.elementType)
of tyRange:
result = "range "
if t.n != nil and t.n.kind == nkRange:
result.add rangeToStr(t.n)
if prefer != preferExported:
result.add("(" & typeToString(t.elementType) & ")")
of tyProc:
result = if tfIterator in t.flags: "iterator "
elif t.owner != nil:
case t.owner.kind
of skTemplate: "template "
of skMacro: "macro "
of skConverter: "converter "
else: "proc "
else:
"proc "
if tfUnresolved in t.flags: result.add "[*missing parameters*]"
result.add "("
for i, a in t.paramTypes:
if i > FirstParamAt: result.add(", ")
let j = paramTypeToNodeIndex(i)
if t.n != nil and j < t.n.len and t.n[j].kind == nkSym:
result.add(t.n[j].sym.name.s)
result.add(": ")
result.add(typeToString(a))
result.add(')')
if t.returnType != nil: result.add(": " & typeToString(t.returnType))
var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv
var hasImplicitRaises = false
if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos:
let pragmasNode = t.owner.ast[pragmasPos]
let raisesSpec = effectSpec(pragmasNode, wRaises)
if not isNil(raisesSpec):
addSep(prag)
prag.add("raises: ")
prag.add(renderTree raisesSpec)
hasImplicitRaises = true
if tfNoSideEffect in t.flags:
addSep(prag)
prag.add("noSideEffect")
if tfThread in t.flags:
addSep(prag)
prag.add("gcsafe")
var effectsOfStr = ""
for i, a in t.paramTypes:
let j = paramTypeToNodeIndex(i)
if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags:
addSep(effectsOfStr)
effectsOfStr.add(t.n[j].sym.name.s)
if effectsOfStr != "":
addSep(prag)
prag.add("effectsOf: ")
prag.add(effectsOfStr)
if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
let effects = t.n[0]
if effects.kind == nkEffectList and effects.len == effectListLen:
var inferredRaisesStr = ""
let effs = effects[exceptionEffects]
if not isNil(effs):
for eff in items(effs):
if not isNil(eff):
addSep(inferredRaisesStr)
inferredRaisesStr.add($eff.typ)
addSep(prag)
prag.add("raises: <inferred> [")
prag.add(inferredRaisesStr)
prag.add("]")
if prag.len != 0: result.add("{." & prag & ".}")
of tyVarargs:
result = typeToStr[t.kind] % typeToString(t.elementType)
of tySink:
result = "sink " & typeToString(t.skipModifier)
of tyOwned:
result = "owned " & typeToString(t.elementType)
else:
result = typeToStr[t.kind]
result.addTypeFlags(t)
result = typeToString(typ, prefer)
proc disamb(g: var TSrcGen; s: PSym): int =
# we group by 's.name.s' to compute the stable name ID.
result = 0
@@ -582,7 +229,6 @@ proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
inc(g.lineLen, s.len)
proc putComment(g: var TSrcGen, s: string) =
const SpecialWhitespace = {' ', '\t', '\r', '\n', '\0'}
if s.len == 0: return
var i = 0
let hi = s.len - 1
@@ -612,12 +258,12 @@ proc putComment(g: var TSrcGen, s: string) =
# gets too long:
# compute length of the following word:
var j = i
while j <= hi and s[j] notin SpecialWhitespace: inc(j)
while j <= hi and s[j] > ' ': inc(j)
if not isCode and (g.col + (j - i) > MaxLineLen):
put(g, tkComment, com)
optNL(g, ind)
com = "## "
while i <= hi and s[i] notin SpecialWhitespace:
while i <= hi and s[i] > ' ':
com.add(s[i])
inc(i)
put(g, tkComment, com)
@@ -1216,28 +862,10 @@ proc genSymSuffix(result: var string, s: PSym) {.inline.} =
result.add '_'
result.addInt s.id
proc gsemmedParams(g: var TSrcGen, n: PNode) =
put(g, tkParLe, "(")
for i in 1..<n.len:
if i > 1:
putWithSpace(g, tkComma, ";")
let x {.cursor.} = n[i]
if x.kind == nkSym:
put g, tkSymbol, renderDefinitionName(x.sym)
putWithSpace(g, tkColon, ":")
put g, tkSymbol, typeToString(x.sym.typ)
else:
gsub(g, x)
put(g, tkParRi, ")")
if not isEmptyType(n[0].typ):
putWithSpace(g, tkColon, ":")
gsub(g, n[0])
proc gproc(g: var TSrcGen, n: PNode) =
var c: TContext = initContext()
var s: PSym = nil
if n[namePos].kind == nkSym:
s = n[namePos].sym
let s = n[namePos].sym
var ret = renderDefinitionName(s)
ret.genSymSuffix(s)
put(g, tkSymbol, ret)
@@ -1252,10 +880,7 @@ proc gproc(g: var TSrcGen, n: PNode) =
gsub(g, n[miscPos][1])
else:
gsub(g, n[genericParamsPos])
if n[paramsPos].len == 0 and s != nil and s.typ != nil and s.typ.n != nil:
gsemmedParams(g, s.typ.n)
else:
gsub(g, n[paramsPos])
gsub(g, n[paramsPos])
if renderNoPragmas notin g.flags:
gsub(g, n[pragmasPos])
if renderNoBody notin g.flags:

View File

@@ -231,7 +231,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
if optOwnedRefs in oldGlobalOptions:
conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
defineSymbol(conf.symbols, "nimv2")
if conf.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc}:
if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
conf.globalOptions.incl {optTinyRtti, optSeqDestructors}
defineSymbol(conf.symbols, "nimv2")
defineSymbol(conf.symbols, "gcdestructors")
@@ -241,8 +241,6 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
defineSymbol(conf.symbols, "gcarc")
of gcOrc:
defineSymbol(conf.symbols, "gcorc")
of gcYrc:
defineSymbol(conf.symbols, "gcyrc")
of gcAtomicArc:
defineSymbol(conf.symbols, "gcatomicarc")
else:

View File

@@ -855,7 +855,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
appendToModule(c.module, result)
trackStmt(c, c.module, result, isTopLevel = true)
if optMultiMethods notin c.config.globalOptions and
c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc} and
c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
Feature.vtables in c.config.features:
sortVTableDispatchers(c.graph)
@@ -889,6 +889,8 @@ proc semWithPContext*(c: PContext, n: PNode): PNode =
else:
result = newNodeI(nkEmpty, n.info)
#if c.config.cmd == cmdIdeTools: findSuggest(c, n)
storeRodNode(c, result)
proc reportUnusedModules(c: PContext) =
if c.config.cmd == cmdM: return

View File

@@ -131,7 +131,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
var sym = syms[0].s
let name = sym.name
var scope = syms[0].scope
c.openShadowScope
if allowTypeBoundOps:
for a in 1 ..< n.len:
# for every already typed argument, add type bound ops
@@ -160,13 +160,9 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
addTypeBoundSymbols(c.graph, arg.typ, name, filter, symMarker, syms)
if z.state == csMatch:
# Iterator preference is heuristic in iterator-admitting contexts.
# The dedicated iterable path uses `iteratorPreference`, other
# context use exact-match bump
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator:
if efPreferIteratorForIterable in flags:
inc(z.iteratorPreference)
elif not (efWantIterator notin flags and efWantIterable in flags):
if not (efWantIterator notin flags and efWantIterable in flags):
inc(z.exactMatches, 200)
else:
dec(z.exactMatches, 200)
@@ -218,10 +214,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
scope = syms[nextSymIndex].scope
inc(nextSymIndex)
if best.state == csMatch and best.calleeSym != nil and best.calleeSym.kind in {skTemplate, skMacro}:
c.closeShadowScope
else:
c.mergeShadowScope
proc effectProblem(f, a: PType; result: var string; c: PContext) =
if f.kind == tyProc and a.kind == tyProc:
@@ -679,7 +671,7 @@ proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
# copied from semOverloadedCallAnalyzeEffects, might be overkill:
const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
let filter =
if flags*{efInTypeof, efWantIterator, efWantIterable, efPreferIteratorForIterable} != {}:
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
baseFilter + {skIterator}
else: baseFilter
# this will add the errors:
@@ -838,21 +830,6 @@ proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
for i in 0 ..< flatUnbound.len():
x.bindings.put(flatUnbound[i], flatBound[i])
proc compactVoidArgs(n: PNode): PNode =
# deletes void args from the argument list, which are created by `setSon`
var hasNil = false
for i in 0..<n.len:
if n[i] == nil:
hasNil = true
break
if not hasNil:
result = n
else:
result = copyNode(n)
for i in 0..<n.len:
if n[i] != nil:
result.add n[i]
proc semResolvedCall(c: PContext, x: var TCandidate,
n: PNode, flags: TExprFlags;
expectedType: PType = nil): PNode =
@@ -903,7 +880,7 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
markUsed(c, info, finalCallee, isGenericInstance = true)
onUse(info, finalCallee, isGenericInstance = true)
result = compactVoidArgs(x.call)
result = x.call
instGenericConvertersSons(c, result, x)
markConvertersUsed(c, result)
result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
@@ -989,7 +966,7 @@ proc setGenericParams(c: PContext, n, expectedParams: PNode) =
if e.typ == nil:
n[i].typ = errorType(c)
else:
n[i].typ = e.typ
n[i].typ = e.typ.skipTypes({tyTypeDesc})
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool): PNode =
assert n.kind == nkBracketExpr

View File

@@ -19,6 +19,8 @@ import
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable,
types, lowerings, trees, parampatterns, astalgo
import ic / ic
type
TOptionEntry* = object # entries to put on a stack for pragma parsing
options*: TOptions
@@ -54,18 +56,7 @@ type
inst*: PInstantiation
TExprFlag* = enum
efLValue,
# The expression is used as an assignable location.
efWantIterator,
# Admit iterator candidates and prefer them during overload resolution.
efWantIterable,
# Admit iterator candidates for expressions that may feed iterable-style
# chaining.
efPreferIteratorForIterable,
# Prefer iterator candidates for `iterable[T]` matching and wrap a
# successful iterator call as `tyIterable`.
efInTypeof,
# The expression is being semchecked under `typeof`.
efLValue, efWantIterator, efWantIterable, efInTypeof,
efNeedStatic,
# Use this in contexts where a static value is mandatory
efPreferStatic,
@@ -180,12 +171,9 @@ type
sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
inUncheckedAssignSection*: int
importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
forwardTypeUpdates*: seq[(PSym, PType, PNode)]
# top-level owner, type, and type node for delayed retries inside a
# type section due to containing forward types
forwardFieldUpdates*: seq[(PType, PNode, PType)]
# object/tuple field definitions whose default values mention forward
# types and need delayed const checking
forwardTypeUpdates*: seq[(PType, PNode)]
# types that need to be updated in a type section
# due to containing forward types, and their corresponding nodes
inTypeofContext*: int
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
@@ -348,14 +336,28 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext =
signatures: initStrTable(),
features: graph.config.features
)
if graph.config.symbolFiles != disabledSf:
let id = module.position
if graph.config.cmd != cmdM:
assert graph.packed[id].status in {undefined, outdated}
graph.packed[id].status = storing
graph.packed[id].module = module
initEncoder graph, module
template packedRepr*(c): untyped = c.graph.packed[c.module.position].fromDisk
template encoder*(c): untyped = c.graph.encoders[c.module.position]
proc addIncludeFileDep*(c: PContext; f: FileIndex) =
discard
if c.config.symbolFiles != disabledSf:
addIncludeFileDep(c.encoder, c.packedRepr, f)
proc addImportFileDep*(c: PContext; f: FileIndex) =
discard
if c.config.symbolFiles != disabledSf:
addImportFileDep(c.encoder, c.packedRepr, f)
proc addPragmaComputation*(c: PContext; n: PNode) =
if c.config.symbolFiles != disabledSf:
addPragmaComputation(c.encoder, c.packedRepr, n)
# Also store for NIF-based IC (cmdM mode or optCompress)
if optCompress in c.config.globalOptions or c.config.cmd == cmdM:
addNifReplayAction(c.graph, c.module.position.int32, n)
@@ -366,28 +368,38 @@ proc inclSym(sq: var seq[PSym], s: PSym): bool =
sq.add s
result = true
proc addConverter*(c: PContext, conv: PSym) =
assert conv != nil
if inclSym(c.converters, conv):
proc addConverter*(c: PContext, conv: LazySym) =
assert conv.sym != nil
if inclSym(c.converters, conv.sym):
add(c.graph.ifaces[c.module.position].converters, conv)
proc addConverterDef*(c: PContext, conv: PSym) =
proc addConverterDef*(c: PContext, conv: LazySym) =
addConverter(c, conv)
if c.config.symbolFiles != disabledSf:
addConverter(c.encoder, c.packedRepr, conv.sym)
proc addPureEnum*(c: PContext, e: PSym) =
assert e != nil
proc addPureEnum*(c: PContext, e: LazySym) =
assert e.sym != nil
add(c.graph.ifaces[c.module.position].pureEnums, e)
if c.config.symbolFiles != disabledSf:
addPureEnum(c.encoder, c.packedRepr, e.sym)
proc addPattern*(c: PContext, p: PSym) =
assert p != nil
if inclSym(c.patterns, p):
proc addPattern*(c: PContext, p: LazySym) =
assert p.sym != nil
if inclSym(c.patterns, p.sym):
add(c.graph.ifaces[c.module.position].patterns, p)
if c.config.symbolFiles != disabledSf:
addTrmacro(c.encoder, c.packedRepr, p.sym)
proc exportSym*(c: PContext; s: PSym) =
strTableAdds(c.graph, c.module, s)
if c.config.symbolFiles != disabledSf:
addExported(c.encoder, c.packedRepr, s)
proc reexportSym*(c: PContext; s: PSym) =
strTableAdds(c.graph, c.module, s)
if c.config.symbolFiles != disabledSf:
addReexport(c.encoder, c.packedRepr, s)
proc newLib*(kind: TLibKind): PLib =
result = PLib(kind: kind) #result.syms = initObjectSet()
@@ -602,11 +614,19 @@ template addExport*(c: PContext; s: PSym) =
## convenience to export a symbol from the current module
addExport(c.graph, c.module, s)
proc storeRodNode*(c: PContext, n: PNode) =
if c.config.symbolFiles != disabledSf:
toPackedNodeTopLevel(n, c.encoder, c.packedRepr)
proc addToGenericProcCache*(c: PContext; s: PSym; inst: PInstantiation) =
c.graph.procInstCache.mgetOrPut(s.itemId, @[]).add inst
c.graph.procInstCache.mgetOrPut(s.itemId, @[]).add LazyInstantiation(module: c.module.position, inst: inst)
if c.config.symbolFiles != disabledSf:
storeInstantiation(c.encoder, c.packedRepr, s, inst)
proc addToGenericCache*(c: PContext; s: PSym; inst: PType) =
c.graph.typeInstCache.mgetOrPut(s.itemId, @[]).add inst
c.graph.typeInstCache.mgetOrPut(s.itemId, @[]).add LazyType(typ: inst)
if c.config.symbolFiles != disabledSf:
storeTypeInst(c.encoder, c.packedRepr, s, inst)
proc sealRodFile*(c: PContext) =
if c.config.symbolFiles != disabledSf:
@@ -622,8 +642,9 @@ proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) =
## in the sem'checked AST. This is very bad for IDE-like tooling
## ("find all usages of this template" would not work). We need special
## logic to remember macro/template expansions. This is done here and
## delegated to the "NIF" file mechanism.
discard "XXX To implement"
## delegated to the "rod" file mechanism.
if c.config.symbolFiles != disabledSf:
storeExpansion(c.encoder, c.packedRepr, info, expandedSym)
const
errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable"
@@ -637,11 +658,6 @@ proc renderNotLValue*(n: PNode): string =
elif n.kind in {nkHiddenStdConv, nkHiddenSubConv} and n.len == 2:
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
proc isSsoStringIndex*(conf: ConfigRef; n: PNode): bool =
result = conf.usesSso() and n.kind == nkBracketExpr and n.len >= 1 and
n[0].typ != nil and
n[0].typ.skipTypes(abstractVar + abstractInst - {tyTypeDesc}).kind == tyString
proc isAssignable(c: PContext, n: PNode): TAssignableResult =
result = parampatterns.isAssignable(c.p.owner, n)
@@ -749,7 +765,7 @@ proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
case kind
of attachedDestructor:
result = n
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedDestructor)
if op != nil:
result[0] = newSymNode(op)
@@ -761,13 +777,13 @@ proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
result[1] = skipAddr(n[1])
of attachedTrace:
result = n
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedTrace)
if op != nil:
result[0] = newSymNode(op)
of attachedDup:
result = n
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedDup)
if op != nil:
result[0] = newSymNode(op)
@@ -777,26 +793,18 @@ proc replaceHookMagic*(c: PContext, n: PNode, kind: TTypeAttachedOp): PNode =
result.add boolLit
of attachedWasMoved:
result = n
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, attachedWasMoved)
if op != nil:
result[0] = newSymNode(op)
analyseIfAddressTakenInCall(c, result, false)
of attachedSink:
result = n
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let op = getAttachedOp(c.graph, t, kind)
if op != nil:
result[0] = newSymNode(op)
result = c.semAsgnOpr(c, n, nkSinkAsgn)
of attachedAsgn:
result = n
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let op = getAttachedOp(c.graph, t, kind)
if op != nil:
result[0] = newSymNode(op)
result = c.semAsgnOpr(c, n, nkAsgn)
of attachedDeepCopy:
result = n
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let t = n[1].typ.skipTypes(abstractVar)
let op = getAttachedOp(c.graph, t, kind)
if op != nil:
result[0] = newSymNode(op)

View File

@@ -333,7 +333,7 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass:
return false
let conf = c.config
if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc}:
if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
let d = skipTypes(dst, abstractInst)
let s = skipTypes(src, abstractInst)
if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal:
@@ -578,14 +578,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
if efExplain in flags:
m.diagnostics = @[]
m.diagnosticsEnabled = true
let rel = typeRel(m, t2, t1)
res = rel >= isSubtype # isNone
if res and rel == isEqual and
not compareTypes(t1, t2,
flags = {ExactTypeDescValues,
PickyCAliases,
PickyBackendAliases}):
res = false
res = typeRel(m, t2, t1) >= isSubtype # isNone
# `res = sameType(t1, t2)` would be wrong, e.g. for `int is (int|float)`
result = newIntNode(nkIntLit, ord(res))
@@ -659,9 +652,6 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode =
result = semExpr(c, result, flags = {efNoUndeclared})
proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
template isViewTarget(t: PType): bool =
t.skipTypes({tyGenericInst, tyAlias, tySink}).kind in {tyVar, tyLent}
case n.kind
of nkCurly:
for i in 0..<n.len:
@@ -690,15 +680,12 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
if f == nil:
globalError(c.config, m.info, "unknown identifier: " & m.sym.name.s)
return
if not isViewTarget(f.typ):
changeType(c, n[i][1], f.typ, check)
changeType(c, n[i][1], f.typ, check)
else:
if not isViewTarget(tup[i]):
changeType(c, n[i][1], tup[i], check)
changeType(c, n[i][1], tup[i], check)
else:
for i in 0..<n.len:
if not isViewTarget(tup[i]):
changeType(c, n[i], tup[i], check)
changeType(c, n[i], tup[i], check)
when false:
var m = n[i]
var a = newNodeIT(nkExprColonExpr, m.info, newType[i])
@@ -721,7 +708,6 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
localError(c.config, n.info, "cannot convert '" & n.sym.name.s &
"' to '" & typeNameAndDesc(newType) & "'")
else: discard
n.typ = newType
proc arrayConstrType(c: PContext, n: PNode): PType =
@@ -827,7 +813,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
inc(lastIndex)
if isGeneric:
for i in 0..<result.len:
if result[i].typ != nil and isIntLit(result[i].typ):
if isIntLit(result[i].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i].typ = nil
result.typ = nil # current result.typ is invalid, index type is nil
@@ -846,6 +832,9 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
proc fixAbstractType(c: PContext, n: PNode) =
for i in 1..<n.len:
let it = n[i]
if it == nil:
localError(c.config, n.info, "'$1' has nil child at index $2" % [renderTree(n, {renderNoComments}), $i])
return
# do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it:
if it.kind == nkHiddenSubConv and
skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
@@ -977,15 +966,12 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
# echo "SUCCESS evaluated at compile time: ", call.renderTree
proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
let oldErrorCount = c.config.errorCounter
inc c.inStaticContext
openScope(c)
let a = semExprWithType(c, n, expectedType = expectedType)
closeScope(c)
dec c.inStaticContext
if a.findUnresolvedStatic != nil or
c.config.errorCounter != oldErrorCount:
return a
if a.findUnresolvedStatic != nil: return a
result = evalStaticExpr(c.module, c.idgen, c.graph, a, c.p.owner)
if result.isNil:
localError(c.config, n.info, errCannotInterpretNodeX % renderTree(n))
@@ -996,7 +982,7 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags; expectedType: PType = nil): PNode =
if flags*{efInTypeof, efWantIterator, efWantIterable, efPreferIteratorForIterable} != {}:
if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
# consider: 'for x in pReturningArray()' --> we don't want the restriction
# to 'skIterator' anymore; skIterator is preferred in sigmatch already
# for typeof support.
@@ -1023,8 +1009,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
# See bug #2051:
result[0] = newSymNode(errorSym(c, n))
elif callee.kind == skIterator:
if result.typ.kind != tyIterable and
flags * {efWantIterable, efPreferIteratorForIterable} != {}:
if efWantIterable in flags:
let typ = newTypeS(tyIterable, c)
rawAddSon(typ, result.typ)
result.typ = typ
@@ -1170,7 +1155,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
localError(c.config, n.info, msg)
return errorNode(c, n)
else:
result = compactVoidArgs(m.call)
result = m.call
instGenericConvertersSons(c, result, m)
markConvertersUsed(c, result)
@@ -1543,7 +1528,7 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
return
# extra flags since LHS may become a call operand:
n[0] = semExprWithType(c, n[0], flags + {efDetermineType, efWantIterable, efAllowSymChoice})
n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice})
#restoreOldStyleType(n[0])
var i = considerQuotedIdent(c, n[1], n)
var ty = n[0].typ
@@ -1676,9 +1661,6 @@ proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode =
n[0] = a
result = n
var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned})
if t.kind == tyTypeDesc:
localError(c.config, n.info, "missing generic parameter")
return nil
case t.kind
of tyRef, tyPtr: n.typ = t.elementType
of tyMetaTypes, tyFromExpr:
@@ -2818,7 +2800,7 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
expectedElementType = typ
if isGeneric:
for i in 0..<n.len:
if n[i].typ != nil and isIntLit(n[i].typ):
if isIntLit(n[i].typ):
# generic instantiation strips int lit type which makes conversions fail
n[i].typ = nil
result.add n[i]
@@ -2931,7 +2913,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
result.add n[i]
if isGeneric:
for i in 0..<result.len:
if result[i][1].typ != nil and isIntLit(result[i][1].typ):
if isIntLit(result[i][1].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i][1].typ = nil
result.typ = makeTypeFromExpr(c, result.copyTree)
@@ -2972,7 +2954,7 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
if isGeneric:
for i in 0..<result.len:
if result[i].typ != nil and isIntLit(result[i].typ):
if isIntLit(result[i].typ):
# generic instantiation strips int lit type which makes conversions fail
result[i].typ = nil
result.typ = makeTypeFromExpr(c, result.copyTree)
@@ -3031,9 +3013,9 @@ proc semExportExcept(c: PContext, n: PNode): PNode =
proc semExport(c: PContext, n: PNode): PNode =
proc specialSyms(c: PContext; s: PSym) {.inline.} =
if s.kind == skConverter: addConverter(c, s)
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
elif s.kind == skType and s.typ != nil and s.typ.kind == tyEnum and sfPure in s.flags:
addPureEnum(c, s)
addPureEnum(c, LazySym(sym: s))
result = newNodeI(nkExportStmt, n.info)
for i in 0..<n.len:

View File

@@ -81,8 +81,7 @@ proc sameInstantiation(a, b: TInstantiation): bool =
if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
flags = {ExactTypeDescValues,
ExactGcSafety,
PickyCAliases,
PickyBackendAliases}): return
PickyCAliases}): return
result = true
else:
result = false

View File

@@ -35,9 +35,7 @@ proc semAddr(c: PContext; n: PNode): PNode =
let x = semExprWithType(c, n)
if x.kind == nkSym:
x.sym.flagsImpl.incl(sfAddrTaken)
let aa = isAssignable(c, x)
if aa notin {arLValue, arLocalLValue, arAddressableConst, arLentValue} and
(aa != arDiscriminant or c.inUncheckedAssignSection <= 0):
if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
localError(c.config, n.info, errExprHasNoAddress)
result.add x
result.typ = makePtrType(c, x.typ.skipTypes({tySink}))
@@ -234,9 +232,10 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
of "stripGenericParams":
result = uninstantiate(operand).toNode(traitCall.info)
of "supportsCopyMem":
result = newIntNodeT(toInt128(ord(supportsCopyMem(operand))), traitCall, c.idgen, c.graph)
of "canFormCycles":
result = newIntNodeT(toInt128(ord(types.canFormAcycle(c.graph, operand))), traitCall, c.idgen, c.graph)
let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
let complexObj = containsGarbageCollectedRef(t) or
hasDestructor(t)
result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph)
of "hasDefaultValue":
result = newIntNodeT(toInt128(ord(not operand.requiresInit)), traitCall, c.idgen, c.graph)
of "isNamedTuple":
@@ -248,13 +247,10 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
assert operand.kind == tyTuple, $operand.kind
result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph)
of "distinctBase":
var arg = operand.skipTypes(skippedTypes)
var arg = operand.skipTypes({tyGenericInst})
let rec = semConstExpr(c, traitCall[2]).intVal != 0
while true:
let distinctArg = arg.skipTypes(skippedTypes + {tyGenericInst})
if distinctArg.kind != tyDistinct:
break
arg = distinctArg.base.skipTypes(skippedTypes)
while arg.kind == tyDistinct:
arg = arg.base.skipTypes(skippedTypes + {tyGenericInst})
if not rec: break
result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
of "rangeBase":
@@ -618,9 +614,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mAsgn:
case n[0].sym.name.s
of "=", "=copy":
result = replaceHookMagic(c, n, attachedAsgn)
result = semAsgnOpr(c, n, nkAsgn)
of "=sink":
result = replaceHookMagic(c, n, attachedSink)
result = semAsgnOpr(c, n, nkSinkAsgn)
else:
result = semShallowCopy(c, n, flags)
of mIsPartOf: result = semIsPartOf(c, n, flags)

View File

@@ -486,11 +486,6 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
# we have to watch out, there are also 'owned proc' types that can be used
# multiple times as long as they don't have closures.
result.typ.incl tfHasOwned
if t.kind == tyForward and efDetermineType in flags:
# a forward object type does not error during determine-type analysis;
# it now stays unresolved long enough for the existing delayed field-default pass to resolve it after the type section finishes.
result.typ = t
return result
if t.kind != tyObject:
return localErrorNode(c, result, if t.kind != tyGenericBody:
"object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t))

View File

@@ -84,8 +84,6 @@ type
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
isInnerProc: bool
inEnforcedNoSideEffects: bool
isArrayIndexing: bool
currentExceptType: PType
unknownRaises: seq[(PSym, TLineInfo)]
currOptions: TOptions
optionsStack: seq[(TOptions, TNoteKinds)]
@@ -149,49 +147,6 @@ proc isLocalSym(a: PEffects, s: PSym): bool =
s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
sfGlobal notin s.flags and s.owner == a.owner
proc isRangeSupertype(conf: ConfigRef; wider, narrower: PType): bool =
## Check if `wider` type fully contains `narrower` type
## Returns true if narrower fits entirely within wider (safe conversion)
if wider.isOrdinalType:
let wideFirst = firstOrd(conf, wider)
let wideLast = lastOrd(conf, wider)
let narrowFirst = firstOrd(conf, narrower)
let narrowLast = lastOrd(conf, narrower)
result = narrowFirst >= wideFirst and narrowLast <= wideLast
elif not narrower.isOrdinalType:
let wideFirst = firstFloat(wider)
let wideLast = lastFloat(wider)
let narrowFirst = firstFloat(narrower)
let narrowLast = lastFloat(narrower)
result = narrowFirst >= wideFirst and narrowLast <= wideLast
else:
# int -> float ranges; warn
result = false
proc shouldWarnRangeConversion(conf: ConfigRef; info: TLineInfo; formalType, argType: PType): bool =
## Determine if an implicit range conversion should warn
## We warn on conversions that are likely to cause panics
let f = formalType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
let a = argType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
if f.kind == tyRange:
# Only warn if formal range doesn't fully contain argument range
# Check if the ranges don't perfectly overlap
if a.kind == tyInt and f.sym != nil and f.sym.owner != nil and
sfSystemModule in f.sym.owner.flags and
(f.sym.name.s == "Positive" or
f.sym.name.s == "Natural"):
# Positive and Natural are special cases that we do not warn on with
# ImplicitRangeConversion, but may warn on with systemRangeConversion
# if that warning is enabled.
if conf.hasWarn(warnSystemRangeConversion):
message(conf, info, warnSystemRangeConversion,
typeToString(argType) & " -> " & typeToString(formalType))
result = false
else:
result = not isRangeSupertype(conf, f, a)
else:
result = false
proc lockLocations(a: PEffects; pragma: PNode) =
if pragma.kind != nkExprColonExpr:
localError(a.config, pragma.info, "locks pragma without argument")
@@ -622,25 +577,11 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
let b = n[i]
if b.kind == nkExceptBranch:
setLen(tracked.init, oldState)
# If this except branch catches exactly one type, record it so an
# empty `raise` inside the branch can be inferred as re-raising that
# specific exception type instead of the generic `Exception`.
var savedExcept: PType = tracked.currentExceptType
var inferredExcept: PType = nil
if b.len == 2:
if b[0].isInfixAs():
assert(b[0][1].kind == nkType)
inferredExcept = b[0][1].typ
else:
assert(b[0].kind == nkType)
inferredExcept = b[0].typ
tracked.currentExceptType = inferredExcept
for j in 0..<b.len - 1:
if b[j].isInfixAs(): # skips initialization checks
assert(b[j][2].kind == nkSym)
tracked.init.add b[j][2].sym.id
track(tracked, b[^1])
tracked.currentExceptType = savedExcept
for i in oldState..<tracked.init.len:
addToIntersection(inter, tracked.init[i], bsNone)
else:
@@ -809,10 +750,6 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
markSideEffect(tracked, a, n.info)
let paramType = if formals != nil and argIndex < formals.signatureLen: formals[argIndex] else: nil
if paramType != nil and paramType.kind in {tyVar}:
let arg = n.skipAddr()
if isSsoStringIndex(tracked.config, arg):
localError(tracked.config, arg.info,
"expression '$1' is immutable, not 'var'" % renderNotLValue(arg))
invalidateFacts(tracked.guards, n)
if n.kind == nkSym and isLocalSym(tracked, n.sym):
makeVolatile(tracked, n.sym)
@@ -1168,7 +1105,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
var (isHook, opKind) = findHookKind(a.sym.name.s)
if isHook:
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar, tySink})
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != getAttachedOp(tracked.graph, t, opKind):
createTypeBoundOps(tracked, t, n.info, explicit = true)
# replace builtin hooks with lifted ones
@@ -1327,14 +1264,7 @@ proc track(tracked: PEffects, n: PNode) =
# A `raise` with no arguments means we're going to re-raise the exception
# being handled or, if outside of an `except` block, a `ReraiseDefect`.
# Here we add a `Exception` tag in order to cover both the cases.
if tracked.currentExceptType != nil:
var en = newNode(nkType)
en.typ = tracked.currentExceptType
en.info = n.info
addRaiseEffect(tracked, en, nil)
createTypeBoundOps(tracked, tracked.currentExceptType, n.info)
else:
addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
of nkCallKinds:
trackCall(tracked, n)
of nkDotExpr:
@@ -1552,12 +1482,6 @@ proc track(tracked: PEffects, n: PNode) =
message(tracked.config, n.info, warnPtrToCstringConv,
$n[1].typ)
# Check for implicit range conversions
if n.kind == nkHiddenStdConv and (not tracked.isArrayIndexing) and
n[1].kind notin {nkCharLit..nkUInt64Lit, nkFloatLit..nkFloat128Lit} and
shouldWarnRangeConversion(tracked.config, n.info, n.typ, n[1].typ):
message(tracked.config, n.info, warnImplicitRangeConversion,
typeToString(n[1].typ) & " -> " & typeToString(n.typ))
let t = n.typ.skipTypes(abstractInst)
if t.kind == tyEnum:
@@ -1596,12 +1520,7 @@ proc track(tracked: PEffects, n: PNode) =
checkBounds(tracked, n[0], n[1])
track(tracked, n[0])
dec tracked.leftPartOfAsgn
for i in 1 ..< n.len:
if i == 1:
tracked.isArrayIndexing = true
track(tracked, n[i])
if i == 1:
tracked.isArrayIndexing = false
for i in 1 ..< n.len: track(tracked, n[i])
inc tracked.leftPartOfAsgn
of nkError:
localError(tracked.config, n.info, errorToString(tracked.config, n))
@@ -1773,7 +1692,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
let param = params[i].sym
let typ = param.typ
if isSinkTypeForParam(typ) or
(t.config.selectedGC in {gcArc, gcOrc, gcYrc, gcAtomicArc} and
(t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
createTypeBoundOps(t, typ, param.info)
if isOutParam(typ) and param.id notin t.init and s.magic == mNone:

View File

@@ -1096,12 +1096,7 @@ proc symForVar(c: PContext, n: PNode): PSym =
proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
result = n
let iterBase = n[^2].typ
let iterType =
if iterBase.kind == tyIterable:
iterBase.skipModifier
else:
skipTypes(iterBase, {tyAlias, tySink, tyOwned})
var iter = skipTypes(iterType, {tyGenericInst})
var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink, tyOwned})
var iterAfterVarLent = iter.skipTypes({tyGenericInst, tyAlias, tyLent, tyVar})
# n.len == 3 means that there is one for loop variable
# and thus no tuple unpacking:
@@ -1134,9 +1129,10 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
else:
var v = symForVar(c, n[0])
if getCurrOwner(c).kind == skModule: incl(v, sfGlobal)
# Use `iterType` here: it removes outer `tyIterable` / alias-like wrappers
# from the loop source, but still preserves `tyGenericInst` for the loop var.
v.typ = iterType
# BUGFIX: don't use `iter` here as that would strip away
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
# for an example:
v.typ = iterBase
n[0] = newSymNode(v)
if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v)
elif v.owner == nil: setOwner(v, getCurrOwner(c))
@@ -1200,14 +1196,14 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
c.p.breakInLoop = oldBreakInLoop
dec(c.p.nestedLoopCounter)
proc implicitIterator(c: PContext, it: string, arg: PNode, flags: TExprFlags): PNode =
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
result = newNodeI(nkCall, arg.info)
result.add(newIdentNode(getIdent(c.cache, it), arg.info))
if arg.typ != nil and arg.typ.kind in {tyVar, tyLent}:
result.add newDeref(arg)
else:
result.add arg
result = semExprNoDeref(c, result, flags + {efWantIterator})
result = semExprNoDeref(c, result, {efWantIterator})
proc isTrivalStmtExpr(n: PNode): bool =
for i in 0..<n.len-1:
@@ -1293,8 +1289,7 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
if result != nil: return result
openScope(c)
result = n
let iteratorFlags = flags * {efPreferIteratorForIterable}
n[^2] = semExprNoDeref(c, n[^2], iteratorFlags + {efWantIterator})
n[^2] = semExprNoDeref(c, n[^2], {efWantIterator})
var call = n[^2]
if call.kind == nkStmtListExpr and (isTrivalStmtExpr(call) or (call.lastSon.kind in nkCallKinds and call.lastSon[0].sym.kind == skIterator)):
@@ -1314,16 +1309,14 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
elif not isCallExpr or call[0].kind != nkSym or
call[0].sym.kind != skIterator:
if n.len == 3:
n[^2] = implicitIterator(c, "items", n[^2], iteratorFlags)
n[^2] = implicitIterator(c, "items", n[^2])
elif n.len == 4:
n[^2] = implicitIterator(c, "pairs", n[^2], iteratorFlags)
n[^2] = implicitIterator(c, "pairs", n[^2])
else:
localError(c.config, n[^2].info, "iterator within for loop context expected")
result = semForVars(c, n, flags)
else:
result = semForVars(c, n, flags)
if n[^2].typ != nil and n[^2].typ.kind == tyIterable:
n[^2].typ = n[^2].typ.skipModifier
# propagate any enforced VoidContext:
if n[^1].typ == c.enforceVoidContext:
result.typ = c.enforceVoidContext
@@ -1808,35 +1801,15 @@ proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
internalAssert c.config, false
proc typeSectionFinalPass(c: PContext, n: PNode) =
# each top level type needs to be processed, each epoch should reify at least one
var remainingOwners = initIntSet()
for (owner, _, _) in c.forwardTypeUpdates:
remainingOwners.incl owner.id
while c.forwardTypeUpdates.len > 0:
let pending = move c.forwardTypeUpdates
var madeProgress = false
for (owner, typ, typeNode) in pending:
# types that need to be updated due to containing forward types
# and their corresponding type nodes
# for example generic invocations of forward types end up here
var reified = semTypeNode(c, typeNode, nil)
assert reified != nil
assignType(typ, reified)
typ.itemId = reified.itemId # same id
if containsForwardType(typ):
c.forwardTypeUpdates.add (owner, typ, typeNode)
elif not remainingOwners.missingOrExcl(owner.id):
madeProgress = true
if not madeProgress:
# can't error here unfortunately
break
for (owner, field, expectedType) in c.forwardFieldUpdates:
semDelayedFieldDefault(c, owner, expectedType, field)
c.forwardFieldUpdates = @[]
for (typ, typeNode) in c.forwardTypeUpdates:
# types that need to be updated due to containing forward types
# and their corresponding type nodes
# for example generic invocations of forward types end up here
var reified = semTypeNode(c, typeNode, nil)
assert reified != nil
assignType(typ, reified)
typ.itemId = reified.itemId # same id
c.forwardTypeUpdates = @[]
for i in 0..<n.len:
var a = n[i]
if a.kind == nkCommentStmt: continue
@@ -1872,13 +1845,6 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
let baseType = s.typ.safeSkipTypes(abstractPtrs)
if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil:
checkForMetaFields(c, baseType.n, hasError)
if s.typ.kind in {tySet, tyArray, tySequence, tyUncheckedArray} and s.typ.elementType.kind == tyNone:
# magic generics are not filled but tyNone is added to its elements by default,
# we lift them to tyBuiltInTypeClass here
s.typ = newTypeS(tyBuiltInTypeClass, c,
newTypeS(s.typ.kind, c))
if not hasError:
checkConstructedType(c.config, s.info, s.typ)
#instAllTypeBoundOp(c, n.info)
@@ -2202,7 +2168,7 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
template notRefc: bool =
# fixes refc with non-var destructor; cancel warnings (#23156)
c.config.backend == backendJs or
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcYrc}
c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}
let cond = case op
of attachedWasMoved:
t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
@@ -2579,9 +2545,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if not hasProto:
implicitPragmas(c, s, n.info, validPragmas)
if {sfError, sfExportc} * s.flags == {sfError, sfExportc}:
localError(c.config, n.info, "{.error.} and {.exportc.} pragmas are incompatible")
if n[pragmasPos].kind != nkEmpty and sfBorrow notin s.flags:
setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s)
s.typ.incl tfEffectSystemWorkaround
@@ -2813,7 +2776,7 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
var t = s.typ
if t.returnType == nil: localError(c.config, n.info, errXNeedsReturnType % "converter")
if t.len != 2: localError(c.config, n.info, "a converter takes exactly one argument")
addConverterDef(c, s)
addConverterDef(c, LazySym(sym: s))
proc semMacroDef(c: PContext, n: PNode): PNode =
result = semProcAux(c, n, skMacro, macroPragmas)
@@ -2936,15 +2899,13 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
proc semStaticStmt(c: PContext, n: PNode): PNode =
#echo "semStaticStmt"
#writeStackTrace()
let oldErrorCount = c.config.errorCounter
inc c.inStaticContext
openScope(c)
let a = semStmt(c, n[0], {})
closeScope(c)
dec c.inStaticContext
n[0] = a
if c.config.errorCounter == oldErrorCount:
evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
evalStaticStmt(c.module, c.idgen, c.graph, a, c.p.owner)
when false:
# for incremental replays, keep the AST as required for replays:
result = n

View File

@@ -925,4 +925,4 @@ proc semPattern(c: PContext, n: PNode; s: PSym): PNode =
elif result.len == 0:
localError(c.config, n.info, "a pattern cannot be empty")
closeScope(c)
addPattern(c, s)
addPattern(c, LazySym(sym: s))

View File

@@ -210,7 +210,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
)
if isPure and sfExported in result.sym.flags:
addPureEnum(c, result.sym)
addPureEnum(c, LazySym(sym: result.sym))
if tfNotNil in e.typ.flags and not hasNull:
result.incl tfRequiresInit
setToStringProc(c.graph, result, genEnumToStrProc(result, n.info, c.graph, c.idgen))
@@ -223,7 +223,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
if base.kind notin {tyGenericParam, tyGenericInvocation}:
if base.kind == tyForward:
c.forwardTypeUpdates.add (getCurrOwner(c), result, n)
c.forwardTypeUpdates.add (base, n[1])
elif not isOrdinalType(base, allowEnumWithHoles = true):
localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
elif lengthOrd(c.config, base) > MaxSetElements:
@@ -318,62 +318,6 @@ proc fitDefaultNode(c: PContext, n: var PNode, expectedType: PType) =
typeAllowedCheck(c, n.info, n.typ, skConst, {taProcContextIsNotMacro, taIsDefaultField})
dec c.inStaticContext
proc containsForwardTypeAux(t: PType; seen: var IntSet): bool
proc containsForwardTypeAux(n: PNode; seen: var IntSet): bool =
result = false
if n.isNil or n.kind in nkLiterals + {nkNilLit, nkEmpty, nkType}:
return
if containsForwardTypeAux(n.typ, seen) or
(n.kind == nkSym and n.sym.typ != n.typ and containsForwardTypeAux(n.sym.typ, seen)):
return true
for i in 0 ..< n.safeLen:
if containsForwardTypeAux(n[i], seen):
return true
proc containsForwardTypeAux(t: PType; seen: var IntSet): bool =
result = false
if t.isNil:
return
if t.kind == tyForward:
return true
if not containsOrIncl(seen, t.id):
if containsForwardTypeAux(t.n, seen):
return true
for i in 0 ..< t.len:
if containsForwardTypeAux(t[i], seen):
return true
proc containsForwardType(arg: PNode): bool =
var seen = initIntSet()
containsForwardTypeAux(arg, seen)
proc containsForwardType(t: PType): bool =
var seen = initIntSet()
containsForwardTypeAux(t, seen)
proc semFieldDefault(c: PContext; owner, expectedType: PType; field: PNode): PType =
result = expectedType
field[^1] = semExprWithType(c, field[^1], {efDetermineType, efAllowSymChoice}, result)
if result == nil:
result = field[^1].typ
if c.inGenericContext == 0:
if containsForwardType(field[^1]):
c.forwardFieldUpdates.add (owner, field, result)
else:
fitDefaultNode(c, field[^1], result)
result = field[^1].typ.skipIntLit(c.idgen)
propagateToOwner(owner, result)
proc semDelayedFieldDefault(c: PContext; owner, expectedType: PType; field: PNode) =
resetSemFlag(field[^1])
fitDefaultNode(c, field[^1], expectedType)
propagateToOwner(owner, field[^1].typ.skipIntLit(c.idgen))
proc isRecursiveType*(t: PType): bool =
# handle simple recusive types before typeFinalPass
var cycleDetector = initIntSet()
@@ -606,7 +550,13 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
var hasDefaultField = a[^1].kind != nkEmpty
if hasDefaultField:
typ = if a[^2].kind != nkEmpty: semTypeNode(c, a[^2], nil) else: nil
typ = semFieldDefault(c, result, typ, a)
if c.inGenericContext > 0:
a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = a[^1].typ
else:
fitDefaultNode(c, a[^1], typ)
typ = a[^1].typ.skipIntLit(c.idgen)
elif a[^2].kind != nkEmpty:
typ = semTypeNode(c, a[^2], nil)
if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
@@ -972,7 +922,14 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
var hasDefaultField = n[^1].kind != nkEmpty
if hasDefaultField:
typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
typ = semFieldDefault(c, rectype, typ, n)
if c.inGenericContext > 0:
n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ)
if typ == nil:
typ = n[^1].typ
else:
fitDefaultNode(c, n[^1], typ)
typ = n[^1].typ.skipIntLit(c.idgen)
propagateToOwner(rectype, typ)
elif n[^2].kind == nkEmpty:
localError(c.config, n.info, errTypeExpected)
typ = errorType(c)
@@ -1057,7 +1014,7 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int,
obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool =
if ((not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags)) or
(innerObj != nil and obj.id == innerObj.id):
(innerObj != nil and obj.sym.id == innerObj.sym.id):
localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'")
result = false
elif obj.kind == tyObject:
@@ -1115,7 +1072,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
if needsForwardUpdate:
# if the inherited object is a forward type,
# the entire object needs to be checked again
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) # we retry in the final pass
c.forwardTypeUpdates.add (result, n) # we retry in the final pass
rawAddSon(result, realBase)
if realBase == nil and tfInheritable in flags:
result.incl tfInheritable
@@ -1192,7 +1149,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
result = t
else: discard
if result.kind == tyRef and
c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc} and
c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
tfTriggersCompileTime notin result.flags:
result.incl tfHasAsgn
@@ -1246,15 +1203,7 @@ proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent;
# is this a bindOnce type class already present in the param list?
for i in 0..<genericParams.len:
if genericParams[i].sym.name.id == finalTypId.id:
if typeClass.kind == tyStatic and genericParams[i].typ.kind != tyStatic:
# The base type (e.g. from `auto`) was already added as a generic param,
# but `static[auto]` requires upgrading it to a `tyStatic` wrapper so
# it is instantiated as a compile-time value (`skConst`).
genericParams[i].sym.linkTo(typeClass)
typeClass.incl tfImplicitTypeParam
return typeClass
else:
return genericParams[i].typ
return genericParams[i].typ
let owner = if typeClass.sym != nil: typeClass.sym
else: getCurrOwner(c)
@@ -1763,7 +1712,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
for i in 1..<n.len:
var elem = semGenericParamInInvocation(c, n[i])
addToResult(elem, true)
c.forwardTypeUpdates.add (getCurrOwner(c), result, n)
c.forwardTypeUpdates.add (result, n)
return
elif t.kind != tyGenericBody:
# we likely got code of the form TypeA[TypeB] where TypeA is
@@ -1790,7 +1739,6 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
var isConcrete = true
let rType = m.call[0].typ
let mIndex = if rType != nil: rType.len - 1 else: -1
var hasForwardTypeParam = false
for i in 1..<m.call.len:
var typ = m.call[i].typ
# is this a 'typedesc' *parameter*? If so, use the typedesc type,
@@ -1807,40 +1755,13 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
skip = false
addToResult(typ, skip)
if typ.kind == tyForward:
hasForwardTypeParam = true
if isConcrete:
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
# XXX: What kind of error is this? is it still relevant?
localError(c.config, n.info, errCannotInstantiateX % s.name.s)
result = newOrPrevType(tyError, prev, c)
elif containsGenericInvocationWithForward(n[0]) or hasForwardTypeParam:
# isConcrete == false means this generic type is not instanciated here because
# it invoked with generic parameters.
# Even if isConcrete == true, don't instanciate it now if there are
# unresolved `tyForward` type params.
# Such `tyForward` type params will be semchecked later and we can
# instanciate this next time.
# Some generic types like std/options.Option[T] need the kind of the
# given type argument before their fields can be resolved.
# return `tyForward` instead of `tyGenericInvocation` because:
# ```nim
# type Foo = object
# x: Option[Foo]
# ```
# returning `tyGenericInvocation` makes `Option[Foo]` to `tyGenericInvocation` and
# next time `semGeneric` is called with `Option[Foo]`, containsGenericType(typeof(`Foo`)) == true
# and `isConcrete == false`.
if prev == nil:
result = newTypeS(tyForward, c)
result.sym = s
else:
assignType(result, newTypeS(tyForward, c))
result.sym = s
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) #fixes 1500
return
elif containsGenericInvocationWithForward(n[0]):
c.forwardTypeUpdates.add (result, n) #fixes 1500
else:
result = instGenericContainer(c, n.info, result,
allowMetaTypes = false)
@@ -2128,9 +2049,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym =
# proc signature for example
if c.inGenericInst > 0:
let bound = result.typ.elementType.sym
# the symbol may still point to the uninstantiated generic body type
if bound != nil and bound.typ == result.typ.elementType:
return bound
if bound != nil: return bound
return result
if result.typ.sym == nil:
localError(c.config, n.info, errTypeExpected)
@@ -2281,9 +2200,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = semAnyRef(c, n, tyPtr, prev)
elif op.id == ord(wRef):
result = semAnyRef(c, n, tyRef, prev)
elif op.id == ord(wStatic):
checkSonsLen(n, 2, c.config)
result = semStaticType(c, n[1], prev)
elif op.id == ord(wType):
checkSonsLen(n, 2, c.config)
result = semTypeOf(c, n[1], prev)
@@ -2303,8 +2219,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else:
result = semTypeNode(c, whenResult, prev)
of nkBracketExpr:
# Actually len >= 2 is required, but it doesn't print errors nicely with empty brackets
checkMinSonsLen(n, 1, c.config)
checkMinSonsLen(n, 2, c.config)
var head = n[0]
var s = if head.kind notin nkCallKinds: semTypeIdent(c, head)
else: symFromExpectedTypeNode(c, semExpr(c, head))
@@ -2322,21 +2237,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
incl result, tfHasAsgn
of mVarargs: result = semVarargs(c, n, prev)
of mTypeDesc, mType, mTypeOf:
if n.len != 2:
let name = case s.magic:
of mTypeDesc: "typedesc"
of mType: "type"
of mTypeOf: "typeof"
else: ""
localError(c.config, n.info, errXExpectsOneTypeParam % name)
else:
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
result.incl tfExplicit
result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
result.incl tfExplicit
of mStatic:
if n.len != 2:
localError(c.config, n.info, errXExpectsOneTypeParam % "static")
else:
result = semStaticType(c, n[1], prev)
result = semStaticType(c, n[1], prev)
of mExpr:
result = semTypeNode(c, n[0], nil)
if result != nil:
@@ -2346,11 +2250,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
for i in 1..<n.len:
result.rawAddSon(semTypeNode(c, n[i], nil))
of mDistinct:
checkSonsLen(n, 2, c.config)
result = newOrPrevType(tyDistinct, prev, c)
addSonSkipIntLit(result, semTypeNode(c, n[1], nil), c.idgen)
of mVar:
checkSonsLen(n, 2, c.config)
result = newOrPrevType(tyVar, prev, c)
var base = semTypeNode(c, n[1], nil)
if base.kind in {tyVar, tyLent}:
@@ -2381,7 +2283,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
else:
result = typeExpr.typ.base
if result.isMetaType and
result.kind notin tyTypeClasses:
result.kind != tyUserTypeClass:
# the dot expression may refer to a concept type in
# a different module. allow a normal alias then.
let preprocessed = semGenericStmt(c, n)
@@ -2474,7 +2376,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
if n.kind == nkIteratorTy and result.kind == tyProc:
result.incl(tfIterator)
if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc}:
if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
result.incl tfHasAsgn
of nkEnumTy: result = semEnum(c, n, prev)
of nkType: result = n.typ
@@ -2528,7 +2430,7 @@ proc processMagicType(c: PContext, m: PSym) =
of mString:
setMagicType(c.config, m, tyString, szUncomputedSize)
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
if optSeqDestructors in c.config.globalOptions:
if optSeqDestructors in c.config.globalOptions or c.config.selectedGC == gcRefc:
incl m.typ, tfHasAsgn
of mCstring:
setMagicIntegral(c.config, m, tyCstring, c.config.target.ptrSize)

View File

@@ -52,8 +52,7 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
for j in FirstGenericParamAt..<key.kidsLen:
# XXX sameType is not really correct for nested generics?
if not compareTypes(inst[j], key[j],
flags = {ExactGenericParams, PickyCAliases,
PickyBackendAliases}):
flags = {ExactGenericParams, PickyCAliases}):
break matchType
return inst
@@ -374,7 +373,6 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
var g: G[string]
]#
# XXX FIXME This causes system.Natural to be duplicated during compilation of system.nim as cl.owner == nil!
result = copySym(s, cl.c.idgen)
incl(result.flagsImpl, sfFromGeneric)
#idTablePut(cl.symMap, s, result)
@@ -564,26 +562,6 @@ proc eraseVoidParams*(t: PType) =
setLen t.n.sons, pos
break
proc eraseTupleVoidFields*(t: PType) =
## Remove void fields from a named tuple type, compacting both `t.n`
## (the field symbol nodes) and `t.sonsImpl` (the child types).
if t.n == nil: return # anonymous tuple, nothing to compact
for i in 0..<t.kidsLen:
if t.n[i].kind == nkRecList or t[i].kind == tyVoid:
# found first void field, compact from here
var pos = i
for j in i+1..<t.kidsLen:
if t[j].kind != tyVoid and j < t.n.len and t.n[j].kind != nkRecList:
t.n[pos] = t.n[j]
t[pos] = t[j]
if t.n[pos].kind == nkSym:
t.n[pos].sym.position = pos
inc pos
# else: skip void entries
setLen t.n.sons, pos
t.setSonsLen pos
break
proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) =
for i, p in t.ikids:
if p == nil: continue
@@ -789,8 +767,6 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
propagateFieldFlags(result, result.n)
if result.kind == tyObject and cl.c.computeRequiresInit(cl.c, result):
result.incl tfRequiresInit
if result.kind == tyTuple:
eraseTupleVoidFields(result)
of tyProc:
eraseVoidParams(result)

View File

@@ -41,7 +41,6 @@ type
CoType
CoOwnerSig
CoIgnoreRange
CoIgnoreRangeInArray
CoConsiderOwned
CoDistinct
CoHashTypeInsideNode
@@ -155,7 +154,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
assert inst.kind == tyGenericInst
c.hashType inst.genericHead, flags, conf
for _, a in inst.genericInstParams:
c.hashType a, flags+{CoDistinct}, conf
c.hashType a, flags, conf
t.typeInstImpl = inst
return
c &= char(t.kind)
@@ -221,17 +220,10 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
else:
for a in t.kids: c.hashType a, flags+{CoIgnoreRange}, conf
of tyRange:
if {CoIgnoreRange, CoIgnoreRangeInArray} * flags == {}:
if CoIgnoreRange notin flags:
c &= char(t.kind)
c.hashTree(t.n, {}, conf)
c.hashType(t.elementType, flags, conf)
elif CoIgnoreRangeInArray in flags:
# include only the length of the range (not its specific bounds)
c &= char(t.kind)
let l = lengthOrd(conf, t)
lowlevel l
else:
c.hashType(t.elementType, flags, conf)
c.hashType(t.elementType, flags, conf)
of tyStatic:
c &= char(t.kind)
c.hashTree(t.n, {}, conf)
@@ -261,7 +253,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
if tfVarargs in t.flags: c &= ".varargs"
of tyArray:
c &= char(t.kind)
c.hashType(t.indexType, flags-{CoIgnoreRange}+{CoIgnoreRangeInArray}, conf)
c.hashType(t.indexType, flags-{CoIgnoreRange}, conf)
c.hashType(t.elementType, flags-{CoIgnoreRange}, conf)
else:
c &= char(t.kind)

View File

@@ -46,8 +46,7 @@ type
TCandidate* = object
c*: PContext
exactMatches*: int
iteratorPreference*: int # prefer iterators in iterator-oriented contexts
exactMatches*: int # also misused to prefer iters over procs
genericMatches: int # also misused to prefer constraints
subtypeMatches: int
intConvMatches: int # conversions to int are not as expensive
@@ -111,8 +110,7 @@ proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
proc initCandidateAux(ctx: PContext,
callee: PType): TCandidate {.inline.} =
result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0,
iteratorPreference: 0, convMatches: 0, intConvMatches: 0,
genericMatches: 0,
convMatches: 0, intConvMatches: 0, genericMatches: 0,
state: csEmpty, firstMismatch: MismatchInfo(),
callee: callee, call: nil, baseTypeMatch: false,
genericConverter: false, inheritancePenalty: -1
@@ -162,7 +160,8 @@ proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) =
arg = newTypeS(tyStatic, m.c, son = evaluated.typ)
arg.n = evaluated
elif formalBase.kind == tyTypeDesc:
discard # if arg is not tyTypeDesc, typeRel will report the mismatch
if arg.kind != tyTypeDesc:
arg = makeTypeDesc(m.c, arg)
else:
arg = arg.skipTypes({tyTypeDesc})
let tm = typeRel(m, formal, arg)
@@ -395,7 +394,6 @@ proc complexDisambiguation(a, b: PType): int =
proc writeMatches*(c: TCandidate) =
echo "Candidate '", c.calleeSym.name.s, "' at ", c.c.config $ c.calleeSym.info
echo " exact matches: ", c.exactMatches
echo " iterator preference: ", c.iteratorPreference
echo " generic matches: ", c.genericMatches
echo " subtype matches: ", c.subtypeMatches
echo " intconv matches: ", c.intConvMatches
@@ -414,8 +412,6 @@ proc cmpInheritancePenalty(a, b: int): int =
proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
result = a.exactMatches - b.exactMatches
if result != 0: return
result = a.iteratorPreference - b.iteratorPreference
if result != 0: return
result = a.genericMatches - b.genericMatches
if result != 0: return
result = a.subtypeMatches - b.subtypeMatches
@@ -619,8 +615,6 @@ proc isGenericObjectOf(f, a: PType): bool =
# use sym equality to check if the `tyGenericBody` types are equal
result = aRoot != nil and f.sym == aRoot.sym
proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
var t = a
assert t.kind == tyObject
@@ -784,17 +778,6 @@ proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation =
# if f is metatype.
result = typeRel(c, f, a)
if result == isEqual and
procParamTypeBackendAliases notin c.c.config.legacyFeatures:
# Ensure types that are semantically equal also match at the backend level.
# E.g. reject assigning proc(csize_t) to proc(uint) since these map to
# different C types (size_t vs unsigned long long).
let fCheck = concreteType(c, f)
let aCheck = concreteType(c, a)
if fCheck != nil and aCheck != nil and
not sameBackendTypePickyAliases(fCheck, aCheck):
result = isNone
if result <= isSubrange or inconsistentVarTypes(f, a):
result = isNone
@@ -1693,6 +1676,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
elif a.kind == tyGenericInst:
if roota.base == rootf.base:
let nextFlags = flags + {trNoCovariance}
var hasCovariance = false
# YYYY
result = isEqual
@@ -1704,7 +1688,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
if res notin {isEqual, isGeneric}:
if trNoCovariance notin flags and ff.kind == aa.kind:
let paramFlags = rootf.base[i-1].flags
let hasCovariance =
hasCovariance =
if tfCovariant in paramFlags:
if tfWeakCovariant in paramFlags:
isCovariantPtr(c, ff, aa)
@@ -1715,36 +1699,35 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
typeRel(c, aa, ff, flags) == isSubtype
if hasCovariance:
continue
result = isNone
break
if result != isNone:
if prev == nil: put(c, f, a)
return isNone
if prev == nil: put(c, f, a)
else:
let fKind = rootf.last.kind
if fKind in {tyAnd, tyOr}:
result = typeRel(c, last(f), a, flags)
if result != isNone: put(c, f, a)
return
let fKind = rootf.last.kind
if fKind in {tyAnd, tyOr}:
result = typeRel(c, last(f), a, flags)
if result != isNone: put(c, f, a)
return
var aAsObject = roota.last
var aAsObject = roota.last
if fKind in {tyRef, tyPtr}:
if aAsObject.kind == tyObject:
# bug #7600, tyObject cannot be passed
# as argument to tyRef/tyPtr
return isNone
elif aAsObject.kind == fKind:
aAsObject = aAsObject.base
if fKind in {tyRef, tyPtr}:
if aAsObject.kind == tyObject:
# bug #7600, tyObject cannot be passed
# as argument to tyRef/tyPtr
return isNone
elif aAsObject.kind == fKind:
aAsObject = aAsObject.base
if aAsObject.kind == tyObject and trIsOutParam notin flags:
let baseType = aAsObject.base
if baseType != nil:
if tfFinal notin aAsObject.flags:
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
let ret = typeRel(c, f, baseType, flags)
return if ret in {isEqual,isGeneric}: isSubtype else: ret
if aAsObject.kind == tyObject and trIsOutParam notin flags:
let baseType = aAsObject.base
if baseType != nil:
if tfFinal notin aAsObject.flags:
inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
let ret = typeRel(c, f, baseType, flags)
return if ret in {isEqual,isGeneric}: isSubtype else: ret
result = isNone
else:
assert last(origF) != nil
result = typeRel(c, last(origF), a, flags)
@@ -2201,9 +2184,9 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
result.typ = errorType(c)
else:
result.typ = f.skipTypes({tySink})
# keep varness, but don't wrap lent types with var
# keep varness
if arg.typ != nil and arg.typ.kind == tyVar:
result.typ = toVar(result.typ.skipTypes({tyLent}), tyVar, c.idgen)
result.typ = toVar(result.typ, tyVar, c.idgen)
# copy the tfVarIsPtr flag
result.typ.flags = arg.typ.flags
else:
@@ -2764,8 +2747,7 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode, newlyTyped: var bool):
result = a
elif a.typ.isNil:
if formal.kind == tyIterable:
let flags = {efDetermineType, efAllowStmt, efWantIterator, efWantIterable,
efPreferIteratorForIterable}
let flags = {efDetermineType, efAllowStmt, efWantIterator, efWantIterable}
result = c.semOperand(c, a, flags)
else:
# XXX This is unsound! 'formal' can differ from overloaded routine to
@@ -2782,20 +2764,6 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode, newlyTyped: var bool):
considerGenSyms(c, result)
if result.kind != nkHiddenDeref and result.typ.kind in {tyVar, tyLent} and c.matchedConcept == nil:
result = newDeref(result)
# Recovery for calls resolved too early as non-iterators.
# TODO: retry only skIterator overloads instead of re-semming,
# or preserve iterator-candidates info from the earlier semcheck.
if formal.kind == tyIterable and result.typ.kind != tyIterable and
a.kind in nkCallKinds and a[0].kind in {nkIdent, nkAccQuoted, nkSym, nkOpenSym}:
let recheck = copyTree(a)
recheck.typ = nil
if recheck[0].kind == nkSym and recheck[0].sym != nil:
recheck[0] = newIdentNode(recheck[0].sym.name, recheck[0].info)
let flags = {efDetermineType, efAllowStmt, efNoUndeclared,
efWantIterator, efWantIterable, efPreferIteratorForIterable}
let fresh = c.semOperand(c, recheck, flags)
if fresh.typ != nil and fresh.typ.kind == tyIterable:
return fresh
proc prepareOperand(c: PContext; a: PNode, newlyTyped: var bool): PNode =
if a.typ.isNil:
@@ -2845,11 +2813,9 @@ proc findFirstArgBlock(m: var TCandidate, n: PNode): int =
else: break
proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) =
template noMatch() =
if m.calleeSym != nil and m.calleeSym.kind notin {skTemplate, skMacro}:
c.mergeShadowScope
else:
c.closeShadowScope
c.mergeShadowScope #merge so that we don't have to resem for later overloads
m.state = csNoMatch
m.firstMismatch.arg = a
m.firstMismatch.formal = formal
@@ -3126,7 +3092,6 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
put(m, formal.typ, defaultValue.typ)
defaultValue.flags.incl nfDefaultParam
setSon(m.call, formal.position + 1, defaultValue)
# forget all inferred types if the overload matching failed
if m.state == csNoMatch:
for t in m.inferredTypes:

View File

@@ -10,7 +10,7 @@
## This module implements threadpool's ``spawn``.
import ast, types, idents, magicsys, msgs, options, modulegraphs,
lowerings, liftdestructors, renderer, trees
lowerings, liftdestructors, renderer
from trees import getMagic, getRoot
proc callProc(a: PNode): PNode =
@@ -37,7 +37,7 @@ proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
else: srFlowVar
proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind =
if c.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc}: fvBlob
if c.selectedGC in {gcArc, gcOrc, gcAtomicArc}: fvBlob
elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
elif containsGarbageCollectedRef(t): fvInvalid
else: fvBlob
@@ -53,24 +53,6 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
if t.kind in {tyVar, tyLent, tySequence}: t = t.elementType
result = not containsGarbageCollectedRef(t)
proc newSpawnMoveStmt(g: ModuleGraph; idgen: IdGenerator; le, ri: PNode): PNode =
let op = getAttachedOp(g, ri.typ.skipTypes({tyGenericInst, tyAlias, tyVar, tySink}), attachedWasMoved)
if op != nil and sfOverridden in op.flags:
result = newNodeI(nkStmtList, le.info)
result.add newFastAsgnStmt(le, ri)
let wasMovedCall = newNodeI(nkCall, ri.info)
wasMovedCall.add newSymNode(op)
if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar:
wasMovedCall.add ri.skipAddr
else:
wasMovedCall.add makeAddr(ri.skipAddr, idgen)
result.add wasMovedCall
else:
result = newFastMoveStmt(g, le, ri)
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; owner: PSym; typ: PType;
v: PNode; useShallowCopy=false): PSym =
result = newSym(skTemp, getIdent(g.cache, genPrefix), idgen, owner, varSection.info,
@@ -84,12 +66,12 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator;
vpart[2] = if varInit.isNil: v else: vpart[1]
varSection.add vpart
if varInit != nil:
if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcYrc}:
if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
# inject destructors pass will do its own analysis
varInit.add newSpawnMoveStmt(g, idgen, newSymNode(result), v)
varInit.add newFastMoveStmt(g, newSymNode(result), v)
else:
if useShallowCopy and typeNeedsNoDeepCopy(typ) or optTinyRtti in g.config.globalOptions:
varInit.add newSpawnMoveStmt(g, idgen, newSymNode(result), v)
varInit.add newFastMoveStmt(g, newSymNode(result), v)
else:
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
deepCopyCall[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))

View File

@@ -118,24 +118,6 @@ proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite
le.flags.incl nfFirstWrite
result[1] = ri
proc resolveBorrowedRoutineSym(c: PTransf; s: PSym; info: TLineInfo): PSym =
# Follow borrow aliases to the underlying implementation symbol.
var s = s
while true:
# Skips over all borrowed procs getting the last proc symbol without an implementation
let body = getBody(c.graph, s)
if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
s = body.sym
else:
break
let body = getBody(c.graph, s)
if body.kind == nkSym:
result = body.sym
else:
result = nil
internalError(c.graph.config, info, "wrong AST for borrowed symbol")
proc transformSymAux(c: PTransf, n: PNode): PNode =
let s = n.sym
if s.typ != nil and s.typ.callConv == ccClosure:
@@ -154,7 +136,17 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
var tc = c.transCon
if sfBorrow in s.flags and s.kind in routineKinds:
# simply exchange the symbol:
b = newSymNode(resolveBorrowedRoutineSym(c, s, n.info), n.info)
var s = s
while true:
# Skips over all borrowed procs getting the last proc symbol without an implementation
let body = getBody(c.graph, s)
if body.kind == nkSym and sfBorrow in body.sym.flags and getBody(c.graph, body.sym).kind == nkSym:
s = body.sym
else:
break
b = getBody(c.graph, s)
if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol")
b = newSymNode(b.sym, n.info)
elif c.inlining > 0:
# see bug #13596: we use ref-based equality in the DFA for destruction
# injections so we need to ensure unique nodes after iterator inlining
@@ -336,7 +328,7 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode =
if a.kind == nkSym:
n[1] = transformSymAux(c, a)
return n
of nkLambdaKinds, nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects?
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: # todo optimize nosideeffects?
result = newTransNode(n)
let x = newSymNode(copySym(n[namePos].sym, c.idgen))
c.transCon.mapping[n[namePos].sym.itemId] = x
@@ -702,11 +694,6 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
of nkAddr, nkHiddenAddr:
result = putArgInto(arg[0], formal)
if result == paViaIndirection: result = paFastAsgn
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
if compareTypes(arg.typ, arg[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
result = putArgInto(arg[1], formal)
else:
result = paFastAsgn
of nkCurly, nkBracket:
for i in 0..<arg.len:
if putArgInto(arg[i], formal) != paDirectMapping:
@@ -798,9 +785,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
discard c.breakSyms.pop
var iter = call[0].sym
if sfBorrow in iter.flags and iter.kind in routineKinds:
iter = resolveBorrowedRoutineSym(c, iter, n.info)
let iter = call[0].sym
var v = newNodeI(nkVarSection, n.info)
for i in 0..<n.len - 2:
@@ -1205,13 +1190,6 @@ proc transform(c: PTransf, n: PNode, noConstFold = false): PNode =
# no need to transform type sections:
return n
of nkVarSection, nkLetSection:
# NIF loads let/var sections with bare nkSym children instead of nkIdentDefs.
# Expand them so transformSons reaches the value expression (e.g. for-loop).
for i in 0 ..< n.len:
if n[i].kind == nkSym:
let impl = n[i].sym.ast # triggers lazy load if Partial
if impl != nil and impl.kind == nkIdentDefs:
n[i] = impl
if c.inlining > 0:
# we need to copy the variables for multiple yield statements:
result = transformVarSection(c, n)

View File

@@ -99,13 +99,12 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}:
# only closure iterators may be assigned to anything.
result = t
let innerFlags = flags - {taObjField, taTupField, taIsOpenArray}
let f = if kind in {skProc, skFunc}: innerFlags+{taNoUntyped} else: innerFlags
let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
for _, a in t.paramTypes:
if result != nil: break
result = typeAllowedAux(marker, a, skParam, c, f)
result = typeAllowedAux(marker, a, skParam, c, f-{taIsOpenArray})
if result.isNil and t.returnType != nil:
result = typeAllowedAux(marker, t.returnType, skResult, c, innerFlags)
result = typeAllowedAux(marker, t.returnType, skResult, c, flags)
of tyTypeDesc:
if kind in {skVar, skLet, skConst} and taProcContextIsNotMacro in flags:
result = t

View File

@@ -274,7 +274,7 @@ proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef
c.typeKey(t.sonsImpl[0], flags-{CoIgnoreRange}, conf)
else:
withTree c.m, toNifTag(t.kind):
for i in 0..<t.sonsImpl.len:
for i in 1..<t.sonsImpl.len:
c.typeKey t.sonsImpl[i], flags, conf
if tfNotNil in t.flagsImpl and CoType notin flags:
c.m.addIdent "´notnil"

View File

@@ -18,9 +18,21 @@ import std/[intsets, strutils]
when defined(nimPreviewSlimSystem):
import std/[assertions, formatfloat]
export isResolvedUserTypeClass, TPreferedDesc, typeToString
type
TPreferedDesc* = enum
preferName, # default
preferDesc, # probably should become what preferResolved is
preferExported,
preferModuleInfo, # fully qualified
preferGenericArg,
preferTypeName,
preferResolved, # fully resolved symbols
preferMixed,
# most useful, shows: symbol + resolved symbols if it differs, e.g.:
# tuple[a: MyInt{int}, b: float]
preferInlayHint,
preferInferredEffects,
TTypeRelation* = enum # order is important!
isNone, isConvertible,
isIntConv,
@@ -43,6 +55,8 @@ type
pcmNotIterator
pcmDifferentCallConv
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) =
if optDeclaredLocs in conf.globalOptions:
result.add typeToString(typ, preferMixed)
@@ -50,6 +64,8 @@ proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) =
else:
result.add typeToString(typ)
template `$`*(typ: PType): string = typeToString(typ)
# ------------------- type iterator: ----------------------------------------
type
TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop
@@ -141,9 +157,15 @@ proc getFloatValue*(n: PNode): BiggestFloat =
of nkHiddenStdConv: getFloatValue(n[1])
else: NaN
proc isIntLit*(t: PType): bool {.inline.} =
result = t.kind == tyInt and t.n != nil and t.n.kind == nkIntLit
proc isFloatLit*(t: PType): bool {.inline.} =
result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit
proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) =
result.add typeToString(typ, prefer)
if getDeclarationPath and typ.sym != nil: result.addDeclaredLoc(conf, typ.sym)
if getDeclarationPath: result.addDeclaredLoc(conf, typ.sym)
proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName; getDeclarationPath = true): string =
assert sym != nil
@@ -438,10 +460,337 @@ proc canFormAcycle*(g: ModuleGraph, typ: PType): bool =
let t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
result = canFormAcycleAux(g, marker, t, t, false, false)
proc valueToString(a: PNode): string =
case a.kind
of nkCharLit, nkUIntLit..nkUInt64Lit:
result = $cast[uint64](a.intVal)
of nkIntLit..nkInt64Lit:
result = $a.intVal
of nkFloatLit..nkFloat128Lit: result = $a.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkStaticExpr: result = "static(" & a[0].renderTree & ")"
else: result = "<invalid value>"
proc rangeToStr(n: PNode): string =
assert(n.kind == nkRange)
result = valueToString(n[0]) & ".." & valueToString(n[1])
const
typeToStr: array[TTypeKind, string] = ["None", "bool", "char", "empty",
"Alias", "typeof(nil)", "untyped", "typed", "typeDesc",
# xxx typeDesc=>typedesc: typedesc is declared as such, and is 10x more common.
"GenericInvocation", "GenericBody", "GenericInst", "GenericParam",
"distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple",
"set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc",
"pointer", "OpenArray[$1]", "string", "cstring", "Forward",
"int", "int8", "int16", "int32", "int64",
"float", "float32", "float64", "float128",
"uint", "uint8", "uint16", "uint32", "uint64",
"owned", "sink",
"lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type",
"BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass", "inferred",
"and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix
"void", "iterable"]
const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,
preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects}
template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
tc.add concrete
tc.incl tfResolved
# TODO: It would be a good idea to kill the special state of a resolved
# concept by switching to tyAlias within the instantiated procs.
# Currently, tyAlias is always skipped with skipModifier, which means that
# we can store information about the matched concept in another position.
# Then builtInFieldAccess can be modified to properly read the derived
# consts and types stored within the concept.
template isResolvedUserTypeClass*(t: PType): bool =
tfResolved in t.flags
proc addTypeFlags(name: var string, typ: PType) {.inline.} =
if tfNotNil in typ.flags: name.add(" not nil")
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
let preferToplevel = prefer
proc getPrefer(prefer: TPreferedDesc): TPreferedDesc =
if preferToplevel in {preferResolved, preferMixed}:
preferToplevel # sticky option
else:
prefer
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
result = ""
let prefer = getPrefer(prefer)
let t = typ
if t == nil: return
if prefer in preferToResolveSymbols and t.sym != nil and
sfAnon notin t.sym.flags and t.kind notin {tySequence, tyInferred}:
if t.kind == tyInt and isIntLit(t):
if prefer == preferInlayHint:
result = t.sym.name.s
else:
result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
elif t.kind == tyAlias and t.elementType.kind != tyAlias:
result = typeToString(t.elementType)
elif prefer in {preferResolved, preferMixed}:
case t.kind
of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}:
result = typeToStr[t.kind]
of tyGenericBody:
result = typeToString(t.last)
of tyCompositeTypeClass:
# avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]`
result = typeToString(t.last.last)
else:
result = t.sym.name.s
if prefer == preferMixed and result != t.sym.name.s:
result = t.sym.name.s & "{" & result & "}"
elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil:
# note: should probably be: {preferName, preferTypeName, preferGenericArg}
result = t.sym.name.s
if t.kind == tyGenericParam and t.genericParamHasConstraints:
result.add ": "
result.add t.elementType.typeToString
else:
result = t.sym.owner.name.s & '.' & t.sym.name.s
result.addTypeFlags(t)
return
case t.kind
of tyInt:
if not isIntLit(t) or prefer == preferExported:
result = typeToStr[t.kind]
else:
case prefer:
of preferGenericArg:
result = $t.n.intVal
of preferInlayHint:
result = "int"
else:
result = "int literal(" & $t.n.intVal & ")"
of tyGenericInst:
result = typeToString(t.genericHead) & '['
for needsComma, a in t.genericInstParams:
if needsComma: result.add(", ")
result.add(typeToString(a, preferGenericArg))
result.add(']')
of tyGenericInvocation:
result = typeToString(t.genericHead) & '['
for needsComma, a in t.genericInvocationParams:
if needsComma: result.add(", ")
result.add(typeToString(a, preferGenericArg))
result.add(']')
of tyGenericBody:
result = typeToString(t.typeBodyImpl) & '['
for i, a in t.genericBodyParams:
if i > 0: result.add(", ")
result.add(typeToString(a, preferTypeName))
result.add(']')
of tyTypeDesc:
if t.elementType.kind == tyNone: result = "typedesc"
else: result = "typedesc[" & typeToString(t.elementType) & "]"
of tyStatic:
if prefer == preferGenericArg and t.n != nil:
result = t.n.renderTree
else:
result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) else: "") & "]"
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
of tyUserTypeClass:
if t.sym != nil and t.sym.owner != nil:
if t.isResolvedUserTypeClass: return typeToString(t.last)
return t.sym.owner.name.s
else:
result = "<invalid tyUserTypeClass>"
of tyBuiltInTypeClass:
result =
case t.base.kind
of tyVar: "var"
of tyRef: "ref"
of tyPtr: "ptr"
of tySequence: "seq"
of tyArray: "array"
of tySet: "set"
of tyRange: "range"
of tyDistinct: "distinct"
of tyProc: "proc"
of tyObject: "object"
of tyTuple: "tuple"
of tyOpenArray: "openArray"
else: typeToStr[t.base.kind]
of tyInferred:
let concrete = t.previouslyInferred
if concrete != nil: result = typeToString(concrete)
else: result = "inferred[" & typeToString(t.base) & "]"
of tyUserTypeClassInst:
let body = t.base
result = body.sym.name.s & "["
for needsComma, a in t.userTypeClassInstParams:
if needsComma: result.add(", ")
result.add(typeToString(a))
result.add "]"
of tyAnd:
for i, son in t.ikids:
if i > 0: result.add(" and ")
result.add(typeToString(son))
of tyOr:
for i, son in t.ikids:
if i > 0: result.add(" or ")
result.add(typeToString(son))
of tyNot:
result = "not " & typeToString(t.elementType)
of tyUntyped:
#internalAssert t.len == 0
result = "untyped"
of tyFromExpr:
if t.n == nil:
result = "unknown"
else:
result = "typeof(" & renderTree(t.n) & ")"
of tyArray:
result = "array"
if t.hasElementType:
if t.indexType.kind == tyRange:
result &= "[" & rangeToStr(t.indexType.n) & ", " &
typeToString(t.elementType) & ']'
else:
result &= "[" & typeToString(t.indexType) & ", " &
typeToString(t.elementType) & ']'
of tyUncheckedArray:
result = "UncheckedArray"
if t.hasElementType:
result &= "[" & typeToString(t.elementType) & ']'
of tySequence:
if t.sym != nil and prefer != preferResolved:
result = t.sym.name.s
else:
result = "seq"
if t.hasElementType:
result &= "[" & typeToString(t.elementType) & ']'
of tyOrdinal:
result = "ordinal"
if t.hasElementType:
result &= "[" & typeToString(t.skipModifier) & ']'
of tySet:
result = "set"
if t.hasElementType:
result &= "[" & typeToString(t.elementType) & ']'
of tyOpenArray:
result = "openArray"
if t.hasElementType:
result &= "[" & typeToString(t.elementType) & ']'
of tyDistinct:
result = "distinct " & typeToString(t.elementType,
if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
of tyIterable:
# xxx factor this pattern
result = "iterable"
if t.hasElementType:
result &= "[" & typeToString(t.skipModifier) & ']'
of tyTuple:
# we iterate over t.sons here, because t.n may be nil
if t.n != nil:
result = "tuple["
for i in 0..<t.n.len:
assert(t.n[i].kind == nkSym)
result.add(t.n[i].sym.name.s & ": " & typeToString(t.n[i].sym.typ))
if i < t.n.len - 1: result.add(", ")
result.add(']')
elif t.isEmptyTupleType:
result = "tuple[]"
elif t.isSingletonTupleType:
result = "("
for son in t.kids:
result.add(typeToString(son))
result.add(",)")
else:
result = "("
for i, son in t.ikids:
if i > 0: result.add ", "
result.add(typeToString(son))
result.add(')')
of tyPtr, tyRef, tyVar, tyLent:
result = if isOutParam(t): "out " else: typeToStr[t.kind]
result.add typeToString(t.elementType)
of tyRange:
result = "range "
if t.n != nil and t.n.kind == nkRange:
result.add rangeToStr(t.n)
if prefer != preferExported:
result.add("(" & typeToString(t.elementType) & ")")
of tyProc:
result = if tfIterator in t.flags: "iterator "
elif t.owner != nil:
case t.owner.kind
of skTemplate: "template "
of skMacro: "macro "
of skConverter: "converter "
else: "proc "
else:
"proc "
if tfUnresolved in t.flags: result.add "[*missing parameters*]"
result.add "("
for i, a in t.paramTypes:
if i > FirstParamAt: result.add(", ")
let j = paramTypeToNodeIndex(i)
if t.n != nil and j < t.n.len and t.n[j].kind == nkSym:
result.add(t.n[j].sym.name.s)
result.add(": ")
result.add(typeToString(a))
result.add(')')
if t.returnType != nil: result.add(": " & typeToString(t.returnType))
var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv
var hasImplicitRaises = false
if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos:
let pragmasNode = t.owner.ast[pragmasPos]
let raisesSpec = effectSpec(pragmasNode, wRaises)
if not isNil(raisesSpec):
addSep(prag)
prag.add("raises: ")
prag.add($raisesSpec)
hasImplicitRaises = true
if tfNoSideEffect in t.flags:
addSep(prag)
prag.add("noSideEffect")
if tfThread in t.flags:
addSep(prag)
prag.add("gcsafe")
var effectsOfStr = ""
for i, a in t.paramTypes:
let j = paramTypeToNodeIndex(i)
if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags:
addSep(effectsOfStr)
effectsOfStr.add(t.n[j].sym.name.s)
if effectsOfStr != "":
addSep(prag)
prag.add("effectsOf: ")
prag.add(effectsOfStr)
if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
let effects = t.n[0]
if effects.kind == nkEffectList and effects.len == effectListLen:
var inferredRaisesStr = ""
let effs = effects[exceptionEffects]
if not isNil(effs):
for eff in items(effs):
if not isNil(eff):
addSep(inferredRaisesStr)
inferredRaisesStr.add($eff.typ)
addSep(prag)
prag.add("raises: <inferred> [")
prag.add(inferredRaisesStr)
prag.add("]")
if prag.len != 0: result.add("{." & prag & ".}")
of tyVarargs:
result = typeToStr[t.kind] % typeToString(t.elementType)
of tySink:
result = "sink " & typeToString(t.skipModifier)
of tyOwned:
result = "owned " & typeToString(t.elementType)
else:
result = typeToStr[t.kind]
result.addTypeFlags(t)
result = typeToString(typ, prefer)
proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
case t.kind
of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError:
@@ -897,11 +1246,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
c.flags = oldFlags
if x == y: return true
let aliasSkipSet = maybeSkipRange(
if PickyBackendAliases in c.flags:
{tyInferred}
else:
{tyAlias, tyInferred})
let aliasSkipSet = maybeSkipRange({tyAlias})
var a = skipTypes(x, aliasSkipSet)
while a.kind == tyUserTypeClass and tfResolved in a.flags:
a = skipTypes(a.last, aliasSkipSet)
@@ -1074,8 +1419,6 @@ proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
result = sameTypeAux(x, y, c)
proc sameBackendTypePickyAliases*(x, y: PType): bool =
let x = x.skipTypes({tyVar, tyLent, tySink, tyOwned})
let y = y.skipTypes({tyVar, tyLent, tySink, tyOwned})
var c = initSameTypeClosure()
c.flags.incl {IgnoreTupleFields, IgnoreRangeShallow, PickyCAliases, PickyBackendAliases}
c.cmp = dcEqIgnoreDistinct
@@ -1785,7 +2128,3 @@ proc reduceToBase*(f: PType): PType =
result = f.elementType
else:
result = f
proc supportsCopyMem*(t: PType): bool =
let t = t.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
result = not containsGarbageCollectedRef(t) and not hasDestructor(t)

View File

@@ -120,7 +120,7 @@ template decodeBx(k: untyped) {.dirty.} =
ensureKind(k)
template move(a, b: untyped) {.dirty.} =
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
a = move b
else:
system.shallowCopy(a, b)
@@ -557,7 +557,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
# Used to keep track of where the execution is resumed.
var savedPC = -1
var savedFrame: PStackFrame = nil
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc) or defined(gcYrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
template updateRegsAlias = discard
template regs: untyped = tos.slots
else:
@@ -663,10 +663,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
of rkNode:
if regs[rb].node.typ.kind notin PtrLikeKinds:
stackTrace(c, tos, pc, "opcCastIntToPtr: regs[rb].node.typ: " & $regs[rb].node.typ.kind)
if regs[rb].node.kind == nkNilLit:
node2.intVal = 0
else:
node2.intVal = regs[rb].node.intVal
node2.intVal = regs[rb].node.intVal
else: stackTrace(c, tos, pc, "opcCastIntToPtr: regs[rb].kind: " & $regs[rb].kind)
regs[ra].node = node2
of opcAsgnComplex:

View File

@@ -62,10 +62,7 @@ proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode =
result = newNodeI(nkIdentDefs, n.info)
result.add n # name
result.add mapTypeToAstX(cache, n.sym.typ, n.info, idgen, true, false) # type
if n.sym.ast != nil:
result.add copyTree(n.sym.ast)
else:
result.add newNodeI(nkEmpty, n.info) # no assigned value
result.add newNodeI(nkEmpty, n.info) # no assigned value
else:
result = copyNode(n)
for i in 0..<n.safeLen:
@@ -90,10 +87,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
var id = newNodeX(nkIdentDefs)
id.add n # name
id.add mapTypeToAst(t, info) # type
if n.sym.ast != nil:
id.add copyTree(n.sym.ast)
else:
id.add newNodeI(nkEmpty, n.info) # no assigned value
id.add newNodeI(nkEmpty, info) # no assigned value
id
template newIdentDefs(s): untyped = newIdentDefs(s, s.typ)

View File

@@ -803,8 +803,6 @@ proc genNarrow(c: PCtx; n: PNode; dest: TDest) =
let first = c.genx(newIntTypeNode(firstOrd(c.config, t), intType))
let last = c.genx(newIntTypeNode(lastOrd(c.config, t), intType))
c.gABC(n, opcNarrowR, dest, first, last)
c.freeTemp(first)
c.freeTemp(last)
proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
@@ -1588,6 +1586,7 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
proc setSlot(c: PCtx; v: PSym) =
# XXX generate type initialization here?
if v.position == 0:
# IC: review this solution again later
v.positionImpl = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
template cannotEval(c: PCtx; n: PNode) =
@@ -1726,9 +1725,6 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
if sameBackendType(le.typ, le[1].typ):
genAsgn(c, le[1], ri, requiresCopy)
of nkStmtListExpr:
for i in 0..<le.len-1: gen(c, le[i])
genAsgn(c, le[^1], ri, requiresCopy)
else:
let dest = c.genx(le, {gfNodeAddr})
genAsgn(c, dest, ri, requiresCopy)

View File

@@ -82,7 +82,7 @@ proc containGenerics(base: PType, s: seq[tuple[depth: int, value: PType]]): bool
break
proc collectVTableDispatchers*(g: ModuleGraph) =
var itemTable = initTable[ItemId, seq[PSym]]()
var itemTable = initTable[ItemId, seq[LazySym]]()
var rootTypeSeq = newSeq[PType]()
var rootItemIdCount = initCountTable[ItemId]()
for bucket in 0..<g.methods.len:
@@ -95,7 +95,7 @@ proc collectVTableDispatchers*(g: ModuleGraph) =
let methodIndexLen = g.bucketTable[baseType.itemId]
if baseType.itemId notin itemTable: # once is enough
rootTypeSeq.add baseType
itemTable[baseType.itemId] = newSeq[PSym](methodIndexLen)
itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
if x.depth >= y.depth: 1
@@ -104,7 +104,7 @@ proc collectVTableDispatchers*(g: ModuleGraph) =
for item in g.objectTree[baseType.itemId]:
if item.value.itemId notin itemTable:
itemTable[item.value.itemId] = newSeq[PSym](methodIndexLen)
itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
var mIndex = 0 # here is the correpsonding index
if baseType.itemId notin rootItemIdCount:
@@ -114,13 +114,13 @@ proc collectVTableDispatchers*(g: ModuleGraph) =
rootItemIdCount.inc(baseType.itemId)
for idx in 0..<g.methods[bucket].methods.len:
let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
itemTable[obj.itemId][mIndex] = g.methods[bucket].methods[idx]
itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
g.addDispatchers genVTableDispatcher(g, g.methods[bucket].methods, mIndex)
else: # if the base object doesn't have this method
g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, g.idgen)
proc sortVTableDispatchers*(g: ModuleGraph) =
var itemTable = initTable[ItemId, seq[PSym]]()
var itemTable = initTable[ItemId, seq[LazySym]]()
var rootTypeSeq = newSeq[ItemId]()
var rootItemIdCount = initCountTable[ItemId]()
for bucket in 0..<g.methods.len:
@@ -133,7 +133,7 @@ proc sortVTableDispatchers*(g: ModuleGraph) =
let methodIndexLen = g.bucketTable[baseType.itemId]
if baseType.itemId notin itemTable: # once is enough
rootTypeSeq.add baseType.itemId
itemTable[baseType.itemId] = newSeq[PSym](methodIndexLen)
itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
if x.depth >= y.depth: 1
@@ -142,7 +142,7 @@ proc sortVTableDispatchers*(g: ModuleGraph) =
for item in g.objectTree[baseType.itemId]:
if item.value.itemId notin itemTable:
itemTable[item.value.itemId] = newSeq[PSym](methodIndexLen)
itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
var mIndex = 0 # here is the correpsonding index
if baseType.itemId notin rootItemIdCount:
@@ -152,7 +152,7 @@ proc sortVTableDispatchers*(g: ModuleGraph) =
rootItemIdCount.inc(baseType.itemId)
for idx in 0..<g.methods[bucket].methods.len:
let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
itemTable[obj.itemId][mIndex] = g.methods[bucket].methods[idx]
itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
for baseType in rootTypeSeq:
g.setMethodsPerType(baseType, itemTable[baseType])
@@ -160,7 +160,7 @@ proc sortVTableDispatchers*(g: ModuleGraph) =
let typ = item.value.skipTypes(skipPtrs)
let idx = typ.itemId
for mIndex in 0..<itemTable[idx].len:
if itemTable[idx][mIndex] == nil:
if itemTable[idx][mIndex].sym == nil:
let parentIndex = typ.baseClass.skipTypes(skipPtrs).itemId
itemTable[idx][mIndex] = itemTable[parentIndex][mIndex]
g.setMethodsPerType(idx, itemTable[idx])

View File

@@ -248,9 +248,6 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?>
</head>
<body>
<div class="document" id="documentId">
<input type="checkbox" id="nav-toggle" hidden>
<label for="nav-toggle" id="nav-burger">&#9776;</label>
<label for="nav-toggle" id="nav-overlay"></label>
<div class="container">
<h1 class="title">$title</h1>$subtitle
$content

View File

@@ -115,7 +115,6 @@ Advanced options:
--docSeeSrcUrl:url activate 'see source' for doc command
(see doc.item.seesrc in config/nimdoc.cfg)
--docInternal also generate documentation for non-exported symbols
--raw turn off markup rendering for JSON docs
--lineDir:on|off generation of #line directive on|off
--embedsrc:on|off embeds the original source code as comments
in the generated output

View File

@@ -735,24 +735,6 @@ with a hyperlink to your own code repository.
In the case of Nim's own documentation, the `commit` value is just a commit
hash to append to a formatted URL to https://github.com/nim-lang/Nim.
Substitution via environment variables
--------------------------------------
A simple substitution using environment variables is available.
A reference written as ``|name|`` is replaced during documentation generation if
a matching variable is provided. You can define it via the compiler with
``--putenv``. This is useful for injecting values like version strings or
build-specific text.
```nim
## |foo|
```
```cmd
nim --putenv:foo=bar doc filename.nim
```
The generated html will contain ``bar`` instead of ``foo``.
Other Input Formats
===================

166
doc/ic.md
View File

@@ -1,166 +0,0 @@
======================================
Incremental Compilation (IC)
======================================
The ``nim ic`` command provides incremental compilation support for Nim projects,
allowing faster rebuilds by reusing previously compiled intermediate representations
of modules that haven't changed.
Overview
========
Incremental compilation works by decomposing the compilation process into several stages:
1. **Parsing** - Source files are parsed into an abstract syntax tree (AST)
2. **Semantic Analysis** - Symbols are resolved and type checking is performed
3. **Code Generation** - Platform-specific code is generated from the analyzed AST
4. **Linking** - The generated code is linked into an executable
The IC mechanism caches the results of earlier stages in NIF files
(Nim intermediate format): ``.p.nif`` (parsed), ``.deps.nif`` (dependencies),
and ``.nif`` (semantically analyzed). When recompiling, only modules that have
changed need to be reprocessed through the semantic analysis and code generation
stages, significantly reducing compilation time for large projects.
NIF File Format
===============
NIF (Nim Intermediate Format) files are text-based files that use a Lisp-like
syntax. They employ a hybrid format where byte offsets into the text are used for
efficient access, making them simultaneously human-readable and machine-efficient.
The text representation is particularly valuable for debugging and introspection.
Each ``.nim`` module produces its own ``.nif`` file during compilation.
The NIF format contains:
- **Header** - Version information (e.g., `(.nif27)`)
- **Dependencies** - List of source files and dependencies
- **Interface** - Exported symbols and their indices
- **Body** - The intermediate representation of the module's code in Lisp-like syntax
The NIF format is designed specifically for Nim and allows efficient serialization
and deserialization of the compiler's intermediate representation while remaining
readable and debuggable by tools and developers.
The ``nim ic`` Switch
=====================
The ``nim ic`` command initiates incremental compilation for a project.
It automatically manages the build process by:
1. Parsing all source files into ``.nif`` format (using the ``nifler`` tool)
2. Performing semantic analysis on modified modules
3. Generating code only for modules with changes or dependencies on changed modules
4. Generating a build file (in NIFMake format) that orchestrates the compilation
5. Executing the build file through ``nifmake``
Prerequisites
-------------
- **nifler** - Tool for parsing Nim source files into NIF format. The ``nim ic`` command uses ``nifler parse --deps`` to generate both parsed files (``.p.nif``) and dependency files (``.deps.nif``).
- **nifmake** - Build orchestration tool that follows dependencies and executes the build rules defined in ``.build.nif`` files.
If these tools are not available, ``nim ic`` will display instructions on how to
obtain them.
Key Modules for IC Logic
=========================
The primary modules in the compiler that handle incremental compilation logic are:
- **deps.nim** - Dependency analysis and build file generation. Contains the
``commandIc`` procedure which is the main entry point for the ``nim ic`` command.
This module orchestrates the incremental compilation process, handling dependency
traversal (via ``nifler deps``), build rule generation, and build file creation.
The build file is written to ``nifcache/`` directory. This module also explicitly
models ``system.nim`` as a dependency of all modules.
- **ast2nif.nim** - Core mapping between AST and NIF.
**Code, Logic & Debugging**
===========================
This section focuses on the compiler-side code paths, the logic you will
inspect while debugging IC, and a pragmatic manual workflow for bug hunting
using local invocations such as ``nim m --nimcache:nifcache``.
Core places to inspect
- **`compiler/deps.nim`**: generates the NIF-based build file and implements
``commandIc`` (entry point for ``nim ic``). Look for how build rules are
emitted (calls to the NIF builder) and how inputs/outputs are wired.
- **`compiler/modulegraphs.nim`** and **`compiler/pipelines.nim`**:
dependency graph and compilation pipeline integration — useful when a module
is rebuilt unexpectedly.
Understanding the NIF text
- NIF files are human-readable; open the per-module ``.nif`` files in
``nifcache/`` to inspect parsed ASTs, dependency lists and interface tables.
- Because NIF uses textual nodes and byte offsets, tools can quickly seek to
positions in the file — but for debugging you usually only need to read the
file top-to-bottom.
Manual bug-hunting workflow
- Prepare a clean nimcache directory (relative to your project):
```bash
mkdir -p nifcache
```
- Parse/semantic-check a single module and write NIF/sem artifacts:
```bash
nim m --nimcache:nifcache path/to/module.nim
```
- ``nim m`` runs the compiler up to the semantic checking stage for the
specified module and emits intermediate cache files into ``nifcache/``.
- Use this to reproduce and isolate failures in the semantic stage.
- Inspect the generated files for that module under ``nifcache/`` (look for
``.nif``, sem/parsed artifacts). Because NIF is text-based you can open and
grep it directly:
```bash
sed -n '1,200p' nifcache/ModuleName.nif
grep -n "someSymbol" -n nifcache/ModuleName.nif
```
- To reproduce a full incremental compilation of the project, generate the
build file and run it (``nim ic`` automates this). The build file is generated
in ``nifcache/`` directory. To debug an individual build step, run the command
that the build file would execute manually:
- Parsing step: ``nifler parse --deps input.nim`` (produces ``.p.nif`` and ``.deps.nif``)
- Semantic step: ``nim m --nimcache:nifcache input.nim`` (produces ``.nif``)
- Code generation: ``nim nifc --nimcache:nifcache input.nim`` (produces executable)
- Force a cache invalidation for a single module by removing its NIF/sem
artifact and re-running the semantic step:
```bash
rm nifcache/ModuleName.nif
nim m --nimcache:nifcache path/to/ModuleName.nim
```
- When investigating incorrect replayed state (pragmas, `{.compile: ...}`):
inspect the replay actions in ``compiler/ic/replayer.nim`` and open the
module's NIF to find the ``toReplay``/action entries that will be executed
during reload.
Tips for efficient debugging
- Use ``--path:...`` flags when invoking ``nim m`` to emulate the exact
search paths used in your project, e.g. ``--path:lib --path:vendor``.
- Compare two successive ``.nif`` files with ``diff`` to see what changed and
why a module was rebuilt.
Where to change behavior
- Cache invalidation decisions and build-rule emission are implemented in
``compiler/deps.nim``. When investigating surprising
rebuilds, instrument those modules to log the footprint/hash/comparison
outcome.
See also
========
- `nif-spec` - NIF format specification (text format and node grammar):
[nifspec/doc/nif-spec.md](../nifspec/doc/nif-spec.md)

View File

@@ -34,10 +34,10 @@ To learn how to compile Nim programs and generate documentation see
the [Compiler User Guide](nimc.html) and the [DocGen Tools Guide](docgen.html).
The language constructs are explained using an extended BNF, in which `(a)*`
means 0 or more *a*'s, `a+` means 1 or more *a*'s, and `(a)?` means an
means 0 or more `a`'s, `a+` means 1 or more `a`'s, and `(a)?` means an
optional *a*. Parentheses may be used to group elements.
`&` is the lookahead operator; `&a` means that an *a* is expected but
`&` is the lookahead operator; `&a` means that an `a` is expected but
not consumed. It will be consumed in the following rule.
The `|`, `/` symbols are used to mark alternatives and have the lowest
@@ -1024,9 +1024,6 @@ These are the major type classes:
* procedural type
* generic type
The compiler's internal type zoo is richer than this summary suggests:
some types that are structurally equal still differ in backend representation.
Ordinal types
-------------
@@ -1147,8 +1144,6 @@ semantic analysis). Assignments from the base type to one of its subrange types
A subrange type has the same size as its base type (`int` in the
Subrange example).
Implicit "downsizing" conversions to range types (for example, `int -> range[0..255]` or `range[1..256] -> range[0..255]`) emit the `ImplicitRangeConversion` warning. Conversions that are clearly safe (for example, `range[0..255] -> range[0..65535]`) and any explicit casts do not trigger this warning. Conversions from `int` to common subranges such as `Natural` or `Positive` do not trigger this warning by default, but can be enabled with `--warning:systemRangeConversion`.
Pre-defined floating-point types
--------------------------------
@@ -2177,10 +2172,6 @@ Procedural type
A procedural type is internally a pointer to a procedure. `nil` is
an allowed value for a variable of a procedural type.
Procedure compatibility also checks the backend representation of the
parameter and result types, not just their source-level shape. Use
`--legacy:procParamTypeBackendAliases` to restore the older behavior.
Examples:
```nim
@@ -2635,10 +2626,10 @@ Overload resolution
In a call `p(args)` where `p` may refer to more than one
candidate, it is said to be a symbol choice. Overload resolution will attempt to
find the best candidate, thus transforming the symbol choice into a resolved symbol.
The routine `p` that matches best is selected following a series of trials explained below.
The routine `p` that matches best is selected following a series of trials explained below.
In order: Category matching, Hierarchical Order Comparison, and finally, Complexity Analysis.
If multiple candidates match equally well after all trials have been tested, the ambiguity
If multiple candidates match equally well after all trials have been tested, the ambiguity
is reported during semantic analysis.
First Trial: Category matching
@@ -2671,7 +2662,7 @@ resolved symbol.
For example, if a candidate with one exact match is compared to a candidate with multiple
generic matches and zero exact matches, the candidate with an exact match will win.
Below is a pseudocode interpretation of category matching, `count(p, m)` counts the number
Below is a pseudocode interpretation of category matching, `count(p, m)` counts the number
of matches of the matching category `m` for the routine `p`.
A routine `p` matches better than a routine `q` if the following
@@ -2699,11 +2690,11 @@ type A[T] = object
```
Matching formals for this type include `T`, `object`, `A`, `A[...]` and `A[C]` where `C` is a concrete type, `A[...]`
is a generic typeclass composition and `T` is an unconstrained generic type variable. This list is in order of
is a generic typeclass composition and `T` is an unconstrained generic type variable. This list is in order of
specificity with respect to `A` as each subsequent category narrows the set of types that are members of their match set.
In this trial, the formal parameters of candidates are compared in order (1st parameter, 2nd parameter, etc.) to search for
a candidate that has an unrivaled specificity. If such a formal parameter is found, the candidate it belongs to is chosen
a candidate that has an unrivaled specificity. If such a formal parameter is found, the candidate it belongs to is chosen
as the resolved symbol.
Third Trial: Complexity Analysis
@@ -2958,13 +2949,13 @@ proc sort*[I: Index; T: Comparable](x: var Indexable[I, T])
In the above example, `Comparable` and `Indexable` are types that will match any type that
can can bind each definition declared in the concept body. The special `Self` type defined
in the concept body refers to the type being matched, also called the "implementation" of
the concept. Implementations that match the concept are generic matches, and the concept
in the concept body refers to the type being matched, also called the "implementation" of
the concept. Implementations that match the concept are generic matches, and the concept
typeclasses themselves work in a similar way to generic type variables in that they are never
concrete types themselves (even if they have concrete type parameters such as `Indexable[int, int]`)
and expressions like `typeof(x)` in the body of `proc sort` from the above example will return the
and expressions like `typeof(x)` in the body of `proc sort` from the above example will return the
type of the implementation, not the concept typeclass. Concepts are useful for providing information
to the compiler in generic contexts, most notably for generic type checking, and as a tool for
to the compiler in generic contexts, most notably for generic type checking, and as a tool for
[Overload resolution]. Generic type checking is forthcoming, so this will only explain overload
resolution for now.
@@ -2991,7 +2982,7 @@ Concept overload resolution
When an operand's type is being matched to a concept, the operand's type is set as the "potential
implementation". For each definition in the concept body, overload resolution is performed by substituting `Self`
for the potential implementation to try and find a match for each definition. If this succeeds, the concept
for the potential implementation to try and find a match for each definition. If this succeeds, the concept
matches. Implementations do not need to exactly match the definitions in the concept. For example:
```nim
@@ -3015,7 +3006,7 @@ This leads to confusing and impractical behavior in most situations, so the rule
1. if a concept is being compared with `T` or any type that accepts all other types (`auto`) the concept
is more specific
2. if the concept is being compared with another concept the result is deferred to [Concept subset matching]
3. in any other case the concept is less specific then it's competitor
3. in any other case the concept is less specific then it's competitor
Currently, the concept evaluation mechanism evaluates to a successful match on the first acceptable candidate
for each defined binding. This has a couple of notable effects:
@@ -4617,10 +4608,10 @@ for any type (with some exceptions) by defining a routine with the name `[]`.
```nim
type Foo = object
data: seq[int]
proc `[]`(foo: Foo, i: int): int =
result = foo.data[i]
let foo = Foo(data: @[1, 2, 3])
echo foo[1] # 2
```
@@ -4631,12 +4622,12 @@ which has precedence over assigning to the result of `[]`.
```nim
type Foo = object
data: seq[int]
proc `[]`(foo: Foo, i: int): int =
result = foo.data[i]
proc `[]=`(foo: var Foo, i: int, val: int) =
foo.data[i] = val
var foo = Foo(data: @[1, 2, 3])
echo foo[1] # 2
foo[1] = 5
@@ -4868,14 +4859,7 @@ default to being inline, but this may change in future versions of the
implementation.
The `iterator` type is always of the calling convention `closure`
implicitly.
Unlike named iterators, anonymous iterator expressions evaluate
to the `iterator` type. In practice, this means a named iterator declaration
without `{.closure.}` defaults to inline, but an expression like `let it =
iterator(): int = yield 1` produces a callable closure iterator value.
The following example shows how to use iterators to implement
implicitly; the following example shows how to use iterators to implement
a `collaborative tasking`:idx: system:
```nim
@@ -6415,7 +6399,7 @@ The default for symbols of entity `type`, `var`, `let` and `const`
is `gensym`. For `proc`, `iterator`, `converter`, `template`,
`macro`, the default is `inject`, but if a `gensym` symbol with the same name
is defined in the same syntax-level scope, it will be `gensym` by default.
This can be overridden by marking the routine as `inject`.
This can be overridden by marking the routine as `inject`.
If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:
@@ -7256,7 +7240,7 @@ identifier is considered ambiguous, which can be resolved in the following ways:
write(stdout, x) # error: x is ambiguous
write(stdout, A.x) # no error: qualifier used
proc bar(a: int): int = a + 1
assert bar(x) == x + 1 # no error: only A.x of type int matches
@@ -7920,7 +7904,7 @@ alignment requirement of the type are ignored.
main()
```
This pragma has no effect on the JavaScript backend and may significantly increase memory usage with the `--mm:refc` option.
This pragma has no effect on the JS backend.
Noalias pragma
@@ -8874,7 +8858,7 @@ Byref pragma
The `byref` pragma can be applied to an object or tuple type or a proc param.
When applied to a type it instructs the compiler to pass the type by reference
(hidden pointer) to procs. When applied to a param it will take precedence, even
if the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or
if the the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or
parameters are marked as `byref` in an `importc` proc, these params translate to pointers.
When an `importcpp` type has a `byref` pragma, these params translate to
C++ references `&`.
@@ -9338,3 +9322,4 @@ It is not valid to pass an lvalue of a supertype to an `out T` parameter:
However, in the future this could be allowed and provide a better way to write object
constructors that take inheritance into account.

View File

@@ -2127,7 +2127,7 @@ can be used in an `isolate` context:
`=destroy`(dest.value)
```
The `.sendable` pragma itself is an experimental, unchecked, unsafe annotation. It is
The `.sendable` pragma itself is an experimenal, unchecked, unsafe annotation. It is
currently only used by `Isolated[T]`.
Virtual pragma

View File

@@ -276,9 +276,9 @@ This parser has 2 modes for inline markup:
2) Compatibility mode which is RST rules.
.. Note:: in both modes the parser interprets text between single
.. Note:: in both modes the parser interpretes text between single
backticks (code) identically:
backslash does not escape; the only exception: ``\`` followed by `
backslash does not escape; the only exception: ``\`` folowed by `
does escape so that we can always input a single backtick ` in
inline code. However that makes impossible to input code with
``\`` at the end in *single* backticks, one must use *double*

View File

@@ -123,6 +123,7 @@ Modified by Boyd Greenfield and narimiran
}
html {
overflow-x: hidden;
max-width: 100%;
box-sizing: border-box;
font-size: 100%;
@@ -155,8 +156,7 @@ body {
margin-left: 1%; }
@media print {
#global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby,
#nav-burger, #nav-overlay, .three.columns {
#global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby {
display:none;
}
.columns {
@@ -175,7 +175,6 @@ body {
height: 100vh;
position: sticky;
top: 0px;
left: 0px;
overflow-y: auto;
padding: 2px;
}
@@ -189,67 +188,9 @@ body {
width: 100%;
margin-left: 0; }
#nav-burger, #nav-overlay {
display: none;
}
@media screen and (max-width: 860px) {
#nav-burger {
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0.25em;
left: 0.25em;
z-index: 200;
width: 1.6rem;
height: 1.6rem;
font-size: 1.25em;
cursor: pointer;
border-radius: 4px;
background-color: var(--secondary-background);
color: var(--text);
border: 1px solid var(--border);
user-select: none;
opacity: 0.55;
}
#nav-burger:hover {
background-color: var(--third-background);
}
#nav-toggle:checked ~ .container .three.columns {
transform: translateX(0);
}
#nav-toggle:checked ~ #nav-overlay {
opacity: 1;
pointer-events: auto;
}
#nav-overlay {
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 99; /* below sidebar */
background: rgba(0, 0, 0, 0.35);
opacity: 0;
pointer-events: none;
transition: opacity 0.22s ease;
}
.three.columns {
display: block;
position: fixed;
left: 0;
width: min(80vw, 24em);
padding-top: 1.6em;
height: 100vh; /* Fallback */
height: 100dvh;
overflow-y: auto;
z-index: 100;
background-color: var(--secondary-background);
box-shadow: 2px 0 12px rgba(0,0,0,0.25);
transform: translateX(-110%);
transition: transform 0.25s ease;
display: none;
}
.nine.columns {
width: 100%;
@@ -259,8 +200,6 @@ body {
body {
font-size: 1em;
line-height: 1.35;
margin-left: 0.35em;
margin-right: 0.35em;
}
}
@@ -419,10 +358,6 @@ img {
h1.title {
page-break-before: avoid; }
.nine.columns h1:first-of-type {
page-break-before: avoid;
}
p, h2, h3 {
orphans: 3;
@@ -490,22 +425,6 @@ h5 {
h6 {
font-size: 1.1em; }
@media screen and (max-width: 860px) {
h1.title {
font-size: 2em;
}
h1 {
font-size: 1.5em;
margin-top: 1.5em;
margin-bottom: 0.75em;
}
h2 {
margin-top: 1.3em;
}
h3 {
margin-top: 1.2em;
}
}
ul, ol {
padding: 0;
@@ -653,8 +572,8 @@ blockquote.markdown-quote {
padding-left: 3px;
padding-right: 3px;
border-radius: 4px;
white-space: pre-wrap;
overflow-wrap: break-word;
white-space: normal;
word-break: break-all;
}
span.tok {
@@ -689,15 +608,6 @@ pre {
border-radius: 6px;
}
@media screen and (max-width: 860px) {
pre {
font-stretch: semi-condensed;
letter-spacing: -0.25px;
line-height: 1.25;
padding: 0.33em;
}
}
.copyToClipBoardBtn {
visibility: hidden;
position: absolute;
@@ -764,8 +674,6 @@ table {
border-collapse: collapse;
border-color: var(--third-background);
border-spacing: 0;
display: block;
overflow-x: auto;
}
table:not(.line-nums-table) {

View File

@@ -52,7 +52,7 @@ Options:
nimgrep --filenames # In current dir
nimgrep --filenames "" DIRECTORY
# Note empty pattern "", lists all files in DIRECTORY
* Interpret patterns:
* Interprete patterns:
--peg PATTERN and PAT are Peg
--re PATTERN and PAT are regular expressions (default)
--rex, -x use the "extended" syntax for the regular expression

View File

@@ -27,7 +27,7 @@ Nim runs on a wide variety of platforms. Support on amd64 and i386 is tested reg
- ppc64el (aka ppc64le)
- riscv64
The following platforms are rarely tested:
The following platforms are seldomly tested:
- alpha
- hppa

View File

@@ -20,8 +20,8 @@ notation meaning
as they succeed. Indicate success if all succeeded.
Otherwise, do not consume any text and indicate failure.
The sequence's precedence is higher than that of ordered
choice: ``A B / C`` means ``(A B) / C`` and
not ``A (B / C)``.
choice: ``A B / C`` means ``(A B) / Z`` and
not ``A (B / Z)``.
``(E)`` Grouping: Parenthesis can be used to change
operator priority.
``{E}`` Capture: Apply expression `E` and store the substring

View File

@@ -1144,7 +1144,7 @@ there is a difference between the `$` and `repr` outputs:
echo myCharacter, ":", repr(myCharacter)
# --> n:'n'
echo myString, ":", repr(myString)
# --> nim:"nim"
# --> nim:0x10fa8c050"nim"
echo myInteger, ":", repr(myInteger)
# --> 42:42
echo myFloat, ":", repr(myFloat)
@@ -1159,8 +1159,8 @@ In Nim new types can be defined within a `type` statement:
```nim test = "nim c $1"
type
BiggestInt = int64 # biggest integer type that is available
BiggestFloat = float64 # biggest float type that is available
biggestInt = int64 # biggest integer type that is available
biggestFloat = float64 # biggest float type that is available
```
Enumeration and object types may only be defined within a

View File

@@ -11,16 +11,15 @@
const
# examples of possible values for repos: Head, ea82b54
NimbleStableCommit = "aa03f886e4a111d6af9090c6a1f1271d64b66f7b" # 0.22.2
AtlasStableCommit = "ff1f4289482dce94ba9f95b3b0ae16d16e21eb3d" # 0.10.1
NimbleStableCommit = "9207e8b2bbdf66b5a4d1020214cff44d2d30df92" # 0.20.1
AtlasStableCommit = "2aa62121b40d580aa2fb27920a37b938d36c5f57" # 0.9.4
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
SatStableCommit = "e63eaea8baf00bed8bcd5a29ffd8823abb265b39"
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
NimonyStableCommit = "750aa47f2139fe5ad69f04b44428b752011fe873" # unversioned \
NimonyStableCommit = "e2cd6eadcaa68eb8ab380cb4d3bdd7fd260677b4" # unversioned \
# Note that Nimony uses Nim as a git submodule but we don't want to install
# Nimony's dependency to Nim as we are Nim. So a `git clone` without --recursive
# is **required** here.
# Commit from 2026-05-05
# examples of possible values for fusion: #head, #ea82b54, 1.2.3
FusionStableHash = "#562467452b32cb7a97410ea177f083e6d8405734"
@@ -189,10 +188,8 @@ proc bundleChecksums(latest: bool) =
let nimonyCommit = if latest: "HEAD" else: NimonyStableCommit
cloneDependency(distDir, "https://github.com/nim-lang/nimony.git", nimonyCommit, allowBundled = true)
if not fileExists("bin/nifler".exe):
nimCompileFold("Compile nifler", "dist/nimony/src/nifler/nifler.nim", options = "-d:release")
if not fileExists("bin/nifmake".exe):
nimCompileFold("Compile nifmake", "dist/nimony/src/nifmake/nifmake.nim", options = "-d:release")
nimCompileFold("Compile nifler", "dist/nimony/src/nifler/nifler.nim", options = "-d:release")
nimCompileFold("Compile nifmake", "dist/nimony/src/nifmake/nifmake.nim", options = "-d:release")
proc bundleNimsuggest(args: string) =
bundleChecksums(false)
@@ -559,7 +556,9 @@ proc icTest(args: string) =
for fragment in content.split("#!EDIT!#"):
let file = inp.replace(".nim", "_temp.nim")
writeFile(file, fragment)
var cmd = nimExe & " ic --hint:Conf:off --warnings:off "
var cmd = nimExe & " cpp --ic:legacy -d:nimIcIntegrityChecks --listcmd "
if i == 0:
cmd.add "-f "
cmd.add quoteShell(file)
exec(cmd)
inc i

View File

@@ -433,15 +433,15 @@ when defined(nimHasNoReturnError):
else:
{.pragma: errorNoReturn.}
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", gcsafe, errorNoReturn.}
proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign, errorNoReturn.}
## Writes an error message at compile time. The optional `n: NimNode`
## parameter is used as the source for file and line number information in
## the compilation error message.
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", gcsafe.}
proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.}
## Writes a warning message at compile time.
proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", gcsafe.}
proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.}
## Writes a hint message at compile time.
proc newStrLitNode*(s: string): NimNode {.noSideEffect.} =
@@ -511,7 +511,7 @@ proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
## Generates a fresh symbol that is guaranteed to be unique. The symbol
## needs to occur in a declaration context.
proc callsite*(): NimNode {.magic: "NCallSite", gcsafe, deprecated:
proc callsite*(): NimNode {.magic: "NCallSite", benign, deprecated:
"Deprecated since v0.18.1; use `varargs[untyped]` in the macro prototype instead".}
## Returns the AST of the invocation expression that invoked this macro.
# see https://github.com/nim-lang/RFCs/issues/387 as candidate replacement.
@@ -933,7 +933,7 @@ proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.}
const collapseSymChoice = not defined(nimLegacyMacrosCollapseSymChoice)
proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.gcsafe.} =
proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} =
if level > 0:
if indented:
res.add("\n")
@@ -982,21 +982,21 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent
if isLisp:
res.add(")")
proc treeRepr*(n: NimNode): string {.gcsafe.} =
proc treeRepr*(n: NimNode): string {.benign.} =
## Convert the AST `n` to a human-readable tree-like string.
##
## See also `repr`, `lispRepr`_, and `astGenRepr`_.
result = ""
n.treeTraverse(result, isLisp = false, indented = true)
proc lispRepr*(n: NimNode; indented = false): string {.gcsafe.} =
proc lispRepr*(n: NimNode; indented = false): string {.benign.} =
## Convert the AST `n` to a human-readable lisp-like string.
##
## See also `repr`, `treeRepr`_, and `astGenRepr`_.
result = ""
n.treeTraverse(result, isLisp = true, indented = indented)
proc astGenRepr*(n: NimNode): string {.gcsafe.} =
proc astGenRepr*(n: NimNode): string {.benign.} =
## Convert the AST `n` to the code required to generate that AST.
##
## See also `repr`_, `treeRepr`_, and `lispRepr`_.
@@ -1005,7 +1005,7 @@ proc astGenRepr*(n: NimNode): string {.gcsafe.} =
NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt}
LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit}
proc traverse(res: var string, level: int, n: NimNode) {.gcsafe.} =
proc traverse(res: var string, level: int, n: NimNode) {.benign.} =
for i in 0..level-1: res.add " "
if n.kind in NodeKinds:
res.add("new" & ($n.kind).substr(3) & "Node(")
@@ -1559,8 +1559,6 @@ macro expandMacros*(body: typed): untyped =
echo body.toStrLit
result = body
proc getTypeInstSkipAlias(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
proc extractTypeImpl(n: NimNode): NimNode =
## attempts to extract the type definition of the given symbol
case n.kind
@@ -1575,17 +1573,11 @@ proc extractTypeImpl(n: NimNode): NimNode =
result = n[0].getImpl()
of nnkTypeDef:
result = n[2]
if result.kind notin {nnkSym, nnkObjectTy, nnkRefTy, nnkPtrTy, nnkBracketExpr}:
# Handle typeof() and similar unresolvable type expressions
let typSym = if n[0].kind == nnkPragmaExpr: n[0][0] else: n[0]
if typSym.kind == nnkSym:
let resolved = typSym.getTypeInstSkipAlias()
if resolved.kind == nnkSym:
return resolved.getImpl.extractTypeImpl()
error("Invalid node to retrieve type implementation of: " & $result.kind)
else: error("Invalid node to retrieve type implementation of: " & $n.kind)
proc getTypeInstSkipAlias(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.}
proc customPragmaNode(n: NimNode): NimNode =
result = nil
expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr})
@@ -1626,15 +1618,6 @@ proc customPragmaNode(n: NimNode): NimNode =
var typDef = getImpl(typInst)
while typDef != nil:
typDef.expectKind(nnkTypeDef)
# Resolve typeof() and similar unresolvable type expressions
if typDef[2].kind notin {nnkSym, nnkObjectTy, nnkRefTy, nnkPtrTy, nnkBracketExpr}:
let typSym = if typDef[0].kind == nnkPragmaExpr: typDef[0][0] else: typDef[0]
if typSym.kind == nnkSym:
let resolved = typSym.getTypeInstSkipAlias()
if resolved.kind == nnkSym:
typDef = getImpl(resolved)
continue
break
let typ = typDef[2].extractTypeImpl()
if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break
let isRef = typ.kind in {nnkRefTy, nnkPtrTy}

View File

@@ -9,11 +9,6 @@
when defined(js):
{.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".}
## .. warning:: NRE is deprecated.
## Use [Regex](https://github.com/nitely/nim-regex) or
## `NRE2 <nre2.html>`_ that wraps Regex so that you can easily replace NRE.
## PCRE library is now at end of life.
##
## What is NRE?
## ============
##
@@ -89,7 +84,7 @@ type
Regex* = ref RegexDesc
## Represents the pattern that things are matched against, constructed with
## `re(string)`. Examples: `re"foo"`, `re(r"(*ANYCRLF)(?x)foo #
## comment")`
## comment".`
##
## `pattern: string`
## : the string that was used to create the pattern. For details on how
@@ -159,7 +154,7 @@ type
## will need to pass these as separate flags to PCRE.
RegexMatch* = object
## Usually seen as `Option[RegexMatch]`, it represents the result of an
## Usually seen as Option[RegexMatch], it represents the result of an
## execution. On failure, it is none, on success, it is some.
##
## `pattern: Regex`

Some files were not shown because too many files have changed in this diff Show More