fixex merge conflicts

This commit is contained in:
Araq 2018-06-08 19:50:36 +02:00
commit 8e9551b1c7
53 changed files with 2648 additions and 355 deletions

View file

@ -65,6 +65,7 @@
- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs. - Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs.
- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division. - Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals. - Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
- Added the proc ``math.prod`` for product of elements in openArray.
### Library changes ### Library changes
@ -90,6 +91,7 @@
API". Using the Nim compiler and its VM as a scripting engine has never been API". Using the Nim compiler and its VM as a scripting engine has never been
easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
use the Nim VM in a native Nim application. use the Nim VM in a native Nim application.
- The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
### Language additions ### Language additions

View file

@ -83,7 +83,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
result = isInCurrentFrame(p, n.sons[0]) result = isInCurrentFrame(p, n.sons[0])
else: discard else: discard
proc genIndexCheck(p: BProc; arr, idx: TLoc) proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
proc openArrayLoc(p: BProc, n: PNode): Rope = proc openArrayLoc(p: BProc, n: PNode): Rope =
var a: TLoc var a: TLoc
@ -97,8 +97,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
initLocExpr(p, q[3], c) initLocExpr(p, q[3], c)
# but first produce the required index checks: # but first produce the required index checks:
if optBoundsCheck in p.options: if optBoundsCheck in p.options:
genIndexCheck(p, a, b) genBoundsCheck(p, a, b, c)
genIndexCheck(p, a, c)
let ty = skipTypes(a.t, abstractVar+{tyPtr}) let ty = skipTypes(a.t, abstractVar+{tyPtr})
case ty.kind case ty.kind
of tyArray: of tyArray:

View file

@ -351,7 +351,9 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
else: else:
useStringh(p.module) useStringh(p.module)
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n", # bug #4799, keep the memcpy for a while
#"memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
"$1 = $2;$n",
rdLoc(dest), rdLoc(src)) rdLoc(dest), rdLoc(src))
of tySet: of tySet:
if mapType(p.config, ty) == ctArray: if mapType(p.config, ty) == ctArray:
@ -873,21 +875,26 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) =
putIntoDest(p, d, n, putIntoDest(p, d, n,
ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage)
proc genIndexCheck(p: BProc; arr, idx: TLoc) = proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
let ty = skipTypes(arr.t, abstractVarRange) let ty = skipTypes(arr.t, abstractVarRange)
case ty.kind case ty.kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError();$n", linefmt(p, cpsStmts,
rdLoc(idx), rdLoc(arr)) "if ($2-$1 != -1 && " &
"((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))) #raiseIndexError();$n",
rdLoc(a), rdLoc(b), rdLoc(arr))
of tyArray: of tyArray:
let first = intLiteral(firstOrd(p.config, ty)) let first = intLiteral(firstOrd(p.config, ty))
if tfUncheckedArray notin ty.flags: if tfUncheckedArray notin ty.flags:
linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError();$n", linefmt(p, cpsStmts,
rdCharLoc(idx), first, intLiteral(lastOrd(p.config, ty))) "if ($2-$1 != -1 && " &
"($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n",
rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty)))
of tySequence, tyString: of tySequence, tyString:
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", "if ($2-$1 != -1 && " &
rdLoc(idx), rdLoc(arr), lenField(p)) "(!$3 || (NU)($1) >= (NU)($3->$4) || (NU)($2) >= (NU)($3->$4))) #raiseIndexError();$n",
rdLoc(a), rdLoc(b), rdLoc(arr), lenField(p))
else: discard else: discard
proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
@ -2326,7 +2333,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
of nkParForStmt: genParForStmt(p, n) of nkParForStmt: genParForStmt(p, n)
of nkState: genState(p, n) of nkState: genState(p, n)
of nkGotoState: genGotoState(p, n) of nkGotoState: genGotoState(p, n)
of nkBreakState: genBreakState(p, n) of nkBreakState: genBreakState(p, n, d)
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
proc genNamedConstExpr(p: BProc, n: PNode): Rope = proc genNamedConstExpr(p: BProc, n: PNode): Rope =

View file

@ -157,6 +157,39 @@ proc genState(p: BProc, n: PNode) =
elif n0.kind == nkStrLit: elif n0.kind == nkStrLit:
linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope) linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope)
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# Called by return and break stmts.
# Deals with issues faced when jumping out of try/except/finally stmts,
var stack = newSeq[tuple[n: PNode, inExcept: bool]](0)
for i in countup(1, howManyTrys):
let tryStmt = p.nestedTryStmts.pop
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
# Pop safe points generated by try
if not tryStmt.inExcept:
linefmt(p, cpsStmts, "#popSafePoint();$n")
# Pop this try-stmt of the list of nested trys
# so we don't infinite recurse on it in the next step.
stack.add(tryStmt)
# Find finally-stmt for this try-stmt
# and generate a copy of its sons
var finallyStmt = lastSon(tryStmt.n)
if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0])
# push old elements again:
for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i])
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
# Pop exceptions that was handled by the
# except-blocks we are in
for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genGotoState(p: BProc, n: PNode) = proc genGotoState(p: BProc, n: PNode) =
# we resist the temptation to translate it into duff's device as it later # we resist the temptation to translate it into duff's device as it later
# will be translated into computed gotos anyway for GCC at least: # will be translated into computed gotos anyway for GCC at least:
@ -167,7 +200,11 @@ proc genGotoState(p: BProc, n: PNode) =
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)]) lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
p.beforeRetNeeded = true p.beforeRetNeeded = true
lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", []) lineF(p, cpsStmts, "case -1:$n", [])
blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlockLen)
lineF(p, cpsStmts, " goto BeforeRet_;$n", [])
var statesCounter = lastOrd(p.config, n.sons[0].typ) var statesCounter = lastOrd(p.config, n.sons[0].typ)
if n.len >= 2 and n[1].kind == nkIntLit: if n.len >= 2 and n[1].kind == nkIntLit:
statesCounter = n[1].intVal statesCounter = n[1].intVal
@ -177,17 +214,17 @@ proc genGotoState(p: BProc, n: PNode) =
lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)]) lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
lineF(p, cpsStmts, "}$n", []) lineF(p, cpsStmts, "}$n", [])
proc genBreakState(p: BProc, n: PNode) = proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
initLoc(d, locExpr, n, OnUnknown)
if n.sons[0].kind == nkClosure: if n.sons[0].kind == nkClosure:
# XXX this produces quite inefficient code!
initLocExpr(p, n.sons[0].sons[1], a) initLocExpr(p, n.sons[0].sons[1], a)
lineF(p, cpsStmts, "if (((NI*) $1)[1] < 0) break;$n", [rdLoc(a)]) d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
else: else:
initLocExpr(p, n.sons[0], a) initLocExpr(p, n.sons[0], a)
# the environment is guaranteed to contain the 'state' field at offset 1: # the environment is guaranteed to contain the 'state' field at offset 1:
lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)]) d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
# lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)])
proc genGotoVar(p: BProc; value: PNode) = proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}: if value.kind notin {nkCharLit..nkUInt64Lit}:
@ -328,40 +365,6 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
else: internalError(p.config, n.info, "genIf()") else: internalError(p.config, n.info, "genIf()")
if sonsLen(n) > 1: fixLabel(p, lend) if sonsLen(n) > 1: fixLabel(p, lend)
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# Called by return and break stmts.
# Deals with issues faced when jumping out of try/except/finally stmts,
var stack = newSeq[tuple[n: PNode, inExcept: bool]](0)
for i in countup(1, howManyTrys):
let tryStmt = p.nestedTryStmts.pop
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
# Pop safe points generated by try
if not tryStmt.inExcept:
linefmt(p, cpsStmts, "#popSafePoint();$n")
# Pop this try-stmt of the list of nested trys
# so we don't infinite recurse on it in the next step.
stack.add(tryStmt)
# Find finally-stmt for this try-stmt
# and generate a copy of its sons
var finallyStmt = lastSon(tryStmt.n)
if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0])
# push old elements again:
for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i])
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
# Pop exceptions that was handled by the
# except-blocks we are in
for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genReturnStmt(p: BProc, t: PNode) = proc genReturnStmt(p: BProc, t: PNode) =
if nfPreventCg in t.flags: return if nfPreventCg in t.flags: return
p.beforeRetNeeded = true p.beforeRetNeeded = true
@ -772,6 +775,13 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
else: else:
genOrdinalCase(p, t, d) genOrdinalCase(p, t, d)
proc genRestoreFrameAfterException(p: BProc) =
if optStackTrace in p.module.config.options:
if not p.hasCurFramePointer:
p.hasCurFramePointer = true
p.procSec(cpsLocals).add(ropecg(p.module, "\tTFrame* _nimCurFrame;$n", []))
p.procSec(cpsInit).add(ropecg(p.module, "\t_nimCurFrame = #getFrame();$n", []))
linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n")
proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
# code to generate: # code to generate:
@ -791,8 +801,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
# finallyPart(); # finallyPart();
template genExceptBranchBody(body: PNode) {.dirty.} = template genExceptBranchBody(body: PNode) {.dirty.} =
if optStackTrace in p.options: genRestoreFrameAfterException(p)
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
expr(p, body, d) expr(p, body, d)
if not isEmptyType(t.typ) and d.k == locNone: if not isEmptyType(t.typ) and d.k == locNone:
@ -895,8 +904,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
endBlock(p) endBlock(p)
startBlock(p, "else {$n") startBlock(p, "else {$n")
linefmt(p, cpsStmts, "#popSafePoint();$n") linefmt(p, cpsStmts, "#popSafePoint();$n")
if optStackTrace in p.options: genRestoreFrameAfterException(p)
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
p.nestedTryStmts[^1].inExcept = true p.nestedTryStmts[^1].inExcept = true
var i = 1 var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch): while (i < length) and (t.sons[i].kind == nkExceptBranch):

View file

@ -68,6 +68,8 @@ type
prc*: PSym # the Nim proc that this C proc belongs to prc*: PSym # the Nim proc that this C proc belongs to
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
threadVarAccessed*: bool # true if the proc already accessed some threadvar threadVarAccessed*: bool # true if the proc already accessed some threadvar
hasCurFramePointer*: bool # true if _nimCurFrame var needed to recover after
# exception is generated
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
currLineInfo*: TLineInfo # AST codegen will make this superfluous currLineInfo*: TLineInfo # AST codegen will make this superfluous
nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]]

1306
compiler/closureiters.nim Normal file

File diff suppressed because it is too large Load diff

View file

@ -7,12 +7,11 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# This include file implements lambda lifting for the transformator. # This file implements lambda lifting for the transformator.
import import
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
idents, renderer, types, magicsys, lowerings, tables, idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos
modulegraphs, lineinfos
discard """ discard """
The basic approach is that captured vars need to be put on the heap and The basic approach is that captured vars need to be put on the heap and
@ -126,7 +125,7 @@ proc newCall(a: PSym, b: PNode): PNode =
result.add newSymNode(a) result.add newSymNode(a)
result.add b result.add b
proc createStateType(g: ModuleGraph; iter: PSym): PType = proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType =
var n = newNodeI(nkRange, iter.info) var n = newNodeI(nkRange, iter.info)
addSon(n, newIntNode(nkIntLit, -1)) addSon(n, newIntNode(nkIntLit, -1))
addSon(n, newIntNode(nkIntLit, 0)) addSon(n, newIntNode(nkIntLit, 0))
@ -137,8 +136,8 @@ proc createStateType(g: ModuleGraph; iter: PSym): PType =
rawAddSon(result, intType) rawAddSon(result, intType)
proc createStateField(g: ModuleGraph; iter: PSym): PSym = proc createStateField(g: ModuleGraph; iter: PSym): PSym =
result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info, {}) result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info)
result.typ = createStateType(g, iter) result.typ = createClosureIterStateType(g, iter)
proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType = proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
# YYY meh, just add the state field for every closure for now, it's too # YYY meh, just add the state field for every closure for now, it's too
@ -146,12 +145,12 @@ proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
result = createObj(g, owner, info, final=false) result = createObj(g, owner, info, final=false)
rawAddField(result, createStateField(g, owner)) rawAddField(result, createStateField(g, owner))
proc getIterResult(iter: PSym; cache: IdentCache): PSym = proc getClosureIterResult*(g: ModuleGraph; iter: PSym): PSym =
if resultPos < iter.ast.len: if resultPos < iter.ast.len:
result = iter.ast.sons[resultPos].sym result = iter.ast.sons[resultPos].sym
else: else:
# XXX a bit hacky: # XXX a bit hacky:
result = newSym(skResult, getIdent(cache, ":result"), iter, iter.info, {}) result = newSym(skResult, getIdent(g.cache, ":result"), iter, iter.info, {})
result.typ = iter.typ.sons[0] result.typ = iter.typ.sons[0]
incl(result.flags, sfUsed) incl(result.flags, sfUsed)
iter.ast.add newSymNode(result) iter.ast.add newSymNode(result)
@ -400,6 +399,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
if not c.capturedVars.containsOrIncl(s.id): if not c.capturedVars.containsOrIncl(s.id):
let obj = getHiddenParam(c.graph, owner).typ.lastSon let obj = getHiddenParam(c.graph, owner).typ.lastSon
#let obj = c.getEnvTypeForOwner(s.owner).lastSon #let obj = c.getEnvTypeForOwner(s.owner).lastSon
if s.name.id == getIdent(c.graph.cache, ":state").id:
obj.n[0].sym.id = -s.id
else:
addField(obj, s, c.graph.cache) addField(obj, s, c.graph.cache)
# but always return because the rest of the proc is only relevant when # but always return because the rest of the proc is only relevant when
# ow != owner: # ow != owner:
@ -598,7 +601,7 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
localError(d.graph.config, n.info, "internal error: not part of closure object type") localError(d.graph.config, n.info, "internal error: not part of closure object type")
result = n result = n
proc getStateField(g: ModuleGraph; owner: PSym): PSym = proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
@ -625,7 +628,7 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
var a = newNodeI(nkAsgn, n.sons[0].info) var a = newNodeI(nkAsgn, n.sons[0].info)
var retVal = liftCapturedVars(n.sons[0], owner, d, c) var retVal = liftCapturedVars(n.sons[0], owner, d, c)
addSon(a, newSymNode(getIterResult(owner, d.graph.cache))) addSon(a, newSymNode(getClosureIterResult(d.graph, owner)))
addSon(a, retVal) addSon(a, retVal)
retStmt.add(a) retStmt.add(a)
else: else:
@ -718,7 +721,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
# echo renderTree(s.getBody, {renderIds}) # echo renderTree(s.getBody, {renderIds})
let oldInContainer = c.inContainer let oldInContainer = c.inContainer
c.inContainer = 0 c.inContainer = 0
let body = wrapIterBody(d.graph, liftCapturedVars(s.getBody, s, d, c), s) var body = liftCapturedVars(s.getBody, s, d, c)
if oldIterTransf in d.graph.config.features:
body = wrapIterBody(d.graph, body, s)
if c.envvars.getOrDefault(s.id).isNil: if c.envvars.getOrDefault(s.id).isNil:
s.ast.sons[bodyPos] = body s.ast.sons[bodyPos] = body
else: else:
@ -761,9 +766,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
if n[1].kind == nkClosure: result = n[1] if n[1].kind == nkClosure: result = n[1]
else: else:
if owner.isIterator: if owner.isIterator:
if n.kind == nkYieldStmt: if oldIterTransf in d.graph.config.features and n.kind == nkYieldStmt:
return transformYield(n, owner, d, c) return transformYield(n, owner, d, c)
elif n.kind == nkReturnStmt: elif oldIterTransf in d.graph.config.features and n.kind == nkReturnStmt:
return transformReturn(n, owner, d, c) return transformReturn(n, owner, d, c)
elif nfLL in n.flags: elif nfLL in n.flags:
# special case 'when nimVm' due to bug #3636: # special case 'when nimVm' due to bug #3636:
@ -811,7 +816,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo
fn.typ.callConv = oldCC fn.typ.callConv = oldCC
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode = proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
# XXX conf.cmd == cmdCompileToJS does not suffice! The compiletime stuff needs # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ... # the transformation even when compiling to JS ...
# However we can do lifting for the stuff which is *only* compiletime. # However we can do lifting for the stuff which is *only* compiletime.
@ -820,6 +825,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
if body.kind == nkEmpty or ( if body.kind == nkEmpty or (
g.config.cmd == cmdCompileToJS and not isCompileTime) or g.config.cmd == cmdCompileToJS and not isCompileTime) or
fn.skipGenericOwner.kind != skModule: fn.skipGenericOwner.kind != skModule:
# ignore forward declaration: # ignore forward declaration:
result = body result = body
tooEarly = true tooEarly = true
@ -831,10 +837,12 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN
d.somethingToDo = true d.somethingToDo = true
if d.somethingToDo: if d.somethingToDo:
var c = initLiftingPass(fn) var c = initLiftingPass(fn)
var newBody = liftCapturedVars(body, fn, d, c) result = liftCapturedVars(body, fn, d, c)
if c.envvars.getOrDefault(fn.id) != nil: if c.envvars.getOrDefault(fn.id) != nil:
newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody) result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
result = wrapIterBody(g, newBody, fn)
if oldIterTransf in g.config.features:
result = wrapIterBody(g, result, fn)
else: else:
result = body result = body
#if fn.name.s == "get2": #if fn.name.s == "get2":
@ -872,7 +880,8 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
cl = createClosure() cl = createClosure()
while true: while true:
let i = foo(cl) let i = foo(cl)
nkBreakState(cl.state) if (nkBreakState(cl.state)):
break
... ...
""" """
if liftingHarmful(g.config, owner): return body if liftingHarmful(g.config, owner): return body
@ -932,5 +941,16 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
loopBody.sons[0] = v2 loopBody.sons[0] = v2
var bs = newNodeI(nkBreakState, body.info) var bs = newNodeI(nkBreakState, body.info)
bs.addSon(call.sons[0]) bs.addSon(call.sons[0])
loopBody.sons[1] = bs
let ibs = newNodeI(nkIfStmt, body.info)
let elifBranch = newNodeI(nkElifBranch, body.info)
elifBranch.add(bs)
let br = newNodeI(nkBreakStmt, body.info)
br.add(g.emptyNode)
elifBranch.add(br)
ibs.add(elifBranch)
loopBody.sons[1] = ibs
loopBody.sons[2] = body[L-1] loopBody.sons[2] = body[L-1]

View file

@ -25,7 +25,7 @@ const
SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'} '|', '=', '%', '&', '$', '@', '~', ':'}
# don't forget to update the 'highlite' module if these charsets should change # don't forget to update the 'highlite' module if these charsets should change

View file

@ -201,7 +201,8 @@ proc parseAssignment(L: var TLexer, tok: var TToken;
else: else:
processSwitch(s, val, passPP, info, config) processSwitch(s, val, passPP, info, config)
proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) = proc readConfigFile(
filename: string; cache: IdentCache; config: ConfigRef): bool =
var var
L: TLexer L: TLexer
tok: TToken tok: TToken
@ -216,7 +217,7 @@ proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) =
while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack) while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack)
if len(condStack) > 0: lexMessage(L, errGenerated, "expected @end") if len(condStack) > 0: lexMessage(L, errGenerated, "expected @end")
closeLexer(L) closeLexer(L)
rawMessage(config, hintConf, filename) return true
proc getUserConfigPath(filename: string): string = proc getUserConfigPath(filename: string): string =
result = joinPath(getConfigDir(), filename) result = joinPath(getConfigDir(), filename)
@ -233,23 +234,33 @@ proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) = proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
setDefaultLibpath(conf) setDefaultLibpath(conf)
var configFiles = newSeq[string]()
template readConfigFile(path: string) =
let configPath = path
if readConfigFile(configPath, cache, conf):
add(configFiles, configPath)
if optSkipConfigFile notin conf.globalOptions: if optSkipConfigFile notin conf.globalOptions:
readConfigFile(getSystemConfigPath(conf, cfg), cache, conf) readConfigFile(getSystemConfigPath(conf, cfg))
if optSkipUserConfigFile notin conf.globalOptions: if optSkipUserConfigFile notin conf.globalOptions:
readConfigFile(getUserConfigPath(cfg), cache, conf) readConfigFile(getUserConfigPath(cfg))
let pd = if conf.projectPath.len > 0: conf.projectPath else: getCurrentDir() let pd = if conf.projectPath.len > 0: conf.projectPath else: getCurrentDir()
if optSkipParentConfigFiles notin conf.globalOptions: if optSkipParentConfigFiles notin conf.globalOptions:
for dir in parentDirs(pd, fromRoot=true, inclusive=false): for dir in parentDirs(pd, fromRoot=true, inclusive=false):
readConfigFile(dir / cfg, cache, conf) readConfigFile(dir / cfg)
if optSkipProjConfigFile notin conf.globalOptions: if optSkipProjConfigFile notin conf.globalOptions:
readConfigFile(pd / cfg, cache, conf) readConfigFile(pd / cfg)
if conf.projectName.len != 0: if conf.projectName.len != 0:
# new project wide config file: # new project wide config file:
var projectConfig = changeFileExt(conf.projectFull, "nimcfg") var projectConfig = changeFileExt(conf.projectFull, "nimcfg")
if not fileExists(projectConfig): if not fileExists(projectConfig):
projectConfig = changeFileExt(conf.projectFull, "nim.cfg") projectConfig = changeFileExt(conf.projectFull, "nim.cfg")
readConfigFile(projectConfig, cache, conf) readConfigFile(projectConfig)
for filename in configFiles:
rawMessage(conf, hintConf, filename)

View file

@ -118,7 +118,8 @@ type
callOperator, callOperator,
parallel, parallel,
destructor, destructor,
notnil notnil,
oldIterTransf
SymbolFilesOption* = enum SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf disabledSf, writeOnlySf, readOnlySf, v2Sf

View file

@ -381,6 +381,10 @@ proc processPush(c: PContext, n: PNode, start: int) =
x.otherPragmas.add n.sons[i] x.otherPragmas.add n.sons[i]
#localError(c.config, n.info, errOptionExpected) #localError(c.config, n.info, errOptionExpected)
# If stacktrace is disabled globally we should not enable it
if optStackTrace notin c.optionStack[0].options:
c.config.options.excl(optStackTrace)
proc processPop(c: PContext, n: PNode) = proc processPop(c: PContext, n: PNode) =
if c.optionStack.len <= 1: if c.optionStack.len <= 1:
localError(c.config, n.info, "{.pop.} without a corresponding {.push.}") localError(c.config, n.info, "{.pop.} without a corresponding {.push.}")

View file

@ -1414,11 +1414,21 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParLe, "(ComesFrom|") put(g, tkParLe, "(ComesFrom|")
gsub(g, n, 0) gsub(g, n, 0)
put(g, tkParRi, ")") put(g, tkParRi, ")")
of nkGotoState, nkState: of nkGotoState:
var c: TContext var c: TContext
initContext c initContext c
putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto" putWithSpace g, tkSymbol, "goto"
gsons(g, n, c) gsons(g, n, c)
of nkState:
var c: TContext
initContext c
putWithSpace g, tkSymbol, "state"
gsub(g, n[0], c)
putWithSpace(g, tkColon, ":")
indentNL(g)
gsons(g, n, c, 1)
dedent(g)
of nkBreakState: of nkBreakState:
put(g, tkTuple, "breakstate") put(g, tkTuple, "breakstate")
of nkTypeClassTy: of nkTypeClassTy:

View file

@ -158,11 +158,13 @@ proc commonType*(x, y: PType): PType =
a = a.lastSon.skipTypes({tyGenericInst}) a = a.lastSon.skipTypes({tyGenericInst})
b = b.lastSon.skipTypes({tyGenericInst}) b = b.lastSon.skipTypes({tyGenericInst})
if a.kind == tyObject and b.kind == tyObject: if a.kind == tyObject and b.kind == tyObject:
result = commonSuperclass(a, b, k) result = commonSuperclass(a, b)
# this will trigger an error later: # this will trigger an error later:
if result.isNil or result == a: return x if result.isNil or result == a: return x
if result == b: return y if result == b: return y
if k != tyNone: # bug #7906, tyRef/tyPtr + tyGenericInst of ref/ptr object ->
# ill-formed AST, no need for additional tyRef/tyPtr
if k != tyNone and x.kind != tyGenericInst:
let r = result let r = result
result = newType(k, r.owner) result = newType(k, r.owner)
result.addSonSkipIntLit(r) result.addSonSkipIntLit(r)

View file

@ -1556,7 +1556,7 @@ proc semYield(c: PContext, n: PNode): PNode =
checkSonsLen(n, 1, c.config) checkSonsLen(n, 1, c.config)
if c.p.owner == nil or c.p.owner.kind != skIterator: if c.p.owner == nil or c.p.owner.kind != skIterator:
localError(c.config, n.info, errYieldNotAllowedHere) localError(c.config, n.info, errYieldNotAllowedHere)
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: elif oldIterTransf in c.features and c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
localError(c.config, n.info, errYieldNotAllowedInTryStmt) localError(c.config, n.info, errYieldNotAllowedInTryStmt)
elif n.sons[0].kind != nkEmpty: elif n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:

View file

@ -2020,6 +2020,14 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
if r == isGeneric: if r == isGeneric:
result.typ = getInstantiatedType(c, arg, m, base(f)) result.typ = getInstantiatedType(c, arg, m, base(f))
m.baseTypeMatch = true m.baseTypeMatch = true
# bug #4799, varargs accepting subtype relation object
elif r == isSubtype:
inc(m.subtypeMatches)
if f.kind == tyTypeDesc:
result = arg
else:
result = implicitConv(nkHiddenSubConv, f, arg, m, c)
m.baseTypeMatch = true
else: else:
result = userConvMatch(c, m, base(f), a, arg) result = userConvMatch(c, m, base(f), a, arg)
if result != nil: m.baseTypeMatch = true if result != nil: m.baseTypeMatch = true

View file

@ -19,9 +19,9 @@
# * transforms 'defer' into a 'try finally' statement # * transforms 'defer' into a 'try finally' statement
import import
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os, intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
idents, renderer, types, passes, semfold, magicsys, cgmeth, idents, renderer, types, passes, semfold, magicsys, cgmeth,
lambdalifting, sempass2, lowerings, lookups, destroyer, liftlocals, lambdalifting, sempass2, lowerings, destroyer, liftlocals, closureiters,
modulegraphs, lineinfos modulegraphs, lineinfos
type type
@ -984,6 +984,10 @@ proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode =
result = liftLocalsIfRequested(prc, result, g.cache, g.config) result = liftLocalsIfRequested(prc, result, g.cache, g.config)
if c.needsDestroyPass: #and newDestructors: if c.needsDestroyPass: #and newDestructors:
result = injectDestructorCalls(g, prc, result) result = injectDestructorCalls(g, prc, result)
if prc.isIterator and oldIterTransf notin g.config.features:
result = g.transformClosureIterator(prc, result)
incl(result.flags, nfTransf) incl(result.flags, nfTransf)
#if prc.name.s == "testbody": #if prc.name.s == "testbody":
# echo renderTree(result) # echo renderTree(result)

View file

@ -1044,7 +1044,7 @@ proc inheritanceDiff*(a, b: PType): int =
inc(result) inc(result)
result = high(int) result = high(int)
proc commonSuperclass*(a, b: PType, k: TTypeKind): PType = proc commonSuperclass*(a, b: PType): PType =
# quick check: are they the same? # quick check: are they the same?
if sameObjectTypes(a, b): return a if sameObjectTypes(a, b): return a
@ -1064,7 +1064,7 @@ proc commonSuperclass*(a, b: PType, k: TTypeKind): PType =
y = skipTypes(y, skipPtrs) y = skipTypes(y, skipPtrs)
if ancestors.contains(y.id): if ancestors.contains(y.id):
# bug #7818, defer the previous skipTypes # bug #7818, defer the previous skipTypes
if k in {tyRef, tyPtr}: t = y if t.kind != tyGenericInst: t = y
return t return t
y = y.sons[0] y = y.sons[0]

View file

@ -855,7 +855,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
of mNewStringOfCap: of mNewStringOfCap:
# we ignore the 'cap' argument and translate it as 'newString(0)'. # we ignore the 'cap' argument and translate it as 'newString(0)'.
# eval n.sons[1] for possible side effects: # eval n.sons[1] for possible side effects:
var tmp = c.genx(n.sons[1]) c.freeTemp(c.genx(n.sons[1]))
var tmp = c.getTemp(n.sons[1].typ)
c.gABx(n, opcLdImmInt, tmp, 0) c.gABx(n, opcLdImmInt, tmp, 0)
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcNewStr, dest, tmp) c.gABC(n, opcNewStr, dest, tmp)

View file

@ -10,11 +10,11 @@
## Regular expression support for Nim. ## Regular expression support for Nim.
## ##
## This module is implemented by providing a wrapper around the ## This module is implemented by providing a wrapper around the
## `PRCE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_ ## `PCRE (Perl-Compatible Regular Expressions) <http://www.pcre.org>`_
## C library. This means that your application will depend on the PRCE ## C library. This means that your application will depend on the PCRE
## library's licence when using this module, which should not be a problem ## library's licence when using this module, which should not be a problem
## though. ## though.
## PRCE's licence follows: ## PCRE's licence follows:
## ##
## .. include:: ../../doc/regexprs.txt ## .. include:: ../../doc/regexprs.txt
## ##

View file

@ -130,7 +130,7 @@ proc nimNumber(g: var GeneralTokenizer, position: int): int =
const const
OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.',
'|', '=', '%', '&', '$', '@', '~', ':', '\x80'..'\xFF'} '|', '=', '%', '&', '$', '@', '~', ':'}
proc nimNextToken(g: var GeneralTokenizer) = proc nimNextToken(g: var GeneralTokenizer) =
const const

View file

@ -82,6 +82,14 @@ const
# Special types # Special types
type Sighandler = proc (a: cint) {.noconv.} type Sighandler = proc (a: cint) {.noconv.}
const StatHasNanoseconds* = defined(linux) or defined(freebsd) or
defined(openbsd) or defined(dragonfly) ## \
## Boolean flag that indicates if the system supports nanosecond time
## resolution in the fields of ``Stat``. Note that the nanosecond based fields
## (``Stat.st_atim``, ``Stat.st_mtim`` and ``Stat.st_ctim``) can be accessed
## without checking this flag, because this module defines fallback procs
## when they are not available.
# Platform specific stuff # Platform specific stuff
when defined(linux) and defined(amd64): when defined(linux) and defined(amd64):
@ -92,9 +100,9 @@ else:
# There used to be this name in posix.nim a long time ago, not sure why! # There used to be this name in posix.nim a long time ago, not sure why!
{.deprecated: [cSIG_HOLD: SIG_HOLD].} {.deprecated: [cSIG_HOLD: SIG_HOLD].}
when not defined(macosx) and not defined(android): when StatHasNanoseconds:
proc st_atime*(s: Stat): Time {.inline.} = proc st_atime*(s: Stat): Time {.inline.} =
## Second-granularity time of last access ## Second-granularity time of last access.
result = s.st_atim.tv_sec result = s.st_atim.tv_sec
proc st_mtime*(s: Stat): Time {.inline.} = proc st_mtime*(s: Stat): Time {.inline.} =
## Second-granularity time of last data modification. ## Second-granularity time of last data modification.
@ -102,6 +110,16 @@ when not defined(macosx) and not defined(android):
proc st_ctime*(s: Stat): Time {.inline.} = proc st_ctime*(s: Stat): Time {.inline.} =
## Second-granularity time of last status change. ## Second-granularity time of last status change.
result = s.st_ctim.tv_sec result = s.st_ctim.tv_sec
else:
proc st_atim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last access.
result.tv_sec = s.st_atime
proc st_mtim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last data modification.
result.tv_sec = s.st_mtime
proc st_ctim*(s: Stat): TimeSpec {.inline.} =
## Nanosecond-granularity time of last data modification.
result.tv_sec = s.st_ctime
when hasAioH: when hasAioH:
proc aio_cancel*(a1: cint, a2: ptr Taiocb): cint {.importc, header: "<aio.h>".} proc aio_cancel*(a1: cint, a2: ptr Taiocb): cint {.importc, header: "<aio.h>".}

View file

@ -215,14 +215,14 @@ type
## For a typed memory object, the length in bytes. ## For a typed memory object, the length in bytes.
## For other file types, the use of this field is ## For other file types, the use of this field is
## unspecified. ## unspecified.
when defined(macosx) or defined(android): when StatHasNanoseconds:
st_atime*: Time ## Time of last access.
st_mtime*: Time ## Time of last data modification.
st_ctime*: Time ## Time of last status change.
else:
st_atim*: Timespec ## Time of last access. st_atim*: Timespec ## Time of last access.
st_mtim*: Timespec ## Time of last data modification. st_mtim*: Timespec ## Time of last data modification.
st_ctim*: Timespec ## Time of last status change. st_ctim*: Timespec ## Time of last status change.
else:
st_atime*: Time ## Time of last access.
st_mtime*: Time ## Time of last data modification.
st_ctime*: Time ## Time of last status change.
st_blksize*: Blksize ## A file system-specific preferred I/O block size st_blksize*: Blksize ## A file system-specific preferred I/O block size
## for this object. In some file system types, this ## for this object. In some file system types, this
## may vary from file to file. ## may vary from file to file.

View file

@ -62,52 +62,6 @@ template createCb(retFutureSym, iteratorNameSym,
identName() identName()
#{.pop.} #{.pop.}
proc generateExceptionCheck(futSym,
tryStmt, rootReceiver, fromNode: NimNode): NimNode {.compileTime.} =
if tryStmt.kind == nnkNilLit:
result = rootReceiver
else:
var exceptionChecks: seq[tuple[cond, body: NimNode]] = @[]
let errorNode = newDotExpr(futSym, newIdentNode("error"))
for i in 1 ..< tryStmt.len:
let exceptBranch = tryStmt[i]
if exceptBranch[0].kind == nnkStmtList:
exceptionChecks.add((newIdentNode("true"), exceptBranch[0]))
else:
var exceptIdentCount = 0
var ifCond: NimNode
for i in 0 ..< exceptBranch.len:
let child = exceptBranch[i]
if child.kind == nnkIdent:
let cond = infix(errorNode, "of", child)
if exceptIdentCount == 0:
ifCond = cond
else:
ifCond = infix(ifCond, "or", cond)
else:
break
exceptIdentCount.inc
expectKind(exceptBranch[exceptIdentCount], nnkStmtList)
exceptionChecks.add((ifCond, exceptBranch[exceptIdentCount]))
# -> -> else: raise futSym.error
exceptionChecks.add((newIdentNode("true"),
newNimNode(nnkRaiseStmt).add(errorNode)))
# Read the future if there is no error.
# -> else: futSym.read
let elseNode = newNimNode(nnkElse, fromNode)
elseNode.add newNimNode(nnkStmtList, fromNode)
elseNode[0].add rootReceiver
let ifBody = newStmtList()
ifBody.add newCall(newIdentNode("setCurrentException"), errorNode)
ifBody.add newIfStmt(exceptionChecks)
ifBody.add newCall(newIdentNode("setCurrentException"), newNilLit())
result = newIfStmt(
(newDotExpr(futSym, newIdentNode("failed")), ifBody)
)
result.add elseNode
template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver, template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
rootReceiver: untyped, fromNode: NimNode) = rootReceiver: untyped, fromNode: NimNode) =
@ -123,8 +77,7 @@ template useVar(result: var NimNode, futureVarNode: NimNode, valueReceiver,
result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode) result.add newNimNode(nnkYieldStmt, fromNode).add(futureVarNode)
# -> future<x>.read # -> future<x>.read
valueReceiver = newDotExpr(futureVarNode, newIdentNode("read")) valueReceiver = newDotExpr(futureVarNode, newIdentNode("read"))
result.add generateExceptionCheck(futureVarNode, tryStmt, rootReceiver, result.add rootReceiver
fromNode)
template createVar(result: var NimNode, futSymName: string, template createVar(result: var NimNode, futSymName: string,
asyncProc: NimNode, asyncProc: NimNode,
@ -154,8 +107,8 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode],
) )
proc processBody(node, retFutureSym: NimNode, proc processBody(node, retFutureSym: NimNode,
subTypeIsVoid: bool, futureVarIdents: seq[NimNode], subTypeIsVoid: bool,
tryStmt: NimNode): NimNode {.compileTime.} = futureVarIdents: seq[NimNode]): NimNode {.compileTime.} =
#echo(node.treeRepr) #echo(node.treeRepr)
result = node result = node
case node.kind case node.kind
@ -173,7 +126,7 @@ proc processBody(node, retFutureSym: NimNode,
result.add newCall(newIdentNode("complete"), retFutureSym) result.add newCall(newIdentNode("complete"), retFutureSym)
else: else:
let x = node[0].processBody(retFutureSym, subTypeIsVoid, let x = node[0].processBody(retFutureSym, subTypeIsVoid,
futureVarIdents, tryStmt) futureVarIdents)
if x.kind == nnkYieldStmt: result.add x if x.kind == nnkYieldStmt: result.add x
else: else:
result.add newCall(newIdentNode("complete"), retFutureSym, x) result.add newCall(newIdentNode("complete"), retFutureSym, x)
@ -224,63 +177,11 @@ proc processBody(node, retFutureSym: NimNode,
var newDiscard = node var newDiscard = node
result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], result.createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1],
newDiscard[0], newDiscard, node) newDiscard[0], newDiscard, node)
of nnkTryStmt:
# try: await x; except: ...
result = newNimNode(nnkStmtList, node)
template wrapInTry(n, tryBody: untyped) =
var temp = n
n[0] = tryBody
tryBody = temp
# Transform ``except`` body.
# TODO: Could we perform some ``await`` transformation here to get it
# working in ``except``?
tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid,
futureVarIdents, nil)
proc processForTry(n: NimNode, i: var int,
res: NimNode): bool {.compileTime.} =
## Transforms the body of the tryStmt. Does not transform the
## body in ``except``.
## Returns true if the tryStmt node was transformed into an ifStmt.
result = false
var skipped = n.skipStmtList()
while i < skipped.len:
var processed = processBody(skipped[i], retFutureSym,
subTypeIsVoid, futureVarIdents, n)
# Check if we transformed the node into an exception check.
# This suggests skipped[i] contains ``await``.
if processed.kind != skipped[i].kind or processed.len != skipped[i].len:
processed = processed.skipUntilStmtList()
expectKind(processed, nnkStmtList)
expectKind(processed[2][1], nnkElse)
i.inc
if not processForTry(n, i, processed[2][1][0]):
# We need to wrap the nnkElse nodes back into a tryStmt.
# As they are executed if an exception does not happen
# inside the awaited future.
# The following code will wrap the nodes inside the
# original tryStmt.
wrapInTry(n, processed[2][1][0])
res.add processed
result = true
else:
res.add skipped[i]
i.inc
var i = 0
if not processForTry(node, i, result):
# If the tryStmt hasn't been transformed we can just put the body
# back into it.
wrapInTry(node, result)
return
else: discard else: discard
for i in 0 ..< result.len: for i in 0 ..< result.len:
result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, result[i] = processBody(result[i], retFutureSym, subTypeIsVoid,
futureVarIdents, nil) futureVarIdents)
proc getName(node: NimNode): string {.compileTime.} = proc getName(node: NimNode): string {.compileTime.} =
case node.kind case node.kind
@ -362,7 +263,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
# -> complete(retFuture, result) # -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prcName & "Iter") var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid, var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
futureVarIdents, nil) futureVarIdents)
# don't do anything with forward bodies (empty) # don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty: if procBody.kind != nnkEmpty:
procBody.add(createFutureVarCompletions(futureVarIdents, nil)) procBody.add(createFutureVarCompletions(futureVarIdents, nil))

View file

@ -167,7 +167,7 @@ proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) =
## increments `c[key]` by `val`. ## increments `c[key]` by `val`.
let oldCount = c.count let oldCount = c.count
var n = rawInsert(c, key) var n = rawInsert(c, key)
if c.count == oldCount or oldCount == 0: if c.count >= oldCount or oldCount == 0:
# not a new key: # not a new key:
inc n.val, val inc n.val, val
@ -322,10 +322,14 @@ proc `$`*[T](c: CritBitTree[T]): string =
const avgItemLen = 16 const avgItemLen = 16
result = newStringOfCap(c.count * avgItemLen) result = newStringOfCap(c.count * avgItemLen)
result.add("{") result.add("{")
when T is void:
for key in keys(c):
if result.len > 1: result.add(", ")
result.addQuoted(key)
else:
for key, val in pairs(c): for key, val in pairs(c):
if result.len > 1: result.add(", ") if result.len > 1: result.add(", ")
result.add($key) result.addQuoted(key)
when T isnot void:
result.add(": ") result.add(": ")
result.addQuoted(val) result.addQuoted(val)
result.add("}") result.add("}")
@ -362,3 +366,12 @@ when isMainModule:
c.inc("a", -5) c.inc("a", -5)
assert c["a"] == 0 assert c["a"] == 0
c.inc("b", 2)
assert c["b"] == 2
c.inc("c", 3)
assert c["c"] == 3
c.inc("a", 1)
assert c["a"] == 1

View file

@ -328,14 +328,14 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
result = newNimNode(nnkBracket) result = newNimNode(nnkBracket)
for i in 0 ..< x.len: for i in 0 ..< x.len:
result.add(toJson(x[i])) result.add(toJson(x[i]))
result = newCall(bindSym"%", result) result = newCall(bindSym("%", brOpen), result)
of nnkTableConstr: # object of nnkTableConstr: # object
if x.len == 0: return newCall(bindSym"newJObject") if x.len == 0: return newCall(bindSym"newJObject")
result = newNimNode(nnkTableConstr) result = newNimNode(nnkTableConstr)
for i in 0 ..< x.len: for i in 0 ..< x.len:
x[i].expectKind nnkExprColonExpr x[i].expectKind nnkExprColonExpr
result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1])) result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1]))
result = newCall(bindSym"%", result) result = newCall(bindSym("%", brOpen), result)
of nnkCurly: # empty object of nnkCurly: # empty object
x.expectLen(0) x.expectLen(0)
result = newCall(bindSym"newJObject") result = newCall(bindSym"newJObject")
@ -343,9 +343,9 @@ proc toJson(x: NimNode): NimNode {.compiletime.} =
result = newCall(bindSym"newJNull") result = newCall(bindSym"newJNull")
of nnkPar: of nnkPar:
if x.len == 1: result = toJson(x[0]) if x.len == 1: result = toJson(x[0])
else: result = newCall(bindSym"%", x) else: result = newCall(bindSym("%", brOpen), x)
else: else:
result = newCall(bindSym"%", x) result = newCall(bindSym("%", brOpen), x)
macro `%*`*(x: untyped): untyped = macro `%*`*(x: untyped): untyped =
## Convert an expression to a JsonNode directly, without having to specify ## Convert an expression to a JsonNode directly, without having to specify

View file

@ -129,6 +129,12 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
## If `x` is empty, 0 is returned. ## If `x` is empty, 0 is returned.
for i in items(x): result = result + i for i in items(x): result = result + i
proc prod*[T](x: openArray[T]): T {.noSideEffect.} =
## Computes the product of the elements in ``x``.
## If ``x`` is empty, 1 is returned.
result = 1.T
for i in items(x): result = result * i
{.push noSideEffect.} {.push noSideEffect.}
when not defined(JS): # C when not defined(JS): # C
proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".} proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".}
@ -274,12 +280,18 @@ when not defined(JS): # C
proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".} proc erfc*(x: float64): float64 {.importc: "erfc", header: "<math.h>".}
## The complementary error function ## The complementary error function
proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
proc gamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
## The gamma function
proc tgamma*(x: float32): float32
{.deprecated: "use gamma instead", importc: "tgammaf", header: "<math.h>".}
proc tgamma*(x: float64): float64
{.deprecated: "use gamma instead", importc: "tgamma", header: "<math.h>".}
## The gamma function
## **Deprecated since version 0.19.0**: Use ``gamma`` instead.
proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".} proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "<math.h>".}
proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".} proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "<math.h>".}
## Natural log of the gamma function ## Natural log of the gamma function
proc tgamma*(x: float32): float32 {.importc: "tgammaf", header: "<math.h>".}
proc tgamma*(x: float64): float64 {.importc: "tgamma", header: "<math.h>".}
## The gamma function
proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".} proc floor*(x: float32): float32 {.importc: "floorf", header: "<math.h>".}
proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".} proc floor*(x: float64): float64 {.importc: "floor", header: "<math.h>".}
@ -551,6 +563,7 @@ when isMainModule and not defined(JS):
return sqrt(num) return sqrt(num)
# check gamma function # check gamma function
assert(gamma(5.0) == 24.0) # 4!
assert($tgamma(5.0) == $24.0) # 4! assert($tgamma(5.0) == $24.0) # 4!
assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0
assert(erf(6.0) > erf(5.0)) assert(erf(6.0) > erf(5.0))
@ -560,6 +573,12 @@ when isMainModule:
# Function for approximate comparison of floats # Function for approximate comparison of floats
proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9)
block: # prod
doAssert prod([1, 2, 3, 4]) == 24
doAssert prod([1.5, 3.4]) == 5.1
let x: seq[float] = @[]
doAssert prod(x) == 1.0
block: # round() tests block: # round() tests
# Round to 0 decimal places # Round to 0 decimal places
doAssert round(54.652) ==~ 55.0 doAssert round(54.652) ==~ 55.0

View file

@ -405,33 +405,40 @@ proc isIpAddress*(address_str: string): bool {.tags: [].} =
return false return false
return true return true
proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage, sl: var Socklen) = proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage,
sl: var Socklen) =
## Converts `IpAddress` and `Port` to `SockAddr` and `Socklen` ## Converts `IpAddress` and `Port` to `SockAddr` and `Socklen`
let port = htons(uint16(port)) let port = htons(uint16(port))
case address.family case address.family
of IpAddressFamily.IPv4: of IpAddressFamily.IPv4:
sl = sizeof(Sockaddr_in).Socklen sl = sizeof(Sockaddr_in).Socklen
let s = cast[ptr Sockaddr_in](addr sa) let s = cast[ptr Sockaddr_in](addr sa)
s.sin_family = type(s.sin_family)(AF_INET) s.sin_family = type(s.sin_family)(toInt(AF_INET))
s.sin_port = port s.sin_port = port
copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0], sizeof(s.sin_addr)) copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0],
sizeof(s.sin_addr))
of IpAddressFamily.IPv6: of IpAddressFamily.IPv6:
sl = sizeof(Sockaddr_in6).Socklen sl = sizeof(Sockaddr_in6).Socklen
let s = cast[ptr Sockaddr_in6](addr sa) let s = cast[ptr Sockaddr_in6](addr sa)
s.sin6_family = type(s.sin6_family)(AF_INET6) s.sin6_family = type(s.sin6_family)(toInt(AF_INET6))
s.sin6_port = port s.sin6_port = port
copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0], sizeof(s.sin6_addr)) copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0],
sizeof(s.sin6_addr))
proc fromSockAddrAux(sa: ptr Sockaddr_storage, sl: Socklen, address: var IpAddress, port: var Port) = proc fromSockAddrAux(sa: ptr Sockaddr_storage, sl: Socklen,
if sa.ss_family.int == AF_INET.int and sl == sizeof(Sockaddr_in).Socklen: address: var IpAddress, port: var Port) =
if sa.ss_family.int == toInt(AF_INET) and sl == sizeof(Sockaddr_in).Socklen:
address = IpAddress(family: IpAddressFamily.IPv4) address = IpAddress(family: IpAddressFamily.IPv4)
let s = cast[ptr Sockaddr_in](sa) let s = cast[ptr Sockaddr_in](sa)
copyMem(addr address.address_v4[0], addr s.sin_addr, sizeof(address.address_v4)) copyMem(addr address.address_v4[0], addr s.sin_addr,
sizeof(address.address_v4))
port = ntohs(s.sin_port).Port port = ntohs(s.sin_port).Port
elif sa.ss_family.int == AF_INET6.int and sl == sizeof(Sockaddr_in6).Socklen: elif sa.ss_family.int == toInt(AF_INET6) and
sl == sizeof(Sockaddr_in6).Socklen:
address = IpAddress(family: IpAddressFamily.IPv6) address = IpAddress(family: IpAddressFamily.IPv6)
let s = cast[ptr Sockaddr_in6](sa) let s = cast[ptr Sockaddr_in6](sa)
copyMem(addr address.address_v6[0], addr s.sin6_addr, sizeof(address.address_v6)) copyMem(addr address.address_v6[0], addr s.sin6_addr,
sizeof(address.address_v6))
port = ntohs(s.sin6_port).Port port = ntohs(s.sin6_port).Port
else: else:
raise newException(ValueError, "Neither IPv4 nor IPv6") raise newException(ValueError, "Neither IPv4 nor IPv6")
@ -1149,7 +1156,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
return 1 return 1
let sslPending = SSLPending(socket.sslHandle) let sslPending = SSLPending(socket.sslHandle)
if sslPending != 0: if sslPending != 0:
return sslPending return min(sslPending, size)
var startTime = epochTime() var startTime = epochTime()
let selRet = select(socket, timeout - int(waited * 1000.0)) let selRet = select(socket, timeout - int(waited * 1000.0))

View file

@ -23,6 +23,10 @@ when defined(windows):
import winlean import winlean
elif defined(posix): elif defined(posix):
import posix import posix
proc toTime(ts: Timespec): times.Time {.inline.} =
result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
else: else:
{.error: "OS module not ported to your operating system!".} {.error: "OS module not ported to your operating system!".}
@ -186,7 +190,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".}
when defined(posix): when defined(posix):
var res: Stat var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError()) if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_mtime.int64) result = res.st_mtim.toTime
else: else:
var f: WIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
@ -199,7 +203,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
when defined(posix): when defined(posix):
var res: Stat var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError()) if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_atime.int64) result = res.st_atim.toTime
else: else:
var f: WIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
@ -216,7 +220,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} =
when defined(posix): when defined(posix):
var res: Stat var res: Stat
if stat(file, res) < 0'i32: raiseOSError(osLastError()) if stat(file, res) < 0'i32: raiseOSError(osLastError())
return fromUnix(res.st_ctime.int64) result = res.st_ctim.toTime
else: else:
var f: WIN32_FIND_DATA var f: WIN32_FIND_DATA
var h = findFirstFile(file, f) var h = findFirstFile(file, f)
@ -228,10 +232,13 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} =
## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s ## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s
## modification time is later than `b`'s. ## modification time is later than `b`'s.
when defined(posix): when defined(posix):
result = getLastModificationTime(a) - getLastModificationTime(b) >= DurationZero # If we don't have access to nanosecond resolution, use '>='
# Posix's resolution sucks so, we use '>=' for posix. when not StatHasNanoseconds:
result = getLastModificationTime(a) >= getLastModificationTime(b)
else: else:
result = getLastModificationTime(a) - getLastModificationTime(b) > DurationZero result = getLastModificationTime(a) > getLastModificationTime(b)
else:
result = getLastModificationTime(a) > getLastModificationTime(b)
proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
## Returns the `current working directory`:idx:. ## Returns the `current working directory`:idx:.
@ -1494,7 +1501,7 @@ type
template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
## Transforms the native file info structure into the one nim uses. ## Transforms the native file info structure into the one nim uses.
## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows, ## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows,
## or a 'Stat' structure on posix ## or a 'Stat' structure on posix
when defined(Windows): when defined(Windows):
template merge(a, b): untyped = a or (b shl 32) template merge(a, b): untyped = a or (b shl 32)
@ -1520,7 +1527,6 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
formalInfo.kind = succ(result.kind) formalInfo.kind = succ(result.kind)
else: else:
template checkAndIncludeMode(rawMode, formalMode: untyped) = template checkAndIncludeMode(rawMode, formalMode: untyped) =
if (rawInfo.st_mode and rawMode) != 0'i32: if (rawInfo.st_mode and rawMode) != 0'i32:
@ -1528,9 +1534,9 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino) formalInfo.id = (rawInfo.st_dev, rawInfo.st_ino)
formalInfo.size = rawInfo.st_size formalInfo.size = rawInfo.st_size
formalInfo.linkCount = rawInfo.st_Nlink.BiggestInt formalInfo.linkCount = rawInfo.st_Nlink.BiggestInt
formalInfo.lastAccessTime = fromUnix(rawInfo.st_atime.int64) formalInfo.lastAccessTime = rawInfo.st_atim.toTime
formalInfo.lastWriteTime = fromUnix(rawInfo.st_mtime.int64) formalInfo.lastWriteTime = rawInfo.st_mtim.toTime
formalInfo.creationTime = fromUnix(rawInfo.st_ctime.int64) formalInfo.creationTime = rawInfo.st_ctim.toTime
result.permissions = {} result.permissions = {}
checkAndIncludeMode(S_IRUSR, fpUserRead) checkAndIncludeMode(S_IRUSR, fpUserRead)
@ -1644,7 +1650,9 @@ proc setLastModificationTime*(file: string, t: times.Time) =
## an error. ## an error.
when defined(posix): when defined(posix):
let unixt = posix.Time(t.toUnix) let unixt = posix.Time(t.toUnix)
var timevals = [Timeval(tv_sec: unixt), Timeval(tv_sec: unixt)] # [last access, last modification] let micro = convert(Nanoseconds, Microseconds, t.nanosecond)
var timevals = [Timeval(tv_sec: unixt, tv_usec: micro),
Timeval(tv_sec: unixt, tv_usec: micro)] # [last access, last modification]
if utimes(file, timevals.addr) != 0: raiseOSError(osLastError()) if utimes(file, timevals.addr) != 0: raiseOSError(osLastError())
else: else:
let h = openHandle(path = file, writeAccess = true) let h = openHandle(path = file, writeAccess = true)

View file

@ -527,8 +527,13 @@ proc format*(value: SomeFloat; specifier: string; res: var string) =
var sign = false var sign = false
if value >= 0.0: if value >= 0.0:
if spec.sign != '-': if spec.sign != '-':
f = spec.sign & f
sign = true sign = true
if value == 0.0:
if 1.0 / value == Inf:
# only insert the sign if value != negZero
f.insert($spec.sign, 0)
else:
f.insert($spec.sign, 0)
else: else:
sign = true sign = true
@ -558,12 +563,16 @@ proc format*(value: string; specifier: string; res: var string) =
## sense to call this directly, but it is required to exist ## sense to call this directly, but it is required to exist
## by the ``&`` macro. ## by the ``&`` macro.
let spec = parseStandardFormatSpecifier(specifier) let spec = parseStandardFormatSpecifier(specifier)
var value = value
case spec.typ case spec.typ
of 's', '\0': discard of 's', '\0': discard
else: else:
raise newException(ValueError, raise newException(ValueError,
"invalid type in format string for string, expected 's', but got " & "invalid type in format string for string, expected 's', but got " &
spec.typ) spec.typ)
if spec.precision != -1:
if spec.precision < runelen(value):
setLen(value, runeOffset(value, spec.precision))
res.add alignString(value, spec.minimumWidth, spec.align, spec.fill) res.add alignString(value, spec.minimumWidth, spec.align, spec.fill)
when isMainModule: when isMainModule:

View file

@ -185,8 +185,7 @@ type
DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts DurationParts* = array[FixedTimeUnit, int64] # Array of Duration parts starts
TimeIntervalParts* = array[TimeUnit, int] # Array of Duration parts starts TimeIntervalParts* = array[TimeUnit, int] # Array of Duration parts starts
TimesMutableTypes = DateTime | Time | Duration | TimeInterval
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time, {.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].} TTimeInterval: TimeInterval, TTimeInfo: DateTime, TimeInfo: DateTime].}
@ -607,30 +606,12 @@ proc `+`*(a: Time, b: Duration): Time {.operator, extern: "ntAddTime".} =
doAssert (fromUnix(0) + initDuration(seconds = 1)) == fromUnix(1) doAssert (fromUnix(0) + initDuration(seconds = 1)) == fromUnix(1)
addImpl[Time](a, b) addImpl[Time](a, b)
proc `+=`*(a: var Time, b: Duration) {.operator.} =
## Modify ``a`` in place by subtracting ``b``.
runnableExamples:
var tm = fromUnix(0)
tm += initDuration(seconds = 1)
doAssert tm == fromUnix(1)
a = addImpl[Time](a, b)
proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} = proc `-`*(a: Time, b: Duration): Time {.operator, extern: "ntSubTime".} =
## Subtracts a duration of time from a ``Time``. ## Subtracts a duration of time from a ``Time``.
runnableExamples: runnableExamples:
doAssert (fromUnix(0) - initDuration(seconds = 1)) == fromUnix(-1) doAssert (fromUnix(0) - initDuration(seconds = 1)) == fromUnix(-1)
subImpl[Time](a, b) subImpl[Time](a, b)
proc `-=`*(a: var Time, b: Duration) {.operator.} =
## Modify ``a`` in place by adding ``b``.
runnableExamples:
var tm = fromUnix(0)
tm -= initDuration(seconds = 1)
doAssert tm == fromUnix(-1)
a = subImpl[Time](a, b)
proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} = proc `<`*(a, b: Time): bool {.operator, extern: "ntLtTime".} =
## Returns true iff ``a < b``, that is iff a happened before b. ## Returns true iff ``a < b``, that is iff a happened before b.
ltImpl(a, b) ltImpl(a, b)
@ -1377,17 +1358,6 @@ proc `+`*(time: Time, interval: TimeInterval): Time =
else: else:
toTime(time.local + interval) toTime(time.local + interval)
proc `+=`*(time: var Time, interval: TimeInterval) =
## Modifies `time` by adding `interval`.
## If `interval` contains any years, months, weeks or days the operation
## is performed in the local timezone.
runnableExamples:
var tm = fromUnix(0)
tm += 5.seconds
doAssert tm == fromUnix(5)
time = time + interval
proc `-`*(time: Time, interval: TimeInterval): Time = proc `-`*(time: Time, interval: TimeInterval): Time =
## Subtracts `interval` from Time `time`. ## Subtracts `interval` from Time `time`.
## If `interval` contains any years, months, weeks or days the operation ## If `interval` contains any years, months, weeks or days the operation
@ -1401,15 +1371,30 @@ proc `-`*(time: Time, interval: TimeInterval): Time =
else: else:
toTime(time.local - interval) toTime(time.local - interval)
proc `-=`*(time: var Time, interval: TimeInterval) = proc `+=`*[T, U: TimesMutableTypes](a: var T, b: U) =
## Modifies `time` by subtracting `interval`. ## Modify ``a`` in place by adding ``b``.
## If `interval` contains any years, months, weeks or days the operation runnableExamples:
## is performed in the local timezone. var tm = fromUnix(0)
tm += initDuration(seconds = 1)
doAssert tm == fromUnix(1)
a = a + b
proc `-=`*[T, U: TimesMutableTypes](a: var T, b: U) =
## Modify ``a`` in place by subtracting ``b``.
runnableExamples: runnableExamples:
var tm = fromUnix(5) var tm = fromUnix(5)
tm -= 5.seconds tm -= initDuration(seconds = 5)
doAssert tm == fromUnix(0) doAssert tm == fromUnix(0)
time = time - interval a = a - b
proc `*=`*[T: TimesMutableTypes, U](a: var T, b: U) =
# Mutable type is often multiplied by number
runnableExamples:
var dur = initDuration(seconds = 1)
dur *= 5
doAssert dur == initDuration(seconds = 5)
a = a * b
proc formatToken(dt: DateTime, token: string, buf: var string) = proc formatToken(dt: DateTime, token: string, buf: var string) =
## Helper of the format proc to parse individual tokens. ## Helper of the format proc to parse individual tokens.

View file

@ -2681,7 +2681,7 @@ when not defined(nimscript) and hasAlloc:
{.warning: "GC_unref is a no-op in JavaScript".} {.warning: "GC_unref is a no-op in JavaScript".}
template GC_getStatistics*(): string = template GC_getStatistics*(): string =
{.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".} {.warning: "GC_getStatistics is a no-op in JavaScript".}
"" ""
template accumulateResult*(iter: untyped) = template accumulateResult*(iter: untyped) =

View file

@ -116,7 +116,7 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel,
if mode == mStore: if mode == mStore:
x[] = alloc0(t.region, seq.len *% mt.base.size +% GenericSeqSize) x[] = alloc0(t.region, seq.len *% mt.base.size +% GenericSeqSize)
else: else:
unsureAsgnRef(x, newObj(mt, seq.len * mt.base.size + GenericSeqSize)) unsureAsgnRef(x, newSeq(mt, seq.len))
var dst = cast[ByteAddress](cast[PPointer](dest)[]) var dst = cast[ByteAddress](cast[PPointer](dest)[])
var dstseq = cast[PGenericSeq](dst) var dstseq = cast[PGenericSeq](dst)
dstseq.len = seq.len dstseq.len = seq.len

View file

@ -41,3 +41,6 @@ proc reraiseException() {.compilerRtl.} =
proc writeStackTrace() = discard proc writeStackTrace() = discard
proc setControlCHook(hook: proc () {.noconv.}) = discard proc setControlCHook(hook: proc () {.noconv.}) = discard
proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
sysFatal(ReraiseError, "exception handling is not available")

View file

@ -131,6 +131,10 @@ proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
quitOrDebug() quitOrDebug()
prev.up = cur.up prev.up = cur.up
proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
if not e.isNil:
currException = e
# some platforms have native support for stack traces: # some platforms have native support for stack traces:
const const
nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and

View file

@ -1,24 +1,5 @@
discard """ discard """
msg: '''BracketExpr output: "OK"
Sym "array"
Infix
Ident ".."
IntLit 0
IntLit 2
BracketExpr
Sym "Vehicle"
Sym "int"
---------
BracketExpr
Sym "array"
Infix
Ident ".."
IntLit 0
IntLit 2
BracketExpr
Sym "Vehicle"
Sym "int"
---------'''
""" """
# bug #7818 # bug #7818
@ -34,12 +15,118 @@ type
Bike[T] = object of Vehicle[T] Bike[T] = object of Vehicle[T]
macro peek(n: typed): untyped = macro peek(n: typed): untyped =
echo getTypeImpl(n).treeRepr let val = getTypeImpl(n).treeRepr
echo "---------" newLit(val)
block test_t7818:
var v = Vehicle[int](tire: 3)
var c = Car[int](tire: 4)
var b = Bike[int](tire: 2)
let y = peek([c, b, v])
let z = peek([v, c, b])
doAssert(y == z)
block test_t7906_1:
proc init(x: typedesc, y: int): ref x =
result = new(ref x)
result.tire = y
var v = init(Vehicle[int], 3)
var c = init(Car[int], 4)
var b = init(Bike[int], 2)
let y = peek([c, b, v])
let z = peek([v, c, b])
doAssert(y == z)
block test_t7906_2:
var v = Vehicle[int](tire: 3)
var c = Car[int](tire: 4)
var b = Bike[int](tire: 2)
let y = peek([c.addr, b.addr, v.addr])
let z = peek([v.addr, c.addr, b.addr])
doAssert(y == z)
block test_t7906_3:
type
Animal[T] = object of RootObj
hair: T
Mammal[T] = object of Animal[T]
Monkey[T] = object of Mammal[T]
var v = Animal[int](hair: 3)
var c = Mammal[int](hair: 4)
var b = Monkey[int](hair: 2)
let z = peek([c.addr, b.addr, v.addr])
let y = peek([v.addr, c.addr, b.addr])
doAssert(y == z)
type
Fruit[T] = ref object of RootObj
color: T
Apple[T] = ref object of Fruit[T]
Banana[T] = ref object of Fruit[T]
proc testArray[T](x: array[3, Fruit[T]]): string =
result = ""
for c in x:
result.add $c.color
proc testOpenArray[T](x: openArray[Fruit[T]]): string =
result = ""
for c in x:
result.add $c.color
block test_t7906_4:
var v = Fruit[int](color: 3)
var c = Apple[int](color: 4)
var b = Banana[int](color: 2)
let y = peek([c, b, v])
let z = peek([v, c, b])
doAssert(y == z)
block test_t7906_5:
var a = Fruit[int](color: 1)
var b = Apple[int](color: 2)
var c = Banana[int](color: 3)
doAssert(testArray([a, b, c]) == "123")
doAssert(testArray([b, c, a]) == "231")
doAssert(testOpenArray([a, b, c]) == "123")
doAssert(testOpenArray([b, c, a]) == "231")
doAssert(testOpenArray(@[a, b, c]) == "123")
doAssert(testOpenArray(@[b, c, a]) == "231")
proc testArray[T](x: array[3, ptr Vehicle[T]]): string =
result = ""
for c in x:
result.add $c.tire
proc testOpenArray[T](x: openArray[ptr Vehicle[T]]): string =
result = ""
for c in x:
result.add $c.tire
block test_t7906_6:
var u = Vehicle[int](tire: 1)
var v = Bike[int](tire: 2)
var w = Car[int](tire: 3)
doAssert(testArray([u.addr, v.addr, w.addr]) == "123")
doAssert(testArray([w.addr, u.addr, v.addr]) == "312")
doAssert(testOpenArray([u.addr, v.addr, w.addr]) == "123")
doAssert(testOpenArray([w.addr, u.addr, v.addr]) == "312")
doAssert(testOpenArray(@[u.addr, v.addr, w.addr]) == "123")
doAssert(testOpenArray(@[w.addr, u.addr, v.addr]) == "312")
echo "OK"
var v = Vehicle[int](tire: 3)
var c = Car[int](tire: 4)
var b = Bike[int](tire: 2)
peek([c, b, v])
peek([v, c, b])

19
tests/async/t7985.nim Normal file
View file

@ -0,0 +1,19 @@
discard """
file: "t7985.nim"
exitcode: 0
output: "(value: 1)"
"""
import json, asyncdispatch
proc getData(): Future[JsonNode] {.async.} =
result = %*{"value": 1}
type
MyData = object
value: BiggestInt
proc main() {.async.} =
let data = to(await(getData()), MyData)
echo data
waitFor(main())

View file

@ -1,18 +1,25 @@
discard """ discard """
errormsg: "invalid control flow: 'yield' within a constructor" output: '''
line: 16 @[1, 2, 3, 4]
123
'''
""" """
# bug #5314, bug #6626 # bug #5314, bug #6626
import asyncdispatch import asyncdispatch
proc bar(): Future[int] {.async.} = proc bar(i: int): Future[int] {.async.} =
await sleepAsync(500) await sleepAsync(2)
result = 3 result = i
proc foo(): Future[seq[int]] {.async.} = proc foo(): Future[seq[int]] {.async.} =
await sleepAsync(500) await sleepAsync(2)
result = @[1, 2, await bar(), 4] # <--- The bug is here result = @[1, 2, await bar(3), 4] # <--- The bug is here
proc foo2() {.async.} =
await sleepAsync(2)
echo(await bar(1), await bar(2), await bar(3))
echo waitFor foo() echo waitFor foo()
waitFor foo2()

View file

@ -3,7 +3,7 @@ discard """
disabled: "windows" disabled: "windows"
output: "Matched" output: "Matched"
""" """
import asyncdispatch import asyncdispatch, strutils
# Tests to ensure our exception trace backs are friendly. # Tests to ensure our exception trace backs are friendly.
@ -82,7 +82,7 @@ Async traceback:
asyncmacro\.nim\(\d+?\)\s+?a asyncmacro\.nim\(\d+?\)\s+?a
asyncmacro\.nim\(\d+?\)\s+?a_continue asyncmacro\.nim\(\d+?\)\s+?a_continue
## Resumes an async procedure ## Resumes an async procedure
asyncmacro\.nim\(\d+?\)\s+?aIter tasync_traceback\.nim\(\d+?\)\s+?aIter
asyncfutures\.nim\(\d+?\)\s+?read asyncfutures\.nim\(\d+?\)\s+?read
\]# \]#
Exception message: b failure Exception message: b failure
@ -110,17 +110,33 @@ Async traceback:
## Executes pending callbacks ## Executes pending callbacks
asyncmacro\.nim\(\d+?\)\s+?foo_continue asyncmacro\.nim\(\d+?\)\s+?foo_continue
## Resumes an async procedure ## Resumes an async procedure
asyncmacro\.nim\(\d+?\)\s+?fooIter tasync_traceback\.nim\(\d+?\)\s+?fooIter
asyncfutures\.nim\(\d+?\)\s+?read asyncfutures\.nim\(\d+?\)\s+?read
\]# \]#
Exception message: bar failure Exception message: bar failure
Exception type: Exception type:
""" """
if result.match(re(expected)): let resLines = splitLines(result.strip)
echo("Matched") let expLines = splitLines(expected.strip)
else:
echo("Not matched!") if resLines.len != expLines.len:
echo("Not matched! Wrong number of lines!")
echo() echo()
echo(result) echo(result)
quit(QuitFailure) quit(QuitFailure)
var ok = true
for i in 0 ..< resLines.len:
if not resLines[i].match(re(expLines[i])):
echo "Not matched! Line ", i + 1
echo "Expected:"
echo expLines[i]
echo "Actual:"
echo resLines[i]
ok = false
if ok:
echo("Matched")
else:
quit(QuitFailure)

View file

@ -9,7 +9,7 @@ Multiple except branches
Multiple except branches 2 Multiple except branches 2
''' '''
""" """
import asyncdispatch import asyncdispatch, strutils
# Here we are testing the ability to catch exceptions. # Here we are testing the ability to catch exceptions.
@ -22,7 +22,7 @@ proc catch() {.async.} =
try: try:
await foobar() await foobar()
except: except:
echo("Generic except: ", getCurrentExceptionMsg()) echo("Generic except: ", getCurrentExceptionMsg().splitLines[0])
try: try:
await foobar() await foobar()

View file

@ -1,10 +1,12 @@
discard """ discard """
file: "tasynctry2.nim" file: "tasynctry2.nim"
errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator" errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator"
line: 15 line: 14
""" """
import asyncdispatch import asyncdispatch
{.experimental: "oldIterTransf".}
proc foo(): Future[bool] {.async.} = discard proc foo(): Future[bool] {.async.} = discard
proc test5(): Future[int] {.async.} = proc test5(): Future[int] {.async.} =

View file

@ -68,15 +68,15 @@ block:
block: block:
var t: CritBitTree[int] var t: CritBitTree[int]
t["a"] = 1 t["a"] = 1
doAssert $t == "{a: 1}" doAssert $t == """{"a": 1}"""
block: block:
var t: CritBitTree[string] var t: CritBitTree[string]
t["a"] = "1" t["a"] = "1"
doAssert $t == """{a: "1"}""" doAssert $t == """{"a": "1"}"""
block: block:
var t: CritBitTree[char] var t: CritBitTree[char]
t["a"] = '1' t["a"] = '1'
doAssert $t == "{a: '1'}" doAssert $t == """{"a": '1'}"""
# Test escaping behavior # Test escaping behavior

407
tests/iter/tyieldintry.nim Normal file
View file

@ -0,0 +1,407 @@
discard """
targets: "c cpp"
output: "ok"
"""
var closureIterResult = newSeq[int]()
proc checkpoint(arg: int) =
closureIterResult.add(arg)
type
TestException = object of Exception
AnotherException = object of Exception
proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) =
closureIterResult.setLen(0)
var exceptionCaught = false
try:
for i in it():
closureIterResult.add(i)
except TestException:
exceptionCaught = true
if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected:
if closureIterResult != @expectedResults:
echo "Expected: ", @expectedResults
echo "Actual: ", closureIterResult
if exceptionCaught != exceptionExpected:
echo "Expected exception: ", exceptionExpected
echo "Got exception: ", exceptionCaught
doAssert(false)
proc test(it: iterator(): int, expectedResults: varargs[int]) =
testClosureIterAux(it, false, expectedResults)
proc testExc(it: iterator(): int, expectedResults: varargs[int]) =
testClosureIterAux(it, true, expectedResults)
proc raiseException() =
raise newException(TestException, "Test exception!")
block:
iterator it(): int {.closure.} =
var i = 5
while i != 0:
yield i
if i == 3:
yield 123
dec i
test(it, 5, 4, 3, 123, 2, 1)
block:
iterator it(): int {.closure.} =
yield 0
try:
checkpoint(1)
raiseException()
except TestException:
checkpoint(2)
yield 3
checkpoint(4)
finally:
checkpoint(5)
checkpoint(6)
test(it, 0, 1, 2, 3, 4, 5, 6)
block:
iterator it(): int {.closure.} =
yield 0
try:
yield 1
checkpoint(2)
finally:
checkpoint(3)
yield 4
checkpoint(5)
yield 6
test(it, 0, 1, 2, 3, 4, 5, 6)
block:
iterator it(): int {.closure.} =
yield 0
try:
yield 1
raiseException()
yield 2
finally:
checkpoint(3)
yield 4
checkpoint(5)
yield 6
testExc(it, 0, 1, 3, 4, 5, 6)
block:
iterator it(): int {.closure.} =
try:
try:
raiseException()
except AnotherException:
yield 123
finally:
checkpoint(3)
finally:
checkpoint(4)
testExc(it, 3, 4)
block:
iterator it(): int {.closure.} =
try:
yield 1
raiseException()
except AnotherException:
checkpoint(123)
finally:
checkpoint(2)
checkpoint(3)
testExc(it, 1, 2)
block:
iterator it(): int {.closure.} =
try:
yield 0
try:
yield 1
try:
yield 2
raiseException()
except AnotherException:
yield 123
finally:
yield 3
except AnotherException:
yield 124
finally:
yield 4
checkpoint(1234)
except:
yield 5
checkpoint(6)
finally:
checkpoint(7)
yield 8
checkpoint(9)
test(it, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
block:
iterator it(): int {.closure.} =
try:
yield 0
return 2
finally:
checkpoint(1)
checkpoint(123)
test(it, 0, 1)
block:
iterator it(): int {.closure.} =
try:
try:
yield 0
raiseException()
finally:
checkpoint(1)
except TestException:
yield 2
return
finally:
yield 3
checkpoint(123)
test(it, 0, 1, 2, 3)
block:
iterator it(): int {.closure.} =
try:
try:
yield 0
raiseException()
finally:
return # Return in finally should stop exception propagation
except AnotherException:
yield 2
return
finally:
yield 3
checkpoint(123)
test(it, 0, 3)
block: # Yield in yield
iterator it(): int {.closure.} =
template foo(): int =
yield 1
2
for i in 0 .. 2:
checkpoint(0)
yield foo()
test(it, 0, 1, 2, 0, 1, 2, 0, 1, 2)
block:
iterator it(): int {.closure.} =
let i = if true:
yield 0
1
else:
2
yield i
test(it, 0, 1)
block:
iterator it(): int {.closure.} =
var foo = 123
let i = try:
yield 0
raiseException()
1
except TestException as e:
assert(e.msg == "Test exception!")
case foo
of 1:
yield 123
2
of 123:
yield 5
6
else:
7
yield i
test(it, 0, 5, 6)
block:
iterator it(): int {.closure.} =
proc voidFoo(i1, i2, i3: int) =
checkpoint(i1)
checkpoint(i2)
checkpoint(i3)
proc foo(i1, i2, i3: int): int =
voidFoo(i1, i2, i3)
i3
proc bar(i1: int): int =
checkpoint(i1)
template tryexcept: int =
try:
yield 1
raiseException()
123
except TestException:
yield 2
checkpoint(3)
4
let e1 = true
template ifelse1: int =
if e1:
yield 10
11
else:
12
template ifelse2: int =
if ifelse1() == 12:
yield 20
21
else:
yield 22
23
let i = foo(bar(0), tryexcept, ifelse2)
discard foo(bar(0), tryexcept, ifelse2)
voidFoo(bar(0), tryexcept, ifelse2)
yield i
test(it,
# let i = foo(bar(0), tryexcept, ifelse2)
0, # bar(0)
1, 2, 3, # tryexcept
10, # ifelse1
22, # ifelse22
0, 4, 23, # foo
# discard foo(bar(0), tryexcept, ifelse2)
0, # bar(0)
1, 2, 3, # tryexcept
10, # ifelse1
22, # ifelse22
0, 4, 23, # foo
# voidFoo(bar(0), tryexcept, ifelse2)
0, # bar(0)
1, 2, 3, # tryexcept
10, # ifelse1
22, # ifelse22
0, 4, 23, # foo
23 # i
)
block:
iterator it(): int {.closure.} =
checkpoint(0)
for i in 0 .. 1:
try:
yield 1
raiseException()
except TestException as e:
doAssert(e.msg == "Test exception!")
yield 2
except AnotherException:
yield 123
except:
yield 1234
finally:
yield 3
checkpoint(4)
yield 5
test(it, 0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5)
block:
iterator it(): int {.closure.} =
var i = 5
template foo(): bool =
yield i
true
while foo():
dec i
if i == 0:
break
test(it, 5, 4, 3, 2, 1)
block: # Short cirquits
iterator it(): int {.closure.} =
template trueYield: bool =
yield 1
true
template falseYield: bool =
yield 0
false
if trueYield or falseYield:
discard falseYield and trueYield
if falseYield and trueYield:
checkpoint(123)
test(it, 1, 0, 0)
block: #7969
type
SomeObj = object
id: int
iterator it(): int {.closure.} =
template yieldAndSomeObj: SomeObj =
var s: SomeObj
s.id = 2
yield 1
s
checkpoint(yieldAndSomeObj().id)
var i = 5
case i
of 0:
checkpoint(123)
of 1, 2, 5:
checkpoint(3)
else:
checkpoint(123)
test(it, 1, 2, 3)
block: # yield in blockexpr
iterator it(): int {.closure.} =
yield(block:
checkpoint(1)
yield 2
3
)
test(it, 1, 2, 3)
echo "ok"

View file

@ -1,12 +0,0 @@
discard """
file: "thexlit.nim"
output: "equal"
"""
var t=0x950412DE
if t==0x950412DE:
echo "equal"
else:
echo "not equal"

View file

@ -1,8 +0,0 @@
type
TArray = array[0x0012..0x0013, int]
var a: TArray
echo a[0x0012] #OUT 0

View file

@ -0,0 +1,27 @@
discard """
action: run
output: "equal"
"""
var t=0x950412DE
if t==0x950412DE:
echo "equal"
else:
echo "not equal"
type
TArray = array[0x0012..0x0013, int]
var a: TArray
doAssert a[0x0012] == 0
# #7884
type Obj = object
ö: int
let o = Obj(ö: 1)
doAssert o.ö == 1

View file

@ -42,6 +42,7 @@ Raises
true true
true true
true true
true
''' '''
""" """
# test os path creation, iteration, and deletion # test os path creation, iteration, and deletion
@ -129,3 +130,12 @@ echo fileExists("../dest/a/b/file.txt")
echo fileExists("../dest/a/b/c/fileC.txt") echo fileExists("../dest/a/b/c/fileC.txt")
removeDir("../dest") removeDir("../dest")
# Test get/set modification times
# Should support at least microsecond resolution
import times
let tm = fromUnix(0) + 100.microseconds
writeFile("a", "")
setLastModificationTime("a", tm)
echo getLastModificationTime("a") == tm
removeFile("a")

View file

@ -12,6 +12,32 @@ var o: Obj
doAssert fmt"{o}" == "foobar" doAssert fmt"{o}" == "foobar"
doAssert fmt"{o:10}" == "foobar " doAssert fmt"{o:10}" == "foobar "
# see issue #7933
var str = "abc"
doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 :: a"
doAssert fmt">7.2 :: {str:>7.2}" == ">7.2 :: ab"
doAssert fmt">7.3 :: {str:>7.3}" == ">7.3 :: abc"
doAssert fmt">7.9 :: {str:>7.9}" == ">7.9 :: abc"
doAssert fmt">7.0 :: {str:>7.0}" == ">7.0 :: "
doAssert fmt" 7.1 :: {str:7.1}" == " 7.1 :: a "
doAssert fmt" 7.2 :: {str:7.2}" == " 7.2 :: ab "
doAssert fmt" 7.3 :: {str:7.3}" == " 7.3 :: abc "
doAssert fmt" 7.9 :: {str:7.9}" == " 7.9 :: abc "
doAssert fmt" 7.0 :: {str:7.0}" == " 7.0 :: "
doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: a "
doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: ab "
doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: abc "
doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: abc "
doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: "
str = "äöüe\u0309\u0319o\u0307\u0359"
doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: ä "
doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: äö "
doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: äöü "
doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: "
# this is actually wrong, but the unicode module has no support for graphemes
doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 :: äöüe "
doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359"
# see issue #7932 # see issue #7932
doAssert fmt"{15:08}" == "00000015" # int, works doAssert fmt"{15:08}" == "00000015" # int, works
doAssert fmt"{1.5:08}" == "000001.5" # float, works doAssert fmt"{1.5:08}" == "000001.5" # float, works
@ -20,3 +46,11 @@ doAssert fmt"{-1.5:0>8}" == "0000-1.5" # even that does not work for negative fl
doAssert fmt"{-1.5:08}" == "-00001.5" # works doAssert fmt"{-1.5:08}" == "-00001.5" # works
doAssert fmt"{1.5:+08}" == "+00001.5" # works doAssert fmt"{1.5:+08}" == "+00001.5" # works
doAssert fmt"{1.5: 08}" == " 00001.5" # works doAssert fmt"{1.5: 08}" == " 00001.5" # works
# only add explicitly requested sign if value != -0.0 (neg zero)
doAssert fmt"{-0.0:g}" == "-0"
doassert fmt"{-0.0:+g}" == "-0"
doassert fmt"{-0.0: g}" == "-0"
doAssert fmt"{0.0:g}" == "0"
doAssert fmt"{0.0:+g}" == "+0"
doAssert fmt"{0.0: g}" == " 0"

View file

@ -11,6 +11,10 @@ discard """
2 2
3 3
4 4
2
1
2
3
''' '''
""" """
@ -47,3 +51,38 @@ foo(toOpenArray(arr, 8, 12))
var seqq = @[1, 2, 3, 4, 5] var seqq = @[1, 2, 3, 4, 5]
foo(toOpenArray(seqq, 1, 3)) foo(toOpenArray(seqq, 1, 3))
# empty openArray issue #7904
foo(toOpenArray(seqq, 0, -1))
foo(toOpenArray(seqq, 1, 0))
doAssertRaises(IndexError):
foo(toOpenArray(seqq, 0, -2))
foo(toOpenArray(arr, 9, 8))
foo(toOpenArray(arr, 0, -1))
foo(toOpenArray(arr, 1, 0))
doAssertRaises(IndexError):
foo(toOpenArray(arr, 10, 8))
# test openArray of openArray
proc oaEmpty(a: openArray[int]) =
foo(toOpenArray(a, 0, -1))
proc oaFirstElm(a: openArray[int]) =
foo(toOpenArray(a, 0, 0))
oaEmpty(toOpenArray(seqq, 0, -1))
oaEmpty(toOpenArray(seqq, 1, 0))
oaEmpty(toOpenArray(seqq, 1, 2))
oaFirstElm(toOpenArray(seqq, 1, seqq.len-1))
var arrNeg: array[-3 .. -1, int] = [1, 2, 3]
foo(toOpenArray(arrNeg, -3, -1))
foo(toOpenArray(arrNeg, 0, -1))
foo(toOpenArray(arrNeg, -3, -4))
doAssertRaises(IndexError):
foo(toOpenArray(arrNeg, -4, -1))
doAssertRaises(IndexError):
foo(toOpenArray(arrNeg, -1, 0))
doAssertRaises(IndexError):
foo(toOpenArray(arrNeg, -1, -3))

245
tests/typerel/t4799.nim Normal file
View file

@ -0,0 +1,245 @@
discard """
output: "OK"
"""
type
GRBase[T] = ref object of RootObj
val: T
GRC[T] = ref object of GRBase[T]
GRD[T] = ref object of GRBase[T]
proc testGR[T](x: varargs[GRBase[T]]): string =
result = ""
for c in x:
result.add $c.val
block test_t4799_1:
var rgv = GRBase[int](val: 3)
var rgc = GRC[int](val: 4)
var rgb = GRD[int](val: 2)
doAssert(testGR(rgb, rgc, rgv) == "243")
doAssert(testGR(rgc, rgv, rgb) == "432")
doAssert(testGR(rgv, rgb, rgc) == "324")
doAssert(testGR([rgb, rgc, rgv]) == "243")
doAssert(testGR([rgc, rgv, rgb]) == "432")
doAssert(testGR([rgv, rgb, rgc]) == "324")
type
PRBase[T] = object of RootObj
val: T
PRC[T] = object of PRBase[T]
PRD[T] = object of PRBase[T]
proc testPR[T](x: varargs[ptr PRBase[T]]): string =
result = ""
for c in x:
result.add $c.val
block test_t4799_2:
var pgv = PRBase[int](val: 3)
var pgc = PRC[int](val: 4)
var pgb = PRD[int](val: 2)
doAssert(testPR(pgb.addr, pgc.addr, pgv.addr) == "243")
doAssert(testPR(pgc.addr, pgv.addr, pgb.addr) == "432")
doAssert(testPR(pgv.addr, pgb.addr, pgc.addr) == "324")
doAssert(testPR([pgb.addr, pgc.addr, pgv.addr]) == "243")
doAssert(testPR([pgc.addr, pgv.addr, pgb.addr]) == "432")
doAssert(testPR([pgv.addr, pgb.addr, pgc.addr]) == "324")
type
RBase = ref object of RootObj
val: int
RC = ref object of RBase
RD = ref object of RBase
proc testR(x: varargs[RBase]): string =
result = ""
for c in x:
result.add $c.val
block test_t4799_3:
var rv = RBase(val: 3)
var rc = RC(val: 4)
var rb = RD(val: 2)
doAssert(testR(rb, rc, rv) == "243")
doAssert(testR(rc, rv, rb) == "432")
doAssert(testR(rv, rb, rc) == "324")
doAssert(testR([rb, rc, rv]) == "243")
doAssert(testR([rc, rv, rb]) == "432")
doAssert(testR([rv, rb, rc]) == "324")
type
PBase = object of RootObj
val: int
PC = object of PBase
PD = object of PBase
proc testP(x: varargs[ptr PBase]): string =
result = ""
for c in x:
result.add $c.val
block test_t4799_4:
var pv = PBase(val: 3)
var pc = PC(val: 4)
var pb = PD(val: 2)
doAssert(testP(pb.addr, pc.addr, pv.addr) == "243")
doAssert(testP(pc.addr, pv.addr, pb.addr) == "432")
doAssert(testP(pv.addr, pb.addr, pc.addr) == "324")
doAssert(testP([pb.addr, pc.addr, pv.addr]) == "243")
doAssert(testP([pc.addr, pv.addr, pb.addr]) == "432")
doAssert(testP([pv.addr, pb.addr, pc.addr]) == "324")
type
PSBase[T, V] = ref object of RootObj
val: T
color: V
PSRC[T] = ref object of PSBase[T, int]
PSRD[T] = ref object of PSBase[T, int]
proc testPS[T, V](x: varargs[PSBase[T, V]]): string =
result = ""
for c in x:
result.add c.val
result.add $c.color
block test_t4799_5:
var a = PSBase[string, int](val: "base", color: 1)
var b = PSRC[string](val: "rc", color: 2)
var c = PSRD[string](val: "rd", color: 3)
doAssert(testPS(a, b, c) == "base1rc2rd3")
doAssert(testPS(b, a, c) == "rc2base1rd3")
doAssert(testPS(c, b, a) == "rd3rc2base1")
doAssert(testPS([a, b, c]) == "base1rc2rd3")
doAssert(testPS([b, a, c]) == "rc2base1rd3")
doAssert(testPS([c, b, a]) == "rd3rc2base1")
type
SBase[T, V] = ref object of RootObj
val: T
color: V
SRC = ref object of SBase[string, int]
SRD = ref object of SBase[string, int]
proc testS[T, V](x: varargs[SBase[T, V]]): string =
result = ""
for c in x:
result.add c.val
result.add $c.color
block test_t4799_6:
var a = SBase[string, int](val: "base", color: 1)
var b = SRC(val: "rc", color: 2)
var c = SRD(val: "rd", color: 3)
doAssert(testS(a, b, c) == "base1rc2rd3")
doAssert(testS(b, a, c) == "rc2base1rd3")
doAssert(testS(c, b, a) == "rd3rc2base1")
doAssert(testS([a, b, c]) == "base1rc2rd3")
# this is not varargs bug, but array construction bug
# see #7955
#doAssert(testS([b, c, a]) == "rc2rd3base1")
#doAssert(testS([c, b, a]) == "rd3rc2base1")
proc test_inproc() =
block test_inproc_1:
var rgv = GRBase[int](val: 3)
var rgc = GRC[int](val: 4)
var rgb = GRD[int](val: 2)
doAssert(testGR(rgb, rgc, rgv) == "243")
doAssert(testGR(rgc, rgv, rgb) == "432")
doAssert(testGR(rgv, rgb, rgc) == "324")
doAssert(testGR([rgb, rgc, rgv]) == "243")
doAssert(testGR([rgc, rgv, rgb]) == "432")
doAssert(testGR([rgv, rgb, rgc]) == "324")
block test_inproc_2:
var pgv = PRBase[int](val: 3)
var pgc = PRC[int](val: 4)
var pgb = PRD[int](val: 2)
doAssert(testPR(pgb.addr, pgc.addr, pgv.addr) == "243")
doAssert(testPR(pgc.addr, pgv.addr, pgb.addr) == "432")
doAssert(testPR(pgv.addr, pgb.addr, pgc.addr) == "324")
doAssert(testPR([pgb.addr, pgc.addr, pgv.addr]) == "243")
doAssert(testPR([pgc.addr, pgv.addr, pgb.addr]) == "432")
doAssert(testPR([pgv.addr, pgb.addr, pgc.addr]) == "324")
test_inproc()
template reject(x) =
static: assert(not compiles(x))
block test_t4799_7:
type
Vehicle[T] = ref object of RootObj
tire: T
Car[T] = object of Vehicle[T]
Bike[T] = object of Vehicle[T]
proc testVehicle[T](x: varargs[Vehicle[T]]): string {.used.} =
result = ""
for c in x:
result.add $c.tire
var v = Vehicle[int](tire: 3)
var c = Car[int](tire: 4)
var b = Bike[int](tire: 2)
reject:
echo testVehicle(b, c, v)
block test_t4799_8:
type
Vehicle = ref object of RootObj
tire: int
Car = object of Vehicle
Bike = object of Vehicle
proc testVehicle(x: varargs[Vehicle]): string {.used.} =
result = ""
for c in x:
result.add $c.tire
var v = Vehicle(tire: 3)
var c = Car(tire: 4)
var b = Bike(tire: 2)
reject:
echo testVehicle(b, c, v)
type
PGVehicle[T] = ptr object of RootObj
tire: T
PGCar[T] = object of PGVehicle[T]
PGBike[T] = object of PGVehicle[T]
proc testVehicle[T](x: varargs[PGVehicle[T]]): string {.used.} =
result = ""
for c in x:
result.add $c.tire
var pgc = PGCar[int](tire: 4)
var pgb = PGBike[int](tire: 2)
reject:
echo testVehicle(pgb, pgc)
type
RVehicle = ptr object of RootObj
tire: int
RCar = object of RVehicle
RBike = object of RVehicle
proc testVehicle(x: varargs[RVehicle]): string {.used.} =
result = ""
for c in x:
result.add $c.tire
var rc = RCar(tire: 4)
var rb = RBike(tire: 2)
reject:
echo testVehicle(rb, rc)
echo "OK"

20
tests/typerel/t4799_1.nim Normal file
View file

@ -0,0 +1,20 @@
discard """
outputsub: '''ObjectAssignmentError'''
exitcode: "1"
"""
type
Vehicle[T] = object of RootObj
tire: T
Car[T] = object of Vehicle[T]
Bike[T] = object of Vehicle[T]
proc testVehicle[T](x: varargs[Vehicle[T]]): string =
result = ""
for c in x:
result.add $c.tire
var v = Vehicle[int](tire: 3)
var c = Car[int](tire: 4)
var b = Bike[int](tire: 2)
echo testVehicle b, c, v

20
tests/typerel/t4799_2.nim Normal file
View file

@ -0,0 +1,20 @@
discard """
outputsub: '''ObjectAssignmentError'''
exitcode: "1"
"""
type
Vehicle[T] = object of RootObj
tire: T
Car[T] = object of Vehicle[T]
Bike[T] = object of Vehicle[T]
proc testVehicle[T](x: varargs[Vehicle[T]]): string =
result = ""
for c in x:
result.add $c.tire
var v = Vehicle[int](tire: 3)
var c = Car[int](tire: 4)
var b = Bike[int](tire: 2)
echo testVehicle([b, c, v])

20
tests/typerel/t4799_3.nim Normal file
View file

@ -0,0 +1,20 @@
discard """
outputsub: '''ObjectAssignmentError'''
exitcode: "1"
"""
type
Vehicle = object of RootObj
tire: int
Car = object of Vehicle
Bike = object of Vehicle
proc testVehicle(x: varargs[Vehicle]): string =
result = ""
for c in x:
result.add $c.tire
var v = Vehicle(tire: 3)
var c = Car(tire: 4)
var b = Bike(tire: 2)
echo testVehicle([b, c, v])

View file

@ -82,3 +82,11 @@ block:
assert fileExists("MISSINGFILE") == false assert fileExists("MISSINGFILE") == false
assert dirExists("MISSINGDIR") == false assert dirExists("MISSINGDIR") == false
# #7210
block:
static:
proc f(size: int): int =
var some = newStringOfCap(size)
result = size
doAssert f(4) == 4