Merge branch 'devel' into araq-orc-hotfix

This commit is contained in:
araq
2023-05-09 20:36:09 +02:00
45 changed files with 531 additions and 122 deletions

View File

@@ -454,6 +454,7 @@
static libraries.
- When compiling for Release the flag `-fno-math-errno` is used for GCC.
- When compiling for Release the flag `--build-id=none` is used for GCC Linker.
## Docgen

View File

@@ -687,7 +687,7 @@ type
mIsPartOf, mAstToStr, mParallel,
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
mNewString, mNewStringOfCap, mParseBiggestFloat,
mMove, mWasMoved, mDestroy, mTrace,
mMove, mWasMoved, mDup, mDestroy, mTrace,
mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
@@ -944,7 +944,8 @@ type
attachedAsgn,
attachedSink,
attachedTrace,
attachedDeepCopy
attachedDeepCopy,
attachedDup
TType* {.acyclic.} = object of TIdObj # \
# types are identical iff they have the
@@ -1515,7 +1516,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
const
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
"=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"]
"=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy", "=dup"]
proc `$`*(s: PSym): string =
if s != nil:

View File

@@ -68,7 +68,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
result.links[j] = h.links[Mhalf + j]
else:
for j in 0..<Mhalf:
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
result.vals[j] = move h.vals[Mhalf + j]
else:
shallowCopy(result.vals[j], h.vals[Mhalf + j])
@@ -91,7 +91,7 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
if less(key, h.keys[j]): break
inc j
for i in countdown(h.entries, j+1):
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
h.vals[i] = move h.vals[i-1]
else:
shallowCopy(h.vals[i], h.vals[i-1])

View File

@@ -269,7 +269,7 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
# Bug https://github.com/status-im/nimbus-eth2/issues/1549
# Aliasing is preferred over stack overflows.
# Also don't regress for non ARC-builds, too risky.
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcOrc} and
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
getSize(p.config, a.lode.typ) < 1024:
getTemp(p, a.lode.typ, result, needsInit=false)
genAssignment(p, result, a, {})

View File

@@ -382,7 +382,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else:
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
of tyArray:
if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcOrc, gcHooks}:
if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
genGenericAsgn(p, dest, src, flags)
else:
linefmt(p, cpsStmts,
@@ -2346,6 +2346,11 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
genAssignment(p, d, a, {})
resetLoc(p, a)
proc genDup(p: BProc; src: TLoc; d: var TLoc; n: PNode) =
if d.k == locNone: getTemp(p, n.typ, d)
linefmt(p, cpsStmts, "#nimDupRef((void**)$1, (void*)$2);$n",
[addrLoc(p.config, d), rdLoc(src)])
proc genDestroy(p: BProc; n: PNode) =
if optSeqDestructors in p.config.globalOptions:
let arg = n[1].skipAddr
@@ -2398,7 +2403,7 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) =
let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon,
prepareForMutation = e[1].kind == nkHiddenDeref and
e[1].typ.skipTypes(abstractInst).kind == tyString and
p.config.selectedGC in {gcArc, gcOrc})
p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc})
if d.k == locNone: getTemp(p, e.typ, d)
linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y])
when false:
@@ -2580,7 +2585,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e)
expr(p, n, d)
of mDeepCopy:
if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
localError(p.config, e.info,
"for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
@@ -2597,6 +2602,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
of mAccessTypeField: genAccessTypeField(p, e, d)
of mSlice: genSlice(p, e, d)
of mTrace: discard "no code to generate"
of mDup:
var a: TLoc
let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
initLocExpr(p, x, a)
genDup(p, a, d, e)
else:
when defined(debugMagics):
echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind

View File

@@ -1581,11 +1581,11 @@ proc genMainProc(m: BModule) =
m.includeHeader("<libc/component.h>")
let initStackBottomCall =
if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope
if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc}: "".rope
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])
inc(m.labels)
let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcOrc}: "1" else: "0"
let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: "1" else: "0"
appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine, isVolatile])
if m.config.target.targetOS == osWindows and
@@ -1725,7 +1725,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
if sfSystemModule in m.module.flags:
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", []))
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
if m.s[cfsInitProc].len > 0:
@@ -2177,7 +2177,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode =
cgsym(m, "rawWrite")
# raise dependencies on behalf of genMainProc
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
cgsym(m, "initStackBottomWith")
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
cgsym(m, "initThreadVarsEmulation")

View File

@@ -238,7 +238,7 @@ proc processCompile(conf: ConfigRef; filename: string) =
extccomp.addExternalFileToCompile(conf, found)
const
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
@@ -262,6 +262,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
of "go": result = conf.selectedGC == gcGo
of "none": result = conf.selectedGC == gcNone
of "stack", "regions": result = conf.selectedGC == gcRegions
of "atomicarc": result = conf.selectedGC == gcAtomicArc
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
of "opt":
case arg.normalize
@@ -516,14 +517,7 @@ proc initOrcDefines*(conf: ConfigRef) =
if conf.exc == excNone and conf.backend != backendCpp:
conf.exc = excGoto
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) =
if isOrc:
conf.selectedGC = gcOrc
defineSymbol(conf.symbols, "gcorc")
else:
conf.selectedGC = gcArc
defineSymbol(conf.symbols, "gcarc")
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) =
defineSymbol(conf.symbols, "gcdestructors")
incl conf.globalOptions, optSeqDestructors
incl conf.globalOptions, optTinyRtti
@@ -562,9 +556,17 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
conf.selectedGC = gcMarkAndSweep
defineSymbol(conf.symbols, "gcmarkandsweep")
of "destructors", "arc":
registerArcOrc(pass, conf, false)
conf.selectedGC = gcArc
defineSymbol(conf.symbols, "gcarc")
registerArcOrc(pass, conf)
of "orc":
registerArcOrc(pass, conf, true)
conf.selectedGC = gcOrc
defineSymbol(conf.symbols, "gcorc")
registerArcOrc(pass, conf)
of "atomicarc":
conf.selectedGC = gcAtomicArc
defineSymbol(conf.symbols, "gcatomicarc")
registerArcOrc(pass, conf)
of "hooks":
conf.selectedGC = gcHooks
defineSymbol(conf.symbols, "gchooks")

View File

@@ -154,5 +154,6 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasGenericDefine")
defineSymbol("nimHasDefineAliases")
defineSymbol("nimHasWarnBareExcept")
defineSymbol("nimHasDup")
defineSymbol("nimHasChecksums")

View File

@@ -493,7 +493,7 @@ proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
gen(c, body)
if root.kind == skResult:
genImplicitReturn(c)
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
result = c.code # will move
else:
shallowCopy(result, c.code)

View File

@@ -66,7 +66,7 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} =
result = ast.hasDestructor(t)
when toDebug.len > 0:
# for more effective debugging
if not result and c.graph.config.selectedGC in {gcArc, gcOrc}:
if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
assert(not containsGarbageCollectedRef(t))
proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
@@ -425,17 +425,34 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let tmp = c.getTemp(s, n.typ, n.info)
if hasDestructor(c, n.typ):
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, isFromSink = true)
result.add m
let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
let op = getAttachedOp(c.graph, typ, attachedDup)
if op != nil:
let src = p(n, c, s, normal)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
genOp(c, op, src)
)
elif typ.kind == tyRef:
let src = p(n, c, s, normal)
result.add newTreeI(nkFastAsgn,
src.info, tmp,
newTreeIT(nkCall, src.info, src.typ,
newSymNode(createMagic(c.graph, c.idgen, "`=dup`", mDup)),
src)
)
else:
result.add c.genWasMoved(tmp)
var m = c.genCopy(tmp, n, {})
m.add p(n, c, s, normal)
c.finishCopy(m, n, isFromSink = true)
result.add m
if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
message(c.graph.config, n.info, hintPerformance,
("passing '$1' to a sink parameter introduces an implicit copy; " &
"if possible, rearrange your program's control flow to prevent it") % $n)
else:
if c.graph.config.selectedGC in {gcArc, gcOrc}:
if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
assert(not containsManagedMemory(n.typ))
if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
@@ -478,7 +495,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
result = arg
proc cycleCheck(n: PNode; c: var Con) =
if c.graph.config.selectedGC != gcArc: return
if c.graph.config.selectedGC notin {gcArc, gcAtomicArc}: return
var value = n[1]
if value.kind == nkClosure:
value = value[1]
@@ -821,7 +838,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing
if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
result[0] = copyTree(n[0])
if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}:
if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc}:
let destroyOld = c.genDestroy(result[1])
result = newTree(nkStmtList, destroyOld, result)
else:

View File

@@ -2174,6 +2174,13 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) =
genReset(p, n)
#lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
proc genDup(p: PProc; n: PNode; r: var TCompRes) =
var a: TCompRes
r.kind = resVal
r.res = p.getTemp()
gen(p, n[1], a)
lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
var a: TCompRes
r.res = rope("[")
@@ -2368,6 +2375,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
r.kind = resExpr
of mMove:
genMove(p, n, r)
of mDup:
genDup(p, n, r)
else:
genCall(p, n, r)
#else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);

View File

@@ -510,7 +510,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
rememberSplit(splitComma)
wrSpace em
of openPars:
if tok.strongSpaceA and not em.endsInWhite and
if tsLeading in tok.spacing and not em.endsInWhite and
(not em.wasExportMarker or tok.tokType == tkCurlyDotLe):
wrSpace em
wr(em, $tok.tokType, ltSomeParLe)
@@ -528,7 +528,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
wr(em, $tok.tokType, ltOther)
if not em.inquote: wrSpace(em)
of tkOpr, tkDotDot:
if em.inquote or (((not tok.strongSpaceA) and tok.strongSpaceB == tsNone) and
if em.inquote or (tok.spacing == {} and
tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
# bug #9504: remember to not spacify a keyword:
lastTokWasTerse = true
@@ -538,7 +538,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
if not em.endsInWhite: wrSpace(em)
wr(em, tok.ident.s, ltOpr)
template isUnary(tok): bool =
tok.strongSpaceB == tsNone and tok.strongSpaceA
tok.spacing == {tsLeading}
if not isUnary(tok):
rememberSplit(splitBinary)

View File

@@ -94,19 +94,18 @@ type
base2, base8, base16
TokenSpacing* = enum
tsNone, tsTrailing, tsEof
tsLeading, tsTrailing, tsEof
Token* = object # a Nim token
tokType*: TokType # the type of the token
base*: NumericalBase # the numerical base; only valid for int
# or float literals
spacing*: set[TokenSpacing] # spaces around token
indent*: int # the indentation; != -1 if the token has been
# preceded with indentation
ident*: PIdent # the parsed identifier
iNumber*: BiggestInt # the parsed integer literal
fNumber*: BiggestFloat # the parsed floating point literal
base*: NumericalBase # the numerical base; only valid for int
# or float literals
strongSpaceA*: bool # leading spaces of an operator
strongSpaceB*: TokenSpacing # trailing spaces of an operator
literal*: string # the parsed (string) literal; and
# documentation comments are here too
line*, col*: int
@@ -178,7 +177,7 @@ proc initToken*(L: var Token) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
L.strongSpaceA = false
L.spacing = {}
L.literal = ""
L.fNumber = 0.0
L.base = base10
@@ -191,7 +190,7 @@ proc fillToken(L: var Token) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
L.strongSpaceA = false
L.spacing = {}
setLen(L.literal, 0)
L.fNumber = 0.0
L.base = base10
@@ -960,13 +959,15 @@ proc getOperator(L: var Lexer, tok: var Token) =
tokenEnd(tok, pos-1)
# advance pos but don't store it in L.bufpos so the next token (which might
# be an operator too) gets the preceding spaces:
tok.strongSpaceB = tsNone
tok.spacing = tok.spacing - {tsTrailing, tsEof}
var trailing = false
while L.buf[pos] == ' ':
inc pos
if tok.strongSpaceB != tsTrailing:
tok.strongSpaceB = tsTrailing
trailing = true
if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
tok.strongSpaceB = tsEof
tok.spacing.incl(tsEof)
elif trailing:
tok.spacing.incl(tsTrailing)
proc getPrecedence*(tok: Token): int =
## Calculates the precedence of the given token.
@@ -1077,7 +1078,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
when defined(nimpretty): tok.literal.add "\L"
if isDoc:
when not defined(nimpretty): tok.literal.add "\n"
inc tok.iNumber
var c = toStrip
while L.buf[pos] == ' ' and c > 0:
inc pos
@@ -1096,8 +1096,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
proc scanComment(L: var Lexer, tok: var Token) =
var pos = L.bufpos
tok.tokType = tkComment
# iNumber contains the number of '\n' in the token
tok.iNumber = 0
assert L.buf[pos+1] == '#'
when defined(nimpretty):
tok.commentOffsetA = L.offsetBase + pos
@@ -1140,7 +1138,6 @@ proc scanComment(L: var Lexer, tok: var Token) =
while L.buf[pos] == ' ' and c > 0:
inc pos
dec c
inc tok.iNumber
else:
if L.buf[pos] > ' ':
L.indentAhead = indent
@@ -1153,7 +1150,7 @@ proc scanComment(L: var Lexer, tok: var Token) =
proc skip(L: var Lexer, tok: var Token) =
var pos = L.bufpos
tokenBegin(tok, pos)
tok.strongSpaceA = false
tok.spacing.excl(tsLeading)
when defined(nimpretty):
var hasComment = false
var commentIndent = L.currLineIndent
@@ -1164,8 +1161,7 @@ proc skip(L: var Lexer, tok: var Token) =
case L.buf[pos]
of ' ':
inc(pos)
if not tok.strongSpaceA:
tok.strongSpaceA = true
tok.spacing.incl(tsLeading)
of '\t':
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
inc(pos)
@@ -1187,7 +1183,7 @@ proc skip(L: var Lexer, tok: var Token) =
pos = L.bufpos
else:
break
tok.strongSpaceA = false
tok.spacing.excl(tsLeading)
when defined(nimpretty):
if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent
if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'):

View File

@@ -159,7 +159,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
let f = n.sym
let b = if c.kind == attachedTrace: y else: y.dotField(f)
if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
c.g.config.selectedGC in {gcArc, gcOrc, gcHooks}) or
c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or
enforceDefaultOp:
defaultOp(c, f.typ, body, x.dotField(f), b)
else:
@@ -463,6 +463,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
body.add genWasMovedCall(c, op, x)
result = true
of attachedDup:
assert false, "cannot happen"
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
temp.typ = getSysType(c.g, body.info, tyInt)
@@ -546,6 +549,8 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# follow all elements:
forallElements(c, t, body, x, y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
@@ -584,6 +589,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
return # protect from recursion
body.add newHookCall(c, op, x, y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
@@ -600,6 +607,8 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedTrace:
discard "strings are atomic and have no inner elements that are to trace"
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc cyclicType*(t: PType): bool =
case t.kind
@@ -699,7 +708,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
#echo "can follow ", elemType, " static ", isFinal(elemType)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
## Closures are really like refs except they always use a virtual destructor
@@ -749,6 +759,8 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedTrace:
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case c.kind
@@ -774,6 +786,8 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
var actions = newNodeI(nkStmtList, c.info)
@@ -800,6 +814,8 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
if c.kind == attachedDeepCopy:
@@ -811,7 +827,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
call[1] = y
body.add newAsgnStmt(x, call)
elif (optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}:
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
xx.typ = getSysType(c.g, c.info, tyPointer)
case c.kind
@@ -835,6 +851,8 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
@@ -851,6 +869,8 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
of attachedDeepCopy: assert(false, "cannot happen")
of attachedTrace: discard
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x)
of attachedDup:
assert false, "cannot happen"
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
case t.kind
@@ -859,7 +879,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
tyPtr, tyUncheckedArray, tyVar, tyLent:
defaultOp(c, t, body, x, y)
of tyRef:
if c.g.config.selectedGC in {gcArc, gcOrc}:
if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
atomicRefOp(c, t, body, x, y)
elif (optOwnedRefs in c.g.config.globalOptions and
optRefCheck in c.g.config.options):
@@ -868,7 +888,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
defaultOp(c, t, body, x, y)
of tyProc:
if t.callConv == ccClosure:
if c.g.config.selectedGC in {gcArc, gcOrc}:
if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
atomicClosureOp(c, t, body, x, y)
else:
closureOp(c, t, body, x, y)
@@ -966,7 +986,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
result.typ = newProcType(info, nextTypeId(idgen), owner)
result.typ.addParam dest
if kind notin {attachedDestructor, attachedWasMoved}:
if kind notin {attachedDestructor, attachedWasMoved, attachedDup}:
result.typ.addParam src
if kind == attachedAsgn and g.config.selectedGC == gcOrc and
@@ -1006,7 +1026,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
let dest = result.typ.n[1].sym
let d = newDeref(newSymNode(dest))
let src = if kind in {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
let src = if kind in {attachedDestructor, attachedWasMoved, attachedDup}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
else: newSymNode(result.typ.n[2].sym)
# register this operation already:
@@ -1019,7 +1039,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
result.ast[bodyPos].add newAsgnStmt(d, src)
else:
var tk: TTypeKind
if g.config.selectedGC in {gcArc, gcOrc, gcHooks}:
if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
else:
tk = tyNone # no special casing for strings and seqs

View File

@@ -95,7 +95,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind)
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
sym.kind != skResult and # ignore `result`
sym.kind != skTemp and # ignore temporary variables created by the compiler
@@ -136,7 +136,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
## Check symbol uses match their definition's style.
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
hintName in ctx.config.notes and # ignore if name checks are not requested
ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
sym.kind != skTemp and # ignore temporary variables created by the compiler
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
@@ -147,6 +147,10 @@ proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragm
if pragmaName != wanted:
lintReport(conf, info, wanted, pragmaName)
template checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
if {optStyleHint, optStyleError} * conf.globalOptions != {}:
checkPragmaUseImpl(conf, info, w, pragmaName)
template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
## Check builtin pragma uses match their definition's style.
## Note: This only applies to builtin pragmas, not user pragmas.
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
hintName in ctx.config.notes and # ignore if name checks are not requested
(sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
checkPragmaUseImpl(ctx.config, info, w, pragmaName)

View File

@@ -226,7 +226,7 @@ proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile)
proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
assert fileIdx.int32 >= 0
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
conf.m.fileInfos[fileIdx.int32].hash = hash
else:
shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
@@ -234,7 +234,7 @@ proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
assert fileIdx.int32 >= 0
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
result = conf.m.fileInfos[fileIdx.int32].hash
else:
shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)

View File

@@ -22,7 +22,7 @@ proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent
let last = first+identLen(line, first)-1
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x
else:
system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
@@ -38,7 +38,7 @@ proc replaceComment*(conf: ConfigRef; info: TLineInfo) =
if line[first] != '#': inc first
var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x
else:
system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)

View File

@@ -184,6 +184,7 @@ type
gcRegions = "regions"
gcArc = "arc"
gcOrc = "orc"
gcAtomicArc = "atomicArc"
gcMarkAndSweep = "markAndSweep"
gcHooks = "hooks"
gcRefc = "refc"

View File

@@ -301,14 +301,13 @@ proc isRightAssociative(tok: Token): bool {.inline.} =
proc isUnary(tok: Token): bool =
## Check if the given token is a unary operator
tok.tokType in {tkOpr, tkDotDot} and
tok.strongSpaceB == tsNone and
tok.strongSpaceA
tok.spacing == {tsLeading}
proc checkBinary(p: Parser) {.inline.} =
## Check if the current parser token is a binary operator.
# we don't check '..' here as that's too annoying
if p.tok.tokType == tkOpr:
if p.tok.strongSpaceB == tsTrailing and not p.tok.strongSpaceA:
if p.tok.spacing == {tsTrailing}:
parMessage(p, warnInconsistentSpacing, prettyTok(p.tok))
#| module = stmt ^* (';' / IND{=})
@@ -516,7 +515,7 @@ proc dotExpr(p: var Parser, a: PNode): PNode =
optInd(p, result)
result.add(a)
result.add(parseSymbol(p, smAfterDot))
if p.tok.tokType == tkBracketLeColon and not p.tok.strongSpaceA:
if p.tok.tokType == tkBracketLeColon and tsLeading notin p.tok.spacing:
var x = newNodeI(nkBracketExpr, p.parLineInfo)
# rewrite 'x.y[:z]()' to 'y[z](x)'
x.add result[1]
@@ -525,7 +524,7 @@ proc dotExpr(p: var Parser, a: PNode): PNode =
var y = newNodeI(nkCall, p.parLineInfo)
y.add x
y.add result[0]
if p.tok.tokType == tkParLe and not p.tok.strongSpaceA:
if p.tok.tokType == tkParLe and tsLeading notin p.tok.spacing:
exprColonEqExprListAux(p, tkParRi, y)
result = y
@@ -883,7 +882,7 @@ proc primarySuffix(p: var Parser, r: PNode,
case p.tok.tokType
of tkParLe:
# progress guaranteed
if p.tok.strongSpaceA:
if tsLeading in p.tok.spacing:
result = commandExpr(p, result, mode)
break
result = namedParams(p, result, nkCall, tkParRi)
@@ -895,13 +894,13 @@ proc primarySuffix(p: var Parser, r: PNode,
result = parseGStrLit(p, result)
of tkBracketLe:
# progress guaranteed
if p.tok.strongSpaceA:
if tsLeading in p.tok.spacing:
result = commandExpr(p, result, mode)
break
result = namedParams(p, result, nkBracketExpr, tkBracketRi)
of tkCurlyLe:
# progress guaranteed
if p.tok.strongSpaceA:
if tsLeading in p.tok.spacing:
result = commandExpr(p, result, mode)
break
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
@@ -2525,7 +2524,7 @@ proc parseAll(p: var Parser): PNode =
setEndInfo()
proc checkFirstLineIndentation*(p: var Parser) =
if p.tok.indent != 0 and p.tok.strongSpaceA:
if p.tok.indent != 0 and tsLeading in p.tok.spacing:
parMessage(p, errInvalidIndentation)
proc parseTopLevelStmt(p: var Parser): PNode =

View File

@@ -535,7 +535,7 @@ proc processCompile(c: PContext, n: PNode) =
n[i] = c.semConstExpr(c, n[i])
case n[i].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
result = n[i].strVal
else:
shallowCopy(result, n[i].strVal)
@@ -676,6 +676,7 @@ proc processPragma(c: PContext, n: PNode, i: int) =
invalidPragma(c, n)
var userPragma = newSym(skTemplate, it[1].ident, c.idgen, c.module, it.info, c.config.options)
styleCheckDef(c, userPragma)
userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1])
strTableAdd(c.userPragmas, userPragma)
@@ -863,7 +864,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
else:
let k = whichKeyword(ident)
if k in validPragmas:
checkPragmaUse(c.config, key.info, k, ident.s)
checkPragmaUse(c, key.info, k, ident.s, (if sym != nil: sym else: c.module))
case k
of wExportc, wExportCpp:
makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info)

View File

@@ -227,7 +227,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
if optOwnedRefs in oldGlobalOptions:
conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
defineSymbol(conf.symbols, "nimv2")
if conf.selectedGC in {gcArc, gcOrc}:
if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
conf.globalOptions.incl {optTinyRtti, optSeqDestructors}
defineSymbol(conf.symbols, "nimv2")

View File

@@ -249,7 +249,7 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass:
return false
let conf = c.config
if conf.selectedGC in {gcArc, gcOrc}:
if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
let d = skipTypes(dst, abstractInst)
let s = skipTypes(src, abstractInst)
if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal:

View File

@@ -1480,7 +1480,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
let param = params[i].sym
let typ = param.typ
if isSinkTypeForParam(typ) or
(t.config.selectedGC in {gcArc, gcOrc} and
(t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
(isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
createTypeBoundOps(t, typ, param.info)
if isOutParam(typ) and param.id notin t.init:

View File

@@ -1815,9 +1815,12 @@ proc whereToBindTypeHook(c: PContext; t: PType): PType =
proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
let t = s.typ
var noError = false
let cond = if op in {attachedDestructor, attachedWasMoved}:
let cond = case op
of {attachedDestructor, attachedWasMoved}:
t.len == 2 and t[0] == nil and t[1].kind == tyVar
elif op == attachedTrace:
of attachedDup:
t.len == 2 and t[0] != nil and t[1].kind == tyVar
of attachedTrace:
t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer
else:
t.len >= 2 and t[0] == nil
@@ -1843,9 +1846,13 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
localError(c.config, n.info, errGenerated,
"type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
if not noError and sfSystemModule notin s.owner.flags:
if op == attachedTrace:
case op
of attachedTrace:
localError(c.config, n.info, errGenerated,
"signature for '=trace' must be proc[T: object](x: var T; env: pointer)")
of attachedDup:
localError(c.config, n.info, errGenerated,
"signature for '=dup' must be proc[T: object](x: var T): T")
else:
localError(c.config, n.info, errGenerated,
"signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
@@ -1938,6 +1945,9 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
of "=wasmoved":
if s.magic != mWasMoved:
bindTypeHook(c, s, n, attachedWasMoved)
of "=dup":
if s.magic != mDup:
bindTypeHook(c, s, n, attachedDup)
else:
if sfOverriden in s.flags:
localError(c.config, n.info, errGenerated,

View File

@@ -16,6 +16,7 @@ const
errIntLiteralExpected = "integer literal expected"
errWrongNumberOfVariables = "wrong number of variables"
errInvalidOrderInEnumX = "invalid order in enum '$1'"
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"
@@ -147,7 +148,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
declarePureEnumField(c, e)
if (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
wrongRedefinition(c, e.info, e.name.s, conflict.info)
inc(counter)
if counter == high(typeof(counter)):
if i > 1 and result.n[i-2].sym.position == high(int):
localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))])
else:
inc(counter)
if isPure and sfExported in result.sym.flags:
addPureEnum(c, LazySym(sym: result.sym))
if tfNotNil in e.typ.flags and not hasNull:
@@ -222,8 +227,8 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
proc fitDefaultNode(c: PContext, n: PNode): PType =
let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
let oldType = n[^1].typ
n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
let oldType = n[^1].typ
n[^1].flags.incl nfSem
if n[^2].kind != nkEmpty:
if expectedType != nil and oldType != expectedType:
@@ -963,7 +968,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
t.rawAddSonNoPropagationOfTypeFlags result
result = t
else: discard
if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc}:
if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
result.flags.incl tfHasAsgn
proc findEnforcedStaticType(t: PType): PType =

View File

@@ -37,7 +37,7 @@ proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
else: srFlowVar
proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind =
if c.selectedGC in {gcArc, gcOrc}: fvBlob
if c.selectedGC in {gcArc, gcOrc, gcAtomicArc}: fvBlob
elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
elif containsGarbageCollectedRef(t): fvInvalid
else: fvBlob
@@ -66,7 +66,7 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator;
vpart[2] = if varInit.isNil: v else: vpart[1]
varSection.add vpart
if varInit != nil:
if g.config.selectedGC in {gcArc, gcOrc}:
if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
# inject destructors pass will do its own analysis
varInit.add newFastMoveStmt(g, newSymNode(result), v)
else:

View File

@@ -121,7 +121,7 @@ template decodeBx(k: untyped) {.dirty.} =
ensureKind(k)
template move(a, b: untyped) {.dirty.} =
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
a = move b
else:
system.shallowCopy(a, b)
@@ -550,7 +550,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
# Used to keep track of where the execution is resumed.
var savedPC = -1
var savedFrame: PStackFrame
when defined(gcArc) or defined(gcOrc):
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
template updateRegsAlias = discard
template regs: untyped = tos.slots
else:

View File

@@ -443,6 +443,7 @@ proc rawGenLiteral(c: PCtx; n: PNode): int =
result = c.constants.len
#assert(n.kind != nkCall)
n.flags.incl nfAllConst
n.flags.excl nfIsRef
c.constants.add n
internalAssert c.config, result < regBxMax
@@ -1400,6 +1401,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
# c.gABx(n, opcNodeToReg, a, a)
# c.genAsgnPatch(arg, a)
c.freeTemp(a)
of mDup:
let arg = n[1]
let a = c.genx(arg)
if dest < 0: dest = c.getTemp(arg.typ)
gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a)
c.freeTemp(a)
of mNodeId:
c.genUnaryABC(n, dest, opcNodeId)
else:

View File

@@ -364,3 +364,9 @@ tcc.options.always = "-w"
clang.options.linker %= "${clang.options.linker} -s"
clang.cpp.options.linker %= "${clang.cpp.options.linker} -s"
@end
# Linker: Skip "Build-ID metadata strings" in binaries when build for release.
@if release or danger:
gcc.options.linker %= "${gcc.options.linker} -Wl,--build-id=none"
gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -Wl,--build-id=none"
@end

View File

@@ -7485,6 +7485,37 @@ generates:
```
size pragma
-----------
Nim automatically determines the size of an enum.
But when wrapping a C enum type, it needs to be of a specific size.
The `size pragma` allows specifying the size of the enum type.
```Nim
type
EventType* {.size: sizeof(uint32).} = enum
QuitEvent,
AppTerminating,
AppLowMemory
doAssert sizeof(EventType) == sizeof(uint32)
```
The `size pragma` can also specify the size of an `importc` incomplete object type
so that one can get the size of it at compile time even if it was declared without fields.
```Nim
type
AtomicFlag* {.importc: "atomic_flag", header: "<stdatomic.h>", size: 1.} = object
static:
# if AtomicFlag didn't have the size pragma, this code would result in a compile time error.
echo sizeof(AtomicFlag)
```
The `size pragma` accepts only the values 1, 2, 4 or 8.
Align pragma
------------
@@ -8041,8 +8072,8 @@ CodegenDecl pragma
------------------
The `codegenDecl` pragma can be used to directly influence Nim's code
generator. It receives a format string that determines how the variable
or proc is declared in the generated code.
generator. It receives a format string that determines how the variable,
proc or object type is declared in the generated code.
For variables, $1 in the format string represents the type of the variable,
$2 is the name of the variable, and each appearance of $# represents $1/$2
@@ -8077,7 +8108,30 @@ will generate this code:
```c
__interrupt void myinterrupt()
```
For object types, the $1 represents the name of the object type, $2 is the list of
fields and $3 is the base type.
```nim
const strTemplate = """
struct $1 {
$2
};
"""
type Foo {.codegenDecl:strTemplate.} = object
a, b: int
```
will generate this code:
```c
struct Foo {
NI a;
NI b;
};
```
`cppNonPod` pragma
------------------

View File

@@ -125,7 +125,13 @@ __AVR__
NIM_THREADVAR declaration based on
http://stackoverflow.com/questions/18298280/how-to-declare-a-variable-as-thread-local-portably
*/
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
#if defined _WIN32
# if defined _MSC_VER || defined __DMC__ || defined __BORLANDC__
# define NIM_THREADVAR __declspec(thread)
# else
# define NIM_THREADVAR __thread
# endif
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
# define NIM_THREADVAR _Thread_local
#elif defined _WIN32 && ( \
defined _MSC_VER || \

View File

@@ -347,6 +347,12 @@ proc arrPut[I: Ordinal;T,S](a: T; i: I;
proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} =
## Generic `destructor`:idx: implementation that can be overridden.
discard
when defined(nimHasDup):
proc `=dup`*[T](x: ref T): ref T {.inline, magic: "Dup".} =
## Generic `dup` implementation that can be overridden.
discard
proc `=sink`*[T](x: var T; y: T) {.inline, nodestroy, magic: "Asgn".} =
## Generic `sink`:idx: implementation that can be overridden.
when defined(gcArc) or defined(gcOrc):

View File

@@ -57,6 +57,21 @@ elif defined(nimArcIds):
const traceId = -1
when defined(gcAtomicArc) and hasThreadSupport:
template decrement(cell: Cell): untyped =
discard atomicDec(cell.rc, rcIncrement)
template increment(cell: Cell): untyped =
discard atomicInc(cell.rc, rcIncrement)
template count(x: Cell): untyped =
atomicLoadN(x.rc.addr, ATOMIC_ACQUIRE) shr rcShift
else:
template decrement(cell: Cell): untyped =
dec(cell.rc, rcIncrement)
template increment(cell: Cell): untyped =
inc(cell.rc, rcIncrement)
template count(x: Cell): untyped =
x.rc shr rcShift
proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
let hdrSize = align(sizeof(RefHeader), alignment)
let s = size + hdrSize
@@ -69,7 +84,7 @@ proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
atomicInc gRefId
if head(result).refId == traceId:
writeStackTrace()
cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).rc shr rcShift)
cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).count)
when traceCollector:
cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
@@ -90,21 +105,21 @@ proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
atomicInc gRefId
if head(result).refId == traceId:
writeStackTrace()
cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).rc shr rcShift)
cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).count)
when traceCollector:
cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
dec head(p).rc, rcIncrement
decrement head(p)
proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
when defined(nimArcDebug):
if head(p).refId == traceId:
writeStackTrace()
cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).rc shr rcShift)
cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).count)
inc head(p).rc, rcIncrement
increment head(p)
when traceCollector:
cprintf("[INCREF] %p\n", head(p))
@@ -173,17 +188,21 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
when defined(nimArcDebug):
if cell.refId == traceId:
writeStackTrace()
cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.rc shr rcShift)
cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.count)
if (cell.rc and not rcMask) == 0:
if cell.count == 0:
result = true
when traceCollector:
cprintf("[ABOUT TO DESTROY] %p\n", cell)
else:
dec cell.rc, rcIncrement
decrement cell
# According to Lins it's correct to do nothing else here.
when traceCollector:
cprintf("[DeCREF] %p\n", cell)
cprintf("[DECREF] %p\n", cell)
proc nimDupRef(dest: ptr pointer, src: pointer) {.compilerRtl, inl.} =
dest[] = src
if src != nil: nimIncRef src
proc GC_unref*[T](x: ref T) =
## New runtime only supports this operation for 'ref T'.

View File

@@ -16,18 +16,21 @@ type
d: PCellArray
proc contains(s: CellSeq, c: PCell): bool {.inline.} =
for i in 0 .. s.len-1:
if s.d[i] == c: return true
for i in 0 ..< s.len:
if s.d[i] == c:
return true
return false
proc resize(s: var CellSeq) =
s.cap = s.cap * 3 div 2
let d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
copyMem(d, s.d, s.len * sizeof(PCell))
dealloc(s.d)
s.d = d
proc add(s: var CellSeq, c: PCell) {.inline.} =
if s.len >= s.cap:
s.cap = s.cap * 3 div 2
var d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
copyMem(d, s.d, s.len * sizeof(PCell))
dealloc(s.d)
s.d = d
# XXX: realloc?
resize(s)
s.d[s.len] = c
inc(s.len)

View File

@@ -267,7 +267,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
if not em.endsInWhite: wr(" ")
wr(tok.ident.s)
template isUnary(tok): bool =
tok.strongSpaceB == tsNone and tok.strongSpaceA
tok.spacing == {tsLeading}
if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
wr(" ")

View File

@@ -272,7 +272,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
if not em.endsInWhite: wr(" ")
wr(tok.ident.s)
template isUnary(tok): bool =
tok.strongSpaceB == tsNone and tok.strongSpaceA
tok.spacing == {tsLeading}
if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
wr(" ")

70
tests/arc/tdup.nim Normal file
View File

@@ -0,0 +1,70 @@
discard """
cmd: "nim c --mm:arc --expandArc:foo --hints:off $file"
nimout: '''
--expandArc: foo
var
x
:tmpD
s
:tmpD_1
x = Ref(id: 8)
inc:
:tmpD = `=dup`(x)
:tmpD
inc:
let blitTmp = x
blitTmp
var id_1 = 777
s = RefCustom(id_2: addr(id_1))
inc_1 :
:tmpD_1 = `=dup`(s)
:tmpD_1
inc_1 :
let blitTmp_1 = s
blitTmp_1
-- end of expandArc ------------------------
'''
"""
type
Ref = ref object
id: int
RefCustom = object
id: ptr int
proc inc(x: sink Ref) =
inc x.id
proc inc(x: sink RefCustom) =
inc x.id[]
proc `=dup`(x: var RefCustom): RefCustom =
result.id = x.id
proc foo =
var x = Ref(id: 8)
inc(x)
inc(x)
var id = 777
var s = RefCustom(id: addr id)
inc s
inc s
foo()
proc foo2 =
var x = Ref(id: 8)
inc(x)
doAssert x.id == 9
inc(x)
doAssert x.id == 10
var id = 777
var s = RefCustom(id: addr id)
inc s
doAssert s.id[] == 778
inc s
doAssert s.id[] == 779
foo2()

View File

@@ -113,8 +113,7 @@ block :tmp:
var :tmpD
sym = shadowScope.symbols[i]
addInterfaceDecl(c):
`=wasMoved`(:tmpD)
`=copy_1`(:tmpD, sym)
:tmpD = `=dup`(sym)
:tmpD
inc(i, 1)
`=destroy`(shadowScope)

View File

@@ -176,3 +176,11 @@ block: # bug #12589
when not defined(gcRefc):
doAssert $typ() == "wkbPoint25D"
block: # bug #21280
type
Test = enum
B = 19
A = int64.high()
doAssert ord(A) == int64.high()

View File

@@ -591,6 +591,29 @@ template main {.dirty.} =
mainSync()
block: # bug #21801
func evaluate(i: int): float =
0.0
func evaluate(): float =
0.0
type SearchOptions = object
evaluation: proc(): float = evaluate
block:
func evaluate(): float =
0.0
type SearchOptions = object
evaluation: proc(): float = evaluate
block:
func evaluate(i: int): float =
0.0
type SearchOptions = object
evaluation = evaluate
static: main()
main()

View File

@@ -0,0 +1 @@
include ../thint

View File

@@ -0,0 +1,2 @@
# See `tstyleCheck`
# Needed to mark `mstyleCheck` as a foreign package.

View File

@@ -0,0 +1,16 @@
discard """
matrix: "--errorMax:0 --styleCheck:error"
action: compile
"""
import foreign_package/foreign_package
# This call tests that:
# - an instantiation of a generic in a foreign package doesn't raise errors
# when the generic body contains:
# - definition and usage violations
# - builtin pragma usage violations
# - user pragma usage violations
# - definition violations in foreign packages are ignored
# - usage violations in foreign packages are ignored
genericProc[int]()

View File

@@ -0,0 +1,43 @@
discard """
matrix: "--styleCheck:hint"
action: compile
"""
# Test violating ident definition:
{.pragma: user_pragma.} #[tt.Hint
^ 'user_pragma' should be: 'userPragma' [Name] ]#
# Test violating ident usage style matches definition style:
{.userPragma.} #[tt.Hint
^ 'userPragma' should be: 'user_pragma' [template declared in thint.nim(7, 9)] [Name] ]#
# Test violating builtin pragma usage style:
{.no_side_effect.}: #[tt.Hint
^ 'no_side_effect' should be: 'noSideEffect' [Name] ]#
discard 0
# Test:
# - definition style violation
# - user pragma usage style violation
# - builtin pragma usage style violation
proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Hint
^ 'generic_proc' should be: 'genericProc' [Name]; tt.Hint
^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Hint
^ 'userPragma' should be: 'user_pragma' [template declared in thint.nim(7, 9)] [Name] ]#
# Test definition style violation:
let snake_case = 0 #[tt.Hint
^ 'snake_case' should be: 'snakeCase' [Name] ]#
# Test user pragma definition style violation:
{.pragma: another_user_pragma.} #[tt.Hint
^ 'another_user_pragma' should be: 'anotherUserPragma' [Name] ]#
# Test user pragma usage style violation:
{.anotherUserPragma.} #[tt.Hint
^ 'anotherUserPragma' should be: 'another_user_pragma' [template declared in thint.nim(31, 11)] [Name] ]#
# Test violating builtin pragma usage style:
{.no_side_effect.}: #[tt.Hint
^ 'no_side_effect' should be: 'noSideEffect' [Name] ]#
# Test usage style violation:
discard snakeCase #[tt.Hint
^ 'snakeCase' should be: 'snake_case' [let declared in thint.nim(28, 7)] [Name] ]#
generic_proc[int]()

69
tests/vm/t21704.nim Normal file
View File

@@ -0,0 +1,69 @@
discard """
matrix: "--hints:off"
nimout: '''
Found 2 tests to run.
Found 3 benches to compile.
--passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow
--passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow
--passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow
'''
"""
# bug #21704
import std/strformat
const testDesc: seq[string] = @[
"tests/t_hash_sha256_vs_openssl.nim",
"tests/t_cipher_chacha20.nim"
]
const benchDesc = [
"bench_sha256",
"bench_hash_to_curve",
"bench_ethereum_bls_signatures"
]
proc setupTestCommand(flags, path: string): string =
return "nim c -r " &
flags &
&" --nimcache:nimcache/{path} " & # Commenting this out also solves the issue
path
proc testBatch(commands: var string, flags, path: string) =
commands &= setupTestCommand(flags, path) & '\n'
proc setupBench(benchName: string): string =
var runFlags = if false: " -r "
else: " " # taking this branch is needed to trigger the bug
echo runFlags # Somehow runflags isn't reset in corner cases
runFlags &= " --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow "
echo runFlags
return "nim c " &
runFlags &
&" benchmarks/{benchName}.nim"
proc buildBenchBatch(commands: var string, benchName: string) =
let command = setupBench(benchName)
commands &= command & '\n'
proc addTestSet(cmdFile: var string) =
echo "Found " & $testDesc.len & " tests to run."
for path in testDesc:
var flags = "" # This is important
cmdFile.testBatch(flags, path)
proc addBenchSet(cmdFile: var string) =
echo "Found " & $benchDesc.len & " benches to compile."
for bd in benchDesc:
cmdFile.buildBenchBatch(bd)
proc task_bug() =
var cmdFile: string
cmdFile.addTestSet() # Comment this out and there is no bug
cmdFile.addBenchSet()
static: task_bug()