replaces implicit passes array registed at runtime with explicit function calls; simplify compilation pipeline (#21444)

* abolish using passes in the compiler; simplify compilation pipeline

* duplicate code

* Really cool to have the same signature...

* haul

* unify other backends

* refactor process

* introduce PipelinePhase

* refactor compiler

* fixes passes

* fixes nimsuggest

* add a sentinel

* enable docs checkj

* activate doc testing

* clean up

* complete cleanups
This commit is contained in:
ringabout 2023-03-03 14:36:38 +08:00 • committed by GitHub
commit d51a392149
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 531 additions and 313 deletions

View file

@ -12,11 +12,13 @@
import import
ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
nversion, nimsets, msgs, bitsets, idents, types, nversion, nimsets, msgs, bitsets, idents, types,
ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, ccgutils, os, ropes, math, wordrecg, treetab, cgmeth,
rodutils, renderer, cgendata, aliases, rodutils, renderer, cgendata, aliases,
lowerings, tables, sets, ndi, lineinfos, pathutils, transf, lowerings, tables, sets, ndi, lineinfos, pathutils, transf,
injectdestructors, astmsgs, modulepaths, backendpragmas injectdestructors, astmsgs, modulepaths, backendpragmas
import pipelineutils
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/assertions import std/assertions
@ -1938,8 +1940,7 @@ template injectG() {.dirty.} =
graph.backend = newModuleList(graph) graph.backend = newModuleList(graph)
let g = BModuleList(graph.backend) let g = BModuleList(graph.backend)
proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
injectG() injectG()
result = newModule(g, module, graph.config) result = newModule(g, module, graph.config)
result.idgen = idgen result.idgen = idgen
@ -2007,7 +2008,7 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
proc genTopLevelStmt*(m: BModule; n: PNode) = proc genTopLevelStmt*(m: BModule; n: PNode) =
## Also called from `ic/cbackend.nim`. ## Also called from `ic/cbackend.nim`.
if passes.skipCodegen(m.config, n): return if pipelineutils.skipCodegen(m.config, n): return
m.initProc.options = initProcOptions(m) m.initProc.options = initProcOptions(m)
#softRnl = if optLineDir in m.config.options: noRnl else: rnl #softRnl = if optLineDir in m.config.options: noRnl else: rnl
# XXX replicate this logic! # XXX replicate this logic!
@ -2020,12 +2021,6 @@ proc genTopLevelStmt*(m: BModule; n: PNode) =
else: else:
genProcBody(m.initProc, transformedN) genProcBody(m.initProc, transformedN)
proc myProcess(b: PPassContext, n: PNode): PNode =
result = n
if b != nil:
var m = BModule(b)
genTopLevelStmt(m, n)
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
if optForceFullMake notin m.config.globalOptions: if optForceFullMake notin m.config.globalOptions:
if not moduleHasChanged(m.g.graph, m.module): if not moduleHasChanged(m.g.graph, m.module):
@ -2102,7 +2097,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}: if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}:
for i in countdown(high(graph.globalDestructors), 0): for i in countdown(high(graph.globalDestructors), 0):
n.add graph.globalDestructors[i] n.add graph.globalDestructors[i]
if passes.skipCodegen(m.config, n): return if pipelineutils.skipCodegen(m.config, n): return
if moduleHasChanged(graph, m.module): if moduleHasChanged(graph, m.module):
# if the module is cached, we don't regenerate the main proc # if the module is cached, we don't regenerate the main proc
# nor the dispatchers? But if the dispatchers changed? # nor the dispatchers? But if the dispatchers changed?
@ -2143,12 +2138,6 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
let mm = m let mm = m
m.g.modulesClosed.add mm m.g.modulesClosed.add mm
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
result = n
if b == nil: return
finalCodegenActions(graph, BModule(b), n)
proc genForwardedProcs(g: BModuleList) = proc genForwardedProcs(g: BModuleList) =
# Forward declared proc:s lack bodies when first encountered, so they're given # Forward declared proc:s lack bodies when first encountered, so they're given
# a second pass here # a second pass here
@ -2175,5 +2164,3 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
m.writeModule(pending=true) m.writeModule(pending=true)
writeMapping(config, g.mapping) writeMapping(config, g.mapping)
if g.generatedHeader != nil: writeHeader(g.generatedHeader) if g.generatedHeader != nil: writeHeader(g.generatedHeader)
const cgenPass* = makePass(myOpen, myProcess, myClose)

View file

@ -9,7 +9,7 @@
# This module implements a dependency file generator. # This module implements a dependency file generator.
import options, ast, ropes, passes, pathutils, msgs, lineinfos import options, ast, ropes, pathutils, msgs, lineinfos
import modulegraphs import modulegraphs
@ -79,7 +79,7 @@ proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym)) let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym))
addDependencyAux(b, parent, child) addDependencyAux(b, parent, child)
proc addDotDependency(c: PPassContext, n: PNode): PNode = proc addDotDependency*(c: PPassContext, n: PNode): PNode =
result = n result = n
let g = PGen(c) let g = PGen(c)
let b = Backend(g.graph.backend) let b = Backend(g.graph.backend)
@ -100,7 +100,7 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
rope(project.splitFile.name), b.dotGraph], rope(project.splitFile.name), b.dotGraph],
changeFileExt(project, "dot")) changeFileExt(project, "dot"))
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
var g: PGen var g: PGen
new(g) new(g)
g.module = module g.module = module
@ -109,6 +109,3 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext
if graph.backend == nil: if graph.backend == nil:
graph.backend = Backend(dotGraph: "") graph.backend = Backend(dotGraph: "")
result = g result = g
const gendependPass* = makePass(open = myOpen, process = addDotDependency)

View file

@ -11,7 +11,7 @@
# semantic checking. # semantic checking.
import import
options, ast, msgs, passes, docgen, lineinfos, pathutils, packages options, ast, msgs, docgen, lineinfos, pathutils, packages
from modulegraphs import ModuleGraph, PPassContext from modulegraphs import ModuleGraph, PPassContext
@ -38,21 +38,21 @@ template closeImpl(body: untyped) {.dirty.} =
except IOError: except IOError:
discard discard
proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
closeImpl: closeImpl:
writeOutput(g.doc, useWarning, groupedToc) writeOutput(g.doc, useWarning, groupedToc)
proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
closeImpl: closeImpl:
writeOutputJson(g.doc, useWarning) writeOutputJson(g.doc, useWarning)
proc processNode(c: PPassContext, n: PNode): PNode = proc processNode*(c: PPassContext, n: PNode): PNode =
result = n result = n
var g = PGen(c) var g = PGen(c)
if shouldProcess(g): if shouldProcess(g):
generateDoc(g.doc, n, n, g.config) generateDoc(g.doc, n, n, g.config)
proc processNodeJson(c: PPassContext, n: PNode): PNode = proc processNodeJson*(c: PPassContext, n: PNode): PNode =
result = n result = n
var g = PGen(c) var g = PGen(c)
if shouldProcess(g): if shouldProcess(g):
@ -68,20 +68,11 @@ template myOpenImpl(ext: untyped) {.dirty.} =
g.doc = d g.doc = d
result = g result = g
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
myOpenImpl(HtmlExt) myOpenImpl(HtmlExt)
proc myOpenTex(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
myOpenImpl(TexExt) myOpenImpl(TexExt)
proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
myOpenImpl(JsonExt) myOpenImpl(JsonExt)
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
const docgen2TexPass* = makePass(open = myOpenTex, process = processNode,
close = close)
const docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson,
close = closeJson)
proc finishDoc2Pass*(project: string) =
discard

View file

@ -31,10 +31,12 @@ implements the required case distinction.
import import
ast, trees, magicsys, options, ast, trees, magicsys, options,
nversion, msgs, idents, types, nversion, msgs, idents, types,
ropes, passes, ccgutils, wordrecg, renderer, ropes, ccgutils, wordrecg, renderer,
cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
transf, injectdestructors, sourcemap, astmsgs, backendpragmas transf, injectdestructors, sourcemap, astmsgs, backendpragmas
import pipelineutils
import json, sets, math, tables, intsets import json, sets, math, tables, intsets
import strutils except addf import strutils except addf
@ -2830,11 +2832,11 @@ proc genModule(p: PProc, n: PNode) =
if optStackTrace in p.options: if optStackTrace in p.options:
p.body.add(frameDestroy(p)) p.body.add(frameDestroy(p))
proc myProcess(b: PPassContext, n: PNode): PNode = proc processJSCodeGen*(b: PPassContext, n: PNode): PNode =
## Generate JS code for a node. ## Generate JS code for a node.
result = n result = n
let m = BModule(b) let m = BModule(b)
if passes.skipCodegen(m.config, n): return n if pipelineutils.skipCodegen(m.config, n): return n
if m.module == nil: internalError(m.config, n.info, "myProcess") if m.module == nil: internalError(m.config, n.info, "myProcess")
let globals = PGlobals(m.graph.backend) let globals = PGlobals(m.graph.backend)
var p = newInitProc(globals, m) var p = newInitProc(globals, m)
@ -2869,7 +2871,7 @@ proc getClassName(t: PType): Rope =
if s.loc.r != "": result = s.loc.r if s.loc.r != "": result = s.loc.r
else: result = rope(s.name.s) else: result = rope(s.name.s)
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = proc finalJSCodeGen*(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
## Finalize JS code generation of a Nim module. ## Finalize JS code generation of a Nim module.
## Param `n` may contain nodes returned from the last module close call. ## Param `n` may contain nodes returned from the last module close call.
var m = BModule(b) var m = BModule(b)
@ -2879,14 +2881,14 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
for i in countdown(high(graph.globalDestructors), 0): for i in countdown(high(graph.globalDestructors), 0):
n.add graph.globalDestructors[i] n.add graph.globalDestructors[i]
# Process any nodes left over from the last call to `myClose`. # Process any nodes left over from the last call to `myClose`.
result = myProcess(b, n) result = processJSCodeGen(b, n)
# Some codegen is different (such as no stacktraces; see `initProcOptions`) # Some codegen is different (such as no stacktraces; see `initProcOptions`)
# when `std/system` is being processed. # when `std/system` is being processed.
if sfSystemModule in m.module.flags: if sfSystemModule in m.module.flags:
PGlobals(graph.backend).inSystem = false PGlobals(graph.backend).inSystem = false
# Check if codegen should continue before any files are generated. # Check if codegen should continue before any files are generated.
# It may bail early is if too many errors have been raised. # It may bail early is if too many errors have been raised.
if passes.skipCodegen(m.config, n): return n if pipelineutils.skipCodegen(m.config, n): return n
# Nim modules are compiled into a single JS file. # Nim modules are compiled into a single JS file.
# If this is the main module, then this is the final call to `myClose`. # If this is the main module, then this is the final call to `myClose`.
if sfMainModule in m.module.flags: if sfMainModule in m.module.flags:
@ -2904,9 +2906,6 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
if not writeRope(code, outFile): if not writeRope(code, outFile):
rawMessage(m.config, errCannotOpenFile, outFile.string) rawMessage(m.config, errCannotOpenFile, outFile.string)
proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = proc setupJSgen*(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
## Create the JS backend pass context `BModule` for a Nim module.
result = newModule(graph, s) result = newModule(graph, s)
result.idgen = idgen result.idgen = idgen
const JSgenPass* = makePass(myOpen, myProcess, myClose)

View file

@ -16,9 +16,9 @@ import
std/[strutils, os, times, tables, sha1, with, json], std/[strutils, os, times, tables, sha1, with, json],
llstream, ast, lexer, syntaxes, options, msgs, llstream, ast, lexer, syntaxes, options, msgs,
condsyms, condsyms,
sem, idents, passes, extccomp, idents, extccomp,
cgen, nversion, cgen, nversion,
platform, nimconf, passaux, depends, vm, platform, nimconf, depends,
modules, modules,
modulegraphs, lineinfos, pathutils, vmprofiler modulegraphs, lineinfos, pathutils, vmprofiler
@ -29,12 +29,10 @@ when defined(nimPreviewSlimSystem):
import ic / [cbackend, integrity, navigator] import ic / [cbackend, integrity, navigator]
from ic / ic import rodViewer from ic / ic import rodViewer
when not defined(leanCompiler): import pipelines
import jsgen, docgen, docgen2
proc semanticPasses(g: ModuleGraph) = when not defined(leanCompiler):
registerPass g, verbosePass import docgen
registerPass g, semPass
proc writeDepsFile(g: ModuleGraph) = proc writeDepsFile(g: ModuleGraph) =
let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps") let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
@ -68,9 +66,8 @@ proc writeCMakeDepsFile(conf: ConfigRef) =
fl.close() fl.close()
proc commandGenDepend(graph: ModuleGraph) = proc commandGenDepend(graph: ModuleGraph) =
semanticPasses(graph) setPipeLinePass(graph, GenDependPass)
registerPass(graph, gendependPass) compilePipelineProject(graph)
compileProject(graph)
let project = graph.config.projectFull let project = graph.config.projectFull
writeDepsFile(graph) writeDepsFile(graph)
generateDot(graph, project) generateDot(graph, project)
@ -87,8 +84,8 @@ proc commandCheck(graph: ModuleGraph) =
defineSymbol(conf.symbols, "nimconfig") defineSymbol(conf.symbols, "nimconfig")
elif conf.backend == backendJs: elif conf.backend == backendJs:
setTarget(conf.target, osJS, cpuJS) setTarget(conf.target, osJS, cpuJS)
semanticPasses(graph) # use an empty backend for semantic checking only setPipeLinePass(graph, SemPass)
compileProject(graph) compilePipelineProject(graph)
if conf.symbolFiles != disabledSf: if conf.symbolFiles != disabledSf:
case conf.ideCmd case conf.ideCmd
@ -102,22 +99,20 @@ when not defined(leanCompiler):
proc commandDoc2(graph: ModuleGraph; ext: string) = proc commandDoc2(graph: ModuleGraph; ext: string) =
handleDocOutputOptions graph.config handleDocOutputOptions graph.config
graph.config.setErrorMaxHighMaybe graph.config.setErrorMaxHighMaybe
semanticPasses(graph)
case ext: case ext:
of TexExt: registerPass(graph, docgen2TexPass) of TexExt:
of JsonExt: registerPass(graph, docgen2JsonPass) setPipeLinePass(graph, Docgen2TexPass)
of HtmlExt: registerPass(graph, docgen2Pass) of JsonExt:
setPipeLinePass(graph, Docgen2JsonPass)
of HtmlExt:
setPipeLinePass(graph, Docgen2Pass)
else: doAssert false, $ext else: doAssert false, $ext
compileProject(graph) compilePipelineProject(graph)
finishDoc2Pass(graph.config.projectName)
proc commandCompileToC(graph: ModuleGraph) = proc commandCompileToC(graph: ModuleGraph) =
let conf = graph.config let conf = graph.config
extccomp.initVars(conf) extccomp.initVars(conf)
semanticPasses(graph)
if conf.symbolFiles == disabledSf: if conf.symbolFiles == disabledSf:
registerPass(graph, cgenPass)
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"): if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile): if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
# nothing changed # nothing changed
@ -127,7 +122,11 @@ proc commandCompileToC(graph: ModuleGraph) =
if not extccomp.ccHasSaneOverflow(conf): if not extccomp.ccHasSaneOverflow(conf):
conf.symbols.defineSymbol("nimEmulateOverflowChecks") conf.symbols.defineSymbol("nimEmulateOverflowChecks")
compileProject(graph) if conf.symbolFiles == disabledSf:
setPipeLinePass(graph, CgenPass)
else:
setPipeLinePass(graph, SemPass)
compilePipelineProject(graph)
if graph.config.errorCounter > 0: if graph.config.errorCounter > 0:
return # issue #9933 return # issue #9933
if conf.symbolFiles == disabledSf: if conf.symbolFiles == disabledSf:
@ -160,33 +159,27 @@ proc commandCompileToJS(graph: ModuleGraph) =
conf.exc = excCpp conf.exc = excCpp
setTarget(conf.target, osJS, cpuJS) setTarget(conf.target, osJS, cpuJS)
defineSymbol(conf.symbols, "ecmascript") # For backward compatibility defineSymbol(conf.symbols, "ecmascript") # For backward compatibility
semanticPasses(graph) setPipeLinePass(graph, JSgenPass)
registerPass(graph, JSgenPass) compilePipelineProject(graph)
compileProject(graph)
if optGenScript in conf.globalOptions: if optGenScript in conf.globalOptions:
writeDepsFile(graph) writeDepsFile(graph)
proc interactivePasses(graph: ModuleGraph) = proc commandInteractive(graph: ModuleGraph) =
graph.config.setErrorMaxHighMaybe
initDefines(graph.config.symbols) initDefines(graph.config.symbols)
defineSymbol(graph.config.symbols, "nimscript") defineSymbol(graph.config.symbols, "nimscript")
# note: seems redundant with -d:nimHasLibFFI # note: seems redundant with -d:nimHasLibFFI
when hasFFI: defineSymbol(graph.config.symbols, "nimffi") when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
registerPass(graph, verbosePass) setPipeLinePass(graph, InterpreterPass)
registerPass(graph, semPass) compilePipelineSystemModule(graph)
registerPass(graph, evalPass)
proc commandInteractive(graph: ModuleGraph) =
graph.config.setErrorMaxHighMaybe
interactivePasses(graph)
compileSystemModule(graph)
if graph.config.commandArgs.len > 0: if graph.config.commandArgs.len > 0:
discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
else: else:
var m = graph.makeStdinModule() var m = graph.makeStdinModule()
incl(m.flags, sfMainModule) incl(m.flags, sfMainModule)
var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config)) let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
processModule(graph, m, idgen, s) discard processPipelineModule(graph, m, idgen, s)
proc commandScan(cache: IdentCache, config: ConfigRef) = proc commandScan(cache: IdentCache, config: ConfigRef) =
var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt) var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
@ -241,8 +234,6 @@ proc mainCommand*(graph: ModuleGraph) =
let conf = graph.config let conf = graph.config
let cache = graph.cache let cache = graph.cache
# In "nim serve" scenario, each command must reset the registered passes
clearPasses(graph)
conf.lastCmdTime = epochTime() conf.lastCmdTime = epochTime()
conf.searchPaths.add(conf.libpath) conf.searchPaths.add(conf.libpath)

View file

@ -57,6 +57,18 @@ type
sym*: PSym sym*: PSym
info*: TLineInfo info*: TLineInfo
PipelinePass* = enum
NonePass
SemPass
JSgenPass
CgenPass
EvalPass
InterpreterPass
GenDependPass
Docgen2TexPass
Docgen2JsonPass
Docgen2Pass
ModuleGraph* {.acyclic.} = ref object ModuleGraph* {.acyclic.} = ref object
ifaces*: seq[Iface] ## indexed by int32 fileIdx ifaces*: seq[Iface] ## indexed by int32 fileIdx
packed*: PackedModuleGraph packed*: PackedModuleGraph
@ -104,6 +116,7 @@ type
cacheCounters*: Table[string, BiggestInt] # IC: implemented cacheCounters*: Table[string, BiggestInt] # IC: implemented
cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented
passes*: seq[TPass] passes*: seq[TPass]
pipelinePass*: PipelinePass
onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}

View file

@ -11,13 +11,8 @@
import import
ast, magicsys, msgs, options, ast, magicsys, msgs, options,
idents, lexer, passes, syntaxes, llstream, modulegraphs, idents, lexer, syntaxes, modulegraphs,
lineinfos, pathutils, tables, packages lineinfos, pathutils
when defined(nimPreviewSlimSystem):
import std/[syncio, assertions]
import ic / replayer
proc resetSystemArtifacts*(g: ModuleGraph) = proc resetSystemArtifacts*(g: ModuleGraph) =
magicsys.resetSysTypes(g) magicsys.resetSysTypes(g)
@ -25,12 +20,12 @@ proc resetSystemArtifacts*(g: ModuleGraph) =
template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent = template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
getIdent(graph.cache, splitFile(filename).name) getIdent(graph.cache, splitFile(filename).name)
proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
let packSym = getPackage(graph, fileIdx) let packSym = getPackage(graph, fileIdx)
result.owner = packSym result.owner = packSym
result.position = int fileIdx result.position = int fileIdx
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
let filename = AbsoluteFile toFullPath(graph.config, fileIdx) let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
# We cannot call ``newSym`` here, because we have to circumvent the ID # We cannot call ``newSym`` here, because we have to circumvent the ID
# mechanism, which we do in order to assign each module a persistent ID. # mechanism, which we do in order to assign each module a persistent ID.
@ -43,99 +38,16 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
partialInitModule(result, graph, fileIdx, filename) partialInitModule(result, graph, fileIdx, filename)
graph.registerModule(result) graph.registerModule(result)
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
var flags = flags
if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
result = graph.getModule(fileIdx)
template processModuleAux(moduleStatus) =
onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
var s: PLLStream
if sfMainModule in flags:
if graph.config.projectIsStdin: s = stdin.llStreamOpen
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
discard processModule(graph, result, idGeneratorFromModule(result), s)
if result == nil:
var cachedModules: seq[FileIndex]
result = moduleFromRodFile(graph, fileIdx, cachedModules)
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
if result == nil:
result = newModule(graph, fileIdx)
result.flags.incl flags
registerModule(graph, result)
processModuleAux("import")
else:
if sfSystemModule in flags:
graph.systemModule = result
partialInitModule(result, graph, fileIdx, filename)
for m in cachedModules:
registerModuleById(graph, m)
replayStateChanges(graph.packed[m.int].module, graph)
replayGenericCacheInformation(graph, m.int)
elif graph.isDirty(result):
result.flags.excl sfDirty
# reset module fields:
initStrTables(graph, result)
result.ast = nil
processModuleAux("import(dirty)")
graph.markClientsDirty(fileIdx)
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
# this is called by the semantic checking phase
assert graph.config != nil
result = compileModule(graph, fileIdx, {}, s)
graph.addDep(s, fileIdx)
# keep track of import relationships
if graph.config.hcrOn:
graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
#if sfSystemModule in result.flags:
# localError(result.info, errAttemptToRedefine, result.name.s)
# restore the notes for outer module:
graph.config.notes =
if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
else: graph.config.foreignPackageNotes
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode = proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
result = syntaxes.parseFile(fileIdx, graph.cache, graph.config) result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
graph.addDep(s, fileIdx) graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.FileIndex, fileIdx) graph.addIncludeDep(s.position.FileIndex, fileIdx)
proc connectCallbacks*(graph: ModuleGraph) =
graph.includeFileCallback = includeModule
graph.importModuleCallback = importModule
proc compileSystemModule*(graph: ModuleGraph) =
if graph.systemModule == nil:
connectCallbacks(graph)
graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
graph.config.libpath / RelativeFile"system.nim")
discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
proc wantMainModule*(conf: ConfigRef) = proc wantMainModule*(conf: ConfigRef) =
if conf.projectFull.isEmpty: if conf.projectFull.isEmpty:
fatal(conf, gCmdLineInfo, "command expects a filename") fatal(conf, gCmdLineInfo, "command expects a filename")
conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
connectCallbacks(graph)
let conf = graph.config
wantMainModule(conf)
configComplete(graph)
let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
conf.projectMainIdx2 = projectFile
let packSym = getPackage(graph, projectFile)
graph.config.mainPackageId = packSym.getPackageId
graph.importStack.add projectFile
if projectFile == systemFileIdx:
discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
else:
graph.compileSystemModule()
discard graph.compileModule(projectFile, {sfMainModule})
proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym = proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
result = graph.newModule(fileInfoIdx(graph.config, filename)) result = graph.newModule(fileInfoIdx(graph.config, filename))
registerModule(graph, result) registerModule(graph, result)

View file

@ -9,10 +9,16 @@
## exposes the Nim VM to clients. ## exposes the Nim VM to clients.
import import
ast, astalgo, modules, passes, condsyms, ast, modules, condsyms,
options, sem, llstream, lineinfos, vm, options, llstream, lineinfos, vm,
vmdef, modulegraphs, idents, os, pathutils, vmdef, modulegraphs, idents, os, pathutils,
passaux, scriptconfig, std/compilesettings scriptconfig, std/compilesettings
import pipelines
when defined(nimPreviewSlimSystem):
import std/[assertions, syncio]
type type
Interpreter* = ref object ## Use Nim as an interpreter with this object Interpreter* = ref object ## Use Nim as an interpreter with this object
@ -76,7 +82,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
let s = if scriptStream != nil: scriptStream let s = if scriptStream != nil: scriptStream
else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead) else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
processModule(i.graph, i.mainModule, i.idgen, s) discard processPipelineModule(i.graph, i.mainModule, i.idgen, s)
proc findNimStdLib*(): string = proc findNimStdLib*(): string =
## Tries to find a path to a valid "system.nim" file. ## Tries to find a path to a valid "system.nim" file.
@ -109,12 +115,10 @@ proc createInterpreter*(scriptName: string;
var conf = newConfigRef() var conf = newConfigRef()
var cache = newIdentCache() var cache = newIdentCache()
var graph = newModuleGraph(cache, conf) var graph = newModuleGraph(cache, conf)
connectCallbacks(graph) connectPipelineCallbacks(graph)
initDefines(conf.symbols) initDefines(conf.symbols)
for define in defines: for define in defines:
defineSymbol(conf.symbols, define[0], define[1]) defineSymbol(conf.symbols, define[0], define[1])
registerPass(graph, semPass)
registerPass(graph, evalPass)
for p in searchPaths: for p in searchPaths:
conf.searchPaths.add(AbsoluteDir p) conf.searchPaths.add(AbsoluteDir p)
@ -129,7 +133,8 @@ proc createInterpreter*(scriptName: string;
if registerOps: if registerOps:
vm.registerAdditionalOps() # Required to register parts of stdlib modules vm.registerAdditionalOps() # Required to register parts of stdlib modules
graph.vm = vm graph.vm = vm
graph.compileSystemModule() setPipeLinePass(graph, EvalPass)
graph.compilePipelineSystemModule()
result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName, idgen: idgen) result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName, idgen: idgen)
proc destroyInterpreter*(i: Interpreter) = proc destroyInterpreter*(i: Interpreter) =
@ -159,13 +164,11 @@ proc runRepl*(r: TLLRepl;
defineSymbol(conf.symbols, "nimscript") defineSymbol(conf.symbols, "nimscript")
if supportNimscript: defineSymbol(conf.symbols, "nimconfig") if supportNimscript: defineSymbol(conf.symbols, "nimconfig")
when hasFFI: defineSymbol(graph.config.symbols, "nimffi") when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
registerPass(graph, verbosePass)
registerPass(graph, semPass)
registerPass(graph, evalPass)
var m = graph.makeStdinModule() var m = graph.makeStdinModule()
incl(m.flags, sfMainModule) incl(m.flags, sfMainModule)
var idgen = idGeneratorFromModule(m) var idgen = idGeneratorFromModule(m)
if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen) if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen)
graph.compileSystemModule() setPipeLinePass(graph, InterpreterPass)
processModule(graph, m, idgen, llStreamOpenStdIn(r)) graph.compilePipelineSystemModule()
discard processPipelineModule(graph, m, idgen, llStreamOpenStdIn(r))

View file

@ -14,13 +14,22 @@ import
options, ast, llstream, msgs, options, ast, llstream, msgs,
idents, idents,
syntaxes, modulegraphs, reorder, syntaxes, modulegraphs, reorder,
lineinfos, pathutils, packages lineinfos,
pipelineutils,
modules, pathutils, packages,
sem, semdata
import ic/replayer
export skipCodegen, resolveMod, prepareConfigNotes
when defined(nimsuggest): when defined(nimsuggest):
import std/sha1 import std/sha1
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/syncio import std/[syncio, assertions]
import std/tables
type type
TPassData* = tuple[input: PNode, closeOutput: PNode] TPassData* = tuple[input: PNode, closeOutput: PNode]
@ -38,12 +47,6 @@ proc makePass*(open: TPassOpen = nil,
result.process = process result.process = process
result.isFrontend = isFrontend result.isFrontend = isFrontend
proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do
# something with `n`. Currently, this ignores `n` and uses the global
# error count instead.
result = config.errorCounter > 0
const const
maxPasses = 10 maxPasses = 10
@ -80,13 +83,6 @@ proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray)
if isNil(m): return false if isNil(m): return false
result = true result = true
proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
let fullPath = findModule(conf, module, relativeTo)
if fullPath.isEmpty:
result = InvalidFileIdx
else:
result = fileInfoIdx(conf, fullPath)
proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind, proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
a: var TPassContextArray; m: PSym) = a: var TPassContextArray; m: PSym) =
# XXX fixme this should actually be relative to the config file! # XXX fixme this should actually be relative to the config file!
@ -100,23 +96,6 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod
importStmt.add str importStmt.add str
if not processTopLevelStmt(graph, importStmt, a): break if not processTopLevelStmt(graph, importStmt, a): break
const
imperativeCode = {low(TNodeKind)..high(TNodeKind)} - {nkTemplateDef, nkProcDef, nkMethodDef,
nkMacroDef, nkConverterDef, nkIteratorDef, nkFuncDef, nkPragma,
nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt}
proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
# don't be verbose unless the module belongs to the main package:
if graph.config.belongsToProjectPackage(module):
graph.config.notes = graph.config.mainPackageNotes
else:
if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
graph.config.notes = graph.config.foreignPackageNotes
proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
result = true
#module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
stream: PLLStream): bool {.discardable.} = stream: PLLStream): bool {.discardable.} =
if graph.stopCompile(): return true if graph.stopCompile(): return true
@ -153,42 +132,22 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module
checkFirstLineIndentation(p) checkFirstLineIndentation(p)
while true: block processCode:
if graph.stopCompile(): break if graph.stopCompile(): break processCode
var n = parseTopLevelStmt(p) # todo merge it var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break if n.kind == nkEmpty: break processCode
if true: # read everything, no streaming possible
# read everything, no streaming possible var sl = newNodeI(nkStmtList, n.info)
var sl = newNodeI(nkStmtList, n.info) sl.add n
while true:
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break
sl.add n sl.add n
while true: if sfReorder in module.flags or codeReordering in graph.config.features:
var n = parseTopLevelStmt(p) sl = reorder(graph, sl, module)
if n.kind == nkEmpty: break discard processTopLevelStmt(graph, sl, a)
sl.add n
if sfReorder in module.flags or codeReordering in graph.config.features:
sl = reorder(graph, sl, module)
discard processTopLevelStmt(graph, sl, a)
break
elif n.kind in imperativeCode:
# read everything until the next proc declaration etc.
var sl = newNodeI(nkStmtList, n.info)
sl.add n
var rest: PNode = nil
while true:
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty or n.kind notin imperativeCode:
rest = n
break
sl.add n
#echo "-----\n", sl
if not processTopLevelStmt(graph, sl, a): break
if rest != nil:
#echo "-----\n", rest
if not processTopLevelStmt(graph, rest, a): break
else:
#echo "----- single\n", n
if not processTopLevelStmt(graph, n, a): break
closeParser(p) closeParser(p)
if s.kind != llsStdIn: break if s.kind != llsStdIn: break
closePasses(graph, a) closePasses(graph, a)
@ -198,3 +157,99 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
# They are responsible for closing the rod files. See `cbackend.nim`. # They are responsible for closing the rod files. See `cbackend.nim`.
closeRodFile(graph, module) closeRodFile(graph, module)
result = true result = true
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
var flags = flags
if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
result = graph.getModule(fileIdx)
template processModuleAux(moduleStatus) =
onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
var s: PLLStream
if sfMainModule in flags:
if graph.config.projectIsStdin: s = stdin.llStreamOpen
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
discard processModule(graph, result, idGeneratorFromModule(result), s)
if result == nil:
var cachedModules: seq[FileIndex]
result = moduleFromRodFile(graph, fileIdx, cachedModules)
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
if result == nil:
result = newModule(graph, fileIdx)
result.flags.incl flags
registerModule(graph, result)
processModuleAux("import")
else:
if sfSystemModule in flags:
graph.systemModule = result
partialInitModule(result, graph, fileIdx, filename)
for m in cachedModules:
registerModuleById(graph, m)
replayStateChanges(graph.packed[m.int].module, graph)
replayGenericCacheInformation(graph, m.int)
elif graph.isDirty(result):
result.flags.excl sfDirty
# reset module fields:
initStrTables(graph, result)
result.ast = nil
processModuleAux("import(dirty)")
graph.markClientsDirty(fileIdx)
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
# this is called by the semantic checking phase
assert graph.config != nil
result = compileModule(graph, fileIdx, {}, s)
graph.addDep(s, fileIdx)
# keep track of import relationships
if graph.config.hcrOn:
graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
#if sfSystemModule in result.flags:
# localError(result.info, errAttemptToRedefine, result.name.s)
# restore the notes for outer module:
graph.config.notes =
if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
else: graph.config.foreignPackageNotes
proc connectCallbacks*(graph: ModuleGraph) =
graph.includeFileCallback = modules.includeModule
graph.importModuleCallback = importModule
proc compileSystemModule*(graph: ModuleGraph) =
if graph.systemModule == nil:
connectCallbacks(graph)
graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
graph.config.libpath / RelativeFile"system.nim")
discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
connectCallbacks(graph)
let conf = graph.config
wantMainModule(conf)
configComplete(graph)
let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
conf.projectMainIdx2 = projectFile
let packSym = getPackage(graph, projectFile)
graph.config.mainPackageId = packSym.getPackageId
graph.importStack.add projectFile
if projectFile == systemFileIdx:
discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
else:
graph.compileSystemModule()
discard graph.compileModule(projectFile, {sfMainModule})
proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
result = preparePContext(graph, module, idgen)
proc mySemClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
var c = PContext(context)
closePContext(graph, c, n)
proc mySemProcess(context: PPassContext, n: PNode): PNode {.nosinks.} =
result = semWithPContext(PContext(context), n)
const semPass* = makePass(mySemOpen, mySemProcess, mySemClose,
isFrontend = true)

254
compiler/pipelines.nim Normal file
View file

@ -0,0 +1,254 @@
import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
packages, syntaxes, depends, vm
import pipelineutils
when not defined(leanCompiler):
import jsgen, docgen2
import std/[syncio, objectdollar, assertions, tables]
import renderer
import ic/replayer
proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
graph.pipelinePass = pass
proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): PNode =
case graph.pipelinePass
of CgenPass:
result = semNode
if bModule != nil:
genTopLevelStmt(BModule(bModule), result)
of JSgenPass:
when not defined(leanCompiler):
result = processJSCodeGen(bModule, semNode)
of GenDependPass:
result = addDotDependency(bModule, semNode)
of SemPass:
result = graph.emptyNode
of Docgen2Pass, Docgen2TexPass:
when not defined(leanCompiler):
result = processNode(bModule, semNode)
of Docgen2JsonPass:
when not defined(leanCompiler):
result = processNodeJson(bModule, semNode)
of EvalPass, InterpreterPass:
result = interpreterCode(bModule, semNode)
of NonePass:
doAssert false, "use setPipeLinePass to set a proper PipelinePass"
proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
) =
# XXX fixme this should actually be relative to the config file!
let relativeTo = toFullPath(graph.config, m.info)
for module in items(implicits):
# implicit imports should not lead to a module importing itself
if m.position != resolveMod(graph.config, module, relativeTo).int32:
var importStmt = newNodeI(nodeKind, m.info)
var str = newStrNode(nkStrLit, module)
str.info = m.info
importStmt.add str
message(graph.config, importStmt.info, hintProcessingStmt, $idgen[])
let semNode = semWithPContext(ctx, importStmt)
if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
break
proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
stream: PLLStream): bool =
if graph.stopCompile(): return true
var
p: Parser
s: PLLStream
fileIdx = module.fileIdx
prepareConfigNotes(graph, module)
let ctx = preparePContext(graph, module, idgen)
let bModule: PPassContext =
case graph.pipelinePass
of CgenPass:
setupCgen(graph, module, idgen)
of JSgenPass:
when not defined(leanCompiler):
setupJSgen(graph, module, idgen)
else:
nil
of EvalPass, InterpreterPass:
setupEvalGen(graph, module, idgen)
of GenDependPass:
setupDependPass(graph, module, idgen)
of Docgen2Pass:
when not defined(leanCompiler):
openHtml(graph, module, idgen)
else:
nil
of Docgen2TexPass:
when not defined(leanCompiler):
openTex(graph, module, idgen)
else:
nil
of Docgen2JsonPass:
when not defined(leanCompiler):
openJson(graph, module, idgen)
else:
nil
of SemPass:
nil
of NonePass:
doAssert false, "use setPipeLinePass to set a proper PipelinePass"
nil
if stream == nil:
let filename = toFullPathConsiderDirty(graph.config, fileIdx)
s = llStreamOpen(filename, fmRead)
if s == nil:
rawMessage(graph.config, errCannotOpenFile, filename.string)
return false
else:
s = stream
while true:
syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config)
if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"):
# XXX what about caching? no processing then? what if I change the
# modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only
# for the interactive mode.
if module.name.s != "nimscriptapi":
processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen
processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen
checkFirstLineIndentation(p)
block processCode:
if graph.stopCompile(): break processCode
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break processCode
# read everything, no streaming possible
var sl = newNodeI(nkStmtList, n.info)
sl.add n
while true:
var n = parseTopLevelStmt(p)
if n.kind == nkEmpty: break
sl.add n
if sfReorder in module.flags or codeReordering in graph.config.features:
sl = reorder(graph, sl, module)
if graph.pipelinePass != EvalPass:
message(graph.config, sl.info, hintProcessingStmt, $idgen[])
var semNode = semWithPContext(ctx, sl)
discard processPipeline(graph, semNode, bModule)
closeParser(p)
if s.kind != llsStdIn: break
let finalNode = closePContext(graph, ctx, nil)
case graph.pipelinePass
of CgenPass:
if bModule != nil:
finalCodegenActions(graph, BModule(bModule), finalNode)
of JSgenPass:
when not defined(leanCompiler):
discard finalJSCodeGen(graph, bModule, finalNode)
of EvalPass, InterpreterPass:
discard interpreterCode(bModule, finalNode)
of SemPass, GenDependPass:
discard
of Docgen2Pass, Docgen2TexPass:
when not defined(leanCompiler):
discard closeDoc(graph, bModule, finalNode)
of Docgen2JsonPass:
when not defined(leanCompiler):
discard closeJson(graph, bModule, finalNode)
of NonePass:
doAssert false, "use setPipeLinePass to set a proper PipelinePass"
if graph.config.backend notin {backendC, backendCpp, backendObjc}:
# We only write rod files here if no C-like backend is active.
# The C-like backends have been patched to support the IC mechanism.
# They are responsible for closing the rod files. See `cbackend.nim`.
closeRodFile(graph, module)
result = true
proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
var flags = flags
if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
result = graph.getModule(fileIdx)
template processModuleAux(moduleStatus) =
onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
var s: PLLStream
if sfMainModule in flags:
if graph.config.projectIsStdin: s = stdin.llStreamOpen
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
discard processPipelineModule(graph, result, idGeneratorFromModule(result), s)
if result == nil:
var cachedModules: seq[FileIndex]
result = moduleFromRodFile(graph, fileIdx, cachedModules)
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
if result == nil:
result = newModule(graph, fileIdx)
result.flags.incl flags
registerModule(graph, result)
processModuleAux("import")
else:
if sfSystemModule in flags:
graph.systemModule = result
partialInitModule(result, graph, fileIdx, filename)
for m in cachedModules:
registerModuleById(graph, m)
replayStateChanges(graph.packed[m.int].module, graph)
replayGenericCacheInformation(graph, m.int)
elif graph.isDirty(result):
result.flags.excl sfDirty
# reset module fields:
initStrTables(graph, result)
result.ast = nil
processModuleAux("import(dirty)")
graph.markClientsDirty(fileIdx)
proc importPipelineModule(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
# this is called by the semantic checking phase
assert graph.config != nil
result = compilePipelineModule(graph, fileIdx, {}, s)
graph.addDep(s, fileIdx)
# keep track of import relationships
if graph.config.hcrOn:
graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
#if sfSystemModule in result.flags:
# localError(result.info, errAttemptToRedefine, result.name.s)
# restore the notes for outer module:
graph.config.notes =
if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
else: graph.config.foreignPackageNotes
proc connectPipelineCallbacks*(graph: ModuleGraph) =
graph.includeFileCallback = modules.includeModule
graph.importModuleCallback = importPipelineModule
proc compilePipelineSystemModule*(graph: ModuleGraph) =
if graph.systemModule == nil:
connectPipelineCallbacks(graph)
graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
graph.config.libpath / RelativeFile"system.nim")
discard graph.compilePipelineModule(graph.config.m.systemFileIdx, {sfSystemModule})
proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
connectPipelineCallbacks(graph)
let conf = graph.config
wantMainModule(conf)
configComplete(graph)
let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
conf.projectMainIdx2 = projectFile
let packSym = getPackage(graph, projectFile)
graph.config.mainPackageId = packSym.getPackageId
graph.importStack.add projectFile
if projectFile == systemFileIdx:
discard graph.compilePipelineModule(projectFile, {sfMainModule, sfSystemModule})
else:
graph.compilePipelineSystemModule()
discard graph.compilePipelineModule(projectFile, {sfMainModule})

View file

@ -0,0 +1,26 @@
import ast, options, lineinfos, pathutils, msgs, modulegraphs, packages
proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do
# something with `n`. Currently, this ignores `n` and uses the global
# error count instead.
result = config.errorCounter > 0
proc resolveMod*(conf: ConfigRef; module, relativeTo: string): FileIndex =
let fullPath = findModule(conf, module, relativeTo)
if fullPath.isEmpty:
result = InvalidFileIdx
else:
result = fileInfoIdx(conf, fullPath)
proc prepareConfigNotes*(graph: ModuleGraph; module: PSym) =
# don't be verbose unless the module belongs to the main package:
if graph.config.belongsToProjectPackage(module):
graph.config.notes = graph.config.mainPackageNotes
else:
if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
graph.config.notes = graph.config.foreignPackageNotes
proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
result = true
#module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")

View file

@ -11,10 +11,10 @@
## language. ## language.
import import
ast, modules, idents, passes, condsyms, ast, modules, idents, condsyms,
options, sem, llstream, vm, vmdef, commands, options, llstream, vm, vmdef, commands,
os, times, osproc, wordrecg, strtabs, modulegraphs, os, times, osproc, wordrecg, strtabs, modulegraphs,
pathutils pathutils, pipelines
when defined(nimPreviewSlimSystem): when defined(nimPreviewSlimSystem):
import std/syncio import std/syncio
@ -197,13 +197,11 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
conf.symbolFiles = disabledSf conf.symbolFiles = disabledSf
let graph = newModuleGraph(cache, conf) let graph = newModuleGraph(cache, conf)
connectCallbacks(graph) connectPipelineCallbacks(graph)
if freshDefines: initDefines(conf.symbols) if freshDefines: initDefines(conf.symbols)
defineSymbol(conf.symbols, "nimscript") defineSymbol(conf.symbols, "nimscript")
defineSymbol(conf.symbols, "nimconfig") defineSymbol(conf.symbols, "nimconfig")
registerPass(graph, semPass)
registerPass(graph, evalPass)
conf.searchPaths.add(conf.libpath) conf.searchPaths.add(conf.libpath)
@ -218,8 +216,9 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
var vm = setupVM(m, cache, scriptName.string, graph, idgen) var vm = setupVM(m, cache, scriptName.string, graph, idgen)
graph.vm = vm graph.vm = vm
graph.compileSystemModule() graph.setPipeLinePass(EvalPass)
discard graph.processModule(m, vm.idgen, stream) graph.compilePipelineSystemModule()
discard graph.processPipelineModule(m, vm.idgen, stream)
# watch out, "newruntime" can be set within NimScript itself and then we need # watch out, "newruntime" can be set within NimScript itself and then we need
# to remember this: # to remember this:

View file

@ -13,7 +13,7 @@ import
ast, strutils, options, astalgo, trees, ast, strutils, options, astalgo, trees,
wordrecg, ropes, msgs, idents, renderer, types, platform, math, wordrecg, ropes, msgs, idents, renderer, types, platform, math,
magicsys, nversion, nimsets, semfold, modulepaths, importer, magicsys, nversion, nimsets, semfold, modulepaths, importer,
procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch, procfind, lookups, pragmas, semdata, semtypinst, sigmatch,
intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
lowerings, plugins/active, lineinfos, strtabs, int128, lowerings, plugins/active, lineinfos, strtabs, int128,
@ -666,38 +666,37 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
n.add prc.ast n.add prc.ast
c.lastGenericIdx = c.generics.len c.lastGenericIdx = c.generics.len
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PContext {.nosinks.} =
var c = newContext(graph, module) result = newContext(graph, module)
c.idgen = idgen result.idgen = idgen
c.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil) result.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil)
c.voidType = newType(tyVoid, nextTypeId(idgen), nil) result.voidType = newType(tyVoid, nextTypeId(idgen), nil)
if c.p != nil: internalError(graph.config, module.info, "sem.myOpen") if result.p != nil: internalError(graph.config, module.info, "sem.preparePContext")
c.semConstExpr = semConstExpr result.semConstExpr = semConstExpr
c.semExpr = semExpr result.semExpr = semExpr
c.semTryExpr = tryExpr result.semTryExpr = tryExpr
c.semTryConstExpr = tryConstExpr result.semTryConstExpr = tryConstExpr
c.computeRequiresInit = computeRequiresInit result.computeRequiresInit = computeRequiresInit
c.semOperand = semOperand result.semOperand = semOperand
c.semConstBoolExpr = semConstBoolExpr result.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall result.semOverloadedCall = semOverloadedCall
c.semInferredLambda = semInferredLambda result.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance result.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode result.semTypeNode = semTypeNode
c.instTypeBoundOp = sigmatch.instTypeBoundOp result.instTypeBoundOp = sigmatch.instTypeBoundOp
c.hasUnresolvedArgs = hasUnresolvedArgs result.hasUnresolvedArgs = hasUnresolvedArgs
c.templInstCounter = new int result.templInstCounter = new int
pushProcCon(c, module) pushProcCon(result, module)
pushOwner(c, c.module) pushOwner(result, result.module)
c.moduleScope = openScope(c) result.moduleScope = openScope(result)
c.moduleScope.addSym(module) # a module knows itself result.moduleScope.addSym(module) # a module knows itself
if sfSystemModule in module.flags: if sfSystemModule in module.flags:
graph.systemModule = module graph.systemModule = module
c.topLevelScope = openScope(c) result.topLevelScope = openScope(result)
result = c
proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool = proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool =
if g.systemModule == nil: return false if g.systemModule == nil: return false
@ -771,8 +770,7 @@ proc recoverContext(c: PContext) =
while getCurrOwner(c).kind != skModule: popOwner(c) while getCurrOwner(c).kind != skModule: popOwner(c)
while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
proc myProcess(context: PPassContext, n: PNode): PNode {.nosinks.} = proc semWithPContext*(c: PContext, n: PNode): PNode {.nosinks.} =
var c = PContext(context)
# no need for an expensive 'try' if we stop after the first error anyway: # no need for an expensive 'try' if we stop after the first error anyway:
if c.config.errorMax <= 1: if c.config.errorMax <= 1:
result = semStmtAndGenerateGenerics(c, n) result = semStmtAndGenerateGenerics(c, n)
@ -793,13 +791,13 @@ proc myProcess(context: PPassContext, n: PNode): PNode {.nosinks.} =
#if c.config.cmd == cmdIdeTools: findSuggest(c, n) #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
storeRodNode(c, result) storeRodNode(c, result)
proc reportUnusedModules(c: PContext) = proc reportUnusedModules(c: PContext) =
for i in 0..high(c.unusedImports): for i in 0..high(c.unusedImports):
if sfUsed notin c.unusedImports[i][0].flags: if sfUsed notin c.unusedImports[i][0].flags:
message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s) message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
var c = PContext(context)
if c.config.cmd == cmdIdeTools and not c.suggestionsMade: if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
suggestSentinel(c) suggestSentinel(c)
closeScope(c) # close module's scope closeScope(c) # close module's scope
@ -814,6 +812,3 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
popOwner(c) popOwner(c)
popProcCon(c) popProcCon(c)
sealRodFile(c) sealRodFile(c)
const semPass* = makePass(myOpen, myProcess, myClose,
isFrontend = true)

View file

@ -36,7 +36,7 @@ import algorithm, sets, prefixmatches, parseutils, tables
from wordrecg import wDeprecated, wError, wAddr, wYield from wordrecg import wDeprecated, wError, wAddr, wYield
when defined(nimsuggest): when defined(nimsuggest):
import passes, tables, pathutils # importer import tables, pathutils # importer
const const
sep = '\t' sep = '\t'

View file

@ -13,7 +13,7 @@
import semmacrosanity import semmacrosanity
import import
std/[strutils, tables, parseutils], std/[strutils, tables, parseutils],
msgs, vmdef, vmgen, nimsets, types, passes, msgs, vmdef, vmgen, nimsets, types,
parser, vmdeps, idents, trees, renderer, options, transf, parser, vmdeps, idents, trees, renderer, options, transf,
gorgeimpl, lineinfos, btrees, macrocacheimpl, gorgeimpl, lineinfos, btrees, macrocacheimpl,
modulegraphs, sighashes, int128, vmprofiler modulegraphs, sighashes, int128, vmprofiler
@ -2309,7 +2309,7 @@ proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
else: else:
refresh(PCtx graph.vm, module, idgen) refresh(PCtx graph.vm, module, idgen)
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = proc setupEvalGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
#var c = newEvalContext(module, emRepl) #var c = newEvalContext(module, emRepl)
#c.features = {allowCast, allowInfiniteLoops} #c.features = {allowCast, allowInfiniteLoops}
#pushStackFrame(c, newStackFrame()) #pushStackFrame(c, newStackFrame())
@ -2318,7 +2318,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext
setupGlobalCtx(module, graph, idgen) setupGlobalCtx(module, graph, idgen)
result = PCtx graph.vm result = PCtx graph.vm
proc myProcess(c: PPassContext, n: PNode): PNode = proc interpreterCode*(c: PPassContext, n: PNode): PNode =
let c = PCtx(c) let c = PCtx(c)
# don't eval errornous code: # don't eval errornous code:
if c.oldErrorCount == c.config.errorCounter: if c.oldErrorCount == c.config.errorCounter:
@ -2328,11 +2328,6 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
result = n result = n
c.oldErrorCount = c.config.errorCounter c.oldErrorCount = c.config.errorCounter
proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
result = myProcess(c, n)
const evalPass* = makePass(myOpen, myProcess, myClose)
proc evalConstExprAux(module: PSym; idgen: IdGenerator; proc evalConstExprAux(module: PSym; idgen: IdGenerator;
g: ModuleGraph; prc: PSym, n: PNode, g: ModuleGraph; prc: PSym, n: PNode,
mode: TEvalMode): PNode = mode: TEvalMode): PNode =

View file

@ -138,6 +138,7 @@ The garbage collector won't try to free them, you need to call their respective
when you are done with them or they will leak. when you are done with them or they will leak.
Heap dump Heap dump
========= =========

View file

@ -23,7 +23,7 @@ import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp
# Do NOT import suggest. It will lead to weird bugs with # Do NOT import suggest. It will lead to weird bugs with
# suggestionResultHook, because suggest.nim is included by sigmatch. # suggestionResultHook, because suggest.nim is included by sigmatch.
# So we import that one instead. # So we import that one instead.
import compiler / [options, commands, modules, sem, import compiler / [options, commands, modules,
passes, passaux, msgs, passes, passaux, msgs,
sigmatch, ast, sigmatch, ast,
idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper, idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper,