IC: next steps (#16729)

* IC: dead code elimination pass
* preparations for a different codegen strategy
* added documentation to the newly written code
* IC: backend code
* IC: backend adjustments
* optimized the compiler a bit
* IC: yet another massive refactoring
* fixes regressions
* cleanups
This commit is contained in:
Andreas Rumpf
2021-01-23 08:06:15 +01:00
committed by GitHub
parent eae3bdf8fe
commit 8241e55023
32 changed files with 729 additions and 323 deletions

View File

@@ -720,6 +720,16 @@ type
module*: int32
item*: int32
proc `==`*(a, b: ItemId): bool {.inline.} =
a.item == b.item and a.module == b.module
proc hash*(x: ItemId): Hash =
var h: Hash = hash(x.module)
h = h !& hash(x.item)
result = !$h
type
TIdObj* = object of RootObj
itemId*: ItemId
PIdObj* = ref TIdObj
@@ -830,10 +840,8 @@ type
TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym
# proc and type instantiations are cached in the generic symbol
case kind*: TSymKind
of skType, skGenericParam:
typeInstCache*: seq[PType]
of routineKinds:
procInstCache*: seq[PInstantiation]
#procInstCache*: seq[PInstantiation]
gcUnsafetyReason*: PSym # for better error messages wrt gcsafe
transformedBody*: PNode # cached body after transf pass
of skLet, skVar, skField, skForVar:
@@ -913,8 +921,6 @@ type
owner*: PSym # the 'owner' of the type
sym*: PSym # types have the sym associated with them
# it is used for converting types to strings
attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc.
methods*: seq[(int,PSym)] # attached methods
size*: BiggestInt # the size of the type in bytes
# -1 means that the size is unkwown
align*: int16 # the type's alignment requirements
@@ -1069,11 +1075,7 @@ type
module*: int32
symId*: int32
typeId*: int32
proc hash*(x: ItemId): Hash =
var h: Hash = hash(x.module)
h = h !& hash(x.item)
result = !$h
sealed*: bool
const
PackageModuleId* = -3'i32
@@ -1083,10 +1085,12 @@ proc idGeneratorFromModule*(m: PSym): IdGenerator =
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
proc nextSymId*(x: IdGenerator): ItemId {.inline.} =
assert(not x.sealed)
inc x.symId
result = ItemId(module: x.module, item: x.symId)
proc nextTypeId*(x: IdGenerator): ItemId {.inline.} =
assert(not x.sealed)
inc x.typeId
result = ItemId(module: x.module, item: x.typeId)
@@ -1210,11 +1214,32 @@ proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[
template previouslyInferred*(t: PType): PType =
if t.sons.len > 1: t.lastSon else: nil
when false:
import tables, strutils
var x: CountTable[string]
addQuitProc proc () {.noconv.} =
for k, v in pairs(x):
echo k
echo v
proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
info: TLineInfo; options: TOptions = {}): PSym =
# generates a symbol and initializes the hash field too
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
options: options, owner: owner, offset: defaultOffset)
when false:
if id.item > 2141:
let s = getStackTrace()
const words = ["createTypeBoundOps",
"initOperators",
"generateInstance",
"semIdentDef", "addLocalDecl"]
for w in words:
if w in s:
x.inc w
return
x.inc "<no category>"
proc astdef*(s: PSym): PNode =
# get only the definition (initializer) portion of the ast
@@ -1422,7 +1447,6 @@ proc assignType*(dest, src: PType) =
dest.n = src.n
dest.size = src.size
dest.align = src.align
dest.attachedOps = src.attachedOps
dest.lockLevel = src.lockLevel
# this fixes 'type TLock = TSysLock':
if src.sym != nil:
@@ -1622,11 +1646,9 @@ template transitionSymKindCommon*(k: TSymKind) =
proc transitionGenericParamToType*(s: PSym) =
transitionSymKindCommon(skType)
s.typeInstCache = obj.typeInstCache
proc transitionRoutineSymKind*(s: PSym, kind: range[skProc..skTemplate]) =
transitionSymKindCommon(kind)
s.procInstCache = obj.procInstCache
s.gcUnsafetyReason = obj.gcUnsafetyReason
s.transformedBody = obj.transformedBody
@@ -1893,10 +1915,8 @@ when false:
for i in 0..<n.safeLen:
if n[i].containsNil: return true
template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
proc hasDisabledAsgn*(t: PType): bool =
t.attachedOps[attachedAsgn] != nil and sfError in t.attachedOps[attachedAsgn].flags
template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {}
template incompleteType*(t: PType): bool =
t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward}
@@ -1936,10 +1956,6 @@ proc addParam*(procType: PType; param: PSym) =
procType.n.add newSymNode(param)
rawAddSon(procType, param.typ)
template destructor*(t: PType): PSym = t.attachedOps[attachedDestructor]
template assignment*(t: PType): PSym = t.attachedOps[attachedAsgn]
template asink*(t: PType): PSym = t.attachedOps[attachedSink]
const magicsThatCanRaise = {
mNone, mSlurp, mStaticExec, mParseExprToAst, mParseStmtToAst, mEcho}

View File

@@ -1292,16 +1292,17 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
genAssignment(p, a, b, {})
else:
let ti = genTypeInfoV1(p.module, typ, a.lode.info)
if bt.destructor != nil and not isTrivialProc(p.module.g.graph, bt.destructor):
let op = getAttachedOp(p.module.g.graph, bt, attachedDestructor)
if op != nil and not isTrivialProc(p.module.g.graph, op):
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
# convention at least:
if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccNimCall:
if op.typ == nil or op.typ.callConv != ccNimCall:
localError(p.module.config, a.lode.info,
"the destructor that is turned into a finalizer needs " &
"to have the 'nimcall' calling convention")
var f: TLoc
initLocExpr(p, newSymNode(bt.destructor), f)
initLocExpr(p, newSymNode(op), f)
p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
if a.storage == OnHeap and usesWriteBarrier(p.config):
@@ -2204,7 +2205,8 @@ proc genDestroy(p: BProc; n: PNode) =
else: discard "nothing to do"
else:
let t = n[1].typ.skipTypes(abstractVar)
if t.destructor != nil and getBody(p.module.g.graph, t.destructor).len != 0:
let op = getAttachedOp(p.module.g.graph, t, attachedDestructor)
if op != nil and getBody(p.module.g.graph, op).len != 0:
internalError(p.config, n.info, "destructor turned out to be not trivial")
discard "ignore calls to the default destructor"
@@ -2236,14 +2238,8 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) =
proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) =
const ToStringProcSlot = -4
let t = e[1].typ.skipTypes(abstractInst+{tyRange})
var toStrProc: PSym = nil
for idx, p in items(t.methods):
if idx == ToStringProcSlot:
toStrProc = p
break
if toStrProc == nil:
toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph, p.module.idgen)
t.methods.add((ToStringProcSlot, toStrProc))
let toStrProc = getToStringProc(p.module.g.graph, t)
# XXX need to modify this logic for IC.
var n = copyTree(e)
n[0] = newSymNode(toStrProc)
expr(p, n, d)
@@ -2645,6 +2641,57 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
if t.kind notin {tySequence, tyString}:
d.storage = OnStatic
proc genConstSetup(p: BProc; sym: PSym): bool =
let m = p.module
useHeader(m, sym)
if sym.loc.k == locNone:
fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic)
if m.hcrOn: incl(sym.loc.flags, lfIndirect)
result = lfNoDecl notin sym.loc.flags
proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
assert(sym.loc.r != nil)
if m.hcrOn:
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]);
m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)])
else:
let headerDecl = "extern NIM_CONST $1 $2;$n" %
[getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]
m.s[cfsData].add(headerDecl)
if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
p.module.g.generatedHeader.s[cfsData].add(headerDecl)
proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
# add a suffix for hcr - will later init the global pointer with this data
let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r
q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(q, sym.typ), actualConstName,
genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ)])
if q.hcrOn:
# generate the global pointer with the real name
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, skVar), sym.loc.r])
# register it (but ignore the boolean result of hcrRegisterGlobal)
q.initProc.procSec(cpsLocals).addf(
"\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
[getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)])
# always copy over the contents of the actual constant with the _const
# suffix ==> this means that the constant is reloadable & updatable!
q.initProc.procSec(cpsLocals).add(ropecg(q,
"\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
[sym.loc.r, actualConstName, rdLoc(sym.loc)]))
proc genConstStmt(p: BProc, n: PNode) =
# This code is only used in the new DCE implementation.
assert useAliveDataFromDce in p.module.flags
let m = p.module
for it in n:
if it[0].kind == nkSym:
let sym = it[0].sym
if not isSimpleConst(sym.typ) and sym.itemId.item in m.alive and genConstSetup(p, sym):
genConstDefinition(m, p, sym)
proc expr(p: BProc, n: PNode, d: var TLoc) =
when defined(nimCompilerStackraceHints):
setFrameMsg p.config$n.info & " " & $n.kind
@@ -2655,7 +2702,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
var sym = n.sym
case sym.kind
of skMethod:
if {sfDispatcher, sfForward} * sym.flags != {}:
if useAliveDataFromDce in p.module.flags or {sfDispatcher, sfForward} * sym.flags != {}:
# we cannot produce code for the dispatcher yet:
fillProcLoc(p.module, n)
genProcPrototype(p.module, sym)
@@ -2668,13 +2715,19 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if sfCompileTime in sym.flags:
localError(p.config, n.info, "request to generate code for .compileTime proc: " &
sym.name.s)
genProc(p.module, sym)
if useAliveDataFromDce in p.module.flags:
fillProcLoc(p.module, n)
genProcPrototype(p.module, sym)
else:
genProc(p.module, sym)
if sym.loc.r == nil or sym.loc.lode == nil:
internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of skConst:
if isSimpleConst(sym.typ):
putIntoDest(p, d, n, genLiteral(p, sym.ast, sym.typ), OnStatic)
elif useAliveDataFromDce in p.module.flags:
genConstHeader(p.module, p.module, p, sym)
else:
genComplexConst(p, sym, d)
of skEnumField:
@@ -2795,7 +2848,10 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkEmpty: discard
of nkWhileStmt: genWhileStmt(p, n)
of nkVarSection, nkLetSection: genVarStmt(p, n)
of nkConstSection: discard # consts generated lazily on use
of nkConstSection:
if useAliveDataFromDce in p.module.flags:
genConstStmt(p, n)
# else: consts generated lazily on use
of nkForStmt: internalError(p.config, n.info, "for statement not eliminated")
of nkCaseStmt: genCase(p, n, d)
of nkReturnStmt: genReturnStmt(p, n)
@@ -2840,13 +2896,16 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
if n[genericParamsPos].kind == nkEmpty:
var prc = n[namePos].sym
# due to a bug/limitation in the lambda lifting, unused inner procs
# are not transformed correctly. We work around this issue (#411) here
# by ensuring it's no inner proc (owner is a module):
if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
if useAliveDataFromDce in p.module.flags:
if p.module.alive.contains(prc.itemId.item):
genProc(p.module, prc)
elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
(sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
(prc.kind == skMethod):
# due to a bug/limitation in the lambda lifting, unused inner procs
# are not transformed correctly. We work around this issue (#411) here
# by ensuring it's no inner proc (owner is a module).
# Generate proc even if empty body, bugfix #11651.
genProc(p.module, prc)
of nkParForStmt: genParForStmt(p, n)

View File

@@ -1530,20 +1530,21 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)])
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
const ObjDiscMappingProcSlot = -5
var theProc: PSym = nil
for idx, p in items(t.methods):
if idx == ObjDiscMappingProcSlot:
theProc = p
break
if theProc == nil:
theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen)
t.methods.add((ObjDiscMappingProcSlot, theProc))
var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8))
call.add newSymNode(theProc)
call.add e
expr(p, call, d)
when false:
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
const ObjDiscMappingProcSlot = -5
var theProc: PSym = nil
for idx, p in items(t.methods):
if idx == ObjDiscMappingProcSlot:
theProc = p
break
if theProc == nil:
theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph, p.module.idgen)
t.methods.add((ObjDiscMappingProcSlot, theProc))
var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8))
call.add newSymNode(theProc)
call.add e
expr(p, call, d)
proc asgnFieldDiscriminant(p: BProc, e: PNode) =
var a, tmp: TLoc

View File

@@ -1308,7 +1308,7 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope =
proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope =
let theProc = t.attachedOps[op]
let theProc = getAttachedOp(m.g.graph, t, op)
if theProc != nil and not isTrivialProc(m.g.graph, theProc):
# the prototype of a destructor is ``=destroy(x: var T)`` and that of a
# finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling
@@ -1476,10 +1476,11 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
genTupleInfo(m, x, origType, result, info)
else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
if t.attachedOps[attachedDeepCopy] != nil:
genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result)
elif origType.attachedOps[attachedDeepCopy] != nil:
genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result)
var op = getAttachedOp(m.g.graph, t, attachedDeepCopy)
if op == nil:
op = getAttachedOp(m.g.graph, origType, attachedDeepCopy)
if op != nil:
genDeepCopyProc(m, op, result)
if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags:
let v2info = genTypeInfoV2(m, origType, info)

View File

@@ -14,7 +14,7 @@ import
nversion, nimsets, msgs, bitsets, idents, types,
ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
rodutils, renderer, cgendata, ccgmerge, aliases,
lowerings, tables, sets, ndi, lineinfos, pathutils, transf, enumtostr,
lowerings, tables, sets, ndi, lineinfos, pathutils, transf,
injectdestructors
when not defined(leanCompiler):
@@ -1189,48 +1189,16 @@ proc genProcNoForward(m: BModule, prc: PSym) =
if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
proc requestConstImpl(p: BProc, sym: PSym) =
var m = p.module
useHeader(m, sym)
if sym.loc.k == locNone:
fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic)
if m.hcrOn: incl(sym.loc.flags, lfIndirect)
if lfNoDecl in sym.loc.flags: return
# declare implementation:
var q = findPendingModule(m, sym)
if q != nil and not containsOrIncl(q.declaredThings, sym.id):
assert q.initProc.module == q
# add a suffix for hcr - will later init the global pointer with this data
let actualConstName = if m.hcrOn: sym.loc.r & "_const" else: sym.loc.r
q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(q, sym.typ), actualConstName,
genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ)])
if m.hcrOn:
# generate the global pointer with the real name
q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r])
# register it (but ignore the boolean result of hcrRegisterGlobal)
q.initProc.procSec(cpsLocals).addf(
"\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
[getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)])
# always copy over the contents of the actual constant with the _const
# suffix ==> this means that the constant is reloadable & updatable!
q.initProc.procSec(cpsLocals).add(ropecg(q,
"\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
[sym.loc.r, actualConstName, rdLoc(sym.loc)]))
# declare header:
if q != m and not containsOrIncl(m.declaredThings, sym.id):
assert(sym.loc.r != nil)
if m.hcrOn:
m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]);
m.initProc.procSec(cpsLocals).addf(
"\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)])
else:
let headerDecl = "extern NIM_CONST $1 $2;$n" %
[getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]
m.s[cfsData].add(headerDecl)
if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
p.module.g.generatedHeader.s[cfsData].add(headerDecl)
if genConstSetup(p, sym):
let m = p.module
# declare implementation:
var q = findPendingModule(m, sym)
if q != nil and not containsOrIncl(q.declaredThings, sym.id):
assert q.initProc.module == q
genConstDefinition(q, p, sym)
# declare header:
if q != m and not containsOrIncl(m.declaredThings, sym.id):
genConstHeader(m, q, p, sym)
proc isActivated(prc: PSym): bool = prc.typ != nil
@@ -1839,7 +1807,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
proc newModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
proc newModule*(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
# we should create only one cgen module for each module sym
result = rawNewModule(g, module, conf)
if module.position >= g.modules.len:
@@ -1922,13 +1890,9 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
genStmts(p, n)
proc myProcess(b: PPassContext, n: PNode): PNode =
result = n
if b == nil: return
var m = BModule(b)
if passes.skipCodegen(m.config, n) or
not moduleHasChanged(m.g.graph, m.module):
return
proc genTopLevelStmt*(m: BModule; n: PNode) =
## Also called from `ic/cbackend.nim`.
if passes.skipCodegen(m.config, n): return
m.initProc.options = initProcOptions(m)
#softRnl = if optLineDir in m.config.options: noRnl else: rnl
# XXX replicate this logic!
@@ -1941,6 +1905,12 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
else:
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 =
if optForceFullMake notin m.config.globalOptions:
if not moduleHasChanged(m.g.graph, m.module):
@@ -2013,7 +1983,7 @@ proc writeModule(m: BModule, pending: bool) =
# that ``system.o`` is missing, so we need to call the C compiler for it:
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
if not fileExists(cf.obj): cf.flags = {CfileFlag.Cached}
if fileExists(cf.obj): cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)
onExit()
@@ -2037,10 +2007,8 @@ proc updateCachedModule(m: BModule) =
cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
result = n
if b == nil: return
var m = BModule(b)
proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
## Also called from IC.
if sfMainModule in m.module.flags:
# phase ordering problem here: We need to announce this
# dependency to 'nimTestErrorFlag' before system.c has been written to disk.
@@ -2091,6 +2059,12 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
let mm = m
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) =
# Forward declared proc:s lack bodies when first encountered, so they're given
# a second pass here

View File

@@ -112,7 +112,8 @@ type
isHeaderFile, # C source file is the header file
includesStringh, # C source file already includes ``<string.h>``
objHasKidsValid # whether we can rely on tfObjHasKids
useAliveDataFromDce # use the `alive: IntSet` field instead of
# computing alive data on our own.
BModuleList* = ref object of RootObj
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
@@ -154,6 +155,7 @@ type
forwTypeCache*: TypeCache # cache for forward declarations of types
declaredThings*: IntSet # things we have declared in this .c file
declaredProtos*: IntSet # prototypes we have declared in this .c file
alive*: IntSet # symbol IDs of alive data as computed by `dce.nim`
headerFiles*: seq[string] # needed headers to include
typeInfoMarker*: TypeCache # needed for generating type information
typeInfoMarkerV2*: TypeCache

View File

@@ -107,11 +107,14 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
s.ast[resultPos] = newNodeI(nkEmpty, s.info)
s.ast[dispatcherPos] = dispatcher
proc createDispatcher(s: PSym; idgen: IdGenerator): PSym =
proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
var disp = copySym(s, nextSymId(idgen))
incl(disp.flags, sfDispatcher)
excl(disp.flags, sfExported)
let old = disp.typ
disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner)
copyTypeProps(g, idgen.module, disp.typ, old)
# we can't inline the dispatcher itself (for now):
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
disp.ast = copyTree(s.ast)
@@ -177,7 +180,7 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym, fromCache: bool) =
of Invalid:
if witness.isNil: witness = g.methods[i].methods[0]
# create a new dispatcher:
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, idgen)))
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
#echo "adding ", s.info
#if fromCache:
# internalError(s.info, "no method dispatcher found")

View File

@@ -82,26 +82,6 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
type
Operators* = object
opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
proc initOperators*(g: ModuleGraph): Operators =
result.opLe = createMagic(g, "<=", mLeI)
result.opLt = createMagic(g, "<", mLtI)
result.opAnd = createMagic(g, "and", mAnd)
result.opOr = createMagic(g, "or", mOr)
result.opIsNil = createMagic(g, "isnil", mIsNil)
result.opEq = createMagic(g, "==", mEqI)
result.opAdd = createMagic(g, "+", mAddI)
result.opSub = createMagic(g, "-", mSubI)
result.opMul = createMagic(g, "*", mMulI)
result.opDiv = createMagic(g, "div", mDivI)
result.opLen = createMagic(g, "len", mLengthSeq)
result.opNot = createMagic(g, "not", mNot)
result.opContains = createMagic(g, "contains", mInSet)
proc swapArgs(fact: PNode, newOp: PSym): PNode =
result = newNodeI(nkCall, fact.info, 3)
result[0] = newSymNode(newOp)
@@ -404,16 +384,16 @@ proc usefulFact(n: PNode; o: Operators): PNode =
type
TModel* = object
s*: seq[PNode] # the "knowledge base"
o*: Operators
g*: ModuleGraph
beSmart*: bool
proc addFact*(m: var TModel, nn: PNode) =
let n = usefulFact(nn, m.o)
let n = usefulFact(nn, m.g.operators)
if n != nil:
if not m.beSmart:
m.s.add n
else:
let c = canon(n, m.o)
let c = canon(n, m.g.operators)
if c.getMagic == mAnd:
addFact(m, c[1])
addFact(m, c[2])
@@ -421,7 +401,7 @@ proc addFact*(m: var TModel, nn: PNode) =
m.s.add c
proc addFactNeg*(m: var TModel, n: PNode) =
let n = n.neg(m.o)
let n = n.neg(m.g.operators)
if n != nil: addFact(m, n)
proc sameOpr(a, b: PSym): bool =
@@ -740,7 +720,7 @@ proc doesImply*(facts: TModel, prop: PNode): TImplication =
if result != impUnknown: return
proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o))
result = doesImply(m, m.g.operators.opIsNil.buildCall(arg).neg(m.g.operators))
proc simpleSlice*(a, b: PNode): BiggestInt =
# returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
@@ -833,7 +813,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
if b.getMagic in someAdd:
if zero() <=? b[2] and a <=? b[1]: return impYes
# x <= y-c if x+c <= y
if b[2] <=? zero() and (canon(m.o.opSub.buildCall(a, b[2]), m.o) <=? b[1]):
if b[2] <=? zero() and (canon(m.g.operators.opSub.buildCall(a, b[2]), m.g.operators) <=? b[1]):
return impYes
# x+c <= y if c <= 0 and x <= y
@@ -847,20 +827,20 @@ proc ple(m: TModel; a, b: PNode): TImplication =
if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
a[1][2].isValue:
# simplify (x div 4) * 2 <= y to x div (c div d) <= y
if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
if ple(m, buildCall(m.g.operators.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
return impYes
# x*3 + x == x*4. It follows that:
# x*3 + y <= x*4 if y <= x and 3 <= 4
if a =~ x*dc + y and b =~ x2*ec:
if sameTree(x, x2):
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
return impYes
elif a =~ x*dc and b =~ x2*ec + y:
#echo "BUG cam ehrer e ", a, " <=? ", b
if sameTree(x, x2):
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
let ec1 = m.g.operators.opAdd.buildCall(ec, minusOne())
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
return impYes
@@ -963,12 +943,12 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
var b = bb
if replacements.len > 0:
m.s = @[]
m.o = model.o
m.g = model.g
# make the other facts consistent:
for fact in model.s:
if fact != nil and fact.getMagic notin someEq:
# XXX 'canon' should not be necessary here, but it is
m.s.add applyReplacements(fact, replacements).canon(m.o)
m.s.add applyReplacements(fact, replacements).canon(m.g.operators)
a = applyReplacements(aa, replacements)
b = applyReplacements(bb, replacements)
else:
@@ -977,19 +957,19 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
result = pleViaModelRec(m, a, b)
proc proveLe*(m: TModel; a, b: PNode): TImplication =
let x = canon(m.o.opLe.buildCall(a, b), m.o)
let x = canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
#echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
result = ple(m, x[1], x[2])
if result == impUnknown:
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o)
let y = canon(m.g.operators.opLe.buildCall(m.g.operators.opAdd.buildCall(b, one()), a), m.g.operators)
result = ~ple(m, y[1], y[2])
proc addFactLe*(m: var TModel; a, b: PNode) =
m.s.add canon(m.o.opLe.buildCall(a, b), m.o)
m.s.add canon(m.g.operators.opLe.buildCall(a, b), m.g.operators)
proc addFactLt*(m: var TModel; a, b: PNode) =
let bb = m.o.opAdd.buildCall(b, minusOne())
let bb = m.g.operators.opAdd.buildCall(b, minusOne())
addFactLe(m, a, bb)
proc settype(n: PNode): PType =
@@ -1021,14 +1001,14 @@ proc buildElse(n: PNode; o: Operators): PNode =
proc addDiscriminantFact*(m: var TModel, n: PNode) =
var fact = newNodeI(nkCall, n.info, 3)
fact[0] = newSymNode(m.o.opEq)
fact[0] = newSymNode(m.g.operators.opEq)
fact[1] = n[0]
fact[2] = n[1]
m.s.add fact
proc addAsgnFact*(m: var TModel, key, value: PNode) =
var fact = newNodeI(nkCall, key.info, 3)
fact[0] = newSymNode(m.o.opEq)
fact[0] = newSymNode(m.g.operators.opEq)
fact[1] = key
fact[2] = value
m.s.add fact
@@ -1044,7 +1024,7 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
# However, nil checking requires exactly the same mechanism! But for now
# we simply use sameTree and live with the unsoundness of the analysis.
var check = newNodeI(nkCall, a.info, 3)
check[0] = newSymNode(m.o.opEq)
check[0] = newSymNode(m.g.operators.opEq)
check[1] = a
check[2] = b
result = m.doesImply(check) == impYes
@@ -1052,9 +1032,9 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
let branch = n[i]
if branch.kind == nkOfBranch:
m.s.add buildOf(branch, n[0], m.o)
m.s.add buildOf(branch, n[0], m.g.operators)
else:
m.s.add n.buildElse(m.o).neg(m.o)
m.s.add n.buildElse(m.g.operators).neg(m.g.operators)
proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
if check[1].kind == nkCurly:
@@ -1072,6 +1052,6 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
for i in 1..<n.len:
let check = buildProperFieldCheck(n[0], n[i], m.o)
let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
if check != nil and m.doesImply(check) != impYes:
message(conf, n.info, warnProveField, renderTree(n[0])); break

102
compiler/ic/cbackend.nim Normal file
View File

@@ -0,0 +1,102 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## New entry point into our C/C++ code generator. Ideally
## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
## to work on packed trees directly and produce the C code as an AST which can
## then be rendered to text in a very simple manner. Unfortunately nobody wrote
## this code. So instead we wrap the existing cgen.nim and its friends so that
## we call directly into the existing code generation logic but avoiding the
## naive, outdated `passes` design. Thus you will see some
## `useAliveDataFromDce in flags` checks in the old code -- the old code is
## also doing cross-module dependency tracking and DCE that we don't need
## anymore. DCE is now done as prepass over the entire packed module graph.
import std / [intsets, algorithm]
import ".." / [ast, options, lineinfos, modulegraphs, cgendata, cgen,
pathutils, extccomp, msgs]
import packed_ast, to_packed_ast, bitabs, dce, rodfiles
proc unpackTree(g: ModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
var decoder = initPackedDecoder(g.config, g.cache)
result = loadNodes(decoder, g.packed, thisModule, tree, n)
proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
if g.backend == nil:
g.backend = cgendata.newModuleList(g)
var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
bmod.idgen = idgenFromLoadedModule(m)
bmod.flags.incl useAliveDataFromDce
bmod.alive = move alive[m.module.position]
for p in allNodes(m.fromDisk.topLevel):
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
cgen.genTopLevelStmt(bmod, n)
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
proc addFileToLink(config: ConfigRef; m: PSym) =
let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
let ext =
if config.backend == backendCpp: ".nim.cpp"
elif config.backend == backendObjc: ".nim.m"
else: ".nim.c"
let cfile = changeFileExt(completeCfilePath(config, withPackageName(config, filename)), ext)
var cf = Cfile(nimname: m.name.s, cname: cfile,
obj: completeCfilePath(config, toObjFile(config, cfile)),
flags: {CfileFlag.Cached})
addFileToCompile(config, cf)
proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
var s = newSeqOfCap[int32](alive[position].len)
for a in items(alive[position]): s.add int32(a)
sort(s)
var f2 = rodfiles.open(asymFile.string)
f2.loadHeader()
f2.loadSection aliveSymsSection
var oldData: seq[int32]
f2.loadSeq(oldData)
f2.close
if f2.err == ok and oldData == s:
result = false
else:
result = true
var f = rodfiles.create(asymFile.string)
f.storeHeader()
f.storeSection aliveSymsSection
f.storeSeq(s)
close f
proc generateCode*(g: ModuleGraph) =
## The single entry point, generate C(++) code for the entire
## Nim program aka `ModuleGraph`.
var alive = computeAliveSyms(g.packed, g.config)
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
of storing, outdated:
generateCodeForModule(g, g.packed[i], alive)
of loaded:
# Even though this module didn't change, DCE might trigger a change.
# Consider this case: Module A uses symbol S from B and B does not use
# S itself. A is then edited not to use S either. Thus we have to
# recompile B in order to remove S from the final result.
if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
generateCodeForModule(g, g.packed[i], alive)
else:
addFileToLink(g.config, g.packed[i].module)

108
compiler/ic/dce.nim Normal file
View File

@@ -0,0 +1,108 @@
#
#
# The Nim Compiler
# (c) Copyright 2021 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Dead code elimination (=DCE) for IC.
import std / intsets
import ".." / [ast, options, lineinfos]
import packed_ast, to_packed_ast, bitabs
type
AliveSyms* = seq[IntSet]
AliveContext* = object ## Purpose is to fill the 'alive' field.
stack: seq[(int, NodePos)] ## A stack for marking symbols as alive.
decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations.
thisModule: int ## The module we're currently analysing for DCE.
alive: AliveSyms ## The final result of our computation.
proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
## "Exported to C" procs are special (these are marked with '.exportc') because these
## must not be optimized away!
let symPtr = addr g[c.thisModule].fromDisk.sh.syms[symId]
let flags = symPtr.flags
# due to a bug/limitation in the lambda lifting, unused inner procs
# are not transformed correctly; issue (#411). However, the whole purpose here
# is to eliminate unused procs. So there is no special logic required for this case.
if sfCompileTime notin flags:
if ({sfExportc, sfCompilerProc} * flags == {sfExportc}) or
(symPtr.kind == skMethod):
result = true
# XXX: This used to be a condition to:
# (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: int32) =
## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will
## be analysed.
if not c.alive[module].containsOrIncl(item):
let body = g[module].fromDisk.sh.syms[item].ast
if body != emptyNodeId:
c.stack.add((module, NodePos(body)))
proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
## Marks the symbols we encounter when we traverse the AST at `tree[n]` as alive, unless
## it is purely in a declarative context (type section etc.).
case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
discard "ignore non-sym atoms"
of nkSym:
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, n.operand)
of nkModuleRef:
let (n1, n2) = sons2(tree, n)
assert n1.kind == nkInt32Lit
assert n2.kind == nkInt32Lit
let m = n1.litId
let item = n2.operand
let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
followLater(c, g, otherModule, item)
of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
nkCommentStmt, nkIteratorDef, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkStaticStmt:
discard
of nkVarSection, nkLetSection, nkConstSection:
discard
of nkProcDef, nkConverterDef, nkMethodDef, nkLambda, nkDo, nkFuncDef:
if n.firstSon.kind == nkSym and isNotGeneric(n):
if isExportedToC(c, g, n.firstSon.operand):
let item = n.operand
# This symbol is alive and everything its body references.
followLater(c, g, c.thisModule, item)
else:
for son in sonsReadonly(tree, n):
aliveCode(c, g, tree, son)
proc followNow(c: var AliveContext; g: PackedModuleGraph) =
## Mark all entries in the stack. Marking can add more entries
## to the stack but eventually we have looked at every alive symbol.
while c.stack.len > 0:
let (modId, ast) = c.stack.pop()
c.thisModule = modId
aliveCode(c, g, g[modId].fromDisk.bodies, ast)
proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms =
## Entry point for our DCE algorithm.
var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf),
thisModule: -1, alive: newSeq[IntSet](g.len))
for i in countdown(high(g), 0):
if g[i].status != undefined:
c.thisModule = i
for p in allNodes(g[i].fromDisk.topLevel):
aliveCode(c, g, g[i].fromDisk.topLevel, p)
followNow(c, g)
result = move(c.alive)
proc isAlive*(a: AliveSyms; module: int, item: int32): bool =
## Backends use this to query if a symbol is `alive` which means
## we need to produce (C/C++/etc) code for it.
result = a[module].contains(item)

View File

@@ -290,6 +290,9 @@ template typ*(n: NodePos): PackedItemId =
template flags*(n: NodePos): TNodeFlags =
tree.nodes[n.int].flags
template operand*(n: NodePos): int32 =
tree.nodes[n.int].operand
proc span*(tree: PackedTree; pos: int): int {.inline.} =
if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
@@ -451,3 +454,10 @@ when false:
dest.add nkStrLit, msg, n.info
copyTree(dest, tree, n)
patch dest, patchPos
iterator allNodes*(tree: PackedTree): NodePos =
var p = 0
while p < tree.len:
yield NodePos(p)
let s = span(tree, p)
inc p, s

View File

@@ -31,6 +31,7 @@ type
bodiesSection
symsSection
typesSection
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
RodFileError* = enum
ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
@@ -134,7 +135,7 @@ proc loadHeader*(f: var RodFile) =
proc storeSection*(f: var RodFile; s: RodSection) =
if f.err != ok: return
assert f.currentSection == pred s
assert f.currentSection < s
f.currentSection = s
storePrim(f, s)

View File

@@ -107,7 +107,7 @@ proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModule): LitId =
c.lastLit = result
assert result != LitId(0)
proc toFileIndex(x: LitId; m: PackedModule; config: ConfigRef): FileIndex =
proc toFileIndex*(x: LitId; m: PackedModule; config: ConfigRef): FileIndex =
result = msgs.fileInfoIdx(config, AbsoluteFile m.sh.strings[x])
proc includesIdentical(m: var PackedModule; config: ConfigRef): bool =
@@ -280,16 +280,19 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel)
storeNode(p, t, n)
for op, s in pairs t.attachedOps:
c.addMissing s
p.attachedOps[op] = s.safeItemId(c, m)
when false:
for op, s in pairs t.attachedOps:
c.addMissing s
p.attachedOps[op] = s.safeItemId(c, m)
p.typeInst = t.typeInst.storeType(c, m)
for kid in items t.sons:
p.types.add kid.storeType(c, m)
for i, s in items t.methods:
c.addMissing s
p.methods.add (i, s.safeItemId(c, m))
when false:
for i, s in items t.methods:
c.addMissing s
p.methods.add (i, s.safeItemId(c, m))
c.addMissing t.sym
p.sym = t.sym.safeItemId(c, m)
c.addMissing t.owner
@@ -568,11 +571,11 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
type
PackedDecoder* = object
lastModule*: int
lastLit*: LitId
lastFile*: FileIndex # remember the last lookup entry.
lastModule: int
lastLit: LitId
lastFile: FileIndex # remember the last lookup entry.
config*: ConfigRef
cache: IdentCache
cache*: IdentCache
type
ModuleStatus* = enum
@@ -596,7 +599,7 @@ type
proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType
proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym
proc toFileIndexCached(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; f: LitId): FileIndex =
proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule: int; f: LitId): FileIndex =
if c.lastLit == f and c.lastModule == thisModule:
result = c.lastFile
else:
@@ -611,8 +614,8 @@ proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; thisModul
result = TLineInfo(line: x.line, col: x.col,
fileIndex: toFileIndexCached(c, g, thisModule, x.file))
proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
let k = n.kind
if k == nkNilRodNode:
return nil
@@ -647,6 +650,14 @@ proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
for n0 in sonsReadonly(tree, n):
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
proc initPackedDecoder*(config: ConfigRef; cache: IdentCache): PackedDecoder =
result = PackedDecoder(
lastModule: int32(-1),
lastLit: LitId(0),
lastFile: FileIndex(-1),
config: config,
cache: cache)
proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
tree: PackedTree; n: NodePos): PNode =
# do not load the body of the proc. This will be done later in
@@ -761,14 +772,16 @@ proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
t: PackedType; si, item: int32; result: PType) =
result.sym = loadSym(c, g, si, t.sym)
result.owner = loadSym(c, g, si, t.owner)
for op, item in pairs t.attachedOps:
result.attachedOps[op] = loadSym(c, g, si, item)
when false:
for op, item in pairs t.attachedOps:
result.attachedOps[op] = loadSym(c, g, si, item)
result.typeInst = loadType(c, g, si, t.typeInst)
for son in items t.types:
result.sons.add loadType(c, g, si, son)
loadAstBody(t, n)
for gen, id in items t.methods:
result.methods.add((gen, loadSym(c, g, si, id)))
when false:
for gen, id in items t.methods:
result.methods.add((gen, loadSym(c, g, si, id)))
proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType =
if t == nilItemId:
@@ -819,11 +832,8 @@ proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
lastFile: FileIndex(-1),
config: conf,
cache: cache)
var p = 0
while p < m.fromDisk.toReplay.len:
m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, NodePos p)
let s = span(m.fromDisk.toReplay, p)
inc p, s
for p in allNodes(m.fromDisk.toReplay):
m.module.ast.add loadNodes(decoder, g, int(fileIdx), m.fromDisk.toReplay, p)
proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
fileIdx: FileIndex): bool =
@@ -979,6 +989,10 @@ proc interfaceSymbol*(config: ConfigRef, cache: IdentCache;
let values = g[int module].iface.getOrDefault(name)
result = loadSym(decoder, g, int(module), values[0])
proc idgenFromLoadedModule*(m: LoadedModule): IdGenerator =
IdGenerator(module: m.module.itemId.module, symId: int32 m.fromDisk.sh.syms.len,
typeId: int32 m.fromDisk.sh.types.len)
# ------------------------- .rod file viewer ---------------------------------
proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =

View File

@@ -260,13 +260,13 @@ proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
result = newTree(nkCall, newSymNode(op), addrExp)
proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
var op = t.attachedOps[kind]
var op = getAttachedOp(c.graph, t, kind)
if op == nil or op.ast[genericParamsPos].kind != nkEmpty:
# give up and find the canonical type instead:
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
let canon = c.graph.canonTypes.getOrDefault(h)
if canon != nil:
op = canon.attachedOps[kind]
op = getAttachedOp(c.graph, canon, kind)
if op == nil:
#echo dest.typ.id
globalError(c.graph.config, dest.info, "internal error: '" & AttachedOpToStr[kind] &
@@ -287,9 +287,9 @@ proc genDestroy(c: var Con; dest: PNode): PNode =
proc canBeMoved(c: Con; t: PType): bool {.inline.} =
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
if optOwnedRefs in c.graph.config.globalOptions:
result = t.kind != tyRef and t.attachedOps[attachedSink] != nil
result = t.kind != tyRef and getAttachedOp(c.graph, t, attachedSink) != nil
else:
result = t.attachedOps[attachedSink] != nil
result = getAttachedOp(c.graph, t, attachedSink) != nil
proc isNoInit(dest: PNode): bool {.inline.} =
result = dest.kind == nkSym and sfNoInit in dest.sym.flags
@@ -302,7 +302,7 @@ proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode =
result = newTree(nkFastAsgn, dest, ri)
else:
let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
if t.attachedOps[attachedSink] != nil:
if getAttachedOp(c.graph, t, attachedSink) != nil:
result = c.genOp(t, attachedSink, dest, ri)
result.add ri
else:
@@ -375,8 +375,8 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
let objType = leDotExpr[0].typ
if hasDestructor(c, objType):
if objType.attachedOps[attachedDestructor] != nil and
sfOverriden in objType.attachedOps[attachedDestructor].flags:
if getAttachedOp(c.graph, objType, attachedDestructor) != nil and
sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""")
result.add newTree(nkFastAsgn, le, tmp)
@@ -1095,7 +1095,7 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n:
echo n
if optCursorInference in g.config.options:
computeCursors(owner, n, g.config)
computeCursors(owner, n, g)
var scope: Scope
let body = p(n, c, scope, normal)

View File

@@ -29,6 +29,10 @@ type
c: PContext # c can be nil, then we are called from lambdalifting!
idgen: IdGenerator
template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor)
template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn)
template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator): PSym
@@ -42,8 +46,9 @@ proc at(a, i: PNode, elemType: PType): PNode =
result[1] = i
result.typ = elemType
proc destructorOverriden(t: PType): bool =
t.attachedOps[attachedDestructor] != nil and sfOverriden in t.attachedOps[attachedDestructor].flags
proc destructorOverriden(g: ModuleGraph; t: PType): bool =
let op = getAttachedOp(g, t, attachedDestructor)
op != nil and sfOverriden in op.flags
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i in 0..<t.len:
@@ -316,7 +321,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
field: var PSym): bool =
if optSeqDestructors in c.g.config.globalOptions:
var op = field
let destructorOverriden = destructorOverriden(t)
let destructorOverriden = destructorOverriden(c.g, t)
if op != nil and op != c.fn and
(sfOverriden in op.flags or destructorOverriden):
if sfError in op.flags:
@@ -368,7 +373,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
if op.ast[genericParamsPos].kind != nkEmpty:
# patch generic destructor:
op = instantiateGeneric(c, op, t, t.typeInst)
t.attachedOps[attachedDestructor] = op
setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
if op == nil and (useNoGc(c, t) or requiresDestructor(c, t)):
op = produceSym(c.g, c.c, t, attachedDestructor, c.info, c.idgen)
@@ -392,7 +397,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
if op.ast[genericParamsPos].kind != nkEmpty:
# patch generic destructor:
op = instantiateGeneric(c, op, t, t.typeInst)
t.attachedOps[attachedDestructor] = op
setAttachedOp(c.g, c.idgen.module, t, attachedDestructor, op)
#markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op)
@@ -400,11 +405,17 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
result = true
#result = addDestructorCall(c, t, body, x)
of attachedAsgn, attachedSink, attachedTrace:
result = considerAsgnOrSink(c, t, body, x, y, t.attachedOps[c.kind])
var op = getAttachedOp(c.g, t, c.kind)
result = considerAsgnOrSink(c, t, body, x, y, op)
if op != nil:
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
of attachedDispose:
result = considerAsgnOrSink(c, t, body, x, nil, t.attachedOps[c.kind])
var op = getAttachedOp(c.g, t, c.kind)
result = considerAsgnOrSink(c, t, body, x, nil, op)
if op != nil:
setAttachedOp(c.g, c.idgen.module, t, c.kind, op)
of attachedDeepCopy:
let op = t.attachedOps[attachedDeepCopy]
let op = getAttachedOp(c.g, t, attachedDeepCopy)
if op != nil:
#markUsed(c.g.config, c.info, op, c.g.usageSym)
onUse(c.info, op)
@@ -523,13 +534,15 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
doAssert t.destructor != nil
body.add destructorCall(c, t.destructor, x)
of attachedTrace:
if t.attachedOps[c.kind] == nil:
let op = getAttachedOp(c.g, t, c.kind)
if op == nil:
return # protect from recursion
body.add newHookCall(c, t.attachedOps[c.kind], x, y)
body.add newHookCall(c, op, x, y)
of attachedDispose:
if t.attachedOps[c.kind] == nil:
let op = getAttachedOp(c.g, t, c.kind)
if op == nil:
return # protect from recursion
body.add newHookCall(c, t.attachedOps[c.kind], x, nil)
body.add newHookCall(c, op, x, nil)
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
@@ -890,10 +903,10 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
idgen: IdGenerator): PSym =
assert typ.kind == tyDistinct
let baseType = typ[0]
if baseType.attachedOps[kind] == nil:
if getAttachedOp(g, baseType, kind) == nil:
discard produceSym(g, c, baseType, kind, info, idgen)
typ.attachedOps[kind] = baseType.attachedOps[kind]
result = typ.attachedOps[kind]
result = getAttachedOp(g, baseType, kind)
setAttachedOp(g, idgen.module, typ, kind, result)
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
info: TLineInfo; idgen: IdGenerator): PSym =
@@ -936,7 +949,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
if typ.kind == tyDistinct:
return produceSymDistinctType(g, c, typ, kind, info, idgen)
result = typ.attachedOps[kind]
result = getAttachedOp(g, typ, kind)
if result == nil:
result = symPrototype(g, typ, typ.owner, kind, info, idgen)
@@ -949,12 +962,12 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
else: newSymNode(result.typ.n[2].sym)
# register this operation already:
typ.attachedOps[kind] = result
setAttachedOpPartial(g, idgen.module, typ, kind, result)
if kind == attachedSink and destructorOverriden(typ):
if kind == attachedSink and destructorOverriden(g, typ):
## compiler can use a combination of `=destroy` and memCopy for sink op
dest.flags.incl sfCursor
result.ast[bodyPos].add newOpCall(a, typ.attachedOps[attachedDestructor], d[0])
result.ast[bodyPos].add newOpCall(a, getAttachedOp(g, typ, attachedDestructor), d[0])
result.ast[bodyPos].add newAsgnStmt(d, src)
else:
var tk: TTypeKind
@@ -970,6 +983,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
else:
fillBody(a, typ, result.ast[bodyPos], d, src)
if not a.canRaise: incl result.flags, sfNeverRaises
completePartialOp(g, idgen.module, typ, kind, result)
proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
@@ -1002,30 +1016,34 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id
if n.kind in nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
let t = n[1].typ.skipTypes(abstractVar)
if t.destructor == nil:
if getAttachedOp(g, t, attachedDestructor) == nil:
discard produceSym(g, c, t, attachedDestructor, info, idgen)
if t.destructor != nil:
if t.destructor.ast[genericParamsPos].kind != nkEmpty:
let op = getAttachedOp(g, t, attachedDestructor)
if op != nil:
if op.ast[genericParamsPos].kind != nkEmpty:
internalError(g.config, info, "resolved destructor is generic")
if t.destructor.magic == mDestroy:
if op.magic == mDestroy:
internalError(g.config, info, "patching mDestroy with mDestroy?")
n[0] = newSymNode(t.destructor)
n[0] = newSymNode(op)
for x in n: patchBody(g, c, x, info, idgen)
template inst(field, t, idgen) =
if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty:
proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: IdGenerator;
info: TLineInfo) =
let op = getAttachedOp(g, t, kind)
if op != nil and op.ast != nil and op.ast[genericParamsPos].kind != nkEmpty:
if t.typeInst != nil:
var a: TLiftCtx
a.info = info
a.g = g
a.kind = k
a.kind = kind
a.c = c
a.idgen = idgen
field = instantiateGeneric(a, field, t, t.typeInst)
if field.ast != nil:
patchBody(g, c, field.ast, info, a.idgen)
let opInst = instantiateGeneric(a, op, t, t.typeInst)
if opInst.ast != nil:
patchBody(g, c, opInst.ast, info, a.idgen)
setAttachedOp(g, idgen.module, t, kind, opInst)
else:
localError(g.config, info, "unresolved generic parameter")
@@ -1062,24 +1080,26 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
else: attachedSink
# bug #15122: We need to produce all prototypes before entering the
# mind boggling recursion. Hacks like these imply we shoule rewrite
# mind boggling recursion. Hacks like these imply we should rewrite
# this module.
var generics: array[attachedDestructor..attachedDispose, bool]
for k in attachedDestructor..lastAttached:
generics[k] = canon.attachedOps[k] != nil
generics[k] = getAttachedOp(g, canon, k) != nil
if not generics[k]:
canon.attachedOps[k] = symPrototype(g, canon, canon.owner, k, info, idgen)
setAttachedOp(g, idgen.module, canon, k,
symPrototype(g, canon, canon.owner, k, info, idgen))
# we generate the destructor first so that other operators can depend on it:
for k in attachedDestructor..lastAttached:
if not generics[k]:
discard produceSym(g, c, canon, k, info, idgen)
else:
inst(canon.attachedOps[k], canon, idgen)
inst(g, c, canon, k, idgen, info)
if canon != orig:
orig.attachedOps[k] = canon.attachedOps[k]
setAttachedOp(g, idgen.module, orig, k, getAttachedOp(g, canon, k))
if not isTrival(orig.destructor):
if not isTrival(getAttachedOp(g, orig, attachedDestructor)):
#or not isTrival(orig.assignment) or
# not isTrival(orig.sink):
orig.flags.incl tfHasAsgn
# ^ XXX Breaks IC!

View File

@@ -21,6 +21,7 @@ import
modules,
modulegraphs, tables, lineinfos, pathutils, vmprofiler
import ic / cbackend
from ic / to_packed_ast import rodViewer
when not defined(leanCompiler):
@@ -73,14 +74,15 @@ proc commandCompileToC(graph: ModuleGraph) =
setOutFile(conf)
extccomp.initVars(conf)
semanticPasses(graph)
registerPass(graph, cgenPass)
if conf.symbolFiles == disabledSf:
registerPass(graph, cgenPass)
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
let proj = changeFileExt(conf.projectFull, "")
if not changeDetectedViaJsonBuildInstructions(conf, proj):
# nothing changed
graph.config.notes = graph.config.mainPackageNotes
return
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
let proj = changeFileExt(conf.projectFull, "")
if not changeDetectedViaJsonBuildInstructions(conf, proj):
# nothing changed
graph.config.notes = graph.config.mainPackageNotes
return
if not extccomp.ccHasSaneOverflow(conf):
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
@@ -88,8 +90,14 @@ proc commandCompileToC(graph: ModuleGraph) =
compileProject(graph)
if graph.config.errorCounter > 0:
return # issue #9933
cgenWriteModules(graph.backend, conf)
if conf.cmd != cmdTcc:
if conf.symbolFiles == disabledSf:
cgenWriteModules(graph.backend, conf)
else:
generateCode(graph)
# graph.backend can be nil under IC when nothing changed at all:
if graph.backend != nil:
cgenWriteModules(graph.backend, conf)
if conf.cmd != cmdTcc and graph.backend != nil:
extccomp.callCCompiler(conf)
# for now we do not support writing out a .json file with the build instructions when HCR is on
if not conf.hcrOn:

View File

@@ -27,9 +27,20 @@ type
pureEnums*: seq[PSym]
interf: TStrTable
Operators* = object
opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym
opAdd*, opSub*, opMul*, opDiv*, opLen*: PSym
ModuleGraph* = ref object
ifaces*: seq[Iface] ## indexed by int32 fileIdx
packed*: PackedModuleGraph
typeInstCache*: Table[ItemId, seq[PType]] # A symbol's ItemId.
procInstCache*: Table[ItemId, seq[PInstantiation]] # A symbol's ItemId.
attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc.
methodsPerType*: Table[ItemId, seq[(int, PSym)]] # Type ID, attached methods
enumToStringProcs*: Table[ItemId, PSym]
startupPackedConfig*: PackedConfig
packageSyms*: TStrTable
deps*: IntSet # the dependency graph or potentially its transitive closure.
@@ -71,6 +82,7 @@ type
strongSemCheck*: proc (graph: ModuleGraph; owner: PSym; body: PNode) {.nimcall.}
compatibleProps*: proc (graph: ModuleGraph; formal, actual: PType): bool {.nimcall.}
idgen*: IdGenerator
operators*: Operators
TPassContext* = object of RootObj # the pass's context
idgen*: IdGenerator
@@ -85,6 +97,67 @@ type
close: TPassClose,
isFrontend: bool]
iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
if g.typeInstCache.contains(s.itemId):
let x = addr(g.typeInstCache[s.itemId])
for t in x[]:
yield t
proc addToGenericCache*(g: ModuleGraph; module: int; s: PSym; inst: PType) =
g.typeInstCache.mgetOrPut(s.itemId, @[]).add inst
# XXX Also add to the packed module!
iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
if g.procInstCache.contains(s.itemId):
let x = addr(g.procInstCache[s.itemId])
for t in x[]:
yield t
proc addToGenericProcCache*(g: ModuleGraph; module: int; s: PSym; inst: PInstantiation) =
g.procInstCache.mgetOrPut(s.itemId, @[]).add inst
# XXX Also add to the packed module!
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
## returns the requested attached operation for type `t`. Can return nil
## if no such operation exists.
result = g.attachedOps[op].getOrDefault(t.itemId)
proc setAttachedOp*(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] = value
# XXX Also add to the packed module!
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] = value
# XXX Also add to the packed module!
proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
discard "To implement"
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
result = g.enumToStringProcs.getOrDefault(t.itemId)
assert result != nil
proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
g.enumToStringProcs[t.itemId] = value
iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
for a, b in items g.methodsPerType.getOrDefault(t.itemId):
yield (a, b)
proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
g.methodsPerType.mgetOrPut(t.itemId, @[]).add (col, m)
proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool =
let op = getAttachedOp(g, t, attachedAsgn)
result = op != nil and sfError in op.flags
proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
for k in low(TTypeAttachedOp)..high(TTypeAttachedOp):
let op = getAttachedOp(g, src, k)
if op != nil:
setAttachedOp(g, module, dest, k, op)
const
cb64 = [
@@ -241,6 +314,22 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[])
initStrTable(g.ifaces[m.position].interf)
proc initOperators(g: ModuleGraph): Operators =
# These are safe for IC.
result.opLe = createMagic(g, "<=", mLeI)
result.opLt = createMagic(g, "<", mLtI)
result.opAnd = createMagic(g, "and", mAnd)
result.opOr = createMagic(g, "or", mOr)
result.opIsNil = createMagic(g, "isnil", mIsNil)
result.opEq = createMagic(g, "==", mEqI)
result.opAdd = createMagic(g, "+", mAddI)
result.opSub = createMagic(g, "-", mSubI)
result.opMul = createMagic(g, "*", mMulI)
result.opDiv = createMagic(g, "div", mDivI)
result.opLen = createMagic(g, "len", mLengthSeq)
result.opNot = createMagic(g, "not", mNot)
result.opContains = createMagic(g, "contains", mInSet)
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
result = ModuleGraph()
# A module ID of -1 means that the symbol is not attached to a module at all,
@@ -265,6 +354,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
result.cacheTables = initTable[string, BTree[string, PNode]]()
result.canonTypes = initTable[SigHash, PType]()
result.symBodyHashes = initTable[int, SigHash]()
result.operators = initOperators(result)
proc resetAllModules*(g: ModuleGraph) =
initStrTable(g.packageSyms)

View File

@@ -460,7 +460,7 @@ proc numLines*(conf: ConfigRef, fileIdx: FileIndex): int =
if result == 0:
try:
for line in lines(toFullPathConsiderDirty(conf, fileIdx).string):
addSourceLine conf, fileIdx, line.string
addSourceLine conf, fileIdx, line
except IOError:
discard
result = conf.m.fileInfos[fileIdx.int32].lines.len

View File

@@ -721,9 +721,9 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile,
result = subdir / RelativeFile f.string.splitPath.tail
#echo "completeGeneratedFilePath(", f, ") = ", result
proc toRodFile*(conf: ConfigRef; f: AbsoluteFile): AbsoluteFile =
proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile =
result = changeFileExt(completeGeneratedFilePath(conf,
withPackageName(conf, f)), RodExt)
withPackageName(conf, f)), ext)
proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile =
for it in conf.searchPaths:

View File

@@ -17,10 +17,7 @@ import
intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
lowerings, plugins/active, lineinfos, strtabs, int128,
isolation_check, typeallowed
from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward,
systemModuleSym, semtab, getBody, someSym, allSyms
isolation_check, typeallowed, modulegraphs, enumtostr
when defined(nimfix):
import nimfix/prettybase
@@ -84,7 +81,7 @@ proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyUntyped:
changeType(c, x, formal, check=true)
result = arg
result = skipHiddenSubConv(result, c.idgen)
result = skipHiddenSubConv(result, c.graph, c.idgen)
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
@@ -138,7 +135,10 @@ proc commonType*(c: PContext; x, y: PType): PType =
let aEmpty = isEmptyContainer(a[i])
let bEmpty = isEmptyContainer(b[i])
if aEmpty != bEmpty:
if nt.isNil: nt = copyType(a, nextTypeId(c.idgen), a.owner)
if nt.isNil:
nt = copyType(a, nextTypeId(c.idgen), a.owner)
copyTypeProps(c.graph, c.idgen.module, nt, a)
nt[i] = if aEmpty: b[i] else: a[i]
if not nt.isNil: result = nt
#elif b[idx].kind == tyEmpty: return x

View File

@@ -685,9 +685,9 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
var x: PType
if param.typ.kind == tyVar:
x = newTypeS(param.typ.kind, c)
x.addSonSkipIntLit(t.baseOfDistinct(c.idgen), c.idgen)
x.addSonSkipIntLit(t.baseOfDistinct(c.graph, c.idgen), c.idgen)
else:
x = t.baseOfDistinct(c.idgen)
x = t.baseOfDistinct(c.graph, c.idgen)
call.add(newNodeIT(nkEmpty, fn.info, x))
if hasDistinct:
let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}

View File

@@ -558,6 +558,7 @@ proc saveRodFile*(c: PContext) =
addPragmaComputation(c, n)
if sfSystemModule in c.module.flags:
c.graph.systemModuleComplete = true
c.idgen.sealed = true # no further additions are allowed
if c.config.symbolFiles != stressTest:
# For stress testing we seek to reload the symbols from memory. This
# way much of the logic is tested but the test is reproducible as it does

View File

@@ -95,9 +95,9 @@ proc sameInstantiation(a, b: TInstantiation): bool =
ExactGcSafety}): return
result = true
proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation;
id: CompilesId): PSym =
for inst in genericSym.procInstCache:
for inst in procInstCacheItems(g, genericSym):
if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]):
return inst.sym
@@ -369,7 +369,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
if tfTriggersCompileTime in result.typ.flags:
incl(result.flags, sfCompileTime)
n[genericParamsPos] = c.graph.emptyNode
var oldPrc = genericCacheGet(fn, entry[], c.compilesContextId)
var oldPrc = genericCacheGet(c.graph, fn, entry[], c.compilesContextId)
if oldPrc == nil:
# we MUST not add potentially wrong instantiations to the caching mechanism.
# This means recursive instantiations behave differently when in
@@ -378,7 +378,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
#if c.compilesContextId == 0:
rawHandleSelf(c, result)
entry.compilesId = c.compilesContextId
fn.procInstCache.add(entry)
addToGenericProcCache(c.graph, c.module.position, fn, entry)
c.generics.add(makeInstPair(fn, entry))
if n[pragmasPos].kind != nkEmpty:
pragma(c, result, n[pragmasPos], allRoutinePragmas)

View File

@@ -382,6 +382,8 @@ proc semUnown(c: PContext; n: PNode): PNode =
let b = unownedType(c, t[^1])
if b != t[^1]:
result = copyType(t, nextTypeId c.idgen, t.owner)
copyTypeProps(c.graph, c.idgen.module, result, t)
result[^1] = b
result.flags.excl tfHasOwned
else:
@@ -541,7 +543,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
# check if we converted this finalizer into a destructor already:
let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
if t != nil and t.attachedOps[attachedDestructor] != nil and t.attachedOps[attachedDestructor].owner == fin:
if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and
getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
discard "already turned this one into a finalizer"
else:
bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
@@ -549,8 +552,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mDestroy:
result = n
let t = n[1].typ.skipTypes(abstractVar)
if t.destructor != nil:
result[0] = newSymNode(t.destructor)
let op = getAttachedOp(c.graph, t, attachedDestructor)
if op != nil:
result[0] = newSymNode(op)
of mUnown:
result = semUnown(c, n)
of mExists, mForall:

View File

@@ -81,7 +81,7 @@ proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx =
result.slices = @[]
result.args = @[]
result.guards.s = @[]
result.guards.o = initOperators(g)
result.guards.g = g
result.graph = g
proc lookupSlot(c: AnalysisCtx; s: PSym): int =
@@ -138,7 +138,7 @@ proc checkLe(c: AnalysisCtx; a, b: PNode) =
proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
checkLe(c, lowBound(c.graph.config, arr), idx)
checkLe(c, idx, highBound(c.graph.config, arr, c.guards.o))
checkLe(c, idx, highBound(c.graph.config, arr, c.graph.operators))
proc addLowerBoundAsFacts(c: var AnalysisCtx) =
for v in c.locals:
@@ -147,8 +147,8 @@ proc addLowerBoundAsFacts(c: var AnalysisCtx) =
proc addSlice(c: var AnalysisCtx; n: PNode; x, le, ri: PNode) =
checkLocal(c, n)
let le = le.canon(c.guards.o)
let ri = ri.canon(c.guards.o)
let le = le.canon(c.graph.operators)
let ri = ri.canon(c.graph.operators)
# perform static bounds checking here; and not later!
let oldState = c.guards.s.len
addLowerBoundAsFacts(c)
@@ -192,7 +192,7 @@ proc subStride(c: AnalysisCtx; n: PNode): PNode =
if isLocal(n):
let s = c.lookupSlot(n.sym)
if s >= 0 and c.locals[s].stride != nil:
result = buildAdd(n, c.locals[s].stride.intVal, c.guards.o)
result = buildAdd(n, c.locals[s].stride.intVal, c.graph.operators)
else:
result = n
elif n.safeLen > 0:
@@ -307,16 +307,16 @@ proc analyseCase(c: var AnalysisCtx; n: PNode) =
proc analyseIf(c: var AnalysisCtx; n: PNode) =
analyse(c, n[0][0])
let oldFacts = c.guards.s.len
addFact(c.guards, canon(n[0][0], c.guards.o))
addFact(c.guards, canon(n[0][0], c.graph.operators))
analyse(c, n[0][1])
for i in 1..<n.len:
let branch = n[i]
setLen(c.guards.s, oldFacts)
for j in 0..i-1:
addFactNeg(c.guards, canon(n[j][0], c.guards.o))
addFactNeg(c.guards, canon(n[j][0], c.graph.operators))
if branch.len > 1:
addFact(c.guards, canon(branch[0], c.guards.o))
addFact(c.guards, canon(branch[0], c.graph.operators))
for i in 0..<branch.len:
analyse(c, branch[i])
setLen(c.guards.s, oldFacts)
@@ -382,13 +382,13 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
# loop may never execute:
let oldState = c.locals.len
let oldFacts = c.guards.s.len
addFact(c.guards, canon(n[0], c.guards.o))
addFact(c.guards, canon(n[0], c.graph.operators))
analyse(c, n[1])
setLen(c.locals, oldState)
setLen(c.guards.s, oldFacts)
# we know after the loop the negation holds:
if not hasSubnodeWith(n[1], nkBreakStmt):
addFactNeg(c.guards, canon(n[0], c.guards.o))
addFactNeg(c.guards, canon(n[0], c.graph.operators))
dec c.inLoop
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:

View File

@@ -723,7 +723,7 @@ proc checkLe(c: PEffects; a, b: PNode) =
proc checkBounds(c: PEffects; arr, idx: PNode) =
checkLe(c, lowBound(c.config, arr), idx)
checkLe(c, idx, highBound(c.config, arr, c.guards.o))
checkLe(c, idx, highBound(c.config, arr, c.guards.g.operators))
proc checkRange(c: PEffects; value: PNode; typ: PType) =
let t = typ.skipTypes(abstractInst - {tyRange})
@@ -818,9 +818,9 @@ proc trackCall(tracked: PEffects; n: PNode) =
if opKind != -1:
# rebind type bounds operations after createTypeBoundOps call
let t = n[1].typ.skipTypes({tyAlias, tyVar})
if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]:
if a.sym != getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind)):
createTypeBoundOps(tracked, t, n.info)
let op = t.attachedOps[TTypeAttachedOp(opKind)]
let op = getAttachedOp(tracked.graph, t, TTypeAttachedOp(opKind))
if op != nil:
n[0].sym = op
@@ -1236,7 +1236,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC
t.ownerModule = s.getModule
t.init = @[]
t.guards.s = @[]
t.guards.o = initOperators(g)
t.guards.g = g
when defined(drnim):
t.currOptions = g.config.options + s.options - {optStaticBoundsCheck}
else:
@@ -1310,7 +1310,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
var goals: set[Goal] = {}
if strictFuncs in c.features: goals.incl constParameters
if views in c.features: goals.incl borrowChecking
var partitions = computeGraphPartitions(s, body, g.config, goals)
var partitions = computeGraphPartitions(s, body, g, goals)
if not t.hasSideEffect and t.hasDangerousAssign:
t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo)
if views in c.features:
@@ -1344,7 +1344,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
when defined(useDfa):
if s.name.s == "testp":
dataflowAnalysis(s, body)
when false: trackWrites(s, body)
if strictNotNil in c.features and s.kind == skProc:
checkNil(s, body, g.config, c.idgen)

View File

@@ -1693,12 +1693,13 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
else: break
if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
obj = canonType(c, obj)
if obj.attachedOps[op] == s:
let ao = getAttachedOp(c.graph, obj, op)
if ao == s:
discard "forward declared destructor"
elif obj.attachedOps[op].isNil and tfCheckedForDestructor notin obj.flags:
obj.attachedOps[op] = s
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
setAttachedOp(c.graph, c.module.position, obj, op, s)
else:
prevDestructor(c, obj.attachedOps[op], obj, n.info)
prevDestructor(c, ao, obj, n.info)
noError = true
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
@@ -1726,7 +1727,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
elif t.kind == tyGenericInvocation: t = t[0]
else: break
if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
if t.attachedOps[attachedDeepCopy].isNil: t.attachedOps[attachedDeepCopy] = s
if getAttachedOp(c.graph, t, attachedDeepCopy).isNil:
setAttachedOp(c.graph, c.module.position, t, attachedDeepCopy, s)
else:
localError(c.config, n.info, errGenerated,
"cannot bind another 'deepCopy' to: " & typeToString(t))
@@ -1766,12 +1768,13 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
obj = canonType(c, obj)
#echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj)
let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink
if obj.attachedOps[k] == s:
let ao = getAttachedOp(c.graph, obj, k)
if ao == s:
discard "forward declared op"
elif obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags:
obj.attachedOps[k] = s
elif ao.isNil and tfCheckedForDestructor notin obj.flags:
setAttachedOp(c.graph, c.module.position, obj, k, s)
else:
prevDestructor(c, obj.attachedOps[k], obj, n.info)
prevDestructor(c, ao, obj, n.info)
if obj.owner.getModule != s.getModule:
localError(c.config, n.info, errGenerated,
"type bound operation `" & name & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
@@ -1827,7 +1830,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
tyAlias, tySink, tyOwned})
if x.kind == tyObject and t.len-1 == n[genericParamsPos].len:
foundObj = true
x.methods.add((col,s))
addMethodToGeneric(c.graph, c.module.position, x, col, s)
message(c.config, n.info, warnDeprecated, "generic methods are deprecated")
#if not foundObj:
# message(c.config, n.info, warnDeprecated, "generic method not attachable to object type is deprecated")

View File

@@ -154,6 +154,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
addPureEnum(c, result.sym)
if tfNotNil in e.typ.flags and not hasNull:
result.flags.incl tfRequiresInit
setToStringProc(c.graph, result, genEnumToStrProc(result, n.info, c.graph, c.idgen))
proc semSet(c: PContext, n: PNode, prev: PType): PType =
result = newOrPrevType(tySet, prev, c)
@@ -1117,6 +1118,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyGenericInst:
if paramType.lastSon.kind == tyUserTypeClass:
var cp = copyType(paramType, nextTypeId c.idgen, getCurrOwner(c))
copyTypeProps(c.graph, c.idgen.module, cp, paramType)
cp.kind = tyUserTypeClassInst
return addImplicitGeneric(c, cp, paramTypId, info, genericParams, paramName)
@@ -1530,6 +1533,7 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
proc freshType(c: PContext; res, prev: PType): PType {.inline.} =
if prev.isNil:
result = copyType(res, nextTypeId c.idgen, res.owner)
copyTypeProps(c.graph, c.idgen.module, result, res)
else:
result = res
@@ -1843,7 +1847,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mExpr:
result = semTypeNode(c, n[0], nil)
if result != nil:
let old = result
result = copyType(result, nextTypeId c.idgen, getCurrOwner(c))
copyTypeProps(c.graph, c.idgen.module, result, old)
for i in 1..<n.len:
result.rawAddSon(semTypeNode(c, n[i], nil))
of mDistinct:
@@ -2117,12 +2123,16 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
typ.flags.incl tfGenericTypeParam
for j in 0..<a.len-2:
let finalType = if j == 0: typ
else: copyType(typ, nextTypeId c.idgen, typ.owner)
# it's important the we create an unique
# type for each generic param. the index
# of the parameter will be stored in the
# attached symbol.
var finalType: PType
if j == 0:
finalType = typ
else:
finalType = copyType(typ, nextTypeId c.idgen, typ.owner)
copyTypeProps(c.graph, c.idgen.module, finalType, typ)
# it's important the we create an unique
# type for each generic param. the index
# of the parameter will be stored in the
# attached symbol.
var paramName = a[j]
var covarianceFlag = tfUnresolved

View File

@@ -10,7 +10,7 @@
# This module does the instantiation of generic types.
import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
lineinfos
lineinfos, modulegraphs
const tfInstClearedFlags = {tfHasMeta, tfUnresolved}
@@ -30,15 +30,12 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
if t[0].kind != tyObject or tfFinal in t[0].flags:
localError(info, errInheritanceOnlyWithNonFinalObjects)
proc searchInstTypes*(key: PType): PType =
proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
let genericTyp = key[0]
if not (genericTyp.kind == tyGenericBody and
key[0] == genericTyp and genericTyp.sym != nil): return
when not defined(nimNoNilSeqs):
if genericTyp.sym.typeInstCache == nil: return
for inst in genericTyp.sym.typeInstCache:
for inst in typeInstCacheItems(g, genericTyp.sym):
if inst.id == key.id: return inst
if inst.len < key.len:
# XXX: This happens for prematurely cached
@@ -57,14 +54,12 @@ proc searchInstTypes*(key: PType): PType =
return inst
proc cacheTypeInst*(inst: PType) =
# XXX: add to module's generics
# update the refcount
proc cacheTypeInst(g: ModuleGraph; moduleId: int; inst: PType) =
let gt = inst[0]
let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
if t.kind in {tyStatic, tyError, tyGenericParam} + tyTypeClasses:
return
gt.sym.typeInstCache.add(inst)
addToGenericCache(g, moduleId, gt.sym, inst)
type
LayeredIdTable* = ref object
@@ -306,6 +301,7 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
result = t.exactReplica
else:
result = copyType(t, nextTypeId(cl.c.idgen), t.owner)
copyTypeProps(cl.c.graph, cl.c.idgen.module, result, t)
#cl.typeMap.topLayer.idTablePut(result, t)
if cl.allowMetaTypes: return
@@ -332,7 +328,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t))
else:
result = searchInstTypes(t)
result = searchInstTypes(cl.c.graph, t)
if result != nil and sameFlags(result, t):
when defined(reportCacheHits):
@@ -351,7 +347,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if header != t:
# search again after first pass:
result = searchInstTypes(header)
result = searchInstTypes(cl.c.graph, header)
if result != nil and sameFlags(result, t):
when defined(reportCacheHits):
echo "Generic instantiation cached ", typeToString(result), " for ",
@@ -368,7 +364,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
# we need to add the candidate here, before it's fully instantiated for
# recursive instantions:
if not cl.allowMetaTypes:
cacheTypeInst(result)
cacheTypeInst(cl.c.graph, cl.c.module.position, result)
else:
idTablePut(cl.localCache, t, result)
@@ -408,13 +404,13 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
rawAddSon(result, newbody)
checkPartialConstructedType(cl.c.config, cl.info, newbody)
let dc = newbody.attachedOps[attachedDeepCopy]
if not cl.allowMetaTypes:
if dc != nil and sfFromGeneric notin newbody.attachedOps[attachedDeepCopy].flags:
let dc = cl.c.graph.getAttachedOp(newbody, attachedDeepCopy)
if dc != nil and sfFromGeneric notin dc.flags:
# 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*:
newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
attachedDeepCopy, 1)
cl.c.graph.setAttachedOp(cl.c.module.position, newbody, attachedDeepCopy,
cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, attachedDeepCopy, 1))
if newbody.typeInst == nil:
# doAssert newbody.typeInst == nil
newbody.typeInst = result
@@ -436,8 +432,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if tfFromGeneric notin mm.flags:
# bug #5479, prevent endless recursions here:
incl mm.flags, tfFromGeneric
let methods = mm.methods
for col, meth in items(methods):
for col, meth in methodsForGeneric(cl.c.graph, mm):
# we instantiate the known methods belonging to that type, this causes
# them to be registered and that's enough, so we 'discard' the result.
discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,

View File

@@ -496,7 +496,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
of tyOpenArray, tyVarargs:
result = transform(c, n[1])
#result = transformSons(c, n)
result.typ = takeType(n.typ, n[1].typ, c.idgen)
result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen)
#echo n.info, " came here and produced ", typeToString(result.typ),
# " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
of tyCString:
@@ -612,6 +612,8 @@ proc isSimpleIteratorVar(c: PTransf; iter: PSym): bool =
rec(getBody(c.graph, iter), iter, dangerousYields)
result = dangerousYields == 0
template destructor(t: PType): PSym = getAttachedOp(c.graph, t, attachedDestructor)
proc transformFor(c: PTransf, n: PNode): PNode =
# generate access statements for the parameters (unless they are constant)
# put mapping from formal parameters to actual parameters

View File

@@ -11,7 +11,7 @@
import
intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options,
lineinfos, int128
lineinfos, int128, modulegraphs
type
TPreferedDesc* = enum
@@ -1307,11 +1307,12 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil)
proc baseOfDistinct*(t: PType; idgen: IdGenerator): PType =
proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
if t.kind == tyDistinct:
result = t[0]
else:
result = copyType(t, nextTypeId idgen, t.owner)
copyTypeProps(g, idgen.module, result, t)
var parent: PType = nil
var it = result
while it.kind in {tyPtr, tyRef, tyOwned}:
@@ -1449,7 +1450,7 @@ proc isEmptyContainer*(t: PType): bool =
of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon)
else: result = false
proc takeType*(formal, arg: PType; idgen: IdGenerator): PType =
proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType =
# param: openArray[string] = []
# [] is an array constructor of length 0 of type string!
if arg.kind == tyNil:
@@ -1458,6 +1459,7 @@ proc takeType*(formal, arg: PType; idgen: IdGenerator): PType =
elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
arg.isEmptyContainer:
let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), nextTypeId(idgen), arg.owner)
copyTypeProps(g, idgen.module, a, arg)
a[ord(arg.kind == tyArray)] = formal[0]
result = a
elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind:
@@ -1465,14 +1467,14 @@ proc takeType*(formal, arg: PType; idgen: IdGenerator): PType =
else:
result = arg
proc skipHiddenSubConv*(n: PNode; idgen: IdGenerator): PNode =
proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode =
if n.kind == nkHiddenSubConv:
# param: openArray[string] = []
# [] is an array constructor of length 0 of type string!
let formal = n.typ
result = n[1]
let arg = result.typ
let dest = takeType(formal, arg, idgen)
let dest = takeType(formal, arg, g, idgen)
if dest == arg and formal.kind != tyUntyped:
#echo n.info, " came here for ", formal.typeToString
result = n

View File

@@ -28,7 +28,7 @@
## See https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm
## for a high-level description of how borrow checking works.
import ast, types, lineinfos, options, msgs, renderer, typeallowed
import ast, types, lineinfos, options, msgs, renderer, typeallowed, modulegraphs
from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr
from isolation_check import canAlias
@@ -99,7 +99,7 @@ type
unanalysableMutation: bool
inAsgnSource, inConstructor, inNoSideEffectSection: int
owner: PSym
config: ConfigRef
g: ModuleGraph
proc mutationAfterConnection(g: MutationInfo): bool {.inline.} =
#echo g.maxMutation.int, " ", g.minConnection.int, " ", g.param
@@ -509,11 +509,11 @@ proc borrowFrom(c: var Partitions; dest: PSym; src: PNode) =
let s = pathExpr(src, c.owner)
if s == nil:
localError(c.config, src.info, "cannot borrow from " & $src & ", it is not a path expression; " & url)
localError(c.g.config, src.info, "cannot borrow from " & $src & ", it is not a path expression; " & url)
elif s.kind == nkSym:
if dest.kind == skResult:
if s.sym.kind != skParam or s.sym.position != 0:
localError(c.config, src.info, "'result' must borrow from the first parameter")
localError(c.g.config, src.info, "'result' must borrow from the first parameter")
let vid = variableId(c, dest)
if vid >= 0:
@@ -539,13 +539,13 @@ proc borrowingCall(c: var Partitions; destType: PType; n: PNode; i: int) =
when false:
let isView = directViewType(destType) == immutableView
if n[0].kind == nkSym and n[0].sym.name.s == "[]=":
localError(c.config, n[i].info, "attempt to mutate an immutable view")
localError(c.g.config, n[i].info, "attempt to mutate an immutable view")
for j in i+1..<n.len:
if getMagic(n[j]) == mSlice:
borrowFrom(c, v.sym, n[j])
else:
localError(c.config, n[i].info, "cannot determine the target of the borrow")
localError(c.g.config, n[i].info, "cannot determine the target of the borrow")
proc borrowingAsgn(c: var Partitions; dest, src: PNode) =
proc mutableParameter(n: PNode): bool {.inline.} =
@@ -569,7 +569,7 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) =
mutableParameter(dest[0][0]):
discard "remains a mutable location anyhow"
else:
localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view")
localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view")
of noView: discard "nothing to do"
proc containsPointer(t: PType): bool =
@@ -594,7 +594,7 @@ proc deps(c: var Partitions; dest, src: PNode) =
for s in sources:
connect(c, t, s, dest.info)
if cursorInference in c.goals and src.kind != nkEmpty:
if cursorInference in c.goals and src.kind != nkEmpty:
let d = pathExpr(dest, c.owner)
if d != nil and d.kind == nkSym:
let vid = variableId(c, d.sym)
@@ -603,7 +603,7 @@ proc deps(c: var Partitions; dest, src: PNode) =
for s in sources:
if s == d.sym:
discard "assignments like: it = it.next are fine"
elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(s.typ):
elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(c.g, s.typ):
# do not borrow from a global variable or from something with a
# disabled assignment operator.
c.s[vid].flags.incl preventCursor
@@ -826,8 +826,8 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
else:
for child in n: computeLiveRanges(c, child)
proc computeGraphPartitions*(s: PSym; n: PNode; config: ConfigRef; goals: set[Goal]): Partitions =
result = Partitions(owner: s, config: config, goals: goals)
proc computeGraphPartitions*(s: PSym; n: PNode; g: ModuleGraph; goals: set[Goal]): Partitions =
result = Partitions(owner: s, g: g, goals: goals)
if s.kind notin {skModule, skMacro}:
let params = s.typ.n
for i in 1..<params.len:
@@ -892,8 +892,8 @@ proc checkBorrowedLocations*(par: var Partitions; body: PNode; config: ConfigRef
#if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]):
# cannotBorrow(config, s, par.graphs[par.s[rid].con.graphIndex])
proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) =
var par = computeGraphPartitions(s, n, config, {cursorInference})
proc computeCursors*(s: PSym; n: PNode; g: ModuleGraph) =
var par = computeGraphPartitions(s, n, g, {cursorInference})
for i in 0 ..< par.s.len:
let v = addr(par.s[i])
if v.flags * {ownsData, preventCursor} == {} and v.sym.kind notin {skParam, skResult} and