summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md23
-rw-r--r--compiler/jsgen.nim2
-rw-r--r--compiler/lambdalifting.nim5
-rw-r--r--compiler/liftlocals.nim12
-rw-r--r--compiler/modulepaths.nim6
-rw-r--r--compiler/msgs.nim4
-rw-r--r--compiler/parser.nim70
-rw-r--r--compiler/semexprs.nim9
-rw-r--r--compiler/semgnrc.nim2
-rw-r--r--compiler/semstmts.nim2
-rw-r--r--compiler/semtempl.nim2
-rw-r--r--compiler/sigmatch.nim3
-rw-r--r--compiler/transf.nim2
-rw-r--r--config/nim.cfg1
-rw-r--r--doc/manual/pragmas.txt2
-rw-r--r--lib/deprecated/pure/sockets.nim2
-rw-r--r--lib/packages/docutils/rstgen.nim3
-rw-r--r--lib/pure/osproc.nim4
-rw-r--r--lib/pure/ropes.nim10
-rw-r--r--lib/pure/strutils.nim67
-rw-r--r--lib/system.nim12
-rw-r--r--lib/system/sysstr.nim2
-rw-r--r--readme.md1
-rw-r--r--tests/array/troof1.nim8
-rw-r--r--tests/array/troofregression.nim46
-rw-r--r--tests/async/tasyncdial.nim1
-rw-r--r--tests/closure/tinvalidclosure3.nim12
-rw-r--r--tests/concepts/tinfrecursion.nim13
-rwxr-xr-xtests/js/tarrayboundscheck.nim44
-rw-r--r--tests/lexer/tind1.nim4
-rw-r--r--tests/parser/tletcolon.nim18
-rw-r--r--tests/stdlib/thttpclient.nim1
-rw-r--r--tests/stdlib/tnetdial.nim1
-rw-r--r--tests/testament/categories.nim3
-rw-r--r--tests/testament/specs.nim4
-rw-r--r--tests/testament/tester.nim10
-rw-r--r--tests/untestable/readme.markdown11
37 files changed, 353 insertions, 69 deletions
diff --git a/changelog.md b/changelog.md
index eebd47fb8..7aa52ece2 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 a7c4e6b42..8be53f11d 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 e64e0a898..f8d107c84 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 f29a4e106..3610a1486 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 d7b5f147d..5d112c6b9 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 8d43103db..2668c72ae 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 113922189..e3bb68da4 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 7a16f495a..d600b1c48 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 fb17dced8..da2c6fe7f 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 f747b9b41..c1bf3662f 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 426290c78..1c9d8271a 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 231dd80f4..97b18306b 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 c3bdd4ddc..baf801cbf 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 6ae55a9b2..a146c4ebf 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 023895f73..db7ce7e63 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 153db9ed8..f068c7d56 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 13016bfc0..6fed40141 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:
         "<object data=\"$1\" type=\"image/svg+xml\"$2 >" & content & "</object>"
-        else: "<img src=\"$1\"$2 />"
+      else:
+        "<img src=\"$1\"$2 />"
     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 71d3d9c72..d768a7de9 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 6e97237e0..6ddd61afa 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 2b87e0d43..0b55e6b1d 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 6adcb9a37..7c095aa2c 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..<L: result[i] = a[Idx(i + xa + int low(a))]
+  for i in 0..<L: result[i] = a[Idx(i + xa)]
 
 proc `[]=`*[Idx, T, U, V](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) =
   ## slice assignment for arrays.
   let xa = a ^^ x.a
   let L = (a ^^ x.b) - xa + 1
   if L == b.len:
-    for i in 0..<L: a[Idx(i + xa + int low(a))] = b[i]
+    for i in 0..<L: a[Idx(i + xa)] = b[i]
   else:
     sysFatal(RangeError, "different lengths for slice assignment")
 
@@ -3565,18 +3565,20 @@ proc `[]=`*[T, U, V](s: var seq[T], x: HSlice[U, V], b: openArray[T]) =
   else:
     spliceImpl(s, a, L, b)
 
-proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} = s[s.len - int(i)]
+proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} =
+  system.`[]`(s, s.len - int(i))
+
 proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline.} =
   a[Idx(a.len - int(i) + int low(a))]
 proc `[]`*(s: string; i: BackwardsIndex): char {.inline.} = s[s.len - int(i)]
 
 proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} =
-  s[s.len - int(i)]
+  system.`[]`(s, s.len - int(i))
 proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} =
   a[Idx(a.len - int(i) + int low(a))]
 
 proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} =
-  s[s.len - int(i)] = x
+  system.`[]=`(s, s.len - int(i), x)
 proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline.} =
   a[Idx(a.len - int(i) + int low(a))] = x
 proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline.} =
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 0627ef2fb..d9586b00d 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -509,7 +509,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
 
   # insert exponent
   t[ti] = 'E'; inc(ti)
-  t[ti] = if exp_negative: '-' else: '+'; inc(ti)
+  t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
   inc(ti, 3)
 
   # insert adjusted exponent
diff --git a/readme.md b/readme.md
index ac24658d9..6fbe60c6a 100644
--- a/readme.md
+++ b/readme.md
@@ -127,6 +127,7 @@ However, if you are short on time, you can just run the tests specific to your
 changes by only running the corresponding categories of tests. Travis CI verifies
 that all tests pass before allowing the pull request to be accepted, so only
 running specific tests should be harmless.
+Integration tests should go in ``tests/untestable``.
 
 If you're looking for ways to contribute, please look at our [issue tracker][nim-issues].
 There are always plenty of issues labelled [``Easy``][nim-issues-easy]; these should
diff --git a/tests/array/troof1.nim b/tests/array/troof1.nim
index 1ee63826e..594ad98a5 100644
--- a/tests/array/troof1.nim
+++ b/tests/array/troof1.nim
@@ -4,7 +4,8 @@ discard """
 3
 @[(Field0: 1, Field1: 2), (Field0: 3, Field1: 5)]
 2
-@[a, new one, c]'''
+@[a, new one, c]
+@[1, 2, 3]'''
 """
 
 proc foo[T](x, y: T): T = x
@@ -35,3 +36,8 @@ useOpenarray([1, 2, 3])
 var z = @["a", "b", "c"]
 mutOpenarray(z)
 echo z
+
+# bug #6675
+var y: array[1..5, int] = [1,2,3,4,5]
+y[3..5] = [1, 2, 3]
+echo y[3..5]
diff --git a/tests/array/troofregression.nim b/tests/array/troofregression.nim
new file mode 100644
index 000000000..0b96123a4
--- /dev/null
+++ b/tests/array/troofregression.nim
@@ -0,0 +1,46 @@
+###############################
+#### part from Arraymancer
+
+type
+  MetadataArray* = object
+    data*: array[8, int]
+    len*: int
+
+# Commenting the converter removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed"
+converter toMetadataArray*(se: varargs[int]): MetadataArray {.inline.} =
+  result.len = se.len
+  for i in 0..<se.len:
+    result.data[i] = se[i]
+
+
+when NimVersion >= "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 d70e14020..fa81235fe 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 000000000..31c4976f8
--- /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 000000000..60db410de
--- /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 000000000..f0eaeb89d
--- /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 8a2aea9b2..ffbde48fd 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 6b86535c8..eab7a8edd 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 e759e2977..54588d3f0 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 da6088d70..695150179 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 675ff946f..68e988975 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 89e786d48..e5506e796 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 0daf4089e..d75c9d770 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 fcb7f4f28..de1ba9459 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.