Merge branch 'devel' of github.com:nim-lang/Nim into devel

This commit is contained in:
Andreas Rumpf
2017-12-27 12:23:17 +01:00
17 changed files with 292 additions and 35 deletions

View File

@@ -48,3 +48,5 @@ script:
- ./koch csource
- ./koch nimsuggest
# - nim c -r nimsuggest/tester
- ( ! grep -F '.. code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )
- ( ! grep -F '..code-block' -l -r --include '*.html' --exclude contributing.html --exclude docgen.html --exclude tut2.html )

View File

@@ -102,10 +102,12 @@ This now needs to be written as:
- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
extension that we called ``:test:``::
```rst
.. code-block:: nim
:test:
# shows how the 'if' statement works
if true: echo "yes"
```
- The ``[]`` proc for strings now raises an ``IndexError`` exception when
the specified slice is out of bounds. See issue
[#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
@@ -130,7 +132,8 @@ This now needs to be written as:
- The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
the new ``rand`` procs. The module now exports the state of the random
number generator as type ``Rand`` so multiple threads can easily use their
own random number generators that do not require locking.
own random number generators that do not require locking. For more information
about this rename see issue [#6934](https://github.com/nim-lang/Nim/issues/6934)
- The compiler is now more consistent in its treatment of ambiguous symbols:
Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
- ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
@@ -141,3 +144,25 @@ This now needs to be written as:
- codegenDecl pragma now works for the JavaScript backend. It returns an empty string for
function return type placeholders.
- Asynchronous programming for the JavaScript backend using the `asyncjs` module.
- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
statements after call to noreturn procs are no longer allowed.
- Noreturn proc calls and raising exceptions branches are now skipped during common type
deduction in if and case expressions. The following code snippets now compile:
```nim
import strutils
let str = "Y"
let a = case str:
of "Y": true
of "N": false
else: raise newException(ValueError, "Invalid boolean")
let b = case str:
of nil, "": raise newException(ValueError, "Invalid boolean")
elif str.startsWith("Y"): true
elif str.startsWith("N"): false
else: false
let c = if str == "Y": true
elif str == "N": false
else:
echo "invalid bool"
quit("this is the end")
```

View File

@@ -26,7 +26,8 @@ type
errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation,
errExceptionExpected, errExceptionAlreadyHandled,
errYieldNotAllowedHere, errYieldNotAllowedInTryStmt,
errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine,
errInvalidNumberOfYieldExpr, errCannotReturnExpr,
errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine,
errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel,
errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected,
errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler,
@@ -179,8 +180,9 @@ const
errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator",
errInvalidNumberOfYieldExpr: "invalid number of \'yield\' expressions",
errCannotReturnExpr: "current routine cannot return an expression",
errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
errAttemptToRedefine: "redefinition of \'$1\'",
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\' or \'continue'",
errStmtInvalidAfterReturn: "statement not allowed after \'return\', \'break\', \'raise\', \'continue\' or proc call with noreturn pragma",
errStmtExpected: "statement expected",
errInvalidLabel: "\'$1\' is no label",
errInvalidCmdLineOption: "invalid command line option: \'$1\'",

View File

@@ -771,6 +771,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wNoreturn:
noVal(it)
incl(sym.flags, sfNoReturn)
if sym.typ[0] != nil:
localError(sym.ast[paramsPos][0].info, errNoReturnWithReturnTypeNotAllowed)
of wDynlib:
processDynLib(c, it, sym)
of wCompilerProc, wCore:

View File

@@ -165,6 +165,19 @@ proc commonType*(x, y: PType): PType =
result = newType(k, r.owner)
result.addSonSkipIntLit(r)
proc endsInNoReturn(n: PNode): bool =
# check if expr ends in raise exception or call of noreturn proc
var it = n
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
it = it.lastSon
result = it.kind == nkRaiseStmt or
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
proc commonType*(x: PType, y: PNode): PType =
# ignore exception raising branches in case/if expressions
if endsInNoReturn(y): return x
commonType(x, y.typ)
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
when defined(nimsuggest):

View File

@@ -165,14 +165,14 @@ proc semIf(c: PContext, n: PNode): PNode =
it.sons[0] = forceBool(c, semExprWithType(c, it.sons[0]))
when not newScopeForIf: openScope(c)
it.sons[1] = semExprBranch(c, it.sons[1])
typ = commonType(typ, it.sons[1].typ)
typ = commonType(typ, it.sons[1])
closeScope(c)
elif it.len == 1:
hasElse = true
it.sons[0] = semExprBranchScope(c, it.sons[0])
typ = commonType(typ, it.sons[0].typ)
typ = commonType(typ, it.sons[0])
else: illFormedAst(it)
if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
for it in n: discardCheck(c, it.lastSon)
result.kind = nkIfStmt
# propagate any enforced VoidContext:
@@ -180,7 +180,8 @@ proc semIf(c: PContext, n: PNode): PNode =
else:
for it in n:
let j = it.len-1
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
if not endsInNoReturn(it.sons[j]):
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
result.kind = nkIfExpr
result.typ = typ
@@ -213,7 +214,7 @@ proc semCase(c: PContext, n: PNode): PNode =
semCaseBranch(c, n, x, i, covered)
var last = sonsLen(x)-1
x.sons[last] = semExprBranchScope(c, x.sons[last])
typ = commonType(typ, x.sons[last].typ)
typ = commonType(typ, x.sons[last])
of nkElifBranch:
chckCovered = false
checkSonsLen(x, 2)
@@ -221,13 +222,13 @@ proc semCase(c: PContext, n: PNode): PNode =
x.sons[0] = forceBool(c, semExprWithType(c, x.sons[0]))
when not newScopeForIf: openScope(c)
x.sons[1] = semExprBranch(c, x.sons[1])
typ = commonType(typ, x.sons[1].typ)
typ = commonType(typ, x.sons[1])
closeScope(c)
of nkElse:
chckCovered = false
checkSonsLen(x, 1)
x.sons[0] = semExprBranchScope(c, x.sons[0])
typ = commonType(typ, x.sons[0].typ)
typ = commonType(typ, x.sons[0])
hasElse = true
else:
illFormedAst(x)
@@ -237,7 +238,7 @@ proc semCase(c: PContext, n: PNode): PNode =
else:
localError(n.info, errNotAllCasesCovered)
closeScope(c)
if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or not hasElse:
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
# propagate any enforced VoidContext:
if typ == enforceVoidContext:
@@ -246,7 +247,8 @@ proc semCase(c: PContext, n: PNode): PNode =
for i in 1..n.len-1:
var it = n.sons[i]
let j = it.len-1
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
if not endsInNoReturn(it.sons[j]):
it.sons[j] = fitNode(c, typ, it.sons[j], it.sons[j].info)
result.typ = typ
proc semTry(c: PContext, n: PNode): PNode =
@@ -1851,8 +1853,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
else:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
case n.sons[i].kind
of LastBlockStmts:
if n.sons[i].kind in LastBlockStmts or
n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags:
for j in countup(i + 1, length - 1):
case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,

View File

@@ -41,7 +41,8 @@ These integer types are pre-defined:
``int``
the generic signed integer type; its size is platform dependent and has the
same size as a pointer. This type should be used in general. An integer
literal that has no type suffix is of this type.
literal that has no type suffix is of this type if it is in the range
``low(int32)..high(int32)`` otherwise the literal's type is ``int64``.
intXX
additional signed integer types of XX bits use this naming scheme

View File

@@ -1234,7 +1234,7 @@ else:
processBasicCallbacks(fd, writeList)
result = true
if Event.User in events or events == {Event.Error}:
if Event.User in events:
processBasicCallbacks(fd, readList)
custom = true
if rLength == 0:

View File

@@ -21,13 +21,41 @@
## ``nim c -r <testfile.nim>`` exits with 0 or 1
##
## Running a single test
## ---------------------
## =====================
##
## Simply specify the test name as a command line argument.
## Specify the test name as a command line argument.
##
## .. code::
##
## nim c -r test "my super awesome test name"
## nim c -r test "my test name" "another test"
##
## Multiple arguments can be used.
##
## Running a single test suite
## ===========================
##
## Specify the suite name delimited by ``"::"``.
##
## .. code::
##
## nim c -r test "my test name::"
##
## Selecting tests by pattern
## ==========================
##
## A single ``"*"`` can be used for globbing.
##
## Delimit the end of a suite name with ``"::"``.
##
## Tests matching **any** of the arguments are executed.
##
## .. code::
##
## nim c -r test fast_suite::mytest1 fast_suite::mytest2
## nim c -r test "fast_suite::mytest*"
## nim c -r test "auth*::" "crypto::hashing*"
## # Run suites starting with 'bug #' and standalone tests starting with '#'
## nim c -r test 'bug #*::' '::#*'
##
## Example
## -------
@@ -121,7 +149,7 @@ var
checkpoints {.threadvar.}: seq[string]
formatters {.threadvar.}: seq[OutputFormatter]
testsToRun {.threadvar.}: HashSet[string]
testsFilters {.threadvar.}: HashSet[string]
when declared(stdout):
abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
@@ -300,22 +328,63 @@ method testEnded*(formatter: JUnitOutputFormatter, testResult: TestResult) =
method suiteEnded*(formatter: JUnitOutputFormatter) =
formatter.stream.writeLine("\t</testsuite>")
proc shouldRun(testName: string): bool =
if testsToRun.len == 0:
proc glob(matcher, filter: string): bool =
## Globbing using a single `*`. Empty `filter` matches everything.
if filter.len == 0:
return true
result = testName in testsToRun
if not filter.contains('*'):
return matcher == filter
let beforeAndAfter = filter.split('*', maxsplit=1)
if beforeAndAfter.len == 1:
# "foo*"
return matcher.startswith(beforeAndAfter[0])
if matcher.len < filter.len - 1:
return false # "12345" should not match "123*345"
return matcher.startsWith(beforeAndAfter[0]) and matcher.endsWith(beforeAndAfter[1])
proc matchFilter(suiteName, testName, filter: string): bool =
if filter == "":
return true
if testName == filter:
# corner case for tests containing "::" in their name
return true
let suiteAndTestFilters = filter.split("::", maxsplit=1)
if suiteAndTestFilters.len == 1:
# no suite specified
let test_f = suiteAndTestFilters[0]
return glob(testName, test_f)
return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1])
when defined(testing): export matchFilter
proc shouldRun(currentSuiteName, testName: string): bool =
## Check if a test should be run by matching suiteName and testName against
## test filters.
if testsFilters.len == 0:
return true
for f in testsFilters:
if matchFilter(currentSuiteName, testName, f):
return true
return false
proc ensureInitialized() =
if formatters == nil:
formatters = @[OutputFormatter(defaultConsoleFormatter())]
if not testsToRun.isValid:
testsToRun.init()
if not testsFilters.isValid:
testsFilters.init()
when declared(paramCount):
# Read tests to run from the command line.
for i in 1 .. paramCount():
testsToRun.incl(paramStr(i))
testsFilters.incl(paramStr(i))
# These two procs are added as workarounds for
# https://github.com/nim-lang/Nim/issues/5549
@@ -395,7 +464,7 @@ template test*(name, body) {.dirty.} =
ensureInitialized()
if shouldRun(name):
if shouldRun(when declared(testSuiteName): testSuiteName else: "", name):
checkpoints = @[]
var testStatusIMPL {.inject.} = OK

View File

@@ -2916,7 +2916,10 @@ when not defined(JS): #and not defined(nimscript):
elif x > y: result = 1
else: result = 0
else:
result = int(c_strcmp(x, y))
let minlen = min(x.len, y.len)
result = int(c_memcmp(x.cstring, y.cstring, minlen.csize))
if result == 0:
result = x.len - y.len
when defined(nimscript):
proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.}

View File

@@ -63,7 +63,6 @@ proc chckObj(obj, subclass: PNimType) {.compilerproc.} =
while x != subclass:
if x == nil:
sysFatal(ObjectConversionError, "invalid object conversion")
break
x = x.base
proc chckObjAsgn(a, b: PNimType) {.compilerproc, inline.} =

View File

@@ -24,10 +24,10 @@ proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
if a == b: return 0
if a == nil: return -1
if b == nil: return 1
when defined(nimNoArrayToCstringConversion):
return c_strcmp(addr a.data, addr b.data)
else:
return c_strcmp(a.data, b.data)
let minlen = min(a.len, b.len)
result = c_memcmp(addr a.data, addr b.data, minlen.csize)
if result == 0:
result = a.len - b.len
proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
if a == b: return true

View File

@@ -36,5 +36,64 @@ var z = case i
echo z
#OUT ayyy
let str1 = "Y"
let str2 = "NN"
let a = case str1:
of "Y": true
of "N": false
else:
echo "no good"
quit("quiting")
let b = case str2:
of nil, "": raise newException(ValueError, "Invalid boolean")
elif str2[0] == 'Y': true
elif str2[0] == 'N': false
else: "error".quit(2)
doAssert(a == true)
doAssert(b == false)
var bb: bool
doassert(not compiles(
bb = case str2:
of nil, "": raise newException(ValueError, "Invalid boolean")
elif str.startsWith("Y"): true
elif str.startsWith("N"): false
))
doassert(not compiles(
bb = case str2:
of "Y": true
of "N": false
))
doassert(not compiles(
bb = case str2:
of "Y": true
of "N": raise newException(ValueError, "N not allowed")
))
doassert(not compiles(
bb = case str2:
of "Y": raise newException(ValueError, "Invalid Y")
else: raise newException(ValueError, "Invalid N")
))
doassert(not compiles(
bb = case str2:
of "Y":
raise newException(ValueError, "Invalid Y")
true
else: raise newException(ValueError, "Invalid")
))
doassert(not compiles(
bb = case str2:
of "Y":
"invalid Y".quit(3)
true
else: raise newException(ValueError, "Invalid")
))

View File

@@ -0,0 +1,22 @@
discard """
ccodeCheck: "\\i @'__attribute__((noreturn))' .*"
"""
proc noret1*(i: int) {.noreturn.} =
echo i
proc noret2*(i: int): void {.noreturn.} =
echo i
var p {.used.}: proc(i: int): int
doAssert(not compiles(
p = proc(i: int): int {.noreturn.} = i # noreturn lambda returns int
))
doAssert(not compiles(
block:
noret1(5)
echo 1 # statement after noreturn
))

View File

@@ -56,4 +56,24 @@ proc test_string_slice() =
echo("OK")
proc test_string_cmp() =
let world = "hello\0world"
let earth = "hello\0earth"
let short = "hello\0"
let hello = "hello"
let goodbye = "goodbye"
doAssert world == world
doAssert world != earth
doAssert world != short
doAssert world != hello
doAssert world != goodbye
doAssert cmp(world, world) == 0
doAssert cmp(world, earth) > 0
doAssert cmp(world, short) > 0
doAssert cmp(world, hello) > 0
doAssert cmp(world, goodbye) > 0
test_string_slice()
test_string_cmp()

View File

@@ -13,6 +13,8 @@ discard """
[Suite] bug #5784
[Suite] test name filtering
'''
"""
@@ -120,3 +122,39 @@ suite "bug #5784":
field: int
var obj: Obj
check obj.isNil or obj.field == 0
when defined(testing):
suite "test name filtering":
test "test name":
check matchFilter("suite1", "foo", "")
check matchFilter("suite1", "foo", "foo")
check matchFilter("suite1", "foo", "::")
check matchFilter("suite1", "foo", "*")
check matchFilter("suite1", "foo", "::foo")
check matchFilter("suite1", "::foo", "::foo")
test "test name - glob":
check matchFilter("suite1", "foo", "f*")
check matchFilter("suite1", "foo", "*oo")
check matchFilter("suite1", "12345", "12*345")
check matchFilter("suite1", "q*wefoo", "q*wefoo")
check false == matchFilter("suite1", "foo", "::x")
check false == matchFilter("suite1", "foo", "::x*")
check false == matchFilter("suite1", "foo", "::*x")
# overlap
check false == matchFilter("suite1", "12345", "123*345")
check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f")
test "suite name":
check matchFilter("suite1", "foo", "suite1::")
check false == matchFilter("suite1", "foo", "suite2::")
check matchFilter("suite1", "qwe::foo", "qwe::foo")
check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo")
test "suite name - glob":
check matchFilter("suite1", "foo", "::*")
check matchFilter("suite1", "foo", "*::*")
check matchFilter("suite1", "foo", "*::foo")
check false == matchFilter("suite1", "foo", "*ite2::")
check matchFilter("suite1", "q**we::foo", "q**we::foo")
check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e")

View File

@@ -1,8 +1,6 @@
version 1.0 battle plan
=======================
- introduce ``nkStmtListExpr`` for template/macro invokations to produce
better stack traces
- let 'doAssert' analyse the expressions and produce more helpful output
- fix "high priority" bugs
- try to fix as many compiler crashes as reasonable
@@ -11,6 +9,8 @@ version 1.0 battle plan
Not critical for 1.0
====================
- introduce ``nkStmtListExpr`` for template/macro invokations to produce
better stack traces
- make 'break' not leave named blocks
- make FlowVar compatible to Futures
- make 'not nil' the default (produce warnings instead of errors for