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: "" & content & "" - 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.