mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 01:14:41 +00:00
154 lines
5.0 KiB
Nim
154 lines
5.0 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2021 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## Integrity checking for a set of .rod files.
|
|
## The set must cover a complete Nim project.
|
|
|
|
import sets
|
|
|
|
when defined(nimPreviewSlimSystem):
|
|
import std/assertions
|
|
|
|
import ".." / [ast, modulegraphs]
|
|
import packed_ast, bitabs, ic
|
|
|
|
type
|
|
CheckedContext = object
|
|
g: ModuleGraph
|
|
thisModule: int32
|
|
checkedSyms: HashSet[ItemId]
|
|
checkedTypes: HashSet[ItemId]
|
|
|
|
proc checkType(c: var CheckedContext; typeId: PackedItemId)
|
|
proc checkForeignSym(c: var CheckedContext; symId: PackedItemId)
|
|
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos)
|
|
|
|
proc checkTypeObj(c: var CheckedContext; typ: PackedType) =
|
|
for child in typ.types:
|
|
checkType(c, child)
|
|
if typ.n != emptyNodeId:
|
|
checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos typ.n)
|
|
if typ.sym != nilItemId:
|
|
checkForeignSym(c, typ.sym)
|
|
if typ.owner != nilItemId:
|
|
checkForeignSym(c, typ.owner)
|
|
checkType(c, typ.typeInst)
|
|
|
|
proc checkType(c: var CheckedContext; typeId: PackedItemId) =
|
|
if typeId == nilItemId: return
|
|
let itemId = translateId(typeId, c.g.packed, c.thisModule, c.g.config)
|
|
if not c.checkedTypes.containsOrIncl(itemId):
|
|
let oldThisModule = c.thisModule
|
|
c.thisModule = itemId.module
|
|
checkTypeObj c, c.g.packed[itemId.module].fromDisk.types[itemId.item]
|
|
c.thisModule = oldThisModule
|
|
|
|
proc checkSym(c: var CheckedContext; s: PackedSym) =
|
|
if s.name != LitId(0):
|
|
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId s.name
|
|
checkType c, s.typ
|
|
if s.ast != emptyNodeId:
|
|
checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos s.ast)
|
|
if s.owner != nilItemId:
|
|
checkForeignSym(c, s.owner)
|
|
|
|
proc checkLocalSym(c: var CheckedContext; item: int32) =
|
|
let itemId = ItemId(module: c.thisModule, item: item)
|
|
if not c.checkedSyms.containsOrIncl(itemId):
|
|
checkSym c, c.g.packed[c.thisModule].fromDisk.syms[item]
|
|
|
|
proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) =
|
|
let itemId = translateId(symId, c.g.packed, c.thisModule, c.g.config)
|
|
if not c.checkedSyms.containsOrIncl(itemId):
|
|
let oldThisModule = c.thisModule
|
|
c.thisModule = itemId.module
|
|
checkSym c, c.g.packed[itemId.module].fromDisk.syms[itemId.item]
|
|
c.thisModule = oldThisModule
|
|
|
|
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
|
|
if tree[n.int].typeId != nilItemId:
|
|
checkType(c, tree[n.int].typeId)
|
|
case n.kind
|
|
of nkEmpty, nkNilLit, nkType, nkNilRodNode:
|
|
discard
|
|
of nkIdent:
|
|
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
|
|
of nkSym:
|
|
checkLocalSym(c, tree.nodes[n.int].operand)
|
|
of directIntLit:
|
|
discard
|
|
of externIntLit, nkFloatLit..nkFloat128Lit:
|
|
assert c.g.packed[c.thisModule].fromDisk.numbers.hasLitId n.litId
|
|
of nkStrLit..nkTripleStrLit:
|
|
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
|
|
of nkModuleRef:
|
|
let (n1, n2) = sons2(tree, n)
|
|
assert n1.kind == nkInt32Lit
|
|
assert n2.kind == nkInt32Lit
|
|
checkForeignSym(c, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
|
|
else:
|
|
for n0 in sonsReadonly(tree, n):
|
|
checkNode(c, tree, n0)
|
|
|
|
proc checkTree(c: var CheckedContext; t: PackedTree) =
|
|
for p in allNodes(t): checkNode(c, t, p)
|
|
|
|
proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) =
|
|
for symId in symIds:
|
|
assert symId >= 0 and symId < m.syms.len, $symId & " " & $m.syms.len
|
|
|
|
proc checkModule(c: var CheckedContext; m: PackedModule) =
|
|
# We check that:
|
|
# - Every symbol references existing types and symbols.
|
|
# - Every tree node references existing types and symbols.
|
|
for i in 0..high(m.syms):
|
|
checkLocalSym c, int32(i)
|
|
|
|
checkTree c, m.toReplay
|
|
checkTree c, m.topLevel
|
|
|
|
for e in m.exports:
|
|
assert e[1] >= 0 and e[1] < m.syms.len
|
|
assert e[0] == m.syms[e[1]].name
|
|
|
|
for e in m.compilerProcs:
|
|
assert e[1] >= 0 and e[1] < m.syms.len
|
|
assert e[0] == m.syms[e[1]].name
|
|
|
|
checkLocalSymIds c, m, m.converters
|
|
checkLocalSymIds c, m, m.methods
|
|
checkLocalSymIds c, m, m.trmacros
|
|
checkLocalSymIds c, m, m.pureEnums
|
|
#[
|
|
To do: Check all these fields:
|
|
|
|
reexports*: seq[(LitId, PackedItemId)]
|
|
macroUsages*: seq[(PackedItemId, PackedLineInfo)]
|
|
|
|
typeInstCache*: seq[(PackedItemId, PackedItemId)]
|
|
procInstCache*: seq[PackedInstantiation]
|
|
attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
|
|
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
|
|
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
|
|
]#
|
|
|
|
proc checkIntegrity*(g: ModuleGraph) =
|
|
var c = CheckedContext(g: g)
|
|
for i in 0..high(g.packed):
|
|
# case statement here to enforce exhaustive checks.
|
|
case g.packed[i].status
|
|
of undefined:
|
|
discard "nothing to do"
|
|
of loading:
|
|
assert false, "cannot check integrity: Module still loading"
|
|
of stored, storing, outdated, loaded:
|
|
c.thisModule = int32 i
|
|
checkModule(c, g.packed[i].fromDisk)
|
|
|