passes.nim 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements the passes functionality. A pass must implement the
  10. ## `TPass` interface.
  11. import
  12. options, ast, llstream, msgs,
  13. idents,
  14. syntaxes, modulegraphs, reorder,
  15. lineinfos,
  16. pipelineutils,
  17. modules, pathutils, packages,
  18. sem, semdata
  19. import ic/replayer
  20. export skipCodegen, resolveMod, prepareConfigNotes
  21. when defined(nimsuggest):
  22. import ../dist/checksums/src/checksums/sha1
  23. when defined(nimPreviewSlimSystem):
  24. import std/[syncio, assertions]
  25. import std/tables
  26. type
  27. TPassData* = tuple[input: PNode, closeOutput: PNode]
  28. # a pass is a tuple of procedure vars ``TPass.close`` may produce additional
  29. # nodes. These are passed to the other close procedures.
  30. # This mechanism used to be used for the instantiation of generics.
  31. proc makePass*(open: TPassOpen = nil,
  32. process: TPassProcess = nil,
  33. close: TPassClose = nil,
  34. isFrontend = false): TPass =
  35. result = (open, process, close, isFrontend)
  36. const
  37. maxPasses = 10
  38. type
  39. TPassContextArray = array[0..maxPasses - 1, PPassContext]
  40. proc clearPasses*(g: ModuleGraph) =
  41. g.passes.setLen(0)
  42. proc registerPass*(g: ModuleGraph; p: TPass) =
  43. internalAssert g.config, g.passes.len < maxPasses
  44. g.passes.add(p)
  45. proc openPasses(g: ModuleGraph; a: var TPassContextArray;
  46. module: PSym; idgen: IdGenerator) =
  47. for i in 0..<g.passes.len:
  48. if not isNil(g.passes[i].open):
  49. a[i] = g.passes[i].open(g, module, idgen)
  50. else: a[i] = nil
  51. proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
  52. var m: PNode = nil
  53. for i in 0..<graph.passes.len:
  54. if not isNil(graph.passes[i].close):
  55. m = graph.passes[i].close(graph, a[i], m)
  56. a[i] = nil # free the memory here
  57. proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray): bool =
  58. # this implements the code transformation pipeline
  59. var m = n
  60. for i in 0..<graph.passes.len:
  61. if not isNil(graph.passes[i].process):
  62. m = graph.passes[i].process(a[i], m)
  63. if isNil(m): return false
  64. result = true
  65. proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
  66. a: var TPassContextArray; m: PSym) =
  67. # XXX fixme this should actually be relative to the config file!
  68. let relativeTo = toFullPath(graph.config, m.info)
  69. for module in items(implicits):
  70. # implicit imports should not lead to a module importing itself
  71. if m.position != resolveMod(graph.config, module, relativeTo).int32:
  72. var importStmt = newNodeI(nodeKind, m.info)
  73. var str = newStrNode(nkStrLit, module)
  74. str.info = m.info
  75. importStmt.add str
  76. if not processTopLevelStmt(graph, importStmt, a): break
  77. proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
  78. stream: PLLStream): bool {.discardable.} =
  79. if graph.stopCompile(): return true
  80. var
  81. p: Parser = default(Parser)
  82. a: TPassContextArray = default(TPassContextArray)
  83. s: PLLStream
  84. fileIdx = module.fileIdx
  85. prepareConfigNotes(graph, module)
  86. openPasses(graph, a, module, idgen)
  87. if stream == nil:
  88. let filename = toFullPathConsiderDirty(graph.config, fileIdx)
  89. s = llStreamOpen(filename, fmRead)
  90. if s == nil:
  91. rawMessage(graph.config, errCannotOpenFile, filename.string)
  92. return false
  93. else:
  94. s = stream
  95. when defined(nimsuggest):
  96. let filename = toFullPathConsiderDirty(graph.config, fileIdx).string
  97. msgs.setHash(graph.config, fileIdx, $sha1.secureHashFile(filename))
  98. while true:
  99. openParser(p, fileIdx, s, graph.cache, graph.config)
  100. if (not belongsToStdlib(graph, module)) or module.name.s == "distros":
  101. # XXX what about caching? no processing then? what if I change the
  102. # modules to include between compilation runs? we'd need to track that
  103. # in ROD files. I think we should enable this feature only
  104. # for the interactive mode.
  105. if module.name.s != "nimscriptapi":
  106. processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module
  107. processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module
  108. checkFirstLineIndentation(p)
  109. block processCode:
  110. if graph.stopCompile(): break processCode
  111. var n = parseTopLevelStmt(p)
  112. if n.kind == nkEmpty: break processCode
  113. # read everything, no streaming possible
  114. var sl = newNodeI(nkStmtList, n.info)
  115. sl.add n
  116. while true:
  117. var n = parseTopLevelStmt(p)
  118. if n.kind == nkEmpty: break
  119. sl.add n
  120. if sfReorder in module.flags or codeReordering in graph.config.features:
  121. sl = reorder(graph, sl, module)
  122. discard processTopLevelStmt(graph, sl, a)
  123. closeParser(p)
  124. if s.kind != llsStdIn: break
  125. closePasses(graph, a)
  126. if graph.config.backend notin {backendC, backendCpp, backendObjc}:
  127. # We only write rod files here if no C-like backend is active.
  128. # The C-like backends have been patched to support the IC mechanism.
  129. # They are responsible for closing the rod files. See `cbackend.nim`.
  130. closeRodFile(graph, module)
  131. result = true
  132. proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
  133. var flags = flags
  134. if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
  135. result = graph.getModule(fileIdx)
  136. template processModuleAux(moduleStatus) =
  137. onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
  138. var s: PLLStream = nil
  139. if sfMainModule in flags:
  140. if graph.config.projectIsStdin: s = stdin.llStreamOpen
  141. elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
  142. discard processModule(graph, result, idGeneratorFromModule(result), s)
  143. if result == nil:
  144. var cachedModules: seq[FileIndex] = @[]
  145. result = moduleFromRodFile(graph, fileIdx, cachedModules)
  146. let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
  147. if result == nil:
  148. result = newModule(graph, fileIdx)
  149. result.flags.incl flags
  150. registerModule(graph, result)
  151. processModuleAux("import")
  152. else:
  153. if sfSystemModule in flags:
  154. graph.systemModule = result
  155. partialInitModule(result, graph, fileIdx, filename)
  156. for m in cachedModules:
  157. registerModuleById(graph, m)
  158. replayStateChanges(graph.packed.pm[m.int].module, graph)
  159. replayGenericCacheInformation(graph, m.int)
  160. elif graph.isDirty(result):
  161. result.flags.excl sfDirty
  162. # reset module fields:
  163. initStrTables(graph, result)
  164. result.ast = nil
  165. processModuleAux("import(dirty)")
  166. graph.markClientsDirty(fileIdx)
  167. proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
  168. # this is called by the semantic checking phase
  169. assert graph.config != nil
  170. result = compileModule(graph, fileIdx, {}, s)
  171. graph.addDep(s, fileIdx)
  172. # keep track of import relationships
  173. if graph.config.hcrOn:
  174. graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
  175. #if sfSystemModule in result.flags:
  176. # localError(result.info, errAttemptToRedefine, result.name.s)
  177. # restore the notes for outer module:
  178. graph.config.notes =
  179. if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
  180. else: graph.config.foreignPackageNotes
  181. proc connectCallbacks*(graph: ModuleGraph) =
  182. graph.includeFileCallback = modules.includeModule
  183. graph.importModuleCallback = importModule
  184. proc compileSystemModule*(graph: ModuleGraph) =
  185. if graph.systemModule == nil:
  186. connectCallbacks(graph)
  187. graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
  188. graph.config.libpath / RelativeFile"system.nim")
  189. discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
  190. proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
  191. connectCallbacks(graph)
  192. let conf = graph.config
  193. wantMainModule(conf)
  194. configComplete(graph)
  195. let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
  196. let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
  197. conf.projectMainIdx2 = projectFile
  198. let packSym = getPackage(graph, projectFile)
  199. graph.config.mainPackageId = packSym.getPackageId
  200. graph.importStack.add projectFile
  201. if projectFile == systemFileIdx:
  202. discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
  203. else:
  204. graph.compileSystemModule()
  205. discard graph.compileModule(projectFile, {sfMainModule})
  206. proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
  207. result = preparePContext(graph, module, idgen)
  208. proc mySemClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
  209. var c = PContext(context)
  210. closePContext(graph, c, n)
  211. proc mySemProcess(context: PPassContext, n: PNode): PNode =
  212. result = semWithPContext(PContext(context), n)
  213. const semPass* = makePass(mySemOpen, mySemProcess, mySemClose,
  214. isFrontend = true)