# # # The Nim Compiler # (c) Copyright 2018 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module implements the new compilation cache. import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp, btrees, trees, condsyms, nversion ## Todo: ## - Dependency computation should use *signature* hashes in order to ## avoid recompiling dependent modules. ## - Patch the rest of the compiler to do lazy loading of proc bodies. ## - Patch the C codegen to cache proc bodies and maybe types. template db(): DbConn = g.incr.db proc encodeConfig(g: ModuleGraph): string = result = newStringOfCap(100) result.add RodFileVersion for d in definedSymbolNames(g.config.symbols): result.add ' ' result.add d template serialize(field) = result.add ' ' result.add($g.config.field) depConfigFields(serialize) proc needsRecompile(g: ModuleGraph; fileIdx: FileIndex; fullpath: string; cycleCheck: var IntSet): bool = let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?", fullpath) if root[0].len == 0: return true if root[1] != hashFileCached(g.config, fileIdx, fullpath): return true # cycle detection: assume "not changed" is correct. if cycleCheck.containsOrIncl(int fileIdx): return false # check dependencies (recursively): for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)", root[0]): let dep = row[0] if needsRecompile(g, g.config.fileInfoIdx(dep), dep, cycleCheck): return true return false proc getModuleId*(g: ModuleGraph; fileIdx: FileIndex; fullpath: string): int = if g.config.symbolFiles in {disabledSf, writeOnlySf} or g.incr.configChanged: return getID() let module = g.incr.db.getRow( sql"select id, fullHash, nimid from modules where fullpath = ?", fullpath) let currentFullhash = hashFileCached(g.config, fileIdx, fullpath) if module[0].len == 0: result = getID() db.exec(sql"insert into modules(fullpath, interfHash, fullHash, nimid) values (?, ?, ?, ?)", fullpath, "", currentFullhash, result) else: result = parseInt(module[2]) if currentFullhash == module[1]: # not changed, so use the cached AST: doAssert(result != 0) var cycleCheck = initIntSet() if not needsRecompile(g, fileIdx, fullpath, cycleCheck): echo "cached successfully! ", fullpath return -result db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0]) db.exec(sql"delete from deps where module = ?", module[0]) db.exec(sql"delete from types where module = ?", module[0]) db.exec(sql"delete from syms where module = ?", module[0]) db.exec(sql"delete from toplevelstmts where module = ?", module[0]) db.exec(sql"delete from statics where module = ?", module[0]) proc pushType(w: var Writer, t: PType) = if not containsOrIncl(w.tmarks, t.id): w.tstack.add(t) proc pushSym(w: var Writer, s: PSym) = if not containsOrIncl(w.smarks, s.id): w.sstack.add(s) template w: untyped = g.incr.w proc encodeNode(g: ModuleGraph; fInfo: TLineInfo, n: PNode, result: var string) = if n == nil: # nil nodes have to be stored too: result.add("()") return result.add('(') encodeVInt(ord(n.kind), result) # we do not write comments for now # Line information takes easily 20% or more of the filesize! Therefore we # omit line information if it is the same as the parent's line information: if fInfo.fileIndex != n.info.fileIndex: result.add('?') encodeVInt(n.info.col, result) result.add(',') encodeVInt(int n.info.line, result) result.add(
discard """
output: '''int: 108'''
"""
# bug #4070
proc id(f: (proc())): auto =
return f
proc foo(myinteger: int): (iterator(): int) =
return iterator(): int {.closure.} =
proc bar() =
proc kk() =
echo "int: ", myinteger
kk()
id(bar)()
discard foo(108)()