mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-27 00:51:39 +00:00
Merge branch 'devel' into pr_Quelle
This commit is contained in:
11
changelog.md
11
changelog.md
@@ -31,6 +31,8 @@ errors.
|
||||
|
||||
- The second parameter of `succ`, `pred`, `inc`, and `dec` in `system` now accepts `SomeInteger` (previously `Ordinal`).
|
||||
|
||||
- Bitshift operators (`shl`, `shr`, `ashr`) now apply bitmasking to the right operand in the C/C++/VM/JS backends.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Additions:"
|
||||
@@ -103,7 +105,16 @@ errors.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- Fixed a bug where `sizeof(T)` inside a `typedesc` template called from a generic type's
|
||||
`when` clause would error with "'sizeof' requires '.importc' types to be '.completeStruct'".
|
||||
The issue was that `hasValuelessStatics` in `semtypinst.nim` didn't recognize
|
||||
`tyTypeDesc(tyGenericParam)` as an unresolved generic parameter.
|
||||
|
||||
## 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`)
|
||||
|
||||
## Documentation changes
|
||||
|
||||
- Added documentation for the `completeStruct` pragma in the manual.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# abstract syntax tree + symbol table
|
||||
|
||||
import
|
||||
lineinfos, options, ropes, idents, int128, wordrecg
|
||||
lineinfos, options, idents, int128, wordrecg
|
||||
|
||||
import std/[tables, hashes]
|
||||
from std/strutils import toLowerAscii
|
||||
@@ -559,18 +559,28 @@ proc addAllowNil*(father, son: PNode) {.inline.} =
|
||||
father.sons.add(son)
|
||||
|
||||
proc add*(father, son: PType) =
|
||||
assert father.kind != tyProc or father.sonsImpl.len == 0
|
||||
assert son != nil
|
||||
father.sonsImpl.add son
|
||||
|
||||
proc addAllowNil*(father, son: PType) {.inline.} =
|
||||
assert father.kind != tyProc or father.sonsImpl.len == 0
|
||||
father.sonsImpl.add son
|
||||
|
||||
template `[]`*(n: PType, i: int): PType =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[i]
|
||||
if n.kind == tyProc and i > 0:
|
||||
assert n.nImpl[i] != nil and n.nImpl[i].sym != nil
|
||||
n.nImpl[i].sym.typ
|
||||
else:
|
||||
n.sonsImpl[i]
|
||||
template `[]=`*(n: PType, i: int; x: PType) =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[i] = x
|
||||
if n.kind == tyProc and i > 0:
|
||||
assert n.nImpl[i] != nil and n.nImpl[i].sym != nil
|
||||
n.nImpl[i].sym.typ = x
|
||||
else:
|
||||
n.sonsImpl[i] = x
|
||||
|
||||
template `[]`*(n: PType, i: BackwardsIndex): PType =
|
||||
if n.state == Partial: loadType(n)
|
||||
@@ -816,7 +826,10 @@ proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} =
|
||||
|
||||
proc last*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[^1]
|
||||
if n.kind == tyProc and n.nImpl.len > 1:
|
||||
n.nImpl[^1].sym.typ
|
||||
else:
|
||||
n.sonsImpl[^1]
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
@@ -852,7 +865,10 @@ proc setIndexType*(n, idx: PType) {.inline.} =
|
||||
|
||||
proc firstParamType*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[1]
|
||||
if n.kind == tyProc:
|
||||
n.nImpl[1].sym.typ
|
||||
else:
|
||||
n.sonsImpl[1]
|
||||
|
||||
proc firstGenericParam*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
@@ -924,10 +940,13 @@ proc `$`*(s: PSym): string =
|
||||
result = "<nil>"
|
||||
|
||||
proc len*(n: PType): int {.inline.} =
|
||||
result = n.sonsImpl.len
|
||||
if n.kind == tyProc:
|
||||
result = if n.nImpl == nil: 0 else: n.nImpl.len
|
||||
else:
|
||||
result = n.sonsImpl.len
|
||||
|
||||
proc sameTupleLengths*(a, b: PType): bool {.inline.} =
|
||||
result = a.sonsImpl.len == b.sonsImpl.len
|
||||
result = a.len == b.len
|
||||
|
||||
iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
|
||||
for i in 0 ..< a.len:
|
||||
@@ -1022,15 +1041,20 @@ proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType
|
||||
alignImpl: defaultAlignment, itemId: id,
|
||||
uniqueId: id, sonsImpl: @[])
|
||||
if son != nil:
|
||||
assert kind != tyProc
|
||||
result.sonsImpl.add son
|
||||
when false:
|
||||
if result.itemId.module == 55 and result.itemId.item == 2:
|
||||
echo "KNID ", kind
|
||||
writeStackTrace()
|
||||
|
||||
proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sonsImpl = sons
|
||||
proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sonsImpl = @[son]
|
||||
proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} =
|
||||
assert dest.kind != tyProc or sons.len <= 1
|
||||
dest.sonsImpl = sons
|
||||
proc setSon*(dest: PType; son: sink PType) {.inline.} =
|
||||
dest.sonsImpl = @[son]
|
||||
proc setSonsLen*(dest: PType; len: int) {.inline.} =
|
||||
assert dest.kind != tyProc or len <= 1
|
||||
setLen(dest.sonsImpl, len)
|
||||
|
||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
@@ -1044,6 +1068,7 @@ proc newSons*(father: PNode, length: int) =
|
||||
setLen(father.sons, length)
|
||||
|
||||
proc newSons*(father: PType, length: int) =
|
||||
assert father.kind != tyProc or length <= 1
|
||||
setLen(father.sonsImpl, length)
|
||||
|
||||
proc truncateInferredTypeCandidates*(t: PType) {.inline.} =
|
||||
@@ -1068,8 +1093,16 @@ proc assignType*(dest, src: PType) =
|
||||
mergeLoc(dest.sym.locImpl, src.sym.loc)
|
||||
else:
|
||||
dest.symImpl = src.sym
|
||||
newSons(dest, src.len)
|
||||
for i in 0..<src.len: dest[i] = src[i]
|
||||
if src.kind == tyProc:
|
||||
# `tyProc` uses only `sonsImpl[0]` to store return type.
|
||||
# parameter symbols and types are stored in `nImpl`.
|
||||
assert src.sonsImpl.len <= 1
|
||||
if src.len > 0:
|
||||
setLen(dest.sonsImpl, 1)
|
||||
dest.sonsImpl[0] = src.sonsImpl[0]
|
||||
else:
|
||||
newSons(dest, src.len)
|
||||
for i in 0..<src.len: dest[i] = src[i]
|
||||
|
||||
proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
|
||||
result = newType(t.kind, idgen, owner)
|
||||
@@ -1179,7 +1212,8 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||
|
||||
proc rawAddSon*(father, son: PType; propagateHasAsgn = true) =
|
||||
ensureMutable father
|
||||
father.sonsImpl.add(son)
|
||||
if father.kind != tyProc or father.sonsImpl.len == 0:
|
||||
father.sonsImpl.add(son)
|
||||
if not son.isNil: propagateToOwner(father, son, propagateHasAsgn)
|
||||
|
||||
proc addSonNilAllowed*(father, son: PNode) =
|
||||
@@ -1585,7 +1619,7 @@ proc newProcType*(info: TLineInfo; idgen: IdGenerator; owner: PSym): PType =
|
||||
result.n.add newNodeI(nkEffectList, info)
|
||||
|
||||
proc addParam*(procType: PType; param: PSym) =
|
||||
param.position = procType.sons.len-1
|
||||
param.position = procType.n.len - 1
|
||||
procType.n.add newSymNode(param)
|
||||
rawAddSon(procType, param.typ)
|
||||
|
||||
@@ -1656,3 +1690,10 @@ type
|
||||
|
||||
template initSymMapping*(): SymMapping = initIdTable[PSym]()
|
||||
template initTypeMapping*(): TypeMapping = initIdTable[PType]()
|
||||
|
||||
proc sameModules*(a, b: PSym): bool {.inline.} =
|
||||
assert a.kind == skModule and b.kind == skModule
|
||||
result = a.position == b.position
|
||||
|
||||
proc sameOwners*(a, b: PSym): bool =
|
||||
result = a == b or (a.kind == skModule and b.kind == skModule and a.position == b.position) or a.id == b.id
|
||||
|
||||
@@ -164,7 +164,7 @@ type
|
||||
|
||||
const
|
||||
# Symbol kinds that are always local to a proc and should never have module suffix
|
||||
skLocalSymKinds = {skParam, skGenericParam, skForVar, skResult, skTemp}
|
||||
skLocalSymKinds = {skParam, skForVar, skResult, skTemp}
|
||||
|
||||
proc isLocalSym(sym: PSym): bool {.inline.} =
|
||||
sym.kindImpl in skLocalSymKinds or
|
||||
@@ -362,10 +362,7 @@ proc writeSymDef(w: var Writer; dest: var TokenBuf; sym: PSym) =
|
||||
writeSym(w, dest, sym.ownerFieldImpl)
|
||||
# Store the AST for routine symbols and constants
|
||||
# Constants need their AST for astdef() to return the constant's value
|
||||
if sym.kindImpl in routineKinds + {skConst}:
|
||||
writeNode(w, dest, sym.astImpl, forAst = true)
|
||||
else:
|
||||
dest.addDotToken
|
||||
writeNode(w, dest, sym.astImpl, forAst = true)
|
||||
writeLoc w, dest, sym.locImpl
|
||||
writeNode(w, dest, sym.constraintImpl)
|
||||
writeSym(w, dest, sym.instantiatedFromImpl)
|
||||
@@ -438,14 +435,20 @@ proc addLocalSym(w: var Writer; n: PNode) =
|
||||
w.locals.incl(n.sym.itemId)
|
||||
|
||||
proc addLocalSyms(w: var Writer; n: PNode) =
|
||||
if n.kind in {nkIdentDefs, nkVarTuple}:
|
||||
case n.kind
|
||||
of nkIdentDefs, nkVarTuple:
|
||||
# nkIdentDefs: [ident1, ident2, ..., type, default]
|
||||
# All children except the last two are identifiers
|
||||
for i in 0 ..< max(0, n.len - 2):
|
||||
addLocalSyms(w, n[i])
|
||||
elif n.kind == nkSym:
|
||||
of nkPostfix:
|
||||
addLocalSyms(w, n[1])
|
||||
of nkPragmaExpr:
|
||||
addLocalSyms(w, n[0])
|
||||
of nkSym:
|
||||
addLocalSym(w, n)
|
||||
|
||||
else:
|
||||
discard
|
||||
|
||||
proc trInclude(w: var Writer; n: PNode) =
|
||||
w.deps.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), trLineInfo(w, n.info)
|
||||
@@ -456,6 +459,9 @@ proc trInclude(w: var Writer; n: PNode) =
|
||||
w.deps.addStrLit child.strVal # raw string literal, no wrapper needed
|
||||
w.deps.addParRi
|
||||
|
||||
proc moduleSuffix(conf: ConfigRef; f: FileIndex): string =
|
||||
cachedModuleSuffix(conf, f)
|
||||
|
||||
proc trImport(w: var Writer; n: PNode) =
|
||||
for child in n:
|
||||
if child.kind == nkSym:
|
||||
@@ -464,7 +470,7 @@ proc trImport(w: var Writer; n: PNode) =
|
||||
w.deps.addDotToken # type
|
||||
let s = child.sym
|
||||
assert s.kindImpl == skModule
|
||||
let fp = toFullPath(w.infos.config, s.positionImpl.FileIndex)
|
||||
let fp = moduleSuffix(w.infos.config, s.positionImpl.FileIndex)
|
||||
w.deps.addStrLit fp # raw string literal, no wrapper needed
|
||||
w.deps.addParRi
|
||||
|
||||
@@ -512,14 +518,14 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
|
||||
of nkNilLit:
|
||||
w.withNode dest, n:
|
||||
discard
|
||||
of nkLetSection, nkVarSection, nkConstSection, nkGenericParams:
|
||||
of nkLetSection, nkVarSection, nkConstSection:
|
||||
# Track local variables declared in let/var sections
|
||||
w.withNode dest, n:
|
||||
for child in n:
|
||||
addLocalSyms w, child
|
||||
# Process the child node
|
||||
writeNode(w, dest, child, forAst)
|
||||
of nkForStmt, nkTypeDef:
|
||||
of nkForStmt:
|
||||
# Track for loop variable (first child is the loop variable)
|
||||
w.withNode dest, n:
|
||||
if n.len > 0:
|
||||
@@ -535,7 +541,7 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
|
||||
addLocalSyms(w, n[i])
|
||||
writeNode(w, dest, n[i], forAst)
|
||||
dec w.inProc
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef, nkMacroDef:
|
||||
of nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef, nkMacroDef, nkTemplateDef:
|
||||
# For top-level named routines (not forAst), just write the symbol.
|
||||
# The full AST will be stored in the symbol's sdef.
|
||||
if not forAst and n[namePos].kind == nkSym:
|
||||
@@ -602,16 +608,53 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
|
||||
# Write the export statement as a regular node
|
||||
w.withNode dest, n:
|
||||
for i in 0 ..< n.len:
|
||||
writeNode(w, dest, n[i], forAst)
|
||||
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:
|
||||
writeNode(w, dest, n[i], forAst)
|
||||
|
||||
proc writeToplevelNode(w: var Writer; dest: var TokenBuf; n: PNode) =
|
||||
proc writeGlobal(w: var Writer; dest: var TokenBuf; n: PNode) =
|
||||
case n.kind
|
||||
of nkVarTuple:
|
||||
writeNode(w, dest, n)
|
||||
of nkIdentDefs, nkConstDef:
|
||||
# nkIdentDefs: [ident1, ident2, ..., type, default]
|
||||
# All children except the last two are identifiers
|
||||
for i in 0 ..< max(0, n.len - 2):
|
||||
writeGlobal(w, dest, n[i])
|
||||
of nkPostfix:
|
||||
writeGlobal(w, dest, n[1])
|
||||
of nkPragmaExpr:
|
||||
writeGlobal(w, dest, n[0])
|
||||
of nkSym:
|
||||
writeSym(w, dest, n.sym)
|
||||
else:
|
||||
discard
|
||||
|
||||
proc writeGlobals(w: var Writer; dest: var TokenBuf; n: PNode) =
|
||||
w.withNode dest, n:
|
||||
for child in n:
|
||||
writeGlobal(w, dest, child)
|
||||
|
||||
proc writeToplevelNode(w: var Writer; dest, bottom: var TokenBuf; n: PNode) =
|
||||
case n.kind
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
for son in n: writeToplevelNode(w, dest, son)
|
||||
for son in n: writeToplevelNode(w, dest, bottom, son)
|
||||
of nkEmpty:
|
||||
discard "ignore"
|
||||
of nkTypeSection, nkCommentStmt, nkMixinStmt, nkBindStmt, nkUsingStmt,
|
||||
nkPragma,
|
||||
nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef, nkMacroDef, nkTemplateDef:
|
||||
# We write purely declarative nodes at the bottom of the file
|
||||
writeNode(w, bottom, n)
|
||||
of nkConstSection:
|
||||
writeGlobals(w, bottom, n)
|
||||
of nkLetSection, nkVarSection:
|
||||
writeGlobals(w, dest, n)
|
||||
else:
|
||||
writeNode w, dest, n
|
||||
|
||||
@@ -652,6 +695,7 @@ 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
|
||||
@@ -706,8 +750,14 @@ proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode;
|
||||
if op.module == thisModule.int:
|
||||
writeOp(w, content, op)
|
||||
|
||||
w.writeToplevelNode content, n
|
||||
var bottom = createTokenBuf(300)
|
||||
w.writeToplevelNode content, bottom, n
|
||||
|
||||
# the implTag is used to tell the loader that the
|
||||
# bottom of the file is the implementation of the module:
|
||||
content.addParLe implTag, NoLineInfo
|
||||
content.addParRi()
|
||||
content.add bottom
|
||||
content.addParRi()
|
||||
|
||||
let m = modname(w.currentModule, w.infos.config)
|
||||
@@ -817,10 +867,14 @@ proc cursorFromIndexEntry(c: var DecodeContext; module: FileIndex; entry: NifInd
|
||||
nifcursors.parse(s[], buf, entry.info)
|
||||
result = cursorAt(buf, 0)
|
||||
|
||||
proc moduleId(c: var DecodeContext; suffix: string): FileIndex =
|
||||
type
|
||||
LoadFlag* = enum
|
||||
LoadFullAst, AlwaysLoadInterface
|
||||
|
||||
proc moduleId(c: var DecodeContext; suffix: string; flags: set[LoadFlag] = {}): FileIndex =
|
||||
var isKnownFile = false
|
||||
result = c.infos.config.registerNifSuffix(suffix, isKnownFile)
|
||||
if not isKnownFile:
|
||||
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):
|
||||
@@ -840,6 +894,9 @@ proc getOffset(c: var DecodeContext; module: FileIndex; nifName: string): NifInd
|
||||
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")
|
||||
@@ -865,8 +922,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.
|
||||
## 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.
|
||||
## For local symbols, fully load them immediately since they have no index offsets.
|
||||
## After this proc returns, n is positioned AFTER the tree.
|
||||
# Handle atoms (non-compound nodes) - just skip them
|
||||
if n.kind != ParLe:
|
||||
inc n
|
||||
@@ -881,7 +938,8 @@ proc extractLocalSymsFromTree(c: var DecodeContext; n: var Cursor; thisModule: s
|
||||
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
|
||||
# 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[]
|
||||
@@ -889,6 +947,17 @@ proc extractLocalSymsFromTree(c: var DecodeContext; n: var Cursor; thisModule: s
|
||||
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
|
||||
inc depth # account for the opening `(` of the sdef
|
||||
loadSymFromCursor(c, sym, n, thisModule, localSyms)
|
||||
sym.state = Sealed # mark as fully loaded
|
||||
# loadSymFromCursor consumed everything including the closing `)`,
|
||||
# so we need to account for it in depth tracking
|
||||
dec depth
|
||||
# Continue processing - loadSymFromCursor already advanced n past the closing `)`
|
||||
continue
|
||||
inc depth
|
||||
elif n.kind == ParRi:
|
||||
dec depth
|
||||
@@ -1099,6 +1168,8 @@ proc loadSymFromCursor(c: var DecodeContext; s: PSym; n: var Cursor; thisModule:
|
||||
inc n
|
||||
var isKnownFile = false
|
||||
s.positionImpl = int c.infos.config.registerNifSuffix(thisModule, isKnownFile)
|
||||
# do to the precompiled mechanism things end up as main modules which are not!
|
||||
excl s.flagsImpl, sfMainModule
|
||||
else:
|
||||
loadField s.positionImpl
|
||||
|
||||
@@ -1110,12 +1181,7 @@ proc loadSymFromCursor(c: var DecodeContext; s: PSym; n: var Cursor; thisModule:
|
||||
s.ownerFieldImpl = loadSymStub(c, n, thisModule, localSyms)
|
||||
# Load the AST for routine symbols and constants
|
||||
# Constants need their AST for astdef() to return the constant's value
|
||||
if s.kindImpl in routineKinds + {skConst}:
|
||||
s.astImpl = loadNode(c, n, thisModule, localSyms)
|
||||
elif n.kind == DotToken:
|
||||
inc n
|
||||
else:
|
||||
raiseAssert "expected '.' for non-routine symbol AST but got " & $n.kind
|
||||
s.astImpl = loadNode(c, n, thisModule, localSyms)
|
||||
loadLoc c, n, s.locImpl
|
||||
s.constraintImpl = loadNode(c, n, thisModule, localSyms)
|
||||
s.instantiatedFromImpl = loadSymStub(c, n, thisModule, localSyms)
|
||||
@@ -1303,9 +1369,6 @@ proc loadNode(c: var DecodeContext; n: var Cursor; thisModule: string;
|
||||
else:
|
||||
raiseAssert "expected string literal but got " & $n.kind
|
||||
|
||||
proc moduleSuffix(conf: ConfigRef; f: FileIndex): string =
|
||||
cachedModuleSuffix(conf, f)
|
||||
|
||||
proc loadSymFromIndexEntry(c: var DecodeContext; module: FileIndex;
|
||||
nifName: string; entry: NifIndexEntry; thisModule: string): PSym =
|
||||
## Loads a symbol from the NIF index entry using the entry directly.
|
||||
@@ -1494,8 +1557,32 @@ proc nextSubtree(r: var Stream; dest: var TokenBuf; tok: var PackedToken) =
|
||||
dec nested
|
||||
if nested == 0: break
|
||||
|
||||
proc processTopLevel(c: var DecodeContext; s: var Stream; loadFullAst: bool; suffix: string; logOps: var seq[LogEntry]; module: int): PNode =
|
||||
result = newNode(nkStmtList)
|
||||
type
|
||||
ModuleSuffix* = distinct string
|
||||
PrecompiledModule* = object
|
||||
topLevel*: PNode # top level statements of the main module
|
||||
deps*: seq[ModuleSuffix] # other modules we need to process the top level statements of
|
||||
logOps*: seq[LogEntry]
|
||||
module*: PSym # set by modulegraphs.nim!
|
||||
|
||||
proc loadImport(c: var DecodeContext; s: var Stream; deps: var seq[ModuleSuffix]; tok: var PackedToken) =
|
||||
tok = next(s) # skip `(import`
|
||||
if tok.kind == DotToken:
|
||||
tok = next(s) # skip dot
|
||||
if tok.kind == DotToken:
|
||||
tok = next(s) # skip dot
|
||||
if tok.kind == StringLit:
|
||||
deps.add ModuleSuffix(pool.strings[tok.litId])
|
||||
tok = next(s)
|
||||
else:
|
||||
raiseAssert "expected StringLit but got " & $tok.kind
|
||||
if tok.kind == ParRi:
|
||||
tok = next(s) # skip )
|
||||
else:
|
||||
raiseAssert "expected ParRi but got " & $tok.kind
|
||||
|
||||
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
|
||||
@@ -1512,60 +1599,62 @@ proc processTopLevel(c: var DecodeContext; s: var Stream; loadFullAst: bool; suf
|
||||
var cursor = cursorAt(buf, 0)
|
||||
let replayNode = loadNode(c, cursor, suffix, localSyms)
|
||||
if replayNode != nil:
|
||||
result.sons.add replayNode
|
||||
result.topLevel.sons.add replayNode
|
||||
t = next(s)
|
||||
if t.kind == ParRi:
|
||||
t = next(s)
|
||||
else:
|
||||
raiseAssert "expected ParRi but got " & $t.kind
|
||||
elif t.tagId == repConverterTag:
|
||||
t = loadLogOp(c, logOps, s, ConverterEntry, attachedTrace, module)
|
||||
t = loadLogOp(c, result.logOps, s, ConverterEntry, attachedTrace, module)
|
||||
elif t.tagId == repDestroyTag:
|
||||
t = loadLogOp(c, logOps, s, HookEntry, attachedDestructor, module)
|
||||
t = loadLogOp(c, result.logOps, s, HookEntry, attachedDestructor, module)
|
||||
elif t.tagId == repWasMovedTag:
|
||||
t = loadLogOp(c, logOps, s, HookEntry, attachedWasMoved, module)
|
||||
t = loadLogOp(c, result.logOps, s, HookEntry, attachedWasMoved, module)
|
||||
elif t.tagId == repCopyTag:
|
||||
t = loadLogOp(c, logOps, s, HookEntry, attachedAsgn, module)
|
||||
t = loadLogOp(c, result.logOps, s, HookEntry, attachedAsgn, module)
|
||||
elif t.tagId == repSinkTag:
|
||||
t = loadLogOp(c, logOps, s, HookEntry, attachedSink, module)
|
||||
t = loadLogOp(c, result.logOps, s, HookEntry, attachedSink, module)
|
||||
elif t.tagId == repDupTag:
|
||||
t = loadLogOp(c, logOps, s, HookEntry, attachedDup, module)
|
||||
t = loadLogOp(c, result.logOps, s, HookEntry, attachedDup, module)
|
||||
elif t.tagId == repTraceTag:
|
||||
t = loadLogOp(c, logOps, s, HookEntry, attachedTrace, module)
|
||||
t = loadLogOp(c, result.logOps, s, HookEntry, attachedTrace, module)
|
||||
elif t.tagId == repDeepCopyTag:
|
||||
t = loadLogOp(c, logOps, s, HookEntry, attachedDeepCopy, module)
|
||||
t = loadLogOp(c, result.logOps, s, HookEntry, attachedDeepCopy, module)
|
||||
elif t.tagId == repEnumToStrTag:
|
||||
t = loadLogOp(c, logOps, s, EnumToStrEntry, attachedTrace, module)
|
||||
t = loadLogOp(c, result.logOps, s, EnumToStrEntry, attachedTrace, module)
|
||||
elif t.tagId == repMethodTag:
|
||||
t = loadLogOp(c, logOps, s, MethodEntry, attachedTrace, module)
|
||||
t = loadLogOp(c, result.logOps, s, MethodEntry, attachedTrace, module)
|
||||
#elif t.tagId == repClassTag:
|
||||
# t = loadLogOp(c, logOps, s, ClassEntry, attachedTrace, module)
|
||||
elif t.tagId == includeTag or t.tagId == importTag:
|
||||
elif t.tagId == includeTag:
|
||||
t = skipTree(s)
|
||||
elif loadFullAst:
|
||||
elif t.tagId == importTag:
|
||||
loadImport(c, s, result.deps, t)
|
||||
elif t.tagId == implTag:
|
||||
cont = false
|
||||
elif LoadFullAst in flags:
|
||||
# Parse the full statement
|
||||
var buf = createTokenBuf(50)
|
||||
nextSubtree(s, buf, t)
|
||||
t = next(s) # skip ParRi
|
||||
var cursor = cursorAt(buf, 0)
|
||||
let stmtNode = loadNode(c, cursor, suffix, localSyms)
|
||||
if stmtNode != nil:
|
||||
result.sons.add stmtNode
|
||||
result.topLevel.sons.add stmtNode
|
||||
else:
|
||||
cont = false
|
||||
else:
|
||||
cont = false
|
||||
|
||||
proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: var TStrTable;
|
||||
logOps: var seq[LogEntry];
|
||||
loadFullAst: bool = false): PNode =
|
||||
let suffix = moduleSuffix(c.infos.config, f)
|
||||
|
||||
# Ensure module index is loaded - moduleId returns the FileIndex for this suffix
|
||||
let module = moduleId(c, suffix)
|
||||
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
|
||||
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, suffix)
|
||||
populateInterfaceTablesFromIndex(c, module, interf, interfHidden, string(suffix))
|
||||
|
||||
# Load the module AST (or just replay actions if loadFullAst is false)
|
||||
let s = addr c.mods[module].stream
|
||||
@@ -1575,10 +1664,14 @@ proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: va
|
||||
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[], loadFullAst, suffix, logOps, f.int)
|
||||
result = processTopLevel(c, s[], flags, string(suffix), module.int)
|
||||
else:
|
||||
result = newNode(nkStmtList)
|
||||
result = PrecompiledModule(topLevel: newNode(nkStmtList))
|
||||
|
||||
proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: var TStrTable;
|
||||
flags: set[LoadFlag] = {}): PrecompiledModule =
|
||||
let suffix = ModuleSuffix(moduleSuffix(c.infos.config, f))
|
||||
result = loadNifModule(c, suffix, interf, interfHidden, flags)
|
||||
|
||||
when isMainModule:
|
||||
import std / syncio
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
#
|
||||
|
||||
import
|
||||
lineinfos, options, ropes, idents, int128, wordrecg
|
||||
lineinfos, options, ropes, idents, int128
|
||||
|
||||
import std/[tables, hashes]
|
||||
from std/strutils import toLowerAscii
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
@@ -154,14 +154,14 @@ template addField(builder: var Builder, constr: var StructInitializer, name: str
|
||||
# no name, can just add value
|
||||
valueBody
|
||||
of siOrderedStruct:
|
||||
# no name, can just add value on C
|
||||
assert name.len != 0, "name has to be given for struct initializer field"
|
||||
# positional init - name not used in output (empty allowed for anonymous unions)
|
||||
valueBody
|
||||
of siNamedStruct:
|
||||
assert name.len != 0, "name has to be given for struct initializer field"
|
||||
builder.add(".")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
# designated init - empty name for anonymous unions (skips .name = prefix)
|
||||
if name.len != 0:
|
||||
builder.add(".")
|
||||
builder.add(name)
|
||||
builder.add(" = ")
|
||||
valueBody
|
||||
|
||||
proc finishStructInitializer(builder: var Builder, constr: StructInitializer) =
|
||||
|
||||
@@ -749,16 +749,16 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
let t = getType()
|
||||
let at = cUintType(k)
|
||||
let bt = cUintType(s)
|
||||
res = cCast(t, cOp(Shr, at, cCast(at, ra), cCast(bt, rb)))
|
||||
res = cCast(t, cOp(Shr, at, cCast(at, ra), cOp(BitAnd, at, cCast(bt, rb), cIntLiteral(k - 1))))
|
||||
of mShlI:
|
||||
let t = getType()
|
||||
let at = cUintType(s)
|
||||
res = cCast(t, cOp(Shl, at, cCast(at, ra), cCast(at, rb)))
|
||||
res = cCast(t, cOp(Shl, at, cCast(at, ra), cOp(BitAnd, at, cCast(at, rb), cIntLiteral(k - 1))))
|
||||
of mAshrI:
|
||||
let t = getType()
|
||||
let at = cIntType(s)
|
||||
let bt = cUintType(s)
|
||||
res = cCast(t, cOp(Shr, at, cCast(at, ra), cCast(bt, rb)))
|
||||
res = cCast(t, cOp(Shr, at, cCast(at, ra), cOp(BitAnd, at, cCast(bt, rb), cIntLiteral(k - 1))))
|
||||
of mBitandI:
|
||||
let t = getType()
|
||||
res = cCast(t, cOp(BitAnd, t, ra, rb))
|
||||
@@ -3713,6 +3713,64 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
of nkMixinStmt, nkBindStmt, nkReplayAction: discard
|
||||
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
|
||||
|
||||
proc isOpaqueImportcType(t: PType): bool =
|
||||
# importc type without completeStruct that can't use aggregate init (e.g. C11 _Atomic)
|
||||
if t.sym != nil and sfImportc in t.sym.flags:
|
||||
if tfCompleteStruct notin t.flags:
|
||||
if tfIncompleteStruct in t.flags:
|
||||
return true
|
||||
if t.kind == tyObject and (t.n == nil or t.n.len == 0):
|
||||
return true
|
||||
return false
|
||||
|
||||
proc containsOpaqueImportcField(typ: PType): bool
|
||||
|
||||
proc containsOpaqueImportcFieldAux(t: PType; n: PNode): bool =
|
||||
if n == nil: return false
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for child in n.sons:
|
||||
if containsOpaqueImportcFieldAux(t, child):
|
||||
return true
|
||||
of nkRecCase:
|
||||
if containsOpaqueImportcFieldAux(t, n[0]):
|
||||
return true
|
||||
for i in 1..<n.len:
|
||||
let branch = n[i]
|
||||
if branch.kind == nkOfBranch or branch.kind == nkElse:
|
||||
if containsOpaqueImportcFieldAux(t, branch.lastSon):
|
||||
return true
|
||||
of nkSym:
|
||||
if containsOpaqueImportcField(n.sym.typ):
|
||||
return true
|
||||
else:
|
||||
discard
|
||||
return false
|
||||
|
||||
proc containsOpaqueImportcField(typ: PType): bool =
|
||||
# Check if type contains opaque importc fields that need designated initializers
|
||||
if typ == nil: return false
|
||||
let t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc})
|
||||
if isOpaqueImportcType(t):
|
||||
return true
|
||||
case t.kind
|
||||
of tyObject:
|
||||
if t.baseClass != nil:
|
||||
if containsOpaqueImportcField(t.baseClass):
|
||||
return true
|
||||
if containsOpaqueImportcFieldAux(t, t.n):
|
||||
return true
|
||||
of tyTuple:
|
||||
for i, a in t.ikids:
|
||||
if containsOpaqueImportcField(a):
|
||||
return true
|
||||
of tyArray:
|
||||
if containsOpaqueImportcField(t.elementType):
|
||||
return true
|
||||
else:
|
||||
discard
|
||||
return false
|
||||
|
||||
proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder) =
|
||||
var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc})
|
||||
case t.kind
|
||||
@@ -3743,24 +3801,34 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Builder)
|
||||
result.addField(closureInit, name = "ClE_0"):
|
||||
result.add(NimNil)
|
||||
of tyObject:
|
||||
# Use designated initializers when opaque importc fields present
|
||||
var objInit: StructInitializer
|
||||
result.addStructInitializer(objInit, kind = siOrderedStruct):
|
||||
let initKind = if containsOpaqueImportcField(t): siNamedStruct else: siOrderedStruct
|
||||
result.addStructInitializer(objInit, kind = initKind):
|
||||
getNullValueAuxT(p, t, t, t.n, nil, result, objInit, true, info)
|
||||
of tyTuple:
|
||||
# Use designated initializers when opaque importc fields present
|
||||
var tupleInit: StructInitializer
|
||||
result.addStructInitializer(tupleInit, kind = siOrderedStruct):
|
||||
let initKind = if containsOpaqueImportcField(t): siNamedStruct else: siOrderedStruct
|
||||
result.addStructInitializer(tupleInit, kind = initKind):
|
||||
if p.vccAndC and t.isEmptyTupleType:
|
||||
result.addField(tupleInit, name = "dummy"):
|
||||
result.addIntValue(0)
|
||||
for i, a in t.ikids:
|
||||
result.addField(tupleInit, name = "Field" & $i):
|
||||
getDefaultValue(p, a, info, result)
|
||||
let elemTyp = skipTypes(a, abstractRange+{tyOwned}-{tyTypeDesc})
|
||||
if not isOpaqueImportcType(elemTyp):
|
||||
result.addField(tupleInit, name = "Field" & $i):
|
||||
getDefaultValue(p, a, info, result)
|
||||
of tyArray:
|
||||
var arrInit: StructInitializer
|
||||
result.addStructInitializer(arrInit, kind = siArray):
|
||||
for i in 0..<toInt(lengthOrd(p.config, t.indexType)):
|
||||
result.addField(arrInit, name = ""):
|
||||
getDefaultValue(p, t.elementType, info, result)
|
||||
let elemTyp = skipTypes(t.elementType, abstractRange+{tyOwned}-{tyTypeDesc})
|
||||
if isOpaqueImportcType(elemTyp):
|
||||
result.add "{0}"
|
||||
else:
|
||||
var arrInit: StructInitializer
|
||||
result.addStructInitializer(arrInit, kind = siArray):
|
||||
for i in 0..<toInt(lengthOrd(p.config, t.indexType)):
|
||||
result.addField(arrInit, name = ""):
|
||||
getDefaultValue(p, t.elementType, info, result)
|
||||
#result = rope"{}"
|
||||
of tyOpenArray, tyVarargs:
|
||||
var openArrInit: StructInitializer
|
||||
@@ -3815,8 +3883,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
var fieldName: string = ""
|
||||
if b.kind == nkRecList and not isEmptyCaseObjectBranch(b):
|
||||
fieldName = "_" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch
|
||||
result.addField(init, name = "<anonymous union>"):
|
||||
# XXX figure out name for the union, see use of `addAnonUnion`
|
||||
result.addField(init, name = ""): # anonymous union
|
||||
var branchInit: StructInitializer
|
||||
result.addStructInitializer(branchInit, kind = siNamedStruct):
|
||||
result.addField(branchInit, name = fieldName):
|
||||
@@ -3825,8 +3892,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
getNullValueAux(p, t, b, constOrNil, result, branchObjInit, isConst, info)
|
||||
elif b.kind == nkSym:
|
||||
fieldName = mangleRecFieldName(p.module, b.sym)
|
||||
result.addField(init, name = "<anonymous union>"):
|
||||
# XXX figure out name for the union, see use of `addAnonUnion`
|
||||
result.addField(init, name = ""): # anonymous union
|
||||
var branchInit: StructInitializer
|
||||
result.addStructInitializer(branchInit, kind = siNamedStruct):
|
||||
result.addField(branchInit, name = fieldName):
|
||||
@@ -3841,6 +3907,9 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
|
||||
|
||||
of nkSym:
|
||||
let field = obj.sym
|
||||
let fieldTyp = skipTypes(field.typ, abstractRange+{tyOwned}-{tyTypeDesc})
|
||||
if isOpaqueImportcType(fieldTyp):
|
||||
return # C zero-initializes omitted fields
|
||||
let sname = mangleRecFieldName(p.module, field)
|
||||
result.addField(init, name = sname):
|
||||
block fieldInit:
|
||||
@@ -3885,11 +3954,10 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
|
||||
|
||||
proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Builder) =
|
||||
let t = n.typ.skipTypes(abstractInstOwned)
|
||||
#if not isObjLackingTypeField(t) and not p.module.compileToCpp:
|
||||
# result.addf("{$1}", [genTypeInfo(p.module, t)])
|
||||
# inc count
|
||||
# Use designated initializers when opaque importc fields present
|
||||
var objInit: StructInitializer
|
||||
result.addStructInitializer(objInit, kind = siOrderedStruct):
|
||||
let initKind = if t.kind == tyObject and containsOpaqueImportcField(t): siNamedStruct else: siOrderedStruct
|
||||
result.addStructInitializer(objInit, kind = initKind):
|
||||
if t.kind == tyObject:
|
||||
getNullValueAuxT(p, t, t, t.n, n, result, objInit, isConst, n.info)
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||
let vn = n[i]
|
||||
let v = vn.sym
|
||||
if sfCompileTime in v.flags: continue
|
||||
ensureMutable v
|
||||
backendEnsureMutable v
|
||||
if sfGlobal in v.flags:
|
||||
assignGlobalVar(p, vn, "")
|
||||
genObjectInit(p, cpsInit, v.typ, v.locImpl, constructObj)
|
||||
@@ -136,7 +136,7 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
|
||||
var field = initLoc(locExpr, vn, tup.storage)
|
||||
let rtup = rdLoc(tup)
|
||||
let fieldName =
|
||||
let fieldName =
|
||||
if t.kind == tyTuple:
|
||||
"Field" & $i
|
||||
else:
|
||||
@@ -490,14 +490,17 @@ proc genClosureVar(p: BProc, a: PNode) =
|
||||
constructLoc(p, v)
|
||||
|
||||
proc genVarStmt(p: BProc, n: PNode) =
|
||||
for it in n.sons:
|
||||
if it.kind == nkCommentStmt: continue
|
||||
if it.kind == nkIdentDefs:
|
||||
for it in n:
|
||||
case it.kind
|
||||
of nkCommentStmt: discard
|
||||
of nkIdentDefs:
|
||||
# can be a lifted var nowadays ...
|
||||
if it[0].kind == nkSym:
|
||||
genSingleVar(p, it)
|
||||
else:
|
||||
genClosureVar(p, it)
|
||||
of nkSym:
|
||||
genSingleVar(p, it.sym, newSymNode(it.sym), it.sym.astdef)
|
||||
else:
|
||||
genVarTuple(p, it)
|
||||
|
||||
@@ -740,9 +743,10 @@ proc genBlock(p: BProc, n: PNode, d: var TLoc) =
|
||||
# named block?
|
||||
assert(n[0].kind == nkSym)
|
||||
var sym = n[0].sym
|
||||
ensureMutable sym
|
||||
backendEnsureMutable sym
|
||||
sym.locImpl.k = locOther
|
||||
sym.position = p.breakIdx+1
|
||||
sym.positionImpl = p.breakIdx+1
|
||||
# ^ IC: review this
|
||||
expr(p, n[1], d)
|
||||
endSimpleBlock(p, scope)
|
||||
|
||||
@@ -1255,7 +1259,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
initElifBranch(p.s(cpsStmts), ifStmt, orExpr)
|
||||
if exvar != nil:
|
||||
fillLocalName(p, exvar.sym)
|
||||
ensureMutable exvar.sym
|
||||
backendEnsureMutable exvar.sym
|
||||
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnStack)
|
||||
linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ),
|
||||
rdLoc(exvar.sym.loc), rope(etmp+1)])
|
||||
@@ -1304,7 +1308,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
if isImportedException(typeNode.typ, p.config):
|
||||
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
||||
fillLocalName(p, exvar.sym)
|
||||
ensureMutable exvar.sym
|
||||
backendEnsureMutable exvar.sym
|
||||
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnStack)
|
||||
startBlockWith(p):
|
||||
lineCg(p, cpsStmts, "catch ($1& $2) {$n", [getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc)])
|
||||
@@ -1396,7 +1400,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||
if t[i][j].isInfixAs():
|
||||
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
||||
fillLocalName(p, exvar.sym)
|
||||
ensureMutable exvar.sym
|
||||
backendEnsureMutable exvar.sym
|
||||
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnUnknown)
|
||||
startBlockWith(p):
|
||||
lineCg(p, cpsStmts, "catch ($1& $2) {$n", [getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc)])
|
||||
|
||||
@@ -844,6 +844,54 @@ proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKin
|
||||
m.s[cfsTypes].addField(name = "Field0", typ = ptrType(elemType))
|
||||
m.s[cfsTypes].addField(name = "Field1", typ = NimInt)
|
||||
|
||||
proc importedCppObject(m: BModule; t, tt: PType; check: var IntSet; kind: TypeDescKind; sig: SigHash; result: var Rope) =
|
||||
let cppNameAsRope = getTypeName(m, t, sig)
|
||||
let cppName = $cppNameAsRope
|
||||
var i = 0
|
||||
var chunkStart = 0
|
||||
|
||||
template addResultType(ty: untyped) =
|
||||
if ty == nil or ty.kind == tyVoid:
|
||||
result.add(CVoid)
|
||||
elif ty.kind == tyStatic:
|
||||
internalAssert m.config, ty.n != nil
|
||||
result.add ty.n.renderTree
|
||||
else:
|
||||
result.add getTypeDescAux(m, ty, check, kind)
|
||||
|
||||
while i < cppName.len:
|
||||
if cppName[i] == '\'':
|
||||
var chunkEnd = i-1
|
||||
var idx, stars: int = 0
|
||||
if scanCppGenericSlot(cppName, i, idx, stars):
|
||||
result.add cppName.substr(chunkStart, chunkEnd)
|
||||
chunkStart = i
|
||||
|
||||
let typeInSlot = resolveStarsInCppType(tt, idx + 1, stars)
|
||||
addResultType(typeInSlot)
|
||||
else:
|
||||
inc i
|
||||
|
||||
if chunkStart != 0:
|
||||
result.add cppName.substr(chunkStart)
|
||||
else:
|
||||
result = cppNameAsRope & "<"
|
||||
for needsComma, a in tt.genericInstParams:
|
||||
if needsComma: result.add(" COMMA ")
|
||||
addResultType(a)
|
||||
result.add("> ")
|
||||
# always call for sideeffects:
|
||||
assert t.kind != tyTuple
|
||||
discard getRecordDesc(m, t, result, check)
|
||||
# The resulting type will include commas and these won't play well
|
||||
# with the C macros for defining procs such as N_NIMCALL. We must
|
||||
# create a typedef for the type and use it in the proc signature:
|
||||
let typedefName = "TY" & $sig
|
||||
m.s[cfsTypes].addTypedef(name = typedefName):
|
||||
m.s[cfsTypes].add(result)
|
||||
m.typeCache[sig] = typedefName
|
||||
result = typedefName
|
||||
|
||||
proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope =
|
||||
# returns only the type's name
|
||||
var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
|
||||
@@ -859,7 +907,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
|
||||
# tyDistinct matters if it is an importc type
|
||||
result = getTypePre(m, origTyp.skipTypes(irrelevantForBackend-{tyOwned, tyDistinct}), sig)
|
||||
defer: # defer is the simplest in this case
|
||||
defer:
|
||||
if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
|
||||
addAbiCheck(m, t, result)
|
||||
|
||||
@@ -993,7 +1041,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
m.s[cfsTypes].addArrayTypedef(name = result, len = 1):
|
||||
m.s[cfsTypes].add(et)
|
||||
of tyArray:
|
||||
var n: BiggestInt = toInt64(lengthOrd(m.config, t))
|
||||
var n = toInt64(lengthOrd(m.config, t))
|
||||
if n <= 0: n = 1 # make an array of at least one element
|
||||
result = getTypeName(m, origTyp, sig)
|
||||
m.typeCache[sig] = result
|
||||
@@ -1004,52 +1052,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes
|
||||
of tyObject, tyTuple:
|
||||
let tt = origTyp.skipTypes({tyDistinct})
|
||||
if isImportedCppType(t) and tt.kind == tyGenericInst:
|
||||
let cppNameAsRope = getTypeName(m, t, sig)
|
||||
let cppName = $cppNameAsRope
|
||||
var i = 0
|
||||
var chunkStart = 0
|
||||
|
||||
template addResultType(ty: untyped) =
|
||||
if ty == nil or ty.kind == tyVoid:
|
||||
result.add(CVoid)
|
||||
elif ty.kind == tyStatic:
|
||||
internalAssert m.config, ty.n != nil
|
||||
result.add ty.n.renderTree
|
||||
else:
|
||||
result.add getTypeDescAux(m, ty, check, kind)
|
||||
|
||||
while i < cppName.len:
|
||||
if cppName[i] == '\'':
|
||||
var chunkEnd = i-1
|
||||
var idx, stars: int = 0
|
||||
if scanCppGenericSlot(cppName, i, idx, stars):
|
||||
result.add cppName.substr(chunkStart, chunkEnd)
|
||||
chunkStart = i
|
||||
|
||||
let typeInSlot = resolveStarsInCppType(tt, idx + 1, stars)
|
||||
addResultType(typeInSlot)
|
||||
else:
|
||||
inc i
|
||||
|
||||
if chunkStart != 0:
|
||||
result.add cppName.substr(chunkStart)
|
||||
else:
|
||||
result = cppNameAsRope & "<"
|
||||
for needsComma, a in tt.genericInstParams:
|
||||
if needsComma: result.add(" COMMA ")
|
||||
addResultType(a)
|
||||
result.add("> ")
|
||||
# always call for sideeffects:
|
||||
assert t.kind != tyTuple
|
||||
discard getRecordDesc(m, t, result, check)
|
||||
# The resulting type will include commas and these won't play well
|
||||
# with the C macros for defining procs such as N_NIMCALL. We must
|
||||
# create a typedef for the type and use it in the proc signature:
|
||||
let typedefName = "TY" & $sig
|
||||
m.s[cfsTypes].addTypedef(name = typedefName):
|
||||
m.s[cfsTypes].add(result)
|
||||
m.typeCache[sig] = typedefName
|
||||
result = typedefName
|
||||
importedCppObject(m, t, tt, check, kind, sig, result)
|
||||
else:
|
||||
result = cacheGetType(m.forwTypeCache, sig)
|
||||
if result == "":
|
||||
@@ -1862,6 +1865,12 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn
|
||||
if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
|
||||
discard genTypeInfoV1(m, t, info)
|
||||
|
||||
proc myModuleOpenForCodegen(m: BModule; idx: FileIndex): bool {.inline.} =
|
||||
if moduleOpenForCodegen(m.g.graph, idx):
|
||||
result = idx.int < m.g.mods.len and m.g.mods[idx.int] != nil
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope =
|
||||
let origType = t
|
||||
# distinct types can have their own destructors
|
||||
@@ -1890,9 +1899,9 @@ proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope =
|
||||
m.typeInfoMarkerV2[sig] = result
|
||||
|
||||
let owner = t.skipTypes(typedescPtrs).itemId.module
|
||||
if owner != m.module.position and moduleOpenForCodegen(m.g.graph, FileIndex owner):
|
||||
if owner != m.module.position and myModuleOpenForCodegen(m, FileIndex owner):
|
||||
# make sure the type info is created in the owner module
|
||||
discard genTypeInfoV2(m.g.modules[owner], origType, info)
|
||||
discard genTypeInfoV2(m.g.mods[owner], origType, info)
|
||||
# reference the type info as extern here
|
||||
cgsym(m, "TNimTypeV2")
|
||||
declareNimType(m, "TNimTypeV2", result, owner)
|
||||
@@ -1975,9 +1984,9 @@ proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope =
|
||||
return prefixTI(result)
|
||||
|
||||
var owner = t.skipTypes(typedescPtrs).itemId.module
|
||||
if owner != m.module.position and moduleOpenForCodegen(m.g.graph, FileIndex owner):
|
||||
if owner != m.module.position and myModuleOpenForCodegen(m, FileIndex owner):
|
||||
# make sure the type info is created in the owner module
|
||||
discard genTypeInfoV1(m.g.modules[owner], origType, info)
|
||||
discard genTypeInfoV1(m.g.mods[owner], origType, info)
|
||||
# reference the type info as extern here
|
||||
cgsym(m, "TNimType")
|
||||
cgsym(m, "TNimNode")
|
||||
|
||||
@@ -69,19 +69,19 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
|
||||
# TODO fixme
|
||||
if m.config.symbolFiles == v2Sf or optCompress in m.config.globalOptions:
|
||||
let ms = s.itemId.module #getModule(s)
|
||||
result = m.g.modules[ms]
|
||||
result = m.g.mods[ms]
|
||||
elif m.config.cmd in {cmdNifC, cmdM}:
|
||||
var ms = getModule(s)
|
||||
registerModule m.g.graph, ms
|
||||
if ms.position >= m.g.modules.len:
|
||||
if ms.position >= m.g.mods.len:
|
||||
result = newModule(m.g, ms, m.config, idGeneratorFromModule(ms))
|
||||
else:
|
||||
result = m.g.modules[ms.position]
|
||||
result = m.g.mods[ms.position]
|
||||
if result == nil:
|
||||
result = newModule(m.g, ms, m.config, idGeneratorFromModule(ms))
|
||||
else:
|
||||
var ms = getModule(s)
|
||||
result = m.g.modules[ms.position]
|
||||
result = m.g.mods[ms.position]
|
||||
|
||||
proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc =
|
||||
result = TLoc(k: k, storage: s, lode: lode,
|
||||
@@ -135,10 +135,10 @@ proc getModuleDllPath(m: BModule): Rope =
|
||||
result = makeCString(dir.string & "/" & filename)
|
||||
|
||||
proc getModuleDllPath(m: BModule, module: int): Rope =
|
||||
result = getModuleDllPath(m.g.modules[module])
|
||||
result = getModuleDllPath(m.g.mods[module])
|
||||
|
||||
proc getModuleDllPath(m: BModule, s: PSym): Rope =
|
||||
result = getModuleDllPath(m.g.modules[s.itemId.module])
|
||||
result = getModuleDllPath(m.g.mods[s.itemId.module])
|
||||
|
||||
import std/macros
|
||||
|
||||
@@ -339,7 +339,10 @@ proc genLineDir(p: BProc, t: PNode) =
|
||||
let line = t.info.safeLineNm
|
||||
|
||||
if optEmbedOrigSrc in p.config.globalOptions:
|
||||
p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
|
||||
var code = sourceLine(p.config, t.info)
|
||||
if code.endsWith('\\'):
|
||||
code.add "#"
|
||||
p.s(cpsStmts).add("// " & code & "\L")
|
||||
let lastFileIndex = p.lastLineInfo.fileIndex
|
||||
let freshLine = freshLineInfo(p, t.info)
|
||||
if freshLine:
|
||||
@@ -1553,7 +1556,7 @@ proc genProcLvl2(m: BModule, prc: PSym) =
|
||||
# externally-to-the-current-module defined proc, also important
|
||||
# to do the declaredProtos check before the call to genProcPrototype
|
||||
if isReloadable(m, prc) and prc.id notin m.declaredProtos and
|
||||
q != nil and q.module.id != m.module.id:
|
||||
q != nil and not sameModules(q.module, m.module):
|
||||
m.s[cfsDynLibInit].add('\t')
|
||||
m.s[cfsDynLibInit].addAssignment(prc.loc.snippet,
|
||||
cCast(getProcTypeCast(m, prc),
|
||||
@@ -1611,7 +1614,7 @@ proc genVarPrototype(m: BModule, n: PNode) =
|
||||
|
||||
if (lfNoDecl in sym.loc.flags) or contains(m.declaredThings, sym.id):
|
||||
return
|
||||
if sym.owner.id != m.module.id:
|
||||
if not sameOwners(sym.owner, m.module):
|
||||
# else we already have the symbol generated!
|
||||
assert(sym.loc.snippet != "")
|
||||
incl(m.declaredThings, sym.id)
|
||||
@@ -1730,9 +1733,12 @@ proc genMainProcs(m: BModule) =
|
||||
|
||||
proc genMainProcsWithResult(m: BModule) =
|
||||
genMainProcs(m)
|
||||
var res = "nim_program_result"
|
||||
if m.hcrOn: res = cDeref(res)
|
||||
m.s[cfsProcs].addReturn(res)
|
||||
if m.config.cmd != cmdNifC:
|
||||
var res = "nim_program_result"
|
||||
if m.hcrOn: res = cDeref(res)
|
||||
m.s[cfsProcs].addReturn(res)
|
||||
else:
|
||||
m.s[cfsProcs].addReturn(cIntValue(0))
|
||||
|
||||
proc genNimMainInner(m: BModule) =
|
||||
m.s[cfsProcs].addDeclWithVisibility(Private):
|
||||
@@ -1970,7 +1976,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
|
||||
|
||||
if m.hcrOn:
|
||||
var hcrModuleMeta = newBuilder("")
|
||||
let systemModulePath = getModuleDllPath(m, g.modules[g.graph.config.m.systemFileIdx.int].module)
|
||||
let systemModulePath = getModuleDllPath(m, g.mods[g.graph.config.m.systemFileIdx.int].module)
|
||||
let mainModulePath = getModuleDllPath(m, m.module)
|
||||
hcrModuleMeta.addDeclWithVisibility(Private):
|
||||
hcrModuleMeta.addArrayVarWithInitializer(kind = Local,
|
||||
@@ -1987,7 +1993,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
|
||||
g.graph.importDeps.withValue(FileIndex(m.module.position), deps):
|
||||
for curr in deps[]:
|
||||
hcrModuleMeta.addField(modules, ""):
|
||||
hcrModuleMeta.add(getModuleDllPath(m, g.modules[curr.int].module))
|
||||
hcrModuleMeta.add(getModuleDllPath(m, g.mods[curr.int].module))
|
||||
hcrModuleMeta.addField(modules, ""):
|
||||
hcrModuleMeta.add("\"\"")
|
||||
hcrModuleMeta.addDeclWithVisibility(ExportLib):
|
||||
@@ -2180,6 +2186,8 @@ proc genInitCode(m: BModule) =
|
||||
else:
|
||||
prcBody.add(extract(m.thing.s(section)))
|
||||
|
||||
#echo "PRE INIT PROC ", m.module.name.s, " ", m.s[cfsVars].buf.len
|
||||
|
||||
if m.preInitProc.s(cpsInit).buf.len > 0 or m.preInitProc.s(cpsStmts).buf.len > 0:
|
||||
# Give this small function its own scope
|
||||
prcBody.addScope():
|
||||
@@ -2396,10 +2404,10 @@ proc newModule(g: BModuleList; module: PSym; conf: ConfigRef; idgen: IdGenerator
|
||||
# we should create only one cgen module for each module sym
|
||||
result = rawNewModule(g, module, conf)
|
||||
result.idgen = idgen
|
||||
if module.position >= g.modules.len:
|
||||
setLen(g.modules, module.position + 1)
|
||||
if module.position >= g.mods.len:
|
||||
setLen(g.mods, module.position + 1)
|
||||
#growCache g.modules, module.position
|
||||
g.modules[module.position] = result
|
||||
g.mods[module.position] = result
|
||||
|
||||
template injectG() {.dirty.} =
|
||||
if graph.backend == nil:
|
||||
@@ -2533,13 +2541,7 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||
rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
|
||||
result = true
|
||||
|
||||
# We need 2 different logics here: pending modules (including
|
||||
# 'nim__dat') may require file merging for the combination of dead code
|
||||
# elimination and incremental compilation! Non pending modules need no
|
||||
# such logic and in fact the logic hurts for the main module at least;
|
||||
# it would generate multiple 'main' procs, for instance.
|
||||
|
||||
proc writeModule(m: BModule, pending: bool) =
|
||||
proc writeModule(m: BModule) =
|
||||
let cfile = getCFile(m)
|
||||
if moduleHasChanged(m.g.graph, m.module):
|
||||
genInitCode(m)
|
||||
@@ -2668,7 +2670,7 @@ proc genForwardedProcs(g: BModuleList) =
|
||||
while g.forwardedProcs.len > 0:
|
||||
let
|
||||
prc = g.forwardedProcs.pop()
|
||||
m = g.modules[prc.itemId.module]
|
||||
m = g.mods[prc.itemId.module]
|
||||
if sfForward in prc.flags:
|
||||
internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
|
||||
|
||||
@@ -2684,6 +2686,6 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
|
||||
genForwardedProcs(g)
|
||||
|
||||
for m in cgenModules(g):
|
||||
m.writeModule(pending=true)
|
||||
m.writeModule()
|
||||
writeMapping(config, g.mapping)
|
||||
if g.generatedHeader != nil: writeHeader(g.generatedHeader)
|
||||
|
||||
@@ -117,7 +117,7 @@ type
|
||||
BModuleList* = ref object of RootObj
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Builder
|
||||
mapping*: Rope # the generated mapping file (if requested)
|
||||
modules*: seq[BModule] # list of all compiled modules
|
||||
mods*: seq[BModule] # list of all compiled modules
|
||||
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
||||
forwardedProcs*: seq[PSym] # procs that did not yet have a body
|
||||
generatedHeader*: BModule
|
||||
|
||||
@@ -499,7 +499,7 @@ proc parseCommand*(command: string): Command =
|
||||
of "nop", "help": cmdNop
|
||||
of "jsonscript": cmdJsonscript
|
||||
of "nifc": cmdNifC # generate C from NIF files
|
||||
of "deps": cmdDeps # generate .build.nif for nifmake
|
||||
of "ic": cmdIc # generate .build.nif for nifmake
|
||||
else: cmdUnknown
|
||||
|
||||
proc setCmd*(conf: ConfigRef, cmd: Command) =
|
||||
@@ -1000,7 +1000,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
# xxx maybe also ic, since not in help?
|
||||
if pass in {passCmd2, passPP}:
|
||||
case arg.normalize
|
||||
of "on": conf.symbolFiles = v2Sf
|
||||
of "on": conf.ic = true
|
||||
of "legacy": conf.symbolFiles = v2Sf
|
||||
of "off": conf.symbolFiles = disabledSf
|
||||
of "writeonly": conf.symbolFiles = writeOnlySf
|
||||
of "readonly": conf.symbolFiles = readOnlySf
|
||||
@@ -1108,6 +1109,9 @@ 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
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
## for details. Note this is a first implementation and only the "Concept matching"
|
||||
## section has been implemented.
|
||||
|
||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
import ast, semdata, lookups, lineinfos, idents, msgs, renderer, types, layeredtable
|
||||
|
||||
import std/intsets
|
||||
import std/sets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
@@ -73,18 +73,20 @@ type
|
||||
MatchFlags* = enum
|
||||
mfDontBind # Do not bind generic parameters
|
||||
mfCheckGeneric # formal <- formal comparison as opposed to formal <- operand
|
||||
|
||||
|
||||
ConceptTypePair = tuple[conceptId, typeId: ItemId]
|
||||
## Pair of (concept type id, implementation type id) used for cycle detection
|
||||
|
||||
MatchCon = object ## Context we pass around during concept matching.
|
||||
bindings: LayeredIdTable
|
||||
marker: IntSet ## Some protection against wild runaway recursions.
|
||||
marker: HashSet[ConceptTypePair] ## Tracks (concept, type) pairs being checked to detect cycles.
|
||||
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
|
||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
||||
## cannot be fixed that easily.
|
||||
## Thus we special case it here.
|
||||
concpt: PType ## current concept being evaluated
|
||||
depthCount = 0
|
||||
flags: set[MatchFlags]
|
||||
|
||||
|
||||
MatchKind = enum
|
||||
mkNoMatch, mkSubset, mkSame
|
||||
|
||||
@@ -188,32 +190,48 @@ iterator traverseTyOr(t: PType): PType {. closure .}=
|
||||
proc matchConceptToImpl(c: PContext, f, potentialImpl: PType; m: var MatchCon): bool =
|
||||
assert not(potentialImpl.reduceToBase.kind == tyConcept)
|
||||
let concpt = f.reduceToBase
|
||||
if m.depthCount > 0:
|
||||
# concepts that are more then 2 levels deep are treated like
|
||||
# tyAnything to stop dependencies from getting out of control
|
||||
|
||||
# Handle self-referential concepts: when a concept references itself in its body
|
||||
# (e.g., `A = concept; proc test(x: Self, y: A)`), the inner type A has n=nil.
|
||||
# We detect this by checking if the concept has the same symbol name as the
|
||||
# one we're currently matching and has no body (n=nil).
|
||||
if concpt.n.isNil:
|
||||
if concpt.sym != nil and m.concpt.sym != nil and
|
||||
concpt.sym == m.concpt.sym:
|
||||
# Self-reference: check if potentialImpl matches what we're already checking
|
||||
return potentialImpl.id == m.potentialImplementation.id
|
||||
# Concept without body that's not a self-reference - cannot match
|
||||
return false
|
||||
|
||||
# Cycle detection: track (concept, type) pairs to prevent infinite recursion.
|
||||
# Returns true on cycle (coinductive semantics) to support co-dependent concepts.
|
||||
let pair: ConceptTypePair = (concpt.itemId, potentialImpl.itemId)
|
||||
if pair in m.marker:
|
||||
return true
|
||||
m.marker.incl pair
|
||||
|
||||
var efPot = potentialImpl
|
||||
if potentialImpl.isSelf:
|
||||
if m.concpt.n == concpt.n:
|
||||
m.marker.excl pair
|
||||
return true
|
||||
efPot = m.potentialImplementation
|
||||
|
||||
|
||||
var oldBindings = m.bindings
|
||||
m.bindings = newTypeMapLayer(m.bindings)
|
||||
let oldPotentialImplementation = m.potentialImplementation
|
||||
m.potentialImplementation = efPot
|
||||
let oldConcept = m.concpt
|
||||
m.concpt = concpt
|
||||
|
||||
|
||||
var invocation: PType = nil
|
||||
if f.kind in {tyGenericInvocation, tyGenericInst}:
|
||||
invocation = f
|
||||
inc m.depthCount
|
||||
result = processConcept(c, concpt, invocation, oldBindings, m)
|
||||
dec m.depthCount
|
||||
m.potentialImplementation = oldPotentialImplementation
|
||||
m.concpt = oldConcept
|
||||
m.bindings = oldBindings
|
||||
m.marker.excl pair
|
||||
|
||||
proc cmpConceptDefs(c: PContext, fn, an: PNode, m: var MatchCon): bool=
|
||||
if fn.kind != an.kind:
|
||||
@@ -610,7 +628,7 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var LayeredIdTable
|
||||
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
|
||||
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
|
||||
## we do not add any bindings at all on an unsuccessful match!
|
||||
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags)
|
||||
var m = MatchCon(bindings: bindings, potentialImplementation: arg, concpt: concpt, flags: flags, marker: initHashSet[ConceptTypePair]())
|
||||
if arg.isConcept:
|
||||
result = conceptsMatch(c, concpt.reduceToBase, arg.reduceToBase, m) >= mkSubset
|
||||
elif arg.acceptsAllTypes:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## This enables incremental and parallel compilation using the `m` switch.
|
||||
|
||||
import std / [os, tables, sets, times, osproc, strutils]
|
||||
import options, msgs, pathutils, lineinfos
|
||||
import options, msgs, lineinfos, pathutils
|
||||
|
||||
import "../dist/nimony/src/lib" / [nifstreams, nifcursors, bitabs, nifreader, nifbuilder]
|
||||
import "../dist/nimony/src/gear2" / modnames
|
||||
@@ -47,15 +47,18 @@ proc semmedFile(c: DepContext; f: FilePair): string =
|
||||
|
||||
proc findNifler(): string =
|
||||
# Look for nifler in common locations
|
||||
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 = ""
|
||||
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")
|
||||
|
||||
proc runNifler(c: DepContext; nimFile: string): bool =
|
||||
## Run nifler deps on a file if needed. Returns true on success.
|
||||
@@ -220,12 +223,14 @@ proc traverseDeps(c: var DepContext; pair: FilePair; current: Node) =
|
||||
|
||||
proc generateBuildFile(c: DepContext): string =
|
||||
## Generate the .build.nif file for nifmake
|
||||
result = getNimcacheDir(c.config).string / c.nodes[0].files[0].modname & ".build.nif"
|
||||
createDir("nifcache")
|
||||
result = "nifcache" / c.nodes[0].files[0].modname & ".build.nif"
|
||||
#getNimcacheDir(c.config).string / c.nodes[0].files[0].modname & ".build.nif"
|
||||
|
||||
var b = nifbuilder.open(result)
|
||||
defer: b.close()
|
||||
|
||||
b.addHeader("nim deps", "nifmake")
|
||||
b.addHeader("nim ic", "nifmake")
|
||||
b.addTree "stmts"
|
||||
|
||||
# Define nifler command
|
||||
@@ -245,6 +250,22 @@ proc generateBuildFile(c: DepContext): string =
|
||||
b.addSymbolDef "nim_m"
|
||||
b.addStrLit getAppFilename()
|
||||
b.addStrLit "m"
|
||||
b.addStrLit "--nimcache:nifcache"
|
||||
# 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:nifcache"
|
||||
# Add search paths
|
||||
for p in c.config.searchPaths:
|
||||
b.addStrLit "--path:" & p.string
|
||||
@@ -279,6 +300,8 @@ 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)
|
||||
@@ -292,15 +315,26 @@ proc generateBuildFile(c: DepContext): string =
|
||||
b.addTree "output"
|
||||
b.addStrLit c.semmedFile(pair)
|
||||
b.endTree()
|
||||
b.addTree "args"
|
||||
b.addStrLit pair.nimFile
|
||||
b.endTree()
|
||||
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) and .nif file (dependency)
|
||||
b.addTree "input"
|
||||
b.addStrLit mainNif
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.addStrLit exeFile
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
b.endTree() # stmts
|
||||
|
||||
proc commandDeps*(conf: ConfigRef) =
|
||||
## Main entry point for `nim deps`
|
||||
proc commandIc*(conf: ConfigRef) =
|
||||
## Main entry point for `nim ic`
|
||||
when not defined(nimKochBootstrap):
|
||||
let nifler = findNifler()
|
||||
if nifler.len == 0:
|
||||
@@ -329,12 +363,27 @@ proc commandDeps*(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
|
||||
rootNode.deps.add sysNode.id
|
||||
|
||||
# Process dependencies
|
||||
traverseDeps(c, rootPair, rootNode)
|
||||
|
||||
# Generate build file
|
||||
let buildFile = generateBuildFile(c)
|
||||
rawMessage(conf, hintSuccess, "generated: " & buildFile)
|
||||
rawMessage(conf, hintSuccess, "run: nifmake run " & 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)
|
||||
else:
|
||||
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")
|
||||
rawMessage(conf, errGenerated, "nim ic not available in bootstrap build")
|
||||
|
||||
@@ -19,7 +19,7 @@ import
|
||||
wordrecg, syntaxes, renderer, lexer,
|
||||
packages/docutils/[rst, rstidx, rstgen, dochelpers],
|
||||
trees, types,
|
||||
typesrenderer, astalgo, lineinfos,
|
||||
typesrenderer, lineinfos,
|
||||
pathutils, nimpaths, renderverbatim, packages
|
||||
import packages/docutils/rstast except FileIndex, TLineInfo
|
||||
|
||||
@@ -433,6 +433,9 @@ 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,
|
||||
@@ -1176,8 +1179,12 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false):
|
||||
"col": %n.info.col}
|
||||
)
|
||||
if comm != nil:
|
||||
result.rst = comm
|
||||
result.rstField = "description"
|
||||
if optDocRaw in d.conf.globalOptions:
|
||||
result.json["description"] = %comm.text
|
||||
else:
|
||||
result.rst = comm
|
||||
result.rstField = "description"
|
||||
|
||||
if r.buf.len > 0:
|
||||
result.json["code"] = %r.buf
|
||||
if k in routineKinds:
|
||||
@@ -1418,7 +1425,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: ItemPre = default(ItemPre)
|
||||
var comm = default(ItemPre)
|
||||
getAllRunnableExamples(d, n, comm)
|
||||
if comm.len != 0: d.modDescPre.add(comm)
|
||||
else: discard
|
||||
|
||||
@@ -40,7 +40,7 @@ proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
|
||||
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).modules[m.module.position]
|
||||
var bmod = BModuleList(g.backend).mods[m.module.position]
|
||||
assert bmod != nil
|
||||
bmod.flags.incl useAliveDataFromDce
|
||||
bmod.alive = move alive[m.module.position]
|
||||
|
||||
@@ -404,140 +404,140 @@ proc parse*(t: typedesc[TSymKind]; s: string): TSymKind =
|
||||
|
||||
proc toNifTag*(s: TTypeKind): string =
|
||||
case s
|
||||
of tyNone: "none"
|
||||
of tyBool: "bool"
|
||||
of tyChar: "char"
|
||||
of tyEmpty: "empty"
|
||||
of tyAlias: "alias"
|
||||
of tyNil: "nil"
|
||||
of tyUntyped: "untyped"
|
||||
of tyTyped: "typed"
|
||||
of tyTypeDesc: "typedesc"
|
||||
of tyGenericInvocation: "ginvoke"
|
||||
of tyGenericBody: "gbody"
|
||||
of tyGenericInst: "ginst"
|
||||
of tyGenericParam: "gparam"
|
||||
of tyDistinct: "distinct"
|
||||
of tyEnum: "enum"
|
||||
of tyOrdinal: "ordinal"
|
||||
of tyArray: "array"
|
||||
of tyObject: "object"
|
||||
of tyTuple: "tuple"
|
||||
of tySet: "set"
|
||||
of tyRange: "range"
|
||||
of tyPtr: "ptr"
|
||||
of tyRef: "ref"
|
||||
of tyVar: "mut"
|
||||
of tySequence: "seq"
|
||||
of tyProc: "proctype"
|
||||
of tyPointer: "pointer"
|
||||
of tyOpenArray: "openarray"
|
||||
of tyString: "string"
|
||||
of tyCstring: "cstring"
|
||||
of tyForward: "forward"
|
||||
of tyInt: "int"
|
||||
of tyInt8: "int8"
|
||||
of tyInt16: "int16"
|
||||
of tyInt32: "int32"
|
||||
of tyInt64: "int64"
|
||||
of tyFloat: "float"
|
||||
of tyFloat32: "float32"
|
||||
of tyFloat64: "float64"
|
||||
of tyFloat128: "float128"
|
||||
of tyUInt: "uint"
|
||||
of tyUInt8: "uint8"
|
||||
of tyUInt16: "uint16"
|
||||
of tyUInt32: "uint32"
|
||||
of tyUInt64: "uint64"
|
||||
of tyOwned: "owned"
|
||||
of tySink: "sink"
|
||||
of tyLent: "lent"
|
||||
of tyVarargs: "varargs"
|
||||
of tyUncheckedArray: "uarray"
|
||||
of tyError: "error"
|
||||
of tyBuiltInTypeClass: "bconcept"
|
||||
of tyUserTypeClass: "uconcept"
|
||||
of tyUserTypeClassInst: "uconceptinst"
|
||||
of tyCompositeTypeClass: "cconcept"
|
||||
of tyInferred: "inferred"
|
||||
of tyAnd: "and"
|
||||
of tyOr: "or"
|
||||
of tyNot: "not"
|
||||
of tyAnything: "anything"
|
||||
of tyStatic: "static"
|
||||
of tyFromExpr: "fromx"
|
||||
of tyConcept: "concept"
|
||||
of tyVoid: "void"
|
||||
of tyIterable: "iterable"
|
||||
of tyNone: "n0"
|
||||
of tyBool: "b0"
|
||||
of tyChar: "c0"
|
||||
of tyEmpty: "e0"
|
||||
of tyAlias: "a0"
|
||||
of tyNil: "n1"
|
||||
of tyUntyped: "U0"
|
||||
of tyTyped: "t0"
|
||||
of tyTypeDesc: "t1"
|
||||
of tyGenericInvocation: "g0"
|
||||
of tyGenericBody: "g1"
|
||||
of tyGenericInst: "g2"
|
||||
of tyGenericParam: "g4"
|
||||
of tyDistinct: "d0"
|
||||
of tyEnum: "e1"
|
||||
of tyOrdinal: "o0"
|
||||
of tyArray: "a1"
|
||||
of tyObject: "o1"
|
||||
of tyTuple: "t2"
|
||||
of tySet: "s0"
|
||||
of tyRange: "r0"
|
||||
of tyPtr: "p0"
|
||||
of tyRef: "r1"
|
||||
of tyVar: "v0"
|
||||
of tySequence: "s1"
|
||||
of tyProc: "p1"
|
||||
of tyPointer: "p2"
|
||||
of tyOpenArray: "o3"
|
||||
of tyString: "s2"
|
||||
of tyCstring: "c1"
|
||||
of tyForward: "F0"
|
||||
of tyInt: "i0"
|
||||
of tyInt8: "i1"
|
||||
of tyInt16: "i2"
|
||||
of tyInt32: "i3"
|
||||
of tyInt64: "i4"
|
||||
of tyFloat: "f0"
|
||||
of tyFloat32: "f1"
|
||||
of tyFloat64: "f2"
|
||||
of tyFloat128: "f3"
|
||||
of tyUInt: "u0"
|
||||
of tyUInt8: "u1"
|
||||
of tyUInt16: "u2"
|
||||
of tyUInt32: "u3"
|
||||
of tyUInt64: "u4"
|
||||
of tyOwned: "o2"
|
||||
of tySink: "s3"
|
||||
of tyLent: "L0"
|
||||
of tyVarargs: "v1"
|
||||
of tyUncheckedArray: "U1"
|
||||
of tyError: "e2"
|
||||
of tyBuiltInTypeClass: "b1"
|
||||
of tyUserTypeClass: "U2"
|
||||
of tyUserTypeClassInst: "U3"
|
||||
of tyCompositeTypeClass: "c2"
|
||||
of tyInferred: "I0"
|
||||
of tyAnd: "a2"
|
||||
of tyOr: "o4"
|
||||
of tyNot: "n2"
|
||||
of tyAnything: "a3"
|
||||
of tyStatic: "s4"
|
||||
of tyFromExpr: "F1"
|
||||
of tyConcept: "c3"
|
||||
of tyVoid: "v2"
|
||||
of tyIterable: "I1"
|
||||
|
||||
|
||||
proc parse*(t: typedesc[TTypeKind]; s: string): TTypeKind =
|
||||
case s
|
||||
of "none": tyNone
|
||||
of "bool": tyBool
|
||||
of "char": tyChar
|
||||
of "empty": tyEmpty
|
||||
of "alias": tyAlias
|
||||
of "nil": tyNil
|
||||
of "untyped": tyUntyped
|
||||
of "typed": tyTyped
|
||||
of "typedesc": tyTypeDesc
|
||||
of "ginvoke": tyGenericInvocation
|
||||
of "gbody": tyGenericBody
|
||||
of "ginst": tyGenericInst
|
||||
of "gparam": tyGenericParam
|
||||
of "distinct": tyDistinct
|
||||
of "enum": tyEnum
|
||||
of "ordinal": tyOrdinal
|
||||
of "array": tyArray
|
||||
of "object": tyObject
|
||||
of "tuple": tyTuple
|
||||
of "set": tySet
|
||||
of "range": tyRange
|
||||
of "ptr": tyPtr
|
||||
of "ref": tyRef
|
||||
of "mut": tyVar
|
||||
of "seq": tySequence
|
||||
of "proctype": tyProc
|
||||
of "pointer": tyPointer
|
||||
of "openarray": tyOpenArray
|
||||
of "string": tyString
|
||||
of "cstring": tyCstring
|
||||
of "forward": tyForward
|
||||
of "int": tyInt
|
||||
of "int8": tyInt8
|
||||
of "int16": tyInt16
|
||||
of "int32": tyInt32
|
||||
of "int64": tyInt64
|
||||
of "float": tyFloat
|
||||
of "float32": tyFloat32
|
||||
of "float64": tyFloat64
|
||||
of "float128": tyFloat128
|
||||
of "uint": tyUInt
|
||||
of "uint8": tyUInt8
|
||||
of "uint16": tyUInt16
|
||||
of "uint32": tyUInt32
|
||||
of "uint64": tyUInt64
|
||||
of "owned": tyOwned
|
||||
of "sink": tySink
|
||||
of "lent": tyLent
|
||||
of "varargs": tyVarargs
|
||||
of "uarray": tyUncheckedArray
|
||||
of "error": tyError
|
||||
of "bconcept": tyBuiltInTypeClass
|
||||
of "uconcept": tyUserTypeClass
|
||||
of "uconceptinst": tyUserTypeClassInst
|
||||
of "cconcept": tyCompositeTypeClass
|
||||
of "inferred": tyInferred
|
||||
of "and": tyAnd
|
||||
of "or": tyOr
|
||||
of "not": tyNot
|
||||
of "anything": tyAnything
|
||||
of "static": tyStatic
|
||||
of "fromx": tyFromExpr
|
||||
of "concept": tyConcept
|
||||
of "void": tyVoid
|
||||
of "iterable": tyIterable
|
||||
of "n0": tyNone
|
||||
of "b0": tyBool
|
||||
of "c0": tyChar
|
||||
of "e0": tyEmpty
|
||||
of "a0": tyAlias
|
||||
of "n1": tyNil
|
||||
of "U0": tyUntyped
|
||||
of "t0": tyTyped
|
||||
of "t1": tyTypeDesc
|
||||
of "g0": tyGenericInvocation
|
||||
of "g1": tyGenericBody
|
||||
of "g2": tyGenericInst
|
||||
of "g4": tyGenericParam
|
||||
of "d0": tyDistinct
|
||||
of "e1": tyEnum
|
||||
of "o0": tyOrdinal
|
||||
of "a1": tyArray
|
||||
of "o1": tyObject
|
||||
of "t2": tyTuple
|
||||
of "s0": tySet
|
||||
of "r0": tyRange
|
||||
of "p0": tyPtr
|
||||
of "r1": tyRef
|
||||
of "v0": tyVar
|
||||
of "s1": tySequence
|
||||
of "p1": tyProc
|
||||
of "p2": tyPointer
|
||||
of "o3": tyOpenArray
|
||||
of "s2": tyString
|
||||
of "c1": tyCstring
|
||||
of "F0": tyForward
|
||||
of "i0": tyInt
|
||||
of "i1": tyInt8
|
||||
of "i2": tyInt16
|
||||
of "i3": tyInt32
|
||||
of "i4": tyInt64
|
||||
of "f0": tyFloat
|
||||
of "f1": tyFloat32
|
||||
of "f2": tyFloat64
|
||||
of "f3": tyFloat128
|
||||
of "u0": tyUInt
|
||||
of "u1": tyUInt8
|
||||
of "u2": tyUInt16
|
||||
of "u3": tyUInt32
|
||||
of "u4": tyUInt64
|
||||
of "o2": tyOwned
|
||||
of "s3": tySink
|
||||
of "L0": tyLent
|
||||
of "v1": tyVarargs
|
||||
of "U1": tyUncheckedArray
|
||||
of "e2": tyError
|
||||
of "b1": tyBuiltInTypeClass
|
||||
of "U2": tyUserTypeClass
|
||||
of "U3": tyUserTypeClassInst
|
||||
of "c2": tyCompositeTypeClass
|
||||
of "I0": tyInferred
|
||||
of "a2": tyAnd
|
||||
of "o4": tyOr
|
||||
of "n2": tyNot
|
||||
of "a3": tyAnything
|
||||
of "s4": tyStatic
|
||||
of "F1": tyFromExpr
|
||||
of "c3": tyConcept
|
||||
of "v2": tyVoid
|
||||
of "I1": tyIterable
|
||||
else: tyNone
|
||||
|
||||
|
||||
|
||||
@@ -370,8 +370,14 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
|
||||
paddingAtEnd: t.paddingAtEnd)
|
||||
storeNode(p, t, n)
|
||||
p.typeInst = t.typeInst.storeType(c, m)
|
||||
for kid in kids t:
|
||||
p.types.add kid.storeType(c, m)
|
||||
if t.kind == tyProc and t.len > 0:
|
||||
# if kind == tyProc, parameter types are stored in t.n
|
||||
# and you can access them with `kits` iterator.
|
||||
# return type is stored in t.sons[0].
|
||||
p.types.add t[0].storeType(c, m)
|
||||
else:
|
||||
for kid in kids t:
|
||||
p.types.add kid.storeType(c, m)
|
||||
c.addMissing t.sym
|
||||
p.sym = t.sym.safeItemId(c, m)
|
||||
c.addMissing t.owner
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Supports the "nim check --ic:on --defusages:FILE,LINE,COL"
|
||||
## 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.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## This module implements the symbol importing mechanism.
|
||||
|
||||
import
|
||||
ast, astalgo, msgs, options, idents, lookups,
|
||||
ast, msgs, options, idents, lookups,
|
||||
semdata, modulepaths, sigmatch, lineinfos,
|
||||
modulegraphs, wordrecg
|
||||
from std/strutils import `%`, startsWith
|
||||
|
||||
@@ -969,7 +969,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
|
||||
isInProc
|
||||
|
||||
if isGlobalPragma:
|
||||
c.graph.procGlobals.add n
|
||||
c.graph.procGlobals.add newTree(nkFastAsgn, v, ri)
|
||||
else:
|
||||
let value = moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
|
||||
result.add value
|
||||
|
||||
@@ -729,44 +729,47 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mShrI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))")
|
||||
applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> (BigInt($2) & 63n))")
|
||||
elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("($1 >> BigInt($2))")
|
||||
applyFormat("($1 >> (BigInt($2) & 63n))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyInt..tyInt32}:
|
||||
let trimmerU = unsignedTrimmer(typ.size)
|
||||
let trimmerS = signedTrimmer(typ.size)
|
||||
r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS]
|
||||
r.res = "((($1 $2) >>> ($3 & $5)) $4)" % [xLoc, trimmerU, yLoc, trimmerS, $bitmask]
|
||||
else:
|
||||
applyFormat("($1 >>> $2)")
|
||||
r.res = "($1 >>> ($2 & $3))" % [xLoc, yLoc, $bitmask]
|
||||
of mShlI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.size == 8:
|
||||
if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asIntN(64, $1 << BigInt($2))")
|
||||
applyFormat("BigInt.asIntN(64, $1 << (BigInt($2) & 63n))")
|
||||
elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("BigInt.asUintN(64, $1 << BigInt($2))")
|
||||
applyFormat("BigInt.asUintN(64, $1 << (BigInt($2) & 63n))")
|
||||
else:
|
||||
applyFormat("($1 * Math.pow(2, $2))")
|
||||
applyFormat("($1 * Math.pow(2, ($2 & 63)))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyUInt..tyUInt32}:
|
||||
let trimmer = unsignedTrimmer(typ.size)
|
||||
r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
|
||||
r.res = "(($1 << ($2 & $4)) $3)" % [xLoc, yLoc, trimmer, $bitmask]
|
||||
else:
|
||||
let trimmer = signedTrimmer(typ.size)
|
||||
r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
|
||||
r.res = "(($1 << ($2 & $4)) $3)" % [xLoc, yLoc, trimmer, $bitmask]
|
||||
of mAshrI:
|
||||
let typ = n[1].typ.skipTypes(abstractVarRange)
|
||||
if typ.size == 8:
|
||||
if optJsBigInt64 in p.config.globalOptions:
|
||||
applyFormat("($1 >> BigInt($2))")
|
||||
applyFormat("($1 >> (BigInt($2) & 63n))")
|
||||
else:
|
||||
applyFormat("Math.floor($1 / Math.pow(2, $2))")
|
||||
applyFormat("Math.floor($1 / Math.pow(2, ($2 & 63)))")
|
||||
else:
|
||||
let bitmask = typ.size * 8 - 1
|
||||
if typ.kind in {tyUInt..tyUInt32}:
|
||||
applyFormat("($1 >>> $2)")
|
||||
r.res = "($1 >>> ($2 & $3)))" % [xLoc, yLoc, $bitmask]
|
||||
else:
|
||||
applyFormat("($1 >> $2)")
|
||||
r.res = "($1 >> ($2 & $3))" % [xLoc, yLoc, $bitmask]
|
||||
of mBitandI: bitwiseExpr("&")
|
||||
of mBitorI: bitwiseExpr("|")
|
||||
of mBitxorI: bitwiseExpr("^")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import std/[tables]
|
||||
import ast, astalgo
|
||||
|
||||
type
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# Built-in types and compilerprocs are registered here.
|
||||
|
||||
import
|
||||
ast, astalgo, msgs, platform, idents,
|
||||
ast, msgs, platform, idents,
|
||||
modulegraphs, lineinfos
|
||||
|
||||
export createMagic
|
||||
|
||||
@@ -442,18 +442,20 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of cmdM:
|
||||
# cmdM uses NIF files, not ROD files
|
||||
graph.config.symbolFiles = disabledSf
|
||||
setUseIc(false)
|
||||
setUseIc(true)
|
||||
commandCheck(graph)
|
||||
of cmdNifC:
|
||||
setUseIc(true)
|
||||
# Generate C code from NIF files
|
||||
wantMainModule(conf)
|
||||
setOutFile(conf)
|
||||
commandNifC(graph)
|
||||
of cmdDeps:
|
||||
of cmdIc:
|
||||
# Generate .build.nif for nifmake
|
||||
setUseIc(true)
|
||||
wantMainModule(conf)
|
||||
when not defined(nimKochBootstrap):
|
||||
commandDeps(conf)
|
||||
commandIc(conf)
|
||||
else:
|
||||
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")
|
||||
of cmdParse:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a rod-file.
|
||||
|
||||
import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
|
||||
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]
|
||||
@@ -471,7 +471,7 @@ proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
|
||||
|
||||
proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
|
||||
result = nil
|
||||
if g.config.symbolFiles == disabledSf:
|
||||
if g.config.symbolFiles == disabledSf and optWithinConfigSystem notin g.config.globalOptions:
|
||||
# For NIF-based compilation, search in loaded NIF modules
|
||||
when not defined(nimKochBootstrap):
|
||||
# Only try to resolve from NIF if we're actually using NIF files (cmdNifC)
|
||||
@@ -599,9 +599,10 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
|
||||
if m.position >= g.packed.len:
|
||||
setLen(g.packed.pm, m.position + 1)
|
||||
|
||||
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
|
||||
uniqueName: rope(uniqueModuleName(g.config, m)))
|
||||
initStrTables(g, m)
|
||||
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.packed[int m].module)
|
||||
@@ -814,31 +815,33 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
proc moduleFromNifFile*(g: ModuleGraph; fileIdx: FileIndex;
|
||||
cachedModules: var seq[FileIndex];
|
||||
loadFullAst: bool = false): PSym =
|
||||
flags: set[LoadFlag] = {}): PrecompiledModule =
|
||||
## Returns 'nil' if the module needs to be recompiled.
|
||||
## Loads module from NIF file when optCompress is enabled.
|
||||
## When loadFullAst is true, loads the complete module AST for code generation.
|
||||
if not fileExists(toNifFilename(g.config, fileIdx)):
|
||||
return nil
|
||||
return PrecompiledModule(module: nil)
|
||||
|
||||
# Create module symbol
|
||||
let filename = AbsoluteFile toFullPath(g.config, fileIdx)
|
||||
result = PSym(
|
||||
|
||||
let m = PSym(
|
||||
kindImpl: skModule,
|
||||
itemId: ItemId(module: int32(fileIdx), item: 0'i32),
|
||||
name: getIdent(g.cache, splitFile(filename).name),
|
||||
infoImpl: newLineInfo(fileIdx, 1, 1),
|
||||
positionImpl: int(fileIdx))
|
||||
setOwner(result, getPackage(g.config, g.cache, fileIdx))
|
||||
|
||||
setOwner(m, getPackage(g.config, g.cache, fileIdx))
|
||||
# Register module in graph
|
||||
registerModule(g, result)
|
||||
var opsLog: seq[LogEntry] = @[]
|
||||
result.astImpl = loadNifModule(ast.program, fileIdx, g.ifaces[fileIdx.int].interf,
|
||||
g.ifaces[fileIdx.int].interfHidden, opsLog, loadFullAst)
|
||||
registerModule(g, m)
|
||||
|
||||
result = loadNifModule(ast.program, fileIdx,
|
||||
g.ifaces[fileIdx.int].interf,
|
||||
g.ifaces[fileIdx.int].interfHidden, flags)
|
||||
result.module = m
|
||||
|
||||
# Register hooks from NIF index with the module graph
|
||||
for x in opsLog:
|
||||
for x in result.logOps:
|
||||
case x.kind
|
||||
of HookEntry:
|
||||
g.loadedOps[x.op][x.key] = x.sym
|
||||
@@ -852,7 +855,6 @@ when not defined(nimKochBootstrap):
|
||||
raiseAssert "GenericInstEntry should not be in the NIF index"
|
||||
# Register methods per type from NIF index
|
||||
discard "todo"
|
||||
cachedModules.add fileIdx
|
||||
|
||||
proc configComplete*(g: ModuleGraph) =
|
||||
rememberStartupConfig(g.startupPackedConfig, g.config)
|
||||
|
||||
@@ -109,9 +109,11 @@ 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(
|
||||
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
||||
{"..": "@d", $os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
||||
|
||||
proc demangleModuleName*(path: string): string =
|
||||
## Demangle a relative module path.
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})
|
||||
result = path.multiReplace({"@@": "@", "@d": "..", "@h": "#", "@s": "/", "@m": "", "@p": "", "@n": "", "@c": ":"})
|
||||
|
||||
@@ -30,7 +30,6 @@ proc toLowerAscii(a: var string) {.inline.} =
|
||||
|
||||
proc flushDot*(conf: ConfigRef) =
|
||||
## safe to call multiple times
|
||||
# xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`.
|
||||
let stdOrr = if optStdout in conf.globalOptions: stdout else: stderr
|
||||
let stdOrrKind = toStdOrrKind(stdOrr)
|
||||
if stdOrrKind in conf.lastMsgWasDot:
|
||||
@@ -52,7 +51,7 @@ proc makeCString*(s: string): Rope =
|
||||
result = newStringOfCap(int(s.len.toFloat * 1.1) + 1)
|
||||
result.add("\"")
|
||||
for i in 0..<s.len:
|
||||
# line wrapping of string litterals in cgen'd code was a bad idea, e.g. causes: bug #16265
|
||||
# line wrapping of string literals in cgen'd code was a bad idea, e.g. causes: bug #16265
|
||||
# It also makes reading c sources or grepping harder, for zero benefit.
|
||||
# const MaxLineLength = 64
|
||||
# if (i + 1) mod MaxLineLength == 0:
|
||||
@@ -65,8 +64,7 @@ proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile; kind = fikSourc
|
||||
shortName: fullPath.extractFilename,
|
||||
quotedFullName: fullPath.string.makeCString,
|
||||
lines: @[],
|
||||
kind: kind
|
||||
)
|
||||
kind: kind)
|
||||
result.quotedName = result.shortName.makeCString
|
||||
when defined(nimpretty):
|
||||
if not result.fullPath.isEmpty:
|
||||
|
||||
@@ -25,28 +25,36 @@ when defined(nimPreviewSlimSystem):
|
||||
import ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
||||
pathutils, extccomp, msgs, modulepaths, idents, types, ast2nif
|
||||
|
||||
proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[PSym] =
|
||||
proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[PrecompiledModule] =
|
||||
## Traverse the module dependency graph using a stack.
|
||||
## Returns all modules that need code generation, in dependency order.
|
||||
var visited = initIntSet()
|
||||
var stack: seq[FileIndex] = @[mainFileIdx]
|
||||
let mainModule = moduleFromNifFile(g, mainFileIdx, {LoadFullAst})
|
||||
|
||||
var stack: seq[ModuleSuffix] = @[]
|
||||
result = @[]
|
||||
var cachedModules: seq[FileIndex] = @[]
|
||||
|
||||
if mainModule.module != nil:
|
||||
incl mainModule.module.flagsImpl, sfMainModule
|
||||
for dep in mainModule.deps:
|
||||
stack.add dep
|
||||
|
||||
var visited = initHashSet[string]()
|
||||
|
||||
while stack.len > 0:
|
||||
let fileIdx = stack.pop()
|
||||
let suffix = stack.pop()
|
||||
|
||||
if not visited.containsOrIncl(int(fileIdx)):
|
||||
# Only load full AST for main module; others are loaded lazily by codegen
|
||||
let isMainModule = fileIdx == mainFileIdx
|
||||
let module = moduleFromNifFile(g, fileIdx, cachedModules, loadFullAst=isMainModule)
|
||||
if module != nil:
|
||||
result.add module
|
||||
# Add dependencies to stack (they come from cachedModules)
|
||||
for dep in cachedModules:
|
||||
if not visited.contains(int(dep)):
|
||||
if not visited.containsOrIncl(suffix.string):
|
||||
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
|
||||
cachedModules.setLen(0)
|
||||
|
||||
if mainModule.module != nil:
|
||||
result.add mainModule
|
||||
|
||||
proc setupNifBackendModule(g: ModuleGraph; module: PSym): BModule =
|
||||
## Set up a BModule for code generation from a NIF module.
|
||||
@@ -54,38 +62,44 @@ proc setupNifBackendModule(g: ModuleGraph; module: PSym): BModule =
|
||||
g.backend = cgendata.newModuleList(g)
|
||||
result = cgen.newModule(BModuleList(g.backend), module, g.config, idGeneratorFromModule(module))
|
||||
|
||||
proc generateCodeForModule(g: ModuleGraph; module: PSym) =
|
||||
## Generate C code for a single module.
|
||||
let moduleId = module.position
|
||||
var bmod = BModuleList(g.backend).modules[moduleId]
|
||||
if bmod == nil:
|
||||
bmod = setupNifBackendModule(g, module)
|
||||
|
||||
# Generate code for the module's top-level statements
|
||||
if module.ast != nil:
|
||||
cgen.genTopLevelStmt(bmod, module.ast)
|
||||
|
||||
proc finishModule(g: ModuleGraph; bmod: BModule) =
|
||||
# Finalize the module (this adds it to modulesClosed)
|
||||
# Create an empty stmt list as the init body - genInitCode in writeModule will set it up properly
|
||||
let initStmt = newNodeI(nkStmtList, module.info)
|
||||
let initStmt = newNode(nkStmtList)
|
||||
finalCodegenActions(g, bmod, initStmt)
|
||||
|
||||
# Generate dispatcher methods
|
||||
for disp in getDispatchers(g):
|
||||
genProcLvl3(bmod, disp)
|
||||
|
||||
proc generateCodeForModule(g: ModuleGraph; precomp: PrecompiledModule) =
|
||||
## Generate C code for a single module.
|
||||
let moduleId = precomp.module.position
|
||||
var bmod = BModuleList(g.backend).mods[moduleId]
|
||||
if bmod == nil:
|
||||
bmod = setupNifBackendModule(g, precomp.module)
|
||||
|
||||
# Generate code for the module's top-level statements
|
||||
if precomp.topLevel != nil:
|
||||
cgen.genTopLevelStmt(bmod, precomp.topLevel)
|
||||
|
||||
proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) =
|
||||
## Main entry point for NIF-based C code generation.
|
||||
## Traverses the module dependency graph and generates C code.
|
||||
|
||||
# Reset backend state
|
||||
resetForBackend(g)
|
||||
let mainModule = g.getModule(mainFileIdx)
|
||||
|
||||
var isKnownFile = false
|
||||
let systemFileIdx = registerNifSuffix(g.config, "sysma2dyk", isKnownFile)
|
||||
g.config.m.systemFileIdx = systemFileIdx
|
||||
#msgs.fileInfoIdx(g.config,
|
||||
# g.config.libpath / RelativeFile"system.nim")
|
||||
|
||||
# Load system module first - it's always needed and contains essential hooks
|
||||
var cachedModules: seq[FileIndex] = @[]
|
||||
if g.config.m.systemFileIdx != InvalidFileIdx:
|
||||
g.systemModule = moduleFromNifFile(g, g.config.m.systemFileIdx, cachedModules)
|
||||
var precompSys = PrecompiledModule(module: nil)
|
||||
precompSys = moduleFromNifFile(g, systemFileIdx, {LoadFullAst, AlwaysLoadInterface})
|
||||
g.systemModule = precompSys.module
|
||||
|
||||
# Load all modules in dependency order using stack traversal
|
||||
# This must happen BEFORE any code generation so that hooks are loaded into loadedOps
|
||||
@@ -96,29 +110,42 @@ proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) =
|
||||
return
|
||||
|
||||
# Set up backend modules for all modules that need code generation
|
||||
for module in modules:
|
||||
discard setupNifBackendModule(g, module)
|
||||
for m in modules:
|
||||
discard setupNifBackendModule(g, m.module)
|
||||
|
||||
# Also ensure system module is set up and generated first if it exists
|
||||
if g.systemModule != nil and g.systemModule != mainModule:
|
||||
let systemBmod = BModuleList(g.backend).modules[g.systemModule.position]
|
||||
if systemBmod == nil:
|
||||
discard setupNifBackendModule(g, g.systemModule)
|
||||
generateCodeForModule(g, g.systemModule)
|
||||
if precompSys.module != nil:
|
||||
discard setupNifBackendModule(g, precompSys.module)
|
||||
generateCodeForModule(g, precompSys)
|
||||
|
||||
# Generate code for all modules except main (main goes last)
|
||||
# This ensures all modules are added to modulesClosed
|
||||
for module in modules:
|
||||
if module != mainModule and module != g.systemModule:
|
||||
generateCodeForModule(g, module)
|
||||
# Track which modules have been processed to avoid duplicates
|
||||
var processed = initIntSet()
|
||||
if precompSys.module != nil:
|
||||
processed.incl precompSys.module.position
|
||||
|
||||
# Generate main module last (so all init procs are registered)
|
||||
# Generate code for all modules (skip system since it's already processed)
|
||||
for m in modules:
|
||||
if not processed.containsOrIncl(m.module.position):
|
||||
generateCodeForModule(g, m)
|
||||
|
||||
# during code generation of `main.nim` we can trigger the code generation
|
||||
# of symbols in different modules so we need to finish these modules
|
||||
# here later, after the above loop!
|
||||
# Important: The main module must be finished LAST so that all other modules
|
||||
# have registered their init procs before genMainProc uses them.
|
||||
var mainModule: BModule = nil
|
||||
for m in BModuleList(g.backend).mods:
|
||||
if m != nil:
|
||||
assert m.module != nil
|
||||
if sfMainModule in m.module.flags:
|
||||
mainModule = m
|
||||
else:
|
||||
finishModule g, m
|
||||
if mainModule != nil:
|
||||
generateCodeForModule(g, mainModule)
|
||||
finishModule g, mainModule
|
||||
|
||||
# Write C files
|
||||
if g.backend != nil:
|
||||
cgenWriteModules(g.backend, g.config)
|
||||
cgenWriteModules(g.backend, g.config)
|
||||
|
||||
# Run C compiler
|
||||
if g.config.cmd != cmdTcc:
|
||||
|
||||
@@ -118,7 +118,7 @@ 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, cmdDeps, cmdM}:
|
||||
conf.cmd in {cmdGendepend, cmdNifC, cmdIc, cmdM}:
|
||||
initOrcDefines(conf)
|
||||
|
||||
mainCommand(graph)
|
||||
|
||||
@@ -25,7 +25,7 @@ const
|
||||
useEffectSystem* = true
|
||||
useWriteTracking* = false
|
||||
hasFFI* = defined(nimHasLibFFI)
|
||||
copyrightYear* = "2025"
|
||||
copyrightYear* = "2026"
|
||||
|
||||
nimEnableCovariance* = defined(nimEnableCovariance)
|
||||
|
||||
@@ -110,8 +110,10 @@ 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
|
||||
|
||||
TGlobalOptions* = set[TGlobalOption]
|
||||
|
||||
@@ -176,7 +178,7 @@ type
|
||||
# old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
|
||||
cmdCompileToNif
|
||||
cmdNifC # generate C code from NIF files
|
||||
cmdDeps # generate .build.nif for nifmake
|
||||
cmdIc # generate .build.nif for nifmake
|
||||
|
||||
const
|
||||
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
|
||||
@@ -368,6 +370,7 @@ type
|
||||
numberOfProcessors*: int # number of processors
|
||||
lastCmdTime*: float # when caas is enabled, we measure each command
|
||||
symbolFiles*: SymbolFilesOption
|
||||
ic*: bool # whether ic is enabled
|
||||
spellSuggestMax*: int # max number of spelling suggestions for typos
|
||||
|
||||
cppDefines*: HashSet[string] # (*)
|
||||
@@ -1044,6 +1047,9 @@ 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
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
|
||||
lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
|
||||
packages, syntaxes, depends, vm, vmdef, pragmas, idents, lookups, wordrecg,
|
||||
packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg,
|
||||
liftdestructors, nifgen
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
import vmdef
|
||||
import ast2nif
|
||||
import "../dist/nimony/src/lib" / [nifstreams, bitabs]
|
||||
|
||||
@@ -241,8 +242,11 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
raiseAssert "use setPipeLinePass to set a proper PipelinePass"
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
if (optCompress in graph.config.globalOptions or graph.config.cmd == cmdM) and
|
||||
not graph.config.isDefined("nimscript"):
|
||||
# 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"):
|
||||
topLevelStmts.add finalNode
|
||||
# Collect replay actions from both pragma computations and VM state diff
|
||||
var replayActions: seq[PNode] = @[]
|
||||
@@ -286,13 +290,23 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
|
||||
sfMainModule notin flags and
|
||||
not graph.withinSystem and
|
||||
not graph.config.isDefined("nimscript"):
|
||||
result = moduleFromNifFile(graph, fileIdx, cachedModules)
|
||||
if result == nil:
|
||||
let precomp = moduleFromNifFile(graph, fileIdx)
|
||||
if precomp.module == nil:
|
||||
let nifPath = toNifFilename(graph.config, fileIdx)
|
||||
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)
|
||||
@@ -385,7 +399,8 @@ proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx
|
||||
graph.config.libpath / RelativeFile"system.nim")
|
||||
var cachedModules: seq[FileIndex] = @[]
|
||||
when not defined(nimKochBootstrap):
|
||||
graph.systemModule = moduleFromNifFile(graph, graph.config.m.systemFileIdx, cachedModules)
|
||||
let precomp = moduleFromNifFile(graph, graph.config.m.systemFileIdx)
|
||||
graph.systemModule = precomp.module
|
||||
if graph.systemModule == nil:
|
||||
let nifPath = toNifFilename(graph.config, graph.config.m.systemFileIdx)
|
||||
localError(graph.config, unknownLineInfo,
|
||||
|
||||
@@ -1836,6 +1836,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
putWithSpace(g, tkSymbol, "error")
|
||||
#gcomma(g, n, c)
|
||||
gsub(g, n[0], c)
|
||||
of nkReplayAction:
|
||||
put(g, tkSymbol, "replayaction")
|
||||
#gsons(g, n, c, 0)
|
||||
else:
|
||||
#nkNone, nkExplicitTypeListCall:
|
||||
internalError(g.config, n.info, "renderer.gsub(" & $n.kind & ')')
|
||||
|
||||
@@ -213,6 +213,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||
unregisterArcOrc(conf)
|
||||
conf.globalOptions.excl optOwnedRefs
|
||||
conf.selectedGC = gcUnselected
|
||||
conf.globalOptions.incl optWithinConfigSystem
|
||||
|
||||
var m = graph.makeModule(scriptName)
|
||||
incl(m, sfMainModule)
|
||||
@@ -251,4 +252,5 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
|
||||
#initDefines()
|
||||
undefSymbol(conf.symbols, "nimscript")
|
||||
undefSymbol(conf.symbols, "nimconfig")
|
||||
conf.globalOptions.excl optWithinConfigSystem
|
||||
conf.symbolFiles = oldSymbolFiles
|
||||
|
||||
@@ -179,29 +179,30 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
let argB = getInt(b)
|
||||
result = newIntNodeT(if argA > argB: argA else: argB, n, idgen, g)
|
||||
of mShlI:
|
||||
let valueB = toInt64(getInt(b)) and (n.typ.size * 8 - 1)
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyInt8: result = newIntNodeT(toInt128(toInt8(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(toInt16(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(toInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt64: result = newIntNodeT(toInt128(toInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyInt:
|
||||
if g.config.target.intSize == 4:
|
||||
result = newIntNodeT(toInt128(toInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(toInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt8: result = newIntNodeT(toInt128(toUInt8(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt16: result = newIntNodeT(toInt128(toUInt16(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt32: result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt64: result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
of tyUInt:
|
||||
if g.config.target.intSize == 4:
|
||||
result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toUInt32(getInt(a)) shl valueB), n, idgen, g)
|
||||
else:
|
||||
result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl toInt64(getInt(b))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(toUInt64(getInt(a)) shl valueB), n, idgen, g)
|
||||
else: internalError(g.config, n.info, "constant folding for shl")
|
||||
of mShrI:
|
||||
var a = cast[uint64](getInt(a))
|
||||
let b = cast[uint64](getInt(b))
|
||||
var a = castToUInt64(getInt(a))
|
||||
let b = castToUInt64(getInt(b)) and cast[uint64](n.typ.size * 8 - 1)
|
||||
# To support the ``-d:nimOldShiftRight`` flag, we need to mask the
|
||||
# signed integers to cut off the extended sign bit in the internal
|
||||
# representation.
|
||||
@@ -220,12 +221,13 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
|
||||
let c = cast[BiggestInt](a shr b)
|
||||
result = newIntNodeT(toInt128(c), n, idgen, g)
|
||||
of mAshrI:
|
||||
let valueB = toInt64(getInt(b)) and (n.typ.size * 8 - 1)
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), toInt8(getInt(b)))), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), toInt16(getInt(b)))), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), toInt32(getInt(b)))), n, idgen, g)
|
||||
of tyInt8: result = newIntNodeT(toInt128(ashr(toInt8(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt16: result = newIntNodeT(toInt128(ashr(toInt16(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt32: result = newIntNodeT(toInt128(ashr(toInt32(getInt(a)), valueB)), n, idgen, g)
|
||||
of tyInt64, tyInt:
|
||||
result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), toInt64(getInt(b)))), n, idgen, g)
|
||||
result = newIntNodeT(toInt128(ashr(toInt64(getInt(a)), valueB)), n, idgen, g)
|
||||
else: internalError(g.config, n.info, "constant folding for ashr")
|
||||
of mDivI:
|
||||
let argA = getInt(a)
|
||||
|
||||
@@ -244,7 +244,8 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
var result = instCopyType(cl, prc.typ)
|
||||
let originalParams = result.n
|
||||
result.n = originalParams.shallowCopy
|
||||
for i, resulti in paramTypes(result):
|
||||
for i in 1 ..< originalParams.len:
|
||||
let resulti = originalParams[i].sym.typ
|
||||
# twrong_field_caching requires these 'resetIdTable' calls:
|
||||
if i > FirstParamAt:
|
||||
resetIdTable(cl.symMap)
|
||||
@@ -258,23 +259,23 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
|
||||
if resulti.kind == tyFromExpr:
|
||||
resulti.incl tfNonConstExpr
|
||||
result[i] = replaceTypeVarsT(cl, resulti)
|
||||
var paramType = replaceTypeVarsT(cl, resulti)
|
||||
if needsStaticSkipping:
|
||||
result[i] = result[i].skipTypes({tyStatic})
|
||||
paramType = paramType.skipTypes({tyStatic})
|
||||
if needsTypeDescSkipping:
|
||||
result[i] = result[i].skipTypes({tyTypeDesc})
|
||||
typeToFit = result[i]
|
||||
paramType = paramType.skipTypes({tyTypeDesc})
|
||||
typeToFit = paramType
|
||||
|
||||
# ...otherwise, we use the instantiated type in `fitNode`
|
||||
if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
|
||||
(typeToFit.kind != tyStatic):
|
||||
typeToFit = result[i]
|
||||
typeToFit = paramType
|
||||
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam, c.idgen)
|
||||
setOwner(param, prc)
|
||||
param.typ = result[i]
|
||||
param.typ = paramType
|
||||
|
||||
# The default value is instantiated and fitted against the final
|
||||
# concrete param type. We avoid calling `replaceTypeVarsN` on the
|
||||
@@ -305,12 +306,12 @@ proc instantiateProcType(c: PContext, pt: LayeredIdTable,
|
||||
param.ast.typ = def.typ
|
||||
else:
|
||||
param.ast = fitNodePostMatch(c, typeToFit, converted)
|
||||
param.typ = result[i]
|
||||
param.typ = paramType
|
||||
|
||||
result.n[i] = newSymNode(param)
|
||||
if isRecursiveStructuralType(result[i]):
|
||||
if isRecursiveStructuralType(paramType):
|
||||
localError(c.config, originalParams[i].sym.info, "illegal recursion in type '" & typeToString(result[i]) & "'")
|
||||
propagateToOwner(result, result[i])
|
||||
propagateToOwner(result, paramType)
|
||||
addDecl(c, param)
|
||||
|
||||
resetIdTable(cl.symMap)
|
||||
|
||||
@@ -243,7 +243,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
|
||||
let cond = operand.kind == tyTuple and operand.n != nil
|
||||
result = newIntNodeT(toInt128(ord(cond)), traitCall, c.idgen, c.graph)
|
||||
of "tupleLen":
|
||||
var operand = operand.skipTypes({tyGenericInst})
|
||||
var operand = operand.skipTypes({tyGenericInst, tyAlias})
|
||||
assert operand.kind == tyTuple, $operand.kind
|
||||
result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph)
|
||||
of "distinctBase":
|
||||
|
||||
@@ -11,7 +11,7 @@ import
|
||||
ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||
wordrecg, options, guards, lineinfos, semfold, semdata,
|
||||
modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
|
||||
semstrictfuncs, suggestsymdb, pushpoppragmas, lowerings
|
||||
semstrictfuncs, suggestsymdb, pushpoppragmas
|
||||
|
||||
import std/[tables, intsets, strutils, sequtils]
|
||||
|
||||
|
||||
@@ -2361,7 +2361,7 @@ proc semCppMember(c: PContext; s: PSym; n: PNode) =
|
||||
typ = typ.elementType
|
||||
if typ.kind != tyObject:
|
||||
localError(c.config, n.info, pragmaName & " must be either ptr to object or object type.")
|
||||
if typ.owner.id == s.owner.id and c.module.id == s.owner.id:
|
||||
if sameOwners(typ.owner, s.owner) and sameOwners(c.module, s.owner):
|
||||
c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s
|
||||
else:
|
||||
localError(c.config, n.info,
|
||||
|
||||
@@ -249,13 +249,24 @@ proc hasValuelessStatics(n: PNode): bool =
|
||||
a
|
||||
proc doThing(_: MyThing)
|
||||
]#
|
||||
result = false
|
||||
if n.safeLen == 0 and n.kind != nkEmpty: # Some empty nodes can get in here
|
||||
n.typ == nil or n.typ.kind == tyStatic
|
||||
if n.typ == nil:
|
||||
result = true
|
||||
elif n.typ.kind == tyStatic:
|
||||
result = true
|
||||
elif n.typ.kind == tyTypeDesc:
|
||||
# Check if the base type is an unresolved generic parameter.
|
||||
# This handles cases where a template containing sizeof(T) is called
|
||||
# inside a generic object's when clause - the T needs to be resolved
|
||||
# before we can evaluate the condition.
|
||||
let base = n.typ.skipTypes({tyTypeDesc})
|
||||
if base.kind == tyGenericParam:
|
||||
result = true
|
||||
else:
|
||||
for x in n:
|
||||
if hasValuelessStatics(x):
|
||||
return true
|
||||
false
|
||||
|
||||
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode =
|
||||
if n == nil: return
|
||||
@@ -542,14 +553,12 @@ proc eraseVoidParams*(t: PType) =
|
||||
|
||||
for i in FirstParamAt..<t.signatureLen:
|
||||
# don't touch any memory unless necessary
|
||||
if t[i].kind == tyVoid:
|
||||
if t.n[i].kind == nkRecList or t[i].kind == tyVoid:
|
||||
var pos = i
|
||||
for j in i+1..<t.signatureLen:
|
||||
if t[j].kind != tyVoid:
|
||||
t[pos] = t[j]
|
||||
t.n[pos] = t.n[j]
|
||||
inc pos
|
||||
newSons t, pos
|
||||
setLen t.n.sons, pos
|
||||
break
|
||||
|
||||
@@ -743,7 +752,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType, isInstValue = false):
|
||||
let r2 = r.skipTypes({tyAlias, tySink, tyOwned})
|
||||
if r2.kind in {tyPtr, tyRef}:
|
||||
r = skipTypes(r2, {tyPtr, tyRef})
|
||||
result[i] = r
|
||||
if result.kind != tyProc or i == 0:
|
||||
result[i] = r
|
||||
if result.kind != tyArray or i != 0:
|
||||
propagateToOwner(result, r)
|
||||
# bug #4677: Do not instantiate effect lists
|
||||
|
||||
@@ -106,6 +106,10 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi
|
||||
c &= "\254"
|
||||
return
|
||||
|
||||
# Ensure type is fully loaded before hashing to avoid hash changing
|
||||
# as properties are accessed and trigger lazy loading.
|
||||
backendEnsureMutable(t)
|
||||
|
||||
case t.kind
|
||||
of tyGenericInvocation:
|
||||
for a in t.kids:
|
||||
|
||||
@@ -233,13 +233,11 @@ proc copyingEraseVoidParams(m: TCandidate, t: var PType) =
|
||||
if not copied:
|
||||
# keep first i children
|
||||
t = copyType(original, m.c.idgen, t.owner)
|
||||
t.setSonsLen(i)
|
||||
t.n = copyNode(original.n)
|
||||
t.n.sons = original.n.sons
|
||||
t.n.sons.setLen(i)
|
||||
copied = true
|
||||
elif copied:
|
||||
t.add(f)
|
||||
t.n.add(original.n[i])
|
||||
|
||||
proc initCandidate*(ctx: PContext, callee: PSym,
|
||||
|
||||
@@ -38,7 +38,7 @@ proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNod
|
||||
sinkType.add argType
|
||||
|
||||
arg.sym.typ = sinkType
|
||||
owner.typ[arg.sym.position+1] = sinkType
|
||||
assert owner.typ.n[arg.sym.position+1].sym == arg.sym
|
||||
|
||||
#message(config, arg.info, warnUser,
|
||||
# ("turned '$1' to a sink parameter") % [$arg])
|
||||
|
||||
@@ -356,12 +356,12 @@ proc filterSymNoOpr(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline
|
||||
not isKeyword(s.name)
|
||||
|
||||
proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
|
||||
let fmoduleId = getModule(f).id
|
||||
result = sfExported in f.flags or fmoduleId == c.module.id
|
||||
let fmodule = getModule(f)
|
||||
result = sfExported in f.flags or sameModules(fmodule, c.module)
|
||||
|
||||
if not result:
|
||||
for module in c.friendModules:
|
||||
if fmoduleId == module.id: return true
|
||||
if sameModules(fmodule, module): return true
|
||||
if f.kind == skField:
|
||||
var symObj = f.owner.typ.toObjectFromRefPtrGeneric.sym
|
||||
assert symObj != nil
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# Implements a table from trees to trees. Does structural equivalence checking.
|
||||
|
||||
import ast, astalgo, types
|
||||
import ast, types
|
||||
|
||||
import std/hashes
|
||||
|
||||
|
||||
@@ -1723,6 +1723,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
let max = (1.BiggestInt shl (rb-1))-1
|
||||
if regs[ra].intVal < min or regs[ra].intVal > max:
|
||||
stackTrace(c, tos, pc, "unhandled exception: value out of range")
|
||||
of opcNarrowR:
|
||||
decodeBC(rkInt)
|
||||
let min = regs[rb].intVal
|
||||
let max = regs[rc].intVal
|
||||
if regs[ra].intVal < min or regs[ra].intVal > max:
|
||||
stackTrace(c, tos, pc, "unhandled exception: value out of range")
|
||||
of opcNarrowU:
|
||||
decodeB(rkInt)
|
||||
regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1)
|
||||
|
||||
@@ -105,7 +105,7 @@ type
|
||||
opcIsNil, opcOf, opcIs,
|
||||
opcParseFloat, opcConv, opcCast,
|
||||
opcQuit, opcInvalidField,
|
||||
opcNarrowS, opcNarrowU,
|
||||
opcNarrowS, opcNarrowU, opcNarrowR
|
||||
opcSignExtend,
|
||||
|
||||
opcAddStrCh,
|
||||
|
||||
@@ -798,6 +798,11 @@ proc genNarrow(c: PCtx; n: PNode; dest: TDest) =
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(size*8))
|
||||
elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8):
|
||||
c.gABC(n, opcNarrowS, dest, TRegister(size*8))
|
||||
elif t.kind in {tyEnum, tyRange}:
|
||||
let intType = getSysType(c.graph, n.info, tyInt)
|
||||
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)
|
||||
|
||||
proc genNarrowU(c: PCtx; n: PNode; dest: TDest) =
|
||||
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
@@ -1069,6 +1074,19 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode =
|
||||
else:
|
||||
(if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex)
|
||||
|
||||
proc sizeLog2(typeSize: BiggestInt): TRegister =
|
||||
case typeSize:
|
||||
of 8:
|
||||
result = 3
|
||||
of 16:
|
||||
result = 4
|
||||
of 32:
|
||||
result = 5
|
||||
of 64:
|
||||
result = 6
|
||||
else:
|
||||
raiseAssert $(typeSize)
|
||||
|
||||
proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) =
|
||||
case m
|
||||
of mAnd: c.genAndOr(n, opcFJmp, dest)
|
||||
@@ -1154,24 +1172,42 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMag
|
||||
of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
|
||||
of mShrI:
|
||||
# modified: genBinaryABC(c, n, dest, opcShrInt)
|
||||
# narrowU is applied to the left operandthe idea here is to narrow the left operand
|
||||
# narrowU is applied to the left operand the idea here is to narrow the left operand
|
||||
let typ = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
let size = getSize(c.config, typ)
|
||||
let tmp = c.genx(n[1])
|
||||
c.genNarrowU(n, tmp)
|
||||
let tmp2 = c.genx(n[2])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcNarrowU, tmp2, sizeLog2(size * 8))
|
||||
c.gABC(n, opcShrInt, dest, tmp, tmp2)
|
||||
c.freeTemp(tmp)
|
||||
c.freeTemp(tmp2)
|
||||
of mShlI:
|
||||
genBinaryABC(c, n, dest, opcShlInt)
|
||||
let typ = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
let size = getSize(c.config, typ)
|
||||
let tmp1 = c.genx(n[1])
|
||||
let tmp2 = c.genx(n[2])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcNarrowU, tmp2, sizeLog2(size * 8))
|
||||
c.gABC(n, opcShlInt, dest, tmp1, tmp2)
|
||||
c.freeTemp(tmp1)
|
||||
c.freeTemp(tmp2)
|
||||
# genNarrowU modified
|
||||
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
let size = getSize(c.config, t)
|
||||
if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
|
||||
if typ.kind in {tyUInt8..tyUInt32} or (typ.kind == tyUInt and size < 8):
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(size*8))
|
||||
elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8):
|
||||
elif typ.kind in {tyInt8..tyInt32} or (typ.kind == tyInt and size < 8):
|
||||
c.gABC(n, opcSignExtend, dest, TRegister(size*8))
|
||||
of mAshrI: genBinaryABC(c, n, dest, opcAshrInt)
|
||||
of mAshrI:
|
||||
let typ = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
let size = getSize(c.config, typ)
|
||||
let tmp1 = c.genx(n[1])
|
||||
let tmp2 = c.genx(n[2])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcNarrowU, tmp2, sizeLog2(size * 8))
|
||||
c.gABC(n, opcAshrInt, dest, tmp1, tmp2)
|
||||
c.freeTemp(tmp1)
|
||||
c.freeTemp(tmp2)
|
||||
of mBitandI: genBinaryABC(c, n, dest, opcBitandInt)
|
||||
of mBitorI: genBinaryABC(c, n, dest, opcBitorInt)
|
||||
of mBitxorI: genBinaryABC(c, n, dest, opcBitxorInt)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
=====================================================
|
||||
Nim -- a Compiler for Nim. https://nim-lang.org/
|
||||
|
||||
Copyright (C) 2006-2025 Andreas Rumpf. All rights reserved.
|
||||
Copyright (C) 2006-2026 Andreas Rumpf. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -115,6 +115,7 @@ 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
|
||||
|
||||
@@ -3025,6 +3025,44 @@ If neither of them are subsets of one another, then the disambiguation proceeds
|
||||
and the concept with the most definitions wins, if any. No definite winner is an ambiguity error at
|
||||
compile time.
|
||||
|
||||
Recursive concepts
|
||||
------------------
|
||||
|
||||
Concepts can reference themselves in their definitions, enabling recursive type constraints.
|
||||
This is useful for matching `distinct` types that should inherit traits from their base type:
|
||||
|
||||
```nim
|
||||
import std/typetraits
|
||||
|
||||
type
|
||||
PrimitiveBase = SomeNumber | bool | ptr | pointer | enum
|
||||
|
||||
# Matches PrimitiveBase directly, or any distinct type whose base is Primitive
|
||||
Primitive = concept x
|
||||
x is PrimitiveBase or distinctBase(x) is Primitive
|
||||
|
||||
# Application: a handle type that should be treated like a primitive
|
||||
Handle = distinct int
|
||||
SpecialHandle = distinct Handle
|
||||
|
||||
assert int is Primitive
|
||||
assert Handle is Primitive
|
||||
assert SpecialHandle is Primitive # works through 2 levels
|
||||
assert not (string is Primitive)
|
||||
```
|
||||
|
||||
Concepts can also be mutually recursive (co-dependent):
|
||||
|
||||
```nim
|
||||
type
|
||||
Serializable = concept
|
||||
proc serialize(s: Self; writer: var Writer)
|
||||
Writer = concept
|
||||
proc write(w: var Self; data: Serializable)
|
||||
```
|
||||
|
||||
The compiler uses cycle detection to handle these cases without infinite recursion.
|
||||
|
||||
Statements and expressions
|
||||
==========================
|
||||
|
||||
@@ -7943,6 +7981,35 @@ underlying C `struct`:c: in a `sizeof` expression:
|
||||
```
|
||||
|
||||
|
||||
CompleteStruct pragma
|
||||
---------------------
|
||||
The `completeStruct` pragma is a contract indicating that an `importc` type
|
||||
declaration contains all fields of the corresponding C type, allowing
|
||||
`sizeof`, `alignof`, and `offsetof` to be computed at compile-time.
|
||||
|
||||
By default, `importc` types are assumed to be incomplete (their size is
|
||||
unknown at compile-time). Use `completeStruct` when you need compile-time
|
||||
size information and can guarantee the Nim definition matches the C layout:
|
||||
|
||||
```Nim
|
||||
type
|
||||
InotifyEvent {.importc: "struct inotify_event", header: "<sys/inotify.h>",
|
||||
completeStruct.} = object
|
||||
wd: cint
|
||||
mask: uint32
|
||||
cookie: uint32
|
||||
len: uint32
|
||||
# All fields must match the C struct exactly
|
||||
```
|
||||
|
||||
If the Nim fields don't match the C struct, a static assertion will fail
|
||||
during C code generation.
|
||||
|
||||
Without `completeStruct`, attempting to use `sizeof` on an `importc` type
|
||||
at compile-time will error with "'sizeof' requires '.importc' types to be
|
||||
'.completeStruct'".
|
||||
|
||||
|
||||
Compile pragma
|
||||
--------------
|
||||
The `compile` pragma can be used to compile and link a C/C++ source file
|
||||
|
||||
@@ -181,6 +181,7 @@ body {
|
||||
|
||||
.nine.columns {
|
||||
width: 75.0%;
|
||||
margin-left: 0;
|
||||
padding-left: 1.5em; }
|
||||
|
||||
.twelve.columns {
|
||||
@@ -192,7 +193,9 @@ body {
|
||||
display: none;
|
||||
}
|
||||
.nine.columns {
|
||||
width: 98.0%;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
body {
|
||||
font-size: 1em;
|
||||
|
||||
@@ -1278,9 +1278,10 @@ Arrays can be constructed using `[]`:
|
||||
echo x[i]
|
||||
```
|
||||
|
||||
The notation `x[i]` is used to access the i-th element of `x`.
|
||||
Array access is always bounds checked (at compile-time or at runtime). These
|
||||
checks can be disabled via pragmas or invoking the compiler with the
|
||||
The notation `x[i]` is used to access the i-th element of `x` in the example
|
||||
above. Valid indexes can be defined by any subrange. Array access is
|
||||
always bounds checked (at compile-time or at runtime). These checks can be
|
||||
disabled via pragmas or invoking the compiler with the
|
||||
``--bound_checks:off`` command line switch.
|
||||
|
||||
Arrays are value types, like any other Nim type. The assignment operator
|
||||
|
||||
9
koch.nim
9
koch.nim
@@ -16,7 +16,7 @@ const
|
||||
ChecksumsStableCommit = "0b8e46379c5bc1bf73d8b3011908389c60fb9b98" # 2.0.1
|
||||
SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
|
||||
|
||||
NimonyStableCommit = "322178d9af6676363d5237382c6d6c1b4e56d3cd" # unversioned \
|
||||
NimonyStableCommit = "fc8baa61b9911caf4666685a5f5ed41b9c04f6f8" # 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.
|
||||
@@ -188,6 +188,11 @@ 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")
|
||||
|
||||
proc bundleNimsuggest(args: string) =
|
||||
bundleChecksums(false)
|
||||
nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim",
|
||||
@@ -553,7 +558,7 @@ proc icTest(args: string) =
|
||||
for fragment in content.split("#!EDIT!#"):
|
||||
let file = inp.replace(".nim", "_temp.nim")
|
||||
writeFile(file, fragment)
|
||||
var cmd = nimExe & " cpp --ic:on -d:nimIcIntegrityChecks --listcmd "
|
||||
var cmd = nimExe & " cpp --ic:legacy -d:nimIcIntegrityChecks --listcmd "
|
||||
if i == 0:
|
||||
cmd.add "-f "
|
||||
cmd.add quoteShell(file)
|
||||
|
||||
@@ -304,6 +304,35 @@ else:
|
||||
proc rotl32(x: uint32, r: int): uint32 {.inline.} =
|
||||
(x shl r) or (x shr (32 - r))
|
||||
|
||||
proc load4e(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or
|
||||
uint32(s[o + 1]) shl 8 or uint32(s[o + 0])
|
||||
|
||||
proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or
|
||||
uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or
|
||||
uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or
|
||||
uint64(s[o + 1]) shl 8 or uint64(s[o + 0])
|
||||
|
||||
when declared(copyMem):
|
||||
from std/endians import littleEndian64, littleEndian32
|
||||
|
||||
proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
when nimvm: result = load4e(s, o)
|
||||
else:
|
||||
when declared copyMem:
|
||||
result = uint32(0)
|
||||
littleEndian32(addr result, addr s[o])
|
||||
else: result = load4e(s, o)
|
||||
|
||||
proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
when nimvm: result = load8e(s, o)
|
||||
else:
|
||||
when declared copyMem:
|
||||
result = uint64(0)
|
||||
littleEndian64(addr result, addr s[o])
|
||||
else: result = load8e(s, o)
|
||||
|
||||
proc murmurHash(x: openArray[byte]): Hash =
|
||||
# https://github.com/PeterScott/murmur3/blob/master/murmur3.c
|
||||
const
|
||||
@@ -320,24 +349,10 @@ proc murmurHash(x: openArray[byte]): Hash =
|
||||
h1: uint32 = uint32(0)
|
||||
i = 0
|
||||
|
||||
|
||||
template impl =
|
||||
var j = stepSize
|
||||
while j > 0:
|
||||
dec j
|
||||
k1 = (k1 shl 8) or (ord(x[i+j])).uint32
|
||||
|
||||
# body
|
||||
while i < n * stepSize:
|
||||
var k1: uint32 = uint32(0)
|
||||
var k1 = load4(x, i)
|
||||
|
||||
when nimvm:
|
||||
impl()
|
||||
else:
|
||||
when declared(copyMem):
|
||||
copyMem(addr k1, addr x[i], 4)
|
||||
else:
|
||||
impl()
|
||||
inc i, stepSize
|
||||
|
||||
k1 = imul(k1, c1)
|
||||
@@ -384,32 +399,6 @@ const k0 = 0xc3a5c85c97cb3127u64 # Primes on (2^63, 2^64) for various uses
|
||||
const k1 = 0xb492b66fbe98f273u64
|
||||
const k2 = 0x9ae16a3b2f90404fu64
|
||||
|
||||
proc load4e(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or
|
||||
uint32(s[o + 1]) shl 8 or uint32(s[o + 0])
|
||||
|
||||
proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or
|
||||
uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or
|
||||
uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or
|
||||
uint64(s[o + 1]) shl 8 or uint64(s[o + 0])
|
||||
|
||||
proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
|
||||
when nimvm: result = load4e(s, o)
|
||||
else:
|
||||
when declared copyMem:
|
||||
result = uint32(0)
|
||||
copyMem result.addr, s[o].addr, result.sizeof
|
||||
else: result = load4e(s, o)
|
||||
|
||||
proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
|
||||
when nimvm: result = load8e(s, o)
|
||||
else:
|
||||
when declared copyMem:
|
||||
result = uint64(0)
|
||||
copyMem result.addr, s[o].addr, result.sizeof
|
||||
else: result = load8e(s, o)
|
||||
|
||||
proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64
|
||||
|
||||
proc shiftMix(v: uint64): uint64 {.inline.} = v xor (v shr 47)
|
||||
|
||||
@@ -573,7 +573,7 @@ proc generateHeaders(requestUrl: Uri, httpMethod: HttpMethod, headers: HttpHeade
|
||||
result = $httpMethod
|
||||
result.add ' '
|
||||
|
||||
if proxy.isNil or (requestUrl.scheme == "https" and proxy.url.scheme == "socks5h"):
|
||||
if proxy.isNil or requestUrl.scheme == "https":
|
||||
# /path?query
|
||||
if not requestUrl.path.startsWith("/"): result.add '/'
|
||||
result.add(requestUrl.path)
|
||||
|
||||
@@ -57,8 +57,12 @@ proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode =
|
||||
when declared(posix_fallocate):
|
||||
while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
|
||||
discard
|
||||
if (e == EINVAL or e == EOPNOTSUPP) and ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError() # fallback arguable; Most portable BUT allows SEGV
|
||||
if e == EINVAL or e == EOPNOTSUPP or e == ENOSYS:
|
||||
# fallback arguable; Most portable BUT allows SEGV
|
||||
if ftruncate(fh, newFileSize) == -1:
|
||||
result = osLastError()
|
||||
else:
|
||||
discard
|
||||
elif e != 0:
|
||||
result = osLastError()
|
||||
else: # shrink the file
|
||||
|
||||
@@ -170,7 +170,7 @@ runnableExamples:
|
||||
assert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False"
|
||||
assert dict.getSectionValue(section4, "purpose") == "formatting for readability"
|
||||
|
||||
import std/[strutils, lexbase, streams, tables]
|
||||
import std/[strformat, strutils, lexbase, streams, tables]
|
||||
import std/private/decode_helpers
|
||||
import std/private/since
|
||||
|
||||
@@ -220,7 +220,7 @@ type
|
||||
const
|
||||
SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', ' ', '\x80'..'\xFF', '.', '/', '\\', '-'}
|
||||
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.}
|
||||
proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe, raises: [ValueError, OSError, IOError].}
|
||||
|
||||
proc open*(c: var CfgParser, input: Stream, filename: string,
|
||||
lineOffset = 0) {.rtl, extern: "npc$1".} =
|
||||
@@ -428,14 +428,12 @@ proc rawGetTok(c: var CfgParser, tok: var Token) =
|
||||
proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
## Returns a properly formatted error message containing current line and
|
||||
## column information.
|
||||
result = `%`("$1($2, $3) Error: $4",
|
||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||
&"{c.filename}({getLine(c)}, {getColumn(c)}) Error: {msg}"
|
||||
|
||||
proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} =
|
||||
## Returns a properly formatted warning message containing current line and
|
||||
## column information.
|
||||
result = `%`("$1($2, $3) Warning: $4",
|
||||
[c.filename, $getLine(c), $getColumn(c), msg])
|
||||
&"{c.filename}({getLine(c)}, {getColumn(c)}) Warning: {msg}"
|
||||
|
||||
proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} =
|
||||
## Returns a properly formatted warning message containing that
|
||||
|
||||
@@ -19,10 +19,12 @@ include "system/inclrtl"
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
{.push gcsafe.}
|
||||
|
||||
const
|
||||
useUnicode = true ## change this to deactivate proper UTF-8 support
|
||||
|
||||
import std/[strutils, macros]
|
||||
import std/[strformat, strutils, macros]
|
||||
import std/private/decode_helpers
|
||||
|
||||
when useUnicode:
|
||||
@@ -562,10 +564,10 @@ template matchOrParse(mopProc: untyped) =
|
||||
# procs. For the former, *enter* and *leave* event handler code generators
|
||||
# are provided which just return *discard*.
|
||||
|
||||
proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.gcsafe, raises: [].} =
|
||||
proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.raises: [].} =
|
||||
result = 0
|
||||
|
||||
proc matchBackRef(s: string, p: Peg, start: int, c: var Captures): int =
|
||||
proc matchBackRef(s: string, p: Peg, start: int, c: var Captures): int {.raises: [].}=
|
||||
# Parse handler code must run in an *of* clause of its own for each
|
||||
# *PegKind*, so we encapsulate the identical clause body for
|
||||
# *pkBackRef..pkBackRefIgnoreStyle* here.
|
||||
@@ -1031,7 +1033,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) =
|
||||
## Symbols declared in an *enter* handler can be made visible in the
|
||||
## corresponding *leave* handler by annotating them with an *inject* pragma.
|
||||
proc rawParse(s: string, p: Peg, start: int, c: var Captures): int
|
||||
{.gensym.} =
|
||||
{.gensym, raises: [ValueError].} =
|
||||
|
||||
# binding from *macros*
|
||||
bind strVal
|
||||
@@ -1297,7 +1299,7 @@ when not defined(nimHasEffectsOf):
|
||||
{.pragma: effectsOf.}
|
||||
|
||||
func replace*(s: string, sub: Peg, cb: proc(
|
||||
match: int, cnt: int, caps: openArray[string]): string): string {.
|
||||
match: int, cnt: int, caps: openArray[string]): string {.gcsafe.}): string {.
|
||||
rtl, extern: "npegs$1cb", effectsOf: cb.} =
|
||||
## Replaces `sub` in `s` by the resulting strings from the callback.
|
||||
## The callback proc receives the index of the current match (starting with 0),
|
||||
@@ -1343,7 +1345,7 @@ func replace*(s: string, sub: Peg, cb: proc(
|
||||
when not defined(js):
|
||||
proc transformFile*(infile, outfile: string,
|
||||
subs: varargs[tuple[pattern: Peg, repl: string]]) {.
|
||||
rtl, extern: "npegs$1".} =
|
||||
rtl, extern: "npegs$1", raises: [ValueError, IOError].} =
|
||||
## reads in the file `infile`, performs a parallel replacement (calls
|
||||
## `parallelReplace`) and writes back to `outfile`. Raises ``IOError`` if an
|
||||
## error occurs. This is supposed to be used for quick scripting.
|
||||
@@ -1482,9 +1484,9 @@ func getLine(L: PegLexer): int {.inline.} =
|
||||
result = L.lineNumber
|
||||
|
||||
func errorStr(L: PegLexer, msg: string, line = -1, col = -1): string =
|
||||
var line = if line < 0: getLine(L) else: line
|
||||
var col = if col < 0: getColumn(L) else: col
|
||||
result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg]
|
||||
let line = if line < 0: getLine(L) else: line
|
||||
let col = if col < 0: getColumn(L) else: col
|
||||
&"{L.filename}({line}, {col}) Error: {msg}"
|
||||
|
||||
func getEscapedChar(c: var PegLexer, tok: var Token) =
|
||||
inc(c.bufpos)
|
||||
@@ -1679,7 +1681,7 @@ func getBuiltin(c: var PegLexer, tok: var Token) =
|
||||
tok.kind = tkEscaped
|
||||
getEscapedChar(c, tok) # may set tok.kind to tkInvalid
|
||||
|
||||
func getTok(c: var PegLexer, tok: var Token) =
|
||||
func getTok(c: var PegLexer, tok: var Token) {.raises: [].} =
|
||||
tok.kind = tkInvalid
|
||||
tok.modifier = modNone
|
||||
setLen(tok.literal, 0)
|
||||
@@ -1822,11 +1824,10 @@ type
|
||||
identIsVerbatim: bool
|
||||
skip: Peg
|
||||
|
||||
func pegError(p: PegParser, msg: string, line = -1, col = -1) {.noreturn.} =
|
||||
var e = (ref EInvalidPeg)(msg: errorStr(p, msg, line, col))
|
||||
raise e
|
||||
func pegError(p: PegParser, msg: string, line = -1, col = -1) {.noreturn, raises: [EInvalidPeg].} =
|
||||
raise (ref EInvalidPeg)(msg: errorStr(p, msg, line, col))
|
||||
|
||||
func getTok(p: var PegParser) =
|
||||
func getTok(p: var PegParser) {.raises: [EInvalidPeg].}=
|
||||
getTok(p, p.tok)
|
||||
if p.tok.kind == tkInvalid: pegError(p, "'" & p.tok.literal & "' is invalid token")
|
||||
|
||||
@@ -1834,7 +1835,7 @@ func eat(p: var PegParser, kind: TokKind) =
|
||||
if p.tok.kind == kind: getTok(p)
|
||||
else: pegError(p, tokKindToStr[kind] & " expected")
|
||||
|
||||
func parseExpr(p: var PegParser): Peg {.gcsafe.}
|
||||
func parseExpr(p: var PegParser): Peg {.raises: [EInvalidPeg].}
|
||||
|
||||
func getNonTerminal(p: var PegParser, name: string): NonTerminal =
|
||||
for i in 0..high(p.nonterms):
|
||||
@@ -1883,7 +1884,7 @@ func token(terminal: Peg, p: PegParser): Peg =
|
||||
if p.skip.kind == pkEmpty: result = terminal
|
||||
else: result = sequence(p.skip, terminal)
|
||||
|
||||
func primary(p: var PegParser): Peg =
|
||||
func primary(p: var PegParser): Peg {.raises: [EInvalidPeg].}=
|
||||
case p.tok.kind
|
||||
of tkAmp:
|
||||
getTok(p)
|
||||
@@ -1976,7 +1977,7 @@ func primary(p: var PegParser): Peg =
|
||||
getTok(p)
|
||||
else: break
|
||||
|
||||
func seqExpr(p: var PegParser): Peg =
|
||||
func seqExpr(p: var PegParser): Peg {.raises: [EInvalidPeg].}=
|
||||
result = primary(p)
|
||||
while true:
|
||||
case p.tok.kind
|
||||
@@ -2042,7 +2043,7 @@ func rawParse(p: var PegParser): Peg =
|
||||
elif ntUsed notin nt.flags and i > 0:
|
||||
pegError(p, "unused rule: " & nt.name, nt.line, nt.col)
|
||||
|
||||
func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
|
||||
func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg {.raises: [EInvalidPeg].} =
|
||||
## constructs a Peg object from `pattern`. `filename`, `line`, `col` are
|
||||
## used for error messages, but they only provide start offsets. `parsePeg`
|
||||
## keeps track of line and column numbers within `pattern`.
|
||||
@@ -2057,7 +2058,7 @@ func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
|
||||
getTok(p)
|
||||
result = rawParse(p)
|
||||
|
||||
func peg*(pattern: string): Peg =
|
||||
func peg*(pattern: string): Peg {.raises: [EInvalidPeg].} =
|
||||
## constructs a Peg object from the `pattern`. The short name has been
|
||||
## chosen to encourage its use as a raw string modifier:
|
||||
##
|
||||
|
||||
@@ -47,7 +47,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
|
||||
of nnkEnumFieldDef:
|
||||
fVal = f[0].strVal
|
||||
case f[1].kind
|
||||
of nnkStrLit:
|
||||
of nnkStrLit .. nnkTripleStrLit:
|
||||
fStr = f[1].strVal
|
||||
of nnkTupleConstr:
|
||||
fStr = f[1][1].strVal
|
||||
@@ -57,7 +57,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
|
||||
fNum = f[1].intVal
|
||||
else:
|
||||
let fAst = f[0].getImpl
|
||||
if fAst.kind == nnkStrLit:
|
||||
if fAst.kind in {nnkStrLit .. nnkTripleStrLit}:
|
||||
fStr = fAst.strVal
|
||||
else:
|
||||
error("Invalid tuple syntax!", f[1])
|
||||
|
||||
@@ -41,7 +41,7 @@ proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.}
|
||||
import std/private/osseps
|
||||
export osseps
|
||||
|
||||
proc absolutePathInternal(path: string): string {.gcsafe.}
|
||||
proc absolutePathInternal(path: string): string {.gcsafe, raises: [ValueError, OSerror].}
|
||||
|
||||
proc normalizePathEnd*(path: var string, trailingSep = false) =
|
||||
## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
{.push profiler:off.}
|
||||
|
||||
include osalloc
|
||||
import std/private/syslocks
|
||||
|
||||
template track(op, address, size) =
|
||||
when defined(memTracker):
|
||||
|
||||
@@ -136,7 +136,10 @@ when defined(nimOldShiftRight):
|
||||
else:
|
||||
proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} =
|
||||
## Computes the `shift right` operation of `x` and `y`, filling
|
||||
## vacant bit positions with the sign bit.
|
||||
## vacant bit positions with the sign bit. `y` (the number of
|
||||
## positions to shift) is reduced to modulo `sizeof(x) * 8`.
|
||||
## That is `15'i32 shr 35` is equivalent to `15'i32 shr 3`
|
||||
## bitmasked to always be in the range `0 ..< sizeof(int)`.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
@@ -158,7 +161,9 @@ else:
|
||||
|
||||
|
||||
proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.} =
|
||||
## Computes the `shift left` operation of `x` and `y`.
|
||||
## Computes the `shift left` operation of `x` and `y`. `y` (the number of
|
||||
## positions to shift) is reduced to modulo `sizeof(x) * 8`.
|
||||
## That is `15'i32 shl 35` is equivalent to `15'i32 shl 3`.
|
||||
##
|
||||
## **Note**: `Operator precedence <manual.html#syntax-precedence>`_
|
||||
## is different than in *C*.
|
||||
@@ -172,7 +177,9 @@ proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.}
|
||||
|
||||
proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} =
|
||||
## Shifts right by pushing copies of the leftmost bit in from the left,
|
||||
## and let the rightmost bits fall off.
|
||||
## and let the rightmost bits fall off. `y` (the number of
|
||||
## positions to shift) is reduced to modulo `sizeof(x) * 8`.
|
||||
## That is `ashr(15'i32, 35)` is equivalent to `ashr(15'i32, 3)`.
|
||||
##
|
||||
## Note that `ashr` is not an operator so use the normal function
|
||||
## call syntax for it.
|
||||
@@ -181,7 +188,7 @@ proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} =
|
||||
## * `shr func<#shr,int,SomeInteger>`_
|
||||
runnableExamples:
|
||||
assert ashr(0b0001_0000'i8, 2) == 0b0000_0100'i8
|
||||
assert ashr(0b1000_0000'i8, 8) == 0b1111_1111'i8
|
||||
assert ashr(0b1000_0000'i8, 8) == 0b1000_0000'i8
|
||||
assert ashr(0b1000_0000'i8, 1) == 0b1100_0000'i8
|
||||
proc ashr*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.}
|
||||
proc ashr*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.}
|
||||
|
||||
@@ -143,8 +143,6 @@
|
||||
when not declared(ThisIsSystem):
|
||||
{.error: "You must not import this module explicitly".}
|
||||
|
||||
import std/private/syslocks
|
||||
|
||||
type
|
||||
pbytes = ptr UncheckedArray[byte]
|
||||
RawChannel {.pure, final.} = object ## msg queue for a thread
|
||||
|
||||
@@ -181,6 +181,7 @@ body {
|
||||
|
||||
.nine.columns {
|
||||
width: 75.0%;
|
||||
margin-left: 0;
|
||||
padding-left: 1.5em; }
|
||||
|
||||
.twelve.columns {
|
||||
@@ -192,7 +193,9 @@ body {
|
||||
display: none;
|
||||
}
|
||||
.nine.columns {
|
||||
width: 98.0%;
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
body {
|
||||
font-size: 1em;
|
||||
|
||||
@@ -202,7 +202,7 @@ Nim. You are explicitly permitted to develop commercial applications using Nim.
|
||||
|
||||
Please read the [copying.txt](copying.txt) file for more details.
|
||||
|
||||
Copyright © 2006-2025 Andreas Rumpf, all rights reserved.
|
||||
Copyright © 2006-2026 Andreas Rumpf, all rights reserved.
|
||||
|
||||
[nim-site]: https://nim-lang.org
|
||||
[nim-forum]: https://forum.nim-lang.org
|
||||
|
||||
@@ -27,6 +27,7 @@ const
|
||||
"io",
|
||||
"js",
|
||||
"ic",
|
||||
"ic_disabled",
|
||||
"lib",
|
||||
"manyloc",
|
||||
"nimble-packages",
|
||||
@@ -489,46 +490,16 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) =
|
||||
|
||||
proc icTests(r: var TResults; testsDir: string, cat: Category, options: string;
|
||||
isNavigatorTest: bool) =
|
||||
const
|
||||
tooltests = ["compiler/nim.nim"]
|
||||
writeOnly = " --incremental:writeonly "
|
||||
readOnly = " --incremental:readonly "
|
||||
incrementalOn = " --incremental:on -d:nimIcIntegrityChecks "
|
||||
navTestConfig = " --ic:on -d:nimIcNavigatorTests --hint:Conf:off --warnings:off "
|
||||
|
||||
template test(x: untyped) =
|
||||
testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache)
|
||||
|
||||
template editedTest(x: untyped) =
|
||||
var test = makeTest(file, x & options, cat)
|
||||
if isNavigatorTest:
|
||||
test.spec.action = actionCompile
|
||||
test.spec.targets = {getTestSpecTarget()}
|
||||
template editedTest() =
|
||||
var test = makeTest(file, options, cat)
|
||||
test.spec.targets = {targetC}
|
||||
test.spec.cmd = compilerPrefix & " ic --hint:Conf:off --warnings:off $options " & file
|
||||
testSpecWithNimcache(r, test, nimcache)
|
||||
|
||||
template checkTest() =
|
||||
var test = makeRawTest(file, options, cat)
|
||||
test.spec.cmd = compilerPrefix & " check --hint:Conf:off --warnings:off --ic:on $options " & file
|
||||
testSpecWithNimcache(r, test, nimcache)
|
||||
|
||||
if not isNavigatorTest:
|
||||
for file in tooltests:
|
||||
let nimcache = nimcacheDir(file, options, getTestSpecTarget())
|
||||
removeDir(nimcache)
|
||||
|
||||
let oldPassed = r.passed
|
||||
checkTest()
|
||||
|
||||
if r.passed == oldPassed+1:
|
||||
checkTest()
|
||||
if r.passed == oldPassed+2:
|
||||
checkTest()
|
||||
|
||||
const tempExt = "_temp.nim"
|
||||
for it in walkDirRec(testsDir):
|
||||
# for it in ["tests/ic/timports.nim"]: # debugging: to try a specific test
|
||||
if isTestFile(it) and not it.endsWith(tempExt):
|
||||
let nimcache = nimcacheDir(it, options, getTestSpecTarget())
|
||||
let nimcache = nimcacheDir(it, options, targetC)
|
||||
removeDir(nimcache)
|
||||
|
||||
let content = readFile(it)
|
||||
@@ -536,7 +507,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string;
|
||||
let file = it.replace(".nim", tempExt)
|
||||
writeFile(file, fragment)
|
||||
let oldPassed = r.passed
|
||||
editedTest(if isNavigatorTest: navTestConfig else: incrementalOn)
|
||||
editedTest()
|
||||
if r.passed != oldPassed+1: break
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
19
tests/arc/tgenerics.nim
Normal file
19
tests/arc/tgenerics.nim
Normal file
@@ -0,0 +1,19 @@
|
||||
discard """
|
||||
matrix: "--mm:refc"
|
||||
"""
|
||||
type
|
||||
State = enum
|
||||
Uninit
|
||||
Init
|
||||
Uart[T: static State] = object
|
||||
baudRate: int
|
||||
port: int
|
||||
|
||||
proc `=destroy`(uart: var Uart[Init]) = raiseAssert "Destroyed"
|
||||
|
||||
# proc `=copy`(a: var Uart[Init], b: Uart[Init]) {.error.} # Error: signature for '=copy' must be proc[T: object](x: var T; y: T)
|
||||
|
||||
proc main() =
|
||||
var a = Uart[Uninit]()
|
||||
|
||||
main()
|
||||
8
tests/ccgbugs/t25387.nim
Normal file
8
tests/ccgbugs/t25387.nim
Normal file
@@ -0,0 +1,8 @@
|
||||
discard """
|
||||
matrix: "--embedsrc=on"
|
||||
"""
|
||||
|
||||
proc trim() =
|
||||
let s = 10
|
||||
let x = s + 5 # user entered literal \
|
||||
trim()
|
||||
178
tests/ccgbugs/timportc_field_init.nim
Normal file
178
tests/ccgbugs/timportc_field_init.nim
Normal file
@@ -0,0 +1,178 @@
|
||||
discard """
|
||||
targets: "c cpp"
|
||||
"""
|
||||
# Test const initialization of objects with opaque importc fields (e.g. FILE from stdio.h)
|
||||
|
||||
type OpaqueFile {.importc: "FILE", header: "<stdio.h>".} = object
|
||||
|
||||
type
|
||||
SimpleStruct = object
|
||||
normal: int
|
||||
opaque: OpaqueFile
|
||||
|
||||
NestedStruct = object
|
||||
inner: SimpleStruct
|
||||
value: float
|
||||
|
||||
VariantStruct = object
|
||||
case kind: bool
|
||||
of true:
|
||||
opaque: OpaqueFile
|
||||
of false:
|
||||
normal: int
|
||||
|
||||
ArrayElementStruct = object
|
||||
id: int
|
||||
atom: OpaqueFile
|
||||
|
||||
const simple = default(SimpleStruct)
|
||||
const nested = default(NestedStruct)
|
||||
const variant = default(VariantStruct)
|
||||
const arr = default(array[3, ArrayElementStruct])
|
||||
|
||||
static:
|
||||
doAssert simple.normal == 0
|
||||
doAssert nested.value == 0.0
|
||||
doAssert arr[0].id == 0
|
||||
|
||||
# completeStruct types use normal aggregate init
|
||||
type CompleteImportc {.importc: "int", completeStruct, nodecl.} = object
|
||||
value: cint
|
||||
|
||||
type StructWithComplete = object
|
||||
c: CompleteImportc
|
||||
x: int
|
||||
|
||||
const withComplete = default(StructWithComplete)
|
||||
|
||||
type TupleWithOpaque = tuple[x: int, s: SimpleStruct, y: float]
|
||||
const tupleVal = default(TupleWithOpaque)
|
||||
|
||||
# Sandwich: opaque between non-opaque fields requires designated init
|
||||
type SandwichStruct = object
|
||||
first: int
|
||||
opaque: OpaqueFile
|
||||
last: float
|
||||
|
||||
const sandwich = default(SandwichStruct)
|
||||
|
||||
static:
|
||||
doAssert withComplete.x == 0
|
||||
doAssert tupleVal.x == 0
|
||||
doAssert sandwich.first == 0
|
||||
doAssert sandwich.last == 0.0
|
||||
|
||||
proc useSimple(s: ptr SimpleStruct) {.exportc, noinline.} = discard
|
||||
proc useNested(s: ptr NestedStruct) {.exportc, noinline.} = discard
|
||||
proc useArr(a: ptr array[3, ArrayElementStruct]) {.exportc, noinline.} = discard
|
||||
proc useComplete(s: ptr StructWithComplete) {.exportc, noinline.} = discard
|
||||
proc useVariant(v: ptr VariantStruct) {.exportc, noinline.} = discard
|
||||
proc useTuple(t: TupleWithOpaque) {.exportc, noinline.} = discard
|
||||
proc useSandwich(s: ptr SandwichStruct) {.exportc, noinline.} = discard
|
||||
|
||||
useSimple(simple.addr)
|
||||
useNested(nested.addr)
|
||||
useArr(arr.addr)
|
||||
useComplete(withComplete.addr)
|
||||
useVariant(variant.addr)
|
||||
useTuple(tupleVal)
|
||||
useSandwich(sandwich.addr)
|
||||
|
||||
# Edge cases: different C/Nim names
|
||||
type OpaqueWithCName {.importc: "FILE", header: "<stdio.h>".} = object
|
||||
|
||||
type StructWithRenamedField = object
|
||||
nimName {.importc: "c_name".}: int
|
||||
opaque: OpaqueWithCName
|
||||
|
||||
const renamedField = default(StructWithRenamedField)
|
||||
proc useRenamedField(s: ptr StructWithRenamedField) {.exportc, noinline.} = discard
|
||||
useRenamedField(renamedField.addr)
|
||||
static: doAssert renamedField.nimName == 0
|
||||
|
||||
type NimTypeName {.importc: "int", completeStruct, nodecl.} = distinct cint
|
||||
|
||||
type StructContainingRenamedType = object
|
||||
inner: NimTypeName
|
||||
opaque: OpaqueFile
|
||||
|
||||
const withRenamedType = default(StructContainingRenamedType)
|
||||
proc useRenamedType(s: ptr StructContainingRenamedType) {.exportc, noinline.} = discard
|
||||
useRenamedType(withRenamedType.addr)
|
||||
|
||||
type StructWithExportedField = object
|
||||
nimField {.exportc: "exported_field".}: int
|
||||
opaque: OpaqueFile
|
||||
|
||||
const withExported = default(StructWithExportedField)
|
||||
proc useExportedField(s: ptr StructWithExportedField) {.exportc, noinline.} = discard
|
||||
useExportedField(withExported.addr)
|
||||
static: doAssert withExported.nimField == 0
|
||||
|
||||
type ByCopyStruct {.bycopy.} = object
|
||||
data: int
|
||||
opaque: OpaqueFile
|
||||
|
||||
const byCopyVal = default(ByCopyStruct)
|
||||
proc useByCopy(s: ByCopyStruct) {.exportc, noinline.} = discard
|
||||
useByCopy(byCopyVal)
|
||||
static: doAssert byCopyVal.data == 0
|
||||
|
||||
type PackedStruct {.packed.} = object
|
||||
a: int8
|
||||
opaque: OpaqueFile
|
||||
b: int8
|
||||
|
||||
const packedVal = default(PackedStruct)
|
||||
proc usePacked(s: ptr PackedStruct) {.exportc, noinline.} = discard
|
||||
usePacked(packedVal.addr)
|
||||
static:
|
||||
doAssert packedVal.a == 0
|
||||
doAssert packedVal.b == 0
|
||||
|
||||
type UnionWithOpaque {.union.} = object
|
||||
intVal: int
|
||||
opaque: OpaqueFile
|
||||
|
||||
const unionVal = default(UnionWithOpaque)
|
||||
proc useUnion(u: ptr UnionWithOpaque) {.exportc, noinline.} = discard
|
||||
useUnion(unionVal.addr)
|
||||
|
||||
# Deep nesting
|
||||
type DeepLevel1 = object
|
||||
field1: int
|
||||
opaque: OpaqueFile
|
||||
|
||||
type DeepLevel2 = object
|
||||
nested: DeepLevel1
|
||||
field2: float
|
||||
|
||||
type DeepLevel3 = object
|
||||
deep: DeepLevel2
|
||||
field3: int
|
||||
opaque2: OpaqueWithCName
|
||||
|
||||
const deepVal = default(DeepLevel3)
|
||||
proc useDeep(d: ptr DeepLevel3) {.exportc, noinline.} = discard
|
||||
useDeep(deepVal.addr)
|
||||
static:
|
||||
doAssert deepVal.deep.nested.field1 == 0
|
||||
doAssert deepVal.deep.field2 == 0.0
|
||||
doAssert deepVal.field3 == 0
|
||||
|
||||
# Multiple opaque fields with renamed non-opaque fields
|
||||
type MultiOpaque = object
|
||||
first {.importc: "first_field".}: int
|
||||
opaque1: OpaqueFile
|
||||
second {.importc: "second_field".}: float
|
||||
opaque2: OpaqueWithCName
|
||||
third: int
|
||||
|
||||
const multiOpaque = default(MultiOpaque)
|
||||
proc useMultiOpaque(m: ptr MultiOpaque) {.exportc, noinline.} = discard
|
||||
useMultiOpaque(multiOpaque.addr)
|
||||
|
||||
static:
|
||||
doAssert multiOpaque.first == 0
|
||||
doAssert multiOpaque.second == 0.0
|
||||
doAssert multiOpaque.third == 0
|
||||
15
tests/concepts/t17630.nim
Normal file
15
tests/concepts/t17630.nim
Normal file
@@ -0,0 +1,15 @@
|
||||
discard """
|
||||
action: "compile"
|
||||
"""
|
||||
|
||||
# https://github.com/nim-lang/Nim/issues/17630
|
||||
# A concept that references itself in a proc signature
|
||||
# should not cause infinite recursion / stack overflow
|
||||
|
||||
type
|
||||
A = concept
|
||||
proc test(x: Self, y: A)
|
||||
|
||||
proc test(x: int, y: int) = discard
|
||||
|
||||
discard (int is A)
|
||||
132
tests/concepts/trecursive_concepts.nim
Normal file
132
tests/concepts/trecursive_concepts.nim
Normal file
@@ -0,0 +1,132 @@
|
||||
discard """
|
||||
action: "run"
|
||||
output: '''
|
||||
int is Primitive: true
|
||||
Handle is Primitive: true
|
||||
SpecialHandle is Primitive: true
|
||||
FileDescriptor is Primitive: true
|
||||
float is Primitive: false
|
||||
string is Primitive: false
|
||||
char is PrimitiveBase: true
|
||||
ptr int is PrimitiveBase: true
|
||||
'''
|
||||
"""
|
||||
|
||||
# Test recursive concepts with cycle detection
|
||||
# This tests concepts that reference themselves via distinctBase
|
||||
|
||||
import std/typetraits
|
||||
|
||||
block: # Basic recursive concept with distinctBase
|
||||
type
|
||||
PrimitiveBase = SomeInteger | bool | char | ptr | pointer
|
||||
|
||||
# Recursive concept: matches PrimitiveBase or any distinct type whose base is Primitive
|
||||
Primitive = concept x
|
||||
x is PrimitiveBase or distinctBase(x) is Primitive
|
||||
|
||||
# Real-world example: handle types that wrap integers
|
||||
Handle = distinct int
|
||||
SpecialHandle = distinct Handle
|
||||
FileDescriptor = distinct SpecialHandle
|
||||
|
||||
# Direct base types
|
||||
echo "int is Primitive: ", int is Primitive
|
||||
|
||||
# Single-level distinct (like a simple handle type)
|
||||
echo "Handle is Primitive: ", Handle is Primitive
|
||||
|
||||
# Two-level distinct
|
||||
echo "SpecialHandle is Primitive: ", SpecialHandle is Primitive
|
||||
|
||||
# Three-level distinct
|
||||
echo "FileDescriptor is Primitive: ", FileDescriptor is Primitive
|
||||
|
||||
# Non-primitive types should NOT match
|
||||
echo "float is Primitive: ", float is Primitive
|
||||
echo "string is Primitive: ", string is Primitive
|
||||
|
||||
block: # Ensure base type matching still works
|
||||
type
|
||||
PrimitiveBase = SomeInteger | bool | char | ptr | pointer
|
||||
|
||||
echo "char is PrimitiveBase: ", char is PrimitiveBase
|
||||
echo "ptr int is PrimitiveBase: ", (ptr int) is PrimitiveBase
|
||||
|
||||
block: # Test that cycle detection doesn't break normal concept matching
|
||||
type
|
||||
Addable = concept x, y
|
||||
x + y is typeof(x)
|
||||
|
||||
doAssert int is Addable
|
||||
doAssert float is Addable
|
||||
|
||||
block: # Test non-matching recursive case
|
||||
type
|
||||
IntegerBase = SomeInteger
|
||||
|
||||
IntegerLike = concept x
|
||||
x is IntegerBase or distinctBase(x) is IntegerLike
|
||||
|
||||
Percentage = distinct float # float base, not integer
|
||||
|
||||
doAssert int is IntegerLike
|
||||
doAssert not(float is IntegerLike)
|
||||
doAssert not(Percentage is IntegerLike) # float base doesn't match
|
||||
|
||||
block: # Test deep distinct chains (5+ levels) - e.g., layered ID types
|
||||
type
|
||||
IdBase = SomeInteger
|
||||
|
||||
IdLike = concept x
|
||||
x is IdBase or distinctBase(x) is IdLike
|
||||
|
||||
EntityId = distinct int
|
||||
UserId = distinct EntityId
|
||||
AdminId = distinct UserId
|
||||
SuperAdminId = distinct AdminId
|
||||
RootId = distinct SuperAdminId
|
||||
|
||||
doAssert int is IdLike
|
||||
doAssert EntityId is IdLike
|
||||
doAssert UserId is IdLike
|
||||
doAssert AdminId is IdLike
|
||||
doAssert SuperAdminId is IdLike
|
||||
doAssert RootId is IdLike
|
||||
doAssert not(float is IdLike)
|
||||
|
||||
block: # Test 3-way mutual recursion (co-dependent concepts)
|
||||
# This tests that cycle detection properly handles A -> B -> C -> A cycles
|
||||
type
|
||||
Serializable = concept
|
||||
proc serialize(x: Self): Bytes
|
||||
|
||||
Bytes = concept
|
||||
proc compress(x: Self): Compressed
|
||||
|
||||
Compressed = concept
|
||||
proc decompress(x: Self): Serializable
|
||||
|
||||
Data = object
|
||||
value: int
|
||||
|
||||
proc serialize(x: Data): Data = x
|
||||
proc compress(x: Data): Data = x
|
||||
proc decompress(x: Data): Data = x
|
||||
|
||||
# Data should satisfy all three mutually recursive concepts
|
||||
doAssert Data is Serializable
|
||||
doAssert Data is Bytes
|
||||
doAssert Data is Compressed
|
||||
|
||||
block: # Test concept with method returning same type
|
||||
type
|
||||
Cloneable = concept
|
||||
proc clone(x: Self): Self
|
||||
|
||||
Document = object
|
||||
content: string
|
||||
|
||||
proc clone(x: Document): Document = x
|
||||
|
||||
doAssert Document is Cloneable
|
||||
17
tests/errmsgs/tvmranges.nim
Normal file
17
tests/errmsgs/tvmranges.nim
Normal file
@@ -0,0 +1,17 @@
|
||||
discard """
|
||||
action: reject
|
||||
nimout: '''
|
||||
stack trace: (most recent call last)
|
||||
tvmranges.nim(14, 10)
|
||||
tvmranges.nim(14, 10) Error: unhandled exception: value out of range
|
||||
'''
|
||||
"""
|
||||
|
||||
type X = enum
|
||||
a
|
||||
b
|
||||
|
||||
when pred(a) == b:
|
||||
echo "a"
|
||||
else:
|
||||
echo "b"
|
||||
34
tests/generic/tgeneric_typedesc_sizeof.nim
Normal file
34
tests/generic/tgeneric_typedesc_sizeof.nim
Normal file
@@ -0,0 +1,34 @@
|
||||
discard """
|
||||
output: '''
|
||||
42
|
||||
'''
|
||||
"""
|
||||
|
||||
# Regression test for semtypinst.nim hasValuelessStatics bug.
|
||||
#
|
||||
# Bug: hasValuelessStatics only checked for tyStatic, missing tyTypeDesc(tyGenericParam)
|
||||
# Fix: Added check for tyTypeDesc wrapping tyGenericParam in compiler/semtypinst.nim
|
||||
#
|
||||
# The bug triggers when:
|
||||
# 1. A generic type has a when clause calling a typedesc template with sizeof(T)
|
||||
# 2. A generic proc on that type is called, triggering instantiation
|
||||
# 3. The T in sizeof(T) becomes tyTypeDesc(tyGenericParam), which wasn't recognized as unresolved
|
||||
#
|
||||
# Error without fix: 'sizeof' requires '.importc' types to be '.completeStruct'
|
||||
|
||||
template isSmall(T: typedesc): bool =
|
||||
sizeof(T) <= 8
|
||||
|
||||
type Foo[T] = object
|
||||
when isSmall(T):
|
||||
a: T
|
||||
else:
|
||||
b: ptr T
|
||||
|
||||
proc bar[T](x: var Foo[T]) =
|
||||
discard
|
||||
|
||||
var x: Foo[int]
|
||||
x.a = 42
|
||||
x.bar()
|
||||
echo x.a
|
||||
2
tests/global/mglobal3.nim
Normal file
2
tests/global/mglobal3.nim
Normal file
@@ -0,0 +1,2 @@
|
||||
proc v*() =
|
||||
let u {.global.} = default(ref int)
|
||||
@@ -62,3 +62,7 @@ proc m2() =
|
||||
assert v == "123"
|
||||
|
||||
m2()
|
||||
|
||||
import mglobal3
|
||||
block:
|
||||
v()
|
||||
@@ -14,6 +14,7 @@ int32
|
||||
0
|
||||
tUnsignedOps OK
|
||||
'''
|
||||
targets: "c cpp js"
|
||||
nimout: "tUnsignedOps OK"
|
||||
"""
|
||||
|
||||
@@ -185,3 +186,67 @@ block tUnsignedOps:
|
||||
testUnsignedOps()
|
||||
static:
|
||||
testUnsignedOps()
|
||||
|
||||
block tshl:
|
||||
# Signed types
|
||||
block:
|
||||
const t0: int8 = 1'i8 shl 8
|
||||
const t1: int16 = 1'i16 shl 16
|
||||
const t2: int32 = 1'i32 shl 32
|
||||
const t3: int64 = 1'i64 shl 64
|
||||
doAssert t0 == 1
|
||||
doAssert t1 == 1
|
||||
doAssert t2 == 1
|
||||
doAssert t3 == 1
|
||||
|
||||
# Unsigned types
|
||||
block:
|
||||
const t0: uint8 = 1'u8 shl 8
|
||||
const t1: uint16 = 1'u16 shl 16
|
||||
const t2: uint32 = 1'u32 shl 32
|
||||
const t3: uint64 = 1'u64 shl 64
|
||||
doAssert t0 == 1
|
||||
doAssert t1 == 1
|
||||
doAssert t2 == 1
|
||||
doAssert t3 == 1
|
||||
|
||||
block bitmaking:
|
||||
|
||||
# test semfold (single expression)
|
||||
doAssert (0x10'i8 shr 2) == (0x10'i8 shr 0b1010_1010)
|
||||
doAssert (0x10'u8 shr 2) == (0x10'u8 shr 0b0101_1010)
|
||||
doAssert (0x10'i16 shr 2) == (0x10'i16 shr 0b1011_0010)
|
||||
doAssert (0x10'u16 shr 2) == (0x10'u16 shr 0b0101_0010)
|
||||
doAssert (0x10'i32 shr 2) == (0x10'i32 shr 0b1010_0010)
|
||||
doAssert (0x10'u32 shr 2) == (0x10'u32 shr 0b0110_0010)
|
||||
doAssert (0x10'i64 shr 2) == (0x10'i32 shr 0b1100_0010)
|
||||
doAssert (0x10'u64 shr 2) == (0x10'u32 shr 0b0100_0010)
|
||||
|
||||
doAssert (0x10'i8 shl 2) == (0x10'i8 shl 0b1010_1010)
|
||||
doAssert (0x10'u8 shl 2) == (0x10'u8 shl 0b0101_1010)
|
||||
doAssert (0x10'i16 shl 2) == (0x10'i16 shl 0b1011_0010)
|
||||
doAssert (0x10'u16 shl 2) == (0x10'u16 shl 0b0101_0010)
|
||||
doAssert (0x10'i32 shl 2) == (0x10'i32 shl 0b1010_0010)
|
||||
doAssert (0x10'u32 shl 2) == (0x10'u32 shl 0b0110_0010)
|
||||
doAssert (0x10'i64 shl 2) == (0x10'i32 shl 0b1100_0010)
|
||||
doAssert (0x10'u64 shl 2) == (0x10'u32 shl 0b0100_0010)
|
||||
|
||||
proc testVmAndBackend[T: SomeInteger](a: T, b1, b2: int) {.sideeffect.} =
|
||||
# this echo is to cause a side effect and therefore ensure this
|
||||
# proc isn't evaluated at compile time when it should not.
|
||||
doAssert((a shr b1) == (a shr b2))
|
||||
doAssert((a shl b1) == (a shl b2))
|
||||
|
||||
proc callTestVmAndBackend() =
|
||||
testVmAndBackend(0x10'i8, 2, 0b1010_1010)
|
||||
testVmAndBackend(0x10'u8, 2, 0b0101_1010)
|
||||
testVmAndBackend(0x10'i16, 2, 0b1011_0010)
|
||||
testVmAndBackend(0x10'u16, 2, 0b0101_0010)
|
||||
testVmAndBackend(0x10'i32, 2, 0b1010_0010)
|
||||
testVmAndBackend(0x10'u32, 2, 0b0110_0010)
|
||||
testVmAndBackend(0x10'i64, 2, 0b1100_0010)
|
||||
testVmAndBackend(0x10'u64, 2, 0b0100_0010)
|
||||
|
||||
callTestVmAndBackend() # test at runtime
|
||||
static:
|
||||
callTestVmAndBackend() # test at compiletime
|
||||
|
||||
9
tests/js/t16726.nim
Normal file
9
tests/js/t16726.nim
Normal file
@@ -0,0 +1,9 @@
|
||||
discard """
|
||||
errormsg: "type mismatch"
|
||||
"""
|
||||
|
||||
# issue #16726
|
||||
# the code below should give a clean CT error, not an internal error
|
||||
import std/jsffi
|
||||
let a = toJs(3)
|
||||
let b = a.toJs(int)
|
||||
@@ -194,6 +194,11 @@ block: # tupleLen
|
||||
MyGenericTuple2Alias2 = MyGenericTuple2Alias[float]
|
||||
static: doAssert MyGenericTuple2Alias2.tupleLen == 3
|
||||
|
||||
type
|
||||
MyGenericTuple3[T] = T
|
||||
MyGenericTuple3Alias = MyGenericTuple3[(string, int)]
|
||||
static: doAssert MyGenericTuple3Alias.tupleLen == 2
|
||||
|
||||
static: doAssert (int, float).tupleLen == 2
|
||||
static: doAssert (1, ).tupleLen == 1
|
||||
static: doAssert ().tupleLen == 0
|
||||
|
||||
5
tests/misc/mrawjson.nim
Normal file
5
tests/misc/mrawjson.nim
Normal file
@@ -0,0 +1,5 @@
|
||||
## Module description. See [someProc]
|
||||
## another line
|
||||
|
||||
proc someProc*(a, b: int) =
|
||||
## Code should be used like `someProc(1, 2)`
|
||||
@@ -251,6 +251,18 @@ sub/mmain.idx""", context
|
||||
doAssert doSomething["col"].getInt == 0
|
||||
doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}"
|
||||
|
||||
block: # nim jsondoc --raw switch
|
||||
let file = testsDir / "misc/mrawjson.nim"
|
||||
let output = "nimcache_tjsondoc.json"
|
||||
defer: removeFile(output)
|
||||
let (msg, exitCode) = execCmdEx(fmt"{nim} jsondoc --raw -o:{output} {file}")
|
||||
doAssert exitCode == 0, msg
|
||||
|
||||
let data = parseFile(output)
|
||||
doAssert data["moduleDescription"].getStr == "Module description. See [someProc]\nanother line"
|
||||
let someProc = data["entries"][0]
|
||||
doAssert someProc["description"].getStr == "Code should be used like `someProc(1, 2)`"
|
||||
|
||||
block: # further issues with `--backend`
|
||||
let file = testsDir / "misc/mbackend.nim"
|
||||
var cmd = fmt"{nim} doc -b:cpp --hints:off --nimcache:{nimcache} {file}"
|
||||
|
||||
@@ -3,7 +3,7 @@ discard """
|
||||
matrix: "--hint:processing"
|
||||
nimout: '''
|
||||
compile start
|
||||
...
|
||||
....
|
||||
warn_module.nim(6, 6) Hint: 'test' is declared but not used [XDeclaredButNotUsed]
|
||||
compile end
|
||||
'''
|
||||
|
||||
@@ -642,6 +642,30 @@ template main() =
|
||||
let myA = CAMPAIGN_TABLE
|
||||
doAssert $parseEnum[Tables](myA) == "wikientries_campaign"
|
||||
|
||||
block:
|
||||
const tripleQuotedStr = """foobar"""
|
||||
|
||||
type MyEnum = enum
|
||||
a = tripleQuotedStr
|
||||
b = """bazquz"""
|
||||
|
||||
let myA = tripleQuotedStr
|
||||
doAssert $parseEnum[MyEnum](myA) == myA
|
||||
let myB = "bazquz"
|
||||
doAssert $parseEnum[MyEnum](myB) == myB
|
||||
|
||||
block:
|
||||
const rawStr = r"foobar"
|
||||
|
||||
type MyEnum = enum
|
||||
a = rawStr
|
||||
b = r"bazquz"
|
||||
|
||||
let myA = rawStr
|
||||
doAssert $parseEnum[MyEnum](myA) == myA
|
||||
let myB = r"bazquz"
|
||||
doAssert $parseEnum[MyEnum](myB) == myB
|
||||
|
||||
block: # check enum defined in block
|
||||
type
|
||||
Bar = enum
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discard """
|
||||
cmd: "nim cpp --incremental:on $file"
|
||||
cmd: "nim cpp --incremental:legacy $file"
|
||||
"""
|
||||
|
||||
{.emit:"""/*TYPESECTION*/
|
||||
@@ -8,4 +8,4 @@ discard """
|
||||
""".}
|
||||
|
||||
type Foo {.importcpp.} = object
|
||||
echo $Foo() #Notice the generic is instantiate in the this module if not, it wouldnt find Foo
|
||||
echo $Foo() #Notice the generic is instantiate in the this module if not, it wouldnt find Foo
|
||||
@@ -28,10 +28,6 @@ const
|
||||
("nkError", "err"),
|
||||
("nkType", "onlytype"),
|
||||
("nkTypeSection", "type"),
|
||||
("tySequence", "seq"),
|
||||
("tyVar", "mut"),
|
||||
("tyProc", "proctype"),
|
||||
("tyUncheckedArray", "uarray"),
|
||||
("nkExprEqExpr", "vv"),
|
||||
("nkExprColonExpr", "kv"),
|
||||
("nkDerefExpr", "deref"),
|
||||
@@ -55,17 +51,75 @@ const
|
||||
("mVar", "varm"),
|
||||
("mInSet", "contains"),
|
||||
("mNil", "nilm"),
|
||||
("tyBuiltInTypeClass", "bconcept"),
|
||||
("tyUserTypeClass", "uconcept"),
|
||||
("tyUserTypeClassInst", "uconceptinst"),
|
||||
("tyCompositeTypeClass", "cconcept"),
|
||||
("tyGenericInvocation", "ginvoke"),
|
||||
("tyGenericBody", "gbody"),
|
||||
("tyGenericInst", "ginst"),
|
||||
("tyGenericParam", "gparam"),
|
||||
("nkStmtList", "stmts"),
|
||||
("nkDotExpr", "dot"),
|
||||
("nkBracketExpr", "at")
|
||||
("nkBracketExpr", "at"),
|
||||
|
||||
("tyNone", "n0"), # we always use a digit for type kinds so there can be no overlap with node kinds
|
||||
("tyBool", "b0"),
|
||||
("tyChar", "c0"),
|
||||
("tyEmpty", "e0"),
|
||||
("tyAlias", "a0"),
|
||||
("tyNil", "n1"),
|
||||
("tyUntyped", "U0"),
|
||||
("tyTyped", "t0"),
|
||||
("tyTypeDesc", "t1"),
|
||||
("tyGenericInvocation", "g0"),
|
||||
("tyGenericBody", "g1"),
|
||||
("tyGenericInst", "g2"),
|
||||
("tyGenericParam", "g4"),
|
||||
("tyDistinct", "d0"),
|
||||
("tyEnum", "e1"),
|
||||
("tyOrdinal", "o0"),
|
||||
("tyArray", "a1"),
|
||||
("tyObject", "o1"),
|
||||
("tyTuple", "t2"),
|
||||
("tySet", "s0"),
|
||||
("tyRange", "r0"),
|
||||
("tyPtr", "p0"),
|
||||
("tyRef", "r1"),
|
||||
("tyVar", "v0"),
|
||||
("tySequence", "s1"),
|
||||
("tyProc", "p1"),
|
||||
("tyPointer", "p2"),
|
||||
("tyOpenArray", "o3"),
|
||||
("tyString", "s2"),
|
||||
("tyCstring", "c1"),
|
||||
("tyForward", "F0"),
|
||||
("tyInt", "i0"),
|
||||
("tyInt8", "i1"),
|
||||
("tyInt16", "i2"),
|
||||
("tyInt32", "i3"),
|
||||
("tyInt64", "i4"),
|
||||
("tyFloat", "f0"),
|
||||
("tyFloat32", "f1"),
|
||||
("tyFloat64", "f2"),
|
||||
("tyFloat128", "f3"),
|
||||
("tyUInt", "u0"),
|
||||
("tyUInt8", "u1"),
|
||||
("tyUInt16", "u2"),
|
||||
("tyUInt32", "u3"),
|
||||
("tyUInt64", "u4"),
|
||||
("tyOwned", "o2"),
|
||||
("tySink", "s3"),
|
||||
("tyLent", "L0"),
|
||||
("tyVarargs", "v1"),
|
||||
("tyUncheckedArray", "U1"),
|
||||
("tyError", "e2"),
|
||||
("tyBuiltInTypeClass", "b1"),
|
||||
("tyUserTypeClass", "U2"),
|
||||
("tyUserTypeClassInst", "U3"),
|
||||
("tyCompositeTypeClass", "c2"),
|
||||
("tyInferred", "I0"),
|
||||
("tyAnd", "a2"),
|
||||
("tyOr", "o4"),
|
||||
("tyNot", "n2"),
|
||||
("tyAnything", "a3"),
|
||||
("tyStatic", "s4"),
|
||||
("tyFromExpr", "F1"),
|
||||
("tyConcept", "c3"),
|
||||
("tyVoid", "v2"),
|
||||
("tyIterable", "I1")
|
||||
]
|
||||
SuffixesToReplace = [
|
||||
("Section", ""), ("Branch", ""), ("Stmt", ""), ("I", ""),
|
||||
|
||||
Reference in New Issue
Block a user