mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-18 21:01:38 +00:00
Merge branch 'devel' into pr_leak2_re
This commit is contained in:
@@ -25,6 +25,11 @@ type
|
||||
pfStructural ## use structural prefix-chain detection and tree-walk
|
||||
pfBidirectional ## also check reverse direction per field in nkObjConstr
|
||||
|
||||
proc isCompileTimeOnlyNode(n: PNode): bool {.inline.} =
|
||||
## `typeof` and typedesc/static values describe types at compile time; they
|
||||
## do not read the runtime location that alias analysis is protecting.
|
||||
n.kind == nkTypeOfExpr or (n.typ != nil and n.typ.isCompileTimeOnly)
|
||||
|
||||
func sameLocation(a, b: PNode): bool =
|
||||
template sameConstIndex(a, b: PNode): bool =
|
||||
a.kind in nkLiterals and b.kind in nkLiterals and a.intVal == b.intVal
|
||||
@@ -157,10 +162,13 @@ proc isPartOf*(a, b: PNode; flags: set[PartFlag] = {}): TAnalysisResult =
|
||||
##
|
||||
## x[] ?<| y depending on type
|
||||
## ```
|
||||
if a.isCompileTimeOnlyNode or b.isCompileTimeOnlyNode:
|
||||
return arNo
|
||||
|
||||
if a.kind == b.kind:
|
||||
case a.kind
|
||||
of nkSym:
|
||||
const varKinds = {skVar, skTemp, skProc, skFunc}
|
||||
const varKinds = {skVar, skTemp, skResult, skProc, skFunc}
|
||||
# same symbol: aliasing:
|
||||
if a.sym.id == b.sym.id: result = arYes
|
||||
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
|
||||
@@ -271,6 +279,11 @@ proc isPartOf*(a, b: PNode; flags: set[PartFlag] = {}): TAnalysisResult =
|
||||
of nkCallKinds:
|
||||
result = arNo
|
||||
for i in 1..<b.len:
|
||||
# A call such as `fill(typeof(result.f))` has a compile-time-only
|
||||
# argument. It must not make the object constructor look aliased with
|
||||
# `result.f`; runtime arguments remain subject to the normal analysis.
|
||||
if b[i].isCompileTimeOnlyNode:
|
||||
continue
|
||||
let res = isPartOf(a, b[i], flags)
|
||||
if res != arNo:
|
||||
result = res
|
||||
|
||||
@@ -2901,22 +2901,6 @@ proc genDestroy(p: BProc; n: PNode) =
|
||||
internalError(p.config, n.info, "destructor turned out to be not trivial")
|
||||
discard "ignore calls to the default destructor"
|
||||
|
||||
proc genDispose(p: BProc; n: PNode) =
|
||||
when false:
|
||||
let elemType = n[1].typ.skipTypes(abstractVar).elementType
|
||||
|
||||
var a: TLoc = initLocExpr(p, n[1].skipAddr)
|
||||
|
||||
if isFinal(elemType):
|
||||
if elemType.destructor != nil:
|
||||
var destroyCall = newNodeI(nkCall, n.info)
|
||||
genStmts(p, destroyCall)
|
||||
lineFmt(p, cpsStmts, "#nimRawDispose($1, NIM_ALIGNOF($2))", [rdLoc(a), getTypeDesc(p.module, elemType)])
|
||||
else:
|
||||
# ``nimRawDisposeVirtual`` calls the ``finalizer`` which is the same as the
|
||||
# destructor, but it uses the runtime type. Afterwards the memory is freed:
|
||||
lineCg(p, cpsStmts, ["#nimDestroyAndDispose($#)", rdLoc(a)])
|
||||
|
||||
proc genSlice(p: BProc; e: PNode; d: var TLoc) =
|
||||
let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.elementType,
|
||||
prepareForMutation = e[1].kind == nkHiddenDeref and
|
||||
@@ -3490,7 +3474,6 @@ proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
|
||||
m.initProc.procSec(cpsLocals).add('\t')
|
||||
m.initProc.procSec(cpsLocals).addAssignmentWithValue(sym.loc.snippet):
|
||||
m.initProc.procSec(cpsLocals).addCast(ptrType(getTypeDesc(m, sym.loc.t, dkVar))):
|
||||
var getGlobalCall: CallBuilder
|
||||
m.initProc.procSec(cpsLocals).addCall("hcrGetGlobal",
|
||||
getModuleDllPath(q, sym),
|
||||
'"' & sym.loc.snippet & '"')
|
||||
@@ -3768,7 +3751,6 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
if delayedCodegen(p.module):
|
||||
genConstStmt(p, n)
|
||||
else: # enforce addressable consts for exportc
|
||||
let m = p.module
|
||||
for it in n:
|
||||
let symNode = skipPragmaExpr(it.firstSon)
|
||||
if symNode.kind == nkSym and sfExportc in symNode.sym.flags:
|
||||
|
||||
@@ -28,9 +28,6 @@ proc detectStrVersion(m: BModule): int =
|
||||
else:
|
||||
detectVersion(strVersion, "nimStrVersion")
|
||||
|
||||
proc detectSeqVersion(m: BModule): int =
|
||||
detectVersion(seqVersion, "nimSeqVersion")
|
||||
|
||||
# ----- Version 1: GC'ed strings and seqs --------------------------------
|
||||
|
||||
proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
||||
@@ -132,25 +129,6 @@ proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Bu
|
||||
result.addField(strInit, name = "p"):
|
||||
result.add(cCast(ptrType("NimStrPayload"), cAddr(pureLit)))
|
||||
|
||||
proc ssoCharLit(ch: char): string =
|
||||
## Return a C char literal for ch, with proper escaping.
|
||||
const hexDigits = "0123456789abcdef"
|
||||
result = "'"
|
||||
case ch
|
||||
of '\'': result.add("\\'")
|
||||
of '\\': result.add("\\\\")
|
||||
of '\0': result.add("\\0")
|
||||
of '\n': result.add("\\n")
|
||||
of '\r': result.add("\\r")
|
||||
of '\t': result.add("\\t")
|
||||
elif ch.ord < 32 or ch.ord == 127:
|
||||
result.add("\\x")
|
||||
result.add(hexDigits[ch.ord shr 4])
|
||||
result.add(hexDigits[ch.ord and 0xf])
|
||||
else:
|
||||
result.add(ch)
|
||||
result.add('\'')
|
||||
|
||||
proc ssoBytesLit(m: BModule; s: string; slen: int): string =
|
||||
## Compute the `bytes` field value for the new SmallString layout.
|
||||
## byte 0 = slen, bytes 1-7 = inline chars 0-6 (zero-padded).
|
||||
@@ -320,19 +298,6 @@ proc genStringLiteralV3(m: BModule; n: PNode; isConst: bool; result: var Builder
|
||||
|
||||
# ------ Version selector ---------------------------------------------------
|
||||
|
||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
isConst: bool; result: var Rope) =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralDataOnlyV1(m, s, result)
|
||||
of 2:
|
||||
let tmp = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
|
||||
result.add tmp
|
||||
of 3:
|
||||
localError(m.config, info, "genStringLiteralDataOnly not supported for SmallString (nimsso)")
|
||||
else:
|
||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
||||
|
||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Builder) =
|
||||
result.add(cCast(ptrType(cgsymValue(m, "NimStringDesc")), NimNil))
|
||||
|
||||
|
||||
@@ -75,6 +75,23 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
cSizeof(getTypeDesc(p.module, typ)))
|
||||
else:
|
||||
specializeResetN(p, accessor, typ.n, typ)
|
||||
if isCaseObj(typ.n):
|
||||
# The active branch was released above. Clear the complete object so
|
||||
# stale bytes from overlapping branches cannot be traced by the GC.
|
||||
# type
|
||||
# Foo = object
|
||||
# case kind: bool
|
||||
# of true:
|
||||
# a: ref Bar # 8 bytes (pointer)
|
||||
# of false:
|
||||
# b: int # 4 bytes
|
||||
# specializeResetT for b emits accessor.b = 0 — writes 4 bytes
|
||||
# But the union is 8 bytes wide (sized by the largest branch)
|
||||
# The remaining 4 bytes where a used to live are untouched
|
||||
# Those stale bytes could contain a heap pointer the GC traces → crash
|
||||
p.s(cpsStmts).addCallStmt(cgsymValue(p.module, "nimZeroMem"),
|
||||
cCast(CPointer, cAddr(accessor)),
|
||||
cSizeof(getTypeDesc(p.module, typ)))
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i, a in typ.ikids:
|
||||
|
||||
@@ -1340,92 +1340,6 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "if (T$1_) std::rethrow_exception(T$1_);$n", [etmp])
|
||||
endSimpleBlock(p, scope)
|
||||
|
||||
proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||
# There are two versions we generate, depending on whether we
|
||||
# catch C++ exceptions, imported via .importcpp or not. The
|
||||
# code can be easier if there are no imported C++ exceptions
|
||||
# to deal with.
|
||||
|
||||
# code to generate:
|
||||
#
|
||||
# try
|
||||
# {
|
||||
# myDiv(4, 9);
|
||||
# } catch (NimExceptionType1&) {
|
||||
# body
|
||||
# } catch (NimExceptionType2&) {
|
||||
# finallyPart()
|
||||
# raise;
|
||||
# }
|
||||
# catch(...) {
|
||||
# general_handler_body
|
||||
# }
|
||||
# finallyPart();
|
||||
|
||||
template genExceptBranchBody(body: PNode) {.dirty.} =
|
||||
genRestoreFrameAfterException(p)
|
||||
expr(p, body, d)
|
||||
|
||||
if not isEmptyType(t.typ) and d.k == locNone:
|
||||
d = getTemp(p, t.typ)
|
||||
genLineDir(p, t)
|
||||
cgsym(p.module, "popCurrentExceptionEx")
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, false, t.kind == nkHiddenTryStmt, 0.Natural))
|
||||
startBlockWith(p):
|
||||
p.s(cpsStmts).add("try {\n")
|
||||
expr(p, t.firstSon, d)
|
||||
endBlockWith(p):
|
||||
p.s(cpsStmts).add("}\n")
|
||||
|
||||
var catchAllPresent = false
|
||||
|
||||
p.nestedTryStmts[^1].inExcept = true
|
||||
for i in 1..<t.len:
|
||||
if t[i].kind != nkExceptBranch: break
|
||||
|
||||
# bug #4230: avoid false sharing between branches:
|
||||
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
|
||||
|
||||
if t[i].len == 1:
|
||||
# general except section:
|
||||
catchAllPresent = true
|
||||
startBlockWith(p):
|
||||
p.s(cpsStmts).add("catch (...) {\n")
|
||||
genExceptBranchBody(t[i].firstSon)
|
||||
endBlockWith(p):
|
||||
p.s(cpsStmts).add("}\n")
|
||||
else:
|
||||
for j in 0..<t[i].len-1:
|
||||
if t[i][j].isInfixAs():
|
||||
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
||||
fillLocalName(p, exvar.sym)
|
||||
backendEnsureMutable exvar.sym
|
||||
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnUnknown)
|
||||
startBlockWith(p):
|
||||
lineCg(p, cpsStmts, "catch ($1& $2) {$n", [getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc)])
|
||||
else:
|
||||
startBlockWith(p):
|
||||
lineCg(p, cpsStmts, "catch ($1&) {$n", [getTypeDesc(p.module, t[i][j].typ)])
|
||||
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
|
||||
endBlockWith(p):
|
||||
p.s(cpsStmts).add("}\n")
|
||||
|
||||
discard pop(p.nestedTryStmts)
|
||||
|
||||
if t[^1].kind == nkFinally:
|
||||
# c++ does not have finally, therefore code needs to be generated twice
|
||||
if not catchAllPresent:
|
||||
# finally requires catch all presence
|
||||
startBlockWith(p):
|
||||
p.s(cpsStmts).add("catch (...) {\n")
|
||||
genStmts(p, t[^1].firstSon)
|
||||
line(p, cpsStmts, "throw;\n")
|
||||
endBlockWith(p):
|
||||
p.s(cpsStmts).add("}\n")
|
||||
|
||||
genSimpleBlock(p, t[^1].firstSon)
|
||||
|
||||
proc bodyCanRaise(p: BProc; n: PNode): bool =
|
||||
case n.kind
|
||||
of nkCallKinds:
|
||||
@@ -1904,22 +1818,6 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
|
||||
if p.config.exc == excGoto:
|
||||
raiseExit(p)
|
||||
|
||||
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 dotExpr = e.firstSon
|
||||
if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr.firstSon
|
||||
|
||||
@@ -1461,8 +1461,6 @@ proc discriminatorTableName(m: BModule; objtype: PType, d: PSym): Rope =
|
||||
internalError(m.config, d.info, "anonymous obj with discriminator")
|
||||
result = "NimDT_$1_$2" % [rope($hashType(objtype, m.config)), rope(d.name.s.mangle)]
|
||||
|
||||
proc rope(arg: Int128): Rope = rope($arg)
|
||||
|
||||
proc discriminatorTableDecl(m: BModule; objtype: PType, d: PSym, result: var Builder) =
|
||||
cgsym(m, "TNimNode")
|
||||
var tmp = discriminatorTableName(m, objtype, d)
|
||||
|
||||
@@ -1894,10 +1894,6 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
|
||||
addNimDefines(res, conf)
|
||||
result = extract(res)
|
||||
|
||||
proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope =
|
||||
## Returns a mangled module name.
|
||||
result = mangleModuleName(conf, filename).mangle
|
||||
|
||||
proc getSomeNameForModule*(m: BModule): Rope =
|
||||
## Returns a mangled module name.
|
||||
assert m.module.kind == skModule
|
||||
@@ -2353,7 +2349,6 @@ proc hcrGetProcLoadCode(builder: var Builder, m: BModule, sym, prefix, handle, g
|
||||
assert prc != nil
|
||||
fillProcLoc(m, prc.ast[namePos])
|
||||
|
||||
var extname = prefix & sym
|
||||
var tmp = mangleDynLibProc(prc)
|
||||
backendEnsureMutable prc
|
||||
prc.locImpl.snippet = tmp
|
||||
@@ -2835,15 +2830,6 @@ proc writeModule(m: BModule) =
|
||||
code = stripCnifMarks(code)
|
||||
registerModuleCode(m, cf, code)
|
||||
|
||||
proc updateCachedModule(m: BModule) =
|
||||
let cfile = getCFile(m)
|
||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
if sfMainModule notin m.module.flags:
|
||||
genMainProc(m)
|
||||
cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
|
||||
proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
|
||||
let prefixedName = m.config.nimMainPrefix & "NimDestroyGlobals"
|
||||
let procname = getIdent(graph.cache, prefixedName)
|
||||
|
||||
@@ -843,6 +843,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "hotcodereloading":
|
||||
processOnOffSwitchG(conf, {optHotCodeReloading}, arg, pass, info)
|
||||
if conf.hcrOn:
|
||||
warningDeprecated(conf, info, "hotCodeReloading is deprecated, see https://github.com/nim-lang/RFCs/issues/573 for further information")
|
||||
defineSymbol(conf.symbols, "hotcodereloading")
|
||||
defineSymbol(conf.symbols, "useNimRtl")
|
||||
# hardcoded linking with dynamic runtime for MSVC for smaller binaries
|
||||
|
||||
@@ -269,10 +269,8 @@ proc conceptsMatch(c: PContext, fc, ac: PType; m: var MatchCon): MatchKind =
|
||||
let
|
||||
fn = fc.conceptBody
|
||||
an = ac.conceptBody
|
||||
sameLen = fc.len == ac.len
|
||||
var match = false
|
||||
for fdef in fn:
|
||||
var cmpResult = false
|
||||
for ia, ndef in an:
|
||||
match = cmpConceptDefs(c, fdef, ndef, m)
|
||||
if match:
|
||||
@@ -330,13 +328,10 @@ proc matchType(c: PContext; fo, ao: PType; m: var MatchCon): bool =
|
||||
result = matchType(c, f.skipModifier, a, m)
|
||||
of tyTypeDesc:
|
||||
if isSelf(f):
|
||||
let ua = a.skipTypes(asymmetricConceptParamMods)
|
||||
if m.magic in {mArrPut, mArrGet}:
|
||||
if m.potentialImplementation.reduceToBase.kind in arrPutGetMagicApplies:
|
||||
bindParam(c, m, a, last m.potentialImplementation)
|
||||
result = true
|
||||
#elif ua.isConcept:
|
||||
# result = matchType(c, m.concpt, ua, m)
|
||||
else:
|
||||
result = matchType(c, a.skipTypes(ignorableForArgType), m.potentialImplementation, m)
|
||||
else:
|
||||
|
||||
@@ -425,12 +425,6 @@ template dispA(conf: ConfigRef; dest: var string, xml, tex: string,
|
||||
if not conf.isLatexCmd: dest.addf(xml, args)
|
||||
else: dest.addf(tex, args)
|
||||
|
||||
proc getVarIdx(varnames: openArray[string], id: string): int =
|
||||
for i in 0..high(varnames):
|
||||
if cmpIgnoreStyle(varnames[i], id) == 0:
|
||||
return i
|
||||
result = -1
|
||||
|
||||
proc genComment(d: PDoc, n: PNode): PRstNode =
|
||||
if n.comment.len > 0:
|
||||
if optDocRaw in d.conf.globalOptions:
|
||||
|
||||
@@ -29,7 +29,6 @@ proc shouldProcess(g: PGen): bool =
|
||||
template closeImpl(body: untyped) {.dirty.} =
|
||||
var g = PGen(p)
|
||||
let useWarning = sfMainModule notin g.module.flags
|
||||
let groupedToc = true
|
||||
if shouldProcess(g):
|
||||
finishGenerateDoc(g.doc)
|
||||
body
|
||||
@@ -41,7 +40,7 @@ template closeImpl(body: untyped) {.dirty.} =
|
||||
proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
result = nil
|
||||
closeImpl:
|
||||
writeOutput(g.doc, useWarning, groupedToc)
|
||||
writeOutput(g.doc, useWarning, true)
|
||||
|
||||
proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
result = nil
|
||||
|
||||
@@ -49,65 +49,3 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||
result.ast = n
|
||||
incl result.flagsImpl, {sfFromGeneric, sfNeverRaises}
|
||||
setHookDisamb(g, result, "$enumtostr", t)
|
||||
|
||||
proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
|
||||
case obj.kind
|
||||
of nkSym:
|
||||
result = nil
|
||||
of nkElse, nkOfBranch:
|
||||
result = searchObjCaseImpl(obj.lastSon, field)
|
||||
else:
|
||||
if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field:
|
||||
result = obj
|
||||
else:
|
||||
result = nil
|
||||
for x in obj:
|
||||
result = searchObjCaseImpl(x, field)
|
||||
if result != nil: break
|
||||
|
||||
proc searchObjCase(t: PType; field: PSym): PNode =
|
||||
result = searchObjCaseImpl(t.n, field)
|
||||
if result == nil and t.baseClass != nil:
|
||||
result = searchObjCase(t.baseClass.skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
|
||||
doAssert result != nil
|
||||
|
||||
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
|
||||
dest.typ = field.typ
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
|
||||
res.typ = getSysType(g, info, tyUInt8)
|
||||
|
||||
result.typ = newType(tyProc, idgen, t.owner)
|
||||
result.typ.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result.typ, res.typ)
|
||||
result.typ.n.add newNodeI(nkEffectList, info)
|
||||
|
||||
result.typ.addParam dest
|
||||
|
||||
var body = newNodeI(nkStmtList, info)
|
||||
var caseStmt = newNodeI(nkCaseStmt, info)
|
||||
caseStmt.add(newSymNode dest)
|
||||
|
||||
let subObj = searchObjCase(t, field)
|
||||
for i in 1..<subObj.len:
|
||||
let ofBranch = subObj[i]
|
||||
var newBranch = newNodeI(ofBranch.kind, ofBranch.info)
|
||||
for j in 0..<ofBranch.len-1:
|
||||
newBranch.add ofBranch[j]
|
||||
|
||||
newBranch.add newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newIntNode(nkInt8Lit, i)))
|
||||
caseStmt.add newBranch
|
||||
|
||||
body.add(caseStmt)
|
||||
|
||||
var n = newNodeI(nkProcDef, info, bodyPos+2)
|
||||
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
||||
n[namePos] = newSymNode(result)
|
||||
n[paramsPos] = result.typ.n
|
||||
n[bodyPos] = body
|
||||
n[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flagsImpl, {sfFromGeneric, sfNeverRaises}
|
||||
|
||||
@@ -173,7 +173,6 @@ template hasDestructorOrAsgn(c: var Con, typ: PType): bool =
|
||||
proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
|
||||
if not hasDestructorOrAsgn(c, n.typ): return true
|
||||
|
||||
let m = skipConvDfa(n)
|
||||
result = isLastReadImpl(n, c, s)
|
||||
|
||||
proc isFirstWrite(n: PNode; c: var Con): bool =
|
||||
|
||||
@@ -340,9 +340,6 @@ proc `*`*(a: Int128, b: int32): Int128 =
|
||||
if b < 0:
|
||||
result = -result
|
||||
|
||||
proc `*=`(a: var Int128, b: int32) =
|
||||
a = a * b
|
||||
|
||||
proc makeInt128(high, low: uint64): Int128 =
|
||||
result = Zero
|
||||
result.udata[0] = cast[uint32](low)
|
||||
|
||||
@@ -148,11 +148,6 @@ proc newGlobals(): PGlobals =
|
||||
typeInfoGenerated: initIntSet()
|
||||
)
|
||||
|
||||
proc initCompRes(): TCompRes =
|
||||
result = TCompRes(address: "", res: "",
|
||||
tmpLoc: "", typ: etyNone, kind: resNone
|
||||
)
|
||||
|
||||
proc rdLoc(a: TCompRes): Rope {.inline.} =
|
||||
if a.typ != etyBaseIndex:
|
||||
result = a.res
|
||||
@@ -594,15 +589,6 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
|
||||
r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
|
||||
r.kind = resExpr
|
||||
|
||||
template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
var x, y, z: TCompRes
|
||||
useMagic(p, magic)
|
||||
gen(p, n[1], x)
|
||||
gen(p, n[2], y)
|
||||
gen(p, n[3], z)
|
||||
r.res = frmt % [x.rdLoc, y.rdLoc, z.rdLoc]
|
||||
r.kind = resExpr
|
||||
|
||||
template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
# $1 binds to n[1], if $2 is present it will be substituted to a tmp of $1
|
||||
useMagic(p, magic)
|
||||
@@ -1182,7 +1168,6 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode; isAsmStmt = false) =
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
p.body.add(it.strVal)
|
||||
of nkSym:
|
||||
let v = it.sym
|
||||
# for backwards compatibility we don't deref syms here :-(
|
||||
if false:
|
||||
discard
|
||||
@@ -1255,17 +1240,6 @@ proc generateHeader(p: PProc, prc: PSym): Rope =
|
||||
result.add(name)
|
||||
result.add("_Idx")
|
||||
|
||||
proc countJsParams(typ: PType): int =
|
||||
result = 0
|
||||
for i in 1..<typ.n.len:
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
var param = typ.n[i].sym
|
||||
if isCompileTimeOnly(param.typ): continue
|
||||
if mapType(param.typ) == etyBaseIndex:
|
||||
inc result, 2
|
||||
else:
|
||||
inc result
|
||||
|
||||
const
|
||||
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
|
||||
nkFloatLit..nkFloat64Lit, nkPar, nkStringToCString,
|
||||
@@ -1797,12 +1771,6 @@ proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) =
|
||||
inc emitted
|
||||
hasArgs = true
|
||||
r.res.add(")")
|
||||
when false:
|
||||
# XXX look into this:
|
||||
let jsp = countJsParams(typ)
|
||||
if emitted != jsp and tfVarargs notin typ.flags:
|
||||
localError(p.config, n.info, "wrong number of parameters emitted; expected: " & $jsp &
|
||||
" but got: " & $emitted)
|
||||
r.kind = resExpr
|
||||
|
||||
proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
|
||||
@@ -2335,9 +2303,6 @@ proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.res.add("]")
|
||||
|
||||
proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
var
|
||||
a: TCompRes
|
||||
line, filen: Rope
|
||||
var op = n[0].sym.magic
|
||||
case op
|
||||
of mOr: genOr(p, n[1], n[2], r)
|
||||
@@ -2594,7 +2559,6 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||
r.kind = resExpr
|
||||
var initList : Rope = ""
|
||||
var fieldIDs = initIntSet()
|
||||
let nTyp = n.typ.skipTypes(abstractInst)
|
||||
for i in 1..<n.len:
|
||||
if i > 1: initList.add(", ")
|
||||
var it = n[i]
|
||||
|
||||
@@ -126,11 +126,6 @@ const
|
||||
paramName* = ":envP"
|
||||
envName* = ":env"
|
||||
|
||||
proc newCall(a: PSym, b: PNode): PNode =
|
||||
result = newNodeI(nkCall, a.info)
|
||||
result.add newSymNode(a)
|
||||
result.add b
|
||||
|
||||
proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PType =
|
||||
var n = newNodeI(nkRange, iter.info)
|
||||
n.add newIntNode(nkIntLit, -1)
|
||||
@@ -288,7 +283,6 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
addVar(v, env)
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
#result.add newCall(getSysSym(g, n.info, "internalNew"), env)
|
||||
result.add genCreateEnv(env)
|
||||
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
|
||||
result.add makeClosure(g, idgen, iter, env, n.info)
|
||||
|
||||
@@ -735,17 +735,11 @@ proc getEscapedChar(L: var Lexer, tok: var Token) =
|
||||
else: lexMessage(L, errGenerated, "invalid character constant")
|
||||
|
||||
proc handleCRLF(L: var Lexer, pos: int): int =
|
||||
template registerLine =
|
||||
let col = L.getColNumber(pos)
|
||||
|
||||
case L.buf[pos]
|
||||
of CR:
|
||||
registerLine()
|
||||
result = nimlexbase.handleCR(L, pos)
|
||||
of LF:
|
||||
registerLine()
|
||||
result = nimlexbase.handleLF(L, pos)
|
||||
else: result = pos
|
||||
result =
|
||||
case L.buf[pos]
|
||||
of CR: nimlexbase.handleCR(L, pos)
|
||||
of LF: nimlexbase.handleLF(L, pos)
|
||||
else: pos
|
||||
|
||||
type
|
||||
StringMode = enum
|
||||
|
||||
@@ -596,12 +596,6 @@ proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
result.add lenCall
|
||||
|
||||
proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthStr, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x))
|
||||
result.add lenCall
|
||||
|
||||
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode; noinit = false): PNode =
|
||||
let lenCall = genBuiltin(c, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
|
||||
@@ -209,22 +209,6 @@ proc commandInteractive(graph: ModuleGraph) =
|
||||
let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
|
||||
discard processPipelineModule(graph, m, idgen, s)
|
||||
|
||||
proc commandScan(cache: IdentCache, config: ConfigRef) =
|
||||
var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
|
||||
var stream = llStreamOpen(f, fmRead)
|
||||
if stream != nil:
|
||||
var
|
||||
L: Lexer = default(Lexer)
|
||||
tok: Token = default(Token)
|
||||
openLexer(L, f, stream, cache, config)
|
||||
while true:
|
||||
rawGetTok(L, tok)
|
||||
printTok(config, tok)
|
||||
if tok.tokType == tkEof: break
|
||||
closeLexer(L)
|
||||
else:
|
||||
rawMessage(config, errGenerated, "cannot open file: " & f.string)
|
||||
|
||||
const
|
||||
PrintRopeCacheStats = false
|
||||
|
||||
|
||||
@@ -432,10 +432,6 @@ proc addDispatchers*(g: ModuleGraph, value: PSym) =
|
||||
# TODO: add it for packed modules
|
||||
g.dispatchers.add value
|
||||
|
||||
iterator resolveLazySymSeq(g: ModuleGraph, list: var seq[PSym]): PSym =
|
||||
for it in list.mitems:
|
||||
yield it
|
||||
|
||||
proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[PSym]) =
|
||||
# TODO: add it for packed modules
|
||||
g.methodsPerType[id] = methods
|
||||
@@ -668,10 +664,6 @@ proc hash*(u: SigHash): Hash =
|
||||
|
||||
proc hash*(x: FileIndex): Hash {.borrow.}
|
||||
|
||||
template getPContext(): untyped =
|
||||
when c is PContext: c
|
||||
else: c.c
|
||||
|
||||
when defined(nimsuggest):
|
||||
template onUse*(info: TLineInfo; s: PSym; isGenericInstance = false) = discard
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
||||
|
||||
@@ -24,10 +24,6 @@ template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
|
||||
template toStdOrrKind(stdOrr): untyped =
|
||||
if stdOrr == stdout: stdOrrStdout else: stdOrrStderr
|
||||
|
||||
proc toLowerAscii(a: var string) {.inline.} =
|
||||
for c in mitems(a):
|
||||
if isUpperAscii(c): c = char(uint8(c) xor 0b0010_0000'u8)
|
||||
|
||||
proc flushDot*(conf: ConfigRef) =
|
||||
## safe to call multiple times
|
||||
let stdOrr = if optStdout in conf.globalOptions: stdout else: stderr
|
||||
@@ -83,7 +79,8 @@ proc canonicalCase(path: var string) {.inline.} =
|
||||
## the idea is to only use this for checking whether a path is already in
|
||||
## the table but otherwise keep the original case
|
||||
when FileSystemCaseSensitive: discard
|
||||
else: toLowerAscii(path)
|
||||
else:
|
||||
for c in mitems(path): c = toLowerAscii(c)
|
||||
|
||||
proc fileInfoKnown*(conf: ConfigRef; filename: AbsoluteFile): bool =
|
||||
var
|
||||
|
||||
@@ -27,7 +27,6 @@ proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
|
||||
## * `modulegraphs.getPackage`
|
||||
let
|
||||
filename = AbsoluteFile toFullPath(conf, fileIdx)
|
||||
name = getIdent(cache, splitFile(filename).name)
|
||||
info = newLineInfo(fileIdx, 1, 1)
|
||||
pkgName = getPackageName(conf, filename.string)
|
||||
pkgIdent = getIdent(cache, pkgName)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import std/intsets
|
||||
import ast, options, lineinfos, pathutils, msgs, modulegraphs, packages
|
||||
|
||||
proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
|
||||
|
||||
@@ -11,25 +11,10 @@
|
||||
# This is needed for proper handling of forward declarations.
|
||||
|
||||
import
|
||||
ast, astalgo, msgs, semdata, types, trees, lookups
|
||||
ast, astalgo, msgs, semdata, types, lookups
|
||||
|
||||
import std/strutils
|
||||
|
||||
proc equalGenericParams(procA, procB: PNode): bool =
|
||||
if procA.len != procB.len: return false
|
||||
for i in 0..<procA.len:
|
||||
if procA[i].kind != nkSym:
|
||||
return false
|
||||
if procB[i].kind != nkSym:
|
||||
return false
|
||||
let a = procA[i].sym
|
||||
let b = procB[i].sym
|
||||
if a.name.id != b.name.id or
|
||||
not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}): return
|
||||
if a.ast != nil and b.ast != nil:
|
||||
if not exprStructuralEquivalent(a.ast, b.ast): return
|
||||
result = true
|
||||
|
||||
proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym =
|
||||
const flags = {ExactGenericParams, ExactTypeDescValues,
|
||||
ExactConstraints, IgnoreCC}
|
||||
@@ -59,30 +44,3 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): tuple[proto: PSym, co
|
||||
scope = scope.parent
|
||||
result.proto = searchForProcAux(c, scope, fn)
|
||||
result.comesFromShadowScope = true
|
||||
|
||||
when false:
|
||||
proc paramsFitBorrow(child, parent: PNode): bool =
|
||||
result = false
|
||||
if child.len == parent.len:
|
||||
for i in 1..<child.len:
|
||||
var m = child[i].sym
|
||||
var n = parent[i].sym
|
||||
assert((m.kind == skParam) and (n.kind == skParam))
|
||||
if not compareTypes(m.typ, n.typ, dcEqOrDistinctOf): return
|
||||
if not compareTypes(child[0].typ, parent[0].typ,
|
||||
dcEqOrDistinctOf): return
|
||||
result = true
|
||||
|
||||
proc searchForBorrowProc*(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||
# Searches for the fn in the symbol table. If the parameter lists are suitable
|
||||
# for borrowing the sym in the symbol table is returned, else nil.
|
||||
var it: TIdentIter = default(TIdentIter)
|
||||
for scope in walkScopes(startScope):
|
||||
result = initIdentIter(it, scope.symbols, fn.Name)
|
||||
while result != nil:
|
||||
# watchout! result must not be the same as fn!
|
||||
if (result.Kind == fn.kind) and (result.id != fn.id):
|
||||
if equalGenericParams(result.ast[genericParamsPos],
|
||||
fn.ast[genericParamsPos]):
|
||||
if paramsFitBorrow(fn.typ.n, result.typ.n): return
|
||||
result = NextIdentIter(it, scope.symbols)
|
||||
|
||||
@@ -537,10 +537,6 @@ proc putNL(g: var TSrcGen, indent: int) =
|
||||
g.lineLen = indent
|
||||
g.pendingWhitespace = -1
|
||||
|
||||
proc previousNL(g: TSrcGen): bool =
|
||||
result = g.pendingNL >= 0 or (g.tokens.len > 0 and
|
||||
g.tokens[^1].kind == tkSpaces)
|
||||
|
||||
proc putNL(g: var TSrcGen) =
|
||||
putNL(g, g.indent)
|
||||
|
||||
@@ -646,28 +642,6 @@ proc maxLineLength(s: string): int =
|
||||
inc(lineLen)
|
||||
inc(i)
|
||||
|
||||
proc putRawStr(g: var TSrcGen, kind: TokType, s: string) =
|
||||
var i = 0
|
||||
let hi = s.len - 1
|
||||
var str = ""
|
||||
while i <= hi:
|
||||
case s[i]
|
||||
of '\r':
|
||||
put(g, kind, str)
|
||||
str = ""
|
||||
inc(i)
|
||||
if i <= hi and s[i] == '\n': inc(i)
|
||||
optNL(g, 0)
|
||||
of '\n':
|
||||
put(g, kind, str)
|
||||
str = ""
|
||||
inc(i)
|
||||
optNL(g, 0)
|
||||
else:
|
||||
str.add(s[i])
|
||||
inc(i)
|
||||
put(g, kind, str)
|
||||
|
||||
proc containsNL(s: string): bool =
|
||||
for i in 0..<s.len:
|
||||
case s[i]
|
||||
@@ -1784,7 +1758,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
gsub(g, n, 1)
|
||||
of nkInfix:
|
||||
if n.len < 3:
|
||||
var i = 0
|
||||
put(g, tkOpr, "Too few children for nkInfix")
|
||||
return
|
||||
let oldLineLen = g.lineLen # we cache this because lineLen gets updated below
|
||||
@@ -2076,7 +2049,6 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
|
||||
of nkPragma:
|
||||
if g.inPragma <= 0:
|
||||
inc g.inPragma
|
||||
#if not previousNL(g):
|
||||
put(g, tkSpaces, Space)
|
||||
put(g, tkCurlyDotLe, "{.")
|
||||
gcomma(g, n, emptyContext)
|
||||
|
||||
@@ -34,14 +34,6 @@ when defined(windows) and defined(bcc):
|
||||
#endif
|
||||
""".}
|
||||
|
||||
proc c_snprintf(s: cstring; n: uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
|
||||
|
||||
|
||||
when not declared(signbit):
|
||||
proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "<math.h>".}
|
||||
proc signbit*(x: SomeFloat): bool {.inline.} =
|
||||
result = c_signbit(x) != 0
|
||||
|
||||
import std/formatfloat
|
||||
|
||||
proc toStrMaxPrecision*(f: BiggestFloat | float32): string =
|
||||
|
||||
@@ -332,7 +332,6 @@ proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
|
||||
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
|
||||
typeAllowedCheck(c, typ.n.info, typ, skProc)
|
||||
|
||||
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
|
||||
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
|
||||
proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
|
||||
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
|
||||
|
||||
@@ -689,7 +689,7 @@ proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
|
||||
baseFilter + {skIterator}
|
||||
else: baseFilter
|
||||
# this will add the errors:
|
||||
var r = resolveOverloads(c, n, n, filter, flags, errors, true)
|
||||
discard resolveOverloads(c, n, n, filter, flags, errors, true)
|
||||
if errors.len == 0:
|
||||
localError(c.config, n.info, "could not resolve: " & $n)
|
||||
else:
|
||||
@@ -926,15 +926,6 @@ proc semResolvedCall(c: PContext, x: var TCandidate,
|
||||
result.typ = finalCallee.typ.returnType
|
||||
updateDefaultParams(c, result)
|
||||
|
||||
proc canDeref(n: PNode): bool {.inline.} =
|
||||
result = n.len >= 2 and (let t = n[1].typ;
|
||||
t != nil and t.skipTypes({tyGenericInst, tyAlias, tySink}).kind in {tyPtr, tyRef})
|
||||
|
||||
proc tryDeref(n: PNode): PNode =
|
||||
result = newNodeI(nkHiddenDeref, n.info)
|
||||
result.typ = n.typ.skipTypes(abstractInst)[0]
|
||||
result.add n
|
||||
|
||||
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
filter: TSymKinds, flags: TExprFlags;
|
||||
expectedType: PType = nil): PNode =
|
||||
|
||||
@@ -186,6 +186,12 @@ type
|
||||
forwardFieldUpdates*: seq[(PType, PNode, PType)]
|
||||
# object/tuple field definitions whose default values mention forward
|
||||
# types and need delayed const checking
|
||||
forwardFlagUpdates*: seq[(PType, PType)]
|
||||
# (owner, son) pairs whose `propagateToOwner` ran on a not yet reified
|
||||
# forward type and has to be redone in the final pass
|
||||
staleTypeFlags*: IntSet
|
||||
# ids of the owners in `forwardFlagUpdates`; their flags are provisional
|
||||
# too, so reading them makes the reader provisional in turn
|
||||
inTypeofContext*: int
|
||||
|
||||
semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.}
|
||||
@@ -369,6 +375,7 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext =
|
||||
unknownIdents: initIntSet(),
|
||||
shadowDiscardedDefs: initIntSet(),
|
||||
realizedDefs: initIntSet(),
|
||||
staleTypeFlags: initIntSet(),
|
||||
cache: graph.cache,
|
||||
graph: graph,
|
||||
signatures: initStrTable(),
|
||||
|
||||
@@ -22,7 +22,6 @@ const
|
||||
errNamedExprExpected = "named expression expected"
|
||||
errNamedExprNotAllowed = "named expression not allowed here"
|
||||
errFieldInitTwice = "field initialized twice: '$1'"
|
||||
errUndeclaredFieldX = "undeclared field: '$1'"
|
||||
|
||||
proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
|
||||
flags: TExprFlags = {}; expectedType: PType = nil): PNode =
|
||||
@@ -770,17 +769,6 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
|
||||
|
||||
n.typ = newType
|
||||
|
||||
proc arrayConstrType(c: PContext, n: PNode): PType =
|
||||
var typ = newTypeS(tyArray, c)
|
||||
rawAddSon(typ, nil) # index type
|
||||
if n.len == 0:
|
||||
rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype!
|
||||
else:
|
||||
var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
|
||||
addSonSkipIntLit(typ, t, c.idgen)
|
||||
typ.setIndexType makeRangeType(c, 0, n.len - 1, n.info)
|
||||
result = typ
|
||||
|
||||
proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||
result = newNodeI(nkBracket, n.info)
|
||||
# nkBracket nodes can also be produced by the VM as seq constant nodes
|
||||
@@ -1339,7 +1327,6 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
|
||||
else: illFormedAst(n, c.config)
|
||||
|
||||
const
|
||||
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
|
||||
tyDotOpTransparent = {tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink}
|
||||
|
||||
proc readTypeParameter(c: PContext, typ: PType,
|
||||
@@ -2326,24 +2313,6 @@ proc semDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PNode =
|
||||
result.info = n.info
|
||||
result.typ = getSysType(c.graph, n.info, tyBool)
|
||||
|
||||
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym =
|
||||
## The argument to the proc should be nkCall(...) or similar
|
||||
## Returns the macro/template symbol
|
||||
if isCallExpr(n):
|
||||
var expandedSym = qualifiedLookUp(c, n[0], {checkUndeclared})
|
||||
if expandedSym == nil:
|
||||
errorUndeclaredIdentifier(c, n.info, n[0].renderTree)
|
||||
return errorSym(c, n[0])
|
||||
|
||||
if expandedSym.kind notin {skMacro, skTemplate}:
|
||||
localError(c.config, n.info, "'$1' is not a macro or template" % expandedSym.name.s)
|
||||
return errorSym(c, n[0])
|
||||
|
||||
result = expandedSym
|
||||
else:
|
||||
localError(c.config, n.info, "'$1' is not a macro or template" % n.renderTree)
|
||||
result = errorSym(c, n)
|
||||
|
||||
proc expectString(c: PContext, n: PNode): string =
|
||||
var n = semConstExpr(c, n)
|
||||
if n.kind in nkStrKinds:
|
||||
@@ -2358,14 +2327,6 @@ proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
|
||||
proc semExpandToAst(c: PContext, n: PNode): PNode =
|
||||
let macroCall = n[1]
|
||||
|
||||
when false:
|
||||
let expandedSym = expectMacroOrTemplateCall(c, macroCall)
|
||||
if expandedSym.kind == skError: return n
|
||||
|
||||
macroCall[0] = newSymNode(expandedSym, macroCall.info)
|
||||
markUsed(c, n.info, expandedSym)
|
||||
onUse(n.info, expandedSym)
|
||||
|
||||
if isCallExpr(macroCall):
|
||||
for i in 1..<macroCall.len:
|
||||
#if macroCall[0].typ[i].kind != tyUntyped:
|
||||
@@ -2537,7 +2498,6 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
let oldInStaticContext = c.inStaticContext
|
||||
let oldProcCon = c.p
|
||||
c.generics = @[]
|
||||
var err: string
|
||||
try:
|
||||
result = semExpr(c, n, flags)
|
||||
if result != nil and efNoSem2Check notin flags:
|
||||
|
||||
@@ -19,7 +19,7 @@ import std/[strutils, math, strtabs]
|
||||
#from system/memory import nimCStrLen
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[assertions, formatfloat]
|
||||
import std/[assertions]
|
||||
|
||||
proc errorType*(g: ModuleGraph): PType =
|
||||
## creates a type representing an error state
|
||||
@@ -121,21 +121,6 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
|
||||
else:
|
||||
result = $x
|
||||
|
||||
proc isFloatRange(t: PType): bool {.inline.} =
|
||||
result = t.kind == tyRange and t.elementType.kind in {tyFloat..tyFloat128}
|
||||
|
||||
proc isIntRange(t: PType): bool {.inline.} =
|
||||
result = t.kind == tyRange and t.elementType.kind in {
|
||||
tyInt..tyInt64, tyUInt8..tyUInt32}
|
||||
|
||||
proc pickIntRange(a, b: PType): PType =
|
||||
if isIntRange(a): result = a
|
||||
elif isIntRange(b): result = b
|
||||
else: result = a
|
||||
|
||||
proc isIntRangeOrLit(t: PType): bool =
|
||||
result = isIntRange(t) or isIntLit(t)
|
||||
|
||||
proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
|
||||
# b and c may be nil
|
||||
result = nil
|
||||
@@ -392,11 +377,6 @@ proc rangeCheck(n: PNode, value: Int128; g: ModuleGraph) =
|
||||
localError(g.config, n.info, "cannot convert " & $value &
|
||||
" to " & typeToString(n.typ))
|
||||
|
||||
proc floatRangeCheck(n: PNode, value: BiggestFloat; g: ModuleGraph) =
|
||||
if value < firstFloat(n.typ) or value > lastFloat(n.typ):
|
||||
localError(g.config, n.info, "cannot convert " & $value &
|
||||
" to " & typeToString(n.typ))
|
||||
|
||||
proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): PNode =
|
||||
let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc})
|
||||
let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc})
|
||||
|
||||
@@ -233,7 +233,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
if s.kind == skType: # don't put types in sym choice
|
||||
var ambig = false
|
||||
if candidates.len > 1:
|
||||
let s2 = searchInScopes(c, ident, ambig)
|
||||
discard searchInScopes(c, ident, ambig)
|
||||
result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags,
|
||||
isAmbiguous = ambig, fromDotExpr = true))
|
||||
else:
|
||||
|
||||
@@ -440,7 +440,7 @@ proc initConstrContext(t: PType, initExpr: PNode): ObjConstrContext =
|
||||
proc computeRequiresInit(c: PContext, t: PType): bool =
|
||||
assert t.kind == tyObject
|
||||
var constrCtx = initConstrContext(t, newNode(nkObjConstr))
|
||||
let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
|
||||
discard semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
|
||||
constrCtx.missingFields.len > 0
|
||||
|
||||
proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
|
||||
@@ -450,7 +450,7 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
|
||||
assert objType != nil
|
||||
if objType.kind == tyObject:
|
||||
var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
|
||||
let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults})
|
||||
discard semConstructTypeAux(c, constrCtx, {efIgnoreDefaults})
|
||||
if constrCtx.missingFields.len > 0:
|
||||
localError(c.config, info,
|
||||
"The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])
|
||||
|
||||
@@ -1006,7 +1006,6 @@ proc trackIf(tracked: PEffects, n: PNode) =
|
||||
|
||||
proc trackBlock(tracked: PEffects, n: PNode; typ: PType) =
|
||||
if n.kind in {nkStmtList, nkStmtListExpr}:
|
||||
let myBlock = tracked.currentBlock
|
||||
var oldState = -1
|
||||
for i in 0..<n.len:
|
||||
if hasSubnodeWith(n[i], nkBreakStmt):
|
||||
@@ -1029,11 +1028,6 @@ proc trackBlock(tracked: PEffects, n: PNode; typ: PType) =
|
||||
else:
|
||||
track(tracked, n)
|
||||
|
||||
proc cstringCheck(tracked: PEffects; n: PNode) =
|
||||
if n[0].typ.kind == tyCstring and (let a = skipConv(n[1]);
|
||||
a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
|
||||
message(tracked.config, n.info, warnUnsafeCode, renderTree(n))
|
||||
|
||||
proc patchResult(c: PEffects; n: PNode) =
|
||||
if n.kind == nkSym and n.sym.kind == skResult:
|
||||
let fn = c.owner
|
||||
@@ -1511,7 +1505,6 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
dec tracked.leftPartOfAsgn
|
||||
addAsgnFact(tracked.guards, n[0], n[1])
|
||||
notNilCheck(tracked, n[1], n[0].typ)
|
||||
when false: cstringCheck(tracked, n)
|
||||
if tracked.owner.kind != skMacro and n[0].typ.kind notin {tyOpenArray, tyVarargs}:
|
||||
createTypeBoundOps(tracked, n[0].typ, n.info)
|
||||
if n[0].kind != nkSym or not isLocalSym(tracked, n[0].sym):
|
||||
@@ -2001,7 +1994,6 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
patchResult(t, ensuresSpec)
|
||||
effects[ensuresEffects] = ensuresSpec
|
||||
|
||||
var mutationInfo = MutationInfo()
|
||||
if views in c.features:
|
||||
var partitions = computeGraphPartitions(s, body, g, {borrowChecking})
|
||||
checkBorrowedLocations(partitions, body, g.config)
|
||||
|
||||
@@ -17,18 +17,14 @@ const
|
||||
errInvalidControlFlowX = "invalid control flow: $1"
|
||||
errSelectorMustBeOfCertainTypes = "selector must be of an ordinal type, float or string"
|
||||
errExprCannotBeRaised = "only a 'ref object' can be raised"
|
||||
errBreakOnlyInLoop = "'break' only allowed in loop construct"
|
||||
errExceptionAlreadyHandled = "exception already handled"
|
||||
errYieldNotAllowedHere = "'yield' only allowed in an iterator"
|
||||
errYieldNotAllowedInTryStmt = "'yield' cannot be used within 'try' in a non-inlined iterator"
|
||||
errInvalidNumberOfYieldExpr = "invalid number of 'yield' expressions"
|
||||
errCannotReturnExpr = "current routine cannot return an expression"
|
||||
errGenericLambdaNotAllowed = "A nested proc can have generic parameters only when " &
|
||||
"it is used as an operand to another routine and the types " &
|
||||
"of the generic paramers can be inferred from the expected signature."
|
||||
errCannotInferTypeOfTheLiteral = "cannot infer the type of the $1"
|
||||
errCannotInferReturnType = "cannot infer the return type of '$1'"
|
||||
errCannotInferStaticParam = "cannot infer the value of the static param '$1'"
|
||||
errProcHasNoConcreteType = "'$1' doesn't have a concrete type, due to unspecified generic parameters."
|
||||
errLetNeedsInit = "'let' symbol requires an initialization"
|
||||
errThreadvarCannotInit = "a thread var cannot be initialized explicitly; this would only run for the main thread"
|
||||
@@ -545,7 +541,6 @@ proc semUsing(c: PContext; n: PNode): PNode =
|
||||
strTableIncl(c.signatures, v)
|
||||
else:
|
||||
localError(c.config, a.info, "'using' section must have a type")
|
||||
var def: PNode
|
||||
if a[^1].kind != nkEmpty:
|
||||
localError(c.config, a.info, "'using' sections cannot contain assignments")
|
||||
|
||||
@@ -1837,6 +1832,24 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
for (owner, field, expectedType) in c.forwardFieldUpdates:
|
||||
semDelayedFieldDefault(c, owner, expectedType, field)
|
||||
c.forwardFieldUpdates = @[]
|
||||
|
||||
# a son that still was a `tyForward` could not propagate `tfHasAsgn` and
|
||||
# friends to its owner back then, see `rememberFlagUpdate`. Now that every
|
||||
# forward declaration has a body, redo those propagations. They are recorded
|
||||
# in declaration order rather than dependency order and an owner can itself
|
||||
# be the son of another pair, so repeat until nothing changes; this
|
||||
# terminates because flags are only ever added.
|
||||
if c.forwardFlagUpdates.len > 0:
|
||||
let updates = move c.forwardFlagUpdates
|
||||
c.staleTypeFlags = initIntSet()
|
||||
var changed = true
|
||||
while changed:
|
||||
changed = false
|
||||
for (owner, elem) in updates:
|
||||
let before = owner.flags
|
||||
propagateToOwner(owner, elem)
|
||||
if owner.flags != before: changed = true
|
||||
|
||||
for i in 0..<n.len:
|
||||
var a = n[i]
|
||||
if a.kind == nkCommentStmt: continue
|
||||
|
||||
@@ -581,7 +581,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
|
||||
result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
|
||||
for i in 0..<a.len: result.add(a[i])
|
||||
result.add(b)
|
||||
let a0 = semTemplBody(c, a[0])
|
||||
discard semTemplBody(c, a[0])
|
||||
result = semTemplBodySons(c, result)
|
||||
of nkCurlyExpr:
|
||||
if a.typ == nil:
|
||||
|
||||
@@ -19,13 +19,11 @@ const
|
||||
errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)"
|
||||
errOrdinalTypeExpected = "ordinal type expected; given: $1"
|
||||
errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
|
||||
errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal"
|
||||
errInheritanceOnlyWithNonFinalObjects = "inheritance only works with non-final objects"
|
||||
errXExpectsOneTypeParam = "'$1' expects one type parameter"
|
||||
errArrayExpectsTwoTypeParams = "array expects two type parameters"
|
||||
errInvalidVisibilityX = "invalid visibility: '$1'"
|
||||
errXCannotBeAssignedTo = "'$1' cannot be assigned to"
|
||||
errIteratorNotAllowed = "iterators can only be defined at the module's top level"
|
||||
errXNeedsReturnType = "$1 needs a return type"
|
||||
errNoReturnTypeDeclared = "no return type declared"
|
||||
errTIsNotAConcreteType = "'$1' is not a concrete type"
|
||||
@@ -60,6 +58,18 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
|
||||
else:
|
||||
result = newTypeS(kind, c)
|
||||
|
||||
proc rememberFlagUpdate(c: PContext; owner, elem: PType) =
|
||||
## `propagateToOwner` just derived `owner`'s `tfHasAsgn` & friends from
|
||||
## `elem`, but inside a type section `elem` can still be an unreified
|
||||
## `tyForward` which has nothing to derive from yet -- and a type that read
|
||||
## such a type is provisional in turn. Remember the pair so
|
||||
## `typeSectionFinalPass` can redo the propagation once every forward
|
||||
## declaration has a body, the same way `forwardFieldUpdates` defers the
|
||||
## field defaults.
|
||||
if elem != nil and (elem.kind == tyForward or elem.id in c.staleTypeFlags):
|
||||
c.forwardFlagUpdates.add (owner, elem)
|
||||
c.staleTypeFlags.incl owner.id
|
||||
|
||||
proc newConstraint(c: PContext, k: TTypeKind): PType =
|
||||
result = newTypeS(tyBuiltInTypeClass, c)
|
||||
result.incl tfCheckedForDestructor
|
||||
@@ -221,6 +231,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
|
||||
var base = semTypeNode(c, n[1], nil)
|
||||
if base.kind == tyTypeDesc: base = base.base # unwrap from type traits like distinctBase
|
||||
addSonSkipIntLit(result, base, c.idgen)
|
||||
rememberFlagUpdate(c, result, base)
|
||||
if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
|
||||
if base.kind notin {tyGenericParam, tyGenericInvocation, tyFromExpr}:
|
||||
if base.kind == tyForward:
|
||||
@@ -239,6 +250,7 @@ proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) =
|
||||
if base.kind == tyVoid:
|
||||
localError(c.config, n.info, errTIsNotAConcreteType % typeToString(base))
|
||||
addSonSkipIntLit(result, base, c.idgen)
|
||||
rememberFlagUpdate(c, result, base)
|
||||
else:
|
||||
localError(c.config, n.info, errXExpectsOneTypeParam % kindStr)
|
||||
addSonSkipIntLit(result, errorType(c), c.idgen)
|
||||
@@ -387,6 +399,7 @@ proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdG
|
||||
localError(c.config, it.info, "illegal recursion in type '" & typeToString(s) & "'")
|
||||
else:
|
||||
propagateToOwner(father, s)
|
||||
rememberFlagUpdate(c, father, s)
|
||||
|
||||
proc semDistinct(c: PContext, n: PNode, prev: PType): PType =
|
||||
if n.len == 0: return newConstraint(c, tyDistinct)
|
||||
@@ -560,6 +573,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
|
||||
# index type:
|
||||
result = newOrPrevType(tyArray, prev, c, indx)
|
||||
addSonSkipIntLit(result, base, c.idgen)
|
||||
rememberFlagUpdate(c, result, base)
|
||||
else:
|
||||
localError(c.config, n.info, errArrayExpectsTwoTypeParams)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
@@ -636,6 +650,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
fSym.sym.ast.flags.incl nfSkipFieldChecking
|
||||
result.n.add fSym
|
||||
addSonSkipIntLit(result, typ, c.idgen)
|
||||
rememberFlagUpdate(c, result, typ)
|
||||
styleCheckDef(c, a[j].info, field)
|
||||
onDef(field.info, field)
|
||||
if result.n.len == 0: result.n = nil
|
||||
@@ -989,6 +1004,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
n[^1] = firstRange(c.config, typ)
|
||||
hasDefaultField = true
|
||||
propagateToOwner(rectype, typ)
|
||||
rememberFlagUpdate(c, rectype, typ)
|
||||
var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
|
||||
else: rectype.sym
|
||||
for i in 0..<n.len-2:
|
||||
@@ -1124,6 +1140,7 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
|
||||
# the entire object needs to be checked again
|
||||
c.forwardTypeUpdates.add (getCurrOwner(c), result, n) # we retry in the final pass
|
||||
rawAddSon(result, realBase)
|
||||
rememberFlagUpdate(c, result, realBase)
|
||||
if realBase == nil and tfInheritable in flags:
|
||||
result.incl tfInheritable
|
||||
if tfAcyclic in flags: result.incl tfAcyclic
|
||||
@@ -1947,7 +1964,6 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
return result
|
||||
|
||||
let
|
||||
pragmas = n[1]
|
||||
inherited = n[2]
|
||||
|
||||
var owner = getCurrOwner(c)
|
||||
|
||||
@@ -180,35 +180,6 @@ proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
for i in 0..<n.safeLen:
|
||||
result.add(prepareNode(cl, n[i]))
|
||||
|
||||
proc isTypeParam(n: PNode): bool =
|
||||
# XXX: generic params should use skGenericParam instead of skType
|
||||
return n.kind == nkSym and
|
||||
(n.sym.kind == skGenericParam or
|
||||
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
|
||||
|
||||
when false: # old workaround
|
||||
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
# This is needed for tuninstantiatedgenericcalls
|
||||
# It's possible that a generic param will be used in a proc call to a
|
||||
# typedesc accepting proc. After generic param substitution, such procs
|
||||
# should be optionally instantiated with the correct type. In order to
|
||||
# perform this instantiation, we need to re-run the generateInstance path
|
||||
# in the compiler, but it's quite complicated to do so at the moment so we
|
||||
# resort to a mild hack; the head symbol of the call is temporary reset and
|
||||
# overload resolution is executed again (which may trigger generateInstance).
|
||||
if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags:
|
||||
var needsFixing = false
|
||||
for i in 1..<n.safeLen:
|
||||
if isTypeParam(n[i]): needsFixing = true
|
||||
if needsFixing:
|
||||
n[0] = newSymNode(n[0].sym.owner)
|
||||
return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
|
||||
|
||||
for i in 0..<n.safeLen:
|
||||
n[i] = reResolveCallsWithTypedescParams(cl, n[i])
|
||||
|
||||
return n
|
||||
|
||||
proc replaceObjBranches(cl: TReplTypeVars, n: PNode): PNode =
|
||||
result = n
|
||||
case n.kind
|
||||
|
||||
@@ -182,7 +182,6 @@ proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) =
|
||||
## state is set to `csMatch` if all generic params match, `csEmpty` if
|
||||
## implicit generic parameters are missing (matches but cannot instantiate),
|
||||
## `csNoMatch` if a constraint fails or param count doesn't match
|
||||
let c = m.c
|
||||
let typeParams = callee.ast[genericParamsPos]
|
||||
let paramCount = typeParams.len
|
||||
let bindingCount = binding.len-1
|
||||
@@ -707,8 +706,6 @@ proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelat
|
||||
result = isEqual
|
||||
elif sameTupleLengths(a, f):
|
||||
result = isEqual
|
||||
let firstField = if f.kind == tyTuple: 0
|
||||
else: 1
|
||||
for _, ff, aa in tupleTypePairs(f, a):
|
||||
var m = typeRel(c, ff, aa, flags)
|
||||
if m < isSubtype: return isNone
|
||||
|
||||
@@ -97,9 +97,7 @@ iterator tokenize*(line: string): (int, string) =
|
||||
## normal JS code. This allows us to map mangled names back to Nim names.
|
||||
## Yields (column, name). Doesn't yield anything but identifiers.
|
||||
## See mangleName in compiler/jsgen.nim for how name mangling is done
|
||||
var
|
||||
col = 0
|
||||
token = ""
|
||||
var col = 0
|
||||
while col < line.len:
|
||||
var
|
||||
token: string = ""
|
||||
@@ -128,7 +126,6 @@ func parse*(source: string): SourceInfo =
|
||||
## So it can convert those into a series of mappings
|
||||
result = default(SourceInfo)
|
||||
var
|
||||
skipFirstLine = true
|
||||
currColumn = 0
|
||||
currLine = 0
|
||||
currFile = ""
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
import ast, types, idents, magicsys, msgs, options, modulegraphs,
|
||||
lowerings, liftdestructors, renderer, trees
|
||||
from trees import getMagic, getRoot
|
||||
|
||||
proc callProc(a: PNode): PNode =
|
||||
result = newNodeI(nkCall, a.info)
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
|
||||
import
|
||||
ast, astalgo, trees, msgs, platform, renderer, options,
|
||||
lineinfos, int128, modulegraphs, astmsgs, wordrecg
|
||||
lineinfos, int128, modulegraphs, astmsgs
|
||||
|
||||
import std/[intsets, strutils]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[assertions, formatfloat]
|
||||
import std/[assertions]
|
||||
|
||||
export isResolvedUserTypeClass, TPreferedDesc, typeToString
|
||||
|
||||
@@ -869,11 +869,6 @@ proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc sameObjectStructures(a, b: PType, c: var TSameTypeClosure): bool =
|
||||
if not sameTypeOrNilAux(a.baseClass, b.baseClass, c): return false
|
||||
if not sameObjectTree(a.n, b.n, c): return false
|
||||
result = true
|
||||
|
||||
proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
|
||||
if not sameTupleLengths(a, b): return false
|
||||
# XXX This is not tuple specific.
|
||||
|
||||
@@ -158,23 +158,6 @@ proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: b
|
||||
of tyFloat64: fun(floatVal, float64, rkFloat)
|
||||
else: return false
|
||||
|
||||
proc createStrKeepNode(x: var TFullReg; keepNode=true) =
|
||||
if x.node.isNil or not keepNode:
|
||||
x.node = newNode(nkStrLit)
|
||||
elif x.node.kind == nkNilLit and keepNode:
|
||||
when defined(useNodeIds):
|
||||
let id = x.node.id
|
||||
x.node[] = TNode(kind: nkStrLit)
|
||||
when defined(useNodeIds):
|
||||
x.node.id = id
|
||||
elif x.node.kind notin {nkStrLit..nkTripleStrLit} or
|
||||
nfAllConst in x.node.flags:
|
||||
# XXX this is hacky; tests/txmlgen triggers it:
|
||||
x.node = newNode(nkStrLit)
|
||||
# It not only hackey, it is also wrong for tgentemplate. The primary
|
||||
# cause of bugs like these is that the VM does not properly distinguish
|
||||
# between variable definitions (var foo = e) and variable updates (foo = e).
|
||||
|
||||
include vmhooks
|
||||
|
||||
template createStr(x) =
|
||||
@@ -1758,7 +1741,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
stackTrace(c, tos, pc, msg2)
|
||||
of opcSetLenStr:
|
||||
decodeB(rkNode)
|
||||
#createStrKeepNode regs[ra]
|
||||
regs[ra].node.strVal.setLen(regs[rb].intVal.int)
|
||||
of opcOf:
|
||||
decodeBC(rkInt)
|
||||
@@ -2203,7 +2185,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
inc pc
|
||||
let desttyp = c.types[c.code[pc].regBx - wordExcess]
|
||||
inc pc
|
||||
let srctyp = c.types[c.code[pc].regBx - wordExcess]
|
||||
|
||||
when hasFFI:
|
||||
let dest = fficast(c.config, regs[rb].node, desttyp)
|
||||
|
||||
@@ -874,11 +874,6 @@ proc genBinaryStmtVar(c: PCtx; n: PNode; opc: TOpcode) =
|
||||
c.freeTemp(tmp)
|
||||
c.freeTemp(dest)
|
||||
|
||||
proc genUnaryStmt(c: PCtx; n: PNode; opc: TOpcode) =
|
||||
let tmp = c.genx(n[1])
|
||||
c.gABC(n, opc, tmp, 0, 0)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
|
||||
if dest < 0: dest = getTemp(c, n.typ)
|
||||
var x = c.getTempRange(n.len-1, slotTempStr)
|
||||
|
||||
@@ -48,9 +48,6 @@ import vmconv, vmmarshal
|
||||
template mathop(op) {.dirty.} =
|
||||
registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`)
|
||||
|
||||
template osop(op) {.dirty.} =
|
||||
registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`)
|
||||
|
||||
template oscommonop(op) {.dirty.} =
|
||||
registerCallback(c, "stdlib.oscommon." & astToStr(op), `op Wrapper`)
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ proc dump*(conf: ConfigRef, pd: ProfileData): string =
|
||||
var data = pd.data
|
||||
result = "\nprof: µs #instr location"
|
||||
for i in 0..<32:
|
||||
var tMax: float
|
||||
var infoMax: ProfileInfo = default(ProfileInfo)
|
||||
var flMax: TLineInfo = default(TLineInfo)
|
||||
for fl, info in data:
|
||||
|
||||
@@ -16,7 +16,6 @@ proc dispatch(x: Base, params: ...) =
|
||||
var disp = newNodeI(nkIfStmt, base.info)
|
||||
|
||||
let nimGetVTableSym = getCompilerProc(g, "nimGetVTable")
|
||||
let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ
|
||||
|
||||
var nTyp = base.typ.n[1].sym.typ
|
||||
var dispatchObject = newSymNode(base.typ.n[1].sym)
|
||||
|
||||
@@ -1154,7 +1154,19 @@ template sysAssert(cond: bool, msg: string) =
|
||||
cstderr.rawWrite "\n"
|
||||
rawQuit 1
|
||||
|
||||
const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript)
|
||||
const
|
||||
hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript)
|
||||
hasDefaultAllocator =
|
||||
hasAlloc and
|
||||
not (defined(useNimRtl) or defined(useMalloc) or defined(gcRegions) or
|
||||
defined(nogc) or defined(boehmgc) or defined(gogc))
|
||||
hasThreadLocalAllocator =
|
||||
hasDefaultAllocator and hasThreadSupport and defined(gcDestructors)
|
||||
|
||||
when hasThreadLocalAllocator:
|
||||
# threadimpl is included before mmdisp provides these implementations.
|
||||
proc initThreadAllocator() {.gcsafe, raises: [].}
|
||||
proc releaseThreadAllocator() {.gcsafe, raises: [].}
|
||||
|
||||
when notJSnotNims and hasAlloc and not defined(nimSeqsV2):
|
||||
proc addChar(s: NimString, c: char): NimString {.compilerproc, gcsafe.}
|
||||
@@ -2425,6 +2437,8 @@ when notJSnotNims and hasAlloc:
|
||||
{.push profiler: off.}
|
||||
include "system/mmdisp"
|
||||
{.pop.}
|
||||
when hasThreadLocalAllocator:
|
||||
initThreadAllocator()
|
||||
{.push stackTrace: off, profiler: off.}
|
||||
when not defined(nimSeqsV2):
|
||||
include "system/sysstr"
|
||||
|
||||
@@ -40,7 +40,7 @@ template track(op, address, size) =
|
||||
#
|
||||
# A deallocation of a small pointer then looks like this
|
||||
#[
|
||||
dealloc -> rawDealloc -> chunk.owner == addr(a) --------------> This thread owns the chunk ------> The current chunk is active -> Chunk is completely unused -----> Chunk references no foreign cells
|
||||
dealloc -> rawDealloc -> chunk.owner == regionOwner(a) -------> This thread owns the chunk ------> The current chunk is active -> Chunk is completely unused -----> Chunk references no foreign cells
|
||||
| | (Add cell into the current chunk) | Return the current chunk back to tlsf
|
||||
| | | |
|
||||
v v v v
|
||||
@@ -63,6 +63,11 @@ const
|
||||
# size of chunks in last matrix bin
|
||||
MaxBigChunkSize = int(1'i32 shl MaxFli - 1'i32 shl (MaxFli-MaxLog2Sli-1))
|
||||
HugeChunkSize = MaxBigChunkSize + 1
|
||||
usesRegionHandles = hasThreadSupport and defined(gcDestructors)
|
||||
# Deliberately *not* `hasThreadLocalAllocator`: this selects the chunk
|
||||
# layout, which is ABI and must match between a `--useNimRtl` client and
|
||||
# the RTL it links against. Whether this module owns a thread local region
|
||||
# is the separate question that `hasThreadLocalAllocator` answers.
|
||||
|
||||
type
|
||||
PTrunk = ptr Trunk
|
||||
@@ -112,11 +117,15 @@ type
|
||||
PChunk = ptr BaseChunk
|
||||
PBigChunk = ptr BigChunk
|
||||
PSmallChunk = ptr SmallChunk
|
||||
SharedFreeLists = array[0..max(1, SmallChunkSize div MemAlign-1), ptr FreeCell]
|
||||
BaseChunk {.pure, inheritable.} = object
|
||||
prevSize: int # size of previous chunk; for coalescing
|
||||
# 0th bit == 1 if 'used
|
||||
size: int # if < PageSize it is a small chunk
|
||||
owner: ptr MemRegion
|
||||
when usesRegionHandles:
|
||||
owner: ptr RegionHandle
|
||||
else:
|
||||
owner: ptr MemRegion
|
||||
|
||||
SmallChunk = object of BaseChunk
|
||||
next, prev: PSmallChunk # chunks of the same size
|
||||
@@ -145,14 +154,16 @@ type
|
||||
next: ptr HeapLinks
|
||||
|
||||
MemRegion = object
|
||||
when usesRegionHandles:
|
||||
regionHandle: ptr RegionHandle
|
||||
when not defined(gcDestructors):
|
||||
minLargeObj, maxLargeObj: int
|
||||
freeSmallChunks: array[0..max(1, SmallChunkSize div MemAlign-1), PSmallChunk]
|
||||
# List of available chunks per size class. Only one is expected to be active per class.
|
||||
when defined(gcDestructors):
|
||||
sharedFreeLists: array[0..max(1, SmallChunkSize div MemAlign-1), ptr FreeCell]
|
||||
# When a thread frees a pointer it did not create, it must not adjust the counters.
|
||||
# Instead, the cell is placed here and deferred until the next allocation.
|
||||
sharedFreeLists: SharedFreeLists
|
||||
# Used directly without threads. Threaded builds use RegionHandle but
|
||||
# retain this 2 KiB spacer: removing it regresses 2-4 KiB allocations.
|
||||
flBitmap: uint32
|
||||
slBitmap: array[RealFli, uint32]
|
||||
matrix: array[RealFli, array[MaxSli, PBigChunk]]
|
||||
@@ -160,7 +171,7 @@ type
|
||||
currMem, maxMem, freeMem, occ: int # memory sizes (allocated from OS)
|
||||
lastSize: int # needed for the case that OS gives us pages linearly
|
||||
when defined(gcDestructors):
|
||||
sharedFreeListBigChunks: PBigChunk # make no attempt at avoiding false sharing for now for this object field
|
||||
sharedFreeListBigChunks: PBigChunk # private pending list with threads; shared queue otherwise
|
||||
|
||||
chunkStarts: IntSet
|
||||
when not defined(gcDestructors):
|
||||
@@ -173,9 +184,24 @@ type
|
||||
when defined(nimTypeNames):
|
||||
allocCounter, deallocCounter: int
|
||||
|
||||
RegionHandle = object
|
||||
# Permanent chunk-owner identity and home of the remote-free queues.
|
||||
sharedFreeLists: SharedFreeLists
|
||||
sharedFreeListBigChunks: PBigChunk
|
||||
# Keep the movable allocator state with its permanent owner while the
|
||||
# owning thread is retired.
|
||||
region: MemRegion
|
||||
next: ptr RegionHandle
|
||||
|
||||
template smallChunkOverhead(): untyped = sizeof(SmallChunk)
|
||||
template bigChunkOverhead(): untyped = sizeof(BigChunk)
|
||||
|
||||
template regionOwner(a: var MemRegion): untyped =
|
||||
when usesRegionHandles:
|
||||
a.regionHandle
|
||||
else:
|
||||
addr a
|
||||
|
||||
when hasThreadSupport:
|
||||
template loada(x: untyped): untyped = atomicLoadN(unsafeAddr x, ATOMIC_RELAXED)
|
||||
template storea(x, y: untyped) = atomicStoreN(unsafeAddr x, y, ATOMIC_RELAXED)
|
||||
@@ -502,6 +528,56 @@ proc pageAddr(p: pointer): PChunk {.inline.} =
|
||||
result = cast[PChunk](cast[int](p) and not PageMask)
|
||||
#sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
|
||||
|
||||
when hasThreadLocalAllocator:
|
||||
var
|
||||
regionPool: ptr RegionHandle
|
||||
regionPoolLock: SysLock
|
||||
initSysLock(regionPoolLock)
|
||||
|
||||
proc moveMemRegion(dest, source: ptr MemRegion) {.inline.} =
|
||||
# MemRegion owns only raw allocator state, so transfer it bitwise and
|
||||
# clear the source to leave exactly one owner.
|
||||
copyMem(dest, source, sizeof(MemRegion))
|
||||
zeroMem(source, sizeof(MemRegion))
|
||||
|
||||
proc acquireMemRegion(a: var MemRegion) {.raises: [], gcsafe.} =
|
||||
if a.regionHandle != nil:
|
||||
return
|
||||
|
||||
acquireSys(regionPoolLock)
|
||||
let handle = regionPool
|
||||
if handle != nil:
|
||||
regionPool = handle.next
|
||||
releaseSys(regionPoolLock)
|
||||
|
||||
if handle == nil:
|
||||
# RegionHandle is larger than llAlloc's one-page metadata slabs and is
|
||||
# retained independently of any checked-out MemRegion.
|
||||
let handleSize = roundup(sizeof(RegionHandle), PageSize)
|
||||
let newHandle = cast[ptr RegionHandle](osAllocPages(handleSize))
|
||||
zeroMem(newHandle, sizeof(RegionHandle))
|
||||
a.regionHandle = newHandle
|
||||
else:
|
||||
moveMemRegion(addr a, addr handle.region)
|
||||
|
||||
proc releaseMemRegion(a: var MemRegion) {.raises: [], gcsafe.} =
|
||||
# Zeroing `a` also clears `a.regionHandle`, which is what keeps a late
|
||||
# `dealloc` on this thread correct: the ownership test can no longer match,
|
||||
# so the cell is routed to its real owner's handle instead of to a region
|
||||
# that is about to be reused. A late *alloc* on the other hand would mint
|
||||
# chunks with a nil owner, so nothing may allocate after this point --
|
||||
# `afterThreadRuns` has already run by the time `threadProcWrapStackFrame`
|
||||
# gets here.
|
||||
if a.regionHandle == nil:
|
||||
return
|
||||
let handle = a.regionHandle
|
||||
moveMemRegion(addr handle.region, addr a)
|
||||
|
||||
acquireSys(regionPoolLock)
|
||||
handle.next = regionPool
|
||||
regionPool = handle
|
||||
releaseSys(regionPoolLock)
|
||||
|
||||
when false:
|
||||
proc writeFreeList(a: MemRegion) =
|
||||
var it = a.freeChunksList
|
||||
@@ -618,7 +694,7 @@ proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk =
|
||||
result.prev = nil
|
||||
# size and not used:
|
||||
result.prevSize = size
|
||||
result.owner = addr a
|
||||
result.owner = regionOwner(a)
|
||||
sysAssert((size and 1) == 0, "splitChunk 2")
|
||||
sysAssert((size and PageMask) == 0,
|
||||
"splitChunk: size is not a multiple of the PageSize")
|
||||
@@ -686,7 +762,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
|
||||
# if we over allocated split the chunk:
|
||||
if result.size > size:
|
||||
splitChunk(a, result, size)
|
||||
result.owner = addr a
|
||||
result.owner = regionOwner(a)
|
||||
else:
|
||||
removeChunkFromMatrix2(a, result, fl, sl)
|
||||
if result.size >= size + PageSize:
|
||||
@@ -694,7 +770,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
|
||||
# set 'used' to true:
|
||||
result.prevSize = 1
|
||||
track("setUsedToFalse", addr result.size, sizeof(int))
|
||||
sysAssert result.owner == addr a, "getBigChunk: No owner set!"
|
||||
sysAssert result.owner == regionOwner(a), "getBigChunk: No owner set!"
|
||||
|
||||
incl(a, a.chunkStarts, pageIndex(result))
|
||||
dec(a.freeMem, size)
|
||||
@@ -710,7 +786,7 @@ proc getHugeChunk(a: var MemRegion; size: int): PBigChunk =
|
||||
result.size = size
|
||||
# set 'used' to true:
|
||||
result.prevSize = 1
|
||||
result.owner = addr a
|
||||
result.owner = regionOwner(a)
|
||||
incl(a, a.chunkStarts, pageIndex(result))
|
||||
|
||||
proc freeHugeChunk(a: var MemRegion; c: PBigChunk) =
|
||||
@@ -791,7 +867,7 @@ proc deallocBigChunk(a: var MemRegion, c: PBigChunk) =
|
||||
when defined(gcDestructors):
|
||||
template atomicPrepend(head, elem: untyped) =
|
||||
# see also https://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange
|
||||
when hasThreadSupport:
|
||||
when usesRegionHandles:
|
||||
while true:
|
||||
elem.next.storea head.loada
|
||||
if atomicCompareExchangeN(addr head, addr elem.next, elem, weak = true, ATOMIC_RELEASE, ATOMIC_RELAXED):
|
||||
@@ -800,30 +876,39 @@ when defined(gcDestructors):
|
||||
elem.next.storea head.loada
|
||||
head.storea elem
|
||||
|
||||
proc addToSharedFreeListBigChunks(a: var MemRegion; c: PBigChunk) {.inline.} =
|
||||
sysAssert c.next == nil, "c.next pointer must be nil"
|
||||
atomicPrepend a.sharedFreeListBigChunks, c
|
||||
when usesRegionHandles:
|
||||
proc addToSharedFreeListBigChunks(handle: ptr RegionHandle;
|
||||
c: PBigChunk) {.inline.} =
|
||||
sysAssert c.next == nil, "c.next pointer must be nil"
|
||||
atomicPrepend handle.sharedFreeListBigChunks, c
|
||||
else:
|
||||
proc addToSharedFreeListBigChunks(a: var MemRegion;
|
||||
c: PBigChunk) {.inline.} =
|
||||
sysAssert c.next == nil, "c.next pointer must be nil"
|
||||
atomicPrepend a.sharedFreeListBigChunks, c
|
||||
|
||||
proc takeFromSharedFreeListBigChunks(a: var MemRegion): PBigChunk {.inline.} =
|
||||
when hasThreadSupport:
|
||||
while true:
|
||||
result = atomicLoadN(addr a.sharedFreeListBigChunks, ATOMIC_ACQUIRE)
|
||||
if result == nil:
|
||||
break
|
||||
let next = result.next.loada
|
||||
var expected = result
|
||||
if atomicCompareExchangeN(addr a.sharedFreeListBigChunks, addr expected, next,
|
||||
weak = true, ATOMIC_ACQUIRE, ATOMIC_RELAXED):
|
||||
result.next.storea nil
|
||||
break
|
||||
else:
|
||||
result = a.sharedFreeListBigChunks
|
||||
if result != nil:
|
||||
a.sharedFreeListBigChunks = result.next
|
||||
result.next = nil
|
||||
when usesRegionHandles:
|
||||
if a.sharedFreeListBigChunks == nil:
|
||||
let sharedHead = addr a.regionHandle.sharedFreeListBigChunks
|
||||
# Detach a batch from the stable remote inbox. The embedded MemRegion
|
||||
# field is now a private pending list and moves with the region.
|
||||
if atomicLoadN(sharedHead, ATOMIC_RELAXED) != nil:
|
||||
a.sharedFreeListBigChunks = atomicExchangeN(sharedHead, nil,
|
||||
ATOMIC_ACQUIRE)
|
||||
result = a.sharedFreeListBigChunks
|
||||
if result != nil:
|
||||
a.sharedFreeListBigChunks = result.next
|
||||
result.next = nil
|
||||
|
||||
proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell; size: int) {.inline.} =
|
||||
atomicPrepend c.owner.sharedFreeLists[size], f
|
||||
when usesRegionHandles:
|
||||
proc addToSharedFreeList(handle: ptr RegionHandle; f: ptr FreeCell;
|
||||
size: int) {.inline.} =
|
||||
atomicPrepend handle.sharedFreeLists[size], f
|
||||
else:
|
||||
proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell;
|
||||
size: int) {.inline.} =
|
||||
atomicPrepend c.owner.sharedFreeLists[size], f
|
||||
|
||||
const MaxSteps = 20
|
||||
|
||||
@@ -846,9 +931,8 @@ when defined(gcDestructors):
|
||||
dec(a.occ, total)
|
||||
|
||||
proc freeDeferredObjects(a: var MemRegion) =
|
||||
# Pop only as many nodes as we can process. Detaching the entire list and
|
||||
# re-enqueuing its unprocessed tail through atomicPrepend would overwrite
|
||||
# that tail's next pointer and lose the rest of the list.
|
||||
# Bound the work per allocation. With threads, takeFromSharedFreeListBigChunks
|
||||
# detaches the shared stack into the region's private pending list first.
|
||||
for _ in 0..MaxSteps:
|
||||
let it = takeFromSharedFreeListBigChunks(a)
|
||||
if it == nil: break
|
||||
@@ -892,17 +976,20 @@ proc rawAlloc(a: var MemRegion, requestedSize: int, alignment: int = 0): pointer
|
||||
|
||||
if size + alignOff <= SmallChunkSize-smallChunkOverhead():
|
||||
template fetchSharedCells(tc: PSmallChunk) =
|
||||
# Consumes cells from (potentially) foreign threads from `a.sharedFreeLists[s]`
|
||||
# Consume cells freed by potentially foreign threads.
|
||||
when defined(gcDestructors):
|
||||
if tc.freeList == nil:
|
||||
when hasThreadSupport:
|
||||
# Steal the entire list from `sharedFreeList`:
|
||||
tc.freeList = atomicExchangeN(addr a.sharedFreeLists[s], nil, ATOMIC_RELAXED)
|
||||
when usesRegionHandles:
|
||||
let sharedHead = addr tc.owner.sharedFreeLists[s]
|
||||
# The owner is the only consumer, so once it observes a non-empty
|
||||
# stack no other thread can make it empty before the exchange.
|
||||
if atomicLoadN(sharedHead, ATOMIC_RELAXED) != nil:
|
||||
tc.freeList = atomicExchangeN(sharedHead, nil, ATOMIC_ACQUIRE)
|
||||
else:
|
||||
tc.freeList = a.sharedFreeLists[s]
|
||||
a.sharedFreeLists[s] = nil
|
||||
# if `tc.freeList` isn't nil, `tc` will gain capacity.
|
||||
# We must calculate how much it gained and how many foreign cells are included.
|
||||
# If `tc.freeList` isn't nil, `tc` gains capacity. Calculate how
|
||||
# much it gained and how many foreign cells are included.
|
||||
compensateCounters(a, tc, size)
|
||||
|
||||
# allocate a small block: for small chunks, we use only its next pointer
|
||||
@@ -921,11 +1008,11 @@ proc rawAlloc(a: var MemRegion, requestedSize: int, alignment: int = 0): pointer
|
||||
c.size = size
|
||||
c.acc = (alignOff + size).uint32
|
||||
c.free = SmallChunkSize - smallChunkOverhead() - alignOff.int32 - size.int32
|
||||
sysAssert c.owner == addr(a), "rawAlloc: No owner set!"
|
||||
sysAssert c.owner == regionOwner(a), "rawAlloc: No owner set!"
|
||||
c.next = nil
|
||||
c.prev = nil
|
||||
# Shared cells are fetched here in case `c.size * 2 >= SmallChunkSize - smallChunkOverhead()`.
|
||||
# For those single cell chunks, we would otherwise have to allocate a new one almost every time.
|
||||
# Fetch deferred cells here for single-cell chunks; otherwise every
|
||||
# allocation of that size would tend to allocate a new chunk.
|
||||
fetchSharedCells(c)
|
||||
if c.free >= size:
|
||||
# Because removals from `a.freeSmallChunks[s]` only happen in the other alloc branch and during dealloc,
|
||||
@@ -963,9 +1050,8 @@ proc rawAlloc(a: var MemRegion, requestedSize: int, alignment: int = 0): pointer
|
||||
dec(c.free, size)
|
||||
sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9")
|
||||
sysAssert(allocInv(a), "rawAlloc: end c != nil")
|
||||
# We fetch deferred cells *after* advancing `c.freeList`/`acc` to adjust `c.free`.
|
||||
# If after the adjustment it turns out there's free cells available,
|
||||
# the chunk stays in `a.freeSmallChunks[s]` and the need for a new chunk is delayed.
|
||||
# Fetch after advancing `freeList`/`acc` so `c.free` can be adjusted. If
|
||||
# cells arrived, keep this chunk active instead of allocating another.
|
||||
fetchSharedCells(c)
|
||||
sysAssert(allocInv(a), "rawAlloc: before c.free < size")
|
||||
if c.free < size:
|
||||
@@ -1030,7 +1116,8 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
# ^ We might access thread foreign storage here.
|
||||
# The other thread cannot possibly free this block as it's still alive.
|
||||
var f = cast[ptr FreeCell](p)
|
||||
if c.owner == addr(a):
|
||||
let owner = c.owner
|
||||
if owner == regionOwner(a):
|
||||
# We own the block, there is no foreign thread involved.
|
||||
dec a.occ, s
|
||||
untrackSize(s)
|
||||
@@ -1093,7 +1180,10 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
when logAlloc: cprintf("dealloc(pointer_%p) # SMALL FROM %p CALLER %p\n", p, c.owner, addr(a))
|
||||
|
||||
when defined(gcDestructors):
|
||||
addToSharedFreeList(c, f, s div MemAlign)
|
||||
when usesRegionHandles:
|
||||
addToSharedFreeList(owner, f, s div MemAlign)
|
||||
else:
|
||||
addToSharedFreeList(c, f, s div MemAlign)
|
||||
sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead() - c.chunkAlignOff) %%
|
||||
s == 0, "rawDealloc 2")
|
||||
else:
|
||||
@@ -1101,10 +1191,14 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
|
||||
when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
|
||||
when logAlloc: cprintf("dealloc(pointer_%p) # BIG %p\n", p, c.owner)
|
||||
when defined(gcDestructors):
|
||||
if c.owner == addr(a):
|
||||
let owner = c.owner
|
||||
if owner == regionOwner(a):
|
||||
deallocBigChunk(a, cast[PBigChunk](c))
|
||||
else:
|
||||
addToSharedFreeListBigChunks(c.owner[], cast[PBigChunk](c))
|
||||
when usesRegionHandles:
|
||||
addToSharedFreeListBigChunks(owner, cast[PBigChunk](c))
|
||||
else:
|
||||
addToSharedFreeListBigChunks(owner[], cast[PBigChunk](c))
|
||||
else:
|
||||
deallocBigChunk(a, cast[PBigChunk](c))
|
||||
|
||||
@@ -1263,6 +1357,13 @@ when defined(nimTypeNames):
|
||||
template instantiateForRegion(allocator: untyped) {.dirty.} =
|
||||
{.push stackTrace: off.}
|
||||
|
||||
when hasThreadLocalAllocator:
|
||||
proc initThreadAllocator() {.gcsafe, raises: [].} =
|
||||
acquireMemRegion(allocator)
|
||||
|
||||
proc releaseThreadAllocator() {.gcsafe, raises: [].} =
|
||||
releaseMemRegion(allocator)
|
||||
|
||||
when defined(nimFulldebug):
|
||||
proc interiorAllocatedPtr*(p: pointer): pointer =
|
||||
result = interiorAllocatedPtr(allocator, p)
|
||||
|
||||
@@ -312,13 +312,17 @@ when not (defined(gcOrc) or defined(gcYrc)):
|
||||
## Forces a full garbage collection pass. With `--mm:arc` a nop.
|
||||
discard
|
||||
|
||||
template setupForeignThreadGc* =
|
||||
## With `--mm:arc` a nop.
|
||||
discard
|
||||
|
||||
template tearDownForeignThreadGc* =
|
||||
## With `--mm:arc` a nop.
|
||||
discard
|
||||
when not hasThreadSupport:
|
||||
template setupForeignThreadGc* = discard
|
||||
template tearDownForeignThreadGc* = discard
|
||||
elif emulatedThreadVars:
|
||||
template setupForeignThreadGc* =
|
||||
{.error: "setupForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".}
|
||||
template tearDownForeignThreadGc* =
|
||||
{.error: "tearDownForeignThreadGc is available only when ``--threads:on`` and ``--tlsEmulation:off`` are used".}
|
||||
elif not hasThreadLocalAllocator:
|
||||
template setupForeignThreadGc* = discard
|
||||
template tearDownForeignThreadGc* = discard
|
||||
|
||||
proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inl.} =
|
||||
result = targetDepth <= source.depth and source.display[targetDepth] == token
|
||||
|
||||
@@ -19,6 +19,13 @@ when not defined(useNimRtl):
|
||||
|
||||
threadType = ThreadType.NimThread
|
||||
|
||||
when hasThreadLocalAllocator and not emulatedThreadVars:
|
||||
proc setupForeignThreadGc*() {.gcsafe, raises: [].} =
|
||||
initThreadAllocator()
|
||||
|
||||
proc tearDownForeignThreadGc*() {.gcsafe, raises: [].} =
|
||||
releaseThreadAllocator()
|
||||
|
||||
when defined(gcDestructors):
|
||||
proc deallocThreadStorage(p: pointer) = c_free(p)
|
||||
else:
|
||||
@@ -83,6 +90,8 @@ else:
|
||||
deallocThreadStorage(thrd.rawStack)
|
||||
|
||||
proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} =
|
||||
when hasThreadLocalAllocator:
|
||||
initThreadAllocator()
|
||||
when defined(boehmgc):
|
||||
boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
|
||||
elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not usesDestructors:
|
||||
@@ -97,6 +106,8 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} =
|
||||
when declared(deallocOsPages): deallocOsPages()
|
||||
else:
|
||||
threadProcWrapDispatch(thrd)
|
||||
when hasThreadLocalAllocator:
|
||||
releaseThreadAllocator()
|
||||
|
||||
template nimThreadProcWrapperBody*(closure: untyped): untyped =
|
||||
var thrd = cast[ptr Thread[TArg]](closure)
|
||||
|
||||
@@ -176,6 +176,7 @@ pkg "unittest2"
|
||||
pkg "unpack"
|
||||
when not defined(arm64):
|
||||
pkg "weave", "nimble install -y cligen@#HEAD; nimble test_gc_arc", useHead = true
|
||||
pkg "web3", "nimble test_slim", useHead = true
|
||||
pkg "websock", "nim c -d:chronicles_log_level=INFO tests/all_tests.nim"
|
||||
pkg "websocket", "nim c websocket.nim"
|
||||
pkg "with"
|
||||
|
||||
@@ -937,3 +937,47 @@ proc mainRegen() =
|
||||
doAssert b.a.c == right
|
||||
|
||||
mainRegen()
|
||||
|
||||
|
||||
from std/typetraits import distinctBase, supportsCopyMem
|
||||
|
||||
block: # bug #26025
|
||||
type
|
||||
M[B] = distinct seq[B]
|
||||
W = object
|
||||
g: U # `U` is only declared below, so it used to be a `tyForward`
|
||||
# here and `W` ended up without `tfHasAsgn`
|
||||
U = M[uint64]
|
||||
|
||||
doAssert not supportsCopyMem(W)
|
||||
|
||||
var h: M[W]
|
||||
seq[W](h).add W(g: U(@[1'u64]))
|
||||
var copied = h
|
||||
for it in items(distinctBase(copied)):
|
||||
doAssert seq[uint64](it.g) == @[1'u64]
|
||||
doAssert seq[uint64](seq[W](h)[0].g) == @[1'u64]
|
||||
|
||||
block: # bug #26025, the propagation has to reach the indirect owners too
|
||||
type
|
||||
M2[B] = distinct seq[B]
|
||||
|
||||
ViaArray = object
|
||||
g: array[2, Late] # the forward type sits inside the field's type
|
||||
|
||||
Outer = object # `Inner` is forward here...
|
||||
a: Inner
|
||||
Inner = object
|
||||
b: Late
|
||||
Late = M2[uint64]
|
||||
|
||||
Reader = object # ...whereas `Outer` is already reified but its
|
||||
z: Outer # own flags were still provisional
|
||||
|
||||
AsTuple = tuple[a: Late]
|
||||
|
||||
doAssert not supportsCopyMem(ViaArray)
|
||||
doAssert not supportsCopyMem(Inner)
|
||||
doAssert not supportsCopyMem(Outer)
|
||||
doAssert not supportsCopyMem(Reader)
|
||||
doAssert not supportsCopyMem(AsTuple)
|
||||
|
||||
16
tests/ccgbugs/t26104.nim
Normal file
16
tests/ccgbugs/t26104.nim
Normal file
@@ -0,0 +1,16 @@
|
||||
discard """
|
||||
action: compile
|
||||
ccodeCheck: "@'((*Result).f);' .*"
|
||||
"""
|
||||
|
||||
# bug #26104: a compile-time-only `typeof` argument was treated as a runtime
|
||||
# alias of the result field, forcing an unnecessarily large temporary and copy.
|
||||
type
|
||||
B = array[131072, byte]
|
||||
Y = object
|
||||
f: B
|
||||
|
||||
proc fill(_: type B): B = discard
|
||||
proc make(): Y = result.f = fill(typeof(result.f))
|
||||
|
||||
discard make()
|
||||
35
tests/ccgbugs/t26112.nim
Normal file
35
tests/ccgbugs/t26112.nim
Normal file
@@ -0,0 +1,35 @@
|
||||
discard """
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
ccodeCheck: "'result.fromScalar = x_p0;'"
|
||||
ccodeCheck: "'result.fromObject = x_p0.fromObject;'"
|
||||
ccodeCheck: "'result.nested.fromNested = x_p0.fromNested;'"
|
||||
"""
|
||||
|
||||
# bug #26112: unrelated parameters were considered potential aliases of the
|
||||
# result location when their types could be contained in the returned object.
|
||||
|
||||
type
|
||||
Inner = object
|
||||
fromNested: int
|
||||
P = object
|
||||
fromScalar: int
|
||||
fromObject: int
|
||||
nested: Inner
|
||||
|
||||
func fromScalar(x: int): P =
|
||||
P(fromScalar: x)
|
||||
|
||||
proc fromObject(x: P): P =
|
||||
P(fromObject: x.fromObject)
|
||||
|
||||
func fromNested(x: Inner): P =
|
||||
P(nested: Inner(fromNested: x.fromNested))
|
||||
|
||||
proc selfAlias(): P =
|
||||
result.fromScalar = 42
|
||||
result = P(fromScalar: result.fromScalar)
|
||||
|
||||
doAssert fromScalar(1).fromScalar == 1
|
||||
doAssert fromObject(P(fromObject: 2)).fromObject == 2
|
||||
doAssert fromNested(Inner(fromNested: 3)).nested.fromNested == 3
|
||||
doAssert selfAlias().fromScalar == 42
|
||||
56
tests/gc/tmove_case_object.nim
Normal file
56
tests/gc/tmove_case_object.nim
Normal file
@@ -0,0 +1,56 @@
|
||||
discard """
|
||||
matrix: "--mm:refc; --mm:orc"
|
||||
"""
|
||||
|
||||
type
|
||||
A = object of RootObj
|
||||
|
||||
V = object
|
||||
case g: bool
|
||||
of true:
|
||||
v: A
|
||||
of false:
|
||||
e: string
|
||||
|
||||
var r = V(g: true, v: A())
|
||||
discard move r
|
||||
GC_fullCollect()
|
||||
|
||||
type
|
||||
Kind = enum nested, other
|
||||
Nested = object
|
||||
case kind: Kind
|
||||
of nested:
|
||||
case enabled: bool
|
||||
of true: payload: A
|
||||
of false: message: string
|
||||
of other:
|
||||
discard
|
||||
|
||||
var n = Nested(kind: nested, enabled: true, payload: A())
|
||||
discard move n
|
||||
GC_fullCollect()
|
||||
|
||||
# Moving from the other branch must keep its value alive and leave the source
|
||||
# in the default state.
|
||||
var s = V(g: false, e: "hello")
|
||||
let moved = move s
|
||||
doAssert moved.e == "hello"
|
||||
doAssert not s.g
|
||||
doAssert s.e.len == 0
|
||||
|
||||
# Reinitializing the zeroed value must also restore embedded object type
|
||||
# headers.
|
||||
type W = object
|
||||
a: A
|
||||
value: V
|
||||
text: string
|
||||
|
||||
var w = W(a: A(), value: V(g: true, v: A()), text: "content")
|
||||
let movedW = move w
|
||||
doAssert movedW.text == "content"
|
||||
doAssert cast[ptr pointer](addr w.a)[] != nil
|
||||
doAssert not w.value.g
|
||||
doAssert w.value.e.len == 0
|
||||
doAssert w.text.len == 0
|
||||
GC_fullCollect()
|
||||
59
tests/threads/tthreadallocatorforeignpool.nim
Normal file
59
tests/threads/tthreadallocatorforeignpool.nim
Normal file
@@ -0,0 +1,59 @@
|
||||
discard """
|
||||
matrix: "--mm:arc --threads:on --tlsEmulation:off; --mm:orc --threads:on --tlsEmulation:off"
|
||||
disabled: "windows"
|
||||
output: "ok"
|
||||
timeout: "30"
|
||||
"""
|
||||
|
||||
import std/posix
|
||||
|
||||
var
|
||||
escaped: pointer
|
||||
reused: pointer
|
||||
|
||||
proc allocateOnForeignThread(_: pointer): pointer {.noconv.} =
|
||||
setupForeignThreadGc()
|
||||
escaped = allocShared(96)
|
||||
cast[ptr int](escaped)[] = 73
|
||||
tearDownForeignThreadGc()
|
||||
result = nil
|
||||
|
||||
proc reuseOnForeignThread(_: pointer): pointer {.noconv.} =
|
||||
setupForeignThreadGc()
|
||||
doAssert cast[ptr int](escaped)[] == 73
|
||||
deallocShared(escaped)
|
||||
reused = allocShared(96)
|
||||
doAssert reused == escaped
|
||||
deallocShared(reused)
|
||||
tearDownForeignThreadGc()
|
||||
result = nil
|
||||
|
||||
proc consumeDeferredFree(_: pointer): pointer {.noconv.} =
|
||||
setupForeignThreadGc()
|
||||
let first = allocShared(96)
|
||||
let second = allocShared(96)
|
||||
# The first allocation advances the active chunk and collects its deferred
|
||||
# foreign frees. The next allocation reuses the remotely returned cell.
|
||||
doAssert second == escaped
|
||||
deallocShared(first)
|
||||
deallocShared(second)
|
||||
tearDownForeignThreadGc()
|
||||
result = nil
|
||||
|
||||
proc run(worker: proc(_: pointer): pointer {.noconv.}) =
|
||||
var thread: Pthread
|
||||
doAssert pthread_create(addr thread, nil, worker, nil) == 0
|
||||
doAssert pthread_join(thread, nil) == 0
|
||||
|
||||
# setup/teardown is the checkout/return boundary. A distinct native thread can
|
||||
# safely inherit the allocator even while one of its allocations is still live.
|
||||
run(allocateOnForeignThread)
|
||||
run(reuseOnForeignThread)
|
||||
|
||||
# A free that arrives while the allocator is idle is queued on its handle and
|
||||
# consumed after that allocator is handed to another foreign thread.
|
||||
run(allocateOnForeignThread)
|
||||
deallocShared(escaped)
|
||||
run(consumeDeferredFree)
|
||||
|
||||
echo "ok"
|
||||
64
tests/threads/tthreadallocatorhandoffrace.nim
Normal file
64
tests/threads/tthreadallocatorhandoffrace.nim
Normal file
@@ -0,0 +1,64 @@
|
||||
discard """
|
||||
matrix: "--mm:arc --threads:on; --mm:orc --threads:on"
|
||||
output: "ok"
|
||||
timeout: "30"
|
||||
"""
|
||||
|
||||
import std/[atomics, typedthreads]
|
||||
|
||||
const
|
||||
pointerCount = 512
|
||||
drainCount = 2048
|
||||
iterations {.intdefine.} = 200
|
||||
sizes = [16, 64, 4000, 4096, 8192]
|
||||
|
||||
var
|
||||
pointers: array[pointerCount, pointer]
|
||||
mayExit: Atomic[bool]
|
||||
|
||||
proc owner() {.thread.} =
|
||||
for i in 0..<pointers.len:
|
||||
let size = sizes[i mod sizes.len]
|
||||
pointers[i] = allocShared(size)
|
||||
cast[ptr byte](pointers[i])[] = byte(i)
|
||||
|
||||
proc borrower() {.thread.} =
|
||||
while not mayExit.load(moAcquire):
|
||||
discard
|
||||
|
||||
proc drain() {.thread.} =
|
||||
var drained: array[drainCount, pointer]
|
||||
for i in 0..<drained.len:
|
||||
drained[i] = allocShared(sizes[i mod sizes.len])
|
||||
for p in drained:
|
||||
deallocShared(p)
|
||||
let occupied = getOccupiedMem()
|
||||
doAssert occupied == 0, "allocator retained " & $occupied & " bytes"
|
||||
|
||||
for _ in 0..<iterations:
|
||||
# The owner retires with live small and big allocations. The borrower checks
|
||||
# out that region while this already-running main thread returns the cells.
|
||||
# This races foreign queue publication against both directions of the
|
||||
# MemRegion handoff without creating an unbounded number of regions.
|
||||
block:
|
||||
var thread: Thread[void]
|
||||
createThread(thread, owner)
|
||||
joinThread(thread)
|
||||
|
||||
mayExit.store(false, moRelaxed)
|
||||
var borrowerThread: Thread[void]
|
||||
createThread(borrowerThread, borrower)
|
||||
for i, p in pointers:
|
||||
if i == pointers.len div 2:
|
||||
# Let the borrower tear the allocator down while the second half of the
|
||||
# foreign publications are still in flight.
|
||||
mayExit.store(true, moRelease)
|
||||
deallocShared(p)
|
||||
joinThread(borrowerThread)
|
||||
|
||||
block:
|
||||
var thread: Thread[void]
|
||||
createThread(thread, drain)
|
||||
joinThread(thread)
|
||||
|
||||
echo "ok"
|
||||
92
tests/threads/tthreadallocatorpool.nim
Normal file
92
tests/threads/tthreadallocatorpool.nim
Normal file
@@ -0,0 +1,92 @@
|
||||
discard """
|
||||
matrix: "--mm:arc --threads:on; --mm:orc --threads:on"
|
||||
output: "ok"
|
||||
timeout: "30"
|
||||
"""
|
||||
|
||||
import std/[atomics, typedthreads]
|
||||
|
||||
const concurrentThreads = 4
|
||||
|
||||
var
|
||||
escaped: pointer
|
||||
reused: pointer
|
||||
bigEscaped: pointer
|
||||
roundAddresses: array[2, array[concurrentThreads, pointer]]
|
||||
ready: Atomic[int]
|
||||
mayExit: Atomic[bool]
|
||||
|
||||
proc allocateEscaped() {.thread.} =
|
||||
escaped = allocShared(64)
|
||||
cast[ptr int](escaped)[] = 42
|
||||
|
||||
proc consumeAfterHandoff() {.thread.} =
|
||||
doAssert cast[ptr int](escaped)[] == 42
|
||||
deallocShared(escaped)
|
||||
reused = allocShared(64)
|
||||
doAssert reused == escaped
|
||||
deallocShared(reused)
|
||||
|
||||
# A live allocation can outlast its original thread. The next thread receives
|
||||
# the same allocator and its stable handle makes the deallocation local again.
|
||||
block:
|
||||
var thread: Thread[void]
|
||||
createThread(thread, allocateEscaped)
|
||||
joinThread(thread)
|
||||
createThread(thread, consumeAfterHandoff)
|
||||
joinThread(thread)
|
||||
|
||||
proc allocateBigEscaped() {.thread.} =
|
||||
bigEscaped = allocShared(8192)
|
||||
cast[ptr int](bigEscaped)[] = 91
|
||||
|
||||
proc consumeBigAfterHandoff() {.thread.} =
|
||||
doAssert cast[ptr int](bigEscaped)[] == 91
|
||||
deallocShared(bigEscaped)
|
||||
let p = allocShared(8192)
|
||||
doAssert p == bigEscaped
|
||||
deallocShared(p)
|
||||
|
||||
# Big chunks use a separate deferred-free queue on the stable handle.
|
||||
block:
|
||||
var thread: Thread[void]
|
||||
createThread(thread, allocateBigEscaped)
|
||||
joinThread(thread)
|
||||
createThread(thread, consumeBigAfterHandoff)
|
||||
joinThread(thread)
|
||||
|
||||
proc allocateConcurrently(arg: tuple[round, index: int]) {.thread.} =
|
||||
let p = allocShared(80)
|
||||
roundAddresses[arg.round][arg.index] = p
|
||||
deallocShared(p)
|
||||
discard ready.fetchAdd(1, moRelease)
|
||||
while not mayExit.load(moAcquire):
|
||||
discard
|
||||
|
||||
proc runConcurrentRound(round: int) =
|
||||
var threads: array[concurrentThreads, Thread[tuple[round, index: int]]]
|
||||
ready.store(0, moRelaxed)
|
||||
mayExit.store(false, moRelaxed)
|
||||
for i in 0..<threads.len:
|
||||
createThread(threads[i], allocateConcurrently, (round, i))
|
||||
while ready.load(moAcquire) != concurrentThreads:
|
||||
discard
|
||||
mayExit.store(true, moRelease)
|
||||
for thread in threads.mitems:
|
||||
joinThread(thread)
|
||||
|
||||
# The first round establishes the peak number of simultaneous allocators. The
|
||||
# following rounds must reuse those regions instead of reserving one region per
|
||||
# new thread.
|
||||
runConcurrentRound(0)
|
||||
for _ in 0..<32:
|
||||
runConcurrentRound(1)
|
||||
for p in roundAddresses[1]:
|
||||
var found = false
|
||||
for old in roundAddresses[0]:
|
||||
if p == old:
|
||||
found = true
|
||||
break
|
||||
doAssert found
|
||||
|
||||
echo "ok"
|
||||
56
tests/threads/tthreadallocatorpoolrace.nim
Normal file
56
tests/threads/tthreadallocatorpoolrace.nim
Normal file
@@ -0,0 +1,56 @@
|
||||
discard """
|
||||
matrix: "--mm:arc --threads:on; --mm:orc --threads:on"
|
||||
output: "ok"
|
||||
timeout: "30"
|
||||
"""
|
||||
|
||||
import std/[atomics, typedthreads]
|
||||
|
||||
const
|
||||
pointerCount = 1024
|
||||
iterations = 100
|
||||
|
||||
var
|
||||
pointers: array[pointerCount, pointer]
|
||||
drainPointers: array[pointerCount, pointer]
|
||||
ready: Atomic[bool]
|
||||
start: Atomic[bool]
|
||||
remoteDone: Atomic[bool]
|
||||
|
||||
proc owner() {.thread.} =
|
||||
for i in 0..<pointers.len:
|
||||
pointers[i] = allocShared(16 + (i mod 8) * 16)
|
||||
ready.store(true, moRelease)
|
||||
while not start.load(moAcquire):
|
||||
discard
|
||||
|
||||
# Race allocator activity against remote frees. The owner can consume cells
|
||||
# while the remote thread is still publishing entries to its handle.
|
||||
while not remoteDone.load(moAcquire):
|
||||
for i in 0..<8:
|
||||
let p = allocShared(16 + i * 16)
|
||||
deallocShared(p)
|
||||
|
||||
# Exhaust local free lists so all remaining remote lists are consumed before
|
||||
# this allocator is returned to the pool.
|
||||
for i in 0..<drainPointers.len:
|
||||
drainPointers[i] = allocShared(16 + (i mod 8) * 16)
|
||||
for p in drainPointers:
|
||||
deallocShared(p)
|
||||
doAssert getOccupiedMem() == 0
|
||||
|
||||
for _ in 0..<iterations:
|
||||
ready.store(false, moRelaxed)
|
||||
start.store(false, moRelaxed)
|
||||
remoteDone.store(false, moRelaxed)
|
||||
var thread: Thread[void]
|
||||
createThread(thread, owner)
|
||||
while not ready.load(moAcquire):
|
||||
discard
|
||||
start.store(true, moRelease)
|
||||
for p in pointers:
|
||||
deallocShared(p)
|
||||
remoteDone.store(true, moRelease)
|
||||
joinThread(thread)
|
||||
|
||||
echo "ok"
|
||||
Reference in New Issue
Block a user