mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-18 02:27:10 +00:00
Merge branch 'devel' into pr_Raffiniert
This commit is contained in:
@@ -549,12 +549,19 @@ proc add*(father, son: PType) =
|
||||
proc addAllowNil*(father, son: PType) {.inline.} =
|
||||
father.sonsImpl.add son
|
||||
|
||||
template `[]`*(n: PType, i: int): PType = n.sonsImpl[i]
|
||||
template `[]`*(n: PType, i: int): PType =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[i]
|
||||
template `[]=`*(n: PType, i: int; x: PType) =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[i] = x
|
||||
|
||||
template `[]`*(n: PType, i: BackwardsIndex): PType = n[n.len - i.int]
|
||||
template `[]=`*(n: PType, i: BackwardsIndex; x: PType) = n[n.len - i.int] = x
|
||||
template `[]`*(n: PType, i: BackwardsIndex): PType =
|
||||
if n.state == Partial: loadType(n)
|
||||
n[n.len - i.int]
|
||||
template `[]=`*(n: PType, i: BackwardsIndex; x: PType) =
|
||||
if n.state == Partial: loadType(n)
|
||||
n[n.len - i.int] = x
|
||||
|
||||
proc getDeclPragma*(n: PNode): PNode =
|
||||
## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found.
|
||||
@@ -791,29 +798,57 @@ proc replaceFirstSon*(n, newson: PNode) {.inline.} =
|
||||
proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} =
|
||||
n.sons[i] = newson
|
||||
|
||||
proc last*(n: PType): PType {.inline.} = n.sonsImpl[^1]
|
||||
proc last*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[^1]
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} = n.sonsImpl[^1]
|
||||
proc skipModifier*(n: PType): PType {.inline.} = n.sonsImpl[^1]
|
||||
proc elementType*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[^1]
|
||||
|
||||
proc indexType*(n: PType): PType {.inline.} = n.sonsImpl[0]
|
||||
proc baseClass*(n: PType): PType {.inline.} = n.sonsImpl[0]
|
||||
proc skipModifier*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[^1]
|
||||
|
||||
proc indexType*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0]
|
||||
|
||||
proc baseClass*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0]
|
||||
|
||||
proc base*(t: PType): PType {.inline.} =
|
||||
if t.state == Partial: loadType(t)
|
||||
result = t.sonsImpl[0]
|
||||
|
||||
proc returnType*(n: PType): PType {.inline.} = n.sonsImpl[0]
|
||||
proc returnType*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0]
|
||||
|
||||
proc setReturnType*(n, r: PType) {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0] = r
|
||||
|
||||
proc setIndexType*(n, idx: PType) {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0] = idx
|
||||
|
||||
proc firstParamType*(n: PType): PType {.inline.} = n.sonsImpl[1]
|
||||
proc firstGenericParam*(n: PType): PType {.inline.} = n.sonsImpl[1]
|
||||
proc firstParamType*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[1]
|
||||
|
||||
proc typeBodyImpl*(n: PType): PType {.inline.} = n.sonsImpl[^1]
|
||||
proc firstGenericParam*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[1]
|
||||
|
||||
proc genericHead*(n: PType): PType {.inline.} = n.sonsImpl[0]
|
||||
proc typeBodyImpl*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[^1]
|
||||
|
||||
proc genericHead*(n: PType): PType {.inline.} =
|
||||
if n.state == Partial: loadType(n)
|
||||
n.sonsImpl[0]
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## Used throughout the compiler code to test whether a type tree contains or
|
||||
@@ -854,14 +889,6 @@ proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode =
|
||||
result = newNode(kind)
|
||||
result.floatVal = floatVal
|
||||
|
||||
proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
|
||||
result = newNode(kind)
|
||||
result.strVal = strVal
|
||||
|
||||
proc newStrNode*(strVal: string; info: TLineInfo): PNode =
|
||||
result = newNodeI(nkStrLit, info)
|
||||
result.strVal = strVal
|
||||
|
||||
proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
|
||||
params,
|
||||
name, pattern, genericParams,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -981,6 +981,14 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||
result.typField = sym.typImpl
|
||||
result.info = info
|
||||
|
||||
proc newStrNode*(kind: TNodeKind, strVal: string): PNode =
|
||||
result = newNode(kind)
|
||||
result.strVal = strVal
|
||||
|
||||
proc newStrNode*(strVal: string; info: TLineInfo): PNode =
|
||||
result = newNodeI(nkStrLit, info)
|
||||
result.strVal = strVal
|
||||
|
||||
proc forcePartial*(s: PSym) =
|
||||
## Resets all impl-fields to their default values and sets state to Partial.
|
||||
## This is useful for creating a stub symbol that can be lazily loaded later.
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
## implementation.
|
||||
|
||||
template detectVersion(field, corename) =
|
||||
if m.g.field == 0:
|
||||
let core = getCompilerProc(m.g.graph, corename)
|
||||
if core == nil or core.kind != skConst:
|
||||
m.g.field = 1
|
||||
else:
|
||||
m.g.field = toInt(ast.getInt(core.astdef))
|
||||
result = m.g.field
|
||||
if m.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcHooks}:
|
||||
result = 2
|
||||
else:
|
||||
result = 1
|
||||
|
||||
proc detectStrVersion(m: BModule): int =
|
||||
detectVersion(strVersion, "nimStrVersion")
|
||||
|
||||
@@ -721,7 +721,7 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
|
||||
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
|
||||
# with heavily templatized C++ code:
|
||||
if not isImportedCppType(rectype):
|
||||
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
|
||||
let fieldType = field.loc.t.skipTypes(abstractInst)
|
||||
var typ: Rope = ""
|
||||
var isFlexArray = false
|
||||
var initializer = ""
|
||||
|
||||
@@ -1307,16 +1307,15 @@ proc countStateOccurences(ctx: var Ctx, n: PNode, stateOccurences: var openArray
|
||||
|
||||
proc replaceDeletedStates(ctx: var Ctx, n: PNode): PNode =
|
||||
result = n
|
||||
for i in 0 ..< n.safeLen:
|
||||
let c = n[i]
|
||||
if c.kind == nkIntLit:
|
||||
let idx = c.intVal
|
||||
if idx >= 0 and idx < ctx.states.len and ctx.states[idx].label == c and ctx.states[idx].deletable:
|
||||
let gt = ctx.replaceDeletedStates(skipStmtList(ctx.states[idx].body))
|
||||
assert(gt.kind == nkGotoState)
|
||||
n[i] = gt[0]
|
||||
else:
|
||||
n[i] = ctx.replaceDeletedStates(c)
|
||||
if n.kind == nkIntLit:
|
||||
let idx = n.intVal
|
||||
if idx >= 0 and idx < ctx.states.len and ctx.states[idx].label == n and ctx.states[idx].deletable:
|
||||
let gt = ctx.replaceDeletedStates(skipStmtList(ctx.states[idx].body))
|
||||
assert(gt.kind == nkGotoState)
|
||||
result = gt[0]
|
||||
else:
|
||||
for i in 0 ..< n.safeLen:
|
||||
n[i] = ctx.replaceDeletedStates(n[i])
|
||||
|
||||
proc replaceInlinedStates(ctx: var Ctx, n: PNode): PNode =
|
||||
## Find all nkGotoState(stateIdx) nodes that do not follow nkYield.
|
||||
@@ -1347,6 +1346,7 @@ proc optimizeStates(ctx: var Ctx) =
|
||||
# Replace deletable state labels to labels of respective non-empty states
|
||||
for i in 0 .. ctx.states.high:
|
||||
ctx.states[i].body = ctx.replaceDeletedStates(ctx.states[i].body)
|
||||
ctx.states[i].excLandingState = ctx.replaceDeletedStates(ctx.states[i].excLandingState)
|
||||
|
||||
# Remove deletable states
|
||||
var i = 0
|
||||
|
||||
@@ -498,6 +498,8 @@ proc parseCommand*(command: string): Command =
|
||||
of "secret": cmdInteractive
|
||||
of "nop", "help": cmdNop
|
||||
of "jsonscript": cmdJsonscript
|
||||
of "nifc": cmdNifC # generate C from NIF files
|
||||
of "deps": cmdDeps # generate .build.nif for nifmake
|
||||
else: cmdUnknown
|
||||
|
||||
proc setCmd*(conf: ConfigRef, cmd: Command) =
|
||||
@@ -510,6 +512,12 @@ proc setCmd*(conf: ConfigRef, cmd: Command) =
|
||||
of cmdCompileToOC: conf.backend = backendObjc
|
||||
of cmdCompileToJS: conf.backend = backendJs
|
||||
of cmdCompileToNif: conf.backend = backendNif
|
||||
of cmdNifC:
|
||||
conf.backend = backendC # NIF to C compilation
|
||||
conf.globalOptions.incl optCompress # enable NIF loading
|
||||
of cmdM:
|
||||
# cmdM requires optCompress for proper IC handling (include files, etc.)
|
||||
conf.globalOptions.incl optCompress
|
||||
else: discard
|
||||
|
||||
proc setCommandEarly*(conf: ConfigRef, command: string) =
|
||||
|
||||
340
compiler/deps.nim
Normal file
340
compiler/deps.nim
Normal file
@@ -0,0 +1,340 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2025 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Generate a .build.nif file for nifmake from a Nim project.
|
||||
## This enables incremental and parallel compilation using the `m` switch.
|
||||
|
||||
import std / [os, tables, sets, times, osproc, strutils]
|
||||
import options, msgs, pathutils, lineinfos
|
||||
|
||||
import "../dist/nimony/src/lib" / [nifstreams, nifcursors, bitabs, nifreader, nifbuilder]
|
||||
import "../dist/nimony/src/gear2" / modnames
|
||||
|
||||
type
|
||||
FilePair = object
|
||||
nimFile: string
|
||||
modname: string
|
||||
|
||||
Node = ref object
|
||||
files: seq[FilePair] # main file + includes
|
||||
deps: seq[int] # indices into DepContext.nodes
|
||||
id: int
|
||||
|
||||
DepContext = object
|
||||
config: ConfigRef
|
||||
nifler: string
|
||||
nodes: seq[Node]
|
||||
processedModules: Table[string, int] # modname -> node index
|
||||
includeStack: seq[string]
|
||||
|
||||
proc toPair(c: DepContext; f: string): FilePair =
|
||||
FilePair(nimFile: f, modname: moduleSuffix(f, cast[seq[string]](c.config.searchPaths)))
|
||||
|
||||
proc depsFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".deps.nif"
|
||||
|
||||
proc parsedFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".p.nif"
|
||||
|
||||
proc semmedFile(c: DepContext; f: FilePair): string =
|
||||
getNimcacheDir(c.config).string / f.modname & ".nif"
|
||||
|
||||
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 = ""
|
||||
|
||||
proc runNifler(c: DepContext; nimFile: string): bool =
|
||||
## Run nifler deps on a file if needed. Returns true on success.
|
||||
let pair = c.toPair(nimFile)
|
||||
let depsPath = c.depsFile(pair)
|
||||
|
||||
# Check if deps file is up-to-date
|
||||
if fileExists(depsPath) and fileExists(nimFile):
|
||||
if getLastModificationTime(depsPath) > getLastModificationTime(nimFile):
|
||||
return true # Already up-to-date
|
||||
|
||||
# Create output directory if needed
|
||||
createDir(parentDir(depsPath))
|
||||
|
||||
# Run nifler deps
|
||||
let cmd = quoteShell(c.nifler) & " deps " & quoteShell(nimFile) & " " & quoteShell(depsPath)
|
||||
let exitCode = execShellCmd(cmd)
|
||||
result = exitCode == 0
|
||||
|
||||
proc resolveFile(c: DepContext; origin, toResolve: string): string =
|
||||
## Resolve an import path relative to origin file
|
||||
# Handle std/ prefix
|
||||
var path = toResolve
|
||||
if path.startsWith("std/"):
|
||||
path = path.substr(4)
|
||||
|
||||
# Try relative to origin first
|
||||
let originDir = parentDir(origin)
|
||||
result = originDir / path.addFileExt("nim")
|
||||
if fileExists(result):
|
||||
return result
|
||||
|
||||
# Try search paths
|
||||
for searchPath in c.config.searchPaths:
|
||||
result = searchPath.string / path.addFileExt("nim")
|
||||
if fileExists(result):
|
||||
return result
|
||||
|
||||
result = ""
|
||||
|
||||
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node)
|
||||
|
||||
proc processInclude(c: var DepContext; includePath: string; current: Node) =
|
||||
let resolved = resolveFile(c, current.files[current.files.len - 1].nimFile, includePath)
|
||||
if resolved.len == 0 or not fileExists(resolved):
|
||||
return
|
||||
|
||||
# Check for recursive includes
|
||||
for s in c.includeStack:
|
||||
if s == resolved:
|
||||
return # Skip recursive include
|
||||
|
||||
c.includeStack.add resolved
|
||||
current.files.add c.toPair(resolved)
|
||||
traverseDeps(c, c.toPair(resolved), current)
|
||||
discard c.includeStack.pop()
|
||||
|
||||
proc processImport(c: var DepContext; importPath: string; current: Node) =
|
||||
let resolved = resolveFile(c, current.files[0].nimFile, importPath)
|
||||
if resolved.len == 0 or not fileExists(resolved):
|
||||
return
|
||||
|
||||
let pair = c.toPair(resolved)
|
||||
let existingIdx = c.processedModules.getOrDefault(pair.modname, -1)
|
||||
|
||||
if existingIdx == -1:
|
||||
# New module - create node and process it
|
||||
let newNode = Node(files: @[pair], id: c.nodes.len)
|
||||
current.deps.add newNode.id
|
||||
c.processedModules[pair.modname] = newNode.id
|
||||
c.nodes.add newNode
|
||||
traverseDeps(c, pair, newNode)
|
||||
else:
|
||||
# Already processed - just add dependency
|
||||
if existingIdx notin current.deps:
|
||||
current.deps.add existingIdx
|
||||
|
||||
proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) =
|
||||
## Read a .deps.nif file and process imports/includes
|
||||
let depsPath = c.depsFile(pair)
|
||||
if not fileExists(depsPath):
|
||||
return
|
||||
|
||||
var s = nifstreams.open(depsPath)
|
||||
defer: nifstreams.close(s)
|
||||
discard processDirectives(s.r)
|
||||
|
||||
var t = next(s)
|
||||
if t.kind != ParLe:
|
||||
return
|
||||
|
||||
# Skip to content (past stmts tag)
|
||||
t = next(s)
|
||||
|
||||
while t.kind != EofToken:
|
||||
if t.kind == ParLe:
|
||||
let tag = pool.tags[t.tagId]
|
||||
case tag
|
||||
of "import", "fromimport":
|
||||
# Read import path
|
||||
t = next(s)
|
||||
# Check for "when" marker (conditional import)
|
||||
if t.kind == Ident and pool.strings[t.litId] == "when":
|
||||
t = next(s) # skip it, still process the import
|
||||
# Handle path expression (could be ident, string, or infix like std/foo)
|
||||
var importPath = ""
|
||||
if t.kind == Ident:
|
||||
importPath = pool.strings[t.litId]
|
||||
elif t.kind == StringLit:
|
||||
importPath = pool.strings[t.litId]
|
||||
elif t.kind == ParLe and pool.tags[t.tagId] == "infix":
|
||||
# Handle std / foo style imports
|
||||
t = next(s) # skip infix tag
|
||||
if t.kind == Ident: # operator (/)
|
||||
t = next(s)
|
||||
if t.kind == Ident: # first part (std)
|
||||
importPath = pool.strings[t.litId]
|
||||
t = next(s)
|
||||
if t.kind == Ident: # second part (foo)
|
||||
importPath = importPath & "/" & pool.strings[t.litId]
|
||||
if importPath.len > 0:
|
||||
processImport(c, importPath, current)
|
||||
# Skip to end of import node
|
||||
var depth = 1
|
||||
while depth > 0:
|
||||
t = next(s)
|
||||
if t.kind == ParLe: inc depth
|
||||
elif t.kind == ParRi: dec depth
|
||||
of "include":
|
||||
# Read include path
|
||||
t = next(s)
|
||||
if t.kind == Ident and pool.strings[t.litId] == "when":
|
||||
t = next(s) # skip conditional marker
|
||||
var includePath = ""
|
||||
if t.kind == Ident:
|
||||
includePath = pool.strings[t.litId]
|
||||
elif t.kind == StringLit:
|
||||
includePath = pool.strings[t.litId]
|
||||
if includePath.len > 0:
|
||||
processInclude(c, includePath, current)
|
||||
# Skip to end
|
||||
var depth = 1
|
||||
while depth > 0:
|
||||
t = next(s)
|
||||
if t.kind == ParLe: inc depth
|
||||
elif t.kind == ParRi: dec depth
|
||||
else:
|
||||
# Skip unknown node
|
||||
var depth = 1
|
||||
while depth > 0:
|
||||
t = next(s)
|
||||
if t.kind == ParLe: inc depth
|
||||
elif t.kind == ParRi: dec depth
|
||||
t = next(s)
|
||||
|
||||
proc traverseDeps(c: var DepContext; pair: FilePair; current: Node) =
|
||||
## Process a module: run nifler and read deps
|
||||
if not runNifler(c, pair.nimFile):
|
||||
rawMessage(c.config, errGenerated, "nifler failed for: " & pair.nimFile)
|
||||
return
|
||||
readDepsFile(c, pair, current)
|
||||
|
||||
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"
|
||||
|
||||
var b = nifbuilder.open(result)
|
||||
defer: b.close()
|
||||
|
||||
b.addHeader("nim deps", "nifmake")
|
||||
b.addTree "stmts"
|
||||
|
||||
# Define nifler command
|
||||
b.addTree "cmd"
|
||||
b.addSymbolDef "nifler"
|
||||
b.addStrLit c.nifler
|
||||
b.addStrLit "parse"
|
||||
b.addStrLit "--deps"
|
||||
b.addTree "input"
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Define nim m command
|
||||
b.addTree "cmd"
|
||||
b.addSymbolDef "nim_m"
|
||||
b.addStrLit getAppFilename()
|
||||
b.addStrLit "m"
|
||||
# Add search paths
|
||||
for p in c.config.searchPaths:
|
||||
b.addStrLit "--path:" & p.string
|
||||
b.addTree "input"
|
||||
b.addIntLit 0
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Build rules for parsing (nifler)
|
||||
var seenFiles = initHashSet[string]()
|
||||
for node in c.nodes:
|
||||
for pair in node.files:
|
||||
let parsed = c.parsedFile(pair)
|
||||
if not seenFiles.containsOrIncl(parsed):
|
||||
b.addTree "do"
|
||||
b.addIdent "nifler"
|
||||
b.addTree "input"
|
||||
b.addStrLit pair.nimFile
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.addStrLit parsed
|
||||
b.endTree()
|
||||
b.addTree "output"
|
||||
b.addStrLit c.depsFile(pair)
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
# Build rules for semantic checking (nim m)
|
||||
for i in countdown(c.nodes.len - 1, 0):
|
||||
let node = c.nodes[i]
|
||||
let pair = node.files[0]
|
||||
b.addTree "do"
|
||||
b.addIdent "nim_m"
|
||||
# Input: all parsed files for this module
|
||||
for f in node.files:
|
||||
b.addTree "input"
|
||||
b.addStrLit c.parsedFile(f)
|
||||
b.endTree()
|
||||
# Also depend on semmed files of dependencies
|
||||
for depIdx in node.deps:
|
||||
b.addTree "input"
|
||||
b.addStrLit c.semmedFile(c.nodes[depIdx].files[0])
|
||||
b.endTree()
|
||||
# Output: semmed file
|
||||
b.addTree "output"
|
||||
b.addStrLit c.semmedFile(pair)
|
||||
b.endTree()
|
||||
b.addTree "args"
|
||||
b.addStrLit pair.nimFile
|
||||
b.endTree()
|
||||
b.endTree()
|
||||
|
||||
b.endTree() # stmts
|
||||
|
||||
proc commandDeps*(conf: ConfigRef) =
|
||||
## Main entry point for `nim deps`
|
||||
when not defined(nimKochBootstrap):
|
||||
let nifler = findNifler()
|
||||
if nifler.len == 0:
|
||||
rawMessage(conf, errGenerated, "nifler tool not found. Install nimony or add nifler to PATH.")
|
||||
return
|
||||
|
||||
let projectFile = conf.projectFull.string
|
||||
if not fileExists(projectFile):
|
||||
rawMessage(conf, errGenerated, "project file not found: " & projectFile)
|
||||
return
|
||||
|
||||
# Create nimcache directory
|
||||
createDir(getNimcacheDir(conf).string)
|
||||
|
||||
var c = DepContext(
|
||||
config: conf,
|
||||
nifler: nifler,
|
||||
nodes: @[],
|
||||
processedModules: initTable[string, int](),
|
||||
includeStack: @[]
|
||||
)
|
||||
|
||||
# Create root node for main project file
|
||||
let rootPair = c.toPair(projectFile)
|
||||
let rootNode = Node(files: @[rootPair], id: 0)
|
||||
c.nodes.add rootNode
|
||||
c.processedModules[rootPair.modname] = 0
|
||||
|
||||
# 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)
|
||||
else:
|
||||
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")
|
||||
@@ -1212,8 +1212,10 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
result.ast[bodyPos].add newAsgnStmt(d, src)
|
||||
else:
|
||||
var tk: TTypeKind
|
||||
var skipped: PType = nil
|
||||
if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
|
||||
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
|
||||
skipped = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink})
|
||||
tk = skipped.kind
|
||||
else:
|
||||
tk = tyNone # no special casing for strings and seqs
|
||||
case tk
|
||||
@@ -1223,7 +1225,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
fillStrOp(a, typ, result.ast[bodyPos], d, src)
|
||||
else:
|
||||
fillBody(a, typ, result.ast[bodyPos], d, src)
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(typ):
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not isObjLackingTypeField(skipped):
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
|
||||
@@ -273,6 +273,10 @@ const
|
||||
errFloatToString* = "cannot convert '$1' to '$2'"
|
||||
|
||||
type
|
||||
FileInfoKind* = enum
|
||||
fikSource, ## A real source file path
|
||||
fikNifModule ## A NIF module suffix (not a real path)
|
||||
|
||||
TFileInfo* = object
|
||||
fullPath*: AbsoluteFile # This is a canonical full filesystem path
|
||||
projPath*: RelativeFile # This is relative to the project's root
|
||||
@@ -291,6 +295,7 @@ type
|
||||
# for 'nimsuggest'
|
||||
hash*: string # the checksum of the file
|
||||
dirty*: bool # for 'nimpretty' like tooling
|
||||
kind*: FileInfoKind # distinguishes real files from NIF suffixes
|
||||
when defined(nimpretty):
|
||||
fullContent*: string
|
||||
FileIndex* = distinct int32
|
||||
|
||||
@@ -32,6 +32,10 @@ import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
import pipelines
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
import nifbackend
|
||||
import deps
|
||||
|
||||
when not defined(leanCompiler):
|
||||
import docgen
|
||||
|
||||
@@ -133,6 +137,22 @@ proc commandCompileToNif(graph: ModuleGraph) =
|
||||
setPipeLinePass(graph, NifgenPass)
|
||||
compilePipelineProject(graph)
|
||||
|
||||
proc commandNifC(graph: ModuleGraph) =
|
||||
## Generate C code from precompiled NIF files.
|
||||
## This is the new IC approach: compile modules to NIF first with `nim m`,
|
||||
## then generate C code from the entry.nif file with whole-program DCE.
|
||||
when not defined(nimKochBootstrap):
|
||||
let conf = graph.config
|
||||
extccomp.initVars(conf)
|
||||
|
||||
if not extccomp.ccHasSaneOverflow(conf):
|
||||
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
|
||||
|
||||
# Use the NIF backend to generate C code
|
||||
nifbackend.generateCode(graph, conf.projectMainIdx)
|
||||
else:
|
||||
rawMessage(graph.config, errGenerated, "NIF backend not available during bootstrap build")
|
||||
|
||||
proc commandCompileToC(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
extccomp.initVars(conf)
|
||||
@@ -420,9 +440,22 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of cmdCheck:
|
||||
commandCheck(graph)
|
||||
of cmdM:
|
||||
graph.config.symbolFiles = v2Sf
|
||||
setUseIc(graph.config.symbolFiles != disabledSf)
|
||||
# cmdM uses NIF files, not ROD files
|
||||
graph.config.symbolFiles = disabledSf
|
||||
setUseIc(false)
|
||||
commandCheck(graph)
|
||||
of cmdNifC:
|
||||
# Generate C code from NIF files
|
||||
wantMainModule(conf)
|
||||
setOutFile(conf)
|
||||
commandNifC(graph)
|
||||
of cmdDeps:
|
||||
# Generate .build.nif for nifmake
|
||||
wantMainModule(conf)
|
||||
when not defined(nimKochBootstrap):
|
||||
commandDeps(conf)
|
||||
else:
|
||||
rawMessage(conf, errGenerated, "nim deps not available in bootstrap build")
|
||||
of cmdParse:
|
||||
wantMainModule(conf)
|
||||
discard parseFile(conf.projectMainIdx, cache, conf)
|
||||
|
||||
@@ -18,6 +18,7 @@ import ic / [packed_ast, ic]
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
import ast2nif
|
||||
import "../dist/nimony/src/lib" / [nifstreams, bitabs]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
@@ -140,6 +141,7 @@ type
|
||||
cachedFiles*: StringTableRef
|
||||
|
||||
procGlobals*: seq[PNode]
|
||||
nifReplayActions*: Table[int32, seq[PNode]] # module position -> replay actions for NIF
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
idgen*: IdGenerator
|
||||
@@ -366,6 +368,10 @@ proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp;
|
||||
## we also need to record this to the packed module.
|
||||
g.attachedOps[op][t.itemId] = LazySym(sym: value)
|
||||
|
||||
proc setAttachedOp*(g: ModuleGraph; module: int; typeId: ItemId; op: TTypeAttachedOp; value: PSym) =
|
||||
## Overload that takes ItemId directly, useful for registering hooks from NIF index.
|
||||
g.attachedOps[op][typeId] = LazySym(sym: value)
|
||||
|
||||
proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||
## we also need to record this to the packed module.
|
||||
g.attachedOps[op][t.itemId] = LazySym(sym: value)
|
||||
@@ -393,6 +399,10 @@ proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[LazySym]) =
|
||||
# TODO: add it for packed modules
|
||||
g.methodsPerType[id] = methods
|
||||
|
||||
proc addNifReplayAction*(g: ModuleGraph; module: int32; n: PNode) =
|
||||
## Stores a replay action for NIF-based incremental compilation.
|
||||
g.nifReplayActions.mgetOrPut(module, @[]).add n
|
||||
|
||||
iterator getMethodsPerType*(g: ModuleGraph; t: PType): PSym =
|
||||
if g.methodsPerType.contains(t.itemId):
|
||||
for it in mitems g.methodsPerType[t.itemId]:
|
||||
@@ -425,7 +435,30 @@ proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
|
||||
|
||||
proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
|
||||
result = nil
|
||||
if g.config.symbolFiles == disabledSf: return nil
|
||||
if g.config.symbolFiles == disabledSf:
|
||||
# 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)
|
||||
if g.config.cmd == cmdNifC:
|
||||
# First try system module (most compilerprocs are there)
|
||||
let systemFileIdx = g.config.m.systemFileIdx
|
||||
if systemFileIdx != InvalidFileIdx:
|
||||
result = tryResolveCompilerProc(ast.program, name, systemFileIdx)
|
||||
if result != nil:
|
||||
strTableAdd(g.compilerprocs, result)
|
||||
return result
|
||||
|
||||
# Try threadpool module (some compilerprocs like FlowVar are there)
|
||||
# Find threadpool module by searching loaded modules
|
||||
for moduleIdx in 0..<g.ifaces.len:
|
||||
let module = g.ifaces[moduleIdx].module
|
||||
if module != nil and module.name.s == "threadpool":
|
||||
let threadpoolFileIdx = module.position.FileIndex
|
||||
result = tryResolveCompilerProc(ast.program, name, threadpoolFileIdx)
|
||||
if result != nil:
|
||||
strTableAdd(g.compilerprocs, result)
|
||||
return result
|
||||
return nil
|
||||
|
||||
# slow, linear search, but the results are cached:
|
||||
for module in 0..<len(g.packed):
|
||||
@@ -745,9 +778,11 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
proc moduleFromNifFile*(g: ModuleGraph; fileIdx: FileIndex;
|
||||
cachedModules: var seq[FileIndex]): PSym =
|
||||
cachedModules: var seq[FileIndex];
|
||||
loadFullAst: bool = false): PSym =
|
||||
## 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
|
||||
@@ -765,7 +800,38 @@ when not defined(nimKochBootstrap):
|
||||
|
||||
# Register module in graph
|
||||
registerModule(g, result)
|
||||
result.astImpl = loadNifModule(ast.program, fileIdx, g.ifaces[fileIdx.int].interf, g.ifaces[fileIdx.int].interfHidden)
|
||||
var hooks = initTable[nifstreams.SymId, HooksPerType]()
|
||||
var converters: seq[(string, string)] = @[]
|
||||
var classes: seq[ClassIndexEntry] = @[]
|
||||
result.astImpl = loadNifModule(ast.program, fileIdx, g.ifaces[fileIdx.int].interf,
|
||||
g.ifaces[fileIdx.int].interfHidden, hooks, converters, classes, loadFullAst)
|
||||
# Register hooks from NIF index with the module graph
|
||||
for typSymId, hooksPerType in hooks:
|
||||
let typeItemId = parseTypeSymIdToItemId(ast.program, typSymId)
|
||||
if typeItemId.module >= 0:
|
||||
for op in AttachedOp:
|
||||
let (hookSymId, isGeneric) = hooksPerType.a[op]
|
||||
if hookSymId != nifstreams.SymId(0):
|
||||
let hookSym = resolveHookSym(ast.program, hookSymId)
|
||||
if hookSym != nil:
|
||||
setAttachedOp(g, int(fileIdx), typeItemId, toTTypeAttachedOp(op), hookSym)
|
||||
# Register converters from NIF index with the module's interface
|
||||
for (destType, convSym) in converters:
|
||||
let symId = pool.syms.getOrIncl(convSym)
|
||||
let convPSym = resolveHookSym(ast.program, symId) # reuse hook resolution
|
||||
if convPSym != nil:
|
||||
g.ifaces[fileIdx.int].converters.add LazySym(sym: convPSym)
|
||||
# Register methods per type from NIF index
|
||||
for classEntry in classes:
|
||||
let typeItemId = parseTypeSymIdToItemId(ast.program, classEntry.cls)
|
||||
if typeItemId.module >= 0:
|
||||
var methodSyms: seq[LazySym] = @[]
|
||||
for methodEntry in classEntry.methods:
|
||||
let methodSym = resolveHookSym(ast.program, methodEntry.fn)
|
||||
if methodSym != nil:
|
||||
methodSyms.add LazySym(sym: methodSym)
|
||||
if methodSyms.len > 0:
|
||||
setMethodsPerType(g, typeItemId, methodSyms)
|
||||
cachedModules.add fileIdx
|
||||
|
||||
proc configComplete*(g: ModuleGraph) =
|
||||
|
||||
@@ -60,11 +60,12 @@ proc makeCString*(s: string): Rope =
|
||||
toCChar(s[i], result)
|
||||
result.add('\"')
|
||||
|
||||
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
|
||||
proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile; kind = fikSource): TFileInfo =
|
||||
result = TFileInfo(fullPath: fullPath, projPath: projPath,
|
||||
shortName: fullPath.extractFilename,
|
||||
quotedFullName: fullPath.string.makeCString,
|
||||
lines: @[]
|
||||
lines: @[],
|
||||
kind: kind
|
||||
)
|
||||
result.quotedName = result.shortName.makeCString
|
||||
when defined(nimpretty):
|
||||
@@ -138,11 +139,18 @@ proc registerNifSuffix*(conf: ConfigRef; suffix: string; isKnownFile: var bool):
|
||||
if result == InvalidFileIdx:
|
||||
isKnownFile = false
|
||||
result = conf.m.fileInfos.len.FileIndex
|
||||
conf.m.fileInfos.add(newFileInfo(AbsoluteFile suffix, RelativeFile suffix))
|
||||
conf.m.fileInfos.add(newFileInfo(AbsoluteFile suffix, RelativeFile suffix, fikNifModule))
|
||||
conf.m.filenameToIndexTbl[suffix] = result
|
||||
else:
|
||||
isKnownFile = true
|
||||
|
||||
proc fileInfoKind*(conf: ConfigRef; fileIdx: FileIndex): FileInfoKind =
|
||||
## Returns the kind of a FileIndex (source file or NIF module suffix).
|
||||
if fileIdx.int >= 0 and fileIdx.int < conf.m.fileInfos.len:
|
||||
result = conf.m.fileInfos[fileIdx.int].kind
|
||||
else:
|
||||
result = fikSource # Default to source for unknown indices
|
||||
|
||||
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
|
||||
result = TLineInfo(fileIndex: fileInfoIdx)
|
||||
if line < int high(uint16):
|
||||
|
||||
121
compiler/nifbackend.nim
Normal file
121
compiler/nifbackend.nim
Normal file
@@ -0,0 +1,121 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2025 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## NIF-based C/C++ code generator backend.
|
||||
##
|
||||
## This module implements C code generation from precompiled NIF files.
|
||||
## It traverses the module dependency graph starting from the main module
|
||||
## and generates C code for all reachable modules.
|
||||
##
|
||||
## Usage:
|
||||
## 1. Compile modules to NIF: nim m mymodule.nim
|
||||
## 2. Generate C from NIF: nim nifc myproject.nim
|
||||
|
||||
import std/[intsets, tables, sets, os]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
||||
pathutils, extccomp, msgs, modulepaths, idents, types, ast2nif
|
||||
|
||||
proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[PSym] =
|
||||
## 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]
|
||||
result = @[]
|
||||
var cachedModules: seq[FileIndex] = @[]
|
||||
|
||||
while stack.len > 0:
|
||||
let fileIdx = 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)):
|
||||
stack.add dep
|
||||
cachedModules.setLen(0)
|
||||
|
||||
proc setupNifBackendModule(g: ModuleGraph; module: PSym): BModule =
|
||||
## Set up a BModule for code generation from a NIF module.
|
||||
if g.backend == nil:
|
||||
g.backend = cgendata.newModuleList(g)
|
||||
result = cgen.newModule(BModuleList(g.backend), module, g.config)
|
||||
|
||||
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)
|
||||
|
||||
# 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)
|
||||
finalCodegenActions(g, bmod, initStmt)
|
||||
|
||||
# Generate dispatcher methods
|
||||
for disp in getDispatchers(g):
|
||||
genProcAux(bmod, disp)
|
||||
|
||||
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)
|
||||
|
||||
# Also ensure system module is set up and generated 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)
|
||||
|
||||
# Load all modules in dependency order using stack traversal
|
||||
let modules = loadModuleDependencies(g, mainFileIdx)
|
||||
if modules.len == 0:
|
||||
rawMessage(g.config, errGenerated,
|
||||
"Cannot load NIF file for main module: " & toFullPath(g.config, mainFileIdx))
|
||||
return
|
||||
|
||||
# Set up backend modules for all modules that need code generation
|
||||
for module in modules:
|
||||
discard setupNifBackendModule(g, module)
|
||||
|
||||
# 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:
|
||||
generateCodeForModule(g, module)
|
||||
|
||||
# Generate main module last (so all init procs are registered)
|
||||
if mainModule != nil:
|
||||
generateCodeForModule(g, mainModule)
|
||||
|
||||
# Write C files
|
||||
if g.backend != nil:
|
||||
cgenWriteModules(g.backend, g.config)
|
||||
|
||||
# Run C compiler
|
||||
if g.config.cmd != cmdTcc:
|
||||
extccomp.callCCompiler(g.config)
|
||||
if not g.config.hcrOn:
|
||||
extccomp.writeJsonBuildInstructions(g.config, g.cachedFiles)
|
||||
@@ -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 == cmdGendepend:
|
||||
conf.cmd in {cmdGendepend, cmdNifC, cmdDeps, cmdM}:
|
||||
initOrcDefines(conf)
|
||||
|
||||
mainCommand(graph)
|
||||
|
||||
@@ -175,6 +175,8 @@ type
|
||||
cmdJsonscript # compile a .json build file
|
||||
# old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
|
||||
cmdCompileToNif
|
||||
cmdNifC # generate C code from NIF files
|
||||
cmdDeps # generate .build.nif for nifmake
|
||||
|
||||
const
|
||||
cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
|
||||
lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
|
||||
packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg,
|
||||
packages, syntaxes, depends, vm, vmdef, pragmas, idents, lookups, wordrecg,
|
||||
liftdestructors, nifgen
|
||||
|
||||
when not defined(nimKochBootstrap):
|
||||
import ast2nif
|
||||
import "../dist/nimony/src/lib" / [nifstreams, bitabs]
|
||||
|
||||
import pipelineutils
|
||||
|
||||
@@ -38,7 +39,12 @@ proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext):
|
||||
of GenDependPass:
|
||||
result = addDotDependency(bModule, semNode)
|
||||
of SemPass:
|
||||
result = graph.emptyNode
|
||||
# Return the semantic node for cmdM (NIF generation needs it)
|
||||
# For regular check, we don't need the result
|
||||
if graph.config.cmd == cmdM:
|
||||
result = semNode
|
||||
else:
|
||||
result = graph.emptyNode
|
||||
of Docgen2Pass, Docgen2TexPass:
|
||||
when not defined(leanCompiler):
|
||||
result = processNode(bModule, semNode)
|
||||
@@ -160,7 +166,7 @@ proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator
|
||||
s = stream
|
||||
graph.interactive = stream.kind == llsStdIn
|
||||
var topLevelStmts =
|
||||
if optCompress in graph.config.globalOptions:
|
||||
if optCompress in graph.config.globalOptions or graph.config.cmd == cmdM:
|
||||
newNodeI(nkStmtList, module.info)
|
||||
else:
|
||||
nil
|
||||
@@ -235,14 +241,60 @@ 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 and not graph.config.isDefined("nimscript"):
|
||||
if (optCompress in graph.config.globalOptions or graph.config.cmd == cmdM) and
|
||||
not graph.config.isDefined("nimscript"):
|
||||
topLevelStmts.add finalNode
|
||||
writeNifModule(graph.config, module.position.int32, topLevelStmts)
|
||||
# Collect replay actions from both pragma computations and VM state diff
|
||||
var replayActions: seq[PNode] = @[]
|
||||
# Get pragma-recorded replay actions (compile, link, passC, passL, etc.)
|
||||
if graph.nifReplayActions.hasKey(module.position.int32):
|
||||
replayActions.add graph.nifReplayActions[module.position.int32]
|
||||
# Also get VM state diff (macro cache operations)
|
||||
if graph.vm != nil:
|
||||
for (m, n) in PCtx(graph.vm).vmstateDiff:
|
||||
if m == module:
|
||||
replayActions.add n
|
||||
# Collect hooks from the module graph for the current module
|
||||
var hooks = default array[AttachedOp, seq[HookIndexEntry]]
|
||||
for op in TTypeAttachedOp:
|
||||
if op == attachedDeepCopy: continue # Not supported in nimony
|
||||
let nimonyOp = toAttachedOp(op)
|
||||
for typeId, lazySym in graph.attachedOps[op]:
|
||||
if typeId.module == module.position.int32:
|
||||
let sym = lazySym.sym
|
||||
if sym != nil:
|
||||
hooks[nimonyOp].add toHookIndexEntry(graph.config, typeId, sym)
|
||||
# Collect converters from the module's interface
|
||||
var converters: seq[(nifstreams.SymId, nifstreams.SymId)] = @[]
|
||||
for lazySym in graph.ifaces[module.position].converters:
|
||||
let sym = lazySym.sym
|
||||
if sym != nil:
|
||||
let entry = toConverterIndexEntry(graph.config, sym)
|
||||
if entry[0] != nifstreams.SymId(0):
|
||||
converters.add entry
|
||||
# Collect methods per type for classes
|
||||
var classes: seq[ClassIndexEntry] = @[]
|
||||
for typeId, methodList in graph.methodsPerType:
|
||||
if typeId.module == module.position.int32:
|
||||
var methods: seq[MethodIndexEntry] = @[]
|
||||
for lazySym in methodList:
|
||||
let sym = lazySym.sym
|
||||
if sym != nil:
|
||||
# Generate a method signature (simplified - name and param count)
|
||||
let sig = sym.name.s & "/" & $sym.typImpl.sonsImpl.len
|
||||
methods.add toMethodIndexEntry(graph.config, sym, sig)
|
||||
if methods.len > 0:
|
||||
classes.add ClassIndexEntry(
|
||||
cls: toClassSymId(graph.config, typeId),
|
||||
methods: methods
|
||||
)
|
||||
writeNifModule(graph.config, module.position.int32, topLevelStmts, hooks, converters, classes, replayActions)
|
||||
|
||||
if graph.config.backend notin {backendC, backendCpp, backendObjc}:
|
||||
if graph.config.backend notin {backendC, backendCpp, backendObjc} and graph.config.cmd != cmdM:
|
||||
# We only write rod files here if no C-like backend is active.
|
||||
# The C-like backends have been patched to support the IC mechanism.
|
||||
# They are responsible for closing the rod files. See `cbackend.nim`.
|
||||
# cmdM uses NIF files only, not ROD files.
|
||||
closeRodFile(graph, module)
|
||||
result = true
|
||||
|
||||
@@ -261,11 +313,21 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
|
||||
if result == nil:
|
||||
var cachedModules: seq[FileIndex] = @[]
|
||||
when not defined(nimKochBootstrap):
|
||||
# Try loading from NIF file first if optCompress is enabled
|
||||
if optCompress in graph.config.globalOptions and not graph.config.isDefined("nimscript"):
|
||||
# For cmdM: load imports from NIF files (but compile the main module from source)
|
||||
# Skip when withinSystem is true (compiling system.nim itself)
|
||||
if graph.config.cmd == cmdM and
|
||||
sfMainModule notin flags and
|
||||
not graph.withinSystem and
|
||||
not graph.config.isDefined("nimscript"):
|
||||
result = moduleFromNifFile(graph, fileIdx, cachedModules)
|
||||
if result == nil:
|
||||
# Fall back to ROD file loading
|
||||
if result == 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
|
||||
if result == nil and graph.config.cmd != cmdM:
|
||||
# Fall back to ROD file loading (not used for cmdM which uses NIF only)
|
||||
result = moduleFromRodFile(graph, fileIdx, cachedModules)
|
||||
let path = toFullPath(graph.config, fileIdx)
|
||||
let filename = AbsoluteFile path
|
||||
@@ -287,8 +349,11 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
|
||||
partialInitModule(result, graph, fileIdx, filename)
|
||||
for m in cachedModules:
|
||||
registerModuleById(graph, m)
|
||||
if sfMainModule in flags and graph.config.cmd == cmdM:
|
||||
discard
|
||||
if graph.config.cmd == cmdM:
|
||||
# cmdM uses NIF files - replay from module AST loaded by loadNifModule
|
||||
let module = graph.getModule(m)
|
||||
if module != nil and module.ast != nil:
|
||||
replayStateChanges(module, graph)
|
||||
else:
|
||||
replayStateChanges(graph.packed.pm[m.int].module, graph)
|
||||
replayGenericCacheInformation(graph, m.int)
|
||||
@@ -346,6 +411,20 @@ proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx
|
||||
graph.withinSystem = true
|
||||
discard graph.compilePipelineModule(projectFile, {sfMainModule, sfSystemModule})
|
||||
graph.withinSystem = false
|
||||
elif graph.config.cmd == cmdM:
|
||||
# For cmdM: load system.nim from NIF first, then compile the main module
|
||||
connectPipelineCallbacks(graph)
|
||||
graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
|
||||
graph.config.libpath / RelativeFile"system.nim")
|
||||
var cachedModules: seq[FileIndex] = @[]
|
||||
when not defined(nimKochBootstrap):
|
||||
graph.systemModule = moduleFromNifFile(graph, graph.config.m.systemFileIdx, cachedModules)
|
||||
if graph.systemModule == nil:
|
||||
let nifPath = toNifFilename(graph.config, graph.config.m.systemFileIdx)
|
||||
localError(graph.config, unknownLineInfo,
|
||||
"nim m requires precompiled NIF for system module (expected: " & nifPath & ")")
|
||||
return
|
||||
discard graph.compilePipelineModule(projectFile, {sfMainModule})
|
||||
else:
|
||||
graph.compilePipelineSystemModule()
|
||||
discard graph.compilePipelineModule(projectFile, {sfMainModule})
|
||||
|
||||
@@ -358,6 +358,9 @@ proc addImportFileDep*(c: PContext; f: FileIndex) =
|
||||
proc addPragmaComputation*(c: PContext; n: PNode) =
|
||||
if c.config.symbolFiles != disabledSf:
|
||||
addPragmaComputation(c.encoder, c.packedRepr, n)
|
||||
# Also store for NIF-based IC (cmdM mode or optCompress)
|
||||
if optCompress in c.config.globalOptions or c.config.cmd == cmdM:
|
||||
addNifReplayAction(c.graph, c.module.position.int32, n)
|
||||
|
||||
proc inclSym(sq: var seq[PSym], s: PSym): bool =
|
||||
for i in 0..<sq.len:
|
||||
|
||||
@@ -85,9 +85,3 @@ func countSetBitsImpl*(x: SomeInteger): int {.inline.} =
|
||||
else:
|
||||
when sizeof(x) <= 4: result = countBitsImpl(x.uint32)
|
||||
else: result = countBitsImpl(x.uint64)
|
||||
|
||||
proc countBits32*(n: uint32): int {.compilerproc, inline.} =
|
||||
result = countSetBitsImpl(n)
|
||||
|
||||
proc countBits64*(n: uint64): int {.compilerproc, inline.} =
|
||||
result = countSetBitsImpl(n)
|
||||
|
||||
@@ -10,6 +10,13 @@
|
||||
# set handling
|
||||
|
||||
|
||||
# IC: compilerprocs now must be defined in system.nim or threadpool.nim!
|
||||
proc countBits32*(n: uint32): int {.compilerproc, inline.} =
|
||||
result = countSetBitsImpl(n)
|
||||
|
||||
proc countBits64*(n: uint64): int {.compilerproc, inline.} =
|
||||
result = countSetBitsImpl(n)
|
||||
|
||||
proc cardSetImpl(s: ptr UncheckedArray[uint8], len: int): int {.inline.} =
|
||||
var i = 0
|
||||
result = 0
|
||||
|
||||
7
tests/destructor/t25341.nim
Normal file
7
tests/destructor/t25341.nim
Normal file
@@ -0,0 +1,7 @@
|
||||
discard """
|
||||
cmd: "nim c --mm:orc $file"
|
||||
output: ""
|
||||
"""
|
||||
import ./t25341_aux/a, ./t25341_aux/b
|
||||
a()
|
||||
b()
|
||||
4
tests/destructor/t25341_aux/a.nim
Normal file
4
tests/destructor/t25341_aux/a.nim
Normal file
@@ -0,0 +1,4 @@
|
||||
import ./module
|
||||
|
||||
proc a*() =
|
||||
discard make1[4]().make2()
|
||||
6
tests/destructor/t25341_aux/b.nim
Normal file
6
tests/destructor/t25341_aux/b.nim
Normal file
@@ -0,0 +1,6 @@
|
||||
import ./module
|
||||
|
||||
var globalObj: Distinct2[4]
|
||||
|
||||
proc b*() =
|
||||
globalObj = make1[4]().make2()
|
||||
14
tests/destructor/t25341_aux/module.nim
Normal file
14
tests/destructor/t25341_aux/module.nim
Normal file
@@ -0,0 +1,14 @@
|
||||
type
|
||||
BaseObject*[N: static int] = object
|
||||
value*: int
|
||||
|
||||
Distinct1*[N: static int] = distinct BaseObject[N]
|
||||
Distinct2*[N: static int] = distinct BaseObject[N]
|
||||
|
||||
proc `=copy`*[N: static int](dest: var Distinct2[N], src: Distinct2[N]) {.error: "no".}
|
||||
|
||||
proc make1*[N: static int](): Distinct1[N] =
|
||||
Distinct1[N](BaseObject[N](value: 0))
|
||||
|
||||
proc make2*[N: static int](u: sink Distinct1[N]): Distinct2[N] =
|
||||
Distinct2[N](BaseObject[N](u))
|
||||
@@ -17,9 +17,12 @@ proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedRe
|
||||
|
||||
var exceptionCaught = false
|
||||
|
||||
var maxIterations = 10000
|
||||
try:
|
||||
for i in it():
|
||||
closureIterResult.add(i)
|
||||
dec maxIterations
|
||||
doAssert(maxIterations > 0, "Too many iterations in test. Infinite loop?")
|
||||
except TestError:
|
||||
exceptionCaught = true
|
||||
|
||||
@@ -847,3 +850,50 @@ block:
|
||||
doAssert(w() == 123)
|
||||
doAssert(getCurrentExceptionMsg() == "Outer error")
|
||||
doAssert(getCurrentExceptionMsg() == "")
|
||||
|
||||
block: #25330 (v1)
|
||||
iterator count1(): int {.closure.} =
|
||||
yield 1
|
||||
raiseTestError()
|
||||
|
||||
iterator count0(): int {.closure.} =
|
||||
try:
|
||||
var count = count1
|
||||
while true:
|
||||
yield count()
|
||||
if finished(count): break
|
||||
finally:
|
||||
try:
|
||||
checkpoint(2)
|
||||
var count2 = count1
|
||||
while true:
|
||||
yield count2()
|
||||
if finished(count2): break
|
||||
discard # removing this outputs "raise"
|
||||
except:
|
||||
checkpoint(3)
|
||||
raise
|
||||
|
||||
testExc(count0, 1, 2, 1, 3)
|
||||
|
||||
block: #25330 (v2)
|
||||
iterator count1(): int {.closure.} =
|
||||
yield 1
|
||||
raiseTestError()
|
||||
|
||||
iterator count0(): int {.closure.} =
|
||||
try:
|
||||
var count = count1
|
||||
for x in 0 .. 10:
|
||||
yield count()
|
||||
finally:
|
||||
try:
|
||||
checkpoint(2)
|
||||
var count2 = count1
|
||||
for x in 0 .. 10:
|
||||
yield count2()
|
||||
except:
|
||||
checkpoint(3)
|
||||
raise
|
||||
|
||||
testExc(count0, 1, 2, 1, 3)
|
||||
|
||||
Reference in New Issue
Block a user