diff --git a/changelog.md b/changelog.md
index eebd47fb8b..7aa52ece28 100644
--- a/changelog.md
+++ b/changelog.md
@@ -39,5 +39,26 @@
- Added ``typetraits.$`` as an alias for ``typetraits.name``.
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
what to return if the environment variable does not exist.
-- Removed PDCurses wrapper from the stdlib and published it as a separate
+- Removed PDCurses wrapper from the stdlib and published it as a separate
Nimble package.
+- Bodies of ``for`` loops now get their own scope:
+
+.. code-block:: nim
+ # now compiles:
+ for i in 0..4:
+ let i = i + 1
+ echo i
+
+- The parsing rules of ``if`` expressions were changed so that multiple
+ statements are allowed in the branches. We found few code examples that
+ now fail because of this change, but here is one:
+
+.. code-block:: nim
+
+ t[ti] = if exp_negative: '-' else: '+'; inc(ti)
+
+This now needs to be written as:
+
+.. code-block:: nim
+
+ t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index a7c4e6b426..8be53f11db 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -1067,7 +1067,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
else:
r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res]
else:
- r.res = "chckIndx($1, $2, $3.length-1)-$2" % [b.res, rope(first), a.res]
+ r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res]
elif first != 0:
r.res = "($1)-$2" % [b.res, rope(first)]
else:
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index e64e0a898a..f8d107c84d 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -764,7 +764,10 @@ proc semCaptureSym*(s, owner: PSym) =
var o = owner.skipGenericOwner
while o.kind != skModule and o != nil:
if s.owner == o:
- owner.typ.callConv = ccClosure
+ if owner.typ.callConv in {ccClosure, ccDefault} or owner.kind == skIterator:
+ owner.typ.callConv = ccClosure
+ else:
+ discard "do not produce an error here, but later"
#echo "computing .closure for ", owner.name.s, " ", owner.info, " because of ", s.name.s
o = o.skipGenericOwner
# since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index f29a4e106e..3610a14862 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -52,17 +52,19 @@ proc lookupParam(params, dest: PNode): PSym =
if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:
return params[i].sym
-proc liftLocalsIfRequested*(prc: PSym) =
+proc liftLocalsIfRequested*(prc: PSym; n: PNode): PNode =
let liftDest = getPragmaVal(prc.ast, wLiftLocals)
- if liftDest == nil: return
+ if liftDest == nil: return n
let partialParam = lookupParam(prc.typ.n, liftDest)
if partialParam.isNil:
localError(liftDest.info, "'$1' is not a parameter of '$2'" %
[$liftDest, prc.name.s])
- return
+ return n
let objType = partialParam.typ.skipTypes(abstractPtrs)
if objType.kind != tyObject or tfPartial notin objType.flags:
localError(liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
- return
+ return n
var c = Ctx(partialParam: partialParam, objType: objType)
- liftLocals(prc.ast, bodyPos, c)
+ let w = newTree(nkStmtList, n)
+ liftLocals(w, 0, c)
+ result = w[0]
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index d7b5f147d9..5d112c6b91 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -98,6 +98,7 @@ proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): strin
proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
+ if result.isNil: result = ""
proc lookupPackage(pkg, subdir: PNode): string =
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
@@ -166,7 +167,8 @@ proc checkModuleName*(n: PNode; doLocalError=true): int32 =
let fullPath = findModule(modulename, n.info.toFullPath)
if fullPath.len == 0:
if doLocalError:
- localError(n.info, errCannotOpenFile, modulename)
+ let m = if modulename.len > 0: modulename else: $n
+ localError(n.info, errCannotOpenFile, m)
result = InvalidFileIDX
else:
- result = fullPath.fileInfoIdx
\ No newline at end of file
+ result = fullPath.fileInfoIdx
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 8d43103dbe..2668c72ae0 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -61,7 +61,7 @@ type
errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects,
errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX,
errCannotInstantiateX, errExprHasNoAddress, errXStackEscape,
- errVarForOutParamNeeded,
+ errVarForOutParamNeededX,
errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX,
errAmbiguousCallXYZ, errWrongNumberOfArguments,
errWrongNumberOfArgumentsInCall,
@@ -268,7 +268,7 @@ const
errCannotInstantiateX: "cannot instantiate: \'$1\'",
errExprHasNoAddress: "expression has no address",
errXStackEscape: "address of '$1' may not escape its stack frame",
- errVarForOutParamNeeded: "for a \'var\' type a variable needs to be passed",
+ errVarForOutParamNeededX: "for a \'var\' type a variable needs to be passed; but '$1' is immutable",
errPureTypeMismatch: "type mismatch",
errTypeMismatch: "type mismatch: got (",
errButExpected: "but expected one of: ",
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 1139221890..e3bb68da43 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -785,21 +785,58 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
#| 'else' colcom expr
#| ifExpr = 'if' condExpr
#| whenExpr = 'when' condExpr
- result = newNodeP(kind, p)
- while true:
- getTok(p) # skip `if`, `elif`
- var branch = newNodeP(nkElifExpr, p)
+ when true:
+ result = newNodeP(kind, p)
+ while true:
+ getTok(p) # skip `if`, `when`, `elif`
+ var branch = newNodeP(nkElifExpr, p)
+ optInd(p, branch)
+ addSon(branch, parseExpr(p))
+ colcom(p, branch)
+ addSon(branch, parseStmt(p))
+ skipComment(p, branch)
+ addSon(result, branch)
+ if p.tok.tokType != tkElif: break # or not sameOrNoInd(p): break
+ if p.tok.tokType == tkElse: # and sameOrNoInd(p):
+ var branch = newNodeP(nkElseExpr, p)
+ eat(p, tkElse)
+ colcom(p, branch)
+ addSon(branch, parseStmt(p))
+ addSon(result, branch)
+ else:
+ var
+ b: PNode
+ wasIndented = false
+ result = newNodeP(kind, p)
+
+ getTok(p)
+ let branch = newNodeP(nkElifExpr, p)
addSon(branch, parseExpr(p))
colcom(p, branch)
+ let oldInd = p.currInd
+ if realInd(p):
+ p.currInd = p.tok.indent
+ wasIndented = true
+ echo result.info, " yes ", p.currInd
addSon(branch, parseExpr(p))
- optInd(p, branch)
- addSon(result, branch)
- if p.tok.tokType != tkElif: break
- var branch = newNodeP(nkElseExpr, p)
- eat(p, tkElse)
- colcom(p, branch)
- addSon(branch, parseExpr(p))
- addSon(result, branch)
+ result.add branch
+ while sameInd(p) or not wasIndented:
+ case p.tok.tokType
+ of tkElif:
+ b = newNodeP(nkElifExpr, p)
+ getTok(p)
+ optInd(p, b)
+ addSon(b, parseExpr(p))
+ of tkElse:
+ b = newNodeP(nkElseExpr, p)
+ getTok(p)
+ else: break
+ colcom(p, b)
+ addSon(b, parseStmt(p))
+ addSon(result, b)
+ if b.kind == nkElseExpr: break
+ if wasIndented:
+ p.currInd = oldInd
proc parsePragma(p: var TParser): PNode =
#| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
@@ -2036,8 +2073,13 @@ proc parseStmt(p: var TParser): PNode =
if a.kind != nkEmpty:
addSon(result, a)
else:
- parMessage(p, errExprExpected, p.tok)
- getTok(p)
+ # This is done to make the new 'if' expressions work better.
+ # XXX Eventually we need to be more strict here.
+ if p.tok.tokType notin {tkElse, tkElif}:
+ parMessage(p, errExprExpected, p.tok)
+ getTok(p)
+ else:
+ break
if not p.hasProgress and p.tok.tokType == tkEof: break
else:
# the case statement is only needed for better error messages:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 7a16f495a7..d600b1c486 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -465,7 +465,7 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
addSon(result, n)
if isAssignable(c, n) notin {arLValue, arLocalLValue}:
- localError(n.info, errVarForOutParamNeeded)
+ localError(n.info, errVarForOutParamNeededX, $n)
proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
result = n
@@ -509,9 +509,10 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
for i in countup(1, sonsLen(n) - 1):
if i < sonsLen(t) and t.sons[i] != nil and
skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
- if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}:
- if n.sons[i].kind != nkHiddenAddr:
- localError(n.sons[i].info, errVarForOutParamNeeded)
+ let it = n[i]
+ if isAssignable(c, it) notin {arLValue, arLocalLValue}:
+ if it.kind != nkHiddenAddr:
+ localError(it.info, errVarForOutParamNeededX, $it)
return
for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkHiddenCallConv:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index fb17dced84..da2c6fe7f3 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -335,8 +335,10 @@ proc semGenericStmt(c: PContext, n: PNode,
n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx)
for i in countup(0, L - 3):
addTempDecl(c, n.sons[i], skForVar)
+ openScope(c)
n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx)
closeScope(c)
+ closeScope(c)
of nkBlockStmt, nkBlockExpr, nkBlockType:
checkSonsLen(n, 2)
openScope(c)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f747b9b416..c1bf3662f6 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -697,7 +697,9 @@ proc semForVars(c: PContext, n: PNode): PNode =
if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addForVarDecl(c, v)
inc(c.p.nestedLoopCounter)
+ openScope(c)
n.sons[length-1] = semStmt(c, n.sons[length-1])
+ closeScope(c)
dec(c.p.nestedLoopCounter)
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 426290c788..1c9d8271af 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -366,8 +366,10 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
n.sons[L-2] = semTemplBody(c, n.sons[L-2])
for i in countup(0, L - 3):
addLocalDecl(c, n.sons[i], skForVar)
+ openScope(c)
n.sons[L-1] = semTemplBody(c, n.sons[L-1])
closeScope(c)
+ closeScope(c)
of nkBlockStmt, nkBlockExpr, nkBlockType:
checkSonsLen(n, 2)
openScope(c)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 231dd80f47..97b18306b2 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -1057,9 +1057,10 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
else: isNone
of tyUserTypeClass, tyUserTypeClassInst:
- if c.c.matchedConcept != nil:
+ if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
# consider this: 'var g: Node' *within* a concept where 'Node'
# is a concept too (tgraph)
+ inc c.c.matchedConcept.depth
let x = typeRel(c, a, f, flags + {trDontBind})
if x >= isGeneric:
return isGeneric
diff --git a/compiler/transf.nim b/compiler/transf.nim
index c3bdd4ddc7..baf801cbf1 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -978,7 +978,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
liftDefer(c, result)
#result = liftLambdas(prc, result)
when useEffectSystem: trackProc(prc, result)
- liftLocalsIfRequested(prc)
+ result = liftLocalsIfRequested(prc, result)
if c.needsDestroyPass and newDestructors:
result = injectDestructorCalls(prc, result)
incl(result.flags, nfTransf)
diff --git a/config/nim.cfg b/config/nim.cfg
index 6ae55a9b2a..a146c4ebf4 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -98,7 +98,6 @@ path="$lib/pure"
clang.options.linker = "-landroid-glob"
clang.cpp.options.linker = "-landroid-glob"
tcc.options.linker = "-landroid-glob"
- define:"useShPath:/system/bin/sh"
@end
@end
diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt
index 023895f730..db7ce7e63e 100644
--- a/doc/manual/pragmas.txt
+++ b/doc/manual/pragmas.txt
@@ -316,7 +316,7 @@ factor.
immediate pragma
----------------
-See `Ordinary vs immediate templates`_.
+See `Typed vs untyped parameters`_.
compilation option pragmas
diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim
index 153db9ed86..f068c7d560 100644
--- a/lib/deprecated/pure/sockets.nim
+++ b/lib/deprecated/pure/sockets.nim
@@ -262,7 +262,7 @@ proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM,
# TODO: Perhaps this should just raise EOS when an error occurs.
when defined(Windows):
- result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered)
+ result = newTSocket(winlean.socket(cint(domain), cint(typ), cint(protocol)), buffered)
else:
result = newTSocket(posix.socket(toInt(domain), toInt(typ), toInt(protocol)), buffered)
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 13016bfc03..6fed401417 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -798,7 +798,8 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
if arg.valid:
let htmlOut = if isObject:
""
- else: "
"
+ else:
+ "
"
dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}",
[arg, options])
if len(n) >= 3: renderRstToOut(d, n.sons[2], result)
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 71d3d9c727..d768a7de99 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -767,7 +767,9 @@ elif not defined(useNimRtl):
var sysCommand: string
var sysArgsRaw: seq[string]
if poEvalCommand in options:
- const useShPath {.strdefine.} = "/bin/sh"
+ const useShPath {.strdefine.} =
+ when not defined(android): "/bin/sh"
+ else: "/system/bin/sh"
sysCommand = useShPath
sysArgsRaw = @[sysCommand, "-c", command]
assert args.len == 0, "`args` has to be empty when using poEvalCommand."
diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim
index 6e97237e04..6ddd61afa6 100644
--- a/lib/pure/ropes.nim
+++ b/lib/pure/ropes.nim
@@ -17,6 +17,7 @@
## runtime efficiency.
include "system/inclrtl"
+import streams
{.deadCodeElim: on.}
@@ -130,7 +131,7 @@ proc insertInCache(s: string, tree: Rope): Rope =
result.left = t
t.right = nil
-proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} =
+proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} =
## Converts a string to a rope.
if s.len == 0:
result = nil
@@ -242,10 +243,13 @@ proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} =
## writes a rope to a file.
for s in leaves(r): write(f, s)
+proc write*(s: Stream, r: Rope) {.rtl, extern: "nroWriteStream".} =
+ ## writes a rope to a stream.
+ for rs in leaves(r): write(s, rs)
+
proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}=
## converts a rope back to a string.
- result = newString(r.len)
- setLen(result, 0)
+ result = newStringOfCap(r.len)
for s in leaves(r): add(result, s)
when false:
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 2b87e0d430..0b55e6b1d8 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -578,15 +578,46 @@ iterator split*(s: string, seps: set[char] = Whitespace,
else:
splitCommon(s, seps, maxsplit, 1)
-iterator splitWhitespace*(s: string): string =
- ## Splits at whitespace.
- oldSplit(s, Whitespace, -1)
+iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
+ ## Splits the string ``s`` at whitespace stripping leading and trailing
+ ## whitespace if necessary. If ``maxsplit`` is specified and is positive,
+ ## no more than ``maxsplit`` splits is made.
+ ##
+ ## The following code:
+ ##
+ ## .. code-block:: nim
+ ## let s = " foo \t bar baz "
+ ## for ms in [-1, 1, 2, 3]:
+ ## echo "------ maxsplit = ", ms, ":"
+ ## for item in s.splitWhitespace(maxsplit=ms):
+ ## echo '"', item, '"'
+ ##
+ ## ...results in:
+ ##
+ ## .. code-block::
+ ## ------ maxsplit = -1:
+ ## "foo"
+ ## "bar"
+ ## "baz"
+ ## ------ maxsplit = 1:
+ ## "foo"
+ ## "bar baz "
+ ## ------ maxsplit = 2:
+ ## "foo"
+ ## "bar"
+ ## "baz "
+ ## ------ maxsplit = 3:
+ ## "foo"
+ ## "bar"
+ ## "baz"
+ ##
+ oldSplit(s, Whitespace, maxsplit)
-proc splitWhitespace*(s: string): seq[string] {.noSideEffect,
+proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
rtl, extern: "nsuSplitWhitespace".} =
- ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
+ ## The same as the `splitWhitespace <#splitWhitespace.i,string,int>`_
## iterator, but is a proc that returns a sequence of substrings.
- accumulateResult(splitWhitespace(s))
+ accumulateResult(splitWhitespace(s, maxsplit))
iterator split*(s: string, sep: char, maxsplit: int = -1): string =
## Splits the string `s` into substrings using a single separator.
@@ -671,7 +702,7 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
maxsplit: int = -1): string =
## Splits the string `s` into substrings from the right using a
## string separator. Works exactly the same as `split iterator
- ## <#split.i,string,char>`_ except in reverse order.
+ ## <#split.i,string,char,int>`_ except in reverse order.
##
## .. code-block:: nim
## for piece in "foo bar".rsplit(WhiteSpace):
@@ -691,7 +722,7 @@ iterator rsplit*(s: string, sep: char,
maxsplit: int = -1): string =
## Splits the string `s` into substrings from the right using a
## string separator. Works exactly the same as `split iterator
- ## <#split.i,string,char>`_ except in reverse order.
+ ## <#split.i,string,char,int>`_ except in reverse order.
##
## .. code-block:: nim
## for piece in "foo:bar".rsplit(':'):
@@ -710,7 +741,7 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
keepSeparators: bool = false): string =
## Splits the string `s` into substrings from the right using a
## string separator. Works exactly the same as `split iterator
- ## <#split.i,string,string>`_ except in reverse order.
+ ## <#split.i,string,string,int>`_ except in reverse order.
##
## .. code-block:: nim
## for piece in "foothebar".rsplit("the"):
@@ -791,13 +822,13 @@ proc countLines*(s: string): int {.noSideEffect,
proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.
noSideEffect, rtl, extern: "nsuSplitCharSet".} =
- ## The same as the `split iterator <#split.i,string,set[char]>`_, but is a
+ ## The same as the `split iterator <#split.i,string,set[char],int>`_, but is a
## proc that returns a sequence of substrings.
accumulateResult(split(s, seps, maxsplit))
proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
rtl, extern: "nsuSplitChar".} =
- ## The same as the `split iterator <#split.i,string,char>`_, but is a proc
+ ## The same as the `split iterator <#split.i,string,char,int>`_, but is a proc
## that returns a sequence of substrings.
accumulateResult(split(s, sep, maxsplit))
@@ -806,7 +837,7 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff
## Splits the string `s` into substrings using a string separator.
##
## Substrings are separated by the string `sep`. This is a wrapper around the
- ## `split iterator <#split.i,string,string>`_.
+ ## `split iterator <#split.i,string,string,int>`_.
doAssert(sep.len > 0)
accumulateResult(split(s, sep, maxsplit))
@@ -814,7 +845,7 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff
proc rsplit*(s: string, seps: set[char] = Whitespace,
maxsplit: int = -1): seq[string]
{.noSideEffect, rtl, extern: "nsuRSplitCharSet".} =
- ## The same as the `rsplit iterator <#rsplit.i,string,set[char]>`_, but is a
+ ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
## proc that returns a sequence of substrings.
##
## A possible common use case for `rsplit` is path manipulation,
@@ -836,7 +867,7 @@ proc rsplit*(s: string, seps: set[char] = Whitespace,
proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
{.noSideEffect, rtl, extern: "nsuRSplitChar".} =
- ## The same as the `split iterator <#rsplit.i,string,char>`_, but is a proc
+ ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc
## that returns a sequence of substrings.
##
## A possible common use case for `rsplit` is path manipulation,
@@ -858,7 +889,7 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
{.noSideEffect, rtl, extern: "nsuRSplitString".} =
- ## The same as the `split iterator <#rsplit.i,string,string>`_, but is a proc
+ ## The same as the `rsplit iterator <#rsplit.i,string,string,int>`_, but is a proc
## that returns a sequence of substrings.
##
## A possible common use case for `rsplit` is path manipulation,
@@ -2599,6 +2630,12 @@ bar
doAssert s.split(' ', maxsplit=1) == @["", "this is an example "]
doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "]
+ doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
+ doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "]
+ doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "]
+ doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "]
+ doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"]
+
block: # formatEng tests
doAssert formatEng(0, 2, trim=false) == "0.00"
doAssert formatEng(0, 2) == "0"
diff --git a/lib/system.nim b/lib/system.nim
index 6adcb9a37f..7c095aa2c4 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -3531,14 +3531,14 @@ proc `[]`*[Idx, T, U, V](a: array[Idx, T], x: HSlice[U, V]): seq[T] =
let xa = a ^^ x.a
let L = (a ^^ x.b) - xa + 1
result = newSeq[T](L)
- for i in 0..= "0.17.3":
+ type Index = int or BackwardsIndex
+ template `^^`(s, i: untyped): untyped =
+ when i is BackwardsIndex:
+ s.len - int(i)
+ else: i
+else:
+ type Index = int
+ template `^^`(s, i: untyped): untyped =
+ i
+
+## With Nim devel from the start of the week (~Oct30) I managed to trigger "lib/system.nim(3536, 4) Error: expression has no address"
+## but I can't anymore after updating Nim (Nov5)
+## Now commenting this plain compiles and removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
+proc `[]`*(a: var MetadataArray, idx: Index): var int {.inline.} =
+ a.data[a ^^ idx]
+
+
+##############################
+### Completely unrelated lib that triggers the issue
+
+type
+ MySeq[T] = ref object
+ data: seq[T]
+
+proc test[T](sx: MySeq[T]) =
+ # Removing the backward index removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
+ echo sx.data[^1] # error here
+
+let s = MySeq[int](data: @[1, 2, 3])
+s.test()
diff --git a/tests/async/tasyncdial.nim b/tests/async/tasyncdial.nim
index d70e14020e..fa81235fef 100644
--- a/tests/async/tasyncdial.nim
+++ b/tests/async/tasyncdial.nim
@@ -4,6 +4,7 @@ discard """
OK AF_INET
OK AF_INET6
'''
+ disabled: "travis"
"""
import
diff --git a/tests/closure/tinvalidclosure3.nim b/tests/closure/tinvalidclosure3.nim
new file mode 100644
index 0000000000..31c4976f88
--- /dev/null
+++ b/tests/closure/tinvalidclosure3.nim
@@ -0,0 +1,12 @@
+discard """
+ line: 9
+ errormsg: "illegal capture 'x'"
+"""
+
+proc outer(arg: string) =
+ var x = 0
+ proc inner {.inline.} =
+ echo "inner", x
+ inner()
+
+outer("abc")
\ No newline at end of file
diff --git a/tests/concepts/tinfrecursion.nim b/tests/concepts/tinfrecursion.nim
new file mode 100644
index 0000000000..60db410dee
--- /dev/null
+++ b/tests/concepts/tinfrecursion.nim
@@ -0,0 +1,13 @@
+
+# bug #6691
+type
+ ConceptA = concept c
+
+ ConceptB = concept c
+ c.myProc(ConceptA)
+
+ Obj = object
+
+proc myProc(obj: Obj, x: ConceptA) = discard
+
+echo Obj is ConceptB
diff --git a/tests/js/tarrayboundscheck.nim b/tests/js/tarrayboundscheck.nim
new file mode 100755
index 0000000000..f0eaeb89de
--- /dev/null
+++ b/tests/js/tarrayboundscheck.nim
@@ -0,0 +1,44 @@
+discard """
+ output: '''idx out of bounds: -1
+month out of bounds: 0
+Jan
+Feb
+Mar
+Apr
+May
+Jun
+Jul
+Aug
+Sep
+Oct
+Nov
+Dec
+month out of bounds: 13
+idx out of bounds: 14
+'''
+"""
+
+{.push boundChecks:on.}
+
+# see issue #6532:
+# js backend 0.17.3: array bounds check for non zero based arrays is buggy
+
+proc test_arrayboundscheck() =
+ var months: array[1..12, string] =
+ ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+
+ var indices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13]
+
+ for i in -1 .. 14:
+ try:
+ let idx = indices[i]
+ try:
+ echo months[idx]
+ except:
+ echo "month out of bounds: ", idx
+ except:
+ echo "idx out of bounds: ", i
+
+test_arrayboundscheck()
+{.pop.}
\ No newline at end of file
diff --git a/tests/lexer/tind1.nim b/tests/lexer/tind1.nim
index 8a2aea9b2f..ffbde48fd1 100644
--- a/tests/lexer/tind1.nim
+++ b/tests/lexer/tind1.nim
@@ -1,6 +1,6 @@
discard """
line: 24
- errormsg: "expression expected, but found 'keyword else'"
+ errormsg: "invalid indentation"
"""
import macros
@@ -11,7 +11,7 @@ var x = if 4 != 5:
else:
"no"
-macro mymacro(n): untyped {.immediate.} =
+macro mymacro(n, b): untyped =
discard
mymacro:
diff --git a/tests/parser/tletcolon.nim b/tests/parser/tletcolon.nim
index 6b86535c82..eab7a8eddb 100644
--- a/tests/parser/tletcolon.nim
+++ b/tests/parser/tletcolon.nim
@@ -32,3 +32,21 @@ let other = x:
echo "no"
let outer = y(5):
echo "yes"
+
+
+# bug #6609
+type
+ TextureInternalFormat = enum RED, RGB, RGBA
+
+const channels = 4
+
+let format =
+ if channels == 1:
+ TextureInternalFormat.RED
+ elif channels == 3:
+ TextureInternalFormat.RGB
+ elif channels == 4:
+ TextureInternalFormat.RGBA
+ else:
+ echo "Texture Format Unknown, assuming RGB" #This echo causes an error
+ TextureInternalFormat.RGB
diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim
index e759e29772..54588d3f0b 100644
--- a/tests/stdlib/thttpclient.nim
+++ b/tests/stdlib/thttpclient.nim
@@ -2,6 +2,7 @@ discard """
cmd: "nim c --threads:on -d:ssl $file"
exitcode: 0
output: "OK"
+ disabled: "travis"
"""
import strutils
diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim
index da6088d709..6951501793 100644
--- a/tests/stdlib/tnetdial.nim
+++ b/tests/stdlib/tnetdial.nim
@@ -2,6 +2,7 @@ discard """
cmd: "nim c --threads:on $file"
exitcode: 0
output: "OK"
+ disabled: "travis"
"""
import os, net, nativesockets, asyncdispatch
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 675ff946f1..68e988975b 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -418,8 +418,9 @@ proc `&?.`(a, b: string): string =
proc processSingleTest(r: var TResults, cat: Category, options, test: string) =
let test = "tests" & DirSep &.? cat.string / test
+ let target = if cat.string.normalize == "js": targetJS else: targetC
- if existsFile(test): testSpec r, makeTest(test, options, cat)
+ if existsFile(test): testSpec r, makeTest(test, options, cat, target = target)
else: echo "[Warning] - ", test, " test does not exist"
proc processCategory(r: var TResults, cat: Category, options: string) =
diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim
index 89e786d481..e5506e7962 100644
--- a/tests/testament/specs.nim
+++ b/tests/testament/specs.nim
@@ -12,6 +12,8 @@ import parseutils, strutils, os, osproc, streams, parsecfg
var compilerPrefix* = "compiler" / "nim "
+let isTravis = existsEnv("TRAVIS")
+
proc cmdTemplate*(): string =
compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file"
@@ -174,6 +176,8 @@ proc parseSpec*(filename: string): TSpec =
when defined(unix): result.err = reIgnored
of "posix":
when defined(posix): result.err = reIgnored
+ of "travis":
+ if isTravis: result.err = reIgnored
else:
raise newException(ValueError, "cannot interpret as a bool: " & e.value)
of "cmd":
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index 0daf4089e5..d75c9d770d 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -439,6 +439,8 @@ proc main() =
var optPrintResults = false
var optFailing = false
+ var targetsStr = ""
+
var p = initOptParser()
p.next()
while p.kind == cmdLongoption:
@@ -446,7 +448,9 @@ proc main() =
of "print", "verbose": optPrintResults = true
of "failing": optFailing = true
of "pedantic": discard "now always enabled"
- of "targets": targets = parseTargets(p.val.string)
+ of "targets":
+ targetsStr = p.val.string
+ targets = parseTargets(targetsStr)
of "nim": compilerPrefix = p.val.string
else: quit Usage
p.next()
@@ -457,7 +461,9 @@ proc main() =
case action
of "all":
let testsDir = "tests" & DirSep
- let myself = quoteShell(findExe("tests" / "testament" / "tester"))
+ var myself = quoteShell(findExe("tests" / "testament" / "tester"))
+ if targetsStr.len > 0:
+ myself &= " '--targets:" & targetsStr & "'"
var cmds: seq[string] = @[]
let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: ""
for kind, dir in walkDir(testsDir):
diff --git a/tests/untestable/readme.markdown b/tests/untestable/readme.markdown
index fcb7f4f288..de1ba9459f 100644
--- a/tests/untestable/readme.markdown
+++ b/tests/untestable/readme.markdown
@@ -1,2 +1,9 @@
-This directory contains tests which are not automatically executed
-for various reasons. Mainly due to dependencies on external services.
\ No newline at end of file
+This directory contains integration tests which are not automatically executed
+for various reasons:
+- dependency on external services
+- dependency on files / configuration / state of the local host
+- tests that are extremely slow or require large amounts of memory or storage
+- tests that spawn local daemons
+
+Integration tests can become stale very quickly. Automated ./koch tests are
+strongly recommended.