From 4396270fc7c447fa7ce9478a6bf9682ba7c496a7 Mon Sep 17 00:00:00 2001 From: Miguel Date: Sun, 22 Dec 2013 20:56:33 +0400 Subject: [PATCH 001/249] 'Connection' argument in 'Open' was not used, so MySQL host and port were always default ones. Now 'Connection' is treated as 'host:port'. --- lib/impure/db_mysql.nim | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 91cf8a5eb..c27be2817 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -194,9 +194,15 @@ proc Open*(connection, user, password, database: string): TDbConn {. ## opens a database connection. Raises `EDb` if the connection could not ## be established. result = mysql.Init(nil) - if result == nil: dbError("could not open database connection") - if mysql.RealConnect(result, "", user, password, database, - 0'i32, nil, 0) == nil: + if result == nil: dbError("could not open database connection") + var + cnctn = connection.split(':') + host = cnctn[0] + port: int32 = 3306 + if cnctn.len > 1: + port = int32((cnctn[1]).parseInt()) + if mysql.RealConnect(result, host, user, password, database, + port, nil, 0) == nil: var errmsg = $mysql.error(result) db_mysql.Close(result) dbError(errmsg) From 11053afff83e00bb04be05590a169ed79ce5f0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Sun, 26 Jan 2014 22:30:20 +0100 Subject: [PATCH 002/249] osproc: fix naming inconsistiences --- lib/pure/osproc.nim | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index c7678b214..f764971c4 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -13,7 +13,7 @@ include "system/inclrtl" import - strutils, os, strtabs, streams, sequtils + strutils, os, strtabs, streams when defined(windows): import winlean @@ -44,7 +44,7 @@ type poStdErrToStdOut, ## merge stdout and stderr to the stdout stream poParentStreams ## use the parent's streams -template poUseShell*: TProcessOption {.deprecated.} = poUsePath +const poUseShell* {.deprecated.} = poUsePath ## Deprecated alias for poUsePath. proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = @@ -604,20 +604,20 @@ elif not defined(useNimRtl): pipe(pStderr) != 0'i32: osError(osLastError()) - var sys_command: string - var sys_args_raw: seq[string] + var sysCommand: string + var sysArgsRaw: seq[string] if poEvalCommand in options: - sys_command = "/bin/sh" - sys_args_raw = @[sys_command, "-c", command] + sysCommand = "/bin/sh" + sysArgsRaw = @[sysCommand, "-c", command] assert args.len == 0 else: - sys_command = command - sys_args_raw = @[command] + sysCommand = command + sysArgsRaw = @[command] for arg in args.items: - sys_args_raw.add arg + sysArgsRaw.add arg - var sys_args = allocCStringArray(sys_args_raw) - finally: deallocCStringArray(sys_args) + var sysArgs = allocCStringArray(sysArgsRaw) + finally: deallocCStringArray(sysArgs) var pid: TPid when defined(posix_spawn) and not defined(useFork): @@ -650,15 +650,15 @@ elif not defined(useNimRtl): else: chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2) - var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env) + var sysEnv = if env == nil: envToCStringArray() else: envToCStringArray(env) var res: cint - # This is incorrect! + # FIXME: chdir is global to process if workingDir.len > 0: os.setCurrentDir(workingDir) if poUsePath in options: - res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env) + res = posix_spawnp(pid, sysCommand, fops, attr, sysArgs, sysEnv) else: - res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env) - deallocCStringArray(sys_env) + res = posix_spawn(pid, sysCommand, fops, attr, sysArgs, sysEnv) + deallocCStringArray(sysEnv) discard posix_spawn_file_actions_destroy(fops) discard posix_spawnattr_destroy(attr) chck res @@ -687,15 +687,15 @@ elif not defined(useNimRtl): if env == nil: if poUsePath in options: - discard execvp(sys_command, sys_args) + discard execvp(sysCommand, sysArgs) else: - discard execv(sys_command, sys_args) + discard execv(sysCommand, sysArgs) else: - var c_env = envToCStringArray(env) + var cEnv = envToCStringArray(env) if poUsePath in options: - discard execvpe(sys_command, sys_args, c_env) + discard execvpe(sysCommand, sysArgs, cEnv) else: - discard execve(sys_command, sys_args, c_env) + discard execve(sysCommand, sysArgs, cEnv) # too risky to raise an exception here: quit("execve call failed: " & $strerror(errno)) # Parent process. Copy process information. From 728be67afb54fc9645ba36edd2b5e430ccf443de Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 9 Feb 2014 21:05:02 +0000 Subject: [PATCH 003/249] Resolved conflict. --- lib/windows/winlean.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 6c8fa4882..74ef9c9ec 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -199,14 +199,14 @@ else: importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.} proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.} - proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. + proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {. importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.} proc removeDirectoryA*(lpPathName: cstring): int32 {. importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.} proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".} - proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {. + proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {. importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} when useWinUnicode: @@ -304,7 +304,7 @@ else: dwFileAttributes: int32): WINBOOL {. stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".} - proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, + proc copyFileA*(lpExistingFileName, lpNewFileName: CString, bFailIfExists: cint): cint {. importc: "CopyFileA", stdcall, dynlib: "kernel32".} From 9ce775f468e6fe7c95ced0426c5596a0585f9d01 Mon Sep 17 00:00:00 2001 From: Micky Latowicki Date: Mon, 10 Feb 2014 09:30:21 +0200 Subject: [PATCH 004/249] dynlib: optionally pass RTLD_GLOBAL to dlopen --- lib/pure/dynlib.nim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index 3ed00fdb2..889912052 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -14,7 +14,7 @@ type TLibHandle* = pointer ## a handle to a dynamically loaded library -proc loadLib*(path: string): TLibHandle +proc loadLib*(path: string, global_symbols=false): TLibHandle ## loads a library from `path`. Returns nil if the library could not ## be loaded. @@ -53,6 +53,7 @@ when defined(posix): # var RTLD_NOW {.importc: "RTLD_NOW", header: "".}: int + RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "".}: int proc dlclose(lib: TLibHandle) {.importc, header: "".} proc dlopen(path: CString, mode: int): TLibHandle {. @@ -60,7 +61,10 @@ when defined(posix): proc dlsym(lib: TLibHandle, name: cstring): pointer {. importc, header: "".} - proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW) + proc loadLib(path: string, global_symbols=false): TLibHandle = + var flags = RTLD_NOW + if global_symbols: flags = flags or RTLD_GLOBAL + return dlopen(path, flags) proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW) proc unloadLib(lib: TLibHandle) = dlclose(lib) proc symAddr(lib: TLibHandle, name: cstring): pointer = @@ -81,7 +85,7 @@ elif defined(windows) or defined(dos): proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {. importc: "GetProcAddress", header: "", stdcall.} - proc loadLib(path: string): TLibHandle = + proc loadLib(path: string, global_symbols=false): TLibHandle = result = cast[TLibHandle](winLoadLibrary(path)) proc loadLib(): TLibHandle = result = cast[TLibHandle](winLoadLibrary(nil)) From 51d2384e1aa890b243f14a80ae2a126308a37d9f Mon Sep 17 00:00:00 2001 From: Miguel Date: Mon, 10 Feb 2014 18:41:39 +0400 Subject: [PATCH 005/249] Fix for db_mysql.Open. 'Connection' argument in 'Open' was not used, so MySQL host and port were always default ones. Now 'Connection' is treated as 'host:port'. --- lib/impure/db_mysql.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 1489a0401..32cda3e4d 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -87,7 +87,7 @@ proc newRow(L: int): TRow = proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) = if row != nil: - while mysql.FetchRow(sqlres) != nil: nil + while mysql.FetchRow(sqlres) != nil: discard mysql.FreeResult(sqlres) iterator fastRows*(db: TDbConn, query: TSqlQuery, @@ -194,13 +194,13 @@ proc open*(connection, user, password, database: string): TDbConn {. ## opens a database connection. Raises `EDb` if the connection could not ## be established. result = mysql.Init(nil) - if result == nil: dbError("could not open database connection") - var - cnctn = connection.split(':') - host = cnctn[0] - port: int32 = 3306 - if cnctn.len > 1: - port = int32((cnctn[1]).parseInt()) + if result == nil: dbError("could not open database connection") + let + colonPos = connection.find(':') + host = if colonPos < 0: connection + else: substr(connection, 0, colonPos-1) + port: int32 = if colonPos < 0: 0'i32 + else: substr(connection, colonPos+1).parseInt.int32 if mysql.RealConnect(result, host, user, password, database, port, nil, 0) == nil: var errmsg = $mysql.error(result) From 007d17ce3f28dda72f26df4298bba438f76edd15 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Feb 2014 17:12:25 +0100 Subject: [PATCH 006/249] more things case consistent --- lib/pure/asyncio.nim | 10 +++++----- lib/pure/nimprof.nim | 4 ++-- todo.txt | 1 - 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim index 96afc6f4f..ab09dc860 100644 --- a/lib/pure/asyncio.nim +++ b/lib/pure/asyncio.nim @@ -167,7 +167,7 @@ proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, result = newAsyncSocket() result.socket = socket(domain, typ, protocol, buffered) result.proto = protocol - if result.socket == InvalidSocket: OSError(OSLastError()) + if result.socket == invalidSocket: osError(osLastError()) result.socket.setBlocking(false) proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket = @@ -357,7 +357,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket, client.sslNeedAccept = false client.info = SockConnected - if c == InvalidSocket: SocketError(server.socket) + if c == invalidSocket: socketError(server.socket) c.setBlocking(false) # TODO: Needs to be tested. # deleg.open is set in ``toDelegate``. @@ -481,7 +481,7 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} = of RecvDisconnected: result = true of RecvFail: - s.SocketError(async = true) + s.socketError(async = true) result = false {.pop.} @@ -615,11 +615,11 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool = if d.hasDataBuffered(d.deleVal): hasDataBufferedCount.inc() d.handleRead(d.deleVal) - if hasDataBufferedCount > 0: return True + if hasDataBufferedCount > 0: return true if readDg.len() == 0 and writeDg.len() == 0: ## TODO: Perhaps this shouldn't return if errorDg has something? - return False + return false if select(readDg, writeDg, errorDg, timeout) != 0: for i in 0..len(d.delegates)-1: diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 02f0366cd..3d0cc2154 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -67,7 +67,7 @@ when withThreads: proc hookAux(st: TStackTrace, costs: int) = # this is quite performance sensitive! - when withThreads: Acquire profilingLock + when withThreads: acquire profilingLock inc totalCalls var last = high(st) while last > 0 and isNil(st[last]): dec last @@ -106,7 +106,7 @@ proc hookAux(st: TStackTrace, costs: int) = h = ((5 * h) + 1) and high(profileData) inc chain maxChainLen = max(maxChainLen, chain) - when withThreads: Release profilingLock + when withThreads: release profilingLock when defined(memProfiler): const diff --git a/todo.txt b/todo.txt index 44aa39791..2e6eb708b 100644 --- a/todo.txt +++ b/todo.txt @@ -4,7 +4,6 @@ version 0.9.4 - fix GC issues - fix macros\tstringinterp.nim - test and fix showoff -- fix closure iterators Bugs From a158053ae9d04ebd882b2c973ddf4a3dd7d4efe8 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 11 Feb 2014 01:14:57 +0200 Subject: [PATCH 007/249] fixes #797; generic procs can be used in places expecting matching concrete proc types --- compiler/sem.nim | 1 + compiler/semdata.nim | 2 + compiler/semexprs.nim | 9 ++- compiler/sigmatch.nim | 77 +++++++++++++++--------- tests/generics/tinferredgenericprocs.nim | 20 ++++++ 5 files changed, 80 insertions(+), 29 deletions(-) create mode 100644 tests/generics/tinferredgenericprocs.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index 00ac79716..845d4ae71 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -332,6 +332,7 @@ proc myOpen(module: PSym): PPassContext = c.semOperand = semOperand c.semConstBoolExpr = semConstBoolExpr c.semOverloadedCall = semOverloadedCall + c.semGenerateInstance = generateInstance c.semTypeNode = semTypeNode pushProcCon(c, module) pushOwner(c.module) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index c9d95e1bf..0bc52d6b7 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -81,6 +81,8 @@ type semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, filter: TSymKinds): PNode {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} + semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, + info: TLineInfo): PSym includedFiles*: TIntSet # used to detect recursive include files userPragmas*: TStrTable evalContext*: PEvalContext diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a8a16672d..3fe1367ec 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -204,7 +204,14 @@ proc semConv(c: PContext, n: PNode): PNode = if not isSymChoice(op): let status = checkConvertible(c, result.typ, op.typ) case status - of convOK: discard + of convOK: + # handle SomeProcType(SomeGenericProc) + # XXX: This needs fixing. checkConvertible uses typeRel internally, but + # doesn't bother to perform the work done in paramTypeMatchAux/fitNode + # so we are redoing the typeRel work here. Why does semConv exist as a + # separate proc from fitNode? + if op.kind == nkSym and op.sym.isGenericRoutine: + result.sons[1] = fitNode(c, result.typ, result.sons[1]) of convNotNeedeed: message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) of convNotLegal: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 335ceafeb..227228f6e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -51,6 +51,8 @@ type isSubtype, isSubrange, # subrange of the wanted type; no type conversion # but apart from that counts as ``isSubtype`` + isInferred, # generic proc was matched against a concrete type + isInferredConvertible, # same as above, but requiring proc CC conversion isGeneric, isFromIntLit, # conversion *from* int literal; proven safe isEqual @@ -338,10 +340,40 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation = proc allowsNil(f: PType): TTypeRelation {.inline.} = result = if tfNotNil notin f.flags: isSubtype else: isNone -proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = - proc inconsistentVarTypes(f, a: PType): bool {.inline.} = - result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) +proc inconsistentVarTypes(f, a: PType): bool {.inline.} = + result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) +proc procParamTypeRel(c: var TCandidate, f, a: PType, + result: var TTypeRelation) = + var + m: TTypeRelation + f = f + + if a.isMetaType: + if f.isMetaType: + # we are matching a generic proc (as proc param) + # to another generic type appearing in the proc + # sigunature. there is a change that the target + # type is already fully-determined, so we are + # going to try resolve it + f = generateTypeInstance(c.c, c.bindings, c.call.info, f) + if f == nil or f.isMetaType: + # no luck resolving the type, so the inference fails + result = isNone + return + let reverseRel = typeRel(c, a, f) + if reverseRel == isGeneric: + m = isInferred + else: + m = typeRel(c, f, a) + + if m <= isSubtype or inconsistentVarTypes(f, a): + result = isNone + return + else: + result = minRel(m, result) + +proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = case a.kind of tyProc: if sonsLen(f) != sonsLen(a): return @@ -350,18 +382,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = result = isEqual # start with maximum; also correct for no # params at all for i in countup(1, sonsLen(f)-1): - var m = typeRel(c, f.sons[i], a.sons[i]) - if m <= isSubtype or inconsistentVarTypes(f.sons[i], a.sons[i]): - return isNone - else: result = minRel(m, result) + procParamTypeRel(c, f.sons[i], a.sons[i], result) if f.sons[0] != nil: if a.sons[0] != nil: - var m = typeRel(c, f.sons[0], a.sons[0]) - # Subtype is sufficient for return types! - if m < isSubtype or inconsistentVarTypes(f.sons[0], a.sons[0]): - return isNone - elif m == isSubtype: result = isConvertible - else: result = minRel(m, result) + procParamTypeRel(c, f.sons[0], a.sons[0], result) else: return isNone elif a.sons[0] != nil: @@ -376,7 +400,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = elif f.callConv != a.callConv: # valid to pass a 'nimcall' thingie to 'closure': if f.callConv == ccClosure and a.callConv == ccDefault: - result = isConvertible + result = if result != isInferred: isConvertible + else: isInferredConvertible else: return isNone when useEffectSystem: @@ -402,18 +427,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = proc matchUserTypeClass*(c: PContext, m: var TCandidate, ff, a: PType): TTypeRelation = - #if f.n == nil: - # let r = typeRel(m, f, a) - # return if r == isGeneric: arg else: nil - var body = ff.skipTypes({tyUserTypeClassInst}) - # var prev = PType(idTableGet(m.bindings, f)) - # if prev != nil: - # if sameType(prev, a): return arg - # else: return nil - - # pushInfoContext(arg.info) openScope(c) inc c.inTypeClass @@ -462,7 +477,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, else: discard return isGeneric - # put(m.bindings, f, a) proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # typeRel can be used to establish various relationships between types: @@ -988,7 +1002,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, arg = argSemantized argType = argType c = m.c - + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to @@ -1022,6 +1036,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, inc(m.subtypeMatches) #result = copyTree(arg) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + of isInferred, isInferredConvertible: + var prc = if arg.kind in nkLambdaKinds: arg[0].sym + else: arg.sym + let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) + result = newSymNode(inferred, arg.info) + if r == isInferredConvertible: + result = implicitConv(nkHiddenStdConv, f, result, m, c) of isGeneric: inc(m.genericMatches) if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}: @@ -1035,10 +1056,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, result = argOrig else: result = copyTree(arg) - result.typ = getInstantiatedType(c, arg, m, f) + result.typ = getInstantiatedType(c, arg, m, f) # BUG: f may not be the right key! if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) # BUGFIX: use ``result.typ`` and not `f` here of isFromIntLit: # too lazy to introduce another ``*matches`` field, so we conflate diff --git a/tests/generics/tinferredgenericprocs.nim b/tests/generics/tinferredgenericprocs.nim new file mode 100644 index 000000000..ac445fd32 --- /dev/null +++ b/tests/generics/tinferredgenericprocs.nim @@ -0,0 +1,20 @@ +discard """ + output: '''123 +1 +2 +3''' +""" + +# https://github.com/Araq/Nimrod/issues/797 +proc foo[T](s:T):string = $s + +type IntStringProc = proc(x: int): string + +var f1 = IntStringProc(foo) +var f2: proc(x: int): string = foo +var f3: IntStringProc = foo + +echo f1(1), f2(2), f3(3) + +for x in map([1,2,3], foo): echo x + From 232af9de6ca112feff39f10be79bd442ac0ec74f Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 11 Feb 2014 16:32:02 +0100 Subject: [PATCH 008/249] fixes #612 --- lib/wrappers/zip/zlib.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/wrappers/zip/zlib.nim b/lib/wrappers/zip/zlib.nim index c4c6ac071..cd3a765c1 100644 --- a/lib/wrappers/zip/zlib.nim +++ b/lib/wrappers/zip/zlib.nim @@ -134,6 +134,7 @@ proc gzerror*(thefile: gzFile, errnum: var int32): pbytef{.cdecl, dynlib: libz, importc: "gzerror".} proc adler32*(adler: uLong, buf: pbytef, length: uInt): uLong{.cdecl, dynlib: libz, importc: "adler32".} + ## **Warning**: Adler-32 requires at least a few hundred bytes to get rolling. proc crc32*(crc: uLong, buf: pbytef, length: uInt): uLong{.cdecl, dynlib: libz, importc: "crc32".} proc deflateInitu*(strm: var TZStream, level: int32, version: cstring, From 02cf019ab63e1be23c0a4856b6591e1c41d57f4e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 11 Feb 2014 23:50:09 +0000 Subject: [PATCH 009/249] Fixes macros.len crashing on nodes which lack the sons field. --- compiler/vm.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 10ac7aaaf..81e712047 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -441,7 +441,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = decodeBImm(nkIntLit) #assert regs[rb].kind == nkBracket # also used by mNLen: - regs[ra].intVal = regs[rb].skipMeta.len - imm + regs[ra].intVal = regs[rb].skipMeta.safeLen - imm of opcLenStr: decodeBImm(nkIntLit) if regs[rb].kind == nkNilLit: From 7e7f5a6927e349a8d6df6cdeaa1b625fd9d04569 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 12 Feb 2014 21:27:00 +0000 Subject: [PATCH 010/249] Rename PFutureVoid to PFutureBase. --- lib/pure/asyncio2.nim | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index cdb4a6f49..606e4b349 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -23,14 +23,13 @@ import sockets2, net # -- Futures type - PFutureVoid* = ref object of PObject - cbVoid: proc () {.closure.} + PFutureBase* = ref object of PObject + cb: proc () {.closure.} finished: bool - PFuture*[T] = ref object of PFutureVoid + PFuture*[T] = ref object of PFutureBase value: T error: ref EBase - cb: proc (future: PFuture[T]) {.closure.} proc newFuture*[T](): PFuture[T] = ## Creates a new future. @@ -44,9 +43,7 @@ proc complete*[T](future: PFuture[T], val: T) = future.value = val future.finished = true if future.cb != nil: - future.cb(future) - if future.cbVoid != nil: - future.cbVoid() + future.cb() proc fail*[T](future: PFuture[T], error: ref EBase) = ## Completes ``future`` with ``error``. @@ -54,27 +51,25 @@ proc fail*[T](future: PFuture[T], error: ref EBase) = future.finished = true future.error = error if future.cb != nil: - future.cb(future) + future.cb() + +proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) = + ## Sets the callback proc to be called when the future completes. + ## + ## If future has already completed then ``cb`` will be called immediately. + ## + ## **Note**: You most likely want the other ``callback`` setter which + ## passes ``future`` as a param to the callback. + future.cb = cb + if future.finished: + future.cb() proc `callback=`*[T](future: PFuture[T], cb: proc (future: PFuture[T]) {.closure.}) = ## Sets the callback proc to be called when the future completes. ## ## If future has already completed then ``cb`` will be called immediately. - future.cb = cb - if future.finished: - future.cb(future) - -proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) = - ## Sets the **void** callback proc to be called when the future completes. - ## - ## If future has already completed then ``cb`` will be called immediately. - ## - ## **Note**: This is used for the ``await`` functionality, you most likely - ## want to use ``callback``. - future.cbVoid = cb - if future.finished: - future.cbVoid() + future.callback = proc () = cb(future) proc read*[T](future: PFuture[T]): T = ## Retrieves the value of ``future``. Future must be finished otherwise From 6a65d1c5154ff28a31caff56d023f428d24f59ee Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 12 Feb 2014 21:28:17 +0000 Subject: [PATCH 011/249] newProc can now be used to construct iterator defs. --- lib/core/macros.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 585ccf869..d14822974 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -516,7 +516,7 @@ proc last*(node: PNimrodNode): PNimrodNode {.compileTime.} = node[node.high] const - RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda} + RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef} AtomicNodes* = {nnkNone..nnkNilLit} CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit, nnkHiddenCallConv} From 7a3106d6597e755b94d8623dc173f0275f81f69b Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Wed, 12 Feb 2014 16:15:34 -0600 Subject: [PATCH 012/249] moved tsets test to collections/ --- tests/{stdlib => collections}/tsets.nim | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{stdlib => collections}/tsets.nim (100%) diff --git a/tests/stdlib/tsets.nim b/tests/collections/tsets.nim similarity index 100% rename from tests/stdlib/tsets.nim rename to tests/collections/tsets.nim From 4dae4fafe931c23d0f95668e4944962c84a40dfa Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 13 Feb 2014 21:21:17 +0100 Subject: [PATCH 013/249] edited compiler's config --- compiler/nimrod.nimrod.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/nimrod.nimrod.cfg b/compiler/nimrod.nimrod.cfg index 657c47b28..cc27d9f36 100644 --- a/compiler/nimrod.nimrod.cfg +++ b/compiler/nimrod.nimrod.cfg @@ -17,4 +17,6 @@ import:testability cincludes: "$lib/wrappers/libffi/common" @end +define:useStdoutAsStdmsg + cs:partial From f637c36a7a145ad2f90e82f79331c97666bcecd0 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 13 Feb 2014 21:29:50 +0100 Subject: [PATCH 014/249] the tester now produces a diff in the JSON --- tests/testament/htmlgen.nim | 44 +++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim index eb674a171..74d8811b8 100644 --- a/tests/testament/htmlgen.nim +++ b/tests/testament/htmlgen.nim @@ -9,7 +9,7 @@ ## HTML generator for the tester. -import db_sqlite, cgi, backend, strutils +import db_sqlite, cgi, backend, strutils, json const TableHeader = """ @@ -114,8 +114,6 @@ proc getCommit(db: TDbConn, c: int): string = for thisCommit in db.rows(sql"select id from [Commit] order by id desc"): if commit == 0: result = thisCommit[0] inc commit - if result.isNil: - quit "cannot determine commit " & $c proc generateHtml*(filename: string, commit: int) = const selRow = """select name, category, target, action, @@ -161,20 +159,48 @@ proc generateHtml*(filename: string, commit: int) = close(outfile) proc generateJson*(filename: string, commit: int) = - const selRow = """select count(*), + const + selRow = """select count(*), sum(result = 'reSuccess'), sum(result = 'reIgnored') - from TestResult - where [commit] = ? and machine = ? - order by category""" + from TestResult + where [commit] = ? and machine = ? + order by category""" + selDiff = """select A.category || '/' || A.target || '/' || A.name, + A.result, + B.result + from TestResult A + inner join TestResult B + on A.name = B.name and A.category = B.category + where A.[commit] = ? and B.[commit] = ? and A.machine = ? + and A.result != B.result""" var db = open(connection="testament.db", user="testament", password="", database="testament") let lastCommit = db.getCommit(commit) + if lastCommit.isNil: + quit "cannot determine commit " & $commit + + let previousCommit = db.getCommit(commit-1) var outfile = open(filename, fmWrite) - let data = db.getRow(sql(selRow), lastCommit, $backend.getMachine(db)) + let machine = $backend.getMachine(db) + let data = db.getRow(sql(selRow), lastCommit, machine) - outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#}""" % data) + outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#""" % data) + + if not previousCommit.isNil: + let diff = newJArray() + + for row in db.rows(sql(selDiff), previousCommit, lastCommit, machine): + var obj = newJObject() + obj["name"] = %row[0] + obj["old"] = %row[1] + obj["new"] = %row[2] + diff.add obj + outfile.writeln(""", "diff": """) + outfile.writeln(diff.pretty) + + outfile.writeln "}" close(db) close(outfile) From 5cc821395035b0a40127f7ce35096b1b280d130b Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 00:16:52 +0100 Subject: [PATCH 015/249] koch install should work now as documented --- koch.nim | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/koch.nim b/koch.nim index 4d2b3bfb7..dcd44870f 100644 --- a/koch.nim +++ b/koch.nim @@ -58,6 +58,15 @@ Boot options: proc exe(f: string): string = return addFileExt(f, ExeExt) +proc findNim(): string = + var nimrod = "nimrod".exe + result = "bin" / nimrod + if existsFile(result): return + for dir in split(getEnv("PATH"), PathSep): + if existsFile(dir / nimrod): return dir / nimrod + # assume there is a symlink to the exe or something: + return nimrod + proc exec(cmd: string) = echo(cmd) if execShellCmd(cmd) != 0: quit("FAILURE") @@ -70,15 +79,15 @@ const compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" proc csource(args: string) = - exec("nimrod cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" % - [args, NimrodVersion, compileNimInst]) + exec("$4 cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" % + [args, NimrodVersion, compileNimInst, findNim()]) proc zip(args: string) = - exec("nimrod cc -r $2 --var:version=$1 zip compiler/nimrod.ini" % - [NimrodVersion, compileNimInst]) + exec("$3 cc -r $2 --var:version=$1 zip compiler/nimrod.ini" % + [NimrodVersion, compileNimInst, findNim()]) proc buildTool(toolname, args: string) = - exec("nimrod cc $# $#" % [args, toolname]) + exec("$# cc $# $#" % [findNim(), args, toolname]) copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe) proc inno(args: string) = @@ -90,13 +99,13 @@ proc inno(args: string) = NimrodVersion) proc install(args: string) = - exec("nimrod cc -r $# --var:version=$# scripts compiler/nimrod.ini" % - [compileNimInst, NimrodVersion]) + exec("$# cc -r $# --var:version=$# scripts compiler/nimrod.ini" % + [findNim(), compileNimInst, NimrodVersion]) exec("sh ./install.sh $#" % args) proc web(args: string) = - exec(("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" & - " --path:$#") % [NimrodVersion, getCurrentDir()]) + exec(("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" & + " --path:$#") % [findNim(), NimrodVersion, getCurrentDir()]) # -------------- boot --------------------------------------------------------- From a5efe849212162f1b52e4e5257c5cce14938ecbb Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 01:53:47 +0100 Subject: [PATCH 016/249] nil->discard --- tools/nimweb.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 56d6bcadb..ff343bd2a 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -130,7 +130,7 @@ proc walkDirRecursively(s: var seq[string], root, ext: string) = if cmpIgnoreCase(ext, splitFile(f).ext) == 0: add(s, f) of pcDir: walkDirRecursively(s, f, ext) - of pcLinkToDir: nil + of pcLinkToDir: discard proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) = for p in items(patterns): @@ -153,7 +153,7 @@ proc parseIniFile(c: var TConfigData) = of cfgSectionStart: section = normalize(k.section) case section - of "project", "links", "tabs", "ticker", "documentation", "var": nil + of "project", "links", "tabs", "ticker", "documentation", "var": discard else: echo("[Warning] Skipping unknown section: " & section) of cfgKeyValuePair: @@ -168,7 +168,7 @@ proc parseIniFile(c: var TConfigData) = of "logo": c.logo = v of "authors": c.authors = v else: quit(errorStr(p, "unknown variable: " & k.key)) - of "var": nil + of "var": discard of "links": let valID = v.split(';') add(c.links, (k.key.replace('_', ' '), valID[1], valID[0])) @@ -186,7 +186,7 @@ proc parseIniFile(c: var TConfigData) = let vSplit = v.split('-') doAssert vSplit.len == 2 c.quotations[k.key.normalize] = (vSplit[0], vSplit[1]) - else: nil + else: discard of cfgOption: quit(errorStr(p, "syntax error")) of cfgError: quit(errorStr(p, k.msg)) @@ -211,9 +211,9 @@ proc buildDocSamples(c: var TConfigData, destPath: string) = ## it didn't make much sense to integrate into the existing generic ## documentation builders. const src = "doc"/"docgen_sample.nim" - Exec("nimrod doc $# -o:$# $#" % + exec("nimrod doc $# -o:$# $#" % [c.nimrodArgs, destPath / "docgen_sample.html", src]) - Exec("nimrod doc2 $# -o:$# $#" % + exec("nimrod doc2 $# -o:$# $#" % [c.nimrodArgs, destPath / "docgen_sample2.html", src]) proc buildDoc(c: var TConfigData, destPath: string) = From 896c96134f47945c497a1a9f38d3d28ca43f119a Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 02:02:01 +0100 Subject: [PATCH 017/249] fixes #892 --- compiler/lookups.nim | 1 + examples/htmltitle.nim | 8 ++++---- koch.nim | 3 ++- tests/template/ttempl5.nim | 13 +++++++++++++ 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index c31eb3121..93a7b7c72 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -32,6 +32,7 @@ proc considerAcc*(n: PNode): PIdent = of nkSym: id.add(x.sym.name.s) else: globalError(n.info, errIdentifierExpected, renderTree(n)) result = getIdent(id) + of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name else: globalError(n.info, errIdentifierExpected, renderTree(n)) diff --git a/examples/htmltitle.nim b/examples/htmltitle.nim index f3c672382..a3280bb13 100644 --- a/examples/htmltitle.nim +++ b/examples/htmltitle.nim @@ -4,10 +4,10 @@ import os, streams, parsexml, strutils -if paramCount() < 1: +if paramCount() < 1: quit("Usage: htmltitle filename[.html]") -var filename = addFileExt(ParamStr(1), "html") +var filename = addFileExt(paramStr(1), "html") var s = newFileStream(filename, fmRead) if s == nil: quit("cannot open the file " & filename) var x: TXmlParser @@ -23,13 +23,13 @@ while true: title.add(x.charData) x.next() if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0: - Echo("Title: " & title) + echo("Title: " & title) quit(0) # Success! else: echo(x.errorMsgExpected("/title")) of xmlEof: break # end of file reached - else: nil # ignore other events + else: discard # ignore other events x.close() quit("Could not determine title!") diff --git a/koch.nim b/koch.nim index dcd44870f..dcb66ae3e 100644 --- a/koch.nim +++ b/koch.nim @@ -105,7 +105,8 @@ proc install(args: string) = proc web(args: string) = exec(("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" & - " --path:$#") % [findNim(), NimrodVersion, getCurrentDir()]) + " --path:$#") % [findNim(), NimrodVersion, + getCurrentDir().quoteIfContainsWhite]) # -------------- boot --------------------------------------------------------- diff --git a/tests/template/ttempl5.nim b/tests/template/ttempl5.nim index 85692e97b..1f2378780 100644 --- a/tests/template/ttempl5.nim +++ b/tests/template/ttempl5.nim @@ -3,3 +3,16 @@ import mtempl5 echo templ() +#bug #892 + +proc parse_to_close(value: string, index: int, open='(', close=')'): int = + discard + +# Call parse_to_close +template get_next_ident: stmt = + discard "{something}".parse_to_close(0, open = '{', close = '}') + +get_next_ident() + + +#identifier expected, but found '(open|open|open)' From f2216b8e3256312f827e645c4488828145b54328 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 02:12:07 +0100 Subject: [PATCH 018/249] fixes #833 --- compiler/semtypinst.nim | 3 +++ tests/ccgbugs/tcgbug.nim | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index a07d91241..73b618f46 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -153,6 +153,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = discard of nkSym: result.sym = replaceTypeVarsS(cl, n.sym) + if result.sym.typ.kind == tyEmpty: + # don't add the 'void' field + result = newNode(nkRecList, n.info) of nkRecWhen: var branch: PNode = nil # the branch to take for i in countup(0, sonsLen(n) - 1): diff --git a/tests/ccgbugs/tcgbug.nim b/tests/ccgbugs/tcgbug.nim index 417b909ae..c0037935a 100644 --- a/tests/ccgbugs/tcgbug.nim +++ b/tests/ccgbugs/tcgbug.nim @@ -21,3 +21,13 @@ q(a) echo "success" + +# bug #833 + +type + PFuture*[T] = ref object + value*: T + finished*: bool + cb: proc (future: PFuture[T]) {.closure.} + +var k = PFuture[void]() From e210b049e99b3a42933f099f7fe0f834bcba1cf3 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 02:29:19 +0100 Subject: [PATCH 019/249] fixes #712 --- compiler/sem.nim | 3 ++- tests/exprs/tifexpr_typeinference.nim | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/exprs/tifexpr_typeinference.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index 845d4ae71..140687721 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -120,7 +120,8 @@ proc commonType*(x, y: PType): PType = if a.kind == tyObject and b.kind == tyObject: result = commonSuperclass(a, b) # this will trigger an error later: - if result.isNil: return x + if result.isNil or result == a: return x + if result == b: return y if k != tyNone: let r = result result = newType(k, r.owner) diff --git a/tests/exprs/tifexpr_typeinference.nim b/tests/exprs/tifexpr_typeinference.nim new file mode 100644 index 000000000..3ae95c571 --- /dev/null +++ b/tests/exprs/tifexpr_typeinference.nim @@ -0,0 +1,20 @@ +#bug #712 + +import tables + +proc test(): TTable[string, string] = + discard + +proc test2(): TTable[string, string] = + discard + +var x = 5 +let blah = + case x + of 5: + test2() + of 2: + test() + else: test() + +echo blah.len From 09c8a5134826ca53e145313c27cb4084528179a5 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 15:46:30 +0100 Subject: [PATCH 020/249] bugfix: 'system' on windows is sane --- lib/pure/os.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index bb70f28b6..75adb1174 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1037,7 +1037,10 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", ## the process has finished. To execute a program without having a ## shell involved, use the `execProcess` proc of the `osproc` ## module. - result = c_system(command) shr 8 + when defined(windows): + result = c_system(command) + else: + result = c_system(command) shr 8 # Environment handling cannot be put into RTL, because the ``envPairs`` # iterator depends on ``environment``. From 49fa421ba77058f683bc476528a3a8f05d20e7e9 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 15:49:44 +0100 Subject: [PATCH 021/249] fixes #914 --- compiler/ccgexprs.nim | 2 +- compiler/ccgtypes.nim | 2 +- compiler/cgendata.nim | 3 ++- tests/ccgbugs/tcgbug.nim | 3 +++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 01f23850b..27f7f4ba5 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -593,7 +593,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = proc genDeref(p: BProc, e: PNode, d: var TLoc) = var a: TLoc - if mapType(e.sons[0].typ) == ctArray: + if mapType(e.sons[0].typ) in {ctArray, ctPtrToArray}: # XXX the amount of hacks for C's arrays is incredible, maybe we should # simply wrap them in a struct? --> Losing auto vectorization then? expr(p, e.sons[0], d) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c92c15fa9..673e888fe 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -185,7 +185,7 @@ proc mapType(typ: PType): TCTypeKind = of tyPtr, tyVar, tyRef: var base = skipTypes(typ.sons[0], typedescInst) case base.kind - of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray + of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctPtrToArray else: result = ctPtr of tyPointer: result = ctPtr of tySequence: result = ctNimSeq diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 9cd2c0d87..0e1148343 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -41,7 +41,8 @@ type ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64, ctFloat128, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64, - ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString + ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, + ctCString TCFileSections* = array[TCFileSection, PRope] # represents a generated C file TCProcSection* = enum # the sections a generated C proc consists of cpsLocals, # section of local variables for C proc diff --git a/tests/ccgbugs/tcgbug.nim b/tests/ccgbugs/tcgbug.nim index c0037935a..535424a27 100644 --- a/tests/ccgbugs/tcgbug.nim +++ b/tests/ccgbugs/tcgbug.nim @@ -19,6 +19,9 @@ var new(a) q(a) +# bug #914 +var x = newWideCString("Hello") + echo "success" From cd2bd7fa7b1048956c72ad7665b70b2eabecd549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Fri, 14 Feb 2014 15:08:02 +0100 Subject: [PATCH 022/249] osproc: use clone with CLONE_VM on Linux for faster process spawning --- lib/posix/linux.nim | 25 ++++++ lib/pure/osproc.nim | 193 +++++++++++++++++++++++++++----------------- 2 files changed, 146 insertions(+), 72 deletions(-) create mode 100644 lib/posix/linux.nim diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim new file mode 100644 index 000000000..1ed1af3b6 --- /dev/null +++ b/lib/posix/linux.nim @@ -0,0 +1,25 @@ +import posix + +const + CSIGNAL* = 0x000000FF + CLONE_VM* = 0x00000100 + CLONE_FS* = 0x00000200 + CLONE_FILES* = 0x00000400 + CLONE_SIGHAND* = 0x00000800 + CLONE_PTRACE* = 0x00002000 + CLONE_VFORK* = 0x00004000 + CLONE_PARENT* = 0x00008000 + CLONE_THREAD* = 0x00010000 + CLONE_NEWNS* = 0x00020000 + CLONE_SYSVSEM* = 0x00040000 + CLONE_SETTLS* = 0x00080000 + CLONE_PARENT_SETTID* = 0x00100000 + CLONE_CHILD_CLEARTID* = 0x00200000 + CLONE_DETACHED* = 0x00400000 + CLONE_UNTRACED* = 0x00800000 + CLONE_CHILD_SETTID* = 0x01000000 + CLONE_STOPPED* = 0x02000000 + +# fn should be of type proc (a2: pointer): void {.cdecl.} +proc clone*(fn: pointer; child_stack: pointer; flags: cint; + arg: pointer; ptid: ptr TPid; tls: pointer; ctid: ptr TPid): cint {.importc, header: "".} diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index c0c7f46d7..40877f638 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -20,6 +20,11 @@ when defined(windows): else: import posix +when defined(linux): + import linux + when not defined(useFork): + const useClone = true + type TProcess = object of TObject when defined(windows): @@ -590,6 +595,19 @@ elif not defined(useNimRtl): copyMem(result[i], addr(x[0]), x.len+1) inc(i) + type TStartProcessData = object + sysCommand: cstring + sysArgs: cstringArray + sysEnv: cstringArray + workingDir: cstring + pStdin, pStdout, pStderr: array[0..1, cint] + optionPoUsePath: bool + optionPoParentStreams: bool + optionPoStdErrToStdOut: bool + + proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} + proc startProcessAfterFork(data: ptr TStartProcessData) {.tags: [FExecIO, FReadEnv], cdecl.} + proc startProcess(command: string, workingDir: string = "", args: openArray[string] = [], @@ -616,88 +634,48 @@ elif not defined(useNimRtl): for arg in args.items: sysArgsRaw.add arg + var pid: TPid + var sysArgs = allocCStringArray(sysArgsRaw) finally: deallocCStringArray(sysArgs) - var pid: TPid - when defined(posix_spawn) and not defined(useFork): - var attr: Tposix_spawnattr - var fops: Tposix_spawn_file_actions - - template chck(e: expr) = - if e != 0'i32: osError(osLastError()) - - chck posix_spawn_file_actions_init(fops) - chck posix_spawnattr_init(attr) - - var mask: Tsigset - chck sigemptyset(mask) - chck posix_spawnattr_setsigmask(attr, mask) - chck posix_spawnattr_setpgroup(attr, 0'i32) - - chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or - POSIX_SPAWN_SETSIGMASK or - POSIX_SPAWN_SETPGROUP) - - if poParentStreams notin options: - chck posix_spawn_file_actions_addclose(fops, pStdin[writeIdx]) - chck posix_spawn_file_actions_adddup2(fops, pStdin[readIdx], readIdx) - chck posix_spawn_file_actions_addclose(fops, pStdout[readIdx]) - chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], writeIdx) - chck posix_spawn_file_actions_addclose(fops, pStderr[readIdx]) - if poStdErrToStdOut in options: - chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2) - else: - chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2) - - var sysEnv = if env == nil: envToCStringArray() else: envToCStringArray(env) - var res: cint - # FIXME: chdir is global to process - if workingDir.len > 0: os.setCurrentDir(workingDir) - if poUsePath in options: - res = posix_spawnp(pid, sysCommand, fops, attr, sysArgs, sysEnv) + var sysEnv = if env == nil: + envToCStringArray() else: - res = posix_spawn(pid, sysCommand, fops, attr, sysArgs, sysEnv) - deallocCStringArray(sysEnv) - discard posix_spawn_file_actions_destroy(fops) - discard posix_spawnattr_destroy(attr) - chck res + envToCStringArray(env) + finally: deallocCStringArray(sysEnv) + + var data: TStartProcessData + data.sysCommand = sysCommand + data.sysArgs = sysArgs + data.sysEnv = sysEnv + data.pStdin = pStdin + data.pStdout = pStdout + data.pStderr = pStderr + data.optionPoParentStreams = poParentStreams in options + data.optionPoUsePath = poUsePath in options + data.optionPoStdErrToStdOut = poStdErrToStdOut in options + data.workingDir = workingDir + + when defined(useClone): + const stackSize = 8096 + let stackEnd = cast[clong](alloc(stackSize)) + let stack = cast[pointer](stackEnd + stackSize) + let fn: pointer = startProcessAfterFork + pid = clone(fn, stack, + cint(CLONE_VM or CLONE_VFORK or SIGCHLD), + pointer(addr data), nil, nil, nil) + + if pid < 0: osError(osLastError()) + elif defined(posix_spawn) and not defined(useFork): + pid = startProcessAuxSpawn(data) else: pid = fork() if pid < 0: osError(osLastError()) if pid == 0: - ## child process: + startProcessAfterFork(addr(data)) - if poParentStreams notin options: - discard close(p_stdin[writeIdx]) - if dup2(p_stdin[readIdx], readIdx) < 0: osError(osLastError()) - discard close(p_stdout[readIdx]) - if dup2(p_stdout[writeIdx], writeIdx) < 0: osError(osLastError()) - discard close(p_stderr[readIdx]) - if poStdErrToStdOut in options: - if dup2(p_stdout[writeIdx], 2) < 0: osError(osLastError()) - else: - if dup2(p_stderr[writeIdx], 2) < 0: osError(osLastError()) - - # Create a new process group - if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno)) - - if workingDir.len > 0: os.setCurrentDir(workingDir) - - if env == nil: - if poUsePath in options: - discard execvp(sysCommand, sysArgs) - else: - discard execv(sysCommand, sysArgs) - else: - var cEnv = envToCStringArray(env) - if poUsePath in options: - discard execvpe(sysCommand, sysArgs, cEnv) - else: - discard execve(sysCommand, sysArgs, cEnv) - # too risky to raise an exception here: - quit("execve call failed: " & $strerror(errno)) # Parent process. Copy process information. if poEchoCmd in options: echo(command, " ", join(args, " ")) @@ -723,6 +701,77 @@ elif not defined(useNimRtl): discard close(pStdin[readIdx]) discard close(pStdout[writeIdx]) + proc startProcessAuxSpawn(data: TStartProcessData): TPid = + var attr: Tposix_spawnattr + var fops: Tposix_spawn_file_actions + + template chck(e: expr) = + if e != 0'i32: osError(osLastError()) + + chck posix_spawn_file_actions_init(fops) + chck posix_spawnattr_init(attr) + + var mask: Tsigset + chck sigemptyset(mask) + chck posix_spawnattr_setsigmask(attr, mask) + chck posix_spawnattr_setpgroup(attr, 0'i32) + + chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or + POSIX_SPAWN_SETSIGMASK or + POSIX_SPAWN_SETPGROUP) + + if not data.optionPoParentStreams: + chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx]) + chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx) + chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx]) + chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx) + chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx]) + if data.optionPoStdErrToStdOut: + chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2) + else: + chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2) + + var res: cint + # FIXME: chdir is global to process + if data.workingDir.len > 0: + setCurrentDir($data.workingDir) + var pid: TPid + + if data.optionPoUsePath: + res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) + else: + res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) + + discard posix_spawn_file_actions_destroy(fops) + discard posix_spawnattr_destroy(attr) + chck res + return pid + + proc startProcessAfterFork(data: ptr TStartProcessData) = + # Warning: no GC here! + if not data.optionPoParentStreams: + discard close(data.pStdin[writeIdx]) + if dup2(data.pStdin[readIdx], readIdx) < 0: osError(osLastError()) + discard close(data.pStdout[readIdx]) + if dup2(data.pStdout[writeIdx], writeIdx) < 0: osError(osLastError()) + discard close(data.pStderr[readIdx]) + if data.optionPoStdErrToStdOut: + if dup2(data.pStdout[writeIdx], 2) < 0: osError(osLastError()) + else: + if dup2(data.pStderr[writeIdx], 2) < 0: osError(osLastError()) + + if data.workingDir.len > 0: + if chdir(data.workingDir) < 0: + quit("chdir failed") + + if data.optionPoUsePath: + discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) + else: + discard execve(data.sysCommand, data.sysArgs, data.sysEnv) + + # too risky to raise an exception here: + quit("execve call failed: " & $strerror(errno)) + proc close(p: PProcess) = if p.inStream != nil: close(p.inStream) if p.outStream != nil: close(p.outStream) From f95a8df243354767dc4fc7f907d5a5b26566b58d Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 22:19:40 +0100 Subject: [PATCH 023/249] fixes #811 --- lib/pure/os.nim | 6 +++--- lib/pure/osproc.nim | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 75adb1174..89bb92f9a 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1037,10 +1037,10 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", ## the process has finished. To execute a program without having a ## shell involved, use the `execProcess` proc of the `osproc` ## module. - when defined(windows): - result = c_system(command) - else: + when defined(linux): result = c_system(command) shr 8 + else: + result = c_system(command) # Environment handling cannot be put into RTL, because the ``envPairs`` # iterator depends on ``environment``. diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 6df85bbc6..e0689c4a6 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -791,7 +791,10 @@ elif not defined(useNimRtl): proc csystem(cmd: cstring): cint {.nodecl, importc: "system".} proc execCmd(command: string): int = - result = csystem(command) shr 8 + when defined(linux) + result = csystem(command) shr 8 + else: + result = csystem(command) proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) = FD_ZERO(fd) From cf544f6a850e2d3cf4ffe9640a57ada7aaa502dc Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 22:37:36 +0100 Subject: [PATCH 024/249] fixes #811 --- lib/pure/osproc.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index e0689c4a6..9a43c0a7d 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -791,7 +791,7 @@ elif not defined(useNimRtl): proc csystem(cmd: cstring): cint {.nodecl, importc: "system".} proc execCmd(command: string): int = - when defined(linux) + when defined(linux): result = csystem(command) shr 8 else: result = csystem(command) From 2b9311e9f185c5cb95a7ce70035efce63b4e6d89 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 14 Feb 2014 23:47:06 +0100 Subject: [PATCH 025/249] fixes #584 --- compiler/guards.nim | 42 ++++++++++++++++++++++++------------- compiler/sempass2.nim | 33 ++++++++++++++++++----------- compiler/transf.nim | 1 + tests/effects/teffects6.nim | 2 +- tests/notnil/tnotnil3.nim | 35 +++++++++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 27 deletions(-) create mode 100644 tests/notnil/tnotnil3.nim diff --git a/compiler/guards.nim b/compiler/guards.nim index 607bb074a..fe868054f 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -9,7 +9,7 @@ ## This module implements the 'implies' relation for guards. -import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer +import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents const someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, @@ -69,9 +69,23 @@ proc isLetLocation(m: PNode, isApprox: bool): bool = proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true) -proc swapArgs(fact: PNode, newOp: string, m: TMagic): PNode = +proc getMagicOp(name: string, m: TMagic): PSym = + result = newSym(skProc, getIdent(name), nil, unknownLineInfo()) + result.magic = m + +let + opLe = getMagicOp("<=", mLeI) + opLt = getMagicOp("<", mLtI) + opAnd = getMagicOp("and", mAnd) + opOr = getMagicOp("or", mOr) + opNot = getMagicOp("not", mNot) + opIsNil = getMagicOp("isnil", mIsNil) + opContains = getMagicOp("contains", mInSet) + opEq = getMagicOp("==", mEqI) + +proc swapArgs(fact: PNode, newOp: PSym): PNode = result = newNodeI(nkCall, fact.info, 3) - result.sons[0] = newSymNode(getSysMagic(newOp, m)) + result.sons[0] = newSymNode(newOp) result.sons[1] = fact.sons[2] result.sons[2] = fact.sons[1] @@ -82,9 +96,9 @@ proc neg(n: PNode): PNode = result = n.sons[1] of someLt: # not (a < b) == a >= b == b <= a - result = swapArgs(n, "<=", mLeI) + result = swapArgs(n, opLe) of someLe: - result = swapArgs(n, "<", mLtI) + result = swapArgs(n, opLt) of mInSet: if n.sons[1].kind != nkCurly: return nil let t = n.sons[2].typ.skipTypes(abstractInst) @@ -110,7 +124,7 @@ proc neg(n: PNode): PNode = b = n.sons[2].neg if a != nil and b != nil: result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(getSysMagic("and", mAnd)) + result.sons[0] = newSymNode(opAnd) result.sons[1] = a result.sons[2] = b elif a != nil: @@ -120,12 +134,12 @@ proc neg(n: PNode): PNode = else: # leave not (a == 4) as it is result = newNodeI(nkCall, n.info, 2) - result.sons[0] = newSymNode(getSysMagic("not", mNot)) + result.sons[0] = newSymNode(opNot) result.sons[1] = n proc buildIsNil(arg: PNode): PNode = result = newNodeI(nkCall, arg.info, 2) - result.sons[0] = newSymNode(getSysMagic("isNil", mIsNil)) + result.sons[0] = newSymNode(opIsNil) result.sons[1] = arg proc usefulFact(n: PNode): PNode = @@ -154,7 +168,7 @@ proc usefulFact(n: PNode): PNode = b = usefulFact(n.sons[2]) if a != nil and b != nil: result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(getSysMagic("and", mAnd)) + result.sons[0] = newSymNode(opAnd) result.sons[1] = a result.sons[2] = b elif a != nil: @@ -177,7 +191,7 @@ proc usefulFact(n: PNode): PNode = b = usefulFact(n.sons[2]).neg if a != nil and b != nil: result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(getSysMagic("and", mAnd)) + result.sons[0] = newSymNode(opAnd) result.sons[1] = a result.sons[2] = b result = result.neg @@ -520,7 +534,7 @@ proc buildOf(it, loc: PNode): PNode = s.typ = settype(loc) for i in 0..it.len-2: s.sons[i] = it.sons[i] result = newNodeI(nkCall, it.info, 3) - result.sons[0] = newSymNode(getSysMagic("contains", mInSet)) + result.sons[0] = newSymNode(opContains) result.sons[1] = s result.sons[2] = loc @@ -532,20 +546,20 @@ proc buildElse(n: PNode): PNode = for j in 0..branch.len-2: s.add(branch.sons[j]) result = newNodeI(nkCall, n.info, 3) - result.sons[0] = newSymNode(getSysMagic("contains", mInSet)) + result.sons[0] = newSymNode(opContains) result.sons[1] = s result.sons[2] = n.sons[0] proc addDiscriminantFact*(m: var TModel, n: PNode) = var fact = newNodeI(nkCall, n.info, 3) - fact.sons[0] = newSymNode(getSysMagic("==", mEqI)) + fact.sons[0] = newSymNode(opEq) fact.sons[1] = n.sons[0] fact.sons[2] = n.sons[1] m.add fact proc addAsgnFact*(m: var TModel, key, value: PNode) = var fact = newNodeI(nkCall, key.info, 3) - fact.sons[0] = newSymNode(getSysMagic("==", mEqI)) + fact.sons[0] = newSymNode(opEq) fact.sons[1] = key fact.sons[2] = value m.add fact diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index fb266ae3a..fedf19c79 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -84,10 +84,10 @@ proc initVar(a: PEffects, n: PNode) = proc initVarViaNew(a: PEffects, n: PNode) = if n.kind != nkSym: return let s = n.sym - if {tfNeedsInit, tfNotNil} * s.typ.flags == {tfNotNil}: + if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}: # 'x' is not nil, but that doesn't mean it's not nil children # are initialized: - initVarViaNew(a, n) + initVar(a, n) proc useVar(a: PEffects, n: PNode) = let s = n.sym @@ -466,8 +466,7 @@ proc track(tracked: PEffects, n: PNode) = mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) for i in 1 .. Date: Sat, 15 Feb 2014 02:27:09 +0100 Subject: [PATCH 026/249] actors.nim compiles again --- compiler/sempass2.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index fedf19c79..a00325277 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -476,7 +476,6 @@ proc track(tracked: PEffects, n: PNode) = if warnProveField in gNotes: checkFieldAccess(tracked.guards, n) of nkTryStmt: trackTryStmt(tracked, n) of nkPragma: trackPragmaStmt(tracked, n) - of nkMacroDef, nkTemplateDef: discard of nkAsgn, nkFastAsgn: track(tracked, n.sons[1]) initVar(tracked, n.sons[0]) @@ -526,7 +525,9 @@ proc track(tracked: PEffects, n: PNode) = if sfDiscriminant in x.sons[0].sym.flags: addDiscriminantFact(tracked.guards, x) setLen(tracked.guards, oldFacts) - of nkTypeSection: discard + of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, + nkMacroDef, nkTemplateDef: + discard else: for i in 0 .. Date: Sat, 15 Feb 2014 02:27:49 +0100 Subject: [PATCH 027/249] made 'koch web' work when current dir has a space in it --- compiler/docgen.nim | 10 ++++++++-- compiler/types.nim | 2 +- koch.nim | 5 ++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 343f415b3..c05b55184 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -42,17 +42,23 @@ proc compilerMsgHandler(filename: string, line, col: int, of mwUnsupportedLanguage: k = warnLanguageXNotSupported globalError(newLineInfo(filename, line, col), k, arg) +proc docgenFindFile(s: string): string {.procvar.} = + result = options.findFile(s) + if result.len == 0: + result = getCurrentDir() / s + if not existsFile(result): result = "" + proc parseRst(text, filename: string, line, column: int, hasToc: var bool, rstOptions: TRstParseOptions): PRstNode = result = rstParse(text, filename, line, column, hasToc, rstOptions, - options.findFile, compilerMsgHandler) + docgenFindFile, compilerMsgHandler) proc newDocumentor*(filename: string, config: PStringTable): PDoc = new(result) initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex), options.gConfigVars, filename, {roSupportRawDirective}, - options.findFile, compilerMsgHandler) + docgenFindFile, compilerMsgHandler) result.id = 100 proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) = diff --git a/compiler/types.nim b/compiler/types.nim index db75cd3c0..bb9f18dc3 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -857,7 +857,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = if a.kind != b.kind: return false of dcEqOrDistinctOf: while a.kind == tyDistinct: a = a.sons[0] - if a.kind != b.kind: return false + if a.kind != b.kind: return false case a.kind of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, tyInt..tyBigNum, tyStmt, tyExpr: diff --git a/koch.nim b/koch.nim index dcb66ae3e..ce01d36a5 100644 --- a/koch.nim +++ b/koch.nim @@ -104,9 +104,8 @@ proc install(args: string) = exec("sh ./install.sh $#" % args) proc web(args: string) = - exec(("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" & - " --path:$#") % [findNim(), NimrodVersion, - getCurrentDir().quoteIfContainsWhite]) + exec("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" % + [findNim(), NimrodVersion]) # -------------- boot --------------------------------------------------------- From bd9e9acdaae199361d63d29bdb368a1f73bde91c Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 15 Feb 2014 11:02:13 +0100 Subject: [PATCH 028/249] Splits token parsing case into separate proc. Refs #740. --- lib/pure/times.nim | 221 +++++++++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 106 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index de6c4e4fa..82ef0d86f 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -557,6 +557,119 @@ proc `$`*(m: TMonth): string = "November", "December"] return lookup[m] +proc format_token(info: TTimeInfo, token: string, buf: var string) = + ## Helper of the format proc to parse individual tokens. + ## + ## Pass the found token in the user input string, and the buffer where the + ## final string is being built. This has to be a var value because certain + ## formatting tokens require modifying the previous characters. + case token + of "d": + buf.add($info.monthday) + of "dd": + if info.monthday < 10: + buf.add("0") + buf.add($info.monthday) + of "ddd": + buf.add(($info.weekday)[0 .. 2]) + of "dddd": + buf.add($info.weekday) + of "h": + buf.add($(if info.hour > 12: info.hour - 12 else: info.hour)) + of "hh": + let amerHour = if info.hour > 12: info.hour - 12 else: info.hour + if amerHour < 10: + buf.add('0') + buf.add($amerHour) + of "H": + buf.add($info.hour) + of "HH": + if info.hour < 10: + buf.add('0') + buf.add($info.hour) + of "m": + buf.add($info.minute) + of "mm": + if info.minute < 10: + buf.add('0') + buf.add($info.minute) + of "M": + buf.add($(int(info.month)+1)) + of "MM": + if info.month < mOct: + buf.add('0') + buf.add($(int(info.month)+1)) + of "MMM": + buf.add(($info.month)[0..2]) + of "MMMM": + buf.add($info.month) + of "s": + buf.add($info.second) + of "ss": + if info.second < 10: + buf.add('0') + buf.add($info.second) + of "t": + if info.hour >= 12: + buf.add('P') + else: buf.add('A') + of "tt": + if info.hour >= 12: + buf.add("PM") + else: buf.add("AM") + of "y": + var fr = ($info.year).len()-1 + if fr < 0: fr = 0 + buf.add(($info.year)[fr .. ($info.year).len()-1]) + of "yy": + var fr = ($info.year).len()-2 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyy": + var fr = ($info.year).len()-3 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyyy": + var fr = ($info.year).len()-4 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear + buf.add(fyear) + of "yyyyy": + var fr = ($info.year).len()-5 + if fr < 0: fr = 0 + var fyear = ($info.year)[fr .. ($info.year).len()-1] + if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear + buf.add(fyear) + of "z": + let hrs = (info.timezone div 60) div 60 + buf.add($hrs) + of "zz": + let hrs = (info.timezone div 60) div 60 + + buf.add($hrs) + if hrs.abs < 10: + var atIndex = buf.len-(($hrs).len-(if hrs < 0: 1 else: 0)) + buf.insert("0", atIndex) + of "zzz": + let hrs = (info.timezone div 60) div 60 + + buf.add($hrs & ":00") + if hrs.abs < 10: + var atIndex = buf.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0)) + buf.insert("0", atIndex) + of "ZZZ": + buf.add(info.tzname) + of "": + discard + else: + raise newException(EInvalidValue, "Invalid format string: " & token) + + proc format*(info: TTimeInfo, f: string): string = ## This function formats `info` as specified by `f`. The following format ## specifiers are available: @@ -600,112 +713,8 @@ proc format*(info: TTimeInfo, f: string): string = while true: case f[i] of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': - case currentF - of "d": - result.add($info.monthday) - of "dd": - if info.monthday < 10: - result.add("0") - result.add($info.monthday) - of "ddd": - result.add(($info.weekday)[0 .. 2]) - of "dddd": - result.add($info.weekday) - of "h": - result.add($(if info.hour > 12: info.hour - 12 else: info.hour)) - of "hh": - let amerHour = if info.hour > 12: info.hour - 12 else: info.hour - if amerHour < 10: - result.add('0') - result.add($amerHour) - of "H": - result.add($info.hour) - of "HH": - if info.hour < 10: - result.add('0') - result.add($info.hour) - of "m": - result.add($info.minute) - of "mm": - if info.minute < 10: - result.add('0') - result.add($info.minute) - of "M": - result.add($(int(info.month)+1)) - of "MM": - if info.month < mOct: - result.add('0') - result.add($(int(info.month)+1)) - of "MMM": - result.add(($info.month)[0..2]) - of "MMMM": - result.add($info.month) - of "s": - result.add($info.second) - of "ss": - if info.second < 10: - result.add('0') - result.add($info.second) - of "t": - if info.hour >= 12: - result.add('P') - else: result.add('A') - of "tt": - if info.hour >= 12: - result.add("PM") - else: result.add("AM") - of "y": - var fr = ($info.year).len()-1 - if fr < 0: fr = 0 - result.add(($info.year)[fr .. ($info.year).len()-1]) - of "yy": - var fr = ($info.year).len()-2 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear - result.add(fyear) - of "yyy": - var fr = ($info.year).len()-3 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear - result.add(fyear) - of "yyyy": - var fr = ($info.year).len()-4 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear - result.add(fyear) - of "yyyyy": - var fr = ($info.year).len()-5 - if fr < 0: fr = 0 - var fyear = ($info.year)[fr .. ($info.year).len()-1] - if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear - result.add(fyear) - of "z": - let hrs = (info.timezone div 60) div 60 - result.add($hrs) - of "zz": - let hrs = (info.timezone div 60) div 60 - - result.add($hrs) - if hrs.abs < 10: - var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0)) - result.insert("0", atIndex) - of "zzz": - let hrs = (info.timezone div 60) div 60 - - result.add($hrs & ":00") - if hrs.abs < 10: - var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0)) - result.insert("0", atIndex) - of "ZZZ": - result.add(info.tzname) - of "": - discard - else: - raise newException(EInvalidValue, "Invalid format string: " & currentF) - + format_token(info, currentF, result) + currentF = "" if f[i] == '\0': break From 293f3564209d2fcec4618dc6ee11c6c6ed8ee53b Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 15 Feb 2014 11:20:00 +0100 Subject: [PATCH 029/249] Supports parsing format times without separators. Refs #740. --- lib/pure/times.nim | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 82ef0d86f..2fce235e2 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -704,8 +704,11 @@ proc format*(info: TTimeInfo, f: string): string = ## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST`` ## ========== ================================================================================= ================================================ ## - ## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``. - ## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,`` + ## Other strings can be inserted by putting them in ``''``. For example + ## ``hh'->'mm`` will give ``01->56``. The following characters can be + ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` + ## ``,``. However you don't need to necessarily separate format specifiers, a + ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too. result = "" var i = 0 @@ -725,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string = inc(i) else: result.add(f[i]) - else: currentF.add(f[i]) + else: + # Check if the letter being added matches previous accumulated buffer. + if currentF.len < 1 or currentF[high(currentF)] == f[i]: + currentF.add(f[i]) + else: + format_token(info, currentF, result) + dec(i) # Move position back to re-process the character separately. + currentF = "" + inc(i) {.pop.} @@ -736,11 +747,15 @@ when isMainModule: var t = getGMTime(fromSeconds(2147483647)) echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") + echo t.format("ddd ddMMMhhmmssZZZyyyy") assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038" + assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038" assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & " ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") == "19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC" + + assert t.format("yyyyMMddhhmmss") == "20380119031407" var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975 assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" & From 4c09fc110f3d269c34ccbfabb665bc34c768b63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Fri, 14 Feb 2014 15:51:06 +0100 Subject: [PATCH 030/249] osproc: make failed execv an exception (when using fork or clone) startProcessAuxFork creates a pipe, which is used by a child to pass an error code if execv fails. --- lib/posix/posix.nim | 1 + lib/pure/osproc.nim | 86 +++++++++++++++++++++++++++++++++------------ 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 41260b36f..138df1aec 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -2066,6 +2066,7 @@ proc pthread_spin_unlock*(a1: ptr Tpthread_spinlock): cint {. proc pthread_testcancel*() {.importc, header: "".} +proc exitnow*(code: int): void {.importc: "_exit", header: "".} proc access*(a1: cstring, a2: cint): cint {.importc, header: "".} proc alarm*(a1: cint): cint {.importc, header: "".} proc chdir*(a1: cstring): cint {.importc, header: "".} diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 40877f638..aa2f6f937 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -600,13 +600,16 @@ elif not defined(useNimRtl): sysArgs: cstringArray sysEnv: cstringArray workingDir: cstring - pStdin, pStdout, pStderr: array[0..1, cint] + pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint] optionPoUsePath: bool optionPoParentStreams: bool optionPoStdErrToStdOut: bool proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} - proc startProcessAfterFork(data: ptr TStartProcessData) {.tags: [FExecIO, FReadEnv], cdecl.} + + proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} + proc startProcessAfterFork(data: ptr TStartProcessData) {. + tags: [FExecIO, FReadEnv], noStackFrame, cdecl.} proc startProcess(command: string, workingDir: string = "", @@ -658,23 +661,11 @@ elif not defined(useNimRtl): data.optionPoStdErrToStdOut = poStdErrToStdOut in options data.workingDir = workingDir - when defined(useClone): - const stackSize = 8096 - let stackEnd = cast[clong](alloc(stackSize)) - let stack = cast[pointer](stackEnd + stackSize) - let fn: pointer = startProcessAfterFork - pid = clone(fn, stack, - cint(CLONE_VM or CLONE_VFORK or SIGCHLD), - pointer(addr data), nil, nil, nil) - if pid < 0: osError(osLastError()) - elif defined(posix_spawn) and not defined(useFork): + when defined(posix_spawn) and not defined(useFork) and not defined(useClone): pid = startProcessAuxSpawn(data) else: - pid = fork() - if pid < 0: osError(osLastError()) - if pid == 0: - startProcessAfterFork(addr(data)) + pid = startProcessAuxFork(data) # Parent process. Copy process information. if poEchoCmd in options: @@ -747,30 +738,79 @@ elif not defined(useNimRtl): chck res return pid + proc startProcessAuxFork(data: TStartProcessData): TPid = + if pipe(data.pErrorPipe) != 0: + osError(osLastError()) + + finally: + discard close(data.pErrorPipe[readIdx]) + + var pid: TPid + var dataCopy = data + + if defined(useClone): + const stackSize = 8096 + let stackEnd = cast[clong](alloc(stackSize)) + let stack = cast[pointer](stackEnd + stackSize) + let fn: pointer = startProcessAfterFork + pid = clone(fn, stack, + cint(CLONE_VM or CLONE_VFORK or SIGCHLD), + pointer(addr dataCopy), nil, nil, nil) + discard close(data.pErrorPipe[writeIdx]) + dealloc(stack) + else: + pid = fork() + if pid == 0: + startProcessAfterFork(addr(dataCopy)) + exitnow(1) + + discard close(data.pErrorPipe[writeIdx]) + if pid < 0: osError(osLastError()) + + var error: cint + let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error)) + if sizeRead == sizeof(error): + osError($strerror(error)) + + return pid + + proc startProcessFail(data: ptr TStartProcessData) {.noStackFrame.} = + var error: cint = errno + discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error)) + exitnow(1) + proc startProcessAfterFork(data: ptr TStartProcessData) = # Warning: no GC here! + # Or anythink that touches global structures - all called nimrod procs + # must be marked with noStackFrame. Inspect C code after making changes. if not data.optionPoParentStreams: discard close(data.pStdin[writeIdx]) - if dup2(data.pStdin[readIdx], readIdx) < 0: osError(osLastError()) + if dup2(data.pStdin[readIdx], readIdx) < 0: + startProcessFail(data) discard close(data.pStdout[readIdx]) - if dup2(data.pStdout[writeIdx], writeIdx) < 0: osError(osLastError()) + if dup2(data.pStdout[writeIdx], writeIdx) < 0: + startProcessFail(data) discard close(data.pStderr[readIdx]) if data.optionPoStdErrToStdOut: - if dup2(data.pStdout[writeIdx], 2) < 0: osError(osLastError()) + if dup2(data.pStdout[writeIdx], 2) < 0: + startProcessFail(data) else: - if dup2(data.pStderr[writeIdx], 2) < 0: osError(osLastError()) + if dup2(data.pStderr[writeIdx], 2) < 0: + startProcessFail(data) if data.workingDir.len > 0: if chdir(data.workingDir) < 0: - quit("chdir failed") + startProcessFail(data) + + discard close(data.pErrorPipe[readIdx]) + discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC) if data.optionPoUsePath: discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) else: discard execve(data.sysCommand, data.sysArgs, data.sysEnv) - # too risky to raise an exception here: - quit("execve call failed: " & $strerror(errno)) + startProcessFail(data) proc close(p: PProcess) = if p.inStream != nil: close(p.inStream) From de8d47330152d3d973c6ff52b89c9d79b42d590c Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 15 Feb 2014 14:15:18 +0000 Subject: [PATCH 031/249] Fixed problems with IOCP procs finishing immediately, added await macro. --- lib/pure/asyncio2.nim | 322 +++++++++++++++++++++++++++++++++++------- 1 file changed, 271 insertions(+), 51 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 606e4b349..1cf2292de 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -import os, oids, tables, strutils +import os, oids, tables, strutils, macros import winlean @@ -38,7 +38,7 @@ proc newFuture*[T](): PFuture[T] = proc complete*[T](future: PFuture[T], val: T) = ## Completes ``future`` with value ``val``. - assert(not future.finished) + assert(not future.finished, "Future already finished, cannot finish twice.") assert(future.error == nil) future.value = val future.finished = true @@ -47,7 +47,7 @@ proc complete*[T](future: PFuture[T], val: T) = proc fail*[T](future: PFuture[T], error: ref EBase) = ## Completes ``future`` with ``error``. - assert(not future.finished) + assert(not future.finished, "Future already finished, cannot finish twice.") future.finished = true future.error = error if future.cb != nil: @@ -140,6 +140,7 @@ when defined(windows): # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html var customOverlapped = cast[PCustomOverlapped](lpOverlapped) if res: + # This is useful for ensuring the reliability of the overlapped struct. assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1)) @@ -148,8 +149,8 @@ when defined(windows): let errCode = OSLastError() if lpOverlapped != nil: assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle - dealloc(customOverlapped) customOverlapped.data.cb(customOverlapped.data.sock, errCode) + dealloc(customOverlapped) else: if errCode.int32 == WAIT_TIMEOUT: # Timed out @@ -248,10 +249,11 @@ when defined(windows): var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - retFuture.complete(0) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint, @@ -260,7 +262,9 @@ when defined(windows): # Request to connect completed immediately. success = true retFuture.complete(0) - dealloc(ol) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. break else: lastError = OSLastError() @@ -278,7 +282,8 @@ when defined(windows): retFuture.fail(newException(EOS, osErrorMsg(lastError))) return retFuture - proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] = + proc recv*(p: PDispatcher, socket: TSocketHandle, size: int, + flags: int = 0): PFuture[string] = ## Reads ``size`` bytes from ``socket``. Returned future will complete once ## all of the requested data is read. @@ -288,31 +293,42 @@ when defined(windows): dataBuf.buf = newString(size) dataBuf.len = size - var bytesReceived, flags: DWord + var bytesReceived: DWord + var flagsio = flags.dword var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - var data = newString(size) - copyMem(addr data[0], addr dataBuf.buf[0], size) - retFuture.complete($data) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + var data = newString(size) + copyMem(addr data[0], addr dataBuf.buf[0], size) + retFuture.complete($data) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived, - addr flags, cast[POverlapped](ol), nil) + addr flagsio, cast[POverlapped](ol), nil) if ret == -1: let err = OSLastError() if err.int32 != ERROR_IO_PENDING: retFuture.fail(newException(EOS, osErrorMsg(err))) dealloc(ol) + elif ret == 0 and bytesReceived == 0: + # Disconnected + retFuture.complete("") + # TODO: "For message-oriented sockets, where a zero byte message is often + # allowable, a failure with an error code of WSAEDISCON is used to + # indicate graceful closure." + # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx else: # Request to read completed immediately. var data = newString(size) copyMem(addr data[0], addr dataBuf.buf[0], size) retFuture.complete($data) - dealloc(ol) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. return retFuture proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = @@ -328,10 +344,11 @@ when defined(windows): var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - retFuture.complete(0) - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived, @@ -343,7 +360,9 @@ when defined(windows): dealloc(ol) else: retFuture.complete(0) - dealloc(ol) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. return retFuture proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): @@ -386,10 +405,11 @@ when defined(windows): var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) = - if errcode == TOSErrorCode(-1): - completeAccept() - else: - retFuture.fail(newException(EOS, osErrorMsg(errcode))) + if not retFuture.finished: + if errcode == TOSErrorCode(-1): + completeAccept() + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx @@ -406,7 +426,9 @@ when defined(windows): dealloc(ol) else: completeAccept() - dealloc(ol) + # We don't deallocate ``ol`` here because even though this completed + # immediately poll will still be notified about its completion and it will + # free ``ol``. return retFuture @@ -429,6 +451,188 @@ when defined(windows): else: # TODO: Selectors. +# -- Await Macro + +template createCb*(cbName, varNameIterSym, retFutureSym: expr): stmt {.immediate, dirty.} = + proc cbName {.closure.} = + if not varNameIterSym.finished: + var next = varNameIterSym() + if next == nil: + assert retFutureSym.finished, "Async procedure's return Future was not finished." + else: + next.callback = cbName + +template createVar(futSymName: string, asyncProc: PNimrodNode, + valueReceiver: expr) {.immediate, dirty.} = + # TODO: Used template here due to bug #926 + result = newNimNode(nnkStmtList) + var futSym = newIdentNode(futSymName) #genSym(nskVar, "future") + result.add newVarStmt(futSym, asyncProc) # -> var future = y + result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future + valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future.read + +proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = + case node.kind + of nnkReturnStmt: + result = newNimNode(nnkStmtList) + result.add newCall(newIdentNode("complete"), retFutureSym, + if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0]) + result.add newNimNode(nnkYieldStmt).add(newNilLit()) + of nnkCommand: + result = node + echo(treeRepr(node)) + if node[0].ident == !"await": + case node[1].kind + of nnkIdent, nnkCall: + # await x + # await foo(p, x) + result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x + else: + error("Invalid node kind in 'await', got: " & $node[1].kind) + elif node[1].kind == nnkIdent and node[1][0].ident == !"await": + # foo await x + var newCommand = node + createVar("future" & $node[0].ident, node[1][0], newCommand[1]) + result.add newCommand + of nnkVarSection, nnkLetSection: + result = node + case node[0][2].kind + of nnkCommand: + if node[0][2][0].ident == !"await": + # var x = await y + var newVarSection = node # TODO: Should this use copyNimNode? + createVar("future" & $node[0][0].ident, node[0][2][1], + newVarSection[0][2]) + result.add newVarSection + else: discard + of nnkAsgn: + result = node + case node[1].kind + of nnkCommand: + if node[1][0].ident == !"await": + # x = await y + var newAsgn = node + createVar("future" & $node[0].ident, node[1][1], newAsgn[1]) + result.add newAsgn + else: discard + of nnkDiscardStmt: + # discard await x + if node[0][0].ident == !"await": + var dummy = newNimNode(nnkStmtList) + createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy) + else: + result = node + for i in 0 .. var retFuture = newFuture[T]() + var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture") + outerProcBody.add( + newVarStmt(retFutureSym, + newCall( + newNimNode(nnkBracketExpr).add( + newIdentNode("newFuture"), + prc[3][0][1])))) # Get type from return type of this proc. + + # -> iterator nameIter(): PFutureBase {.closure.} = + # -> var result: T + # -> + # -> complete(retFuture, result) + var iteratorNameSym = newIdentNode($prc[0].ident & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter") + var procBody = prc[6].processBody(retFutureSym) + procBody.insert(0, newNimNode(nnkVarSection).add( + newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T + procBody.add( + newCall(newIdentNode("complete"), + retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) + + var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")], + procBody, nnkIteratorDef) + closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure")) + outerProcBody.add(closureIterator) + + # -> var nameIterVar = nameIter + # -> var first = nameIterVar() + var varNameIterSym = newIdentNode($prc[0].ident & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar") + var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym) + outerProcBody.add varNameIter + var varFirstSym = genSym(nskVar, "first") + var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym)) + outerProcBody.add varFirst + + # -> createCb(cb, nameIter, retFuture) + var cbName = newIdentNode("cb") + var procCb = newCall("createCb", cbName, varNameIterSym, retFutureSym) + outerProcBody.add procCb + + # -> first.callback = cb + outerProcBody.add newAssignment( + newDotExpr(varFirstSym, newIdentNode("callback")), + cbName) + + # -> return retFuture + outerProcBody.add newNimNode(nnkReturnStmt).add(retFutureSym) + + result = prc + + # Remove the 'async' pragma. + for i in 0 .. 0 and c == "\L": + discard await p.recv(socket, 1) + addNLIfEmpty() + return + elif c == "\L": + addNLIfEmpty() + return + add(result.string, c) when isMainModule: @@ -437,39 +641,55 @@ when isMainModule: #sock.setBlocking false p.register(sock) - when true: - var f = p.connect(sock, "irc.freenode.org", TPort(6667)) - f.callback = - proc (future: PFuture[int]) = - echo("Connected in future!") - echo(future.read) - for i in 0 .. 50: - var recvF = p.recv(sock, 10) - recvF.callback = - proc (future: PFuture[string]) = - echo("Read: ", future.read) + when true: + # Await tests + proc main(p: PDispatcher): PFuture[int] {.async.} = + discard await p.connect(sock, "localhost", TPort(6667)) + while true: + var line = await p.recvLine(sock) + echo("Line is: ", line.repr) + if line == "": + echo "Disconnected" + break + + + var f = main(p) + else: + when true: - sock.bindAddr(TPort(6667)) - sock.listen() - proc onAccept(future: PFuture[TSocketHandle]) = - echo "Accepted" - var t = p.send(future.read, "test\c\L") - t.callback = + var f = p.connect(sock, "irc.freenode.org", TPort(6667)) + f.callback = proc (future: PFuture[int]) = + echo("Connected in future!") echo(future.read) - + for i in 0 .. 50: + var recvF = p.recv(sock, 10) + recvF.callback = + proc (future: PFuture[string]) = + echo("Read: ", future.read) + + else: + + sock.bindAddr(TPort(6667)) + sock.listen() + proc onAccept(future: PFuture[TSocketHandle]) = + echo "Accepted" + var t = p.send(future.read, "test\c\L") + t.callback = + proc (future: PFuture[int]) = + echo(future.read) + + var f = p.accept(sock) + f.callback = onAccept + var f = p.accept(sock) f.callback = onAccept - - var f = p.accept(sock) - f.callback = onAccept while true: p.poll() - echo "polled" From 492fa86638f20c3230d9086296b9d1c76ae66916 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Feb 2014 17:41:35 +0200 Subject: [PATCH 032/249] the delegator pragma becomes a set of dot operators --- compiler/ast.nim | 11 ++++- compiler/parser.nim | 2 +- compiler/semcall.nim | 38 +++++++++++---- compiler/semexprs.nim | 34 ++++---------- doc/manual.txt | 89 +++++++++++++++++++++--------------- tests/specialops/tdotops.nim | 66 ++++++++++++++++++++++++++ web/news.txt | 4 +- 7 files changed, 169 insertions(+), 75 deletions(-) create mode 100644 tests/specialops/tdotops.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index cd002eef1..fe724f4dd 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -409,7 +409,9 @@ type # efficiency nfTransf, # node has been transformed nfSem # node has been checked for semantics - nfDelegate # the call can use a delegator + nfDotField # the call can use a dot operator + nfDotSetter # the call can use a setter dot operarator + nfExplicitCall # x.y() was used instead of x.y nfExprCall # this is an attempt to call a regular expression nfIsRef # this node is a 'ref' node; used for the VM @@ -843,7 +845,8 @@ const ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, - nfAllConst, nfDelegate, nfIsRef} + nfDotSetter, nfDotField, + nfAllConst,nfIsRef} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 @@ -1044,6 +1047,10 @@ proc newStrNode(kind: TNodeKind, strVal: string): PNode = result = newNode(kind) result.strVal = strVal +proc withInfo*(n: PNode, info: TLineInfo): PNode = + n.info = info + return n + proc newIdentNode(ident: PIdent, info: TLineInfo): PNode = result = newNode(nkIdent) result.ident = ident diff --git a/compiler/parser.nim b/compiler/parser.nim index ff3324b47..5a5bfb574 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -281,7 +281,7 @@ proc parseSymbol(p: var TParser): PNode = add(result, newIdentNodeP(getIdent"{}", p)) getTok(p) eat(p, tkCurlyRi) - of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot: + of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot: add(result, newIdentNodeP(p.tok.ident, p)) getTok(p) of tkIntLit..tkCharLit: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 6b19dc359..0cd27a443 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) = # fail fast: globalError(n.info, errTypeMismatch, "") var result = msgKindToString(errTypeMismatch) - add(result, describeArgs(c, n, 1 + ord(nfDelegate in n.flags))) + add(result, describeArgs(c, n, 1 + ord(nfDotField in n.flags))) add(result, ')') var candidates = "" @@ -138,17 +138,35 @@ proc resolveOverloads(c: PContext, n, orig: PNode, let overloadsState = result.state if overloadsState != csMatch: - if nfDelegate in n.flags: - internalAssert f.kind == nkIdent - let calleeName = newStrNode(nkStrLit, f.ident.s) - calleeName.info = n.info + if nfDotField in n.flags: + internalAssert f.kind == nkIdent and n.sonsLen >= 2 + let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info) - let callOp = newIdentNode(idDelegator, n.info) - n.sons[0..0] = [callOp, calleeName] - orig.sons[0..0] = [callOp, calleeName] - + # leave the op head symbol empty, + # we are going to try multiple variants + n.sons[0..1] = [nil, n[1], calleeName] + orig.sons[0..1] = [nil, orig[1], calleeName] + + template tryOp(x) = + let op = newIdentNode(getIdent(x), n.info) + n.sons[0] = op + orig.sons[0] = op + pickBest(op) + + if nfExplicitCall in n.flags: + tryOp ".()" + + if result.state in {csEmpty, csNoMatch}: + tryOp "." + + elif nfDotSetter in n.flags: + internalAssert f.kind == nkIdent and n.sonsLen == 3 + let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info) + let callOp = newIdentNode(getIdent".=", n.info) + n.sons[0..1] = [callOp, n[1], calleeName] + orig.sons[0..1] = [callOp, orig[1], calleeName] pickBest(callOp) - + if overloadsState == csEmpty and result.state == csEmpty: localError(n.info, errUndeclaredIdentifier, considerAcc(f).s) return diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3fe1367ec..30ab344c2 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -683,20 +683,21 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, incl(c.p.owner.flags, sfSideEffect) proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode -proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = +proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil checkMinSonsLen(n, 1) var prc = n.sons[0] - if n.sons[0].kind == nkDotExpr: + if n.sons[0].kind == nkDotExpr: checkSonsLen(n.sons[0], 2) n.sons[0] = semFieldAccess(c, n.sons[0]) - if n.sons[0].kind == nkDotCall: + if n.sons[0].kind == nkDotCall: # it is a static call! result = n.sons[0] result.kind = nkCall + result.flags.incl nfExplicitCall for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i]) return semExpr(c, result, flags) - else: + else: n.sons[0] = semExpr(c, n.sons[0]) let nOrig = n.copyTree semOpAux(c, n) @@ -999,7 +1000,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode = else: var i = considerAcc(n.sons[1]) result = newNodeI(nkDotCall, n.info) - result.flags.incl nfDelegate + result.flags.incl nfDotField addSon(result, newIdentNode(i, n[1].info)) addSon(result, copyTree(n[0])) @@ -1082,12 +1083,13 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = var id = considerAcc(a[1]) - let setterId = newIdentNode(getIdent(id.s & '='), n.info) + var setterId = newIdentNode(getIdent(id.s & '='), n.info) # a[0] is already checked for semantics, that does ``builtinFieldAccess`` # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # nodes? let aOrig = nOrig[0] result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])]) + result.flags.incl nfDotSetter let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]]) result = semOverloadedCallAnalyseEffects(c, result, orig, {}) @@ -1777,22 +1779,6 @@ proc semBlock(c: PContext, n: PNode): PNode = closeScope(c) dec(c.p.nestedBlockCounter) -proc buildCall(n: PNode): PNode = - if n.kind == nkDotExpr and n.len == 2: - # x.y --> y(x) - result = newNodeI(nkCall, n.info, 2) - result.sons[0] = n.sons[1] - result.sons[1] = n.sons[0] - elif n.kind in nkCallKinds and n.sons[0].kind == nkDotExpr: - # x.y(a) -> y(x, a) - let a = n.sons[0] - result = newNodeI(nkCall, n.info, n.len+1) - result.sons[0] = a.sons[1] - result.sons[1] = a.sons[0] - for i in 1 .. delegator("b", a) - a.b(c, d) => delegator("b", a, c) - a b, c, d => delegator("a", b, c, d) - - -The delegators can be any callable symbol type (procs, templates, macros) -depending on the desired effect: - -.. code-block:: nimrod - proc `()` (field: string, js: PJsonNode): JSON {.delegator.} = js[field] - - var js = parseJson("{ x: 1, y: 2}") - echo js.x # outputs 1 - echo js.y # outputs 2 - - procvar pragma -------------- The `procvar`:idx: pragma is used to mark a proc that it can be passed to a diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim new file mode 100644 index 000000000..ce5b3942d --- /dev/null +++ b/tests/specialops/tdotops.nim @@ -0,0 +1,66 @@ +discard """ + output: ''' +10 +assigning z = 20 +reading field y +20 +call to y +dot call +no params call to a +100 +no params call to b +100 +one param call to c with 10 +100''' +""" + +type + T1 = object + x*: int + + TD = distinct T1 + + T2 = object + x: int + +proc `.`*(v: T1, f: string): int = + echo "reading field ", f + return v.x + +proc `.=`(x: var T1, f: string{lit}, v: int) = + echo "assigning ", f, " = ", v + x.x = v + +template `.()`(x: T1, f: string, args: varargs[expr]): string = + echo "call to ", f + "dot call" + +echo "" + +var t = T1(x: 10) + +echo t.x +t.z = 20 +echo t.y +echo t.y() + +var d = TD(t) +assert(not compiles(d.y)) + +proc `.`(v: T2, f: string): int = + echo "no params call to ", f + return v.x + +proc `.`*(v: T2, f: string, a: int): int = + echo "one param call to ", f, " with ", a + return v.x + +var tt = T2(x: 100) + +echo tt.a +echo tt.b() +echo tt.c(10) + +assert(not compiles(tt.d("x"))) +assert(not compiles(tt.d(1, 2))) + diff --git a/web/news.txt b/web/news.txt index a045eb880..b28e5c64a 100644 --- a/web/news.txt +++ b/web/news.txt @@ -68,8 +68,8 @@ Language Additions - Exported templates are allowed to access hidden fields. - The ``using statement`` enables you to more easily author domain-specific languages and libraries providing OOP-like syntactic sugar. -- Added a new ``delegator pragma`` for handling calls to missing procs and - fields at compile-time. +- Added the possibility to override various dot operators in order to handle + calls to missing procs and reads from undeclared fields at compile-time. - The overload resolution now supports ``static[T]`` params that must be evaluable at compile-time. - Support for user-defined type classes has been added. From c2b50c0e38d758f86ae8909649da545235a6b7c6 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 15 Feb 2014 17:50:40 +0100 Subject: [PATCH 033/249] Adds posix.timegm(), brother of posix.mktime(). --- lib/posix/posix.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 41260b36f..e2c436749 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -2265,6 +2265,7 @@ proc gmtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: " proc localtime*(a1: var TTime): ptr Ttm {.importc, header: "".} proc localtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "".} proc mktime*(a1: var Ttm): TTime {.importc, header: "".} +proc timegm*(a1: var Ttm): TTime {.importc, header: "".} proc nanosleep*(a1, a2: var Ttimespec): cint {.importc, header: "".} proc strftime*(a1: cstring, a2: int, a3: cstring, a4: var Ttm): int {.importc, header: "".} From c1c6b6e50e3755f25f734c0e55b6b81f51a7852c Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Feb 2014 19:05:58 +0200 Subject: [PATCH 034/249] handle nested case objects in destructor generation --- compiler/semdestruct.nim | 67 +++++++++++++++----------------- tests/destructor/tdestructor.nim | 44 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index fb05826cb..791bef823 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -55,7 +55,9 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = useSym(destructableT.destructor), n.sons[paramsPos][1][0]])) -proc destroyField(c: PContext, field: PSym, holder: PNode): PNode = +proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode + +proc destroySym(c: PContext, field: PSym, holder: PNode): PNode = let destructableT = instantiateDestructor(c, field.typ) if destructableT != nil: result = newNode(nkCall, field.info, @[ @@ -70,56 +72,49 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = for i in countup(1, n.len - 1): # of A, B: var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2]) - let recList = n[i].lastSon - var destroyRecList = newNode(nkStmtList, n[i].info, @[]) - template addField(f: expr): stmt = - let stmt = destroyField(c, f, holder) - if stmt != nil: - destroyRecList.addSon(stmt) - inc nonTrivialFields - - case recList.kind - of nkSym: - addField(recList.sym) - of nkRecList: - for j in countup(0, recList.len - 1): - addField(recList[j].sym) + + let stmt = destroyFieldOrFields(c, n[i].lastSon, holder) + if stmt == nil: + caseBranch.addSon(newNode(nkStmtList, n[i].info, @[])) else: - internalAssert false - - caseBranch.addSon(destroyRecList) + caseBranch.addSon(stmt) + nonTrivialFields += stmt.len + result.addSon(caseBranch) + # maybe no fields were destroyed? if nonTrivialFields == 0: result = nil - + +proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode = + template maybeAddLine(e: expr): stmt = + let stmt = e + if stmt != nil: + if result == nil: result = newNode(nkStmtList) + result.addSon(stmt) + + case field.kind + of nkRecCase: + maybeAddLine destroyCase(c, field, holder) + of nkSym: + maybeAddLine destroySym(c, field.sym, holder) + of nkRecList: + for son in field: + maybeAddLine destroyFieldOrFields(c, son, holder) + else: + internalAssert false + proc generateDestructor(c: PContext, t: PType): PNode = ## generate a destructor for a user-defined object or tuple type ## returns nil if the destructor turns out to be trivial - template addLine(e: expr): stmt = - if result == nil: result = newNode(nkStmtList) - result.addSon(e) - # XXX: This may be true for some C-imported types such as # Tposix_spawnattr if t.n == nil or t.n.sons == nil: return internalAssert t.n.kind == nkRecList let destructedObj = newIdentNode(destructorParam, unknownLineInfo()) # call the destructods of all fields - for s in countup(0, t.n.sons.len - 1): - case t.n.sons[s].kind - of nkRecCase: - let stmt = destroyCase(c, t.n.sons[s], destructedObj) - if stmt != nil: addLine(stmt) - of nkSym: - let stmt = destroyField(c, t.n.sons[s].sym, destructedObj) - if stmt != nil: addLine(stmt) - else: - # XXX just skip it for now so that the compiler doesn't crash, but - # please zahary fix it! arbitrary nesting of nkRecList/nkRecCase is - # possible. Any thread example seems to trigger this. - discard + result = destroyFieldOrFields(c, t.n, destructedObj) # base classes' destructors will be automatically called by # semProcAux for both auto-generated and user-defined destructors diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim index bb1410d92..f10c0cc9c 100644 --- a/tests/destructor/tdestructor.nim +++ b/tests/destructor/tdestructor.nim @@ -12,6 +12,13 @@ myobj destroyed ---- mygeneric3 constructed mygeneric1 destroyed +---- +mygeneric1 destroyed +---- +myobj destroyed +---- +---- +myobj destroyed ''' """ @@ -31,6 +38,22 @@ type x: A y: B z: C + + TObjKind = enum A, B, C, D + + TCaseObj = object + case kind: TObjKind + of A: + x: TMyGeneric1[int] + of B, C: + y: TMyObj + else: + case innerKind: TObjKind + of A, B, C: + p: TMyGeneric3[int, float, string] + of D: + q: TMyGeneric3[TMyObj, int, int] + r: string proc destruct(o: var TMyObj) {.destructor.} = if o.p != nil: dealloc o.p @@ -82,3 +105,24 @@ mygeneric2[int](10) echo "----" mygeneric3() +proc caseobj = + block: + echo "----" + var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10)) + + block: + echo "----" + var o2 = TCaseObj(kind: B, y: open()) + + block: + echo "----" + var o3 = TCaseObj(kind: D, innerKind: B, r: "test", + p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test")) + + block: + echo "----" + var o4 = TCaseObj(kind: D, innerKind: D, r: "test", + q: TMyGeneric3[TMyObj, int, int](x: open(), y: 1, z: 0)) + +caseobj() + From c1f1f841946dce9454e6971e9ce30115789cfb61 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Feb 2014 20:44:01 +0200 Subject: [PATCH 035/249] fix some regressions caused by tyTypeDesc[tyNone] --- compiler/semdata.nim | 1 - compiler/semexprs.nim | 19 +++++++++---------- compiler/semtypes.nim | 5 ++--- compiler/sigmatch.nim | 24 +++++++++++------------- compiler/types.nim | 9 ++++----- tests/metatype/tusertypeclasses.nim | 13 +++++++++++++ 6 files changed, 39 insertions(+), 32 deletions(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 0bc52d6b7..234526054 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -213,7 +213,6 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = makeTypeDesc(c, typ) - rawAddSon(typedesc, newTypeS(tyNone, c)) let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc) return newSymNode(sym, info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 30ab344c2..f09ee1295 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -346,22 +346,21 @@ proc semIs(c: PContext, n: PNode): PNode = result = n n.typ = getSysType(tyBool) - - n.sons[1] = semExprWithType(c, n[1], {efDetermineType}) - + + n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: let t2 = semTypeNode(c, n[2], nil) n.sons[2] = newNodeIT(nkType, n[2].info, t2) - if n[1].typ.kind != tyTypeDesc: - n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info) - elif n[1].typ.sonsLen == 0: + let lhsType = n[1].typ + if lhsType.kind != tyTypeDesc: + n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info) + elif lhsType.base.kind == tyNone: # this is a typedesc variable, leave for evals return - let t1 = n[1].typ.sons[0] # BUGFIX: don't evaluate this too early: ``T is void`` - if not containsGenericType(t1): result = isOpImpl(c, n) + if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n) proc semOpAux(c: PContext, n: PNode) = const flags = {efDetermineType} @@ -918,8 +917,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = var ty = n.sons[0].typ var f: PSym = nil result = nil - if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1: - if ty.kind == tyTypeDesc: ty = ty.sons[0] + if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.base.kind != tyNone: + if ty.kind == tyTypeDesc: ty = ty.base case ty.kind of tyEnum: # look up if the identifier belongs to the enum: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 44e414e9c..184aca4f8 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1030,9 +1030,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if s.kind != skError: localError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) elif s.kind == skParam and s.typ.kind == tyTypeDesc: - assert s.typ.len > 0 - internalAssert prev == nil - result = s.typ.sons[0] + internalAssert s.typ.base.kind != tyNone and prev == nil + result = s.typ.base elif prev == nil: result = s.typ else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 227228f6e..fce0bdf48 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -866,7 +866,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: internalAssert a.sons != nil and a.sons.len > 0 c.typedescMatched = true - result = typeRel(c, f.sons[0], a.sons[0]) + result = typeRel(c, f.base, a.base) else: result = isNone else: @@ -896,22 +896,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone of tyTypeDesc: + if a.kind != tyTypeDesc: return isNone + var prev = PType(idTableGet(c.bindings, f)) if prev == nil: - if a.kind == tyTypeDesc: - if f.sons[0].kind == tyNone: - result = isGeneric - else: - result = typeRel(c, f.sons[0], a.sons[0]) - if result != isNone: - put(c.bindings, f, a) + if f.base.kind == tyNone: + result = isGeneric else: - result = isNone + result = typeRel(c, f.base, a.base) + if result != isNone: + put(c.bindings, f, a) else: - internalAssert prev.sonsLen == 1 let toMatch = if tfUnresolved in f.flags: a - else: a.sons[0] - result = typeRel(c, prev.sons[0], toMatch) + else: a.base + result = typeRel(c, prev.base, toMatch) of tyStmt: result = isGeneric @@ -1015,7 +1013,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, argType = arg.typ var - a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc}) + a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor}) else: argType r = typeRel(m, f, a) diff --git a/compiler/types.nim b/compiler/types.nim index db75cd3c0..ef73ea783 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -431,8 +431,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(result, typeToString(t.sons[i])) add(result, ']') of tyTypeDesc: - if t.len == 0: result = "typedesc" - else: result = "typedesc[" & typeToString(t.sons[0]) & "]" + if t.base.kind == tyNone: result = "typedesc" + else: result = "typedesc[" & typeToString(t.base) & "]" of tyStatic: internalAssert t.len > 0 result = "static[" & typeToString(t.sons[0]) & "]" @@ -1231,8 +1231,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter: result = computeSizeAux(lastSon(typ), a) of tyTypeDesc: - result = if typ.len == 1: computeSizeAux(typ.sons[0], a) - else: szUnknownSize + result = computeSizeAux(typ.base, a) of tyForward: return szIllegalRecursion else: #internalError("computeSizeAux()") @@ -1258,7 +1257,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool = return true if t.kind == tyTypeDesc: - if t.sons[0].kind == tyNone: return true + if t.base.kind == tyNone: return true if containsGenericTypeIter(t.base, closure): return true return false diff --git a/tests/metatype/tusertypeclasses.nim b/tests/metatype/tusertypeclasses.nim index 4c2f07b85..4c8c0fc56 100644 --- a/tests/metatype/tusertypeclasses.nim +++ b/tests/metatype/tusertypeclasses.nim @@ -26,3 +26,16 @@ foo 10 foo "test" foo(@[TObj(x: 10), TObj(x: 20)]) +proc intval(x: int) = discard + +# check real and virtual fields +type + TFoo = generic T + intval T.x + intval T.y + +proc y(x: TObj): int = 10 + +proc testFoo(x: TFoo) = discard +testFoo(TObj(x: 10)) + From fb7598d25adee57f33549db97bb5de023f40a234 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 15 Feb 2014 21:13:23 +0000 Subject: [PATCH 036/249] Async readLine now works. Fixes recv issues. When using MSG_PEEK and data is retrieved ``lpNumberOfBytesRecvd`` will not be set to the number of bytes read by WSARecv. The buffer must therefore be checked to ensure it's empty when determining whether ``recv`` shall return "" to signal disconnection as we want to read as much data as has been received by the system. --- lib/pure/asyncio2.nim | 74 +++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 1cf2292de..00cb60d57 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -99,7 +99,8 @@ when defined(windows): TCompletionData* = object sock: TSocketHandle - cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.} + cb: proc (sock: TSocketHandle, bytesTransferred: DWORD, + errcode: TOSErrorCode) {.closure.} PDispatcher* = ref object ioPort: THandle @@ -143,13 +144,15 @@ when defined(windows): # This is useful for ensuring the reliability of the overlapped struct. assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle - customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1)) + customOverlapped.data.cb(customOverlapped.data.sock, + lpNumberOfBytesTransferred, TOSErrorCode(-1)) dealloc(customOverlapped) else: let errCode = OSLastError() if lpOverlapped != nil: assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle - customOverlapped.data.cb(customOverlapped.data.sock, errCode) + customOverlapped.data.cb(customOverlapped.data.sock, + lpNumberOfBytesTransferred, errCode) dealloc(customOverlapped) else: if errCode.int32 == WAIT_TIMEOUT: @@ -248,7 +251,7 @@ when defined(windows): # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): retFuture.complete(0) @@ -297,16 +300,19 @@ when defined(windows): var flagsio = flags.dword var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): - var data = newString(size) - copyMem(addr data[0], addr dataBuf.buf[0], size) - retFuture.complete($data) + if bytesCount == 0 and dataBuf.buf[0] == '\0': + retFuture.complete("") + else: + var data = newString(size) + copyMem(addr data[0], addr dataBuf.buf[0], size) + retFuture.complete($data) else: retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) - + let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived, addr flagsio, cast[POverlapped](ol), nil) if ret == -1: @@ -314,8 +320,13 @@ when defined(windows): if err.int32 != ERROR_IO_PENDING: retFuture.fail(newException(EOS, osErrorMsg(err))) dealloc(ol) - elif ret == 0 and bytesReceived == 0: - # Disconnected + elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0': + # We have to ensure that the buffer is empty because WSARecv will tell + # us immediatelly when it was disconnected, even when there is still + # data in the buffer. + # We want to give the user as much data as we can. So we only return + # the empty string (which signals a disconnection) when there is + # nothing left to read. retFuture.complete("") # TODO: "For message-oriented sockets, where a zero byte message is often # allowable, a failure with an error code of WSAEDISCON is used to @@ -343,7 +354,7 @@ when defined(windows): var bytesReceived, flags: DWord var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): retFuture.complete(0) @@ -404,7 +415,7 @@ when defined(windows): var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, errcode: TOSErrorCode) = + proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): completeAccept() @@ -525,6 +536,15 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = for i in 0 .. var result: T # -> # -> complete(retFuture, result) - var iteratorNameSym = newIdentNode($prc[0].ident & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter") + var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter") var procBody = prc[6].processBody(retFutureSym) procBody.insert(0, newNimNode(nnkVarSection).add( newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T @@ -568,7 +588,7 @@ macro async*(prc: stmt): stmt {.immediate.} = # -> var nameIterVar = nameIter # -> var first = nameIterVar() - var varNameIterSym = newIdentNode($prc[0].ident & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar") + var varNameIterSym = newIdentNode($prc[0].getName & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar") var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym) outerProcBody.add varNameIter var varFirstSym = genSym(nskVar, "first") @@ -600,18 +620,14 @@ macro async*(prc: stmt): stmt {.immediate.} = echo(toStrLit(result)) proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} = - ## Reads a line of data from ``socket``. + ## Reads a line of data from ``socket``. Returned future will complete once + ## a full line is read or an error occurs. ## ## If a full line is read ``\r\L`` is not ## added to ``line``, however if solely ``\r\L`` is read then ``line`` ## will be set to it. ## ## If the socket is disconnected, ``line`` will be set to ``""``. - ## - ## An EOS exception will be raised in the case of a socket error. - ## - ## A timeout can be specified in miliseconds, if data is not received within - ## the specified time an ETimeout exception will be raised. template addNLIfEmpty(): stmt = if result.len == 0: @@ -629,7 +645,7 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} discard await p.recv(socket, 1) addNLIfEmpty() return - elif c == "\L": + elif c == "\L": addNLIfEmpty() return add(result.string, c) @@ -645,14 +661,24 @@ when isMainModule: when true: # Await tests proc main(p: PDispatcher): PFuture[int] {.async.} = - discard await p.connect(sock, "localhost", TPort(6667)) + discard await p.connect(sock, "irc.freenode.net", TPort(6667)) while true: var line = await p.recvLine(sock) echo("Line is: ", line.repr) if line == "": echo "Disconnected" break - + + proc peekTest(p: PDispatcher): PFuture[int] {.async.} = + discard await p.connect(sock, "localhost", TPort(6667)) + while true: + var line = await p.recv(sock, 1, MSG_PEEK) + var line2 = await p.recv(sock, 1) + echo(line.repr) + echo(line2.repr) + echo("---") + if line2 == "": break + sleep(500) var f = main(p) From 228d5173ffe3f7b0b474448994c8d520d916bb77 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 15 Feb 2014 23:40:29 +0100 Subject: [PATCH 037/249] nil->discard --- tests/manyloc/argument_parser/argument_parser.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index 95c71c08c..1edda4aa0 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -178,14 +178,14 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter = ## #parsed_param3 = new_parsed_parameter(PK_INT, "231") var result {.gensym.}: Tparsed_parameter result.kind = tkind - when tkind == PK_EMPTY: nil + when tkind == PK_EMPTY: discard elif tkind == PK_INT: result.int_val = expr elif tkind == PK_BIGGEST_INT: result.big_int_val = expr elif tkind == PK_FLOAT: result.float_val = expr elif tkind == PK_BIGGEST_FLOAT: result.big_float_val = expr elif tkind == PK_STRING: result.str_val = expr elif tkind == PK_BOOL: result.bool_val = expr - elif tkind == PK_HELP: nil + elif tkind == PK_HELP: discard else: {.error: "unknown kind".} result From ce5a494927abf66cc39cf849b9c66e2dadbe579e Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Sat, 15 Feb 2014 18:57:03 -0500 Subject: [PATCH 038/249] Changed tests and tools to use 'discard' statements instead of 'nil' for empty blocks. --- compiler/c2nim/cparse.nim | 8 ++++---- compiler/c2nim/cpp.nim | 4 ++-- compiler/pas2nim/paslex.nim | 2 +- compiler/pas2nim/pasparse.nim | 24 ++++++++++++------------ examples/htmlrefs.nim | 2 +- tests/ambsym/mambsym1.nim | 2 +- tests/ambsym/mambsys1.nim | 2 +- tests/ambsym/mambsys2.nim | 2 +- tests/casestmt/tcasestm.nim | 4 ++-- tests/concurrency/tnodeadlocks.nim | 2 +- tests/destructor/tdictdestruct.nim | 2 +- tests/generics/tgeneric3.nim | 6 +++--- tests/lookups/tredef.nim | 14 +++++++------- tests/method/tmethods1.nim | 4 ++-- tests/overload/toverwr.nim | 14 +++++++------- tests/patterns/targlist.nim | 2 +- tests/range/tsubrange2.nim | 2 +- tests/range/tsubrange3.nim | 2 +- tests/sets/tsets.nim | 18 +++++++++--------- tests/stdlib/tircbot.nim | 8 ++++---- tests/stdlib/tmath2.nim | 2 +- tests/stdlib/tos.nim | 2 +- tests/stdlib/tpegs.nim | 14 +++++++------- tests/table/ttableconstr.nim | 2 +- tests/typerel/typalias.nim | 2 +- tools/nimgrep.nim | 2 +- 26 files changed, 74 insertions(+), 74 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index ffab05788..e4abe11a0 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode = if p.tok.s == "unsigned": isUnsigned = true elif p.tok.s == "signed" or p.tok.s == "int": - nil + discard else: add(x, p.tok.s) getTok(p, nil) @@ -746,7 +746,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = result = declarator(p, a, ident) eat(p, pxParRi, result) else: - nil + discard return parseTypeSuffix(p, a) proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode = @@ -1165,7 +1165,7 @@ proc enumSpecifier(p: var TParser): PNode = proc setBaseFlags(n: PNode, base: TNumericalBase) = case base - of base10: nil + of base10: discard of base2: incl(n.flags, nfBase2) of base8: incl(n.flags, nfBase8) of base16: incl(n.flags, nfBase16) @@ -1686,7 +1686,7 @@ proc switchStatement(p: var TParser): PNode = break of "case", "default": break - else: nil + else: discard addSon(result, statement(p)) if sonsLen(result) == 0: # translate empty statement list to Nimrod's ``nil`` statement diff --git a/compiler/c2nim/cpp.nim b/compiler/c2nim/cpp.nim index 1707b75db..84b4c4dfb 100644 --- a/compiler/c2nim/cpp.nim +++ b/compiler/c2nim/cpp.nim @@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) = m.body.add(tok) of pxDirConc: # just ignore this token: this implements token merging correctly - nil + discard else: m.body.add(p.tok) # we do not want macro expansion here: @@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode = of pxDirectiveParLe, pxDirective: case p.tok.s of "else", "endif", "elif": break - else: nil + else: discard addSon(result, statement(p)) proc eatEndif(p: var TParser) = diff --git a/compiler/pas2nim/paslex.nim b/compiler/pas2nim/paslex.nim index 67473e71f..f24b0c420 100644 --- a/compiler/pas2nim/paslex.nim +++ b/compiler/pas2nim/paslex.nim @@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) = h = h +% ord(c) h = h +% h shl 10 h = h xor (h shr 6) - of '_': nil + of '_': discard else: break inc(pos) h = h +% h shl 3 diff --git a/compiler/pas2nim/pasparse.nim b/compiler/pas2nim/pasparse.nim index 928896338..a6f8363f6 100644 --- a/compiler/pas2nim/pasparse.nim +++ b/compiler/pas2nim/pasparse.nim @@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind, proc setBaseFlags(n: PNode, base: TNumericalBase) = case base - of base10: nil + of base10: discard of base2: incl(n.flags, nfBase2) of base8: incl(n.flags, nfBase8) of base16: incl(n.flags, nfBase16) @@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind = eat(p, pxCurlyDirRi) opNode.ident = getIdent("&") else: - nil + discard of pxMinus: if p.tok.xkind == pxPer: getTok(p) @@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind = of pxNeq: opNode.ident = getIdent("!=") else: - nil + discard skipCom(p, opNode) # read sub-expression with higher priority nextop = lowestExprAux(p, v2, opPred) addSon(node, opNode) @@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode = (n.sons[2].kind in {nkCharLit, nkStrLit}): n.sons[0].ident = getIdent("&") # fix operator else: - nil + discard if not (n.kind in {nkEmpty..nkNilLit}): for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i]) @@ -603,7 +603,7 @@ proc parseStmtList(p: var TParser): PNode = of pxCurlyDirLe, pxStarDirLe: if not isHandledDirective(p): break else: - nil + discard addSon(result, parseStmt(p)) if sonsLen(result) == 1: result = result.sons[0] @@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode = addSon(b, c) addSon(a, b) if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id: - nil + discard else: addSon(s, a) addSon(result, s) @@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode = getTok(p) v = newNodeP(nkVarTy, p) else: - nil + discard while true: case p.tok.xkind of pxSymbol: a = createIdentNodeP(p.tok.ident, p) @@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode = proc exSymbol(n: var PNode) = case n.kind of nkPostfix: - nil + discard of nkPragmaExpr: exSymbol(n.sons[0]) of nkIdent, nkAccQuoted: @@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) = for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i]) of nkIdentDefs: for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i]) - of nkNilLit, nkEmpty: nil + of nkNilLit, nkEmpty: discard else: internalError(n.info, "fixRecordDef(): " & $n.kind) proc addPragmaToIdent(ident: var PNode, pragma: PNode) = @@ -1191,7 +1191,7 @@ proc parseRecordBody(p: var TParser, result, definition: PNode) = if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p)) else: internalError(result.info, "anonymous record is not supported") else: - nil + discard opt(p, pxSemicolon) skipCom(p, result) @@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) = proc exSymbols(n: PNode) = case n.kind - of nkEmpty..nkNilLit: nil + of nkEmpty..nkNilLit: discard of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos]) of nkWhenStmt, nkStmtList: for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i]) @@ -1410,7 +1410,7 @@ proc exSymbols(n: PNode) = exSymbol(n.sons[i].sons[0]) if n.sons[i].sons[2].kind == nkObjectTy: fixRecordDef(n.sons[i].sons[2]) - else: nil + else: discard proc parseBegin(p: var TParser, result: PNode) = getTok(p) diff --git a/examples/htmlrefs.nim b/examples/htmlrefs.nim index 824c1d8c7..8b668325f 100644 --- a/examples/htmlrefs.nim +++ b/examples/htmlrefs.nim @@ -36,7 +36,7 @@ block mainLoop: case x.kind of xmlEof: break mainLoop of xmlElementClose: break - else: nil + else: discard x.next() # skip ``xmlElementClose`` # now we have the description for the ``a`` element var desc = "" diff --git a/tests/ambsym/mambsym1.nim b/tests/ambsym/mambsym1.nim index cf8ac5242..d9d57b5e5 100644 --- a/tests/ambsym/mambsym1.nim +++ b/tests/ambsym/mambsym1.nim @@ -7,4 +7,4 @@ type proc ha() = var x: TExport # no error - nil + discard diff --git a/tests/ambsym/mambsys1.nim b/tests/ambsym/mambsys1.nim index 5472b5ae4..04f9561d3 100644 --- a/tests/ambsym/mambsys1.nim +++ b/tests/ambsym/mambsys1.nim @@ -4,4 +4,4 @@ type TExport* = enum x, y, z proc foo*(x: int) = - nil + discard diff --git a/tests/ambsym/mambsys2.nim b/tests/ambsym/mambsys2.nim index 395425b86..d59706865 100644 --- a/tests/ambsym/mambsys2.nim +++ b/tests/ambsym/mambsys2.nim @@ -1,4 +1,4 @@ type TExport* = enum x, y, z # exactly the same type! -proc foo*(x: int) = nil +proc foo*(x: int) = discard diff --git a/tests/casestmt/tcasestm.nim b/tests/casestmt/tcasestm.nim index 003ec6e50..b033b98ec 100644 --- a/tests/casestmt/tcasestm.nim +++ b/tests/casestmt/tcasestm.nim @@ -19,8 +19,8 @@ of eB, eC: write(stdout, "b or c") case x of "Andreas", "Rumpf": write(stdout, "Hallo Meister!") of "aa", "bb": write(stdout, "Du bist nicht mein Meister") -of "cc", "hash", "when": nil -of "will", "it", "finally", "be", "generated": nil +of "cc", "hash", "when": discard +of "will", "it", "finally", "be", "generated": discard var z = case i of 1..5, 8, 9: "aa" diff --git a/tests/concurrency/tnodeadlocks.nim b/tests/concurrency/tnodeadlocks.nim index 18fdca3e9..3f27e24f6 100644 --- a/tests/concurrency/tnodeadlocks.nim +++ b/tests/concurrency/tnodeadlocks.nim @@ -12,7 +12,7 @@ var thr: array [0..5, TThread[tuple[a, b: int]]] L, M, N: TLock -proc doNothing() = nil +proc doNothing() = discard proc threadFunc(interval: tuple[a, b: int]) {.thread.} = doNothing() diff --git a/tests/destructor/tdictdestruct.nim b/tests/destructor/tdictdestruct.nim index ec1084105..b7043f7b7 100644 --- a/tests/destructor/tdictdestruct.nim +++ b/tests/destructor/tdictdestruct.nim @@ -6,7 +6,7 @@ type PDict[TK, TV] = ref TDict[TK, TV] proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) = - nil + discard proc destroyDict[TK, TV](a: PDict[TK, TV]) = return diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim index 3c543ecfa..963d0ccfb 100644 --- a/tests/generics/tgeneric3.nim +++ b/tests/generics/tgeneric3.nim @@ -32,7 +32,7 @@ const proc len[T,D] (n:PNode[T,D]): Int {.inline.} = return n.Count -proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = nil +proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = discard proc clean[T: string|seq](o: var T) {.inline.} = o = nil @@ -98,7 +98,7 @@ proc DeleteItem[T,D] (n: PNode[T,D], x: Int): PNode[T,D] {.inline.} = of cLen3 : setLen(n.slots, cLen3) of cLenCenter : setLen(n.slots, cLenCenter) of cLen4 : setLen(n.slots, cLen4) - else: nil + else: discard Result = n else : @@ -232,7 +232,7 @@ proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], AKey: T, AValue: D) = of cLen3: setLen(APath.Nd.slots, cLenCenter) of cLenCenter: setLen(APath.Nd.slots, cLen4) of cLen4: setLen(APath.Nd.slots, cLenMax) - else: nil + else: discard for i in countdown(APath.Nd.Count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1]) APath.Nd.slots[x] = setItem(AKey, AValue, ANode) diff --git a/tests/lookups/tredef.nim b/tests/lookups/tredef.nim index 02d1f7776..a1647000c 100644 --- a/tests/lookups/tredef.nim +++ b/tests/lookups/tredef.nim @@ -1,28 +1,28 @@ -template foo(a: int, b: string) = nil +template foo(a: int, b: string) = discard foo(1, "test") -proc bar(a: int, b: string) = nil +proc bar(a: int, b: string) = discard bar(1, "test") template foo(a: int, b: string) = bar(a, b) foo(1, "test") block: - proc bar(a: int, b: string) = nil - template foo(a: int, b: string) = nil + proc bar(a: int, b: string) = discard + template foo(a: int, b: string) = discard foo(1, "test") bar(1, "test") proc baz = - proc foo(a: int, b: string) = nil + proc foo(a: int, b: string) = discard proc foo(b: string) = - template bar(a: int, b: string) = nil + template bar(a: int, b: string) = discard bar(1, "test") foo("test") block: - proc foo(b: string) = nil + proc foo(b: string) = discard foo("test") foo(1, "test") diff --git a/tests/method/tmethods1.nim b/tests/method/tmethods1.nim index f4add6af4..43a260bca 100644 --- a/tests/method/tmethods1.nim +++ b/tests/method/tmethods1.nim @@ -14,8 +14,8 @@ type TSomethingElse = object PSomethingElse = ref TSomethingElse -method foo(a: PNode, b: PSomethingElse) = nil -method foo(a: PNodeFoo, b: PSomethingElse) = nil +method foo(a: PNode, b: PSomethingElse) = discard +method foo(a: PNodeFoo, b: PSomethingElse) = discard var o: TObject o.somethin() diff --git a/tests/overload/toverwr.nim b/tests/overload/toverwr.nim index ef25e8913..32d50faaa 100644 --- a/tests/overload/toverwr.nim +++ b/tests/overload/toverwr.nim @@ -1,13 +1,13 @@ -discard """ - file: "toverwr.nim" - output: "hello" -""" +discard """ + file: "toverwr.nim" + output: "hello" +""" # Test the overloading resolution in connection with a qualifier proc write(t: TFile, s: string) = - nil # a nop + discard # a nop system.write(stdout, "hello") #OUT hello - - + + diff --git a/tests/patterns/targlist.nim b/tests/patterns/targlist.nim index a2fa1fa48..e416edf0a 100644 --- a/tests/patterns/targlist.nim +++ b/tests/patterns/targlist.nim @@ -2,7 +2,7 @@ discard """ output: "12false3ha" """ -proc f(x: varargs[string, `$`]) = nil +proc f(x: varargs[string, `$`]) = discard template optF{f(X)}(x: varargs[expr]) = writeln(stdout, x) diff --git a/tests/range/tsubrange2.nim b/tests/range/tsubrange2.nim index 51598713b..d14111bb9 100644 --- a/tests/range/tsubrange2.nim +++ b/tests/range/tsubrange2.nim @@ -8,7 +8,7 @@ type TRange = range[0..40] proc p(r: TRange) = - nil + discard var r: TRange diff --git a/tests/range/tsubrange3.nim b/tests/range/tsubrange3.nim index b3e02fd29..9afb5018b 100644 --- a/tests/range/tsubrange3.nim +++ b/tests/range/tsubrange3.nim @@ -8,7 +8,7 @@ type TRange = range[0..40] proc p(r: TRange) = - nil + discard var r: TRange diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim index 7b806f15b..e370209ed 100644 --- a/tests/sets/tsets.nim +++ b/tests/sets/tsets.nim @@ -1,7 +1,7 @@ -discard """ - file: "tsets.nim" - output: "Ha ein F ist in s!" -""" +discard """ + file: "tsets.nim" + output: "Ha ein F ist in s!" +""" # Test the handling of sets import @@ -38,7 +38,7 @@ type TTokTypes* = set[TTokTypeRange] const - toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), + toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), tkStrLit..tkTripleStrLit} var @@ -51,14 +51,14 @@ else: write(stdout, "BUG: F ist nicht in s!\n") a = {} #{'a'..'z'} for x in low(TAZ) .. high(TAZ): incl(a, x) - if x in a: nil + if x in a: discard else: write(stdout, "BUG: something not in a!\n") for x in low(TTokTypeRange) .. high(TTokTypeRange): if x in tokTypes: - nil + discard #writeln(stdout, "the token '$1' is in the set" % repr(x)) #OUT Ha ein F ist in s! - - + + diff --git a/tests/stdlib/tircbot.nim b/tests/stdlib/tircbot.nim index 6008838ff..71ecb0b48 100644 --- a/tests/stdlib/tircbot.nim +++ b/tests/stdlib/tircbot.nim @@ -183,7 +183,7 @@ type channel: string timestamp: TTime case kind*: TSeenType - of PSeenJoin: nil + of PSeenJoin: discard of PSeenPart, PSeenQuit, PSeenMsg: msg: string of PSeenNick: @@ -200,7 +200,7 @@ proc setSeen(d: TDb, s: TSeen) = var hashToSet = @[("type", $s.kind.int), ("channel", s.channel), ("timestamp", $s.timestamp.int)] case s.kind - of PSeenJoin: nil + of PSeenJoin: discard of PSeenPart, PSeenMsg, PSeenQuit: hashToSet.add(("msg", s.msg)) of PSeenNick: @@ -338,7 +338,7 @@ proc hubConnect(state: PState) = proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) = case event.typ - of EvConnected: nil + of EvConnected: discard of EvDisconnected: while not state.ircClient.isConnected: try: @@ -424,7 +424,7 @@ proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) = seenNick.newNick = event.params[0] state.database.setSeen(seenNick) else: - nil # TODO: ? + discard # TODO: ? proc open(port: TPort = TPort(5123)): PState = var res: PState diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim index 6a1dae54d..935b08634 100644 --- a/tests/stdlib/tmath2.nim +++ b/tests/stdlib/tmath2.nim @@ -1,7 +1,7 @@ # tests for the interpreter proc loops(a: var int) = - nil + discard #var # b: int #b = glob diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index fa9993cc9..ebe577b00 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -7,6 +7,6 @@ proc walkDirTree(root: string) = case k of pcFile, pcLinkToFile: echo(f) of pcDir: walkDirTree(f) - of pcLinkToDir: nil + of pcLinkToDir: discard walkDirTree(".") diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index bdd8db0f8..7775091a1 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -72,7 +72,7 @@ type rule: TNode ## the rule that the symbol refers to TNode {.final, shallow.} = object case kind: TPegKind - of pkEmpty..pkWhitespace: nil + of pkEmpty..pkWhitespace: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] @@ -123,7 +123,7 @@ proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s) proc copyPeg(a: TPeg): TPeg = result.kind = a.kind case a.kind - of pkEmpty..pkWhitespace: nil + of pkEmpty..pkWhitespace: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: result.term = a.term of pkChar, pkGreedyRepChar: @@ -229,7 +229,7 @@ when false: case a.kind of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, - pkCharChoice, pkGreedyRepSet: nil + pkCharChoice, pkGreedyRepSet: discard of pkNonTerminal: return true else: for i in 0..a.sons.len-1: @@ -318,7 +318,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {. proc spaceCost(n: TPeg): int = case n.kind - of pkEmpty: nil + of pkEmpty: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, pkAny..pkWhitespace, pkGreedyAny: @@ -1111,7 +1111,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) = of 'A'..'F': xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) inc(c.bufpos) - else: nil + else: discard proc getEscapedChar(c: var TPegLexer, tok: var TToken) = inc(c.bufpos) @@ -1341,7 +1341,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) = of "i": tok.modifier = modIgnoreCase of "y": tok.modifier = modIgnoreStyle of "v": tok.modifier = modVerbatim - else: nil + else: discard setLen(tok.literal, 0) if c.buf[c.bufpos] == '$': getDollar(c, tok) @@ -1488,7 +1488,7 @@ proc primary(p: var TPegParser): TPeg = of tkCurlyAt: getTok(p) return !*\primary(p).token(p) - else: nil + else: discard case p.tok.kind of tkIdentifier: if p.identIsVerbatim: diff --git a/tests/table/ttableconstr.nim b/tests/table/ttableconstr.nim index c627e68e8..1a21a18d1 100644 --- a/tests/table/ttableconstr.nim +++ b/tests/table/ttableconstr.nim @@ -1,7 +1,7 @@ # Test if the new table constructor syntax works: template ignoreExpr(e: expr): stmt {.immediate.} = - nil + discard # test first class '..' syntactical citizen: ignoreExpr x <> 2..4 diff --git a/tests/typerel/typalias.nim b/tests/typerel/typalias.nim index ba9f38ed9..40ff06765 100644 --- a/tests/typerel/typalias.nim +++ b/tests/typerel/typalias.nim @@ -9,7 +9,7 @@ proc init: TYourObj = result.y = -1 proc f(x: var TYourObj) = - nil + discard var m: TMyObj = init() f(m) diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index b20e86a68..28d92402e 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -249,7 +249,7 @@ proc walker(dir: string) = of pcDir: if optRecursive in options: walker(path) - else: nil + else: discard if existsFile(dir): processFile(dir) proc writeHelp() = From 74f49014303a87a8cf649d6d0aec041d683509cd Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 16 Feb 2014 02:23:06 +0200 Subject: [PATCH 039/249] fix argument_parser --- compiler/sigmatch.nim | 53 +++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index fce0bdf48..8361d4681 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -343,53 +343,57 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} = proc inconsistentVarTypes(f, a: PType): bool {.inline.} = result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar) -proc procParamTypeRel(c: var TCandidate, f, a: PType, - result: var TTypeRelation) = - var - m: TTypeRelation - f = f - +proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = + var f = f + if a.isMetaType: if f.isMetaType: - # we are matching a generic proc (as proc param) + # We are matching a generic proc (as proc param) # to another generic type appearing in the proc - # sigunature. there is a change that the target + # signature. There is a change that the target # type is already fully-determined, so we are # going to try resolve it f = generateTypeInstance(c.c, c.bindings, c.call.info, f) if f == nil or f.isMetaType: # no luck resolving the type, so the inference fails - result = isNone - return + return isNone let reverseRel = typeRel(c, a, f) if reverseRel == isGeneric: - m = isInferred + result = isInferred + inc c.genericMatches else: - m = typeRel(c, f, a) + result = typeRel(c, f, a) - if m <= isSubtype or inconsistentVarTypes(f, a): + if result <= isSubtype or inconsistentVarTypes(f, a): result = isNone - return - else: - result = minRel(m, result) - + + if result == isEqual: + inc c.exactMatches + proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = case a.kind of tyProc: if sonsLen(f) != sonsLen(a): return - # Note: We have to do unification for the parameters before the - # return type! result = isEqual # start with maximum; also correct for no # params at all - for i in countup(1, sonsLen(f)-1): - procParamTypeRel(c, f.sons[i], a.sons[i], result) + + template checkParam(f, a) = + result = minRel(result, procParamTypeRel(c, f, a)) + if result == isNone: return + + # Note: We have to do unification for the parameters before the + # return type! + for i in 1 .. Date: Sun, 16 Feb 2014 02:08:36 +0100 Subject: [PATCH 040/249] Fixed issue 391 (nested break in except-stmts) --- compiler/ccgstmts.nim | 34 ++++++++++++++++------ compiler/cgendata.nim | 1 + tests/exception/texceptionbreak.nim | 45 +++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 tests/exception/texceptionbreak.nim diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index af0d657f1..443d845f6 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -65,6 +65,7 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n", setLen(p.blocks, result + 1) p.blocks[result].id = p.labels p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16 + p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16 proc assignLabel(b: var TBlock): PRope {.inline.} = b.label = con("LA", b.id.toRope) @@ -260,14 +261,22 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = else: internalError(n.info, "genIf()") if sonsLen(n) > 1: fixLabel(p, lend) -proc blockLeaveActions(p: BProc, howMany: int) = - var L = p.nestedTryStmts.len + +proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = + # This is called by return and break stmts. + # When jumping out of try/except/finally stmts, + # we need to pop safe points from try statements, + # execute finally-stmts, and pop exceptions + # from except stmts + + let L = p.nestedTryStmts.len + # danger of endless recursion! we workaround this here by a temp stack var stack: seq[PNode] - newSeq(stack, howMany) - for i in countup(1, howMany): + newSeq(stack, howManyTrys) + for i in countup(1, howManyTrys): stack[i-1] = p.nestedTryStmts[L-i] - setLen(p.nestedTryStmts, L-howMany) + setLen(p.nestedTryStmts, L-howManyTrys) var alreadyPoppedCnt = p.inExceptBlock for tryStmt in items(stack): @@ -276,21 +285,26 @@ proc blockLeaveActions(p: BProc, howMany: int) = dec alreadyPoppedCnt else: linefmt(p, cpsStmts, "#popSafePoint();$n") + # Find finally-stmts for this try-stmt + # and generate a copy of the finally stmts here var finallyStmt = lastSon(tryStmt) if finallyStmt.kind == nkFinally: genStmts(p, finallyStmt.sons[0]) # push old elements again: - for i in countdown(howMany-1, 0): + for i in countdown(howManyTrys-1, 0): p.nestedTryStmts.add(stack[i]) + if gCmd != cmdCompileToCpp: - for i in countdown(p.inExceptBlock-1, 0): + # Pop exceptions that was handled by the + # except-blocks we are in + for i in countdown(howManyExcepts-1, 0): linefmt(p, cpsStmts, "#popCurrentException();$n") proc genReturnStmt(p: BProc, t: PNode) = p.beforeRetNeeded = true genLineDir(p, t) if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) - blockLeaveActions(p, min(1, p.nestedTryStmts.len)) + blockLeaveActions(p, min(1, p.nestedTryStmts.len), p.inExceptBlock) lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", []) proc genComputedGoto(p: BProc; n: PNode) = @@ -450,7 +464,9 @@ proc genBreakStmt(p: BProc, t: PNode) = if idx < 0 or not p.blocks[idx].isLoop: internalError(t.info, "no loop to break") let label = assignLabel(p.blocks[idx]) - blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts) + blockLeaveActions(p, + p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts, + p.inExceptBlock - p.blocks[idx].nestedExceptStmts) genLineDir(p, t) lineF(p, cpsStmts, "goto $1;$n", [label]) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 0e1148343..71479abdd 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -58,6 +58,7 @@ type sections*: TCProcSections # the code beloging isLoop*: bool # whether block is a loop nestedTryStmts*: int16 # how many try statements is it nested into + nestedExceptStmts*: int16 # how many except statements is it nested into frameLen*: int16 TCProc{.final.} = object # represents C proc that is currently generated diff --git a/tests/exception/texceptionbreak.nim b/tests/exception/texceptionbreak.nim new file mode 100644 index 000000000..76e986787 --- /dev/null +++ b/tests/exception/texceptionbreak.nim @@ -0,0 +1,45 @@ +discard """ + file: "tnestedbreak.nim" + output: "1\n2\n3\n4" +""" + +# First variety +try: + raise newException(EOS, "Problem") +except EOS: + for y in [1, 2, 3]: + discard + try: + discard + except EOS: + discard +echo "1" + +# Second Variety +try: + raise newException(EOS, "Problem") +except EOS: + for y in [1, 2, 3]: + discard + for y in [1, 2, 3]: + discard + +echo "2" + +# Third Variety +try: + raise newException(EOS, "Problem") +except EOS: + block: + break + +echo "3" + +# Fourth Variety +block: + try: + raise newException(EOS, "Problem") + except EOS: + break + +echo "4" \ No newline at end of file From 7d2ed73a6257f671374b3f09180a1fdfd9a2009c Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 16 Feb 2014 13:55:06 +0000 Subject: [PATCH 041/249] Fixes issues with 'discard' in async macro. --- lib/pure/asyncio2.nim | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 00cb60d57..d3e4bcc98 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -104,6 +104,7 @@ when defined(windows): PDispatcher* = ref object ioPort: THandle + hasHandles: bool TCustomOverlapped = object Internal*: DWORD @@ -125,9 +126,13 @@ when defined(windows): if CreateIOCompletionPort(sock.THandle, p.ioPort, cast[TCompletionKey](sock), 1) == 0: OSError(OSLastError()) + p.hasHandles = true proc poll*(p: PDispatcher, timeout = 500) = ## Waits for completion events and processes them. + if not p.hasHandles: + raise newException(EInvalidValue, "No handles registered in dispatcher.") + let llTimeout = if timeout == -1: winlean.INFINITE else: timeout.int32 @@ -483,6 +488,7 @@ template createVar(futSymName: string, asyncProc: PNimrodNode, valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future.read proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = + result = node case node.kind of nnkReturnStmt: result = newNimNode(nnkStmtList) @@ -490,8 +496,6 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0]) result.add newNimNode(nnkYieldStmt).add(newNilLit()) of nnkCommand: - result = node - echo(treeRepr(node)) if node[0].ident == !"await": case node[1].kind of nnkIdent, nnkCall: @@ -500,13 +504,13 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x else: error("Invalid node kind in 'await', got: " & $node[1].kind) - elif node[1].kind == nnkIdent and node[1][0].ident == !"await": + elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and + node[1][0].ident == !"await": # foo await x var newCommand = node createVar("future" & $node[0].ident, node[1][0], newCommand[1]) result.add newCommand of nnkVarSection, nnkLetSection: - result = node case node[0][2].kind of nnkCommand: if node[0][2][0].ident == !"await": @@ -517,7 +521,6 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = result.add newVarSection else: discard of nnkAsgn: - result = node case node[1].kind of nnkCommand: if node[1][0].ident == !"await": @@ -532,10 +535,12 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = var dummy = newNimNode(nnkStmtList) createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy) else: - result = node for i in 0 .. Date: Sun, 16 Feb 2014 13:55:38 +0000 Subject: [PATCH 042/249] Added await test. --- tests/async/tasyncawait.nim | 65 +++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/async/tasyncawait.nim diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim new file mode 100644 index 000000000..84a4164d9 --- /dev/null +++ b/tests/async/tasyncawait.nim @@ -0,0 +1,65 @@ +discard """ + file: "tasyncawait.nim" + cmd: "nimrod cc --hints:on $# $#" + output: "5000" +""" +import asyncio2, sockets2, net, strutils + +var disp = newDispatcher() +var msgCount = 0 + +const + swarmSize = 50 + messagesToSend = 100 + +var clientCount = 0 + +proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} = + for i in 0 .. Date: Sun, 16 Feb 2014 19:17:12 +0100 Subject: [PATCH 043/249] fixes #922 --- compiler/vmgen.nim | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a6d044e24..6fca7f907 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -321,9 +321,18 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = c.gen(n.sons[2], dest) c.patch(L1) +proc nilLiteral(n: PNode): PNode = + if n.kind == nkNilLit and n.typ.sym != nil and + n.typ.sym.magic == mPNimrodNode: + let nilo = newNodeIT(nkNilLit, n.info, n.typ) + result = newNodeIT(nkMetaNode, n.info, n.typ) + result.add nilo + else: + result = n + proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len - c.constants.add n + c.constants.add n.nilLiteral internalAssert result < 0x7fff proc sameConstant*(a, b: PNode): bool = @@ -1109,7 +1118,12 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkFloatLit, info, t) of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef: - result = newNodeIT(nkNilLit, info, t) + if t.sym != nil and t.sym.magic == mPNimrodNode: + let nilo = newNodeIT(nkNilLit, info, t) + result = newNodeIT(nkMetaNode, info, t) + result.add nilo + else: + result = newNodeIT(nkNilLit, info, t) of tyProc: if t.callConv != ccClosure: result = newNodeIT(nkNilLit, info, t) From 17ef758cc3b08a19eb80e33fd26b5d3bafd22d4c Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 16 Feb 2014 19:56:17 +0000 Subject: [PATCH 044/249] Fix processing of 'await' with a nnkCall. Specifically, ``discard readMessages(disp, await disp.accept(server))`` works now, i.e. using 'await' as one of the params to a proc call. --- lib/pure/asyncio2.nim | 18 +++++++++++------- tests/async/tasyncawait.nim | 3 +-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index d3e4bcc98..8541b2ba7 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -498,10 +498,14 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = of nnkCommand: if node[0].ident == !"await": case node[1].kind - of nnkIdent, nnkCall: + of nnkIdent: # await x - # await foo(p, x) result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x + of nnkCall: + # await foo(p, x) + var futureValue: PNimrodNode + createVar("future" & $node[1][0].toStrLit, node[1], futureValue) + result.add futureValue else: error("Invalid node kind in 'await', got: " & $node[1].kind) elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and @@ -510,6 +514,7 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = var newCommand = node createVar("future" & $node[0].ident, node[1][0], newCommand[1]) result.add newCommand + of nnkVarSection, nnkLetSection: case node[0][2].kind of nnkCommand: @@ -534,11 +539,10 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = if node[0][0].ident == !"await": var dummy = newNimNode(nnkStmtList) createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy) - else: - for i in 0 .. Date: Mon, 17 Feb 2014 00:47:45 +0200 Subject: [PATCH 045/249] fix #188 --- compiler/msgs.nim | 4 +++- compiler/semstmts.nim | 24 ++++++++++++++++++++++++ tests/generics/tmetafield.nim | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/generics/tmetafield.nim diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 61336aa87..268205361 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -88,7 +88,8 @@ type errTemplateInstantiationTooNested, errInstantiationFrom, errInvalidIndexValueForTuple, errCommandExpectsFilename, errMainModuleMustBeSpecified, - errXExpected, + errXExpected, + errTIsNotAConcreteType, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, @@ -312,6 +313,7 @@ const errCommandExpectsFilename: "command expects a filename argument", errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errXExpected: "\'$1\' expected", + errTIsNotAConcreteType: "\'$1\' is not a concrete type.", errInvalidSectionStart: "invalid section start", errGridTableNotImplemented: "grid table is not implemented", errGeneralParseError: "general parse error", diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 0871b7fb7..e592b1e81 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -764,6 +764,28 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = s.ast = a popOwner() +proc checkForMetaFields(n: PNode) = + template checkMeta(t) = + if t.isMetaType and tfGenericTypeParam notin t.flags: + localError(n.info, errTIsNotAConcreteType, t.typeToString) + + case n.kind + of nkRecList, nkRecCase: + for s in n: checkForMetaFields(s) + of nkOfBranch, nkElse: + checkForMetaFields(n.lastSon) + of nkSym: + let t = n.sym.typ + case t.kind + of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, + tyProc, tyGenericInvokation, tyGenericInst: + for s in t.sons: + checkMeta(s) + else: + checkMeta(t) + else: + internalAssert false + proc typeSectionFinalPass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] @@ -780,6 +802,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assignType(s.typ, t) s.typ.id = t.id # same id checkConstructedType(s.info, s.typ) + if s.typ.kind in {tyObject, tyTuple}: + checkForMetaFields(s.typ.n) let aa = a.sons[2] if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and aa.sons[0].kind == nkObjectTy: diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim new file mode 100644 index 000000000..5d2ec9b33 --- /dev/null +++ b/tests/generics/tmetafield.nim @@ -0,0 +1,18 @@ +discard """ + cmd: "nimrod check $# $#" + msg: "'proc' is not a concrete type" + msg: "'seq[Foo]' is not a concrete type." + msg: "invalid type: 'TBaseMed'" +""" + +type + Foo[T] = object + x: T + + TBaseMed = object + doSmth: proc + data: seq[Foo] + +var a: TBaseMed + +# issue 188 From 9dd753f218cb3f8a3460d66cf4f917fab74eb233 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 17 Feb 2014 00:59:18 +0200 Subject: [PATCH 046/249] quite messy implementation of generic lambdas, needs reworking; fixes #715 --- compiler/sem.nim | 1 + compiler/semdata.nim | 1 + compiler/semexprs.nim | 4 +- compiler/semstmts.nim | 63 ++++++++++++++++++++++++------- compiler/semtypinst.nim | 21 ++++++++--- compiler/sigmatch.nim | 9 +++-- compiler/types.nim | 3 +- tests/generics/tgenericlambda.nim | 10 +++++ tests/generics/tmetafield.nim | 2 +- 9 files changed, 87 insertions(+), 27 deletions(-) create mode 100644 tests/generics/tgenericlambda.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index 140687721..09b2511f1 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -333,6 +333,7 @@ proc myOpen(module: PSym): PPassContext = c.semOperand = semOperand c.semConstBoolExpr = semConstBoolExpr c.semOverloadedCall = semOverloadedCall + c.semInferredLambda = semInferredLambda c.semGenerateInstance = generateInstance c.semTypeNode = semTypeNode pushProcCon(c, module) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 234526054..df58c896f 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -81,6 +81,7 @@ type semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, filter: TSymKinds): PNode {.nimcall.} semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.} + semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym includedFiles*: TIntSet # used to detect recursive include files diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f09ee1295..c271911ab 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1829,8 +1829,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = symChoice(c, n, s, scClosed) if result.kind == nkSym: markIndirect(c, result.sym) - if isGenericRoutine(result.sym): - localError(n.info, errInstantiateXExplicitely, s.name.s) + # if isGenericRoutine(result.sym): + # localError(n.info, errInstantiateXExplicitely, s.name.s) of nkSym: # because of the changed symbol binding, this does not mean that we # don't have to check the symbol for semantics here again! diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e592b1e81..ee085a821 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -766,7 +766,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = proc checkForMetaFields(n: PNode) = template checkMeta(t) = - if t.isMetaType and tfGenericTypeParam notin t.flags: + if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags: localError(n.info, errTIsNotAConcreteType, t.typeToString) case n.kind @@ -779,8 +779,9 @@ proc checkForMetaFields(n: PNode) = case t.kind of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef, tyProc, tyGenericInvokation, tyGenericInst: - for s in t.sons: - checkMeta(s) + let start = ord(t.kind in {tyGenericInvokation, tyGenericInst}) + for i in start .. 0 and n.sons[genericParamsPos].kind == nkEmpty: + # we have a list of implicit type parameters: + n.sons[genericParamsPos] = gp else: s.typ = newTypeS(tyProc, c) rawAddSon(s.typ, nil) @@ -924,12 +932,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) #if efDetermineType notin flags: # XXX not good enough; see tnamedparamanonproc.nim - pushProcCon(c, s) - addResult(c, s.typ.sons[0], n.info, skProc) - let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) - n.sons[bodyPos] = transformBody(c.module, semBody, s) - addResultNode(c, n) - popProcCon(c) + if n.sons[genericParamsPos].kind == nkEmpty: + pushProcCon(c, s) + addResult(c, s.typ.sons[0], n.info, skProc) + let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) + n.sons[bodyPos] = transformBody(c.module, semBody, s) + addResultNode(c, n) + popProcCon(c) sideEffectsCheck(c, s) else: localError(n.info, errImplOfXexpected, s.name.s) @@ -937,6 +946,34 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = popOwner() result.typ = s.typ +proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = + var n = n + + n = replaceTypesInBody(c, pt, n) + result = n + + n.sons[genericParamsPos] = emptyNode + n.sons[paramsPos] = n.typ.n + + openScope(c) + var s = n.sons[namePos].sym + addParams(c, n.typ.n, skProc) + pushProcCon(c, s) + addResult(c, n.typ.sons[0], n.info, skProc) + let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) + n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym) + addResultNode(c, n) + popProcCon(c) + closeScope(c) + + s.ast = result + + # alternative variant (not quite working): + # var prc = arg[0].sym + # let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) + # result = inferred.ast + # result.kind = arg.kind + proc activate(c: PContext, n: PNode) = # XXX: This proc is part of my plan for getting rid of # forward declarations. stay tuned. diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 73b618f46..f08214f1e 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -29,6 +29,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) = localError(info, errVarVarTypeNotAllowed) elif computeSize(t) == szIllegalRecursion: localError(info, errIllegalRecursionInTypeX, typeToString(t)) + when false: if t.kind == tyObject and t.sons[0] != nil: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: @@ -409,14 +410,22 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = else: discard +proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars = + initIdTable(result.symMap) + copyIdTable(result.typeMap, pt) + initIdTable(result.localCache) + result.info = info + result.c = p + +proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode = + var cl = initTypeVars(p, pt, n.info) + pushInfoContext(n.info) + result = replaceTypeVarsN(cl, n) + popInfoContext() + proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = - var cl: TReplTypeVars - initIdTable(cl.symMap) - copyIdTable(cl.typeMap, pt) - initIdTable(cl.localCache) - cl.info = info - cl.c = p + var cl = initTypeVars(p, pt, info) pushInfoContext(info) result = replaceTypeVarsT(cl, t) popInfoContext() diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 8361d4681..5fe474ef3 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1039,10 +1039,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, #result = copyTree(arg) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) of isInferred, isInferredConvertible: - var prc = if arg.kind in nkLambdaKinds: arg[0].sym - else: arg.sym - let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info) - result = newSymNode(inferred, arg.info) + if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: + result = c.semInferredLambda(c, m.bindings, arg) + else: + let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) + result = newSymNode(inferred, arg.info) if r == isInferredConvertible: result = implicitConv(nkHiddenStdConv, f, result, m, c) of isGeneric: diff --git a/compiler/types.nim b/compiler/types.nim index 55a89920a..d7148f110 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1042,7 +1042,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, of tyEmpty: result = taField in flags of tyTypeClasses: - result = true + result = tfGenericTypeParam in t.flags or + taField notin flags of tyGenericBody, tyGenericParam, tyGenericInvokation, tyNone, tyForward, tyFromExpr, tyFieldAccessor: result = false diff --git a/tests/generics/tgenericlambda.nim b/tests/generics/tgenericlambda.nim new file mode 100644 index 000000000..a71c592c5 --- /dev/null +++ b/tests/generics/tgenericlambda.nim @@ -0,0 +1,10 @@ +discard """ + output: "10\n10" +""" + +proc test(x: proc (a, b: int): int) = + echo x(5, 5) + +test(proc (a, b): auto = a + b) + +test do (a, b) -> auto: a + b diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim index 5d2ec9b33..42353006d 100644 --- a/tests/generics/tmetafield.nim +++ b/tests/generics/tmetafield.nim @@ -1,7 +1,7 @@ discard """ cmd: "nimrod check $# $#" msg: "'proc' is not a concrete type" - msg: "'seq[Foo]' is not a concrete type." + msg: "'Foo' is not a concrete type." msg: "invalid type: 'TBaseMed'" """ From 350a49cc26803bc38a3c83d469f8deaeecf0f112 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 17 Feb 2014 07:52:41 +0100 Subject: [PATCH 047/249] fixes #923 --- compiler/vm.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 81e712047..0929e072b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1025,7 +1025,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = regs[ra].sons[0].flags.incl nfIsRef of opcNCopyNimNode: decodeB(nkMetaNode) - setMeta(regs[ra], copyNode(regs[rb])) + setMeta(regs[ra], copyNode(regs[rb].skipMeta)) of opcNCopyNimTree: decodeB(nkMetaNode) setMeta(regs[ra], copyTree(regs[rb])) From 3ec29738757c4b8eb0a7071333a0351d802e30a6 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 17 Feb 2014 08:26:44 +0100 Subject: [PATCH 048/249] fixes #926 --- compiler/vmgen.nim | 19 ++++++++++++------- tests/macros/tvarnimnode.nim | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 tests/macros/tvarnimnode.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 6fca7f907..80cf4a9bf 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -916,23 +916,28 @@ proc requiresCopy(n: PNode): bool = proc unneededIndirection(n: PNode): bool = n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef -proc skipDeref(n: PNode): PNode = - if n.kind in {nkDerefExpr, nkHiddenDeref} and unneededIndirection(n.sons[0]): - result = n.sons[0] - else: - result = n - proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = # a nop for certain types let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags - if unneededIndirection(n.sons[0]): + # consider: + # proc foo(f: var ref int) = + # f = new(int) + # proc blah() = + # var x: ref int + # foo x + # + # The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for + # nkAddr we must not use 'unneededIndirection', but for deref we use it. + if opc != opcAddr and unneededIndirection(n.sons[0]): gen(c, n.sons[0], dest, flags) + message(n.info, warnUser, "YES") else: let tmp = c.genx(n.sons[0], flags) if dest < 0: dest = c.getTemp(n.typ) gABC(c, n, opc, dest, tmp) c.freeTemp(tmp) + message(n.info, warnUser, "NO") proc whichAsgnOpc(n: PNode): TOpcode = case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind diff --git a/tests/macros/tvarnimnode.nim b/tests/macros/tvarnimnode.nim new file mode 100644 index 000000000..73fcc16ea --- /dev/null +++ b/tests/macros/tvarnimnode.nim @@ -0,0 +1,19 @@ +discard """ + output: 10 +""" + +#bug #926 + +import macros + +proc test(f: var PNimrodNode) {.compileTime.} = + f = newNimNode(nnkStmtList) + f.add newCall(newIdentNode("echo"), newLit(10)) + +macro blah(prc: stmt): stmt = + result = prc + + test(result) + +proc test() {.blah.} = + echo 5 From 14bdedb0f7a8dbd15d685866fc022ad559a4e35b Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 17 Feb 2014 08:27:16 +0100 Subject: [PATCH 049/249] got rid of debugging code --- compiler/vmgen.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 80cf4a9bf..206cca0d7 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -931,13 +931,11 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; # nkAddr we must not use 'unneededIndirection', but for deref we use it. if opc != opcAddr and unneededIndirection(n.sons[0]): gen(c, n.sons[0], dest, flags) - message(n.info, warnUser, "YES") else: let tmp = c.genx(n.sons[0], flags) if dest < 0: dest = c.getTemp(n.typ) gABC(c, n, opc, dest, tmp) c.freeTemp(tmp) - message(n.info, warnUser, "NO") proc whichAsgnOpc(n: PNode): TOpcode = case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind From eb3958f1b9c36f7308abaaeb8b52441fa0674f1a Mon Sep 17 00:00:00 2001 From: vocalbit Date: Mon, 17 Feb 2014 00:35:35 -0800 Subject: [PATCH 050/249] Add two news items to website. --- web/news.txt | 141 +++++++++++++++++++++++++++---------------------- web/ticker.txt | 10 ++++ 2 files changed, 88 insertions(+), 63 deletions(-) diff --git a/web/news.txt b/web/news.txt index b28e5c64a..c84a1001c 100644 --- a/web/news.txt +++ b/web/news.txt @@ -3,88 +3,103 @@ News ==== -2014-XX-XX Version 0.9.4 released -================================= +.. + 2014-XX-XX Version 0.9.4 released + ================================= -Bugfixes --------- + Bugfixes + -------- -Library Additions ------------------ + Library Additions + ----------------- -- Added ``macros.genSym`` builtin for AST generation. -- Added ``macros.newLit`` procs for easier AST generation. + - Added ``macros.genSym`` builtin for AST generation. + - Added ``macros.newLit`` procs for easier AST generation. -Changes affecting backwards compatibility ------------------------------------------ + Changes affecting backwards compatibility + ----------------------------------------- -- The scoping rules for the ``if`` statement changed for better interaction - with the new syntactic construct ``(;)``. -- ``OSError`` family of procedures has been deprecated. Procedures with the same - name but which take different parameters have been introduced. These procs now - require an error code to be passed to them. This error code can be retrieved - using the new ``OSLastError`` proc. -- ``os.parentDir`` now returns "" if there is no parent dir. -- In CGI scripts stacktraces are shown to the user only - if ``cgi.setStackTraceStdout`` is used. -- The symbol binding rules for clean templates changed: ``bind`` for any - symbol that's not a parameter is now the default. ``mixin`` can be used - to require instantiation scope for a symbol. -- ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely - passed to shell, instead of just adding double quotes. -- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``, - ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated. -- The ``nil`` statement has been deprecated, use an empty ``discard`` instead. -- ``sockets.select`` now prunes sockets that are **not** ready from the list - of sockets given to it. + - The scoping rules for the ``if`` statement changed for better interaction + with the new syntactic construct ``(;)``. + - ``OSError`` family of procedures has been deprecated. Procedures with the same + name but which take different parameters have been introduced. These procs now + require an error code to be passed to them. This error code can be retrieved + using the new ``OSLastError`` proc. + - ``os.parentDir`` now returns "" if there is no parent dir. + - In CGI scripts stacktraces are shown to the user only + if ``cgi.setStackTraceStdout`` is used. + - The symbol binding rules for clean templates changed: ``bind`` for any + symbol that's not a parameter is now the default. ``mixin`` can be used + to require instantiation scope for a symbol. + - ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely + passed to shell, instead of just adding double quotes. + - ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``, + ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated. + - The ``nil`` statement has been deprecated, use an empty ``discard`` instead. + - ``sockets.select`` now prunes sockets that are **not** ready from the list + of sockets given to it. -Compiler Additions ------------------- + Compiler Additions + ------------------ -- The compiler can now warn about "uninitialized" variables. (There are no - real uninitialized variables in Nimrod as they are initialized to binary - zero). Activate via ``{.warning[Uninit]:on.}``. -- The compiler now enforces the ``not nil`` constraint. -- The compiler now supports a ``codegenDecl`` pragma for even more control - over the generated code. -- The compiler now supports a ``computedGoto`` pragma to support very fast - dispatching for interpreters and the like. -- The old evaluation engine has been replaced by a proper register based - virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro - evaluation. -- ``--gc:none`` produces warnings when code uses the GC. + - The compiler can now warn about "uninitialized" variables. (There are no + real uninitialized variables in Nimrod as they are initialized to binary + zero). Activate via ``{.warning[Uninit]:on.}``. + - The compiler now enforces the ``not nil`` constraint. + - The compiler now supports a ``codegenDecl`` pragma for even more control + over the generated code. + - The compiler now supports a ``computedGoto`` pragma to support very fast + dispatching for interpreters and the like. + - The old evaluation engine has been replaced by a proper register based + virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro + evaluation. + - ``--gc:none`` produces warnings when code uses the GC. -Language Additions ------------------- + Language Additions + ------------------ -- Arrays can now be declared with a single integer literal ``N`` instead of a - range; the range is then ``0..N-1``. -- Added ``requiresInit`` pragma to enforce explicit initialization. -- Exported templates are allowed to access hidden fields. -- The ``using statement`` enables you to more easily author domain-specific - languages and libraries providing OOP-like syntactic sugar. -- Added the possibility to override various dot operators in order to handle - calls to missing procs and reads from undeclared fields at compile-time. -- The overload resolution now supports ``static[T]`` params that must be - evaluable at compile-time. -- Support for user-defined type classes has been added. -- The *command syntax* is supported in a lot more contexts. -- Anonymous iterators are now supported and iterators can capture variables - of an outer proc. + - Arrays can now be declared with a single integer literal ``N`` instead of a + range; the range is then ``0..N-1``. + - Added ``requiresInit`` pragma to enforce explicit initialization. + - Exported templates are allowed to access hidden fields. + - The ``using statement`` enables you to more easily author domain-specific + languages and libraries providing OOP-like syntactic sugar. + - Added the possibility to override various dot operators in order to handle + calls to missing procs and reads from undeclared fields at compile-time. + - The overload resolution now supports ``static[T]`` params that must be + evaluable at compile-time. + - Support for user-defined type classes has been added. + - The *command syntax* is supported in a lot more contexts. + - Anonymous iterators are now supported and iterators can capture variables + of an outer proc. -Tools improvements ------------------- + Tools improvements + ------------------ -- c2nim can deal with a subset of C++. Use the ``--cpp`` command line option - to activate. + - c2nim can deal with a subset of C++. Use the ``--cpp`` command line option + to activate. +2014-02-11 Nimrod Featured in Dr. Dobb's +======================================== + +Nimrod has been `featured`_ +as the cover story in the February 2014 issue of Dr. Dobb's Journal. + +2014-01-15 Nimrod Talk is Online +================================ + +Andreas Rumpf presented *Nimrod: A New Approach to Metaprogramming* at +`Strange Loop 2013`_. +The `video and slides`_ +of the talk are now available. + 2013-05-20 New website design! ============================== diff --git a/web/ticker.txt b/web/ticker.txt index 00bc6a125..e99987903 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,3 +1,13 @@ + +

Feb 11, 2014

+

Nimrod in Dr. Dobb's

+
+ + +

Jan 15, 2014

+

Nimrod Video is Online

+
+

May 20, 2013

New website design!

From a6c825709c588897f8ed43689aad72ed19e68a58 Mon Sep 17 00:00:00 2001 From: vocalbit Date: Mon, 17 Feb 2014 00:47:05 -0800 Subject: [PATCH 051/249] Fix website news formatting and grammar to be consistent. --- web/news.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/news.txt b/web/news.txt index c84a1001c..00ec4bc0c 100644 --- a/web/news.txt +++ b/web/news.txt @@ -91,7 +91,8 @@ News Nimrod has been `featured`_ as the cover story in the February 2014 issue of Dr. Dobb's Journal. - + + 2014-01-15 Nimrod Talk is Online ================================ @@ -99,7 +100,8 @@ Andreas Rumpf presented *Nimrod: A New Approach to Metaprogramming* at `Strange Loop 2013`_. The `video and slides`_ of the talk are now available. - + + 2013-05-20 New website design! ============================== From 6c908fbaf15ae28f8b4c6e67cfb0b4a58053e705 Mon Sep 17 00:00:00 2001 From: vocalbit Date: Mon, 17 Feb 2014 00:48:21 -0800 Subject: [PATCH 052/249] Make ticker puntuation consistent. --- web/ticker.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/ticker.txt b/web/ticker.txt index e99987903..1e9673745 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,11 +1,11 @@

Feb 11, 2014

-

Nimrod in Dr. Dobb's

+

Nimrod in Dr. Dobb's.

Jan 15, 2014

-

Nimrod Video is Online

+

Nimrod video is online.

From eaab22089da855163485949d8bd6a6ae9c1d25c8 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 17 Feb 2014 20:44:11 +0200 Subject: [PATCH 053/249] fix #807 --- compiler/semstmts.nim | 4 ++-- tests/destructor/tdestructor.nim | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ee085a821..503ea4bc1 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1324,8 +1324,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = let (outer, inner) = insertDestructors(c, n.sons[i]) if outer != nil: n.sons[i] = outer - for j in countup(i+1, length-1): - inner.addSon(semStmt(c, n.sons[j])) + var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1]) + inner.addSon(semStmtList(c, rest, flags)) n.sons.setLen(i+1) return of LastBlockStmts: diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim index f10c0cc9c..e5236aaab 100644 --- a/tests/destructor/tdestructor.nim +++ b/tests/destructor/tdestructor.nim @@ -80,13 +80,13 @@ proc mygeneric1() = echo "mygeneric1 constructed" proc mygeneric2[T](val: T) = - var - a = open() - b = TMyGeneric2[int, T](x: 10, y: val) - c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test") - + var a = open() + + var b = TMyGeneric2[int, T](x: 10, y: val) echo "mygeneric2 constructed" + var c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test") + proc mygeneric3 = var x = TMyGeneric3[int, string, TMyGeneric1[int]]( x: 10, y: "test", z: TMyGeneric1[int](x: 10)) From 420d4197f139c277f7b7d0eaf462ed0fe9e00745 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 17 Feb 2014 23:59:32 +0100 Subject: [PATCH 054/249] todo.txt changes --- todo.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/todo.txt b/todo.txt index 2e6eb708b..656bd06fc 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,10 @@ version 0.9.4 ============= +- fix macros\tstringinterp.nim: + - problem: needs another level of indirection for 'seq' + - problem: deref is not correct - fix GC issues -- fix macros\tstringinterp.nim - test and fix showoff From 765c682c92f27e13d079b146941033d7b68e261b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 18 Feb 2014 01:18:59 +0200 Subject: [PATCH 055/249] fix #204; --- compiler/msgs.nim | 2 ++ compiler/semstmts.nim | 7 ++++++- lib/pure/sockets2.nim | 8 ++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 268205361..0140d1ac4 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -104,6 +104,7 @@ type errXhasSideEffects, errIteratorExpected, errLetNeedsInit, errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errXCannotBeClosure, errXMustBeCompileTime, + errCannotInferTypeOfTheLiteral, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -348,6 +349,7 @@ const errIllegalCaptureX: "illegal capture '$1'", errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXMustBeCompileTime: "'$1' can only be used in compile-time context", + errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 503ea4bc1..a9a907953 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -349,7 +349,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # BUGFIX: ``fitNode`` is needed here! # check type compability between def.typ and typ: if typ != nil: def = fitNode(c, typ, def) - else: typ = skipIntLit(def.typ) + else: + typ = skipIntLit(def.typ) + if typ.kind in {tySequence, tyArray, tySet} and + typ.lastSon.kind == tyEmpty: + localError(def.info, errCannotInferTypeOfTheLiteral, + ($typ.kind).substr(2).toLower) else: def = ast.emptyNode if symkind == skLet: localError(a.info, errLetNeedsInit) diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim index 22624bbad..f8284b339 100644 --- a/lib/pure/sockets2.nim +++ b/lib/pure/sockets2.nim @@ -89,7 +89,7 @@ when defined(posix): of AF_UNIX: result = posix.AF_UNIX of AF_INET: result = posix.AF_INET of AF_INET6: result = posix.AF_INET6 - else: nil + else: discard proc toInt(typ: TType): cint = case typ @@ -97,7 +97,7 @@ when defined(posix): of SOCK_DGRAM: result = posix.SOCK_DGRAM of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET of SOCK_RAW: result = posix.SOCK_RAW - else: nil + else: discard proc toInt(p: TProtocol): cint = case p @@ -107,7 +107,7 @@ when defined(posix): of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 of IPPROTO_RAW: result = posix.IPPROTO_RAW of IPPROTO_ICMP: result = posix.IPPROTO_ICMP - else: nil + else: discard else: proc toInt(domain: TDomain): cint = @@ -199,4 +199,4 @@ proc htons*(x: int16): int16 = when defined(Windows): var wsa: TWSADATA - if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) \ No newline at end of file + if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) From b01945a061f495099fa8bbcd1d481a88ae56fa9c Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 17 Feb 2014 23:22:29 +0000 Subject: [PATCH 056/249] Modified website news titles and made ticker titles consistent. --- web/news.txt | 8 ++++---- web/ticker.txt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/web/news.txt b/web/news.txt index 00ec4bc0c..187797b58 100644 --- a/web/news.txt +++ b/web/news.txt @@ -86,15 +86,15 @@ News to activate. -2014-02-11 Nimrod Featured in Dr. Dobb's -======================================== +2014-02-11 Nimrod Featured in Dr. Dobb's Journal +================================================ Nimrod has been `featured`_ as the cover story in the February 2014 issue of Dr. Dobb's Journal. -2014-01-15 Nimrod Talk is Online -================================ +2014-01-15 Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online +============================================================================ Andreas Rumpf presented *Nimrod: A New Approach to Metaprogramming* at `Strange Loop 2013`_. diff --git a/web/ticker.txt b/web/ticker.txt index 1e9673745..07d7f5043 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,11 +1,11 @@

Feb 11, 2014

-

Nimrod in Dr. Dobb's.

+

Nimrod Featured in Dr. Dobb's Journal

Jan 15, 2014

-

Nimrod video is online.

+

Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online.

From 4d3846e26b38ddf7fade05489f0f3335db057950 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 18 Feb 2014 01:37:00 +0200 Subject: [PATCH 057/249] fix tbindtypedesc and tactiontable2 --- compiler/sigmatch.nim | 17 ++++++++++++----- tests/actiontable/tactiontable2.nim | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 5fe474ef3..240145118 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -900,20 +900,27 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone of tyTypeDesc: - if a.kind != tyTypeDesc: return isNone - var prev = PType(idTableGet(c.bindings, f)) if prev == nil: + # proc foo(T: typedesc, x: T) + # when `f` is an unresolved typedesc, `a` could be any + # type, so we should not perform this check earlier + if a.kind != tyTypeDesc: return isNone + if f.base.kind == tyNone: result = isGeneric else: result = typeRel(c, f.base, a.base) + if result != isNone: put(c.bindings, f, a) else: - let toMatch = if tfUnresolved in f.flags: a - else: a.base - result = typeRel(c, prev.base, toMatch) + if tfUnresolved in f.flags: + result = typeRel(c, prev.base, a) + elif a.kind == tyTypeDesc: + result = typeRel(c, prev.base, a.base) + else: + result = isNone of tyStmt: result = isGeneric diff --git a/tests/actiontable/tactiontable2.nim b/tests/actiontable/tactiontable2.nim index 00b427603..878356321 100644 --- a/tests/actiontable/tactiontable2.nim +++ b/tests/actiontable/tactiontable2.nim @@ -1,6 +1,6 @@ discard """ line: 21 - errormsg: "invalid type: 'TTable'" + errormsg: "invalid type: 'TTable[string, proc (string)]'" """ import tables From 4cd558bdadaf6ba3b51d21b5bc62322a821ac5e0 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 18 Feb 2014 00:04:09 +0000 Subject: [PATCH 058/249] Improved community page and fixed ticker links. --- web/community.txt | 58 ++++++++++++++++++++++++++++++++++++++++------- web/ticker.txt | 6 ++--- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/web/community.txt b/web/community.txt index b9a0a4196..d45e7df61 100644 --- a/web/community.txt +++ b/web/community.txt @@ -1,15 +1,55 @@ -Discuss Nimrod in our `forum `_. +Forum +===== -Visit our project page at GitHub: http://github.com/Araq/Nimrod. +The `Nimrod forum `_ is the place where most +discussions related to the language happen. It not only includes discussions +relating to the design of Nimrod but also allows for beginners to ask questions +relating to Nimrod. -Wiki: http://github.com/Araq/Nimrod/wiki. +IRC +==== -Bug reports: http://github.com/Araq/Nimrod/issues. +Many Nimrod developers are a part of the +`#nimrod IRC channel `_ on +Freenode. That is the place where the rest of the discussion relating to Nimrod +occurs. Be sure to join us there if you wish to discuss Nimrod in real-time. +IRC is the perfect place for people just starting to learn Nimrod and we +welcome any questions that you may have! -For quickest feedback, join our IRC channel: irc://irc.freenode.net/nimrod -(logs at ``_). +You may also be interested in reading the +`IRC logs `_ which are an archive of all +of the previous discussions that took place in the IRC channel. -Check out our Twitter account for latest news and announcements: `@nimrodlang `_. +Github +====== + +Nimrod's `source code `_ is hosted on Github. +Together with the `wiki `_ and +`issue tracker `_. + +Github also hosts other projects relating to Nimrod. These projects are a part +of the `nimrod-code organisation `_. +This includes the `Babel package manager `_ +and its `package repository `_. + +Twitter +======= + +Follow us `@nimrodlang `_ for latest news about +Nimrod. + +Reddit +====== + +Subscribe to `/r/nimrod `_ for latest news about +Nimrod. + +StackOverflow +============= + +When asking a question relating to Nimrod, be sure to use the +`Nimrod `_ tag in your +question. How to help =========== @@ -26,7 +66,9 @@ can't find anything you fancy doing, you can always ask for inspiration on IRC Donations --------- -If you love what we do and are feeling generous then you can always donate: +If you love what we do and are feeling generous then you can always donate. +Contributions of any quantity are greatly appreciated and will contribute to +making Nimrod even better! Gittip `````` diff --git a/web/ticker.txt b/web/ticker.txt index 07d7f5043..a989238e1 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,14 +1,14 @@ - +

Feb 11, 2014

Nimrod Featured in Dr. Dobb's Journal

- +

Jan 15, 2014

Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online.

- +

May 20, 2013

New website design!

From 1ac7f369525331f404c74573850a60b4314a5916 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 18 Feb 2014 00:08:31 +0000 Subject: [PATCH 059/249] Fix line-height for h1 in website. --- web/assets/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/assets/style.css b/web/assets/style.css index 715214a1a..5cee279fc 100644 --- a/web/assets/style.css +++ b/web/assets/style.css @@ -65,7 +65,7 @@ html, body { #page { position:relative; float:left; padding:20px 30px 50px 50px; width:620px; color:#343739; } - #page h1 { margin-top:40px; } + #page h1 { margin-top:40px; line-height: 28px; } #page h2 { margin-top:40px; } #page p { text-align:justify; } From 866205cba4c9ac9a5831afe5ffd997eec5c9bebf Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 18 Feb 2014 00:18:34 +0000 Subject: [PATCH 060/249] Added google analytics to website. --- tools/website.tmpl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/website.tmpl b/tools/website.tmpl index 091079c1c..08c0b450c 100644 --- a/tools/website.tmpl +++ b/tools/website.tmpl @@ -74,5 +74,15 @@ + From 15953dfed952cf2a54f6466b335abcbf676de141 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 18 Feb 2014 00:27:40 +0000 Subject: [PATCH 061/249] Make ticker title consistent. --- web/ticker.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/ticker.txt b/web/ticker.txt index a989238e1..844ed3033 100644 --- a/web/ticker.txt +++ b/web/ticker.txt @@ -1,6 +1,6 @@

Feb 11, 2014

-

Nimrod Featured in Dr. Dobb's Journal

+

Nimrod featured in Dr. Dobb's Journal

From cda92048ba9408d356e0023543fcfb45ea357da8 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 18 Feb 2014 02:46:14 +0200 Subject: [PATCH 062/249] fix some trivial errors in the test suite and some more regressions caused by tyTypeDesc[tyNone] --- compiler/semtypes.nim | 5 +-- lib/system/jssys.nim | 59 +++++++++++++++---------------- tests/generics/tgenericlambda.nim | 10 +++++- tests/global/globalaux.nim | 15 ++++++++ tests/global/globalaux2.nim | 4 +++ tests/module/trecinca.nim | 2 +- tests/module/trecincb.nim | 2 +- tests/stdlib/tircbot.nim | 2 +- tests/{ => template}/sunset.tmpl | 0 tests/typerel/tvoid.nim | 6 +++- 10 files changed, 68 insertions(+), 37 deletions(-) create mode 100644 tests/global/globalaux.nim create mode 100644 tests/global/globalaux2.nim rename tests/{ => template}/sunset.tmpl (100%) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 184aca4f8..98abaf005 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -669,7 +669,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyTypeDesc: if tfUnresolved notin paramType.flags: # naked typedescs are not bindOnce types - if paramType.sonsLen == 0 and paramTypId != nil and + if paramType.base.kind == tyNone and paramTypId != nil and paramTypId.id == typedescId.id: paramTypId = nil result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) @@ -1011,7 +1011,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mOrdinal: result = semOrdinal(c, n, prev) of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mVarargs: result = semVarargs(c, n, prev) - of mExpr, mTypeDesc: + of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: result = copyType(result, getCurrOwner(), false) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 4fc5f479b..0714f4589 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -418,76 +418,75 @@ proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} = return Math.floor(`a` % `b`); """ -proc NegInt(a: int): int {.compilerproc.} = +proc negInt(a: int): int {.compilerproc.} = result = a*(-1) -proc NegInt64(a: int64): int64 {.compilerproc.} = +proc negInt64(a: int64): int64 {.compilerproc.} = result = a*(-1) -proc AbsInt(a: int): int {.compilerproc.} = +proc absInt(a: int): int {.compilerproc.} = result = if a < 0: a*(-1) else: a -proc AbsInt64(a: int64): int64 {.compilerproc.} = +proc absInt64(a: int64): int64 {.compilerproc.} = result = if a < 0: a*(-1) else: a -proc LeU(a, b: int): bool {.compilerproc.} = +proc leU(a, b: int): bool {.compilerproc.} = result = abs(a) <= abs(b) -proc LtU(a, b: int): bool {.compilerproc.} = +proc ltU(a, b: int): bool {.compilerproc.} = result = abs(a) < abs(b) -proc LeU64(a, b: int64): bool {.compilerproc.} = +proc leU64(a, b: int64): bool {.compilerproc.} = result = abs(a) <= abs(b) - -proc LtU64(a, b: int64): bool {.compilerproc.} = +proc ltU64(a, b: int64): bool {.compilerproc.} = result = abs(a) < abs(b) -proc AddU(a, b: int): int {.compilerproc.} = +proc addU(a, b: int): int {.compilerproc.} = result = abs(a) + abs(b) -proc AddU64(a, b: int64): int64 {.compilerproc.} = +proc addU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) + abs(b) -proc SubU(a, b: int): int {.compilerproc.} = +proc subU(a, b: int): int {.compilerproc.} = result = abs(a) - abs(b) -proc SubU64(a, b: int64): int64 {.compilerproc.} = +proc subU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) - abs(b) -proc MulU(a, b: int): int {.compilerproc.} = +proc mulU(a, b: int): int {.compilerproc.} = result = abs(a) * abs(b) -proc MulU64(a, b: int64): int64 {.compilerproc.} = +proc mulU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) * abs(b) -proc DivU(a, b: int): int {.compilerproc.} = +proc divU(a, b: int): int {.compilerproc.} = result = abs(a) div abs(b) -proc DivU64(a, b: int64): int64 {.compilerproc.} = +proc divU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) div abs(b) -proc ModU(a, b: int): int {.compilerproc.} = +proc modU(a, b: int): int {.compilerproc.} = result = abs(a) mod abs(b) -proc ModU64(a, b: int64): int64 {.compilerproc.} = +proc modU64(a, b: int64): int64 {.compilerproc.} = result = abs(a) mod abs(b) -proc Ze(a: int): int {.compilerproc.} = - result = a -proc Ze64(a: int64): int64 {.compilerproc.} = +proc ze*(a: int): int {.compilerproc.} = result = a -proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} = +proc ze64*(a: int64): int64 {.compilerproc.} = + result = a + +proc toU8*(a: int): int8 {.noStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} = +proc toU16*(a: int): int16 {.noStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} = +proc toU32*(a: int64): int32 {.noStackFrame, compilerproc.} = asm """ return `a`; """ - proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b @@ -500,9 +499,9 @@ proc isFatPointer(ti: PNimType): bool = tyArray, tyArrayConstr, tyTuple, tyOpenArray, tySet, tyVar, tyRef, tyPtr} -proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} +proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.} -proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = +proc nimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = case n.kind of nkNone: sysAssert(false, "NimCopyAux") of nkSlot: @@ -518,7 +517,7 @@ proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} = } """ -proc NimCopy(x: pointer, ti: PNimType): pointer = +proc nimCopy(x: pointer, ti: PNimType): pointer = case ti.kind of tyPtr, tyRef, tyVar, tyNil: if not isFatPointer(ti): @@ -585,7 +584,7 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} = else: result = nil -proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. +proc arrayConstr(len: int, value: pointer, typ: PNimType): pointer {. noStackFrame, compilerproc.} = # types are fake asm """ diff --git a/tests/generics/tgenericlambda.nim b/tests/generics/tgenericlambda.nim index a71c592c5..f7aafe1d9 100644 --- a/tests/generics/tgenericlambda.nim +++ b/tests/generics/tgenericlambda.nim @@ -1,5 +1,5 @@ discard """ - output: "10\n10" + output: "10\n10\n1\n2\n3" """ proc test(x: proc (a, b: int): int) = @@ -8,3 +8,11 @@ proc test(x: proc (a, b: int): int) = test(proc (a, b): auto = a + b) test do (a, b) -> auto: a + b + +proc foreach[T](s: seq[T], body: proc(x: T)) = + for e in s: + body(e) + +foreach(@[1,2,3]) do (x): + echo x + diff --git a/tests/global/globalaux.nim b/tests/global/globalaux.nim new file mode 100644 index 000000000..5f6f72721 --- /dev/null +++ b/tests/global/globalaux.nim @@ -0,0 +1,15 @@ +type + TObj*[T] = object + val*: T + +var + totalGlobals* = 0 + +proc makeObj[T](x: T): TObj[T] = + totalGlobals += 1 + result.val = x + +proc globalInstance*[T]: var TObj[T] = + var g {.global.} = when T is int: makeObj(10) else: makeObj("hello") + result = g + diff --git a/tests/global/globalaux2.nim b/tests/global/globalaux2.nim new file mode 100644 index 000000000..6c77f1f48 --- /dev/null +++ b/tests/global/globalaux2.nim @@ -0,0 +1,4 @@ +import globalaux + +echo "in globalaux2: ", globalInstance[int]().val + diff --git a/tests/module/trecinca.nim b/tests/module/trecinca.nim index 73a0ec937..62d37783c 100644 --- a/tests/module/trecinca.nim +++ b/tests/module/trecinca.nim @@ -1,7 +1,7 @@ discard """ file: "tests/reject/trecincb.nim" line: 9 - errormsg: "recursive dependency: 'tests/reject/trecincb.nim'" + errormsg: "recursive dependency: 'tests/module/trecincb.nim'" """ # Test recursive includes diff --git a/tests/module/trecincb.nim b/tests/module/trecincb.nim index 9dd7d51de..a2934052f 100644 --- a/tests/module/trecincb.nim +++ b/tests/module/trecincb.nim @@ -1,7 +1,7 @@ discard """ file: "trecincb.nim" line: 9 - errormsg: "recursive dependency: 'tests/reject/trecincb.nim'" + errormsg: "recursive dependency: 'tests/module/trecincb.nim'" """ # Test recursive includes diff --git a/tests/stdlib/tircbot.nim b/tests/stdlib/tircbot.nim index 71ecb0b48..f0417c7ac 100644 --- a/tests/stdlib/tircbot.nim +++ b/tests/stdlib/tircbot.nim @@ -183,7 +183,7 @@ type channel: string timestamp: TTime case kind*: TSeenType - of PSeenJoin: discard + of PSeenJoin: nil of PSeenPart, PSeenQuit, PSeenMsg: msg: string of PSeenNick: diff --git a/tests/sunset.tmpl b/tests/template/sunset.tmpl similarity index 100% rename from tests/sunset.tmpl rename to tests/template/sunset.tmpl diff --git a/tests/typerel/tvoid.nim b/tests/typerel/tvoid.nim index bb569e7f8..d31936217 100644 --- a/tests/typerel/tvoid.nim +++ b/tests/typerel/tvoid.nim @@ -1,5 +1,9 @@ discard """ - output: "he, no return type;abc a string" + output: '''12 +empty +he, no return type; +abc a string +ha''' """ proc ReturnT[T](x: T): T = From ab72377ce64cf2b563ea90204925b793082971cb Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 18 Feb 2014 09:57:59 +0100 Subject: [PATCH 063/249] renamed noStackFrame to asmNoStackFrame --- compiler/pragmas.nim | 10 ++++--- compiler/wordrecg.nim | 4 +-- doc/manual.txt | 10 +++---- lib/system.nim | 4 +-- lib/system/arithm.nim | 12 ++++---- lib/system/jssys.nim | 68 +++++++++++++++++++++---------------------- todo.txt | 4 --- web/news.txt | 2 ++ 8 files changed, 57 insertions(+), 57 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d9ed50cfe..602c70f4d 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -23,7 +23,7 @@ const wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, - wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, + wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, wGensym, wInject, wRaises, wTags, wOperator, wDelegator} converterPragmas* = procPragmas methodPragmas* = procPragmas @@ -47,7 +47,7 @@ const wInjectStmt} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, - wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wNoStackFrame, + wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wRaises, wTags} typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, @@ -548,9 +548,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, of wNodecl: noVal(it) incl(sym.loc.flags, lfNoDecl) - of wPure, wNoStackFrame: + of wPure, wAsmNoStackFrame: noVal(it) - if sym != nil: incl(sym.flags, sfPure) + if sym != nil: + if k == wPure and sym.kind in routineKinds: invalidPragma(it) + else: incl(sym.flags, sfPure) of wVolatile: noVal(it) incl(sym.flags, sfVolatile) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 837bb4f50..1c8e4516c 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -62,7 +62,7 @@ type wWatchPoint, wSubsChar, wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, - wNoStackFrame, + wAsmNoStackFrame, wImplicitStatic, wGlobal, wCodegenDecl, wAuto, wBool, wCatch, wChar, wClass, @@ -145,7 +145,7 @@ const "subschar", "acyclic", "shallow", "unroll", "linearscanend", "computedgoto", "injectstmt", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", - "nostackframe", "implicitstatic", "global", "codegendecl", + "asmnostackframe", "implicitstatic", "global", "codegendecl", "auto", "bool", "catch", "char", "class", "const_cast", "default", "delete", "double", diff --git a/doc/manual.txt b/doc/manual.txt index 53817c508..f3602dc58 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -4893,16 +4893,16 @@ field which is used for runtime type identification is omitted. This is necessary for binary compatibility with other compiled languages. -NoStackFrame pragma -------------------- -A proc can be marked with the `noStackFrame`:idx: pragma to tell the compiler +AsmNoStackFrame pragma +---------------------- +A proc can be marked with the `AsmNoStackFrame`:idx: pragma to tell the compiler it should not generate a stack frame for the proc. There are also no exit statements like ``return result;`` generated and the generated C function is declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on the used C compiler). -**Note**: This pragma should only be used by procs which consist solely of assembler -statements. +**Note**: This pragma should only be used by procs which consist solely of +assembler statements. error pragma ------------ diff --git a/lib/system.nim b/lib/system.nim index 2acb989c5..27be9f815 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1514,7 +1514,7 @@ when not defined(NimrodVM): proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = result = cast[pointer](x) else: - proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} = + proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} = asm """return `x`""" proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = @@ -1797,7 +1797,7 @@ type len*: int ## length of the inspectable slots when defined(JS): - proc add*(x: var string, y: cstring) {.noStackFrame.} = + proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = asm """ var len = `x`[0].length-1; for (var i = 0; i < `y`.length; ++i) { diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim index d764a6672..d9b3aebac 100644 --- a/lib/system/arithm.nim +++ b/lib/system/arithm.nim @@ -111,7 +111,7 @@ const when asmVersion and not defined(gcc) and not defined(llvm_gcc): # assembler optimized versions for compilers that # have an intel syntax assembler: - proc addInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc addInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = # a in eax, and b in edx asm """ mov eax, `a` @@ -121,7 +121,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): theEnd: """ - proc subInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc subInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` sub eax, `b` @@ -130,7 +130,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): theEnd: """ - proc negInt(a: int): int {.compilerProc, noStackFrame.} = + proc negInt(a: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` neg eax @@ -139,7 +139,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): theEnd: """ - proc divInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc divInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` mov ecx, `b` @@ -150,7 +150,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): theEnd: """ - proc modInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc modInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` mov ecx, `b` @@ -162,7 +162,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc): mov eax, edx """ - proc mulInt(a, b: int): int {.compilerProc, noStackFrame.} = + proc mulInt(a, b: int): int {.compilerProc, asmNoStackFrame.} = asm """ mov eax, `a` mov ecx, `b` diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 4fc5f479b..a19c456f7 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -23,9 +23,9 @@ type PCallFrame = ptr TCallFrame TCallFrame {.importc, nodecl, final.} = object prev: PCallFrame - procname: CString + procname: cstring line: int # current line number - filename: CString + filename: cstring var framePtr {.importc, nodecl, volatile.}: PCallFrame @@ -48,7 +48,7 @@ proc getCurrentExceptionMsg*(): string = proc auxWriteStackTrace(f: PCallFrame): string = type - TTempFrame = tuple[procname: CString, line: int] + TTempFrame = tuple[procname: cstring, line: int] var it = f i = 0 @@ -84,7 +84,7 @@ proc rawWriteStackTrace(): string = framePtr = nil proc raiseException(e: ref E_Base, ename: cstring) {. - compilerproc, noStackFrame.} = + compilerproc, asmNoStackFrame.} = e.name = ename if excHandler != nil: excHandler.exc = e @@ -104,7 +104,7 @@ proc raiseException(e: ref E_Base, ename: cstring) {. alert(buf) asm """throw `e`;""" -proc reraiseException() {.compilerproc, noStackFrame.} = +proc reraiseException() {.compilerproc, asmNoStackFrame.} = if excHandler == nil: raise newException(ENoExceptionToReraise, "no exception to reraise") else: @@ -125,7 +125,7 @@ proc raiseIndexError() {.compilerproc, noreturn.} = proc raiseFieldError(f: string) {.compilerproc, noreturn.} = raise newException(EInvalidField, f & " is not accessible") -proc SetConstr() {.varargs, noStackFrame, compilerproc.} = +proc SetConstr() {.varargs, asmNoStackFrame, compilerproc.} = asm """ var result = {}; for (var i = 0; i < arguments.length; ++i) { @@ -141,7 +141,7 @@ proc SetConstr() {.varargs, noStackFrame, compilerproc.} = return result; """ -proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} = +proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} = asm """ var result = []; for (var i = 0; i < `c`.length; ++i) { @@ -151,7 +151,7 @@ proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} = return result; """ -proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} = +proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} = asm """ var len = `s`.length-1; var result = new Array(len); @@ -162,7 +162,7 @@ proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} = return result.join(""); """ -proc mnewString(len: int): string {.noStackFrame, compilerproc.} = +proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = asm """ var result = new Array(`len`+1); result[0] = 0; @@ -170,7 +170,7 @@ proc mnewString(len: int): string {.noStackFrame, compilerproc.} = return result; """ -proc SetCard(a: int): int {.compilerproc, noStackFrame.} = +proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = # argument type is a fake asm """ var result = 0; @@ -178,14 +178,14 @@ proc SetCard(a: int): int {.compilerproc, noStackFrame.} = return result; """ -proc SetEq(a, b: int): bool {.compilerproc, noStackFrame.} = +proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} = asm """ for (var elem in `a`) { if (!`b`[elem]) return false; } for (var elem in `b`) { if (!`a`[elem]) return false; } return true; """ -proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} = +proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} = asm """ for (var elem in `a`) { if (!`b`[elem]) return false; } return true; @@ -194,7 +194,7 @@ proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} = proc SetLt(a, b: int): bool {.compilerproc.} = result = SetLe(a, b) and not SetEq(a, b) -proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} = +proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} = asm """ var result = {}; for (var elem in `a`) { @@ -203,7 +203,7 @@ proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} = return result; """ -proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} = +proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} = asm """ var result = {}; for (var elem in `a`) { result[elem] = true; } @@ -211,7 +211,7 @@ proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} = return result; """ -proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} = +proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} = asm """ var result = {}; for (var elem in `a`) { @@ -220,7 +220,7 @@ proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} = return result; """ -proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} = +proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} = asm """ if (`a` == `b`) return 0; if (!`a`) return -1; @@ -234,7 +234,7 @@ proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} = proc cmp(x, y: string): int = return cmpStrings(x, y) -proc eqStrings(a, b: string): bool {.noStackFrame, compilerProc.} = +proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} = asm """ if (`a` == `b`) return true; if ((!`a`) || (!`b`)) return false; @@ -300,7 +300,7 @@ type setAttributeNode*: proc (attr: ref TNode) {.nimcall.} when defined(kwin): - proc rawEcho {.compilerproc, nostackframe.} = + proc rawEcho {.compilerproc, asmNoStackFrame.} = asm """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { @@ -312,7 +312,7 @@ when defined(kwin): elif defined(nodejs): proc ewriteln(x: cstring) = log(x) - proc rawEcho {.compilerproc, nostackframe.} = + proc rawEcho {.compilerproc, asmNoStackFrame.} = asm """ var buf = ""; for (var i = 0; i < arguments.length; ++i) { @@ -345,42 +345,42 @@ else: node.appendChild(document.createElement("br")) # Arithmetic: -proc addInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` + `b`; if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); return result; """ -proc subInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` - `b`; if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); return result; """ -proc mulInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` * `b`; if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); return result; """ -proc divInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); return Math.floor(`a` / `b`); """ -proc modInt(a, b: int): int {.noStackFrame, compilerproc.} = +proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); return Math.floor(`a` % `b`); """ -proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` + `b`; if (result > 9223372036854775807 @@ -388,7 +388,7 @@ proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} = return result; """ -proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` - `b`; if (result > 9223372036854775807 @@ -396,7 +396,7 @@ proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} = return result; """ -proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ var result = `a` * `b`; if (result > 9223372036854775807 @@ -404,14 +404,14 @@ proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} = return result; """ -proc divInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); return Math.floor(`a` / `b`); """ -proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} = +proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = asm """ if (`b` == 0) `raiseDivByZero`(); if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); @@ -472,17 +472,17 @@ proc Ze(a: int): int {.compilerproc.} = proc Ze64(a: int64): int64 {.compilerproc.} = result = a -proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} = +proc ToU8(a: int): int8 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} = +proc ToU16(a: int): int16 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ -proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} = +proc ToU32(a: int): int32 {.asmNoStackFrame, compilerproc.} = asm """ return `a`; """ @@ -586,7 +586,7 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} = result = nil proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {. - noStackFrame, compilerproc.} = + asmNoStackFrame, compilerproc.} = # types are fake asm """ var result = new Array(`len`); @@ -620,7 +620,7 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} = x = x.base return true -proc addChar(x: string, c: char) {.compilerproc, noStackFrame.} = +proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} = asm """ `x`[`x`.length-1] = `c`; `x`.push(0); """ diff --git a/todo.txt b/todo.txt index 656bd06fc..d3ed9957f 100644 --- a/todo.txt +++ b/todo.txt @@ -19,8 +19,6 @@ Bugs - docgen: sometimes effects are listed twice - 'result' is not properly cleaned for NRVO --> use uninit checking instead - blocks can "export" an identifier but the CCG generates {} for them ... -- osproc execProcesses can deadlock if all processes fail (as experienced - in c++ mode) version 0.9.x @@ -144,11 +142,9 @@ Not essential for 1.0.0 semantics instead ... - implement "closure tuple consists of a single 'ref'" optimization - optimize method dispatchers -- ``with proc `+`(x, y: T): T`` for generic code - new feature: ``distinct T with operations`` - arglist as a type (iterator chaining); variable length type lists for generics - implement marker procs for message passing -- activate more thread tests - implement closures that support nesting of *procs* > 1 - object constructors: static check for fields if discriminator is known at compile time diff --git a/web/news.txt b/web/news.txt index 187797b58..f9ac1b66d 100644 --- a/web/news.txt +++ b/web/news.txt @@ -41,6 +41,8 @@ News - The ``nil`` statement has been deprecated, use an empty ``discard`` instead. - ``sockets.select`` now prunes sockets that are **not** ready from the list of sockets given to it. + - The ``noStackFrame`` pragma has been renamed to ``asmNoStackFrame`` to + ensure you only use it when you know what you're doing. Compiler Additions From 0bbf6081d00e83329021a0051d92c3433fc8a00f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 18 Feb 2014 20:04:58 +0200 Subject: [PATCH 064/249] fix #931 and few more tests --- compiler/semtypes.nim | 1 - compiler/semtypinst.nim | 9 ++++++--- tests/metatype/tbindtypedesc.nim | 23 ++++++++++------------- tests/threads/nimrod.cfg | 1 + 4 files changed, 17 insertions(+), 17 deletions(-) create mode 100644 tests/threads/nimrod.cfg diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 98abaf005..0eba602cc 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -705,7 +705,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) - result.lastSon.shouldHaveMeta result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = addImplicitGeneric(result) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index f08214f1e..22edc6e32 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -220,7 +220,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = # is difficult to handle: var body = t.sons[0] if body.kind != tyGenericBody: internalError(cl.info, "no generic body") - var header: PType = nil + var header: PType = t # search for some instantiation here: if cl.allowMetaTypes: result = PType(idTableGet(cl.localCache, t)) @@ -232,11 +232,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = if x.kind == tyGenericParam: x = lookupTypeVar(cl, x) if x != nil: - if header == nil: header = instCopyType(cl, t) + if header == t: header = instCopyType(cl, t) header.sons[i] = x propagateToOwner(header, x) + else: + propagateToOwner(header, x) - if header != nil: + if header != t: # search again after first pass: result = searchInstTypes(header) if result != nil: return @@ -244,6 +246,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = header = instCopyType(cl, t) result = newType(tyGenericInst, t.sons[0].owner) + result.flags = header.flags # be careful not to propagate unnecessary flags here (don't use rawAddSon) result.sons = @[header.sons[0]] # ugh need another pass for deeply recursive generic types (e.g. PActor) diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim index 5ea8cf063..84527362f 100644 --- a/tests/metatype/tbindtypedesc.nim +++ b/tests/metatype/tbindtypedesc.nim @@ -1,10 +1,10 @@ discard """ - msg: ''' -int -float -TFoo -TFoo -''' + msg: '''int int +float float +int int +TFoo TFoo +int float +TFoo TFoo''' """ import typetraits @@ -24,9 +24,8 @@ template reject(e: expr) = proc genericParamRepeated[T: typedesc](a: T, b: T) = static: - echo a.name - echo b.name - + echo a.name, " ", b.name + accept genericParamRepeated(int, int) accept genericParamRepeated(float, float) @@ -35,8 +34,7 @@ reject genericParamRepeated(int, float) proc genericParamOnce[T: typedesc](a, b: T) = static: - echo a.name - echo b.name + echo a.name, " ", b.name accept genericParamOnce(int, int) accept genericParamOnce(TFoo, TFoo) @@ -68,8 +66,7 @@ reject typePairs2(string, int, TBAR, TBAR) proc dontBind(a: typedesc, b: typedesc) = static: - echo a.name - echo b.name + echo a.name, " ", b.name accept dontBind(int, float) accept dontBind(TFoo, TFoo) diff --git a/tests/threads/nimrod.cfg b/tests/threads/nimrod.cfg new file mode 100644 index 000000000..b81c89721 --- /dev/null +++ b/tests/threads/nimrod.cfg @@ -0,0 +1 @@ +threads:on From 1f376d8594d9e20aa20b900853a166cbb49af5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Tue, 18 Feb 2014 20:22:40 +0100 Subject: [PATCH 065/249] osproc: use push stacktrace:off instead of nostackframe --- lib/pure/osproc.nim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index aa2f6f937..93d737aa6 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -606,10 +606,11 @@ elif not defined(useNimRtl): optionPoStdErrToStdOut: bool proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} - proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} + {.push stacktrace: off, profiler: off.} proc startProcessAfterFork(data: ptr TStartProcessData) {. - tags: [FExecIO, FReadEnv], noStackFrame, cdecl.} + tags: [FExecIO, FReadEnv], cdecl.} + {.pop.} proc startProcess(command: string, workingDir: string = "", @@ -774,7 +775,8 @@ elif not defined(useNimRtl): return pid - proc startProcessFail(data: ptr TStartProcessData) {.noStackFrame.} = + {.push stacktrace: off, profiler: off.} + proc startProcessFail(data: ptr TStartProcessData) = var error: cint = errno discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error)) exitnow(1) @@ -811,6 +813,7 @@ elif not defined(useNimRtl): discard execve(data.sysCommand, data.sysArgs, data.sysEnv) startProcessFail(data) + {.pop} proc close(p: PProcess) = if p.inStream != nil: close(p.inStream) From 1a6d05515ff96e8bba294b352493cb7da2794a96 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 20 Feb 2014 23:33:58 +0200 Subject: [PATCH 066/249] fix #945 --- compiler/semstmts.nim | 9 +++++---- tests/metatype/tusertypeclasses.nim | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a9a907953..6c4e29f29 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -143,10 +143,11 @@ proc discardCheck(c: PContext, result: PNode) = while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil - elif c.inTypeClass > 0 and result.typ.kind == tyBool: - let verdict = semConstExpr(c, result) - if verdict.intVal == 0: - localError(result.info, "type class predicate failed") + elif c.inTypeClass > 0: + if result.typ.kind == tyBool: + let verdict = semConstExpr(c, result) + if verdict.intVal == 0: + localError(result.info, "type class predicate failed") elif result.typ.kind != tyError and gCmd != cmdInteractive: if result.typ.kind == tyNil: fixNilType(result) diff --git a/tests/metatype/tusertypeclasses.nim b/tests/metatype/tusertypeclasses.nim index 4c8c0fc56..5b04c490f 100644 --- a/tests/metatype/tusertypeclasses.nim +++ b/tests/metatype/tusertypeclasses.nim @@ -31,9 +31,10 @@ proc intval(x: int) = discard # check real and virtual fields type TFoo = generic T - intval T.x + T.x + y(T) intval T.y - + proc y(x: TObj): int = 10 proc testFoo(x: TFoo) = discard From 12c95f31dcd6587d09f9eeedfd58d1ec96189abd Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Thu, 20 Feb 2014 23:32:03 +0100 Subject: [PATCH 067/249] Documents system.readAll() limitations. Refs #298. --- lib/system.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 2acb989c5..b683a4955 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2014,8 +2014,10 @@ when not defined(JS): #and not defined(NimrodVM): ## Flushes `f`'s buffer. proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].} - ## Reads all data from the stream `file`. Raises an IO exception - ## in case of an error + ## Reads all data from the stream `file`. + ## + ## Raises an IO exception in case of an error. It is an error if the + ## current file position is not at the beginning of the file. proc readFile*(filename: string): TaintedString {.tags: [FReadIO].} ## Opens a file named `filename` for reading. Then calls `readAll` From adb390af4bfbb3549672280007e9c46bf7a223c2 Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Fri, 21 Feb 2014 00:32:28 +0100 Subject: [PATCH 068/249] Checks that exported symbols are valid C identifiers. Refs #800. --- compiler/msgs.nim | 3 ++- compiler/pragmas.nim | 25 ++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 0140d1ac4..8c41a8191 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -105,7 +105,7 @@ type errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errXCannotBeClosure, errXMustBeCompileTime, errCannotInferTypeOfTheLiteral, - errUser, + errBadExport, errUser warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnDeprecated, warnConfigDeprecated, @@ -350,6 +350,7 @@ const errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXMustBeCompileTime: "'$1' can only be used in compile-time context", errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", + errBadExport: "invalid exported symbol, $1", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d9ed50cfe..6e21a85b9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -97,8 +97,27 @@ proc makeExternImport(s: PSym, extname: string) = incl(s.flags, sfImportc) excl(s.flags, sfForward) -proc makeExternExport(s: PSym, extname: string) = +const invalidIdentChars = AllChars - IdentChars + +proc validateExternName(s: PSym, info: TLineInfo) = + ## Validates that the symbol name in s.loc.r is a valid C identifier. + ## + ## Valid identifiers are those alphanumeric including the underscore not + ## starting with a number. If the check fails, a friendly error will be + ## displayed to the user. + let target = ropeToStr(s.loc.r) + if target.len < 1: + localError(info, errBadExport, "can't be zero length") + if not (target[0] in IdentStartChars): + localError(info, errBadExport, "'" & target & "' can't start with a number") + if not target.allCharsInSet(IdentChars): + let pos = target.find(invalidIdentChars, 1) + localError(info, errBadExport, "'" & target & + "' contains bad character at byte " & $pos) + +proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = setExternName(s, extname) + validateExternName(s, info) incl(s.flags, sfExportc) proc processImportCompilerProc(s: PSym, extname: string) = @@ -515,7 +534,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, if k in validPragmas: case k of wExportc: - makeExternExport(sym, getOptionalStr(c, it, "$1")) + makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) incl(sym.flags, sfUsed) # avoid wrong hints of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1")) of wImportCompilerProc: @@ -601,7 +620,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, processDynLib(c, it, sym) of wCompilerproc: noVal(it) # compilerproc may not get a string! - makeExternExport(sym, "$1") + makeExternExport(sym, "$1", it.info) incl(sym.flags, sfCompilerProc) incl(sym.flags, sfUsed) # suppress all those stupid warnings registerCompilerProc(sym) From 6621454dec0492044f76d1c78e9df7682048b83b Mon Sep 17 00:00:00 2001 From: Fabio Cevasco Date: Fri, 21 Feb 2014 14:14:43 +0100 Subject: [PATCH 069/249] pegs.findAll iterator fix Modified the findAll iterator so that it continues looking for a match within the input string (bug?). --- lib/pure/pegs.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 70b617393..3b1516e17 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -836,7 +836,9 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string = while i < s.len: c.ml = 0 var L = rawMatch(s, pattern, i, c) - if L < 0: break + if L < 0: + inc(i, 1) + continue yield substr(s, i, i+L-1) inc(i, L) From 6e584c42c2d340a31acad85add79b579b3e56f0b Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 21 Feb 2014 17:23:37 +0100 Subject: [PATCH 070/249] vm2: new representation of registers --- compiler/ast.nim | 6 +- compiler/ccgexprs.nim | 1 - compiler/jsgen.nim | 1 - compiler/renderer.nim | 2 +- compiler/semfold.nim | 2 +- compiler/vm.nim | 935 ++++++++++++++++++++++-------------------- compiler/vmdef.nim | 20 +- compiler/vmgen.nim | 128 +++--- todo.txt | 7 +- 9 files changed, 568 insertions(+), 534 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index fe724f4dd..bd8bdb30b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -62,7 +62,7 @@ type nkTripleStrLit, # a triple string literal """ nkNilLit, # the nil literal # end of atoms - nkMetaNode, # difficult to explain; represents itself + nkMetaNode_Obsolete, # difficult to explain; represents itself # (used for macros) nkDotCall, # used to temporarily flag a nkCall node; # this is used @@ -1112,10 +1112,6 @@ proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = result.info = info result.typ = typ -proc newMetaNodeIT*(tree: PNode, info: TLineInfo, typ: PType): PNode = - result = newNodeIT(nkMetaNode, info, typ) - result.add(tree) - var emptyParams = newNode(nkFormalParams) emptyParams.addSon(emptyNode) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 27f7f4ba5..8cb423688 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1915,7 +1915,6 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = internalError(n.info, "expr: proc not init " & sym.name.s) putLocIntoDest(p, d, sym.loc) of nkClosure: genClosure(p, n, d) - of nkMetaNode: expr(p, n.sons[0], d) of nkEmpty: discard of nkWhileStmt: genWhileStmt(p, n) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index c0fc4131a..a8a15f542 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1600,7 +1600,6 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if lfNoDecl in s.loc.flags or s.magic != mNone: discard elif not p.g.generatedSyms.containsOrIncl(s.id): app(p.locals, genProc(p, s)) - of nkMetaNode: gen(p, n.sons[0], r) of nkType: r.res = genTypeInfo(p, n.typ) of nkStmtList, nkStmtListExpr: # this shows the distinction is nice for backends and should be kept diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 1afb5961e..509c815c1 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1268,7 +1268,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkBracketLe, "[") gcomma(g, n) put(g, tkBracketRi, "]") - of nkMetaNode: + of nkMetaNode_Obsolete: put(g, tkParLe, "(META|") gsub(g, n.sons[0]) put(g, tkParRi, ")") diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 4740ddcb3..925a80832 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -537,7 +537,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = if result.kind == nkExprColonExpr: result = result.sons[1] else: localError(n.info, errIndexOutOfBounds) - of nkBracket, nkMetaNode: + of nkBracket: if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)] else: localError(n.info, errIndexOutOfBounds) of nkStrLit..nkTripleStrLit: diff --git a/compiler/vm.nim b/compiler/vm.nim index 0929e072b..8480cd1c6 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -8,7 +8,7 @@ # ## This file implements the new evaluation engine for Nimrod code. -## An instruction is 1-2 int32s in memory, it is a register based VM. +## An instruction is 1-3 int32s in memory, it is a register based VM. import ast except getstr @@ -23,10 +23,22 @@ when hasFFI: import evalffi type + TRegisterKind = enum + rkNone, rkNode, rkStr, rkInt, rkFloat, rkRegisterAddr, rkNodeAddr + TRegister = object # with a custom mark proc, we could use the same + # data representation as LuaJit (tagged NaNs). + case kind: TRegisterKind + of rkNone: nil + of rkInt: intVal: BiggestInt + of rkFloat: floatVal: BiggestFloat + of rkNode, rkStr: node: PNode + of rkRegisterAddr: regAddr: ptr TRegister + of rkNodeAddr: nodeAddr: ptr PNode + PStackFrame* = ref TStackFrame TStackFrame* = object prc: PSym # current prc; proc that is evaluated - slots: TNodeSeq # parameters passed to the proc + locals; + slots: seq[TRegister] # parameters passed to the proc + locals; # parameters come first next: PStackFrame # for stacking comesFrom: int @@ -63,24 +75,9 @@ proc bailOut(c: PCtx; tos: PStackFrame) = when not defined(nimComputedGoto): {.pragma: computedGoto.} -proc myreset(n: PNode) = +proc myreset(n: var TRegister) = when defined(system.reset): - var oldInfo = n.info - reset(n[]) - n.info = oldInfo - -proc skipMeta(n: PNode): PNode = (if n.kind != nkMetaNode: n else: n.sons[0]) - -proc setMeta(n, child: PNode) = - assert n.kind == nkMetaNode - let child = child.skipMeta - if n.sons.isNil: n.sons = @[child] - else: n.sons[0] = child - -proc uast(n: PNode): PNode {.inline.} = - # "underlying ast" - assert n.kind == nkMetaNode - n.sons[0] + reset(n) template ensureKind(k: expr) {.immediate, dirty.} = if regs[ra].kind != k: @@ -112,23 +109,20 @@ template decodeBx(k: expr) {.immediate, dirty.} = template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler -proc moveConst(x, y: PNode) = +template createStr(x) = + if x.node.isNil: x.node = newNode(nkStrLit) + +proc moveConst(x: var TRegister, y: TRegister) = if x.kind != y.kind: myreset(x) x.kind = y.kind - x.typ = y.typ case x.kind - of nkCharLit..nkInt64Lit: x.intVal = y.intVal - of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal - of nkStrLit..nkTripleStrLit: move(x.strVal, y.strVal) - of nkIdent: x.ident = y.ident - of nkSym: x.sym = y.sym - of nkMetaNode: - if x.sons.isNil: x.sons = @[y.sons[0]] - else: x.sons[0] = y.sons[0] - else: - if x.kind notin {nkEmpty..nkNilLit}: - move(x.sons, y.sons) + of rkNone: discard + of rkInt: x.intVal = y.intVal + of rkFloat: x.floatVal = y.floatVal + of rkStr, rkNode: x.node = y.node + of rkRegisterAddr: x.regAddr = y.regAddr + of rkNodeAddr: x.nodeAddr = y.nodeAddr # this seems to be the best way to model the reference semantics # of PNimrodNode: @@ -155,29 +149,59 @@ proc copyValue(src: PNode): PNode = for i in countup(0, sonsLen(src) - 1): result.sons[i] = copyValue(src.sons[i]) -proc asgnComplex(x, y: PNode) = +proc asgnComplex(x: var TRegister, y: TRegister) = if x.kind != y.kind: myreset(x) x.kind = y.kind - x.typ = y.typ case x.kind - of nkCharLit..nkInt64Lit: x.intVal = y.intVal - of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal - of nkStrLit..nkTripleStrLit: x.strVal = y.strVal - of nkIdent: x.ident = y.ident - of nkSym: x.sym = y.sym - of nkMetaNode: - if x.sons.isNil: x.sons = @[y.sons[0]] - else: x.sons[0] = y.sons[0] + of rkNone: discard + of rkInt: x.intVal = y.intVal + of rkFloat: x.floatVal = y.floatVal + of rkStr, rkNode: x.node = copyValue(y.node) + of rkRegisterAddr: x.regAddr = y.regAddr + of rkNodeAddr: x.nodeAddr = y.nodeAddr + +proc putIntoNode(n: PNode; x: TRegister) = + case x.kind + of rkNone: discard + of rkStr: + if n.kind == nkNilLit: + system.reset(n[]) + n.kind = nkStrLit + n.strVal = x.node.strVal + of rkInt: n.intVal = x.intVal + of rkFloat: n.floatVal = x.floatVal + of rkNode: n[] = x.node[] + of rkRegisterAddr: putIntoNode(n, x.regAddr[]) + of rkNodeAddr: n[] = x.nodeAddr[][] + +proc putIntoReg(dest: var TRegister; n: PNode) = + case n.kind + of nkStrLit..nkTripleStrLit: + dest.kind = rkStr + createStr(dest) + dest.node.strVal = n.strVal + of nkCharLit..nkUInt64Lit: + dest.kind = rkInt + dest.intVal = n.intVal + of nkFloatLit..nkFloat128Lit: + dest.kind = rkFloat + dest.floatVal = n.floatVal else: - if x.kind notin {nkEmpty..nkNilLit}: - let y = y.copyValue - for i in countup(0, sonsLen(y) - 1): - if i < x.len: x.sons[i] = y.sons[i] - else: addSon(x, y.sons[i]) + dest.kind = rkNode + dest.node = n + +proc regToNode(x: TRegister): PNode = + case x.kind + of rkNone: result = newNode(nkEmpty) + of rkInt: result = newNode(nkIntLit); result.intVal = x.intVal + of rkFloat: result = newNode(nkFloatLit); result.floatVal = x.floatVal + of rkStr, rkNode: result = x.node + of rkRegisterAddr: result = regToNode(x.regAddr[]) + of rkNodeAddr: result = x.nodeAddr[] template getstr(a: expr): expr = - (if a.kind in {nkStrLit..nkTripleStrLit}: a.strVal else: $chr(int(a.intVal))) + (if a.kind == rkStr: a.node.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = if f.safePoints.isNil: f.safePoints = @[] @@ -185,7 +209,7 @@ proc pushSafePoint(f: PStackFrame; pc: int) = proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop() -proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: TNodeSeq): int = +proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: seq[TRegister]): int = let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: @@ -227,50 +251,51 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = return pc return -1 -proc opConv*(dest, src: PNode, typ: PType): bool = - if typ.kind == tyString: - if dest.kind != nkStrLit: +proc opConv*(dest: var TRegister, src: TRegister, desttyp, srctyp: PType): bool = + if desttyp.kind == tyString: + if dest.kind != rkStr: myreset(dest) - dest.kind = nkStrLit - case src.typ.skipTypes(abstractRange).kind + dest.kind = rkStr + dest.node = newNode(nkStrLit) + case srctyp.skipTypes(abstractRange).kind of tyEnum: - dest.strVal = ordinalValToString(src) + dest.node.strVal = "too implement" #ordinalValToString(src) of tyInt..tyInt64, tyUInt..tyUInt64: - dest.strVal = $src.intVal + dest.node.strVal = $src.intVal of tyBool: - dest.strVal = if src.intVal == 0: "false" else: "true" + dest.node.strVal = if src.intVal == 0: "false" else: "true" of tyFloat..tyFloat128: - dest.strVal = $src.floatVal + dest.node.strVal = $src.floatVal of tyString, tyCString: - dest.strVal = src.strVal + dest.node.strVal = src.node.strVal of tyChar: - dest.strVal = $chr(src.intVal) + dest.node.strVal = $chr(src.intVal) else: - internalError("cannot convert to string " & typ.typeToString) + internalError("cannot convert to string " & desttyp.typeToString) else: - case skipTypes(typ, abstractRange).kind + case skipTypes(desttyp, abstractRange).kind of tyInt..tyInt64: - if dest.kind != nkIntLit: - myreset(dest); dest.kind = nkIntLit - case skipTypes(src.typ, abstractRange).kind + if dest.kind != rkInt: + myreset(dest); dest.kind = rkInt + case skipTypes(srctyp, abstractRange).kind of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: dest.intVal = src.intVal - if dest.intVal < firstOrd(typ) or dest.intVal > lastOrd(typ): + if dest.intVal < firstOrd(desttyp) or dest.intVal > lastOrd(desttyp): return true of tyUInt..tyUInt64: - if dest.kind != nkIntLit: - myreset(dest); dest.kind = nkIntLit - case skipTypes(src.typ, abstractRange).kind + if dest.kind != rkInt: + myreset(dest); dest.kind = rkInt + case skipTypes(srctyp, abstractRange).kind of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: - dest.intVal = src.intVal and ((1 shl typ.size)-1) + dest.intVal = src.intVal and ((1 shl desttyp.size)-1) of tyFloat..tyFloat64: - if dest.kind != nkFloatLit: - myreset(dest); dest.kind = nkFloatLit - case skipTypes(src.typ, abstractRange).kind + if dest.kind != rkFloat: + myreset(dest); dest.kind = rkFloat + case skipTypes(srctyp, abstractRange).kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: dest.floatVal = toFloat(src.intVal.int) else: @@ -282,15 +307,10 @@ proc compile(c: PCtx, s: PSym): int = result = vmgen.genProc(c, s) #c.echoCode -proc regsContents(regs: TNodeSeq) = - for i in 0.. high(int): stackTrace(c, tos, pc, errIndexOutOfBounds) let idx = regs[rc].intVal.int # XXX what if the array is not 0-based? -> codegen should insert a sub - assert regs[rb].kind != nkMetaNode - let src = regs[rb] + let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: - if instr.opcode == opcLdArrRef and false: - # XXX activate when seqs are fixed - asgnRef(regs[ra], src.sons[idx]) - else: - asgnComplex(regs[ra], src.sons[idx]) + regs[ra].node = src.sons[idx] else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdStrIdx: - decodeBC(nkIntLit) + decodeBC(rkInt) let idx = regs[rc].intVal.int - if idx <=% regs[rb].strVal.len: - regs[ra].intVal = regs[rb].strVal[idx].ord + if idx <=% regs[rb].node.strVal.len: + regs[ra].intVal = regs[rb].node.strVal[idx].ord else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrArr: # a[b] = c - let rb = instr.regB - let rc = instr.regC + decodeBC(rkNode) let idx = regs[rb].intVal.int - if idx <% regs[ra].len: - asgnComplex(regs[ra].sons[idx], regs[rc]) - else: - stackTrace(c, tos, pc, errIndexOutOfBounds) - of opcWrArrRef: - let rb = instr.regB - let rc = instr.regC - let idx = regs[rb].intVal.int - if idx <% regs[ra].len: - asgnRef(regs[ra].sons[idx], regs[rc]) + if idx <% regs[ra].node.len: + putIntoNode(regs[ra].node.sons[idx], regs[rc]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdObj: # a = b.c - let rb = instr.regB - let rc = instr.regC - #Message(c.debug[pc], warnUser, $regs[rb].safeLen & " " & $rc) - asgnComplex(regs[ra], regs[rb].sons[rc]) - of opcLdObjRef: - # a = b.c - let rb = instr.regB - let rc = instr.regC - # XXX activate when seqs are fixed - asgnComplex(regs[ra], regs[rb].sons[rc]) - #asgnRef(regs[ra], regs[rb].sons[rc]) - of opcWrObj: - # a.b = c - let rb = instr.regB - let rc = instr.regC - #if regs[ra].isNil or regs[ra].sons.isNil or rb >= len(regs[ra]): - # debug regs[ra] - # debug regs[rc] - # echo "RB ", rb - # internalError(c.debug[pc], "argl") - asgnComplex(regs[ra].sons[rb], regs[rc]) - of opcWrObjRef: - let rb = instr.regB - let rc = instr.regC - asgnRef(regs[ra].sons[rb], regs[rc]) - of opcWrStrIdx: - decodeBC(nkStrLit) - let idx = regs[rb].intVal.int - if idx <% regs[ra].strVal.len: - regs[ra].strVal[idx] = chr(regs[rc].intVal) + decodeBC(rkNode) + let src = regs[rb].node + if src.kind notin {nkEmpty..nkNilLit}: + regs[ra].node = src.sons[rc] else: stackTrace(c, tos, pc, errIndexOutOfBounds) - of opcAddr: - decodeB(nkRefTy) - if regs[ra].len == 0: regs[ra].add regs[rb] - else: regs[ra].sons[0] = regs[rb] - of opcDeref: + of opcWrObj: + # a.b = c + decodeBC(rkNode) + putIntoNode(regs[ra].node.sons[rb], regs[rc]) + of opcWrStrIdx: + decodeBC(rkStr) + let idx = regs[rb].intVal.int + if idx <% regs[ra].node.strVal.len: + regs[ra].node.strVal[idx] = chr(regs[rc].intVal) + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) + of opcAddrReg: + decodeB(rkRegisterAddr) + regs[ra].regAddr = addr(regs[rb]) + of opcAddrNode: + decodeB(rkNodeAddr) + regs[ra].nodeAddr = addr(regs[rb].node) + of opcLdDeref: # a = b[] + let ra = instr.regA let rb = instr.regB - if regs[rb].kind == nkNilLit: - stackTrace(c, tos, pc, errNilAccess) - assert regs[rb].kind == nkRefTy - # XXX this is not correct - regs[ra] = regs[rb].sons[0] + case regs[rb].kind + of rkNodeAddr: + ensureKind(rkNode) + regs[ra].node = regs[rb].nodeAddr[] + of rkRegisterAddr: + ensureKind(regs[rb].regAddr.kind) + regs[ra] = regs[rb].regAddr[] + of rkNode: + if regs[rb].node.kind == nkNilLit: + stackTrace(c, tos, pc, errNilAccess) + assert regs[rb].node.kind == nkRefTy + regs[ra].node = regs[rb].node.sons[0] + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) + of opcWrDeref: + # a[] = b + decodeBC(rkNode) + putIntoNode(regs[ra].node, regs[rc]) of opcAddInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal + regs[rc].intVal of opcAddImmInt: - decodeBImm(nkIntLit) + decodeBImm(rkInt) regs[ra].intVal = regs[rb].intVal + imm of opcSubInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal - regs[rc].intVal of opcSubImmInt: - decodeBImm(nkIntLit) + decodeBImm(rkInt) regs[ra].intVal = regs[rb].intVal - imm of opcLenSeq: - decodeBImm(nkIntLit) + decodeBImm(rkInt) #assert regs[rb].kind == nkBracket # also used by mNLen: - regs[ra].intVal = regs[rb].skipMeta.safeLen - imm + regs[ra].intVal = regs[rb].node.safeLen - imm of opcLenStr: - decodeBImm(nkIntLit) - if regs[rb].kind == nkNilLit: - stackTrace(c, tos, pc, errNilAccess) - else: - assert regs[rb].kind in {nkStrLit..nkTripleStrLit} - regs[ra].intVal = regs[rb].strVal.len - imm + decodeBImm(rkInt) + assert regs[rb].kind == rkStr + regs[ra].intVal = regs[rb].node.strVal.len - imm of opcIncl: - decodeB(nkCurly) - if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb])) + decodeB(rkNode) + let b = regs[rb].regToNode + if not inSet(regs[ra].node, b): + addSon(regs[ra].node, copyTree(b)) of opcInclRange: - decodeBC(nkCurly) + decodeBC(rkNode) var r = newNode(nkRange) - r.add regs[rb] - r.add regs[rc] - addSon(regs[ra], r.copyTree) + r.add regs[rb].node + r.add regs[rc].node + addSon(regs[ra].node, r.copyTree) of opcExcl: - decodeB(nkCurly) - var b = newNodeIT(nkCurly, regs[rb].info, regs[rb].typ) - addSon(b, regs[rb]) - var r = diffSets(regs[ra], b) - discardSons(regs[ra]) - for i in countup(0, sonsLen(r) - 1): addSon(regs[ra], r.sons[i]) + decodeB(rkNode) + var b = newNodeIT(nkCurly, regs[rb].node.info, regs[rb].node.typ) + addSon(b, regs[rb].regToNode) + var r = diffSets(regs[ra].node, b) + discardSons(regs[ra].node) + for i in countup(0, sonsLen(r) - 1): addSon(regs[ra].node, r.sons[i]) of opcCard: - decodeB(nkIntLit) - regs[ra].intVal = nimsets.cardSet(regs[rb]) + decodeB(rkInt) + regs[ra].intVal = nimsets.cardSet(regs[rb].node) of opcMulInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal * regs[rc].intVal of opcDivInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal div regs[rc].intVal of opcModInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal of opcAddFloat: - decodeBC(nkFloatLit) + decodeBC(rkFloat) regs[ra].floatVal = regs[rb].floatVal + regs[rc].floatVal of opcSubFloat: - decodeBC(nkFloatLit) + decodeBC(rkFloat) regs[ra].floatVal = regs[rb].floatVal - regs[rc].floatVal of opcMulFloat: - decodeBC(nkFloatLit) + decodeBC(rkFloat) regs[ra].floatVal = regs[rb].floatVal * regs[rc].floatVal of opcDivFloat: - decodeBC(nkFloatLit) + decodeBC(rkFloat) regs[ra].floatVal = regs[rb].floatVal / regs[rc].floatVal of opcShrInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal shr regs[rc].intVal of opcShlInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal of opcBitandInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal and regs[rc].intVal of opcBitorInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal or regs[rc].intVal of opcBitxorInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal xor regs[rc].intVal of opcAddu: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal +% regs[rc].intVal of opcSubu: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal of opcMulu: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal of opcDivu: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal /% regs[rc].intVal of opcModu: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal %% regs[rc].intVal of opcEqInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal == regs[rc].intVal) of opcLeInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal <= regs[rc].intVal) of opcLtInt: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal < regs[rc].intVal) of opcEqFloat: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].floatVal == regs[rc].floatVal) of opcLeFloat: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].floatVal <= regs[rc].floatVal) of opcLtFloat: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].floatVal < regs[rc].floatVal) of opcLeu: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal <=% regs[rc].intVal) of opcLtu: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal) of opcEqRef: - decodeBC(nkIntLit) - regs[ra].intVal = ord((regs[rb].kind == nkNilLit and - regs[rc].kind == nkNilLit) or - regs[rb].sons == regs[rc].sons) + decodeBC(rkInt) + regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and + regs[rc].node.kind == nkNilLit) or + regs[rb].node == regs[rc].node) of opcEqNimrodNode: - decodeBC(nkIntLit) - regs[ra].intVal = ord(regs[rb].skipMeta == regs[rc].skipMeta) + decodeBC(rkInt) + regs[ra].intVal = ord(regs[rb].node == regs[rc].node) of opcXor: - decodeBC(nkIntLit) + decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) of opcNot: - decodeB(nkIntLit) - assert regs[rb].kind == nkIntLit + decodeB(rkInt) + assert regs[rb].kind == rkInt regs[ra].intVal = 1 - regs[rb].intVal of opcUnaryMinusInt: - decodeB(nkIntLit) - assert regs[rb].kind == nkIntLit + decodeB(rkInt) + assert regs[rb].kind == rkInt regs[ra].intVal = -regs[rb].intVal of opcUnaryMinusFloat: - decodeB(nkFloatLit) - assert regs[rb].kind == nkFloatLit + decodeB(rkFloat) + assert regs[rb].kind == rkFloat regs[ra].floatVal = -regs[rb].floatVal of opcBitnotInt: - decodeB(nkIntLit) - assert regs[rb].kind == nkIntLit + decodeB(rkInt) + assert regs[rb].kind == rkInt regs[ra].intVal = not regs[rb].intVal of opcEqStr: - decodeBC(nkIntLit) - regs[ra].intVal = ord(regs[rb].strVal == regs[rc].strVal) + decodeBC(rkInt) + regs[ra].intVal = ord(regs[rb].node.strVal == regs[rc].node.strVal) of opcLeStr: - decodeBC(nkIntLit) - regs[ra].intVal = ord(regs[rb].strVal <= regs[rc].strVal) + decodeBC(rkInt) + regs[ra].intVal = ord(regs[rb].node.strVal <= regs[rc].node.strVal) of opcLtStr: - decodeBC(nkIntLit) - regs[ra].intVal = ord(regs[rb].strVal < regs[rc].strVal) + decodeBC(rkInt) + regs[ra].intVal = ord(regs[rb].node.strVal < regs[rc].node.strVal) of opcLeSet: - decodeBC(nkIntLit) - regs[ra].intVal = ord(containsSets(regs[rb], regs[rc])) + decodeBC(rkInt) + regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node)) of opcEqSet: - decodeBC(nkIntLit) - regs[ra].intVal = ord(equalSets(regs[rb], regs[rc])) + decodeBC(rkInt) + regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node)) of opcLtSet: - decodeBC(nkIntLit) - let a = regs[rb] - let b = regs[rc] + decodeBC(rkInt) + let a = regs[rb].node + let b = regs[rc].node regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b)) of opcMulSet: - decodeBC(nkCurly) - move(regs[ra].sons, nimsets.intersectSets(regs[rb], regs[rc]).sons) + decodeBC(rkNode) + move(regs[ra].node.sons, + nimsets.intersectSets(regs[rb].node, regs[rc].node).sons) of opcPlusSet: - decodeBC(nkCurly) - move(regs[ra].sons, nimsets.unionSets(regs[rb], regs[rc]).sons) + decodeBC(rkNode) + move(regs[ra].node.sons, + nimsets.unionSets(regs[rb].node, regs[rc].node).sons) of opcMinusSet: - decodeBC(nkCurly) - move(regs[ra].sons, nimsets.diffSets(regs[rb], regs[rc]).sons) + decodeBC(rkNode) + move(regs[ra].node.sons, + nimsets.diffSets(regs[rb].node, regs[rc].node).sons) of opcSymdiffSet: - decodeBC(nkCurly) - move(regs[ra].sons, nimsets.symdiffSets(regs[rb], regs[rc]).sons) + decodeBC(rkNode) + move(regs[ra].node.sons, + nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) of opcConcatStr: - decodeBC(nkStrLit) - regs[ra].strVal = getstr(regs[rb]) + decodeBC(rkStr) + createStr regs[ra] + regs[ra].node.strVal = getstr(regs[rb]) for i in rb+1..rb+rc-1: - regs[ra].strVal.add getstr(regs[i]) + regs[ra].node.strVal.add getstr(regs[i]) of opcAddStrCh: - decodeB(nkStrLit) - regs[ra].strVal.add(regs[rb].intVal.chr) + decodeB(rkStr) + createStr regs[ra] + regs[ra].node.strVal.add(regs[rb].intVal.chr) of opcAddStrStr: - decodeB(nkStrLit) - regs[ra].strVal.add(regs[rb].strVal) + decodeB(rkStr) + createStr regs[ra] + regs[ra].node.strVal.add(regs[rb].node.strVal) of opcAddSeqElem: - decodeB(nkBracket) - regs[ra].add(copyTree(regs[rb])) + decodeB(rkNode) + regs[ra].node.add(copyTree(regs[rb].node)) of opcEcho: let rb = instr.regB for i in ra..ra+rb-1: - #if regs[i].kind != nkStrLit: debug regs[i] - write(stdout, regs[i].strVal) + #if regs[i].kind != rkStr: debug regs[i] + write(stdout, regs[i].node.strVal) writeln(stdout, "") of opcContainsSet: - decodeBC(nkIntLit) - regs[ra].intVal = ord(inSet(regs[rb], regs[rc])) + decodeBC(rkInt) + regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].node)) of opcSubStr: - decodeBC(nkStrLit) + decodeBC(rkStr) inc pc assert c.code[pc].opcode == opcSubStr let rd = c.code[pc].regA - regs[ra].strVal = substr(regs[rb].strVal, regs[rc].intVal.int, - regs[rd].intVal.int) + createStr regs[ra] + regs[ra].node.strVal = substr(regs[rb].node.strVal, + regs[rc].intVal.int, regs[rd].intVal.int) of opcRangeChck: let rb = instr.regB let rc = instr.regC - if not (leValueConv(regs[rb], regs[ra]) and - leValueConv(regs[ra], regs[rc])): + if not (leValueConv(regs[rb].regToNode, regs[ra].regToNode) and + leValueConv(regs[ra].regToNode, regs[rc].regToNode)): stackTrace(c, tos, pc, errGenerated, msgKindToString(errIllegalConvFromXtoY) % [ "unknown type" , "unknown type"]) @@ -644,8 +669,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = # dest = call regStart, n; where regStart = fn, arg1, ... let rb = instr.regB let rc = instr.regC - let isClosure = regs[rb].kind == nkPar - let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym + let bb = regs[rb].node + let isClosure = bb.kind == nkPar + let prc = if not isClosure: bb.sym else: bb.sons[0].sym if sfImportc in prc.flags: if allowFFI notin c.features: globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI") @@ -659,7 +685,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = rb+1, rc-1, c.debug[pc]) if newValue.kind != nkEmpty: assert instr.opcode == opcIndCallAsgn - asgnRef(regs[ra], newValue) + putIntoReg(regs[ra], newValue) else: globalError(c.debug[pc], errGenerated, "VM not built with FFI support") elif prc.kind != skTemplate: @@ -668,15 +694,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode = var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset) if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro: - newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info) - # pass every parameter by var (the language definition allows this): + putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info)) for i in 1 .. rc-1: newFrame.slots[i] = regs[rb+i] if isClosure: - newFrame.slots[rc] = regs[rb].sons[1] + newFrame.slots[rc].kind = rkNode + newFrame.slots[rc].node = regs[rb].node.sons[1] # allocate the temporaries: - for i in rc+ord(isClosure) .. = 0) + regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) >= 0) of opcIs: - decodeBC(nkIntLit) - let t1 = regs[rb].typ.skipTypes({tyTypeDesc}) + decodeBC(rkInt) + let t1 = regs[rb].node.typ.skipTypes({tyTypeDesc}) let t2 = c.types[regs[rc].intVal.int] # XXX: This should use the standard isOpImpl let match = if t2.kind == tyUserTypeClass: true else: sameType(t1, t2) regs[ra].intVal = ord(match) of opcSetLenSeq: - decodeB(nkBracket) - let newLen = regs[rb].getOrdValue.int - setLen(regs[ra].sons, newLen) + decodeB(rkNode) + let newLen = regs[rb].intVal.int + if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) + else: setLen(regs[ra].node.sons, newLen) of opcSwap, opcReset: internalError(c.debug[pc], "too implement") of opcIsNil: - decodeB(nkIntLit) - regs[ra].intVal = ord(regs[rb].skipMeta.kind == nkNilLit) + decodeB(rkInt) + regs[ra].intVal = ord(regs[rb].node.kind == nkNilLit) of opcNBindSym: - decodeBx(nkMetaNode) - setMeta(regs[ra], copyTree(c.constants.sons[rbx])) + decodeBx(rkNode) + regs[ra].node = copyTree(c.constants.sons[rbx]) of opcNChild: - decodeBC(nkMetaNode) - if regs[rb].kind != nkMetaNode: - internalError(c.debug[pc], "no MetaNode") + decodeBC(rkNode) let idx = regs[rc].intVal.int - let src = regs[rb].uast + let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: - setMeta(regs[ra], src.sons[idx]) + regs[ra].node = src.sons[idx] else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcNSetChild: - decodeBC(nkMetaNode) + decodeBC(rkNode) let idx = regs[rb].intVal.int - var dest = regs[ra].uast + var dest = regs[ra].node if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len: - dest.sons[idx] = regs[rc].uast + dest.sons[idx] = regs[rc].node else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcNAdd: - decodeBC(nkMetaNode) - var u = regs[rb].uast - u.add(regs[rc].uast) - setMeta(regs[ra], u) + decodeBC(rkNode) + var u = regs[rb].node + u.add(regs[rc].node) + regs[ra].node = u of opcNAddMultiple: - decodeBC(nkMetaNode) - let x = regs[rc] - var u = regs[rb].uast + decodeBC(rkNode) + let x = regs[rc].node + var u = regs[rb].node # XXX can be optimized: - for i in 0.. ord(high(TNodeKind)) or k == ord(nkMetaNode): + if k < 0 or k > ord(high(TNodeKind)): internalError(c.debug[pc], "request to create a NimNode of invalid kind") - let cc = regs[rc].skipMeta - setMeta(regs[ra], newNodeI(TNodeKind(int(k)), - if cc.kind == nkNilLit: c.debug[pc] else: cc.info)) - regs[ra].sons[0].flags.incl nfIsRef + let cc = regs[rc].node + regs[ra].node = newNodeI(TNodeKind(int(k)), + if cc.kind == nkNilLit: c.debug[pc] else: cc.info) + regs[ra].node.flags.incl nfIsRef of opcNCopyNimNode: - decodeB(nkMetaNode) - setMeta(regs[ra], copyNode(regs[rb].skipMeta)) + decodeB(rkNode) + regs[ra].node = copyNode(regs[rb].node) of opcNCopyNimTree: - decodeB(nkMetaNode) - setMeta(regs[ra], copyTree(regs[rb])) + decodeB(rkNode) + regs[ra].node = copyTree(regs[rb].node) of opcNDel: - decodeBC(nkMetaNode) + decodeBC(rkNode) let bb = regs[rb].intVal.int for i in countup(0, regs[rc].intVal.int-1): - delSon(regs[ra].uast, bb) + delSon(regs[ra].node, bb) of opcGenSym: - decodeBC(nkMetaNode) + decodeBC(rkNode) let k = regs[rb].intVal - let name = if regs[rc].strVal.len == 0: ":tmp" else: regs[rc].strVal + let name = if regs[rc].node.strVal.len == 0: ":tmp" + else: regs[rc].node.strVal if k < 0 or k > ord(high(TSymKind)): internalError(c.debug[pc], "request to create symbol of invalid kind") var sym = newSym(k.TSymKind, name.getIdent, c.module, c.debug[pc]) incl(sym.flags, sfGenSym) - setMeta(regs[ra], newSymNode(sym)) + regs[ra].node = newSymNode(sym) of opcTypeTrait: # XXX only supports 'name' for now; we can use regC to encode the # type trait operation - decodeB(nkStrLit) - var typ = regs[rb].typ + decodeB(rkStr) + var typ = regs[rb].node.typ internalAssert typ != nil while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0] - regs[ra].strVal = typ.typeToString(preferExported) - of opcGlobalOnce: - let rb = instr.regBx - if c.globals.sons[rb - wordExcess - 1].kind != nkEmpty: - # skip initialization instructions: - while true: - inc pc - if c.code[pc].opcode in {opcWrGlobal, opcWrGlobalRef} and - c.code[pc].regBx == rb: - break - of opcGlobalAlias: - let rb = instr.regBx - wordExcess - 1 - regs[ra] = c.globals.sons[rb] + createStr regs[ra] + regs[ra].node.strVal = typ.typeToString(preferExported) inc pc proc fixType(result, n: PNode) {.inline.} = @@ -1074,8 +1124,7 @@ proc fixType(result, n: PNode) {.inline.} = proc execute(c: PCtx, start: int): PNode = var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) - for i in 0 .. = L # return value: - tos.slots[0] = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) + tos.slots[0].kind = rkNode + tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0]) # setup parameters: for i in 1 .. < min(tos.slots.len, L): - tos.slots[i] = setupMacroParam(n.sons[i]) + tos.slots[i].kind = rkNode + tos.slots[i].node = setupMacroParam(n.sons[i]) # temporary storage: - for i in L .. = high(TRegister): + internalError("cannot generate code; too many registers required") result = TRegister(c.maxSlots) c.slots[c.maxSlots] = (inUse: true, kind: k) inc c.maxSlots -proc getGlobalSlot(c: PCtx; n: PNode; s: PSym): TRegister = - let p = c.prc - for i in 0 .. p.maxSlots-1: - if p.globals[i] == s.id: return TRegister(i) - - result = TRegister(p.maxSlots) - p.slots[p.maxSlots] = (inUse: true, kind: slotFixedVar) - p.globals[p.maxSlots] = s.id - inc p.maxSlots - # XXX this is still not correct! We need to load the global in a proc init - # section, otherwise control flow could lead to a usage before it's been - # loaded. - c.gABx(n, opcGlobalAlias, result, s.position) - # XXX add some internal asserts here - proc freeTemp(c: PCtx; r: TRegister) = let c = c.prc if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false @@ -322,13 +309,7 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = c.patch(L1) proc nilLiteral(n: PNode): PNode = - if n.kind == nkNilLit and n.typ.sym != nil and - n.typ.sym.magic == mPNimrodNode: - let nilo = newNodeIT(nkNilLit, n.info, n.typ) - result = newNodeIT(nkMetaNode, n.info, n.typ) - result.add nilo - else: - result = n + result = n proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len @@ -471,20 +452,24 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) = c.freeTempRange(x, n.len) proc needsAsgnPatch(n: PNode): bool = - n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr} + n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, + nkDerefExpr, nkHiddenDeref} proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0]) + let dest = c.genx(le.sons[0], {gfAddrOf}) let idx = c.genx(le.sons[1]) - c.gABC(le, opcWrArrRef, dest, idx, value) + c.gABC(le, opcWrArr, dest, idx, value) of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0]) + let dest = c.genx(left.sons[0], {gfAddrOf}) let idx = c.genx(left.sons[1]) - c.gABC(left, opcWrObjRef, dest, idx, value) + c.gABC(left, opcWrObj, dest, idx, value) + of nkDerefExpr, nkHiddenDeref: + let dest = c.genx(le.sons[0], {gfAddrOf}) + c.gABC(le, opcWrDeref, dest, value) else: discard @@ -598,6 +583,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) c.gABx(n, opc, 0, genType(c, n.typ)) + if opc == opcConv: c.gABx(n, opc, 0, genType(c, arg.typ)) c.freeTemp(tmp) proc genCard(c: PCtx; n: PNode; dest: var TDest) = @@ -905,6 +891,10 @@ const tyFloat, tyFloat32, tyFloat64, tyFloat128, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64} +proc fitsRegister*(t: PType): bool = + t.skipTypes(abstractInst-{tyTypeDesc}).kind in { + tyRange, tyEnum, tyBool, tyInt..tyUInt64} + proc requiresCopy(n: PNode): bool = if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes: result = false @@ -919,7 +909,8 @@ proc unneededIndirection(n: PNode): bool = proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = # a nop for certain types - let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags + let isAddr = opc in {opcAddrNode, opcAddrReg} + let flags = if isAddr: flags+{gfAddrOf} else: flags # consider: # proc foo(f: var ref int) = # f = new(int) @@ -929,12 +920,17 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; # # The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for # nkAddr we must not use 'unneededIndirection', but for deref we use it. - if opc != opcAddr and unneededIndirection(n.sons[0]): + if not isAddr and unneededIndirection(n.sons[0]): gen(c, n.sons[0], dest, flags) else: let tmp = c.genx(n.sons[0], flags) if dest < 0: dest = c.getTemp(n.typ) - gABC(c, n, opc, dest, tmp) + if not isAddr: + gABC(c, n, opc, dest, tmp) + elif c.prc.slots[tmp].kind >= slotTempUnknown: + gABC(c, n, opcAddrReg, dest, tmp) + else: + gABC(c, n, opcAddrNode, dest, tmp) c.freeTemp(tmp) proc whichAsgnOpc(n: PNode): TOpcode = @@ -952,8 +948,7 @@ proc whichAsgnOpc(n: PNode): TOpcode = proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef -proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = - if isRef(n.typ): succ(opc) else: opc +proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) @@ -974,29 +969,36 @@ proc setSlot(c: PCtx; v: PSym) = proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: - let dest = c.genx(le.sons[0]) + let dest = c.genx(le.sons[0], {gfAddrOf}) let idx = c.genx(le.sons[1]) let tmp = c.genx(ri) if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { tyString, tyCString}: c.gABC(le, opcWrStrIdx, dest, idx, tmp) else: - c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp) + c.gABC(le, opcWrArr, dest, idx, tmp) c.freeTemp(tmp) of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] - let dest = c.genx(left.sons[0]) + let dest = c.genx(left.sons[0], {gfAddrOf}) let idx = c.genx(left.sons[1]) let tmp = c.genx(ri) - c.gABC(left, whichAsgnOpc(left, opcWrObj), dest, idx, tmp) + c.gABC(left, opcWrObj, dest, idx, tmp) + c.freeTemp(tmp) + of nkDerefExpr, nkHiddenDeref: + let dest = c.genx(le, {gfAddrOf}) + let tmp = c.genx(ri) + c.gABC(le, opcWrDeref, dest, tmp) c.freeTemp(tmp) of nkSym: let s = le.sym if s.isGlobal: withTemp(tmp, le.typ): - gen(c, ri, tmp) - c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position) + c.gen(le, tmp, {gfAddrOf}) + let val = c.genx(ri) + c.gABC(le, opcWrDeref, tmp, val) + c.freeTemp(val) else: if s.kind == skForVar and c.mode == emRepl: c.setSlot s internalAssert s.position > 0 or (s.position == 0 and @@ -1004,7 +1006,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = var dest: TRegister = s.position + ord(s.kind == skParam) gen(c, ri, dest) else: - let dest = c.genx(le) + let dest = c.genx(le, {gfAddrOf}) genAsgn(c, dest, ri, requiresCopy) proc genLit(c: PCtx; n: PNode; dest: var TDest) = @@ -1040,10 +1042,6 @@ proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = # This is rather hard to support, due to the laziness of the VM code # generator. See tests/compile/tmacro2 for why this is necesary: # var decls{.compileTime.}: seq[PNimrodNode] = @[] - c.gABx(n, opcGlobalOnce, 0, s.position) - let tmp = c.genx(s.ast) - c.gABx(n, whichAsgnOpc(n, opcWrGlobal), tmp, s.position) - c.freeTemp(tmp) proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = let s = n.sym @@ -1055,11 +1053,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = if s.position == 0: if sfImportc in s.flags: c.importcSym(n.info, s) else: genGlobalInit(c, n, s) - if dest < 0: - dest = c.getGlobalSlot(n, s) - #c.gABx(n, opcAliasGlobal, dest, s.position) - else: - c.gABx(n, opcLdGlobal, dest, s.position) + c.gABx(n, opcLdGlobal, dest, s.position) else: if s.kind == skForVar and c.mode == emRepl: c.setSlot s if s.position > 0 or (s.position == 0 and @@ -1078,7 +1072,13 @@ proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; let a = c.genx(n.sons[0], flags) let b = c.genx(n.sons[1], {}) if dest < 0: dest = c.getTemp(n.typ) - c.gABC(n, (if gfAddrOf in flags: succ(opc) else: opc), dest, a, b) + if gfAddrOf notin flags and fitsRegister(n.typ): + var cc = c.getTemp(n.typ) + c.gABC(n, opc, cc, a, b) + c.gABC(n, opcNodeToReg, dest, cc) + c.freeTemp(cc) + else: + c.gABC(n, opc, dest, a, b) c.freeTemp(a) c.freeTemp(b) @@ -1121,12 +1121,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkFloatLit, info, t) of tyVar, tyPointer, tyPtr, tyCString, tySequence, tyString, tyExpr, tyStmt, tyTypeDesc, tyStatic, tyRef: - if t.sym != nil and t.sym.magic == mPNimrodNode: - let nilo = newNodeIT(nkNilLit, info, t) - result = newNodeIT(nkMetaNode, info, t) - result.add nilo - else: - result = newNodeIT(nkNilLit, info, t) + result = newNodeIT(nkNilLit, info, t) of tyProc: if t.callConv != ccClosure: result = newNodeIT(nkNilLit, info, t) @@ -1154,6 +1149,9 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = result = newNodeIT(nkCurly, info, t) else: internalError("getNullValue: " & $t.kind) +proc ldNullOpcode(t: PType): TOpcode = + if fitsRegister(t): opcLdNullReg else: opcLdNull + proc genVarSection(c: PCtx; n: PNode) = for a in n: if a.kind == nkCommentStmt: continue @@ -1165,7 +1163,7 @@ proc genVarSection(c: PCtx; n: PNode) = # v = t[i] var v: TDest = -1 genRdVar(c, a[i], v) - c.gABC(n, opcLdObj, v, tmp, i) + c.gABC(n, opcWrObj, v, tmp, i) # XXX globals? c.freeTemp(tmp) elif a.sons[0].kind == nkSym: @@ -1177,27 +1175,27 @@ proc genVarSection(c: PCtx; n: PNode) = let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast c.globals.add(sa) s.position = c.globals.len - # "Once support" is unnecessary here if a.sons[2].kind == nkEmpty: when false: withTemp(tmp, s.typ): c.gABx(a, opcLdNull, tmp, c.genType(s.typ)) c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position) else: - let tmp = genx(c, a.sons[2]) - c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position) - c.freeTemp(tmp) + let tmp = c.genx(a.sons[0], {gfAddrOf}) + let val = c.genx(a.sons[2]) + c.gABC(a, opcWrDeref, tmp, val) + c.freeTemp(val) else: setSlot(c, s) if a.sons[2].kind == nkEmpty: - c.gABx(a, opcLdNull, s.position, c.genType(s.typ)) + c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ)) else: gen(c, a.sons[2], s.position.TRegister) else: # assign to a.sons[0]; happens for closures if a.sons[2].kind == nkEmpty: let tmp = genx(c, a.sons[0]) - c.gABx(a, opcLdNull, tmp, c.genType(a.sons[0].typ)) + c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a.sons[0].typ)) c.freeTemp(tmp) else: genAsgn(c, a.sons[0], a.sons[2], true) @@ -1208,7 +1206,7 @@ proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = if n.len > 0: let intType = getSysType(tyInt) var tmp = getTemp(c, intType) - c.gABx(n, opcLdNull, tmp, c.genType(intType)) + c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) for x in n: let a = c.genx(x) c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a) @@ -1320,8 +1318,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkDotExpr: genObjAccess(c, n, dest, flags) of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags) of nkBracketExpr: genArrAccess(c, n, dest, flags) - of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref, flags) - of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr, flags) + of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags) + of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags) of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest) of nkCaseStmt: genCase(c, n, dest) of nkWhileStmt: diff --git a/todo.txt b/todo.txt index d3ed9957f..3985343e0 100644 --- a/todo.txt +++ b/todo.txt @@ -1,9 +1,8 @@ version 0.9.4 ============= -- fix macros\tstringinterp.nim: - - problem: needs another level of indirection for 'seq' - - problem: deref is not correct +- make vmgen insert opcNodeToReg, opcRegToNode +- fix macros\tstringinterp.nim - fix GC issues - test and fix showoff @@ -40,7 +39,7 @@ version 0.9.x - built-in 'getImpl' - change comment handling in the AST; that's lots of work as c2nim and pas2nim - make use of the fast every node can have a comment! + make use of the fact every node can have a comment! version 0.9.X From ee74706c3b1e040dded3baf2ee3c05ae111c968a Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Feb 2014 01:09:43 +0100 Subject: [PATCH 071/249] fixed opcConv --- compiler/astalgo.nim | 10 ++++++++-- compiler/vm.nim | 32 +++++++++++++++++++++++++------- compiler/vmgen.nim | 5 ++--- todo.txt | 1 - 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 64c1b717c..cce2cc215 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -337,7 +337,10 @@ proc treeToYamlAux(n: PNode, marker: var TIntSet, indent: int, appf(result, ",$N$1\"floatVal\": $2", [istr, toRope(n.floatVal.toStrMaxPrecision)]) of nkStrLit..nkTripleStrLit: - appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + if n.strVal.isNil: + appf(result, ",$N$1\"strVal\": null", [istr]) + else: + appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: appf(result, ",$N$1\"sym\": $2", [istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)]) @@ -407,7 +410,10 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope = appf(result, ",$N$1\"floatVal\": $2", [istr, toRope(n.floatVal.toStrMaxPrecision)]) of nkStrLit..nkTripleStrLit: - appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + if n.strVal.isNil: + appf(result, ",$N$1\"strVal\": null", [istr]) + else: + appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: appf(result, ",$N$1\"sym\": $2_$3", [istr, toRope(n.sym.name.s), toRope(n.sym.id)]) diff --git a/compiler/vm.nim b/compiler/vm.nim index 8480cd1c6..f17da1c69 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -257,11 +257,25 @@ proc opConv*(dest: var TRegister, src: TRegister, desttyp, srctyp: PType): bool myreset(dest) dest.kind = rkStr dest.node = newNode(nkStrLit) - case srctyp.skipTypes(abstractRange).kind - of tyEnum: - dest.node.strVal = "too implement" #ordinalValToString(src) - of tyInt..tyInt64, tyUInt..tyUInt64: + let styp = srctyp.skipTypes(abstractRange) + case styp.kind + of tyEnum: + let n = styp.n + let x = src.intVal.int + if x <% n.len and (let f = n.sons[x].sym; f.position == x): + dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal + else: + for i in 0.. Date: Sat, 22 Feb 2014 01:37:34 +0100 Subject: [PATCH 072/249] simple macros work again --- compiler/vm.nim | 72 ++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index f17da1c69..820a2022c 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -24,14 +24,14 @@ when hasFFI: type TRegisterKind = enum - rkNone, rkNode, rkStr, rkInt, rkFloat, rkRegisterAddr, rkNodeAddr + rkNone, rkNode, rkInt, rkFloat, rkRegisterAddr, rkNodeAddr TRegister = object # with a custom mark proc, we could use the same # data representation as LuaJit (tagged NaNs). case kind: TRegisterKind of rkNone: nil of rkInt: intVal: BiggestInt of rkFloat: floatVal: BiggestFloat - of rkNode, rkStr: node: PNode + of rkNode: node: PNode of rkRegisterAddr: regAddr: ptr TRegister of rkNodeAddr: nodeAddr: ptr PNode @@ -111,6 +111,9 @@ template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) template createStr(x) = if x.node.isNil: x.node = newNode(nkStrLit) + elif x.node.kind == nkNilLit: + system.reset(x.node[]) + x.node.kind = nkStrLit proc moveConst(x: var TRegister, y: TRegister) = if x.kind != y.kind: @@ -120,7 +123,7 @@ proc moveConst(x: var TRegister, y: TRegister) = of rkNone: discard of rkInt: x.intVal = y.intVal of rkFloat: x.floatVal = y.floatVal - of rkStr, rkNode: x.node = y.node + of rkNode: x.node = y.node of rkRegisterAddr: x.regAddr = y.regAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr @@ -157,18 +160,13 @@ proc asgnComplex(x: var TRegister, y: TRegister) = of rkNone: discard of rkInt: x.intVal = y.intVal of rkFloat: x.floatVal = y.floatVal - of rkStr, rkNode: x.node = copyValue(y.node) + of rkNode: x.node = copyValue(y.node) of rkRegisterAddr: x.regAddr = y.regAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr proc putIntoNode(n: PNode; x: TRegister) = case x.kind of rkNone: discard - of rkStr: - if n.kind == nkNilLit: - system.reset(n[]) - n.kind = nkStrLit - n.strVal = x.node.strVal of rkInt: n.intVal = x.intVal of rkFloat: n.floatVal = x.floatVal of rkNode: n[] = x.node[] @@ -178,7 +176,7 @@ proc putIntoNode(n: PNode; x: TRegister) = proc putIntoReg(dest: var TRegister; n: PNode) = case n.kind of nkStrLit..nkTripleStrLit: - dest.kind = rkStr + dest.kind = rkNode createStr(dest) dest.node.strVal = n.strVal of nkCharLit..nkUInt64Lit: @@ -196,12 +194,12 @@ proc regToNode(x: TRegister): PNode = of rkNone: result = newNode(nkEmpty) of rkInt: result = newNode(nkIntLit); result.intVal = x.intVal of rkFloat: result = newNode(nkFloatLit); result.floatVal = x.floatVal - of rkStr, rkNode: result = x.node + of rkNode: result = x.node of rkRegisterAddr: result = regToNode(x.regAddr[]) of rkNodeAddr: result = x.nodeAddr[] template getstr(a: expr): expr = - (if a.kind == rkStr: a.node.strVal else: $chr(int(a.intVal))) + (if a.kind == rkNode: a.node.strVal else: $chr(int(a.intVal))) proc pushSafePoint(f: PStackFrame; pc: int) = if f.safePoints.isNil: f.safePoints = @[] @@ -253,9 +251,9 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = proc opConv*(dest: var TRegister, src: TRegister, desttyp, srctyp: PType): bool = if desttyp.kind == tyString: - if dest.kind != rkStr: + if dest.kind != rkNode: myreset(dest) - dest.kind = rkStr + dest.kind = rkNode dest.node = newNode(nkStrLit) let styp = srctyp.skipTypes(abstractRange) case styp.kind @@ -355,7 +353,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = decodeB(rkInt) regs[ra].intVal = regs[rb].intVal of opcAsgnStr: - decodeB(rkStr) + decodeB(rkNode) createStr regs[ra] regs[ra].node.strVal = regs[rb].node.strVal of opcAsgnFloat: @@ -423,7 +421,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = decodeBC(rkNode) putIntoNode(regs[ra].node.sons[rb], regs[rc]) of opcWrStrIdx: - decodeBC(rkStr) + decodeBC(rkNode) let idx = regs[rb].intVal.int if idx <% regs[ra].node.strVal.len: regs[ra].node.strVal[idx] = chr(regs[rc].intVal) @@ -476,7 +474,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = regs[ra].intVal = regs[rb].node.safeLen - imm of opcLenStr: decodeBImm(rkInt) - assert regs[rb].kind == rkStr + assert regs[rb].kind == rkNode regs[ra].intVal = regs[rb].node.strVal.len - imm of opcIncl: decodeB(rkNode) @@ -638,17 +636,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = move(regs[ra].node.sons, nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons) of opcConcatStr: - decodeBC(rkStr) + decodeBC(rkNode) createStr regs[ra] regs[ra].node.strVal = getstr(regs[rb]) for i in rb+1..rb+rc-1: regs[ra].node.strVal.add getstr(regs[i]) of opcAddStrCh: - decodeB(rkStr) + decodeB(rkNode) createStr regs[ra] regs[ra].node.strVal.add(regs[rb].intVal.chr) of opcAddStrStr: - decodeB(rkStr) + decodeB(rkNode) createStr regs[ra] regs[ra].node.strVal.add(regs[rb].node.strVal) of opcAddSeqElem: @@ -657,14 +655,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = of opcEcho: let rb = instr.regB for i in ra..ra+rb-1: - #if regs[i].kind != rkStr: debug regs[i] + #if regs[i].kind != rkNode: debug regs[i] write(stdout, regs[i].node.strVal) writeln(stdout, "") of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].node)) of opcSubStr: - decodeBC(rkStr) + decodeBC(rkNode) inc pc assert c.code[pc].opcode == opcSubStr let rd = c.code[pc].regA @@ -729,7 +727,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = c.module var macroCall = newNodeI(nkCall, c.debug[pc]) macroCall.add(newSymNode(prc)) - for i in 1 .. rc-1: macroCall.add(regs[rb+i].node) + for i in 1 .. rc-1: macroCall.add(regs[rb+i].regToNode) let a = evalTemplate(macroCall, prc, genSymOwner) ensureKind(rkNode) regs[ra].node = a @@ -806,7 +804,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = for i in 0 .. 0: typ = typ.sons[0] From 067c3816ba317d2d2bcafdd3c2806df886b855b2 Mon Sep 17 00:00:00 2001 From: Fabio Cevasco Date: Sat, 22 Feb 2014 10:07:37 +0100 Subject: [PATCH 073/249] Rewrote the changes to findAll using if/else --- lib/pure/pegs.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 3b1516e17..68b1ab223 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -838,9 +838,9 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string = var L = rawMatch(s, pattern, i, c) if L < 0: inc(i, 1) - continue - yield substr(s, i, i+L-1) - inc(i, L) + else: + yield substr(s, i, i+L-1) + inc(i, L) proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {. nosideEffect, rtl, extern: "npegs$1".} = From 3aa7f65240005a7d5ce26519248fa435f3ed3e5b Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sat, 22 Feb 2014 10:18:33 +0100 Subject: [PATCH 074/249] Addresses issues raised on #947. Refs #800. * Uses errGenerated instead of deprecated extending of enums. * Reduces bloat and usefulness of end user error messages. * Limits checks to C, Cpp and Objc targets. --- compiler/msgs.nim | 3 +-- compiler/pragmas.nim | 20 +++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8c41a8191..0140d1ac4 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -105,7 +105,7 @@ type errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errXCannotBeClosure, errXMustBeCompileTime, errCannotInferTypeOfTheLiteral, - errBadExport, errUser + errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnDeprecated, warnConfigDeprecated, @@ -350,7 +350,6 @@ const errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXMustBeCompileTime: "'$1' can only be used in compile-time context", errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", - errBadExport: "invalid exported symbol, $1", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 6e21a85b9..75c16f6cd 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -99,25 +99,23 @@ proc makeExternImport(s: PSym, extname: string) = const invalidIdentChars = AllChars - IdentChars -proc validateExternName(s: PSym, info: TLineInfo) = +proc validateExternCName(s: PSym, info: TLineInfo) = ## Validates that the symbol name in s.loc.r is a valid C identifier. ## ## Valid identifiers are those alphanumeric including the underscore not - ## starting with a number. If the check fails, a friendly error will be + ## starting with a number. If the check fails, a generic error will be ## displayed to the user. let target = ropeToStr(s.loc.r) - if target.len < 1: - localError(info, errBadExport, "can't be zero length") - if not (target[0] in IdentStartChars): - localError(info, errBadExport, "'" & target & "' can't start with a number") - if not target.allCharsInSet(IdentChars): - let pos = target.find(invalidIdentChars, 1) - localError(info, errBadExport, "'" & target & - "' contains bad character at byte " & $pos) + if target.len < 1 or (not (target[0] in IdentStartChars)) or + (not target.allCharsInSet(IdentChars)): + localError(info, errGenerated, "invalid exported symbol") proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = setExternName(s, extname) - validateExternName(s, info) + case gCmd + of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC: + validateExternCName(s, info) + else: discard incl(s.flags, sfExportc) proc processImportCompilerProc(s: PSym, extname: string) = From 3b5825e9bcf09bb6da98601d07af10c2640f4cd1 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 22 Feb 2014 17:22:19 +0000 Subject: [PATCH 075/249] Implemented selector support for asyncio2. --- lib/posix/epoll.nim | 8 +- lib/posix/posix.nim | 2 +- lib/pure/asyncio2.nim | 231 ++++++++++++++++++++--- lib/pure/net.nim | 17 +- lib/pure/selectors.nim | 357 ++++++++++++++++++------------------ lib/pure/sockets2.nim | 8 +- tests/async/tasyncawait.nim | 4 +- 7 files changed, 416 insertions(+), 211 deletions(-) diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index d50394f60..366521551 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -7,6 +7,8 @@ # distribution, for details about the copyright. # +from posix import TSocketHandle + const EPOLLIN* = 0x00000001 EPOLLPRI* = 0x00000002 @@ -33,8 +35,8 @@ const type epoll_data* {.importc: "union epoll_data", header: "", pure, final.} = object # TODO: This is actually a union. - thePtr* {.importc: "ptr".}: pointer # \ - #fd*: cint + #thePtr* {.importc: "ptr".}: pointer + fd*: cint # \ #u32*: uint32 #u64*: uint64 @@ -54,7 +56,7 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1", ## Same as epoll_create but with an FLAGS parameter. The unused SIZE ## parameter has been dropped. -proc epoll_ctl*(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint {. +proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {. importc: "epoll_ctl", header: "".} ## Manipulate an epoll instance "epfd". Returns 0 in case of success, ## -1 in case of error ( the "errno" variable will contain the diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 41260b36f..bb4039c1b 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -2356,7 +2356,7 @@ proc FD_ZERO*(a1: var TFdSet) {.importc, header: "".} proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec, a6: var Tsigset): cint {.importc, header: "".} -proc select*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {. +proc select*(a1: cint | TSocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {. importc, header: "".} when hasSpawnH: diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 8541b2ba7..12d4cb5a3 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -9,8 +9,6 @@ import os, oids, tables, strutils, macros -import winlean - import sockets2, net ## Asyncio2 @@ -93,7 +91,10 @@ proc failed*[T](future: PFuture[T]): bool = ## Determines whether ``future`` completed with an error. future.error != nil -when defined(windows): +# TODO: Get rid of register. Do it implicitly. + +when defined(windows) or defined(nimdoc): + import winlean type TCompletionKey = dword @@ -293,7 +294,10 @@ when defined(windows): proc recv*(p: PDispatcher, socket: TSocketHandle, size: int, flags: int = 0): PFuture[string] = ## Reads ``size`` bytes from ``socket``. Returned future will complete once - ## all of the requested data is read. + ## all of the requested data is read. If socket is disconnected during the + ## recv operation then the future may complete with only a part of the + ## requested data read. If socket is disconnected and no data is available + ## to be read then the future will complete with a value of ``""``. var retFuture = newFuture[string]() @@ -448,24 +452,206 @@ when defined(windows): return retFuture - proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] = - ## Accepts a new connection. Returns a future containing the client socket - ## corresponding to that connection. - ## The future will complete when the connection is successfully accepted. - var retFut = newFuture[TSocketHandle]() - var fut = p.acceptAddr(socket) - fut.callback = - proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) = - assert future.finished - if future.failed: - retFut.fail(future.error) - else: - retFut.complete(future.read.client) - return retFut - initAll() else: - # TODO: Selectors. + import selectors + from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK + type + TCallback = proc (sock: TSocketHandle): bool {.closure.} + + PData* = ref object of PObject + sock: TSocketHandle + readCBs: seq[TCallback] + writeCBs: seq[TCallback] + + PDispatcher* = ref object + selector: PSelector + + proc newDispatcher*(): PDispatcher = + new result + result.selector = newSelector() + + proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) = + assert sock in p.selector + echo("Update: ", events) + if events == {}: + discard p.selector.unregister(sock) + else: + discard p.selector.update(sock, events) + + proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = + if sock notin p.selector: + var data = PData(sock: sock, readCBs: @[cb], writeCBs: @[]) + p.selector.register(sock, {EvRead}, data.PObject) + else: + p.selector[sock].data.PData.readCBs.add(cb) + p.update(sock, p.selector[sock].events + {EvRead}) + + proc addWrite(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = + if sock notin p.selector: + var data = PData(sock: sock, readCBs: @[], writeCBs: @[cb]) + p.selector.register(sock, {EvWrite}, data.PObject) + else: + p.selector[sock].data.PData.writeCBs.add(cb) + p.update(sock, p.selector[sock].events + {EvWrite}) + + proc poll*(p: PDispatcher, timeout = 500) = + for info in p.selector.select(timeout): + let data = PData(info.key.data) + assert data.sock == info.key.fd + echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events) + + if EvRead in info.events: + var newReadCBs: seq[TCallback] = @[] + for cb in data.readCBs: + if not cb(data.sock): + # Callback wants to be called again. + newReadCBs.add(cb) + data.readCBs = newReadCBs + + if EvWrite in info.events: + var newWriteCBs: seq[TCallback] = @[] + for cb in data.writeCBs: + if not cb(data.sock): + # Callback wants to be called again. + newWriteCBs.add(cb) + data.writeCBs = newWriteCBs + + var newEvents: set[TEvent] + if data.readCBs.len != 0: newEvents = {EvRead} + if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite} + p.update(data.sock, newEvents) + + proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, + af = AF_INET): PFuture[int] = + var retFuture = newFuture[int]() + + proc cb(sock: TSocketHandle): bool = + # We have connected. + retFuture.complete(0) + return true + + var aiList = getAddrInfo(address, port, af) + var success = false + var lastError: TOSErrorCode + var it = aiList + while it != nil: + var ret = connect(socket, it.ai_addr, it.ai_addrlen.TSocklen) + if ret == 0: + # Request to connect completed immediately. + success = true + retFuture.complete(0) + break + else: + lastError = osLastError() + if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS: + success = true + addWrite(p, socket, cb) + break + else: + success = false + it = it.ai_next + + dealloc(aiList) + if not success: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + return retFuture + + proc recv*(p: PDispatcher, socket: TSocketHandle, size: int, + flags: int = 0): PFuture[string] = + var retFuture = newFuture[string]() + + var readBuffer = newString(size) + var sizeRead = 0 + + proc cb(sock: TSocketHandle): bool = + result = true + let netSize = size - sizeRead + let res = recv(sock, addr readBuffer[sizeRead], netSize, flags.cint) + if res < 0: + let lastError = osLastError() + if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + else: + result = false # We still want this callback to be called. + elif res == 0: + # Disconnected + if sizeRead == 0: + retFuture.complete("") + else: + readBuffer.setLen(sizeRead) + retFuture.complete(readBuffer) + else: + sizeRead.inc(res) + if res != netSize: + result = false # We want to read all the data requested. + else: + retFuture.complete(readBuffer) + + addRead(p, socket, cb) + return retFuture + + proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = + var retFuture = newFuture[int]() + + var written = 0 + + proc cb(sock: TSocketHandle): bool = + result = true + let netSize = data.len-written + var d = data.cstring + let res = send(sock, addr d[written], netSize, 0.cint) + if res < 0: + let lastError = osLastError() + if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + else: + result = false # We still want this callback to be called. + else: + written.inc(res) + if res != netSize: + result = false # We still have data to send. + else: + retFuture.complete(0) + addWrite(p, socket, cb) + return retFuture + + + proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): + PFuture[tuple[address: string, client: TSocketHandle]] = + var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]() + proc cb(sock: TSocketHandle): bool = + result = true + var sockAddress: Tsockaddr_in + var addrLen = sizeof(sockAddress).TSocklen + var client = accept(sock, cast[ptr TSockAddr](addr(sockAddress)), + addr(addrLen)) + if client == osInvalidSocket: + let lastError = osLastError() + assert lastError.int32 notin {EWOULDBLOCK, EAGAIN} + if lastError.int32 == EINTR: + return false + else: + retFuture.fail(newException(EOS, osErrorMsg(lastError))) + else: + retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client)) + addRead(p, socket, cb) + return retFuture + +proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] = + ## Accepts a new connection. Returns a future containing the client socket + ## corresponding to that connection. + ## The future will complete when the connection is successfully accepted. + var retFut = newFuture[TSocketHandle]() + var fut = p.acceptAddr(socket) + fut.callback = + proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) = + assert future.finished + if future.failed: + retFut.fail(future.error) + else: + retFut.complete(future.read.client) + return retFut # -- Await Macro @@ -665,8 +851,7 @@ when isMainModule: var p = newDispatcher() var sock = socket() - #sock.setBlocking false - p.register(sock) + sock.setBlocking false when false: @@ -706,7 +891,7 @@ when isMainModule: var recvF = p.recv(sock, 10) recvF.callback = proc (future: PFuture[string]) = - echo("Read: ", future.read) + echo("Read ", future.read.len, ": ", future.read.repr) else: diff --git a/lib/pure/net.nim b/lib/pure/net.nim index bdcae677e..0ec007009 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -37,4 +37,19 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {. if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32: dealloc(aiList) osError(osLastError()) - dealloc(aiList) \ No newline at end of file + dealloc(aiList) + +proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} = + ## Sets blocking mode on socket + when defined(Windows): + var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking + if ioctlsocket(s, FIONBIO, addr(mode)) == -1: + osError(osLastError()) + else: # BSD sockets + var x: int = fcntl(s, F_GETFL, 0) + if x == -1: + osError(osLastError()) + else: + var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK + if fcntl(s, F_SETFL, mode) == -1: + osError(osLastError()) \ No newline at end of file diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 83c158da1..6482a01a6 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2013 Dominik Picheta +# (c) Copyright 2014 Dominik Picheta # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,212 +9,211 @@ # TODO: Docs. -import tables, os, unsigned -when defined(windows): - import winlean -else: - import posix +import tables, os, unsigned, hashes + +when defined(linux): import posix, epoll +elif defined(windows): import winlean + +proc hash*(x: TSocketHandle): THash {.borrow.} type TEvent* = enum EvRead, EvWrite - TSelectorKey* = object - fd: cint - events: set[TEvent] - data: PObject + PSelectorKey* = ref object + fd*: TSocketHandle + events*: set[TEvent] ## The events which ``fd`` listens for. + data*: PObject ## User object. - TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]] + TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]] - PSelector* = ref object of PObject ## Selector interface. - fds*: TTable[cint, TSelectorKey] - registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent], - data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].} - unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].} - selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].} - closeImpl*: proc (s: PSelector) {.nimcall.} - -template initSelector(r: expr) = - new r - r.fds = initTable[cint, TSelectorKey]() - -proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject): - TSelectorKey = - if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data) - -proc unregister*(s: PSelector, fd: cint): TSelectorKey = - ## - ## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey`` - ## will only have the ``fd`` field set. This is an optimisation and may - ## change in the future if a viable use case is presented. - if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd) - -proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] = - ## - ## The ``events`` field of the returned ``key`` contains the original events - ## for which the ``fd`` was bound. This is contrary to the ``events`` field - ## of the ``TReadyInfo`` tuple which determines which events are ready - ## on the ``fd``. - - if not s.selectImpl.isNil: result = s.selectImpl(s, timeout) - -proc close*(s: PSelector) = - if not s.closeImpl.isNil: s.closeImpl(s) - -# ---- Select() ---------------------------------------------------------------- - -type - PSelectSelector* = ref object of PSelector ## Implementation of select() - -proc ssRegister(s: PSelector, fd: cint, events: set[TEvent], - data: PObject): TSelectorKey = - if s.fds.hasKey(fd): - raise newException(EInvalidValue, "FD already exists in selector.") - var sk = TSelectorKey(fd: fd, events: events, data: data) - s.fds[fd] = sk - result = sk - -proc ssUnregister(s: PSelector, fd: cint): TSelectorKey = - result = s.fds[fd] - s.fds.del(fd) - -proc ssClose(s: PSelector) = nil - -proc timeValFromMilliseconds(timeout: int): TTimeVal = - if timeout != -1: - var seconds = timeout div 1000 - result.tv_sec = seconds.int32 - result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 - -proc createFdSet(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey], - m: var int) = - FD_ZERO(rd); FD_ZERO(wr) - for k, v in pairs(fds): - if EvRead in v.events: - m = max(m, int(k)) - FD_SET(k, rd) - if EvWrite in v.events: - m = max(m, int(k)) - FD_SET(k, wr) - -proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey]): - seq[TReadyInfo] = - result = @[] - for k, v in pairs(fds): - var events: set[TEvent] = {} - if FD_ISSET(k, rd) != 0'i32: - events = events + {EvRead} - if FD_ISSET(k, wr) != 0'i32: - events = events + {EvWrite} - result.add((v, events)) - -proc select(fds: TTable[cint, TSelectorKey], timeout = 500): - seq[TReadyInfo] = - var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout) - - var rd, wr: TFdSet - var m = 0 - createFdSet(rd, wr, fds, m) - - var retCode = 0 - if timeout != -1: - retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv))) - else: - retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil)) - - if retCode < 0: - OSError(OSLastError()) - elif retCode == 0: - return @[] - else: - return getReadyFDs(rd, wr, fds) - -proc ssSelect(s: PSelector, timeout: int): seq[TReadyInfo] = - result = select(s.fds, timeout) - -proc newSelectSelector*(): PSelectSelector = - initSelector(result) - result.registerImpl = ssRegister - result.unregisterImpl = ssUnregister - result.selectImpl = ssSelect - result.closeImpl = ssClose - -# ---- Epoll ------------------------------------------------------------------- - -when defined(linux): - import epoll +when defined(linux) or defined(nimdoc): type - PEpollSelector* = ref object of PSelector + PSelector* = ref object epollFD: cint events: array[64, ptr epoll_event] + fds: TTable[TSocketHandle, PSelectorKey] - TDataWrapper = object - fd: cint - boundEvents: set[TEvent] ## The events which ``fd`` listens for. - data: PObject ## User object. - - proc esRegister(s: PSelector, fd: cint, events: set[TEvent], - data: PObject): TSelectorKey = - var es = PEpollSelector(s) - var event: epoll_event + proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event = if EvRead in events: - event.events = EPOLLIN + result.events = EPOLLIN if EvWrite in events: - event.events = event.events or EPOLLOUT - - var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd - dw.fd = fd - dw.boundEvents = events - dw.data = data - event.data.thePtr = dw - - if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: - OSError(OSLastError()) - - result = TSelectorKey(fd: fd, events: events, data: data) + result.events = result.events or EPOLLOUT + result.data.fd = fd.cint - proc esUnregister(s: PSelector, fd: cint): TSelectorKey = - # We cannot find out the information about this ``fd`` from the epoll - # context. As such I will simply return an almost empty TSelectorKey. - var es = PEpollSelector(s) - if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: + proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], + data: PObject): PSelectorKey {.discardable.} = + ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent + ## ``events``. + if s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor already exists.") + + var event = createEventStruct(events, fd) + + if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: OSError(OSLastError()) - # We could fill in the ``fds`` TTable to get the info, but that wouldn't - # be nice for our memory. - result = TSelectorKey(fd: fd, events: {}, data: nil) + + var key = PSelectorKey(fd: fd, events: events, data: data) + + s.fds[fd] = key + result = key + + proc update*(s: PSelector, fd: TSocketHandle, + events: set[TEvent]): PSelectorKey {.discardable.} = + ## Updates the events which ``fd`` wants notifications for. + if not s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor not found.") + var event = createEventStruct(events, fd) + + s.fds[fd].events = events + echo("About to update") + if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: + OSError(OSLastError()) + echo("finished updating") + result = s.fds[fd] + + proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = + if not s.fds.hasKey(fd): + raise newException(EInvalidValue, "File descriptor not found.") + if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: + OSError(OSLastError()) + result = s.fds[fd] + s.fds.del(fd) - proc esClose(s: PSelector) = - var es = PEpollSelector(s) - if es.epollFD.close() != 0: OSError(OSLastError()) - dealloc(addr es.events) # TODO: Test this + proc close*(s: PSelector) = + if s.epollFD.close() != 0: OSError(OSLastError()) + dealloc(addr s.events) # TODO: Test this - proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] = + proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = + ## + ## The ``events`` field of the returned ``key`` contains the original events + ## for which the ``fd`` was bound. This is contrary to the ``events`` field + ## of the ``TReadyInfo`` tuple which determines which events are ready + ## on the ``fd``. result = @[] - var es = PEpollSelector(s) - let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint) + let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint) if evNum < 0: OSError(OSLastError()) if evNum == 0: return @[] for i in 0 .. 0: echo ready[0].events i.inc if i == 6: + assert selector.unregister(sock.getFD).fd == sock.getFD selector.close() break diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim index f8284b339..031217b90 100644 --- a/lib/pure/sockets2.nim +++ b/lib/pure/sockets2.nim @@ -17,11 +17,13 @@ when hostos == "solaris": when defined(Windows): import winlean + export ioctlsocket else: import posix + export fcntl, F_GETFL, O_NONBLOCK, F_SETFL export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, - inet_ntoa + inet_ntoa, recv, `==`, connect, send, accept type @@ -63,10 +65,10 @@ type when defined(windows): let - OSInvalidSocket* = winlean.INVALID_SOCKET + osInvalidSocket* = winlean.INVALID_SOCKET else: let - OSInvalidSocket* = posix.INVALID_SOCKET + osInvalidSocket* = posix.INVALID_SOCKET proc `==`*(a, b: TPort): bool {.borrow.} ## ``==`` for ports. diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index bcaffc287..bde5bf8c8 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -21,7 +21,7 @@ proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.asyn proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} = for i in 0 .. Date: Sun, 23 Feb 2014 00:19:18 +0100 Subject: [PATCH 076/249] Added tougher test case for return within finally statement. --- tests/exception/tfinally4.nim | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/exception/tfinally4.nim diff --git a/tests/exception/tfinally4.nim b/tests/exception/tfinally4.nim new file mode 100644 index 000000000..05c57c4f5 --- /dev/null +++ b/tests/exception/tfinally4.nim @@ -0,0 +1,40 @@ +discard """ + file: "tfinally4.nim" + output: "B1\nA1\n1\nB1\nB2\ncatch\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\ncatch\nA1\nA2\n0\nB1\nA1\n1\nB1\nB2\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\nA1\nA2\n3" +""" + +# More thorough test of return-in-finaly + +var raiseEx = true +var returnA = true +var returnB = false + +proc main: int = + try: #A + try: #B + if raiseEx: + raise newException(EOS, "") + return 3 + finally: #B + echo "B1" + if returnB: + return 2 + echo "B2" + except EOS: #A + echo "catch" + finally: #A + echo "A1" + if returnA: + return 1 + echo "A2" + +for x in [true, false]: + for y in [true, false]: + for z in [true, false]: + # echo "raiseEx: " & $x + # echo "returnA: " & $y + # echo "returnB: " & $z + raiseEx = x + returnA = y + returnB = z + echo main() From ef379d0a10e800982d4a10ad623d2426d68e830d Mon Sep 17 00:00:00 2001 From: Audun Wilhelmsen Date: Sun, 23 Feb 2014 00:20:16 +0100 Subject: [PATCH 077/249] Added test cases for return in except statements. --- tests/exception/tnestedreturn.nim | 40 ++++++++++++++++++++++++++++++ tests/exception/tnestedreturn2.nim | 20 +++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/exception/tnestedreturn.nim create mode 100644 tests/exception/tnestedreturn2.nim diff --git a/tests/exception/tnestedreturn.nim b/tests/exception/tnestedreturn.nim new file mode 100644 index 000000000..b9f7843f6 --- /dev/null +++ b/tests/exception/tnestedreturn.nim @@ -0,0 +1,40 @@ +discard """ + file: "tnestedreturn.nim" + output: "A\nB\nC\n" +""" + +# Various tests of return nested in double try/except statements + +proc test1() = + + finally: echo "A" + + try: + raise newException(EOS, "Problem") + except EOS: + return + +test1() + + +proc test2() = + + finally: echo "B" + + try: + return + except EOS: + discard + +test2() + +proc test3() = + try: + try: + raise newException(EOS, "Problem") + except EOS: + return + finally: + echo "C" + +test3() diff --git a/tests/exception/tnestedreturn2.nim b/tests/exception/tnestedreturn2.nim new file mode 100644 index 000000000..14a2dab92 --- /dev/null +++ b/tests/exception/tnestedreturn2.nim @@ -0,0 +1,20 @@ +discard """ + file: "tnestedreturn.nim" + outputsub: "Error: unhandled exception: Problem [EOS]" + exitcode: "1" +""" + +proc test4() = + try: + try: + raise newException(EOS, "Problem") + except EOS: + return + finally: + discard + +# Should cause unhandled exception error, +# but could cause segmentation fault if +# exceptions are not handled properly. +test4() +raise newException(EOS, "Problem") From 739b4f214b62f55f5fcfc8c71a246c385146fca8 Mon Sep 17 00:00:00 2001 From: Audun Wilhelmsen Date: Sun, 23 Feb 2014 00:23:06 +0100 Subject: [PATCH 078/249] Fixed #688 : return in except statments. Also fixed return in finally statements. --- compiler/ccgstmts.nim | 41 +++++++++++++++++++++++++---------------- compiler/cgendata.nim | 7 +++++-- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 443d845f6..4576a54b5 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -263,33 +263,33 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = - # This is called by return and break stmts. - # When jumping out of try/except/finally stmts, - # we need to pop safe points from try statements, - # execute finally-stmts, and pop exceptions - # from except stmts + # Called by return and break stmts. + # Deals with issues faced when jumping out of try/except/finally stmts, - let L = p.nestedTryStmts.len - - # danger of endless recursion! we workaround this here by a temp stack var stack: seq[PNode] - newSeq(stack, howManyTrys) - for i in countup(1, howManyTrys): - stack[i-1] = p.nestedTryStmts[L-i] - setLen(p.nestedTryStmts, L-howManyTrys) + newSeq(stack, 0) var alreadyPoppedCnt = p.inExceptBlock - for tryStmt in items(stack): + for i in countup(1, howManyTrys): + if gCmd != cmdCompileToCpp: + # Pop safe points generated by try if alreadyPoppedCnt > 0: dec alreadyPoppedCnt else: linefmt(p, cpsStmts, "#popSafePoint();$n") - # Find finally-stmts for this try-stmt - # and generate a copy of the finally stmts here + + # Pop this try-stmt of the list of nested trys + # so we don't infinite recurse on it in the next step. + var tryStmt = p.nestedTryStmts.pop + stack.add(tryStmt) + + # Find finally-stmt for this try-stmt + # and generate a copy of its sons var finallyStmt = lastSon(tryStmt) if finallyStmt.kind == nkFinally: genStmts(p, finallyStmt.sons[0]) + # push old elements again: for i in countdown(howManyTrys-1, 0): p.nestedTryStmts.add(stack[i]) @@ -304,7 +304,14 @@ proc genReturnStmt(p: BProc, t: PNode) = p.beforeRetNeeded = true genLineDir(p, t) if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) - blockLeaveActions(p, min(1, p.nestedTryStmts.len), p.inExceptBlock) + blockLeaveActions(p, + howManyTrys = p.nestedTryStmts.len, + howManyExcepts = p.inExceptBlock) + if (p.finallySafePoints.len > 0): + # If we're in a finally block, and we came here by exception + # consume it before we return. + var safePoint = p.finallySafePoints[p.finallySafePoints.len-1] + linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint) lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", []) proc genComputedGoto(p: BProc; n: PNode) = @@ -843,7 +850,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = discard pop(p.nestedTryStmts) endBlock(p) # end of else block if i < length and t.sons[i].kind == nkFinally: + p.finallySafePoints.add(safePoint) exprBlock(p, t.sons[i].sons[0], d) + discard pop(p.finallySafePoints) linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 71479abdd..0df7bb6dc 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -65,11 +65,13 @@ type prc*: PSym # the Nimrod proc that this C proc belongs to beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed threadVarAccessed*: bool # true if the proc already accessed some threadvar - nestedTryStmts*: seq[PNode] # in how many nested try statements we are - # (the vars must be volatile then) + nestedTryStmts*: seq[PNode] # in how many nested try statements we are + # (the vars must be volatile then) inExceptBlock*: int # are we currently inside an except block? # leaving such scopes by raise or by return must # execute any applicable finally blocks + finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when + # using return in finally statements labels*: Natural # for generating unique labels in the C proc blocks*: seq[TBlock] # nested blocks breakIdx*: int # the block that will be exited @@ -142,6 +144,7 @@ proc newProc*(prc: PSym, module: BModule): BProc = else: result.options = gOptions newSeq(result.blocks, 1) result.nestedTryStmts = @[] + result.finallySafePoints = @[] iterator cgenModules*: var BModule = for i in 0..high(gModules): From 95eb2b88e6820e23d74e4a54e9aa4fba5415ec5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Sun, 23 Feb 2014 21:01:03 +0100 Subject: [PATCH 079/249] osproc: increase stack size from 8k to 64k --- lib/pure/osproc.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 93d737aa6..5c34c5a11 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -750,7 +750,7 @@ elif not defined(useNimRtl): var dataCopy = data if defined(useClone): - const stackSize = 8096 + const stackSize = 65536 let stackEnd = cast[clong](alloc(stackSize)) let stack = cast[pointer](stackEnd + stackSize) let fn: pointer = startProcessAfterFork From 6712aa6e38fdab6e250532c622d3191c16c2dc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Sun, 23 Feb 2014 21:02:13 +0100 Subject: [PATCH 080/249] osproc: use fork by default on Linux, clone if requested by useClone flag --- lib/pure/osproc.nim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 5c34c5a11..0ac01091c 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -22,8 +22,6 @@ else: when defined(linux): import linux - when not defined(useFork): - const useClone = true type TProcess = object of TObject @@ -663,7 +661,7 @@ elif not defined(useNimRtl): data.workingDir = workingDir - when defined(posix_spawn) and not defined(useFork) and not defined(useClone): + when defined(posix_spawn) and not defined(useFork) and not defined(useClone) and not defined(linux): pid = startProcessAuxSpawn(data) else: pid = startProcessAuxFork(data) From 0c31686fec4b3b5db1335d14142ea91a82954f7a Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 24 Feb 2014 01:30:40 +0100 Subject: [PATCH 081/249] new VM is getting stable --- compiler/msgs.nim | 4 +-- compiler/rodread.nim | 2 +- compiler/sem.nim | 2 +- compiler/semexprs.nim | 4 +++ compiler/semstmts.nim | 9 ++++-- compiler/vm.nim | 50 +++++++++++++++++++++++----------- compiler/vmdef.nim | 3 +- compiler/vmgen.nim | 59 ++++++++++++++++++++++++++++++++-------- tests/macros/tmacro5.nim | 2 +- todo.txt | 2 -- 10 files changed, 98 insertions(+), 39 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 268205361..ae673bd0e 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -67,7 +67,7 @@ type errAmbiguousCallXYZ, errWrongNumberOfArguments, errXCannotBePassedToProcVar, errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, - errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValue, + errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, errInvalidOrderInArrayConstructor, errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, @@ -264,7 +264,7 @@ const errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", errImplOfXexpected: "implementation of \'$1\' expected", errNoSymbolToBorrowFromFound: "no symbol to borrow from found", - errDiscardValue: "value returned by statement has to be discarded", + errDiscardValueX: "value of type '$1' has to be discarded", errInvalidDiscard: "statement returns no value that can be discarded", errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid", errCannotBindXTwice: "cannot bind parameter \'$1\' twice", diff --git a/compiler/rodread.nim b/compiler/rodread.nim index b53135a95..036e6cc3c 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -890,7 +890,7 @@ proc loadStub*(s: PSym) = # deactivate the GC here because we do a deep recursion and generate no # garbage when restoring parts of the object graph anyway. - # Since we die with internal errors if this fails, so no try-finally is + # Since we die with internal errors if this fails, no try-finally is # necessary. GC_disable() rawLoadStub(s) diff --git a/compiler/sem.nim b/compiler/sem.nim index 09b2511f1..5ee46654e 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -220,7 +220,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = return nil result = fixupTypeAfterEval(c, result, e) - except: + except ERecoverableError: return nil proc semConstExpr(c: PContext, n: PNode): PNode = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c271911ab..538489490 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -641,9 +641,11 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = result = evalStaticExpr(c.module, call, c.p.owner) if result.isNil: localError(n.info, errCannotInterpretNodeX, renderTree(call)) + else: result = fixupTypeAfterEval(c, result, n) else: result = evalConstExpr(c.module, call) if result.isNil: result = n + else: result = fixupTypeAfterEval(c, result, n) #if result != n: # echo "SUCCESS evaluated at compile time: ", call.renderTree @@ -653,6 +655,8 @@ proc semStaticExpr(c: PContext, n: PNode): PNode = if result.isNil: localError(n.info, errCannotInterpretNodeX, renderTree(n)) result = emptyNode + else: + result = fixupTypeAfterEval(c, result, a) proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 503ea4bc1..1a2f9a6a6 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -126,7 +126,7 @@ proc implicitlyDiscardable(n: PNode): bool = proc fixNilType(n: PNode) = if isAtom(n): if n.kind != nkNilLit and n.typ != nil: - localError(n.info, errDiscardValue) + localError(n.info, errDiscardValueX, n.typ.typeToString) elif n.kind in {nkStmtList, nkStmtListExpr}: n.kind = nkStmtList for it in n: fixNilType(it) @@ -154,7 +154,7 @@ proc discardCheck(c: PContext, result: PNode) = else: var n = result while n.kind in skipForDiscardable: n = n.lastSon - localError(n.info, errDiscardValue) + localError(n.info, errDiscardValueX, result.typ.typeToString) proc semIf(c: PContext, n: PNode): PNode = result = n @@ -331,6 +331,7 @@ proc checkNilable(v: PSym) = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode result = copyNode(n) + var hasCompileTime = false for i in countup(0, sonsLen(n)-1): var a = n.sons[i] if gCmd == cmdIdeTools: suggestStmt(c, a) @@ -405,7 +406,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = v.typ = tup.sons[j] b.sons[j] = newSymNode(v) checkNilable(v) - + if sfCompileTime in v.flags: hasCompileTime = true + if hasCompileTime: vm.setupCompileTimeVar(c.module, result) + proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) for i in countup(0, sonsLen(n) - 1): diff --git a/compiler/vm.nim b/compiler/vm.nim index 820a2022c..6277b2dc6 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -109,11 +109,17 @@ template decodeBx(k: expr) {.immediate, dirty.} = template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler -template createStr(x) = - if x.node.isNil: x.node = newNode(nkStrLit) +template createStrKeepNode(x) = + if x.node.isNil: + x.node = newNode(nkStrLit) elif x.node.kind == nkNilLit: system.reset(x.node[]) x.node.kind = nkStrLit + else: + assert x.node.kind in {nkStrLit..nkTripleStrLit} + +template createStr(x) = + x.node = newNode(nkStrLit) proc moveConst(x: var TRegister, y: TRegister) = if x.kind != y.kind: @@ -254,7 +260,7 @@ proc opConv*(dest: var TRegister, src: TRegister, desttyp, srctyp: PType): bool if dest.kind != rkNode: myreset(dest) dest.kind = rkNode - dest.node = newNode(nkStrLit) + dest.node = newNode(nkStrLit) let styp = srctyp.skipTypes(abstractRange) case styp.kind of tyEnum: @@ -329,8 +335,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = #{.computedGoto.} let instr = c.code[pc] let ra = instr.regA - #echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra - #message(c.debug[pc], warnUser, "gah") + #if c.traceActive: + # echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra + # message(c.debug[pc], warnUser, "Trace") case instr.opcode of opcEof: return regs[ra] of opcRet: @@ -354,7 +361,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = regs[ra].intVal = regs[rb].intVal of opcAsgnStr: decodeB(rkNode) - createStr regs[ra] + createStrKeepNode regs[ra] regs[ra].node.strVal = regs[rb].node.strVal of opcAsgnFloat: decodeB(rkFloat) @@ -454,7 +461,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = of opcWrDeref: # a[] = b decodeBC(rkNode) - putIntoNode(regs[ra].node, regs[rc]) + putIntoNode(regs[ra].node, regs[rb]) of opcAddInt: decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal + regs[rc].intVal @@ -484,8 +491,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = of opcInclRange: decodeBC(rkNode) var r = newNode(nkRange) - r.add regs[rb].node - r.add regs[rc].node + r.add regs[rb].regToNode + r.add regs[rc].regToNode addSon(regs[ra].node, r.copyTree) of opcExcl: decodeB(rkNode) @@ -643,15 +650,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = regs[ra].node.strVal.add getstr(regs[i]) of opcAddStrCh: decodeB(rkNode) - createStr regs[ra] + createStrKeepNode regs[ra] regs[ra].node.strVal.add(regs[rb].intVal.chr) of opcAddStrStr: decodeB(rkNode) - createStr regs[ra] + createStrKeepNode regs[ra] regs[ra].node.strVal.add(regs[rb].node.strVal) of opcAddSeqElem: decodeB(rkNode) - regs[ra].node.add(copyTree(regs[rb].node)) + if regs[ra].node.kind == nkBracket: + regs[ra].node.add(copyTree(regs[rb].regToNode)) + else: + stackTrace(c, tos, pc, errNilAccess) of opcEcho: let rb = instr.regB for i in ra..ra+rb-1: @@ -660,7 +670,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = writeln(stdout, "") of opcContainsSet: decodeBC(rkInt) - regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].node)) + regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) of opcSubStr: decodeBC(rkNode) inc pc @@ -815,8 +825,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] regs[ra].node = getNullValue(typ, c.debug[pc]) - if regs[ra].node.kind in {nkStrLit..nkTripleStrLit}: - regs[ra].kind = rkNode + # opcLdNull really is the gist of the VM's problems: should it load + # a fresh null to regs[ra].node or to regs[ra].node[]? This really + # depends on whether regs[ra] represents the variable itself or wether + # it holds the indirection! Due to the way registers are re-used we cannot + # say for sure here! --> The codegen has to deal with it + # via 'genAsgnPatch'. of opcLdNullReg: let typ = c.types[instr.regBx - wordExcess] if typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}).kind in { @@ -858,7 +872,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = return TRegister(kind: rkNone) of opcSetLenStr: decodeB(rkNode) - createStr regs[ra] + createStrKeepNode regs[ra] regs[ra].node.strVal.setLen(regs[rb].intVal.int) of opcOf: decodeBC(rkInt) @@ -1209,6 +1223,9 @@ proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode = proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) = discard evalConstExprAux(module, prc, e, emStaticStmt) +proc setupCompileTimeVar*(module: PSym, n: PNode) = + discard evalConstExprAux(module, nil, n, emStaticStmt) + proc setupMacroParam(x: PNode): PNode = result = x if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1] @@ -1250,3 +1267,4 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode = if cyclicTree(result): globalError(n.info, errCyclicTree) dec(evalMacroCounter) c.callsite = nil + #debug result diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index ea7c94856..72689c879 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -163,8 +163,6 @@ type blocks*: seq[TBlock] # blocks; temp data structure slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]] maxSlots*: int - globals*: array[TRegister, int] # hack: to support passing globals byref - # we map a slot persistently to a global PCtx* = ref TCtx TCtx* = object of passes.TPassContext # code gen context @@ -181,6 +179,7 @@ type callsite*: PNode mode*: TEvalMode features*: TSandboxFlags + traceActive*: bool TPosition* = distinct int diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index cddda7bd3..b594c00a9 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -451,9 +451,11 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) = c.gABC(n, opcIndCallAsgn, dest, x, n.len) c.freeTempRange(x, n.len) +template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar + proc needsAsgnPatch(n: PNode): bool = n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, - nkDerefExpr, nkHiddenDeref} + nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal) proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind @@ -461,15 +463,25 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = let dest = c.genx(le.sons[0], {gfAddrOf}) let idx = c.genx(le.sons[1]) c.gABC(le, opcWrArr, dest, idx, value) + c.freeTemp(dest) + c.freeTemp(idx) of nkDotExpr, nkCheckedFieldExpr: # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] let dest = c.genx(left.sons[0], {gfAddrOf}) let idx = c.genx(left.sons[1]) c.gABC(left, opcWrObj, dest, idx, value) + c.freeTemp(dest) + c.freeTemp(idx) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) c.gABC(le, opcWrDeref, dest, value) + c.freeTemp(dest) + of nkSym: + if le.sym.isGlobal: + let dest = c.genx(le, {gfAddrOf}) + c.gABC(le, opcWrDeref, dest, value) + c.freeTemp(dest) else: discard @@ -608,6 +620,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: unused(n, dest) + # XXX generates inefficient code for globals var d = c.genx(n.sons[1]).TDest c.genAddSubInt(n, d, if m == mInc: opcAddInt else: opcSubInt) c.genAsgnPatch(n.sons[1], d) @@ -621,6 +634,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.genNewSeq(n) of mNewString: genUnaryABC(c, n, dest, opcNewStr) + # XXX buggy of mNewStringOfCap: # we ignore the 'cap' argument and translate it as 'newString(0)'. # eval n.sons[1] for possible side effects: @@ -629,6 +643,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opcNewStr, dest, tmp) c.freeTemp(tmp) + # XXX buggy of mLengthOpenArray, mLengthArray, mLengthSeq: genUnaryABI(c, n, dest, opcLenSeq) of mLengthStr: @@ -955,8 +970,6 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) = gABC(c, ri, whichAsgnOpc(ri), dest, tmp) c.freeTemp(tmp) -template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar - proc setSlot(c: PCtx; v: PSym) = # XXX generate type initialization here? if v.position == 0: @@ -1035,14 +1048,22 @@ proc cannotEval(n: PNode) {.noinline.} = globalError(n.info, errGenerated, "cannot evaluate at compile time: " & n.renderTree) +proc getNullValue*(typ: PType, info: TLineInfo): PNode + proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = - c.globals.add(emptyNode.copyNode) + c.globals.add(getNullValue(s.typ, n.info)) s.position = c.globals.len # This is rather hard to support, due to the laziness of the VM code # generator. See tests/compile/tmacro2 for why this is necesary: # var decls{.compileTime.}: seq[PNimrodNode] = @[] + let dest = c.getTemp(s.typ) + c.gABx(n, opcLdGlobal, dest, s.position) + let tmp = c.genx(s.ast) + c.gABC(n, opcWrDeref, dest, tmp) + c.freeTemp(dest) + c.freeTemp(tmp) -proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = +proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let s = n.sym if s.isGlobal: if sfCompileTime in s.flags or c.mode == emRepl: @@ -1052,7 +1073,14 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) = if s.position == 0: if sfImportc in s.flags: c.importcSym(n.info, s) else: genGlobalInit(c, n, s) - c.gABx(n, opcLdGlobal, dest, s.position) + if dest < 0: dest = c.getTemp(n.typ) + if gfAddrOf notin flags and fitsRegister(s.typ): + var cc = c.getTemp(n.typ) + c.gABx(n, opcLdGlobal, cc, s.position) + c.gABC(n, opcNodeToReg, dest, cc) + c.freeTemp(cc) + else: + c.gABx(n, opcLdGlobal, dest, s.position) else: if s.kind == skForVar and c.mode == emRepl: c.setSlot s if s.position > 0 or (s.position == 0 and @@ -1095,7 +1123,6 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = else: genAccess(c, n, dest, opcLdArr, flags) -proc getNullValue*(typ: PType, info: TLineInfo): PNode proc getNullValueAux(obj: PNode, result: PNode) = case obj.kind of nkRecList: @@ -1161,7 +1188,7 @@ proc genVarSection(c: PCtx; n: PNode) = setSlot(c, a[i].sym) # v = t[i] var v: TDest = -1 - genRdVar(c, a[i], v) + genRdVar(c, a[i], v, {gfAddrOf}) c.gABC(n, opcWrObj, v, tmp, i) # XXX globals? c.freeTemp(tmp) @@ -1184,6 +1211,7 @@ proc genVarSection(c: PCtx; n: PNode) = let val = c.genx(a.sons[2]) c.gABC(a, opcWrDeref, tmp, val) c.freeTemp(val) + c.freeTemp(tmp) else: setSlot(c, s) if a.sons[2].kind == nkEmpty: @@ -1202,8 +1230,17 @@ proc genVarSection(c: PCtx; n: PNode) = proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) c.gABx(n, opcLdNull, dest, c.genType(n.typ)) + + let intType = getSysType(tyInt) + let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc}) + if seqType.kind == tySequence: + var tmp = c.getTemp(intType) + c.gABx(n, opcLdImmInt, tmp, n.len) + c.gABx(n, opcNewSeq, dest, c.genType(seqType)) + c.gABx(n, opcNewSeq, tmp, 0) + c.freeTemp(tmp) + if n.len > 0: - let intType = getSysType(tyInt) var tmp = getTemp(c, intType) c.gABx(n, opcLdNullReg, tmp, c.genType(intType)) for x in n: @@ -1271,7 +1308,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = let s = n.sym case s.kind of skVar, skForVar, skTemp, skLet, skParam, skResult: - genRdVar(c, n, dest) + genRdVar(c, n, dest, flags) of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator: # 'skTemplate' is only allowed for 'getAst' support: if sfImportc in s.flags: c.importcSym(n.info, s) @@ -1503,7 +1540,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "traverse": + #if s.name.s == "importImpl_forward" or s.name.s == "importImpl": # c.echoCode(result) # echo renderTree(body) c.prc = oldPrc diff --git a/tests/macros/tmacro5.nim b/tests/macros/tmacro5.nim index 39324e497..9882ad90d 100644 --- a/tests/macros/tmacro5.nim +++ b/tests/macros/tmacro5.nim @@ -51,7 +51,7 @@ macro okayy:stmt = for node in decls: result.add node for node in impls: result.add node -importimpl(Item, int): +importImpl(Item, int): echo 42 importImpl(Foo, int16): echo 77 diff --git a/todo.txt b/todo.txt index 7009d9a84..4bee45516 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ version 0.9.4 ============= -- fix macros\tstringinterp.nim - fix GC issues - test and fix showoff @@ -24,7 +23,6 @@ version 0.9.x - implement 'union' and 'bits' pragmas - fix closures -- test and fix exception handling - ensure (ref T)(a, b) works as a type conversion and type constructor - optimize 'genericReset'; 'newException' leads to code bloat - stack-less GC From b320e02903898401fdc99ec7bb7b167efc2cce12 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 24 Feb 2014 16:41:26 +0100 Subject: [PATCH 082/249] keine_schweine test is not platform dependent --- .../keineschweine/dependencies/chipmunk/chipmunk.nim | 7 +++---- .../manyloc/keineschweine/dependencies/enet/enet.nim | 9 ++++----- .../manyloc/keineschweine/dependencies/sfml/sfml.nim | 12 +++++++++--- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim index 8226b0b04..d9c933939 100644 --- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim +++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim @@ -18,10 +18,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # -when defined(Linux): - const Lib = "libchipmunk.so.6.1.1" -else: - {.error: "Platform unsupported".} + +const Lib = "libchipmunk.so.6.1.1" + when defined(MoreNimrod): {.hint: "MoreNimrod defined; some Chipmunk functions replaced in Nimrod".} {.deadCodeElim: on.} diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim index ad43c69b7..df1b743ee 100644 --- a/tests/manyloc/keineschweine/dependencies/enet/enet.nim +++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim @@ -17,10 +17,9 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -when defined(Linux): - const Lib = "libenet.so.1(|.0.3)" -else: - {.error: "Your platform has not been accounted for."} + +const Lib = "libenet.so.1(|.0.3)" + {.deadCodeElim: ON.} const ENET_VERSION_MAJOR* = 1 @@ -267,7 +266,7 @@ const ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000 ENET_PEER_FREE_RELIABLE_WINDOWS = 8 -when defined(Linux): +when defined(Linux) or true: import posix const ENET_SOCKET_NULL*: cint = -1 diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim index 27163e271..0d09d40e3 100644 --- a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim +++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim @@ -6,7 +6,12 @@ when defined(linux): LibS = "libcsfml-system.so.2.0" LibW = "libcsfml-window.so.2.0" else: - {.error: "Platform unsupported".} + # We only compile for testing here, so it doesn't matter it's not supported + const + LibG = "libcsfml-graphics.so.2.0" + LibS = "libcsfml-system.so.2.0" + LibW = "libcsfml-window.so.2.0" + #{.error: "Platform unsupported".} {.deadCodeElim: on.} {.pragma: pf, pure, final.} type @@ -153,8 +158,9 @@ type KeyF15, #/< The F15 key KeyPause, #/< The Pause key KeyCount #/< Keep last -- the total number of keyboard keys -when defined(linux): #or defined(bsd) ?? - type TWindowHandle* = clong + +type TWindowHandle* = clong + #elif defined(mac): # type TWindowHandle* = pointer ##typedef void* sfWindowHandle; <- whatever the hell that is #elif defined(windows): From 30a3098095e2d04939cc8f1074507343f65a58d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Sun, 23 Feb 2014 23:08:48 +0100 Subject: [PATCH 083/249] osproc: MacOSX workaround for lack of execvpe --- lib/pure/osproc.nim | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 79a4de04b..4e17d3e02 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -779,6 +779,9 @@ elif not defined(useNimRtl): discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error)) exitnow(1) + when defined(macosx): + var environ {.importc.}: cstringArray + proc startProcessAfterFork(data: ptr TStartProcessData) = # Warning: no GC here! # Or anythink that touches global structures - all called nimrod procs @@ -806,7 +809,13 @@ elif not defined(useNimRtl): discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC) if data.optionPoUsePath: - discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) + when defined(macosx): + # MacOSX doesn't have execvpe, so we need workaround. + # On MacOSX we can arrive here only from fork, so this is safe: + environ = data.sysEnv + discard execvp(data.sysCommand, data.sysArgs) + else: + discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv) else: discard execve(data.sysCommand, data.sysArgs, data.sysEnv) From 7314f11adc742169770ac718ca4a5417ef16d69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Mon, 24 Feb 2014 17:45:45 +0100 Subject: [PATCH 084/249] osproc: MacOSX fix - if -> when --- lib/pure/osproc.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 4e17d3e02..2a685f3fb 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -747,7 +747,7 @@ elif not defined(useNimRtl): var pid: TPid var dataCopy = data - if defined(useClone): + when defined(useClone): const stackSize = 65536 let stackEnd = cast[clong](alloc(stackSize)) let stack = cast[pointer](stackEnd + stackSize) From e6b0b7ecc9bb81d94eec19fbc4fc62e104f59253 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 25 Feb 2014 00:49:24 +0200 Subject: [PATCH 085/249] some fixes for static params usage in macros --- compiler/cgen.nim | 16 +++++++-------- compiler/msgs.nim | 3 +++ compiler/semtypes.nim | 12 +++++++++-- compiler/sigmatch.nim | 46 ++++++++++++++++++++++++++++--------------- 4 files changed, 51 insertions(+), 26 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 5057ae558..87ed23f36 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -962,8 +962,8 @@ proc genMainProc(m: BModule) = NimMainBody = "N_CDECL(void, NimMain)(void) {$N" & "\tPreMain();$N" & - "$1$N" & - "}$N" + "$1" & + "}$N$N" PosixNimMain = "int cmdCount;$N" & @@ -977,20 +977,20 @@ proc genMainProc(m: BModule) = "\tcmdCount = argc;$N" & "\tgEnv = env;$N" & MainProcsWithResult & - "}$N" + "}$N$N" StandaloneCMain = "int main(void) {$N" & MainProcs & "\treturn 0;$N" & - "}$N" + "}$N$N" WinNimMain = NimMainBody WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" & " HINSTANCE hPrevInstance, $N" & " LPSTR lpCmdLine, int nCmdShow) {$N" & - MainProcsWithResult & "}$N" + MainProcsWithResult & "}$N$N" WinNimDllMain = "N_LIB_EXPORT " & NimMainBody @@ -998,14 +998,14 @@ proc genMainProc(m: BModule) = "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" & " LPVOID lpvReserved) {$N" & "\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" & - "\treturn 1;$N}$N" + "\treturn 1;$N}$N$N" PosixNimDllMain = WinNimDllMain PosixCDllMain = "void NIM_POSIX_INIT NimMainInit(void) {$N" & MainProcs & - "}$N" + "}$N$N" var nimMain, otherMain: TFormatStr if platform.targetOS == osWindows and @@ -1034,7 +1034,7 @@ proc genMainProc(m: BModule) = platform.targetOS == osStandalone: "".toRope else: ropecg(m, "\t#initStackBottom();$N") inc(m.labels) - appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N", [ + appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N$N", [ mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit]) appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)]) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 0140d1ac4..b44ca2ff0 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -94,6 +94,7 @@ type errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely, errOnlyACallOpCanBeDelegator, errUsingNoSymbol, + errMacroBodyDependsOnGenericTypes, errDestructorNotGenericEnough, errXExpectsTwoArguments, @@ -326,6 +327,8 @@ const errInstantiateXExplicitely: "instantiate '$1' explicitely", errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator", errUsingNoSymbol: "'$1' is not a variable, constant or a proc name", + errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " & + "because the parameter '$1' has a generic type", errDestructorNotGenericEnough: "Destructor signarue is too specific. " & "A destructor must be associated will all instantiations of a generic type", errXExpectsTwoArguments: "\'$1\' expects two arguments", diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0eba602cc..809b80428 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -653,6 +653,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name else: nil + template maybeLift(typ: PType): expr = + let lifted = liftingWalk(typ) + (if lifted != nil: lifted else: typ) + template addImplicitGeneric(e: expr): expr = addImplicitGenericImpl(e, paramTypId) @@ -663,7 +667,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyStatic: # proc(a: expr{string}, b: expr{nkLambda}) # overload on compile time values and AST trees - result = addImplicitGeneric(c.newTypeWithSons(tyStatic, paramType.sons)) + let base = paramType.base.maybeLift + if base.isMetaType and procKind == skMacro: + localError(info, errMacroBodyDependsOnGenericTypes, paramName) + result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base])) result.flags.incl tfHasStatic of tyTypeDesc: @@ -671,7 +678,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # naked typedescs are not bindOnce types if paramType.base.kind == tyNone and paramTypId != nil and paramTypId.id == typedescId.id: paramTypId = nil - result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons)) + result = addImplicitGeneric( + c.newTypeWithSons(tyTypeDesc, @[paramType.base])) of tyDistinct: if paramType.sonsLen == 1: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 240145118..f8e3459df 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1029,6 +1029,28 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, r = typeRel(m, f, a) + if r != isNone and m.calleeSym != nil and + m.calleeSym.kind in {skMacro, skTemplate}: + # XXX: duplicating this is ugly, maybe we should move this + # directly into typeRel using return-like templates + case r + of isConvertible, isIntConv: inc(m.convMatches) + of isSubtype, isSubrange: inc(m.subtypeMatches) + of isGeneric, isInferred: inc(m.genericMatches) + of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches) + of isFromIntLit: inc(m.intConvMatches, 256) + of isEqual: inc(m.exactMatches) + of isNone: discard + + if f.kind == tyStmt and argOrig.kind == nkDo: + return argOrig[bodyPos] + elif f.kind == tyTypeDesc: + return arg + elif f.kind == tyStatic: + return arg.typ.n + else: + return argOrig + case r of isConvertible: inc(m.convMatches) @@ -1046,31 +1068,23 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, #result = copyTree(arg) result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) of isInferred, isInferredConvertible: + inc(m.genericMatches) if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds: result = c.semInferredLambda(c, m.bindings, arg) else: let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) result = newSymNode(inferred, arg.info) if r == isInferredConvertible: + inc(m.convMatches) result = implicitConv(nkHiddenStdConv, f, result, m, c) of isGeneric: inc(m.genericMatches) - if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}: - if f.kind == tyStmt and argOrig.kind == nkDo: - result = argOrig[bodyPos] - elif f.kind == tyTypeDesc: - result = arg - elif f.kind == tyStatic: - result = arg.typ.n - else: - result = argOrig - else: - result = copyTree(arg) - result.typ = getInstantiatedType(c, arg, m, f) - # BUG: f may not be the right key! - if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) - # BUGFIX: use ``result.typ`` and not `f` here + result = copyTree(arg) + result.typ = getInstantiatedType(c, arg, m, f) + # BUG: f may not be the right key! + if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}: + result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + # BUGFIX: use ``result.typ`` and not `f` here of isFromIntLit: # too lazy to introduce another ``*matches`` field, so we conflate # ``isIntConv`` and ``isIntLit`` here: From 4c26c3a4281b70efe2865499326afe802de98db2 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 25 Feb 2014 00:40:21 +0100 Subject: [PATCH 086/249] bugfix: typo --- compiler/types.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/types.nim b/compiler/types.nim index d7148f110..e6178f446 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -463,7 +463,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyAnd: result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) of tyOr: - result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) + result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1]) of tyNot: result = "not " & typeToString(t.sons[0]) of tyExpr: From 263cabd1c27977aa32c849ffb334984e8d476b97 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 25 Feb 2014 01:02:10 +0100 Subject: [PATCH 087/249] added canonizer --- compiler/canonicalizer.nim | 288 +++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 compiler/canonicalizer.nim diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim new file mode 100644 index 000000000..fb5b3b9ce --- /dev/null +++ b/compiler/canonicalizer.nim @@ -0,0 +1,288 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the canonalization for the various caching mechanisms. + +import strutils, db_sqlite, md5 + +var db: TDbConn + +# We *hash* the relevant information into 128 bit hashes. This should be good enough +# to prevent any collisions. + +type + TUid = distinct MD5Digest + +# For name mangling we encode these hashes via a variant of base64 (called +# 'base64a') and prepend the *primary* identifier to ease the debugging pain. +# So a signature like: +# +# proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) +# +# is mangled into: +# gABI_MTdmOWY5MTQ1MDcyNGQ3ZA +# +# This is a good compromise between correctness and brevity. ;-) + +const + cb64 = [ + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", + "O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z", + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", + "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "_A", "_B"] + +proc toBase64a(s: cstring, len: int): string = + ## encodes `s` into base64 representation. After `lineLen` characters, a + ## `newline` is added. + var total = ((len + 2) div 3) * 4 + result = newStringOfCap(total) + var i = 0 + while i < s.len - 2: + let a = ord(s[i]) + let b = ord(s[i+1]) + let c = ord(s[i+2]) + result.add cb64[a shr 2] + result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] + result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)] + result.add cb64[c and 0x3F] + inc(i, 3) + if i < s.len-1: + let a = ord(s[i]) + let b = ord(s[i+1]) + result.add cb64[a shr 2] + result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)] + result.add cb64[((b and 0x0F) shl 2)] + elif i < s.len: + let a = ord(s[i]) + result.add cb64[a shr 2] + result.add cb64[(a and 3) shl 4] + +proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u)) + +proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len) + +proc hashSym(c: var MD5Context, s: PSym) = + if sfAnon in s.flags or s.kind == skGenericParam: + c &= ":anon" + else: + var it = s.owner + while it != nil: + hashSym(c, it) + c &= "." + it = s.owner + c &= s.name.s + +proc hashTree(c: var MD5Context, n: PNode) = + if n == nil: + c &= "null" + return + var k = n.kind + md5Update(c, cast[cstring](addr(k)), 1) + # we really must not hash line information. 'n.typ' is debatable but + # shouldn't be necessary for now and avoids potential infinite recursions. + case n.kind + of nkEmpty, nkNilLit, nkType: discard + of nkIdent: + c &= n.ident.s + of nkSym: + hashSym(c, n.sym) + of nkCharLit..nkUInt64Lit: + var v = n.intVal + md5Update(c, cast[cstring](addr(v)), sizeof(v)) + of nkFloatLit..nkFloat64Lit: + var v = n.floatVal + md5Update(c, cast[cstring](addr(v)), sizeof(v)) + of nkStrLit..nkTripleStrLit: + c &= n.strVal + else: + for i in 0.. 1: add(result, ", ") + add(result, typeToString(t.sons[i])) + add(result, ']') + of tyTypeDesc: + if t.base.kind == tyNone: result = "typedesc" + else: result = "typedesc[" & typeToString(t.base) & "]" + of tyStatic: + internalAssert t.len > 0 + result = "static[" & typeToString(t.sons[0]) & "]" + of tyUserTypeClass: + internalAssert t.sym != nil and t.sym.owner != nil + return t.sym.owner.name.s + of tyBuiltInTypeClass: + result = case t.base.kind: + of tyVar: "var" + of tyRef: "ref" + of tyPtr: "ptr" + of tySequence: "seq" + of tyArray: "array" + of tySet: "set" + of tyRange: "range" + of tyDistinct: "distinct" + of tyProc: "proc" + of tyObject: "object" + of tyTuple: "tuple" + else: (internalAssert(false); "") + of tyUserTypeClassInst: + let body = t.base + result = body.sym.name.s & "[" + for i in countup(1, sonsLen(t) - 2): + if i > 1: add(result, ", ") + add(result, typeToString(t.sons[i])) + result.add "]" + of tyAnd: + result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) + of tyOr: + result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1]) + of tyNot: + result = "not " & typeToString(t.sons[0]) + of tyExpr: + internalAssert t.len == 0 + result = "expr" + of tyFromExpr, tyFieldAccessor: + result = renderTree(t.n) + of tyArray: + if t.sons[0].kind == tyRange: + result = "array[" & hashTree(t.sons[0].n) & ", " & + typeToString(t.sons[1]) & ']' + else: + result = "array[" & typeToString(t.sons[0]) & ", " & + typeToString(t.sons[1]) & ']' + of tyArrayConstr: + result = "Array constructor[" & hashTree(t.sons[0].n) & ", " & + typeToString(t.sons[1]) & ']' + of tySequence: + result = "seq[" & typeToString(t.sons[0]) & ']' + of tyOrdinal: + result = "ordinal[" & typeToString(t.sons[0]) & ']' + of tySet: + result = "set[" & typeToString(t.sons[0]) & ']' + of tyOpenArray: + result = "openarray[" & typeToString(t.sons[0]) & ']' + of tyDistinct: + result = "distinct " & typeToString(t.sons[0], preferName) + of tyTuple: + # we iterate over t.sons here, because t.n may be nil + result = "tuple[" + if t.n != nil: + assert(sonsLen(t.n) == sonsLen(t)) + for i in countup(0, sonsLen(t.n) - 1): + assert(t.n.sons[i].kind == nkSym) + add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) + if i < sonsLen(t.n) - 1: add(result, ", ") + else: + for i in countup(0, sonsLen(t) - 1): + add(result, typeToString(t.sons[i])) + if i < sonsLen(t) - 1: add(result, ", ") + add(result, ']') + of tyPtr, tyRef, tyVar, tyMutable, tyConst: + result = typeToStr[t.kind] & typeToString(t.sons[0]) + of tyRange: + result = "range " & hashTree(t.n) + if prefer != preferExported: + result.add("(" & typeToString(t.sons[0]) & ")") + of tyProc: + result = if tfIterator in t.flags: "iterator (" else: "proc (" + for i in countup(1, sonsLen(t) - 1): + add(result, typeToString(t.sons[i])) + if i < sonsLen(t) - 1: add(result, ", ") + add(result, ')') + if t.sons[0] != nil: add(result, ": " & typeToString(t.sons[0])) + var prag: string + if t.callConv != ccDefault: prag = CallingConvToStr[t.callConv] + else: prag = "" + if tfNoSideEffect in t.flags: + addSep(prag) + add(prag, "noSideEffect") + if tfThread in t.flags: + addSep(prag) + add(prag, "thread") + if len(prag) != 0: add(result, "{." & prag & ".}") + of tyVarargs, tyIter: + result = typeToStr[t.kind] % typeToString(t.sons[0]) + else: + result = typeToStr[t.kind] + if tfShared in t.flags: result = "shared " & result + if tfNotNil in t.flags: result.add(" not nil") + + +proc createDb() = + db.exec(sql""" + create table if not exists Module( + id integer primary key, + name varchar(256) not null, + fullpath varchar(256) not null, + interfHash varchar(256) not null, + fullHash varchar(256) not null, + + created timestamp not null default (DATETIME('now')), + );""") + + db.exec(sql""" + create table if not exists Symbol( + id integer primary key, + module integer not null, + name varchar(max) not null, + data varchar(max) not null, + created timestamp not null default (DATETIME('now')), + + foreign key (module) references module(id) + );""") + + db.exec(sql""" + create table if not exists Type( + id integer primary key, + module integer not null, + name varchar(max) not null, + data varchar(max) not null, + created timestamp not null default (DATETIME('now')), + + foreign key (module) references module(id) + );""") + + + #db.exec(sql""" + # --create unique index if not exists TsstNameIx on TestResult(name); + # """, []) + From b677110bd6d2c143b4efbbbe32ffbb243277d89b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 25 Feb 2014 02:16:57 +0200 Subject: [PATCH 088/249] rename compilerProcs to match the recently changed jssys.nim --- compiler/jsgen.nim | 76 +++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index c0fc4131a..946ef1fda 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -275,11 +275,11 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 - ["AddU", "AddU", "AddU($1, $2)", "AddU($1, $2)"], # AddU - ["SubU", "SubU", "SubU($1, $2)", "SubU($1, $2)"], # SubU - ["MulU", "MulU", "MulU($1, $2)", "MulU($1, $2)"], # MulU - ["DivU", "DivU", "DivU($1, $2)", "DivU($1, $2)"], # DivU - ["ModU", "ModU", "ModU($1, $2)", "ModU($1, $2)"], # ModU + ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU + ["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU + ["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU + ["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU + ["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI @@ -289,10 +289,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 - ["LeU", "LeU", "LeU($1, $2)", "LeU($1, $2)"], # LeU - ["LtU", "LtU", "LtU($1, $2)", "LtU($1, $2)"], # LtU - ["LeU64", "LeU64", "LeU64($1, $2)", "LeU64($1, $2)"], # LeU64 - ["LtU64", "LtU64", "LtU64($1, $2)", "LtU64($1, $2)"], # LtU64 + ["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU + ["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU + ["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64 + ["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64 ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum @@ -309,10 +309,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 != $2)", "($1 != $2)"], # Xor ["", "", "($1 == $2)", "($1 == $2)"], # EqProc - ["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI - ["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64 - ["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI - ["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64 + ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI + ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 + ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI + ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64 ["", "", "!($1)", "!($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI @@ -327,9 +327,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64 ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64 ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64 - ["ToU8", "ToU8", "ToU8($1)", "ToU8($1)"], # ToU8 - ["ToU16", "ToU16", "ToU16($1)", "ToU16($1)"], # ToU16 - ["ToU32", "ToU32", "ToU32($1)", "ToU32($1)"], # ToU32 + ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8 + ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16 + ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32 ["", "", "$1", "$1"], # ToFloat ["", "", "$1", "$1"], # ToBiggestFloat ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt @@ -375,11 +375,11 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64 ["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64 ["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64 - ["AddU", "AddU", "AddU($1, $2)", "AddU($1, $2)"], # AddU - ["SubU", "SubU", "SubU($1, $2)", "SubU($1, $2)"], # SubU - ["MulU", "MulU", "MulU($1, $2)", "MulU($1, $2)"], # MulU - ["DivU", "DivU", "DivU($1, $2)", "DivU($1, $2)"], # DivU - ["ModU", "ModU", "ModU($1, $2)", "ModU($1, $2)"], # ModU + ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU + ["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU + ["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU + ["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU + ["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU ["", "", "($1 == $2)", "($1 == $2)"], # EqI ["", "", "($1 <= $2)", "($1 <= $2)"], # LeI ["", "", "($1 < $2)", "($1 < $2)"], # LtI @@ -389,10 +389,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqF64 ["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64 ["", "", "($1 < $2)", "($1 < $2)"], # LtF64 - ["LeU", "LeU", "LeU($1, $2)", "LeU($1, $2)"], # LeU - ["LtU", "LtU", "LtU($1, $2)", "LtU($1, $2)"], # LtU - ["LeU64", "LeU64", "LeU64($1, $2)", "LeU64($1, $2)"], # LeU64 - ["LtU64", "LtU64", "LtU64($1, $2)", "LtU64($1, $2)"], # LtU64 + ["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU + ["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU + ["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64 + ["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64 ["", "", "($1 == $2)", "($1 == $2)"], # EqEnum ["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum ["", "", "($1 < $2)", "($1 < $2)"], # LtEnum @@ -409,10 +409,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["", "", "($1 == $2)", "($1 == $2)"], # EqCString ["", "", "($1 != $2)", "($1 != $2)"], # Xor ["", "", "($1 == $2)", "($1 == $2)"], # EqProc - ["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI - ["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64 - ["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI - ["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64 + ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI + ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64 + ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI + ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64 ["", "", "not ($1)", "not ($1)"], # Not ["", "", "+($1)", "+($1)"], # UnaryPlusI ["", "", "~($1)", "~($1)"], # BitnotI @@ -427,9 +427,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op ["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64 ["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64 ["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64 - ["ToU8", "ToU8", "ToU8($1)", "ToU8($1)"], # ToU8 - ["ToU16", "ToU16", "ToU16($1)", "ToU16($1)"], # ToU16 - ["ToU32", "ToU32", "ToU32($1)", "ToU32($1)"], # ToU32 + ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8 + ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16 + ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32 ["", "", "$1", "$1"], # ToFloat ["", "", "$1", "$1"], # ToBiggestFloat ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt @@ -812,8 +812,8 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = if needsNoCopy(y) or noCopyNeeded: appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: - useMagic(p, "NimCopy") - appf(p.body, "$1 = NimCopy($2, $3);$n", + useMagic(p, "nimCopy") + appf(p.body, "$1 = nimCopy($2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: @@ -1113,8 +1113,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = var length = int(lengthOrd(t)) var e = elemType(t) if length > 32: - useMagic(p, "ArrayConstr") - result = ropef("ArrayConstr($1, $2, $3)", [toRope(length), + useMagic(p, "arrayConstr") + result = ropef("arrayConstr($1, $2, $3)", [toRope(length), createVar(p, e, false), genTypeInfo(p, e)]) else: result = toRope("[") @@ -1171,8 +1171,8 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = if needsNoCopy(n): s = a.res else: - useMagic(p, "NimCopy") - s = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)]) + useMagic(p, "nimCopy") + s = ropef("nimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)]) of etyBaseIndex: if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit") if {sfAddrTaken, sfGlobal} * v.flags != {}: From b7bb56bdca9ced8fdc0dea18dc54f732c6e17d3e Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 25 Feb 2014 08:41:29 +0100 Subject: [PATCH 089/249] made some tests green; htmlgen still broken --- compiler/vm.nim | 8 ++++++-- compiler/vmgen.nim | 12 +++++++----- tests/discard/tneedsdiscard.nim | 2 +- tests/exprs/tstmtexp.nim | 4 ++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 6277b2dc6..108979739 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -109,14 +109,18 @@ template decodeBx(k: expr) {.immediate, dirty.} = template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler -template createStrKeepNode(x) = +proc createStrKeepNode(x: var TRegister) = if x.node.isNil: x.node = newNode(nkStrLit) elif x.node.kind == nkNilLit: system.reset(x.node[]) x.node.kind = nkStrLit else: - assert x.node.kind in {nkStrLit..nkTripleStrLit} + # XXX this is hacky; tests/txmlgen triggers it: + x.node = newNode(nkStrLit) + #if x.node.kind notin {nkStrLit..nkTripleStrLit}: + # debug x.node + #assert x.node.kind in {nkStrLit..nkTripleStrLit} template createStr(x) = x.node = newNode(nkStrLit) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index b594c00a9..a9029442e 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -924,7 +924,7 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; flags: TGenFlags) = # a nop for certain types let isAddr = opc in {opcAddrNode, opcAddrReg} - let flags = if isAddr: flags+{gfAddrOf} else: flags + let newflags = if isAddr: flags+{gfAddrOf} else: flags # consider: # proc foo(f: var ref int) = # f = new(int) @@ -935,12 +935,14 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; # The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for # nkAddr we must not use 'unneededIndirection', but for deref we use it. if not isAddr and unneededIndirection(n.sons[0]): - gen(c, n.sons[0], dest, flags) + gen(c, n.sons[0], dest, newflags) else: - let tmp = c.genx(n.sons[0], flags) + let tmp = c.genx(n.sons[0], newflags) if dest < 0: dest = c.getTemp(n.typ) if not isAddr: gABC(c, n, opc, dest, tmp) + if gfAddrOf notin flags and fitsRegister(n.typ): + c.gABC(n, opcNodeToReg, dest, dest) elif c.prc.slots[tmp].kind >= slotTempUnknown: gABC(c, n, opcAddrReg, dest, tmp) else: @@ -1541,8 +1543,8 @@ proc genProc(c: PCtx; s: PSym): int = c.optimizeJumps(result) s.offset = c.prc.maxSlots #if s.name.s == "importImpl_forward" or s.name.s == "importImpl": - # c.echoCode(result) - # echo renderTree(body) + #c.echoCode(result) + #echo renderTree(body) c.prc = oldPrc else: c.prc.maxSlots = s.offset diff --git a/tests/discard/tneedsdiscard.nim b/tests/discard/tneedsdiscard.nim index 24f5b2eee..2a7856b4a 100644 --- a/tests/discard/tneedsdiscard.nim +++ b/tests/discard/tneedsdiscard.nim @@ -1,6 +1,6 @@ discard """ line: 10 - errormsg: "value returned by statement has to be discarded" + errormsg: "value of type 'bool' has to be discarded" """ proc p = diff --git a/tests/exprs/tstmtexp.nim b/tests/exprs/tstmtexp.nim index 7cbf2eb3d..fe60dd3ba 100644 --- a/tests/exprs/tstmtexp.nim +++ b/tests/exprs/tstmtexp.nim @@ -1,9 +1,9 @@ discard """ file: "tstmtexp.nim" line: 8 - errormsg: "value returned by statement has to be discarded" + errormsg: "value of type 'int literal(5)' has to be discarded" """ # Test 3 -1+4 #ERROR_MSG value returned by statement has to be discarded +1+4 From ad79b125e7ae9a703b8ea900ea3dd760ee1ff0c9 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 25 Feb 2014 18:08:40 +0100 Subject: [PATCH 090/249] bootstrapping should work again --- compiler/vm.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 108979739..0a3c17e8e 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -115,10 +115,9 @@ proc createStrKeepNode(x: var TRegister) = elif x.node.kind == nkNilLit: system.reset(x.node[]) x.node.kind = nkStrLit - else: + elif x.node.kind notin {nkStrLit..nkTripleStrLit}: # XXX this is hacky; tests/txmlgen triggers it: x.node = newNode(nkStrLit) - #if x.node.kind notin {nkStrLit..nkTripleStrLit}: # debug x.node #assert x.node.kind in {nkStrLit..nkTripleStrLit} From 384a018906bf999355019bd3761f8b085b352eb6 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 25 Feb 2014 21:30:18 +0100 Subject: [PATCH 091/249] htmlgen works again --- compiler/vm.nim | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 0a3c17e8e..ed259636e 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -460,11 +460,16 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = assert regs[rb].node.kind == nkRefTy regs[ra].node = regs[rb].node.sons[0] else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, errNilAccess) of opcWrDeref: # a[] = b - decodeBC(rkNode) - putIntoNode(regs[ra].node, regs[rb]) + let ra = instr.regA + let rb = instr.regB + case regs[ra].kind + of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rb]) + of rkRegisterAddr: regs[ra].regAddr[] = regs[rb] + of rkNode: putIntoNode(regs[ra].node, regs[rb]) + else: stackTrace(c, tos, pc, errNilAccess) of opcAddInt: decodeBC(rkInt) regs[ra].intVal = regs[rb].intVal + regs[rc].intVal From 2b271e7b17a08e27b2fe961e93eebb3089461dbb Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 26 Feb 2014 06:29:22 +0100 Subject: [PATCH 092/249] renamed TRegister to TFullReg so that older versions can compile it --- compiler/vm.nim | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index ed259636e..8411c4a4b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -25,20 +25,20 @@ when hasFFI: type TRegisterKind = enum rkNone, rkNode, rkInt, rkFloat, rkRegisterAddr, rkNodeAddr - TRegister = object # with a custom mark proc, we could use the same + TFullReg = object # with a custom mark proc, we could use the same # data representation as LuaJit (tagged NaNs). case kind: TRegisterKind of rkNone: nil of rkInt: intVal: BiggestInt of rkFloat: floatVal: BiggestFloat of rkNode: node: PNode - of rkRegisterAddr: regAddr: ptr TRegister + of rkRegisterAddr: regAddr: ptr TFullReg of rkNodeAddr: nodeAddr: ptr PNode PStackFrame* = ref TStackFrame TStackFrame* = object prc: PSym # current prc; proc that is evaluated - slots: seq[TRegister] # parameters passed to the proc + locals; + slots: seq[TFullReg] # parameters passed to the proc + locals; # parameters come first next: PStackFrame # for stacking comesFrom: int @@ -75,7 +75,7 @@ proc bailOut(c: PCtx; tos: PStackFrame) = when not defined(nimComputedGoto): {.pragma: computedGoto.} -proc myreset(n: var TRegister) = +proc myreset(n: var TFullReg) = when defined(system.reset): reset(n) @@ -109,7 +109,7 @@ template decodeBx(k: expr) {.immediate, dirty.} = template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b) # XXX fix minor 'shallowCopy' overloading bug in compiler -proc createStrKeepNode(x: var TRegister) = +proc createStrKeepNode(x: var TFullReg) = if x.node.isNil: x.node = newNode(nkStrLit) elif x.node.kind == nkNilLit: @@ -124,7 +124,7 @@ proc createStrKeepNode(x: var TRegister) = template createStr(x) = x.node = newNode(nkStrLit) -proc moveConst(x: var TRegister, y: TRegister) = +proc moveConst(x: var TFullReg, y: TFullReg) = if x.kind != y.kind: myreset(x) x.kind = y.kind @@ -161,7 +161,7 @@ proc copyValue(src: PNode): PNode = for i in countup(0, sonsLen(src) - 1): result.sons[i] = copyValue(src.sons[i]) -proc asgnComplex(x: var TRegister, y: TRegister) = +proc asgnComplex(x: var TFullReg, y: TFullReg) = if x.kind != y.kind: myreset(x) x.kind = y.kind @@ -173,7 +173,7 @@ proc asgnComplex(x: var TRegister, y: TRegister) = of rkRegisterAddr: x.regAddr = y.regAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr -proc putIntoNode(n: PNode; x: TRegister) = +proc putIntoNode(n: PNode; x: TFullReg) = case x.kind of rkNone: discard of rkInt: n.intVal = x.intVal @@ -182,7 +182,7 @@ proc putIntoNode(n: PNode; x: TRegister) = of rkRegisterAddr: putIntoNode(n, x.regAddr[]) of rkNodeAddr: n[] = x.nodeAddr[][] -proc putIntoReg(dest: var TRegister; n: PNode) = +proc putIntoReg(dest: var TFullReg; n: PNode) = case n.kind of nkStrLit..nkTripleStrLit: dest.kind = rkNode @@ -198,7 +198,7 @@ proc putIntoReg(dest: var TRegister; n: PNode) = dest.kind = rkNode dest.node = n -proc regToNode(x: TRegister): PNode = +proc regToNode(x: TFullReg): PNode = case x.kind of rkNone: result = newNode(nkEmpty) of rkInt: result = newNode(nkIntLit); result.intVal = x.intVal @@ -216,7 +216,7 @@ proc pushSafePoint(f: PStackFrame; pc: int) = proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop() -proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: seq[TRegister]): int = +proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: seq[TFullReg]): int = let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) var f = tos while true: @@ -258,7 +258,7 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = return pc return -1 -proc opConv*(dest: var TRegister, src: TRegister, desttyp, srctyp: PType): bool = +proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = if desttyp.kind == tyString: if dest.kind != rkNode: myreset(dest) @@ -328,10 +328,10 @@ proc compile(c: PCtx, s: PSym): int = result = vmgen.genProc(c, s) #c.echoCode -proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = +proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos - var regs: seq[TRegister] # alias to tos.slots for performance + var regs: seq[TFullReg] # alias to tos.slots for performance move(regs, tos.slots) #echo "NEW RUN ------------------------" while true: @@ -877,7 +877,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TRegister = message(c.debug[pc], hintQuitCalled) quit(int(getOrdValue(regs[ra].regToNode))) else: - return TRegister(kind: rkNone) + return TFullReg(kind: rkNone) of opcSetLenStr: decodeB(rkNode) createStrKeepNode regs[ra] From a79579da6fd03f11dc48f03cc9e8598603fa3863 Mon Sep 17 00:00:00 2001 From: Charlie Barto Date: Wed, 26 Feb 2014 22:25:02 -0500 Subject: [PATCH 093/249] added an overload for highlight.initGeneralTokenizer that accepts a cstring. I use this when calling the highlighter from C#. --- lib/packages/docutils/highlite.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 4ca0c79e0..c507f5e1c 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -61,9 +61,8 @@ proc getSourceLanguage*(name: string): TSourceLanguage = if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0: return i result = langNone - -proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = - g.buf = cstring(buf) +proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) = + g.buf = buf g.kind = low(TTokenClass) g.start = 0 g.length = 0 @@ -71,6 +70,8 @@ proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = var pos = 0 # skip initial whitespace: while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) g.pos = pos +proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) = + initGeneralTokenizer(g, cstring(buf)) proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) = discard From f4371bfef85e5a1340de803d90ec176291094d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Thu, 27 Feb 2014 19:48:53 +0100 Subject: [PATCH 094/249] osproc: add warning about using waitForExit without poParentStreams --- lib/pure/osproc.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 2a685f3fb..582b3c960 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -168,6 +168,9 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} = proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl, extern: "nosp$1", tags: [].} ## waits for the process to finish and returns `p`'s error code. + ## + ## **Warning**: Be careful when using waitForExit for processes created without + ## poParentStreams because they may fill output buffers, causing deadlock. proc peekExitCode*(p: PProcess): int {.tags: [].} ## return -1 if the process is still running. Otherwise the process' exit code From 8a4138da5504ff314b6849fd0f5b317ab770a93c Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Feb 2014 20:28:41 +0100 Subject: [PATCH 095/249] fixed typo in error message From e1600f209fd5a2e152f0ff556c10e097e5bec256 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Feb 2014 20:29:02 +0100 Subject: [PATCH 096/249] nil -> discard --- lib/impure/db_mysql.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 8cdccda01..74aaa1a59 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -87,7 +87,7 @@ proc newRow(L: int): TRow = proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) = if row != nil: - while mysql.FetchRow(sqlres) != nil: nil + while mysql.FetchRow(sqlres) != nil: discard mysql.FreeResult(sqlres) iterator fastRows*(db: TDbConn, query: TSqlQuery, From 2f692dcc3ae8820969e61ab86f6f29fe33308e91 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Feb 2014 20:39:34 +0100 Subject: [PATCH 097/249] make C backend more robust against compiler bugs --- compiler/cgen.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 87ed23f36..8da753d04 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -761,6 +761,8 @@ proc genProcAux(m: BModule, prc: PSym) = var returnStmt: PRope = nil assert(prc.ast != nil) if sfPure notin prc.flags and prc.typ.sons[0] != nil: + if resultPos >= prc.ast.len: + internalError(prc.info, "proc has no result symbol") var res = prc.ast.sons[resultPos].sym # get result symbol if not isInvalidReturnType(prc.typ.sons[0]): if sfNoInit in prc.flags: incl(res.flags, sfNoInit) From d828b0b96eef8fd77a57a5b71f65e2ae9f6b1f30 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Feb 2014 20:43:10 +0100 Subject: [PATCH 098/249] some progress on the new name mangler --- compiler/canonicalizer.nim | 381 ++++++++++++++++++++++++------------- 1 file changed, 251 insertions(+), 130 deletions(-) diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index fb5b3b9ce..94cb8e355 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -13,8 +13,8 @@ import strutils, db_sqlite, md5 var db: TDbConn -# We *hash* the relevant information into 128 bit hashes. This should be good enough -# to prevent any collisions. +# We *hash* the relevant information into 128 bit hashes. This should be good +# enough to prevent any collisions. type TUid = distinct MD5Digest @@ -42,8 +42,7 @@ const proc toBase64a(s: cstring, len: int): string = ## encodes `s` into base64 representation. After `lineLen` characters, a ## `newline` is added. - var total = ((len + 2) div 3) * 4 - result = newStringOfCap(total) + result = newStringOfCap(((len + 2) div 3) * 4) var i = 0 while i < s.len - 2: let a = ord(s[i]) @@ -82,7 +81,7 @@ proc hashSym(c: var MD5Context, s: PSym) = proc hashTree(c: var MD5Context, n: PNode) = if n == nil: - c &= "null" + c &= "noTreeKind" return var k = n.kind md5Update(c, cast[cstring](addr(k)), 1) @@ -105,146 +104,268 @@ proc hashTree(c: var MD5Context, n: PNode) = else: for i in 0.. 1: add(result, ", ") - add(result, typeToString(t.sons[i])) - add(result, ']') - of tyTypeDesc: - if t.base.kind == tyNone: result = "typedesc" - else: result = "typedesc[" & typeToString(t.base) & "]" - of tyStatic: - internalAssert t.len > 0 - result = "static[" & typeToString(t.sons[0]) & "]" + for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)): + c.hashType t.sons[i] of tyUserTypeClass: internalAssert t.sym != nil and t.sym.owner != nil - return t.sym.owner.name.s - of tyBuiltInTypeClass: - result = case t.base.kind: - of tyVar: "var" - of tyRef: "ref" - of tyPtr: "ptr" - of tySequence: "seq" - of tyArray: "array" - of tySet: "set" - of tyRange: "range" - of tyDistinct: "distinct" - of tyProc: "proc" - of tyObject: "object" - of tyTuple: "tuple" - else: (internalAssert(false); "") + c &= t.sym.owner.name.s of tyUserTypeClassInst: let body = t.base - result = body.sym.name.s & "[" + c.hashSym body.sym for i in countup(1, sonsLen(t) - 2): - if i > 1: add(result, ", ") - add(result, typeToString(t.sons[i])) - result.add "]" - of tyAnd: - result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1]) - of tyOr: - result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1]) - of tyNot: - result = "not " & typeToString(t.sons[0]) - of tyExpr: - internalAssert t.len == 0 - result = "expr" + c.hashType t.sons[i] of tyFromExpr, tyFieldAccessor: - result = renderTree(t.n) - of tyArray: - if t.sons[0].kind == tyRange: - result = "array[" & hashTree(t.sons[0].n) & ", " & - typeToString(t.sons[1]) & ']' - else: - result = "array[" & typeToString(t.sons[0]) & ", " & - typeToString(t.sons[1]) & ']' - of tyArrayConstr: - result = "Array constructor[" & hashTree(t.sons[0].n) & ", " & - typeToString(t.sons[1]) & ']' - of tySequence: - result = "seq[" & typeToString(t.sons[0]) & ']' - of tyOrdinal: - result = "ordinal[" & typeToString(t.sons[0]) & ']' - of tySet: - result = "set[" & typeToString(t.sons[0]) & ']' - of tyOpenArray: - result = "openarray[" & typeToString(t.sons[0]) & ']' - of tyDistinct: - result = "distinct " & typeToString(t.sons[0], preferName) + c.hashTree(t.n) + of tyArrayConstr: + c.hashTree(t.sons[0].n) + c.hashType(t.sons[1]) of tyTuple: - # we iterate over t.sons here, because t.n may be nil - result = "tuple[" - if t.n != nil: + if t.n != nil: assert(sonsLen(t.n) == sonsLen(t)) for i in countup(0, sonsLen(t.n) - 1): assert(t.n.sons[i].kind == nkSym) - add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) - if i < sonsLen(t.n) - 1: add(result, ", ") - else: - for i in countup(0, sonsLen(t) - 1): - add(result, typeToString(t.sons[i])) - if i < sonsLen(t) - 1: add(result, ", ") - add(result, ']') - of tyPtr, tyRef, tyVar, tyMutable, tyConst: - result = typeToStr[t.kind] & typeToString(t.sons[0]) + c &= t.n.sons[i].sym.name.s + c &= ":" + c.hashType(t.sons[i]) + c &= "," + else: + for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i] of tyRange: - result = "range " & hashTree(t.n) - if prefer != preferExported: - result.add("(" & typeToString(t.sons[0]) & ")") + c.hashTree(t.n) + c.hashType(t.sons[0]) of tyProc: - result = if tfIterator in t.flags: "iterator (" else: "proc (" - for i in countup(1, sonsLen(t) - 1): - add(result, typeToString(t.sons[i])) - if i < sonsLen(t) - 1: add(result, ", ") - add(result, ')') - if t.sons[0] != nil: add(result, ": " & typeToString(t.sons[0])) - var prag: string - if t.callConv != ccDefault: prag = CallingConvToStr[t.callConv] - else: prag = "" - if tfNoSideEffect in t.flags: - addSep(prag) - add(prag, "noSideEffect") - if tfThread in t.flags: - addSep(prag) - add(prag, "thread") - if len(prag) != 0: add(result, "{." & prag & ".}") - of tyVarargs, tyIter: - result = typeToStr[t.kind] % typeToString(t.sons[0]) - else: - result = typeToStr[t.kind] - if tfShared in t.flags: result = "shared " & result - if tfNotNil in t.flags: result.add(" not nil") + c &= (if tfIterator in t.flags: "iterator " else: "proc ") + for i in 0.. ') + +proc encodeType(w: PRodWriter, t: PType, result: var string) = + if t == nil: + # nil nodes have to be stored too: + result.add("[]") + return + # we need no surrounding [] here because the type is in a line of its own + if t.kind == tyForward: internalError("encodeType: tyForward") + # for the new rodfile viewer we use a preceeding [ so that the data section + # can easily be disambiguated: + add(result, '[') + encodeVInt(ord(t.kind), result) + add(result, '+') + encodeVInt(t.id, result) + if t.n != nil: + encodeNode(w, unknownLineInfo(), t.n, result) + if t.flags != {}: + add(result, '$') + encodeVInt(cast[int32](t.flags), result) + if t.callConv != low(t.callConv): + add(result, '?') + encodeVInt(ord(t.callConv), result) + if t.owner != nil: + add(result, '*') + encodeVInt(t.owner.id, result) + pushSym(w, t.owner) + if t.sym != nil: + add(result, '&') + encodeVInt(t.sym.id, result) + pushSym(w, t.sym) + if t.size != - 1: + add(result, '/') + encodeVBiggestInt(t.size, result) + if t.align != 2: + add(result, '=') + encodeVInt(t.align, result) + encodeLoc(w, t.loc, result) + for i in countup(0, sonsLen(t) - 1): + if t.sons[i] == nil: + add(result, "^()") + else: + add(result, '^') + encodeVInt(t.sons[i].id, result) + pushType(w, t.sons[i]) + +proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) = + add(result, '|') + encodeVInt(ord(lib.kind), result) + add(result, '|') + encodeStr(ropeToStr(lib.name), result) + add(result, '|') + encodeNode(w, info, lib.path, result) + +proc encodeSym(w: PRodWriter, s: PSym, result: var string) = + if s == nil: + # nil nodes have to be stored too: + result.add("{}") + return + # we need no surrounding {} here because the symbol is in a line of its own + encodeVInt(ord(s.kind), result) + result.add('+') + encodeVInt(s.id, result) + result.add('&') + encodeStr(s.name.s, result) + if s.typ != nil: + result.add('^') + encodeVInt(s.typ.id, result) + pushType(w, s.typ) + result.add('?') + if s.info.col != -1'i16: encodeVInt(s.info.col, result) + result.add(',') + if s.info.line != -1'i16: encodeVInt(s.info.line, result) + result.add(',') + encodeVInt(fileIdx(w, toFilename(s.info)), result) + if s.owner != nil: + result.add('*') + encodeVInt(s.owner.id, result) + pushSym(w, s.owner) + if s.flags != {}: + result.add('$') + encodeVInt(cast[int32](s.flags), result) + if s.magic != mNone: + result.add('@') + encodeVInt(ord(s.magic), result) + if s.options != w.options: + result.add('!') + encodeVInt(cast[int32](s.options), result) + if s.position != 0: + result.add('%') + encodeVInt(s.position, result) + if s.offset != - 1: + result.add('`') + encodeVInt(s.offset, result) + encodeLoc(w, s.loc, result) + if s.annex != nil: encodeLib(w, s.annex, s.info, result) + if s.constraint != nil: + add(result, '#') + encodeNode(w, unknownLineInfo(), s.constraint, result) + # lazy loading will soon reload the ast lazily, so the ast needs to be + # the last entry of a symbol: + if s.ast != nil: + # we used to attempt to save space here by only storing a dummy AST if + # it is not necessary, but Nimrod's heavy compile-time evaluation features + # make that unfeasible nowadays: + encodeNode(w, s.info, s.ast, result) proc createDb() = From f0a70c7aaebeba4e6f637e50447d7f5a9dc5c698 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Feb 2014 20:47:44 +0100 Subject: [PATCH 099/249] xmltree.`<>` macro works again --- compiler/nimeval.nim | 8 ++++---- compiler/vm.nim | 6 ++++-- compiler/vmgen.nim | 8 ++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index a239d4ef2..0ee108d48 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -17,11 +17,11 @@ proc execute*(program: string) = passes.gIncludeFile = includeModule passes.gImportModule = importModule initDefines() - LoadConfigs(DefaultConfig) + loadConfigs(DefaultConfig) initDefines() - DefineSymbol("nimrodvm") - when hasFFI: DefineSymbol("nimffi") + defineSymbol("nimrodvm") + when hasFFI: defineSymbol("nimffi") registerPass(verbosePass) registerPass(semPass) registerPass(vmPass) @@ -30,4 +30,4 @@ proc execute*(program: string) = compileSystemModule() var m = makeStdinModule() incl(m.flags, sfMainModule) - processModule(m, LLStreamOpen(program), nil) + processModule(m, llStreamOpen(program), nil) diff --git a/compiler/vm.nim b/compiler/vm.nim index 8411c4a4b..f9b143bce 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -173,12 +173,14 @@ proc asgnComplex(x: var TFullReg, y: TFullReg) = of rkRegisterAddr: x.regAddr = y.regAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr -proc putIntoNode(n: PNode; x: TFullReg) = +proc putIntoNode(n: var PNode; x: TFullReg) = case x.kind of rkNone: discard of rkInt: n.intVal = x.intVal of rkFloat: n.floatVal = x.floatVal - of rkNode: n[] = x.node[] + of rkNode: + if nfIsRef in x.node.flags: n = x.node + else: n[] = x.node[] of rkRegisterAddr: putIntoNode(n, x.regAddr[]) of rkNodeAddr: n[] = x.nodeAddr[][] diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a9029442e..687db1b18 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1001,7 +1001,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = c.gABC(left, opcWrObj, dest, idx, tmp) c.freeTemp(tmp) of nkDerefExpr, nkHiddenDeref: - let dest = c.genx(le, {gfAddrOf}) + let dest = c.genx(le.sons[0], {gfAddrOf}) let tmp = c.genx(ri) c.gABC(le, opcWrDeref, dest, tmp) c.freeTemp(tmp) @@ -1542,9 +1542,9 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "importImpl_forward" or s.name.s == "importImpl": - #c.echoCode(result) - #echo renderTree(body) + #if s.name.s == "xmlConstructor": + # echo renderTree(body) + # c.echoCode(result) c.prc = oldPrc else: c.prc.maxSlots = s.offset From c4f9c3b53eb6df46b0b900f2634991ff071e7071 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Feb 2014 23:28:15 +0100 Subject: [PATCH 100/249] tstmtexprs.nim works again --- compiler/vmdef.nim | 2 +- compiler/vmgen.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 72689c879..30beea29c 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -157,7 +157,7 @@ type slotTempInt, # some temporary int slotTempFloat, # some temporary float slotTempStr, # some temporary string - slotTempComplex # some complex temporary (n.sons field is used) + slotTempComplex # some complex temporary (s.node field is used) PProc* = ref object blocks*: seq[TBlock] # blocks; temp data structure diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 687db1b18..591c5ade8 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -944,9 +944,9 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; if gfAddrOf notin flags and fitsRegister(n.typ): c.gABC(n, opcNodeToReg, dest, dest) elif c.prc.slots[tmp].kind >= slotTempUnknown: - gABC(c, n, opcAddrReg, dest, tmp) - else: gABC(c, n, opcAddrNode, dest, tmp) + else: + gABC(c, n, opcAddrReg, dest, tmp) c.freeTemp(tmp) proc whichAsgnOpc(n: PNode): TOpcode = From 0d263f155bc19fcd3797b212d177c22dc213a2d4 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 28 Feb 2014 19:31:12 +0000 Subject: [PATCH 101/249] Fixes sockets2 on Windows. --- lib/pure/sockets2.nim | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim index 031217b90..3542a0694 100644 --- a/lib/pure/sockets2.nim +++ b/lib/pure/sockets2.nim @@ -17,7 +17,6 @@ when hostos == "solaris": when defined(Windows): import winlean - export ioctlsocket else: import posix export fcntl, F_GETFL, O_NONBLOCK, F_SETFL @@ -66,6 +65,16 @@ type when defined(windows): let osInvalidSocket* = winlean.INVALID_SOCKET + + const + IOCPARM_MASK* = 127 + IOC_IN* = int(-2147483648) + FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or + (102 shl 8) or 126 + + proc ioctlsocket*(s: TSocketHandle, cmd: clong, + argptr: ptr clong): cint {. + stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".} else: let osInvalidSocket* = posix.INVALID_SOCKET From d4263b1012f50e7d468e53d07d592b39983f026a Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Mar 2014 11:15:31 +0100 Subject: [PATCH 102/249] bugfix: walkFiles on windows shouldn't yield directories --- lib/pure/os.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 89bb92f9a..bfecc569a 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1192,7 +1192,8 @@ iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} = res = findFirstFile(pattern, f) if res != -1: while true: - if not skipFindData(f): + if not skipFindData(f) and + (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) == 0'i32: yield splitFile(pattern).dir / extractFilename(getFilename(f)) if findNextFile(res, f) == 0'i32: break findClose(res) From c55f5b34ee4d3c44c21b17c93e8d38dd867fb9cc Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Mar 2014 15:41:53 +0100 Subject: [PATCH 103/249] better handling of packages, still incomplete --- compiler/options.nim | 57 +++++++++++++++++++++------- lib/packages/docutils/docutils.babel | 6 +++ lib/stdlib.babel | 6 +++ todo.txt | 8 +++- 4 files changed, 63 insertions(+), 14 deletions(-) create mode 100644 lib/packages/docutils/docutils.babel create mode 100644 lib/stdlib.babel diff --git a/compiler/options.nim b/compiler/options.nim index 4f642e626..69d41c562 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -209,21 +209,52 @@ proc getGeneratedPath: string = result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / genSubDir +var packageCache = newStringTable(when FileSystemCaseSensitive: + modeCaseInsensitive + else: + modeCaseSensitive) + +iterator myParentDirs(p: string): string = + # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug... + var current = p + while true: + current = current.parentDir + if current.len == 0: break + yield current + proc getPackageName*(path: string): string = - var q = 1 - var b = 0 - if path[len(path)-1] in {DirSep, AltSep}: q = 2 - for i in countdown(len(path)-q, 0): - if path[i] in {DirSep, AltSep}: - if b == 0: b = i - else: - let x = path.substr(i+1, b-1) - case x.normalize - of "lib", "src", "source", "package", "pckg", "library", "private": - b = i + var parents = 0 + block packageSearch: + for d in myParentDirs(path): + if packageCache.hasKey(d): + #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name + return packageCache[d] + inc parents + for file in walkFiles(d / "*.babel"): + result = file.splitFile.name + break packageSearch + # we also store if we didn't find anything: + if result.isNil: result = "" + for d in myParentDirs(path): + #echo "set cache ", d, " |", result, "|", parents + packageCache[d] = result + dec parents + if parents <= 0: break + when false: + var q = 1 + var b = 0 + if path[len(path)-1] in {DirSep, AltSep}: q = 2 + for i in countdown(len(path)-q, 0): + if path[i] in {DirSep, AltSep}: + if b == 0: b = i else: - return x.replace('.', '_') - result = "" + let x = path.substr(i+1, b-1) + case x.normalize + of "lib", "src", "source", "package", "pckg", "library", "private": + b = i + else: + return x.replace('.', '_') + result = "" proc withPackageName*(path: string): string = let x = path.getPackageName diff --git a/lib/packages/docutils/docutils.babel b/lib/packages/docutils/docutils.babel new file mode 100644 index 000000000..1ed86ca05 --- /dev/null +++ b/lib/packages/docutils/docutils.babel @@ -0,0 +1,6 @@ +[Package] +name = "docutils" +version = "0.9.0" +author = "Andreas Rumpf" +description = "Nimrod's reStructuredText processor." +license = "MIT" diff --git a/lib/stdlib.babel b/lib/stdlib.babel new file mode 100644 index 000000000..f22598aba --- /dev/null +++ b/lib/stdlib.babel @@ -0,0 +1,6 @@ +[Package] +name = "stdlib" +version = "0.9.0" +author = "Dominik Picheta" +description = "Nimrod's standard library." +license = "MIT" diff --git a/todo.txt b/todo.txt index 4bee45516..5cbe2fe8b 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,11 @@ version 0.9.4 ============= +- fix gensym capture bug +- make the compiler aware of packages +- vm + - at least try to get the basic type zoo ops right + - optimize opcAsgnStr - fix GC issues - test and fix showoff @@ -21,8 +26,9 @@ Bugs version 0.9.x ============= +- memory manager: add a measure of fragmentation - implement 'union' and 'bits' pragmas -- fix closures +- fix closures/lambdalifting - ensure (ref T)(a, b) works as a type conversion and type constructor - optimize 'genericReset'; 'newException' leads to code bloat - stack-less GC From d8793dcf56b76e3a0cfabbb9a15759a54474777e Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Sun, 2 Mar 2014 23:15:30 +0100 Subject: [PATCH 104/249] Avoids crash parsing unknown rst raw directive. Fixes #761. --- lib/packages/docutils/rst.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index bb018bc1e..30cc9026b 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1543,7 +1543,7 @@ proc dirRaw(p: var TRstParser): PRstNode = elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0: dirRawAux(p, result, rnRawLatex, parseLiteralBlock) else: - rstMessage(p, meInvalidDirective, result.sons[0].text) + rstMessage(p, meInvalidDirective, result.sons[0].sons[0].text) else: dirRawAux(p, result, rnRaw, parseSectionWrapper) From 3249f16d85c2adee46e283e060ce6d1e31464d06 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Sat, 1 Mar 2014 17:41:50 -0500 Subject: [PATCH 105/249] Integrated 'babel' with testament --- tests/testament/categories.nim | 70 +++++++++++++++++++++++++++++++++- tests/testament/specs.nim | 2 + 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 442dd1212..0f6c24716 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -222,9 +222,75 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = else: testNoSpec r, makeTest(test, options, cat, actionCompile) +# ----------------------------- babel ---------------------------------------- + +let + babelExe = findExe("babel") + babelDir = getHomeDir() / ".babel" + packageDir = babelDir / "pkgs" + packageIndex = babelDir / "packages.json" + +proc waitForExitEx(p: PProcess): int = + var outp: PStream = outputStream(p) + var line = newStringOfCap(120).TaintedString + while true: + if outp.readLine(line): + discard + else: + result = peekExitCode(p) + if result != -1: break + close(p) + +proc getPackageDir(package: string): string = + ## TODO - Replace this with dom's version comparison magic. + var commandOutput = execCmdEx("babel path $#" % package) + if commandOutput.exitCode != quitSuccess: + return "" + else: + result = commandOutput[0] + +iterator listPackages(listAll = false): tuple[name, url: string] = + let packageList = parseFile(packageIndex) + + for package in packageList.items(): + let + name = package["name"].str + url = package["url"].str + isCorePackage = "nimrod-code" in normalize(url) + if isCorePackage or listAll: + yield (name, url) + +proc testBabelPackages(r: var TResults, options: string, cat: Category) = + if babelExe == "": + quit("Cannot run babel tests: Babel binary not found.", quitFailure) + + if execCmd("$# update" % babelExe) == quitFailure: + quit("Cannot run babel tests: Babel update failed.") + + for name, url in listPackages(): + var test = makeTest(name, options, cat) + echo(url) + let + installProcess = startProcess(babelExe, "", ["install", "-y", name]) + installStatus = waitForExitEx(installProcess) + installProcess.close + if installStatus != quitSuccess: + r.addResult(test, "", "", reInstallFailed) + continue + + let + buildPath = getPackageDir(name)[0.. -3] + let + buildProcess = startProcess(babelExe, buildPath, ["build"]) + buildStatus = waitForExitEx(buildProcess) + buildProcess.close + if buildStatus != quitSuccess: + r.addResult(test, "", "", reBuildFailed) + r.addResult(test, "", "", reSuccess) + # ---------------------------------------------------------------------------- -const AdditionalCategories = ["debugger", "tools", "examples", "stdlib"] +const AdditionalCategories = ["debugger", "tools", "examples", "stdlib", "babel"] proc `&.?`(a, b: string): string = # candidate for the stdlib? @@ -264,6 +330,8 @@ proc processCategory(r: var TResults, cat: Category, options: string) = compileExample(r, "examples/*.nim", options, cat) compileExample(r, "examples/gtk/*.nim", options, cat) compileExample(r, "examples/talk/*.nim", options, cat) + of "babel": + testBabelPackages(r, options, cat) else: for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"): testSpec r, makeTest(name, options, cat) diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index e97015946..f7982127f 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -28,6 +28,8 @@ type reCodegenFailure, reCodeNotFound, reExeNotFound, + reInstallFailed # package installation failed + reBuildFailed # package building failed reIgnored, # test is ignored reSuccess # test was successful TTarget* = enum From 614557994ec6143e20c1f2534f0a6fa87ca4c0c4 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 2 Mar 2014 23:46:20 +0100 Subject: [PATCH 106/249] the compiler is now aware of packages --- compiler/ast.nim | 1 + compiler/cgen.nim | 4 +++- compiler/modules.nim | 9 +++++---- compiler/options.nim | 28 +++++++++------------------- todo.txt | 1 - 5 files changed, 18 insertions(+), 25 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index bd8bdb30b..61b8ca795 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -481,6 +481,7 @@ type skStub, # symbol is a stub and not yet loaded from the ROD # file (it is loaded on demand, which may # mean: never) + skPackage # symbol is a package (used for canonicalization) TSymKinds* = set[TSymKind] const diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8da753d04..c3a28527e 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1044,8 +1044,10 @@ proc genMainProc(m: BModule) = appcg(m, m.s[cfsProcs], otherMain, []) proc getSomeInitName(m: PSym, suffix: string): PRope = + assert m.kind == skModule + assert m.owner.kind == skPackage if {sfSystemModule, sfMainModule} * m.flags == {}: - result = m.info.toFullPath.getPackageName.mangle.toRope + result = m.owner.name.s.mangle.toRope result.app m.name.s result.app suffix diff --git a/compiler/modules.nim b/compiler/modules.nim index 6a1491682..fb1940741 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -20,7 +20,7 @@ type TModuleInMemory* = object compiledAt*: float crc*: TCrc32 - deps*: seq[int32] ## XXX: slurped files are not currently tracked + deps*: seq[int32] ## XXX: slurped files are currently not tracked needsRecompile*: TNeedRecompile crcStatus*: TCrcStatus @@ -83,7 +83,7 @@ proc resetAllModules* = for i in 0..gCompiledModules.high: if gCompiledModules[i] != nil: resetModule(i.int32) - + resetPackageCache() # for m in cgenModules(): echo "CGEN MODULE FOUND" proc checkDepMem(fileIdx: int32): TNeedRecompile = @@ -120,8 +120,9 @@ proc newModule(fileIdx: int32): PSym = if not isNimrodIdentifier(result.name.s): rawMessage(errInvalidModuleName, result.name.s) - result.owner = result # a module belongs to itself result.info = newLineInfo(fileIdx, 1, 1) + result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil, + result.info) result.position = fileIdx growCache gMemCacheData, fileIdx diff --git a/compiler/options.nim b/compiler/options.nim index 69d41c562..102ebc386 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -209,10 +209,15 @@ proc getGeneratedPath: string = result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir / genSubDir -var packageCache = newStringTable(when FileSystemCaseSensitive: - modeCaseInsensitive - else: - modeCaseSensitive) +template newPackageCache(): expr = + newStringTable(when FileSystemCaseSensitive: + modeCaseInsensitive + else: + modeCaseSensitive) + +var packageCache = newPackageCache() + +proc resetPackageCache*() = packageCache = newPackageCache() iterator myParentDirs(p: string): string = # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug... @@ -240,21 +245,6 @@ proc getPackageName*(path: string): string = packageCache[d] = result dec parents if parents <= 0: break - when false: - var q = 1 - var b = 0 - if path[len(path)-1] in {DirSep, AltSep}: q = 2 - for i in countdown(len(path)-q, 0): - if path[i] in {DirSep, AltSep}: - if b == 0: b = i - else: - let x = path.substr(i+1, b-1) - case x.normalize - of "lib", "src", "source", "package", "pckg", "library", "private": - b = i - else: - return x.replace('.', '_') - result = "" proc withPackageName*(path: string): string = let x = path.getPackageName diff --git a/todo.txt b/todo.txt index 5cbe2fe8b..974e18360 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,6 @@ version 0.9.4 ============= - fix gensym capture bug -- make the compiler aware of packages - vm - at least try to get the basic type zoo ops right - optimize opcAsgnStr From 74549c327e44adf310c3bcc6aa8db450214b46d0 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 3 Mar 2014 08:17:51 +0100 Subject: [PATCH 107/249] added a warning for htmlgen --- lib/pure/htmlgen.nim | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index 63737d583..b9d6aec7b 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -1,12 +1,17 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # +## **Warning**: This module uses ``immediate`` macros which are known to +## cause problems. Do yourself a favor and import the module +## as ``from htmlgen import nil`` and then fully qualify the macros. +## +## ## This module implements a simple `XML`:idx: and `HTML`:idx: code ## generator. Each commonly used HTML tag has a corresponding macro ## that generates a string with its HTML representation. @@ -15,11 +20,11 @@ ## ## .. code-block:: nimrod ## var nim = "Nimrod" -## echo h1(a(href="http://nimrod-code.org", nim)) +## echo h1(a(href="http://nimrod-lang.org", nim)) ## ## Writes the string:: ## -##

Nimrod

+##

Nimrod

## import From 269bf8e334bd9edfe86b2923fffa157cf35f668d Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Mon, 3 Mar 2014 06:26:22 -0500 Subject: [PATCH 108/249] Removed babel package tests from those run when 'all' is specified. --- tests/testament/categories.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 0f6c24716..9dc51567d 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -290,7 +290,7 @@ proc testBabelPackages(r: var TResults, options: string, cat: Category) = # ---------------------------------------------------------------------------- -const AdditionalCategories = ["debugger", "tools", "examples", "stdlib", "babel"] +const AdditionalCategories = ["debugger", "tools", "examples", "stdlib"] proc `&.?`(a, b: string): string = # candidate for the stdlib? From 9620893d7201184a8635931aee9c684dea0bc5b9 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Mon, 3 Mar 2014 17:53:47 -0500 Subject: [PATCH 109/249] Allowed specification of what babel packages to install via new categories - 'babel-core', 'babel-extra', and 'babel-all' --- tests/testament/categories.nim | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 9dc51567d..bf65d26de 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -223,6 +223,10 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = testNoSpec r, makeTest(test, options, cat, actionCompile) # ----------------------------- babel ---------------------------------------- +type PackageFilter = enum + pfCoreOnly + pfExtraOnly + pfAll let babelExe = findExe("babel") @@ -249,7 +253,7 @@ proc getPackageDir(package: string): string = else: result = commandOutput[0] -iterator listPackages(listAll = false): tuple[name, url: string] = +iterator listPackages(filter: PackageFilter): tuple[name, url: string] = let packageList = parseFile(packageIndex) for package in packageList.items(): @@ -257,18 +261,25 @@ iterator listPackages(listAll = false): tuple[name, url: string] = name = package["name"].str url = package["url"].str isCorePackage = "nimrod-code" in normalize(url) - if isCorePackage or listAll: + case filter: + of pfCoreOnly: + if isCorePackage: + yield (name, url) + of pfExtraOnly: + if not isCorePackage: + yield (name, url) + of pfAll: yield (name, url) -proc testBabelPackages(r: var TResults, options: string, cat: Category) = +proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = if babelExe == "": quit("Cannot run babel tests: Babel binary not found.", quitFailure) if execCmd("$# update" % babelExe) == quitFailure: quit("Cannot run babel tests: Babel update failed.") - for name, url in listPackages(): - var test = makeTest(name, options, cat) + for name, url in listPackages(filter): + var test = makeTest(name, "", cat) echo(url) let installProcess = startProcess(babelExe, "", ["install", "-y", name]) @@ -290,7 +301,7 @@ proc testBabelPackages(r: var TResults, options: string, cat: Category) = # ---------------------------------------------------------------------------- -const AdditionalCategories = ["debugger", "tools", "examples", "stdlib"] +const AdditionalCategories = ["debugger", "tools", "examples", "stdlib", "babel-core"] proc `&.?`(a, b: string): string = # candidate for the stdlib? @@ -330,8 +341,12 @@ proc processCategory(r: var TResults, cat: Category, options: string) = compileExample(r, "examples/*.nim", options, cat) compileExample(r, "examples/gtk/*.nim", options, cat) compileExample(r, "examples/talk/*.nim", options, cat) - of "babel": - testBabelPackages(r, options, cat) + of "babel-core": + testBabelPackages(r, cat, pfCoreOnly) + of "babel-extra": + testBabelPackages(r, cat, pfExtraOnly) + of "babel-all": + testBabelPackages(r, cat, pfAll) else: for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"): testSpec r, makeTest(name, options, cat) From af31f19ebbb042c4ff5c490df9b53f74bab520f3 Mon Sep 17 00:00:00 2001 From: Varriount Date: Mon, 3 Mar 2014 18:43:41 -0500 Subject: [PATCH 110/249] Fix categories.nim --- tests/testament/categories.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index bf65d26de..627c3d5ea 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -251,7 +251,7 @@ proc getPackageDir(package: string): string = if commandOutput.exitCode != quitSuccess: return "" else: - result = commandOutput[0] + result = commandOutput[0].string iterator listPackages(filter: PackageFilter): tuple[name, url: string] = let packageList = parseFile(packageIndex) From e328d015f12c03a1f8391360d5dafc09596d0b72 Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Tue, 4 Mar 2014 00:49:08 +0100 Subject: [PATCH 111/249] Added a IpAddress structure to the net module --- lib/pure/net.nim | 232 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 231 insertions(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 0ec007009..cf5b4634e 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -9,7 +9,237 @@ ## This module implements a high-level cross-platform sockets interface. -import sockets2, os +import sockets2, os, strutils, unsigned + +type + IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address + IPv6, ## IPv6 address + IPv4 ## IPv4 address + + TIpAddress* = object ## stores an arbitrary IP address + case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6) + of IpAddressFamily.IPv6: + address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in case of IPv6 + of IpAddressFamily.IPv4: + address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in case of IPv4 + +proc IPv4_any*(): TIpAddress = + ## Returns the IPv4 any address, which can be used to listen on all available + ## network adapters + result = TIpAddress( + family: IpAddressFamily.IPv4, + address_v4: [0'u8, 0'u8, 0'u8, 0'u8]) + +proc IPv4_loopback*(): TIpAddress = + ## Returns the IPv4 loopback address (127.0.0.1) + result = TIpAddress( + family: IpAddressFamily.IPv4, + address_v4: [127'u8, 0'u8, 0'u8, 1'u8]) + +proc IPv4_broadcast*(): TIpAddress = + ## Returns the IPv4 broadcast address (255.255.255.255) + result = TIpAddress( + family: IpAddressFamily.IPv4, + address_v4: [255'u8, 255'u8, 255'u8, 255'u8]) + +proc IPv6_any*(): TIpAddress = + ## Returns the IPv6 any address (::0), which can be used + ## to listen on all available network adapters + result = TIpAddress( + family: IpAddressFamily.IPv6, + address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8]) + +proc IPv6_loopback*(): TIpAddress = + ## Returns the IPv6 loopback address (::1) + result = TIpAddress( + family: IpAddressFamily.IPv6, + address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,1'u8]) + +proc `$`*(address: TIpAddress): string = + ## Converts an TIpAddress into the textual representation + result = "" + case address.family + of IpAddressFamily.IPv4: + for i in 0 .. 3: + if i != 0: + result.add('.') + result.add($address.address_v4[i]) + of IpAddressFamily.IPv6: + var + currentZeroStart = -1 + currentZeroCount = 0 + biggestZeroStart = -1 + biggestZeroCount = 0 + # Look for the largest block of zeros + for i in 0..7: + var isZero = address.address_v6[i*2] == 0 and address.address_v6[i*2+1] == 0 + if isZero: + if currentZeroStart == -1: + currentZeroStart = i + currentZeroCount = 1 + else: + currentZeroCount.inc() + if currentZeroCount > biggestZeroCount: + biggestZeroCount = currentZeroCount + biggestZeroStart = currentZeroStart + else: + currentZeroStart = -1 + + if biggestZeroCount == 8: # Special case ::0 + result.add("::") + else: # Print address + var printedLastGroup = false + for i in 0..7: + var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8 + word = word or cast[uint16](address.address_v6[i*2+1]) + + if biggestZeroCount != 0 and # Check if in skip group + (i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)): + if i == biggestZeroStart: # skip start + result.add("::") + printedLastGroup = false + else: + if printedLastGroup: + result.add(':') + result.add(toHex(BiggestInt(word),4)) # this has too many digits + printedLastGroup = true + +proc parseIPv4Address(address_str: string): TIpAddress = + ## Parses IPv4 adresses + ## Raises EInvalidValue on errors + var + byteCount = 0 + currentByte:uint16 = 0 + seperatorValid = false + + result.family = IpAddressFamily.IPv4 + + for i in 0 .. high(address_str): + if address_str[i] in {'0'..'9'}: # Character is a number + currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) + if currentByte > 255'u16: raise new EInvalidValue + seperatorValid = true + elif address_str[i] == '.': # IPv4 address separator + if not seperatorValid or byteCount >= 3: + raise new EInvalidValue + result.address_v4[byteCount] = cast[uint8](currentByte) + currentByte = 0 + byteCount.inc + seperatorValid = false + else: + raise new EInvalidValue # Invalid character + + if byteCount != 3 or not seperatorValid: + raise new EInvalidValue + result.address_v4[byteCount] = cast[uint8](currentByte) + +proc parseIPv6Address(address_str: string): TIpAddress = + ## Parses IPv6 adresses + ## Raises EInvalidValue on errors + result.family = IpAddressFamily.IPv6 + if address_str.len < 2: raise new EInvalidValue + + var + groupCount = 0 + currentGroupStart = 0 + currentShort:uint32 = 0 + seperatorValid = true + dualColonGroup = -1 + lastWasColon = false + v4StartPos = -1 + byteCount = 0 + + for i,c in address_str: + if c == ':': + if not seperatorValid: raise new EInvalidValue + if lastWasColon: + if dualColonGroup != -1: raise new EInvalidValue + dualColonGroup = groupCount + seperatorValid = false + elif i != 0 and i != high(address_str): + if groupCount >= 8: raise new EInvalidValue + result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) + result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) + currentShort = 0 + groupCount.inc() + if dualColonGroup != -1: seperatorValid = false + elif i == 0: # only valid if address starts with :: + if address_str[1] != ':': raise new EInvalidValue + else: # i == high(address_str) - only valid if address ends with :: + if address_str[high(address_str)-1] != ':': raise new EInvalidValue + lastWasColon = true + currentGroupStart = i + 1 + elif c == '.': # Switch to parse IPv4 mode + if i < 3 or not seperatorValid or groupCount >= 7: raise new EInvalidValue + v4StartPos = currentGroupStart + currentShort = 0 + seperatorValid = false + break + elif c in {'0'..'9','a'..'f','A'..'F'}: + if c in {'0'..'9'}: # Normal digit + currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0')) + elif c >= 'a' and c <= 'f': # Lower case hex + currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10 + else: # Upper case hex + currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 + if currentShort > 65535'u32: raise new EInvalidValue + lastWasColon = false + seperatorValid = true + else: + raise new EInvalidValue + + + if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff + if seperatorValid: # Copy remaining data + if groupCount >= 8: raise new EInvalidValue + result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) + result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) + groupCount.inc() + else: # Must parse IPv4 address + for i,c in address_str[v4StartPos..high(address_str)]: + if c in {'0'..'9'}: # Character is a number + currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) + if currentShort > 255'u32: raise new EInvalidValue + seperatorValid = true + elif c == '.': # IPv4 address separator + if not seperatorValid or byteCount >= 3: + raise new EInvalidValue + result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) + currentShort = 0 + byteCount.inc() + seperatorValid = false + else: # Invalid character + raise new EInvalidValue + + if byteCount != 3 or not seperatorValid: + raise new EInvalidValue + result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) + groupCount += 2 + + # Shift and fill zeros in case of :: + if groupCount > 8: + raise new EInvalidValue + elif groupCount < 8: # must fill + if dualColonGroup == -1: raise new EInvalidValue + var toFill = 8 - groupCount # The number of groups to fill + var toShift = groupCount - dualColonGroup # Nr of known groups after :: + for i in 0..2*toShift-1: # shift + result.address_v6[15-i] = result.address_v6[groupCount*2-i-1] + for i in 0..2*toFill-1: # fill with 0s + result.address_v6[dualColonGroup*2+i] = 0 + elif dualColonGroup != -1: raise new EInvalidValue + + +proc parseIpAddress*(address_str: string): TIpAddress = + ## Parses an IP address + ## Throws EInvalidValue on error + if address_str == nil: + raise new EInvalidValue + if address_str.contains(':'): + return parseIPv6Address(address_str) + else: + return parseIPv4Address(address_str) + type TSocket* = TSocketHandle From f54556518eba317dbe5a4aa86086fa2de8671f7d Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Mar 2014 08:29:41 +0100 Subject: [PATCH 112/249] don't produce nested indents for nested stmt lists --- compiler/renderer.nim | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 509c815c1..d68cb91c0 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -558,16 +558,19 @@ proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool = result = true break -proc gstmts(g: var TSrcGen, n: PNode, c: TContext) = - if n.kind == nkEmpty: return +proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) = + if n.kind == nkEmpty: return if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: - indentNL(g) - for i in countup(0, sonsLen(n) - 1): + if doIndent: indentNL(g) + for i in countup(0, sonsLen(n) - 1): optNL(g) - gsub(g, n.sons[i]) + if n.sons[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: + gstmts(g, n.sons[i], c, doIndent=false) + else: + gsub(g, n.sons[i]) gcoms(g) - dedent(g) - else: + if doIndent: dedent(g) + else: if rfLongMode in c.flags: indentNL(g) gsub(g, n) gcoms(g) From 7918c30b903123b9c12cc664f99f43d0754c4fdd Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Mar 2014 08:30:11 +0100 Subject: [PATCH 113/249] removed dead code --- compiler/ccgmerge.nim | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 5b04f1358..2d27257ce 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -145,33 +145,6 @@ proc atEndMark(buf: cstring, pos: int): bool = while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s result = s == NimMergeEndMark.len -when false: - proc readVerbatimSection(L: var TBaseLexer): PRope = - var pos = L.bufpos - var buf = L.buf - result = newMutableRope(30_000) - while true: - case buf[pos] - of CR: - pos = nimlexbase.HandleCR(L, pos) - buf = L.buf - result.data.add(tnl) - of LF: - pos = nimlexbase.HandleLF(L, pos) - buf = L.buf - result.data.add(tnl) - of '\0': - InternalError("ccgmerge: expected: " & NimMergeEndMark) - break - else: - if atEndMark(buf, pos): - inc pos, NimMergeEndMark.len - break - result.data.add(buf[pos]) - inc pos - L.bufpos = pos - freezeMutableRope(result) - proc readVerbatimSection(L: var TBaseLexer): PRope = var pos = L.bufpos var buf = L.buf From ed5bbfe36d584b8fd2a451718b05fba8149b0958 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Mar 2014 08:31:11 +0100 Subject: [PATCH 114/249] fixes #968 --- compiler/ccgutils.nim | 1 - compiler/types.nim | 3 ++- todo.txt | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 1129ecbef..da1673ca4 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -130,7 +130,6 @@ proc getUniqueType*(key: PType): PType = idTablePut(gTypeTable[k], key, key) result = key of tyProc: - # tyVar is not 100% correct, but would speeds things up a little: if key.callConv != ccClosure: result = key else: diff --git a/compiler/types.nim b/compiler/types.nim index e6178f446..edf5ab47b 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -452,7 +452,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyProc: "proc" of tyObject: "object" of tyTuple: "tuple" - else: (internalAssert(false); "") + of tyOpenArray: "openarray" + else: typeToStr[t.base.kind] of tyUserTypeClassInst: let body = t.base result = body.sym.name.s & "[" diff --git a/todo.txt b/todo.txt index 974e18360..ba062a949 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,7 @@ version 0.9.4 ============= +- make testament produce full JSON information - fix gensym capture bug - vm - at least try to get the basic type zoo ops right From ab0fea919900be21c0872d1932a4ef90dd440a42 Mon Sep 17 00:00:00 2001 From: Varriount Date: Tue, 4 Mar 2014 10:10:00 -0500 Subject: [PATCH 115/249] Changed behavior when babel cannot be found/run Failure to find and run babel when using the tester now produces a warning message instead of causing the tester to quit. --- tests/testament/categories.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 627c3d5ea..9bb4838e0 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -273,10 +273,12 @@ iterator listPackages(filter: PackageFilter): tuple[name, url: string] = proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) = if babelExe == "": - quit("Cannot run babel tests: Babel binary not found.", quitFailure) + echo("[Warning] - Cannot run babel tests: Babel binary not found.") + return if execCmd("$# update" % babelExe) == quitFailure: - quit("Cannot run babel tests: Babel update failed.") + echo("[Warning] - Cannot run babel tests: Babel update failed.") + return for name, url in listPackages(filter): var test = makeTest(name, "", cat) From 016492375f149a0e71239845d55f8771e151299c Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 4 Mar 2014 22:38:43 +0200 Subject: [PATCH 116/249] failing test case for static evaluation --- tests/vm/tstaticprintseq.nim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/vm/tstaticprintseq.nim diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim new file mode 100644 index 000000000..99a56d161 --- /dev/null +++ b/tests/vm/tstaticprintseq.nim @@ -0,0 +1,21 @@ +discard """ + msg: '''1 +2 +3 +1 +2 +3''' +""" + +const s = @[1,2,3] + +macro foo: stmt = + for e in s: + echo e + +foo() + +static: + for e in s: + echo e + From 0f88ee7d0c17852cbd622b65f1b02422b006a81b Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Tue, 4 Mar 2014 22:41:53 +0100 Subject: [PATCH 117/249] Use character ranges from strutils. --- lib/pure/net.nim | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index cf5b4634e..90e21c644 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -55,6 +55,17 @@ proc IPv6_loopback*(): TIpAddress = family: IpAddressFamily.IPv6, address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,1'u8]) +proc `==`*(lhs, rhs: TIpAddress): bool = + ## Compares two IpAddresses for Equality. Returns two if the addresses are equal + if lhs.family != rhs.family: return false + if lhs.family == IpAddressFamily.IPv4: + for i in low(lhs.address_v4) .. high(lhs.address_v4): + if lhs.address_v4[i] != rhs.address_v4[i]: return false + else: # IPv6 + for i in low(lhs.address_v6) .. high(lhs.address_v6): + if lhs.address_v6[i] != rhs.address_v6[i]: return false + return true + proc `$`*(address: TIpAddress): string = ## Converts an TIpAddress into the textual representation result = "" @@ -115,7 +126,7 @@ proc parseIPv4Address(address_str: string): TIpAddress = result.family = IpAddressFamily.IPv4 for i in 0 .. high(address_str): - if address_str[i] in {'0'..'9'}: # Character is a number + if address_str[i] in strutils.Digits: # Character is a number currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) if currentByte > 255'u16: raise new EInvalidValue seperatorValid = true @@ -175,8 +186,8 @@ proc parseIPv6Address(address_str: string): TIpAddress = currentShort = 0 seperatorValid = false break - elif c in {'0'..'9','a'..'f','A'..'F'}: - if c in {'0'..'9'}: # Normal digit + elif c in strutils.HexDigits: + if c in strutils.Digits: # Normal digit currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0')) elif c >= 'a' and c <= 'f': # Lower case hex currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10 @@ -197,7 +208,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = groupCount.inc() else: # Must parse IPv4 address for i,c in address_str[v4StartPos..high(address_str)]: - if c in {'0'..'9'}: # Character is a number + if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) if currentShort > 255'u32: raise new EInvalidValue seperatorValid = true @@ -232,7 +243,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = proc parseIpAddress*(address_str: string): TIpAddress = ## Parses an IP address - ## Throws EInvalidValue on error + ## Raises EInvalidValue on error if address_str == nil: raise new EInvalidValue if address_str.contains(':'): @@ -282,4 +293,4 @@ proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} = else: var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK if fcntl(s, F_SETFL, mode) == -1: - osError(osLastError()) \ No newline at end of file + osError(osLastError()) From 74d51a77000144f6ddf53e366e4a7e58393201ab Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Tue, 4 Mar 2014 23:02:23 +0100 Subject: [PATCH 118/249] $ for TIpAddress now prints in the recommended format --- lib/pure/net.nim | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 90e21c644..ab9c3ac23 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -104,7 +104,7 @@ proc `$`*(address: TIpAddress): string = var word:uint16 = (cast[uint16](address.address_v6[i*2])) shl 8 word = word or cast[uint16](address.address_v6[i*2+1]) - if biggestZeroCount != 0 and # Check if in skip group + if biggestZeroCount != 0 and # Check if group is in skip group (i >= biggestZeroStart and i < (biggestZeroStart + biggestZeroCount)): if i == biggestZeroStart: # skip start result.add("::") @@ -112,7 +112,18 @@ proc `$`*(address: TIpAddress): string = else: if printedLastGroup: result.add(':') - result.add(toHex(BiggestInt(word),4)) # this has too many digits + var + afterLeadingZeros = false + mask = 0xF000'u16 + for j in 0'u16..3'u16: + var val = (mask and word) shr (4'u16*(3'u16-j)) + if val != 0 or afterLeadingZeros: + if val < 0xA: + result.add(chr(uint16(ord('0'))+val)) + else: # val >= 0xA + result.add(chr(uint16(ord('a'))+val-0xA)) + afterLeadingZeros = true + mask = mask shr 4 printedLastGroup = true proc parseIPv4Address(address_str: string): TIpAddress = From 5324c9ebba1aa21c893db03c1d56d3ce1b42f162 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 5 Mar 2014 01:14:37 +0200 Subject: [PATCH 119/249] iterators now return tyIter(T); tyIter(T) represents an "iteration yielding values of type T" I'm planning to use that in the context of the `is` operator supporting predicates such as `C.items is iterator` and also in the upcoming support for higher-order inline iterators. --- compiler/cgen.nim | 2 +- compiler/cgendata.nim | 2 +- compiler/semexprs.nim | 4 ++-- compiler/semstmts.nim | 5 +++-- compiler/semtypes.nim | 8 +++++--- compiler/sigmatch.nim | 8 ++++++-- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index c3a28527e..e8739d20e 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1192,7 +1192,7 @@ proc nullify[T](arr: var T) = for i in low(arr)..high(arr): arr[i] = nil -proc resetModule*(m: var BModule) = +proc resetModule*(m: BModule) = # between two compilations in CAAS mode, we can throw # away all the data that was written to disk initLinkedList(m.headerFiles) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 0df7bb6dc..e7d818556 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -146,7 +146,7 @@ proc newProc*(prc: PSym, module: BModule): BProc = result.nestedTryStmts = @[] result.finallySafePoints = @[] -iterator cgenModules*: var BModule = +iterator cgenModules*: BModule = for i in 0..high(gModules): # ultimately, we are iterating over the file ids here. # some "files" won't have an associated cgen module (like stdin) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 538489490..34d58118d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1266,7 +1266,7 @@ proc semYield(c: PContext, n: PNode): PNode = n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: var restype = c.p.owner.typ.sons[0] if restype != nil: - n.sons[0] = fitNode(c, restype, n.sons[0]) + n.sons[0] = fitNode(c, restype.base, n.sons[0]) if n.sons[0].typ == nil: internalError(n.info, "semYield") semYieldVarResult(c, n, restype) else: @@ -1884,7 +1884,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = message(n.info, warnDeprecated, "bind") result = semExpr(c, n.sons[0], flags) of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy: - var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) + var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter}) result.typ = makeTypeDesc(c, typ) #result = symNodeFromType(c, typ, n.info) of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c8d9e353a..b11f45b38 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -616,7 +616,8 @@ proc symForVar(c: PContext, n: PNode): PSym = proc semForVars(c: PContext, n: PNode): PNode = result = n var length = sonsLen(n) - var iter = skipTypes(n.sons[length-2].typ, {tyGenericInst}) + let iterBase = n.sons[length-2].typ.skipTypes({tyIter}) + var iter = skipTypes(iterBase, {tyGenericInst}) # length == 3 means that there is one for loop variable # and thus no tuple unpacking: if iter.kind != tyTuple or length == 3: @@ -626,7 +627,7 @@ proc semForVars(c: PContext, n: PNode): PNode = # BUGFIX: don't use `iter` here as that would strip away # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim`` # for an example: - v.typ = n.sons[length-2].typ + v.typ = iterBase n.sons[0] = newSymNode(v) if sfGenSym notin v.flags: addForVarDecl(c, v) else: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 809b80428..9c6f5114f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -845,8 +845,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, n.sons[0].info) if lifted != nil: r = lifted r.flags.incl tfRetType - result.sons[0] = skipIntLit(r) - res.typ = result.sons[0] + r = skipIntLit(r) + if kind == skIterator: r = newTypeWithSons(c, tyIter, @[r]) + result.sons[0] = r + res.typ = r proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = checkMinSonsLen(n, 1) @@ -959,7 +961,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkTypeOfExpr: # for ``type(countup(1,3))``, see ``tests/ttoseq``. checkSonsLen(n, 1) - result = semExprWithType(c, n.sons[0], {efInTypeof}).typ + result = semExprWithType(c, n.sons[0], {efInTypeof}).typ.skipTypes({tyIter}) of nkPar: if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index f8e3459df..ce6120f08 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -921,14 +921,18 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, prev.base, a.base) else: result = isNone - + + of tyIter: + if a.kind == f.kind: result = typeRel(c, f.base, a.base) + else: result = isNone + of tyStmt: result = isGeneric of tyProxy: result = isEqual - else: internalError("typeRel: " & $f.kind) + else: internalAssert false proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = var m: TCandidate From aa92669d9219816f9cd9036b4977ddbdd16d3c5d Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Mar 2014 02:14:53 +0100 Subject: [PATCH 120/249] fixes #937 --- compiler/canonicalizer.nim | 7 +++++++ compiler/vmdef.nim | 1 + compiler/vmgen.nim | 31 +++++++++++++++++++++++++------ tests/vm/twrongwhen.nim | 13 +++++++++++++ todo.txt | 1 + 5 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 tests/vm/twrongwhen.nim diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 94cb8e355..07e932b28 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -161,6 +161,13 @@ proc hashType(c: var MD5Context, t: PType) = if tfShared in t.flags: c &= "shared" if tfNotNil in t.flags: c &= "not nil" +proc canonConst(n: PNode): TUid = + var c: MD5Context + md5Init(c) + c.hashTree(n) + c.hashType(n.typ) + md5Final(c, MD5Digest(result)) + proc canonSym(s: PSym): TUid var c: MD5Context md5Init(c) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 30beea29c..102fc3024 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -161,6 +161,7 @@ type PProc* = ref object blocks*: seq[TBlock] # blocks; temp data structure + sym*: PSym slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]] maxSlots*: int diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 591c5ade8..123394436 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -980,6 +980,25 @@ proc setSlot(c: PCtx; v: PSym) = kind: if v.kind == skLet: slotFixedLet else: slotFixedVar) inc c.prc.maxSlots +proc cannotEval(n: PNode) {.noinline.} = + globalError(n.info, errGenerated, "cannot evaluate at compile time: " & + n.renderTree) + +proc isOwnedBy(a, b: PSym): bool = + var a = a.owner + while a != nil and a.kind != skModule: + if a == b: return true + a = a.owner + +proc checkCanEval(c: PCtx; n: PNode) = + # we need to ensure that we don't evaluate 'x' here: + # proc foo() = var x ... + let s = n.sym + if s.position == 0: + if s.kind in {skVar, skTemp, skLet, skParam, skResult} and + not s.isOwnedBy(c.prc.sym) and s.owner != c.module: + cannotEval(n) + proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = case le.kind of nkBracketExpr: @@ -1007,6 +1026,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = c.freeTemp(tmp) of nkSym: let s = le.sym + checkCanEval(c, le) if s.isGlobal: withTemp(tmp, le.typ): c.gen(le, tmp, {gfAddrOf}) @@ -1014,7 +1034,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = c.gABC(le, opcWrDeref, tmp, val) c.freeTemp(val) else: - if s.kind == skForVar and c.mode == emRepl: c.setSlot s + if s.kind == skForVar: c.setSlot s internalAssert s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}) var dest: TRegister = s.position + ord(s.kind == skParam) @@ -1046,10 +1066,6 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) = localError(info, errGenerated, "cannot 'importc' variable at compile time") -proc cannotEval(n: PNode) {.noinline.} = - globalError(n.info, errGenerated, "cannot evaluate at compile time: " & - n.renderTree) - proc getNullValue*(typ: PType, info: TLineInfo): PNode proc genGlobalInit(c: PCtx; n: PNode; s: PSym) = @@ -1190,12 +1206,14 @@ proc genVarSection(c: PCtx; n: PNode) = setSlot(c, a[i].sym) # v = t[i] var v: TDest = -1 + checkCanEval(c, a[i]) genRdVar(c, a[i], v, {gfAddrOf}) c.gABC(n, opcWrObj, v, tmp, i) # XXX globals? c.freeTemp(tmp) elif a.sons[0].kind == nkSym: let s = a.sons[0].sym + checkCanEval(c, a.sons[0]) if s.isGlobal: if s.position == 0: if sfImportc in s.flags: c.importcSym(a.info, s) @@ -1308,6 +1326,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = case n.kind of nkSym: let s = n.sym + checkCanEval(c, n) case s.kind of skVar, skForVar, skTemp, skLet, skParam, skResult: genRdVar(c, n, dest, flags) @@ -1525,7 +1544,7 @@ proc genProc(c: PCtx; s: PSym): int = # procs easily: let body = s.getBody let procStart = c.xjmp(body, opcJmp, 0) - var p = PProc(blocks: @[]) + var p = PProc(blocks: @[], sym: s) let oldPrc = c.prc c.prc = p # iterate over the parameters and allocate space for them: diff --git a/tests/vm/twrongwhen.nim b/tests/vm/twrongwhen.nim new file mode 100644 index 000000000..085bb6fb6 --- /dev/null +++ b/tests/vm/twrongwhen.nim @@ -0,0 +1,13 @@ +discard """ + output: "Error: cannot evaluate at compile time: x" + line: 7 +""" + +proc bla(x:int) = + when x == 0: + echo "oops" + else: + echo "good" + +bla(2) # echos "oops" + diff --git a/todo.txt b/todo.txt index ba062a949..a13c86b8f 100644 --- a/todo.txt +++ b/todo.txt @@ -1,6 +1,7 @@ version 0.9.4 ============= +- implement unchecked arrays - make testament produce full JSON information - fix gensym capture bug - vm From 4f946cb44e4fe60451b8ce4d15868ef0bce6949a Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Mar 2014 08:30:05 +0100 Subject: [PATCH 121/249] fixes #942 --- compiler/semstmts.nim | 2 +- tests/discard/tdiscardable.nim | 16 ++++++++++++++++ tests/exprs/texprstmt.nim | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c8d9e353a..b2b3ceb6d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1320,7 +1320,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): voidContext = true n.typ = enforceVoidContext - if i == last and efWantValue in flags: + if i == last and (length == 1 or efWantValue in flags): n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr elif i != last or voidContext or c.inTypeClass > 0: diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim index c0551ba2f..a806ccdce 100644 --- a/tests/discard/tdiscardable.nim +++ b/tests/discard/tdiscardable.nim @@ -11,3 +11,19 @@ proc q[T](x, y: T): T {.discardable.} = p(8, 2) q[float](0.8, 0.2) +# bug #942 + +template maybeMod(x: Tinteger, module:Natural):expr = + if module > 0: x mod module + else: x + +proc foo(b: int):int = + var x = 1 + result = x.maybeMod(b) # Works fine + +proc bar(b: int):int = + result = 1 + result = result.maybeMod(b) # Error: value returned by statement has to be discarded + +echo foo(0) +echo bar(0) diff --git a/tests/exprs/texprstmt.nim b/tests/exprs/texprstmt.nim index b32394d8d..355da2407 100644 --- a/tests/exprs/texprstmt.nim +++ b/tests/exprs/texprstmt.nim @@ -1,6 +1,6 @@ discard """ line: 10 - errormsg: "value returned by statement has to be discarded" + errormsg: "value of type 'string' has to be discarded" """ # bug #578 From 3dfb4891c77f03b038f6f65c750460c4fc882c7f Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Mar 2014 08:47:27 +0100 Subject: [PATCH 122/249] bugfix: unreported 'compiles' bug --- compiler/lookups.nim | 2 +- tests/compiles/tcompiles.nim | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 93a7b7c72..fcb5b6731 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -92,7 +92,7 @@ proc errorSym*(c: PContext, n: PNode): PSym = result.typ = errorType(c) incl(result.flags, sfDiscardable) # pretend it's imported from some unknown module to prevent cascading errors: - if gCmd != cmdInteractive: + if gCmd != cmdInteractive and c.inCompilesContext == 0: c.importTable.addSym(result) type diff --git a/tests/compiles/tcompiles.nim b/tests/compiles/tcompiles.nim index d0fccdaff..b3d9c17ce 100644 --- a/tests/compiles/tcompiles.nim +++ b/tests/compiles/tcompiles.nim @@ -24,3 +24,5 @@ ok supports(`+`, 34) no compiles(4+5.0 * "hallo") +no compiles(undeclaredIdentifier) +no compiles(undeclaredIdentifier) From b0625d2aa31c7d9708f3d248968a846e987c244b Mon Sep 17 00:00:00 2001 From: Pontus Stenetorp Date: Wed, 5 Mar 2014 18:36:59 +0900 Subject: [PATCH 123/249] No execute bit for c-sources build.sh, fixing doc --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 3eaef0b35..38e04233f 100644 --- a/readme.md +++ b/readme.md @@ -33,7 +33,7 @@ If you are on a fairly modern *nix system, the following steps should work: $ git clone git://github.com/Araq/Nimrod.git $ cd Nimrod $ git clone --depth 1 git://github.com/nimrod-code/csources -$ cd csources && ./build.sh +$ cd csources && sh build.sh $ cd .. $ bin/nimrod c koch $ ./koch boot -d:release From 6e42513acd19e447f7a546a3a6f9891978244b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Zieli=C5=84ski?= Date: Wed, 5 Mar 2014 18:31:22 +0100 Subject: [PATCH 124/249] zmq: remove unnecessary 'var' decls from high-level procs --- lib/wrappers/zmq.nim | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/wrappers/zmq.nim b/lib/wrappers/zmq.nim index 4e658028e..9826ab813 100644 --- a/lib/wrappers/zmq.nim +++ b/lib/wrappers/zmq.nim @@ -299,12 +299,12 @@ proc open*(address: string, server: bool, mode: TConnectionMode = conDEALER, else: if connect(result.s, address) != 0'i32: zmqError() -proc close*(c: var TConnection) = +proc close*(c: TConnection) = ## closes the connection. if close(c.s) != 0'i32: zmqError() if term(c.c) != 0'i32: zmqError() -proc send*(c: var TConnection, msg: string) = +proc send*(c: TConnection, msg: string) = ## sends a message over the connection. var m: TMsg if msg_init(m, msg.len) != 0'i32: zmqError() @@ -312,7 +312,7 @@ proc send*(c: var TConnection, msg: string) = if send(c.s, m, 0'i32) != 0'i32: zmqError() discard msg_close(m) -proc receive*(c: var TConnection): string = +proc receive*(c: TConnection): string = ## receives a message from a connection. var m: TMsg if msg_init(m) != 0'i32: zmqError() @@ -320,4 +320,3 @@ proc receive*(c: var TConnection): string = result = newString(msg_size(m)) copyMem(addr(result[0]), msg_data(m), result.len) discard msg_close(m) - From a066e4e5c5a8ed5967316a08803af61a3dc66d34 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Mar 2014 20:05:05 +0100 Subject: [PATCH 125/249] c2nim knows about the 'union' pragma --- compiler/c2nim/cparse.nim | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index e4abe11a0..555e1af10 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -681,11 +681,6 @@ proc parseField(p: var TParser, kind: TNodeKind): PNode = else: result = mangledIdent(p.tok.s, p) getTok(p, result) -proc takeOnlyFirstField(p: TParser, isUnion: bool): bool = - # if we generate an interface to a header file, *all* fields can be - # generated: - result = isUnion and p.options.header.len == 0 - proc parseStructBody(p: var TParser, isUnion: bool, kind: TNodeKind = nkRecList): PNode = result = newNodeP(kind, p) @@ -698,8 +693,7 @@ proc parseStructBody(p: var TParser, isUnion: bool, var i = parseField(p, kind) t = parseTypeSuffix(p, t) addSon(def, i, t, ast.emptyNode) - if not takeOnlyFirstField(p, isUnion) or sonsLen(result) < 1: - addSon(result, def) + addSon(result, def) if p.tok.xkind != pxComma: break getTok(p, def) eat(p, pxSemicolon, lastSon(result)) @@ -710,11 +704,12 @@ proc structPragmas(p: TParser, name: PNode, origName: string): PNode = result = newNodeP(nkPragmaExpr, p) addSon(result, exportSym(p, name, origName)) var pragmas = newNodeP(nkPragma, p) - addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p)) + #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p)) if p.options.header.len > 0: addSon(pragmas, newIdentStrLitPair("importc", origName, p), newIdentStrLitPair("header", p.options.header, p)) - addSon(result, pragmas) + if pragmas.len > 0: addSon(result, pragmas) + else: addSon(result, ast.emptyNode) proc enumPragmas(p: TParser, name: PNode): PNode = result = newNodeP(nkPragmaExpr, p) @@ -726,9 +721,13 @@ proc enumPragmas(p: TParser, name: PNode): PNode = addSon(pragmas, e) addSon(result, pragmas) -proc parseStruct(p: var TParser, isUnion: bool): PNode = +proc parseStruct(p: var TParser, isUnion: bool): PNode = result = newNodeP(nkObjectTy, p) - addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance + var pragmas = ast.emptyNode + if isUnion: + pragmas = newNodeP(nkPragma, p) + addSon(pragmas, newIdentNodeP("union", p)) + addSon(result, pragmas, ast.emptyNode) # no inheritance if p.tok.xkind == pxCurlyLe: addSon(result, parseStructBody(p, isUnion)) else: From 5506e8491dc9acc17537ffd96f5f763994077f4e Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Mar 2014 20:19:04 +0100 Subject: [PATCH 126/249] implemented 'union' and 'unchecked' pragmas --- compiler/ast.nim | 2 ++ compiler/ccgexprs.nim | 2 +- compiler/ccgtypes.nim | 44 +++++++++++++++++++++++++--------------- compiler/condsyms.nim | 1 + compiler/pragmas.nim | 12 +++++++++-- compiler/wordrecg.nim | 4 ++-- doc/manual.txt | 47 +++++++++++++++++++++++++++++++++++++++++++ lib/nimbase.h | 4 ++-- todo.txt | 3 +-- 9 files changed, 94 insertions(+), 25 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 61b8ca795..93630979b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -488,6 +488,8 @@ const routineKinds* = {skProc, skMethod, skIterator, skConverter, skMacro, skTemplate} tfIncompleteStruct* = tfVarargs + tfUncheckedArray* = tfVarargs + tfUnion* = tfNoSideEffect skError* = skUnknown # type flags that are essential for type equality: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 8cb423688..40ebcbfa8 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -736,7 +736,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) = var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs) var first = intLiteral(firstOrd(ty)) # emit range check: - if (optBoundsCheck in p.options): + if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags: if not isConstExpr(e.sons[1]): # semantic pass has already checked for const index expressions if firstOrd(ty) == 0: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 673e888fe..156ebee5e 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -376,10 +376,13 @@ proc getTypePre(m: BModule, typ: PType): PRope = else: result = getSimpleTypeDesc(m, typ) if result == nil: result = cacheGetType(m.typeCache, typ) - + +proc structOrUnion(t: PType): PRope = + (if tfUnion in t.flags: toRope("union") else: toRope("struct")) + proc getForwardStructFormat(): string = - if gCmd == cmdCompileToCpp: result = "struct $1;$n" - else: result = "typedef struct $1 $1;$n" + if gCmd == cmdCompileToCpp: result = "$1 $2;$n" + else: result = "typedef $1 $2 $2;$n" proc getTypeForward(m: BModule, typ: PType): PRope = result = cacheGetType(m.forwTypeCache, typ) @@ -390,7 +393,8 @@ proc getTypeForward(m: BModule, typ: PType): PRope = of tySequence, tyTuple, tyObject: result = getTypeName(typ) if not isImportedType(typ): - appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) + appf(m.s[cfsForwardTypes], getForwardStructFormat(), + [structOrUnion(typ), result]) idTablePut(m.forwTypeCache, typ, result) else: internalError("getTypeForward(" & $typ.kind & ')') @@ -445,7 +449,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname]) else: ae = sname fillLoc(field.loc, locField, field.typ, ae, OnUnknown) - appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname]) + let fieldType = field.loc.t + if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: + appf(result, "$1 $2[SEQ_DECL_SIZE];$n", + [getTypeDescAux(m, fieldType.elemType, check), sname]) + else: + appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname]) else: internalError(n.info, "genRecordFieldsAux()") proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope = @@ -455,23 +464,24 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope, check: var TIntSet): PRope = # declare the record: var hasField = false + let aStruct = structOrUnion(typ) if typ.kind == tyObject: if typ.sons[0] == nil: if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags: - result = ropecg(m, "struct $1 {$n", [name]) + result = ropecg(m, "$1 $2 {$n", [aStruct, name]) else: - result = ropecg(m, "struct $1 {$n#TNimType* m_type;$n", [name]) + result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name]) hasField = true elif gCmd == cmdCompileToCpp: - result = ropecg(m, "struct $1 : public $2 {$n", - [name, getTypeDescAux(m, typ.sons[0], check)]) + result = ropecg(m, "$1 $2 : public $3 {$n", + [aStruct, name, getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - result = ropecg(m, "struct $1 {$n $2 Sup;$n", - [name, getTypeDescAux(m, typ.sons[0], check)]) + result = ropecg(m, "$1 $2 {$n $3 Sup;$n", + [aStruct, name, getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - result = ropef("struct $1 {$n", [name]) + result = ropef("$1 $2 {$n", [aStruct, name]) var desc = getRecordFields(m, typ, check) if (desc == nil) and not hasField: appf(result, "char dummy;$n", []) @@ -480,8 +490,8 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope, app(result, "};" & tnl) proc getTupleDesc(m: BModule, typ: PType, name: PRope, - check: var TIntSet): PRope = - result = ropef("struct $1 {$n", [name]) + check: var TIntSet): PRope = + result = ropef("$1 $2 {$n", [structOrUnion(typ), name]) var desc: PRope = nil for i in countup(0, sonsLen(typ) - 1): appf(desc, "$1 Field$2;$n", @@ -557,7 +567,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = if result == nil: result = getTypeName(t) if not isImportedType(t): - appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) + appf(m.s[cfsForwardTypes], getForwardStructFormat(), + [structOrUnion(t), result]) idTablePut(m.forwTypeCache, t, result) assert(cacheGetType(m.typeCache, t) == nil) idTablePut(m.typeCache, t, con(result, "*")) @@ -588,7 +599,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope = if result == nil: result = getTypeName(t) if not isImportedType(t): - appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result]) + appf(m.s[cfsForwardTypes], getForwardStructFormat(), + [structOrUnion(t), result]) idTablePut(m.forwTypeCache, t, result) idTablePut(m.typeCache, t, result) # always call for sideeffects: if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index c79fda13e..ddf2075b4 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -47,6 +47,7 @@ proc initDefines*() = defineSymbol("nimeffects") defineSymbol("nimbabel") defineSymbol("nimcomputedgoto") + defineSymbol("nimunion") # add platform specific symbols: case targetCPU diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 4304e5fb7..bf3564016 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -50,9 +50,9 @@ const wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wRaises, wTags} typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, - wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, + wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, - wInheritable, wGensym, wInject, wRequiresInit} + wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportCpp, wImportObjC, wError} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -718,6 +718,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, noVal(it) if sym.typ == nil: invalidPragma(it) else: incl(sym.typ.flags, tfIncompleteStruct) + of wUnchecked: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfUncheckedArray) + of wUnion: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfUnion) of wRequiresInit: noVal(it) if sym.typ == nil: invalidPragma(it) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 1c8e4516c..f4a0c59f4 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -63,7 +63,7 @@ type wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt, wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, wAsmNoStackFrame, - wImplicitStatic, wGlobal, wCodegenDecl, + wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wAuto, wBool, wCatch, wChar, wClass, wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, @@ -145,7 +145,7 @@ const "subschar", "acyclic", "shallow", "unroll", "linearscanend", "computedgoto", "injectstmt", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", - "asmnostackframe", "implicitstatic", "global", "codegendecl", + "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", "auto", "bool", "catch", "char", "class", "const_cast", "default", "delete", "double", diff --git a/doc/manual.txt b/doc/manual.txt index f3602dc58..98219360e 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -5320,6 +5320,53 @@ strings automatically: printf("hallo %s", "world") # "world" will be passed as C string +Union pragma +------------ +The `union`:idx: pragma can be applied to any ``object`` type. It means all +of the object's fields are overlaid in memory. This produces a ``union`` +instead of a ``struct`` in the generated C/C++ code. The object declaration +then must not use inheritance or any GC'ed memory but this is currently not +checked. + +**Future directions**: GC'ed memory should be allowed in unions and the GC +should scan unions conservatively. + + +Unchecked pragma +---------------- +The `unchecked`:idx: pragma can be used to mark a named array as ``unchecked`` +meaning its bounds are not checked. This is often useful when one wishes to +implement his own flexibly sized arrays. Additionally an unchecked array is +translated into a C array of undetermined size: + +.. code-block:: nimrod + type + ArrayPart{.unchecked.} = array[0..0, int] + MySeq = object + len, cap: int + data: ArrayPart + +Produces roughly this C code: + +.. code-block:: C + typedef struct { + NI len; + NI cap; + NI data[]; + } MySeq; + +The bounds checking done at compile time is not disabled for now, so to access +``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to +include ``C``. + +The base type of the unchecked array may not contain any GC'ed memory but this +is currently not checked. + +**Future directions**: GC'ed memory should be allowed in unchecked arrays and +there should be an explicit annotation of how the GC is to determine the +runtime size of the array. + + Dynlib pragma for import ------------------------ With the `dynlib`:idx: pragma a procedure or a variable can be imported from diff --git a/lib/nimbase.h b/lib/nimbase.h index 19d161adf..f73dca190 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) { typedef struct TStringDesc* string; -/* declared size of a sequence: */ -#if defined(__GNUC__) +/* declared size of a sequence/variable length array: */ +#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) # define SEQ_DECL_SIZE /* empty is correct! */ #else # define SEQ_DECL_SIZE 1000000 diff --git a/todo.txt b/todo.txt index a13c86b8f..a67ff5172 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ version 0.9.4 ============= -- implement unchecked arrays - make testament produce full JSON information - fix gensym capture bug - vm @@ -28,7 +27,7 @@ version 0.9.x ============= - memory manager: add a measure of fragmentation -- implement 'union' and 'bits' pragmas +- implement 'bits' pragmas - fix closures/lambdalifting - ensure (ref T)(a, b) works as a type conversion and type constructor - optimize 'genericReset'; 'newException' leads to code bloat From c5db5875f7b601fc2c625d249f43ad5bad21ddd3 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Mar 2014 21:30:02 +0100 Subject: [PATCH 127/249] system.nim makes use of the 'unchecked' pragma --- lib/system.nim | 5 ++++- web/news.txt | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 869658727..5e27bd83a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -185,6 +185,8 @@ proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} = when not defined(niminheritable): {.pragma: inheritable.} +when not defined(nimunion): + {.pragma: unchecked.} const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \ ## "fake variables" like 'var EBADF {.importc.}: cint'. @@ -194,9 +196,10 @@ when not defined(JS): TGenericSeq {.compilerproc, pure, inheritable.} = object len, reserved: int PGenericSeq {.exportc.} = ptr TGenericSeq + UncheckedCharArray {.unchecked.} = array[0..100_000_000, char] # len and space without counting the terminating zero: NimStringDesc {.compilerproc, final.} = object of TGenericSeq - data: array[0..100_000_000, char] + data: UncheckedCharArray NimString = ptr NimStringDesc when not defined(JS) and not defined(NimrodVM): diff --git a/web/news.txt b/web/news.txt index f9ac1b66d..0001cece7 100644 --- a/web/news.txt +++ b/web/news.txt @@ -60,6 +60,9 @@ News virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro evaluation. - ``--gc:none`` produces warnings when code uses the GC. + - A ``union`` pragma for better C interoperability is now supported. + - Arrays can be annotated to be ``unchecked`` for easier low level + manipulations of memory. Language Additions From 6a83a5e468c971a8e5018ae68232291e80ebb5e9 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Thu, 6 Mar 2014 01:12:18 +0100 Subject: [PATCH 128/249] Add type-specific allocation procs. --- lib/system.nim | 57 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 2acb989c5..304c22f50 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1161,6 +1161,14 @@ when not defined(nimrodVM): ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! ## Use `allocShared` to allocate from a shared heap. + proc alloc*(T: typedesc, size = 1): ptr T {.inline.} = + ## allocates a new memory block with at least ``T.sizeof * size`` + ## bytes. The block has to be freed with ``realloc(block, 0)`` or + ## ``dealloc(block)``. The block is not initialized, so reading + ## from it before writing to it is undefined behaviour! + ## The allocated memory belongs to its allocating thread! + ## Use `allocShared` to allocate from a shared heap. + cast[ptr T](alloc(T.sizeof * size)) proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].} ## allocates a new memory block with at least ``size`` bytes. The ## block has to be freed with ``realloc(block, 0)`` or @@ -1168,14 +1176,31 @@ when not defined(nimrodVM): ## containing zero, so it is somewhat safer than ``alloc``. ## The allocated memory belongs to its allocating thread! ## Use `allocShared0` to allocate from a shared heap. - proc realloc*(p: pointer, newsize: int): pointer {.noconv, rtl, tags: [].} + proc alloc0*(T: typedesc, size = 1): ptr T {.inline.} = + ## allocates a new memory block with at least ``T.sizeof * size`` + ## bytes. The block has to be freed with ``realloc(block, 0)`` or + ## ``dealloc(block)``. The block is initialized with all bytes + ## containing zero, so it is somewhat safer than ``alloc``. + ## The allocated memory belongs to its allocating thread! + ## Use `allocShared0` to allocate from a shared heap. + cast[ptr T](alloc0(T.sizeof * size)) + proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].} ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least - ## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil** + ## ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** ## ``realloc`` calls ``dealloc(p)``. In other cases the block has to ## be freed with ``dealloc``. ## The allocated memory belongs to its allocating thread! ## Use `reallocShared` to reallocate from a shared heap. + proc reallocType*[T](p: ptr T, newSize: int): ptr T {.inline.} = + ## grows or shrinks a given memory block. If p is **nil** then a new + ## memory block is returned. In either way the block has at least + ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not + ## **nil** ``realloc`` calls ``dealloc(p)``. In other cases the block + ## has to be freed with ``dealloc``. The allocated memory belongs to + ## its allocating thread! + ## Use `reallocShared` to reallocate from a shared heap. + cast[ptr T](realloc(p, T.sizeof * newSize)) proc dealloc*(p: pointer) {.noconv, rtl, tags: [].} ## frees the memory allocated with ``alloc``, ``alloc0`` or ## ``realloc``. This procedure is dangerous! If one forgets to @@ -1184,25 +1209,45 @@ when not defined(nimrodVM): ## or other memory may be corrupted. ## The freed memory must belong to its allocating thread! ## Use `deallocShared` to deallocate from a shared heap. - proc allocShared*(size: int): pointer {.noconv, rtl.} ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## is not initialized, so reading from it before writing to it is ## undefined behaviour! + proc allocShared*(T: typedesc, size: int): ptr T {.inline.} = + ## allocates a new memory block on the shared heap with at + ## least ``T.sizeof * size`` bytes. The block has to be freed with + ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block + ## is not initialized, so reading from it before writing to it is + ## undefined behaviour! + cast[ptr T](allocShared(T.sizeof * size)) proc allocShared0*(size: int): pointer {.noconv, rtl.} ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``allocShared``. - proc reallocShared*(p: pointer, newsize: int): pointer {.noconv, rtl.} + proc allocShared0*(T: typedesc, size: int): ptr T {.inline.} = + ## allocates a new memory block on the shared heap with at + ## least ``T.sizeof * size`` bytes. The block has to be freed with + ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. + ## The block is initialized with all bytes + ## containing zero, so it is somewhat safer than ``allocShared``. + cast[ptr T](allocShared(T.sizeof * size)) + proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.} ## grows or shrinks a given memory block on the heap. If p is **nil** - ## then a new memory block is returned. In either way the block has at least - ## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil** + ## then a new memory block is returned. In either way the block has at + ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** ## ``reallocShared`` calls ``deallocShared(p)``. In other cases the ## block has to be freed with ``deallocShared``. + proc reallocSharedType*[T](p: ptr T, newSize: int): ptr T {.inline.} = + ## grows or shrinks a given memory block on the heap. If p is **nil** + ## then a new memory block is returned. In either way the block has at + ## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is + ## not **nil** ``reallocShared`` calls ``deallocShared(p)``. In other + ## cases the block has to be freed with ``deallocShared``. + reallocShared(p, T.sizeof * n) proc deallocShared*(p: pointer) {.noconv, rtl.} ## frees the memory allocated with ``allocShared``, ``allocShared0`` or ## ``reallocShared``. This procedure is dangerous! If one forgets to From bbd245b47bb1d368b9bd91548ec7fec4c800462b Mon Sep 17 00:00:00 2001 From: EXetoC Date: Thu, 6 Mar 2014 02:43:05 +0100 Subject: [PATCH 129/249] Add missing cast. --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 304c22f50..e0977d104 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1247,7 +1247,7 @@ when not defined(nimrodVM): ## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is ## not **nil** ``reallocShared`` calls ``deallocShared(p)``. In other ## cases the block has to be freed with ``deallocShared``. - reallocShared(p, T.sizeof * n) + cast[ptr T](reallocShared(p, T.sizeof * newSize)) proc deallocShared*(p: pointer) {.noconv, rtl.} ## frees the memory allocated with ``allocShared``, ``allocShared0`` or ## ``reallocShared``. This procedure is dangerous! If one forgets to From 853bdbf4948db7774bc3caf74f67ba1228f65016 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Thu, 6 Mar 2014 02:45:55 +0100 Subject: [PATCH 130/249] Add allocation unit tests. --- tests/system/alloc.nim | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/system/alloc.nim diff --git a/tests/system/alloc.nim b/tests/system/alloc.nim new file mode 100644 index 000000000..665b448ac --- /dev/null +++ b/tests/system/alloc.nim @@ -0,0 +1,45 @@ +var x: ptr int + +x = cast[ptr int](alloc(7)) +assert x != nil + +x = alloc(int, 3) +assert x != nil +x.dealloc() + +x = alloc0(int, 4) +assert cast[ptr array[4, int]](x)[0] == 0 +assert cast[ptr array[4, int]](x)[1] == 0 +assert cast[ptr array[4, int]](x)[2] == 0 +assert cast[ptr array[4, int]](x)[3] == 0 + +x = cast[ptr int](x.realloc(2)) +assert x != nil + +x = x.reallocType(4) +assert x != nil +x.dealloc() + +x = cast[ptr int](allocShared(100)) +assert x != nil +deallocShared(x) + +x = allocShared(int, 3) +assert x != nil +x.deallocShared() + +x = allocShared0(int, 3) +assert x != nil +assert cast[ptr array[3, int]](x)[0] == 0 +assert cast[ptr array[3, int]](x)[1] == 0 +assert cast[ptr array[3, int]](x)[2] == 0 + +x = cast[ptr int](reallocShared(x, 2)) +assert x != nil + +x = reallocType(x, 12) +assert x != nil + +x = reallocSharedType(x, 1) +assert x != nil +x.deallocShared() From 862c0ef83d7a85798df0474522dd4ba8cfcab674 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 6 Mar 2014 21:57:35 +0200 Subject: [PATCH 131/249] split the inline and closure iterators into different symbol kinds for easier discrimination between them --- compiler/ast.nim | 22 +++++++++++-------- compiler/ccgexprs.nim | 2 +- compiler/ccgstmts.nim | 2 +- compiler/ccgtypes.nim | 2 +- compiler/cgen.nim | 2 +- compiler/docgen.nim | 2 +- compiler/importer.nim | 2 +- compiler/jsgen.nim | 2 +- compiler/lambdalifting.nim | 15 ++++++------- compiler/lookups.nim | 2 +- compiler/pragmas.nim | 11 ++++++++++ compiler/semcall.nim | 7 +++--- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 24 +++++++++++---------- compiler/semgnrc.nim | 4 ++-- compiler/semstmts.nim | 22 ++++++++++--------- compiler/semtempl.nim | 4 ++-- compiler/semtypes.nim | 44 ++++++++++++++++++++++---------------- compiler/sigmatch.nim | 2 +- compiler/transf.nim | 7 +++--- compiler/types.nim | 2 +- compiler/vmgen.nim | 2 +- doc/idetools.txt | 4 ++-- lib/core/macros.nim | 2 +- tests/exprs/texprstmt.nim | 2 +- tests/iter/tanoniter1.nim | 2 +- 26 files changed, 110 insertions(+), 84 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 61b8ca795..eef12aebc 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -469,7 +469,8 @@ type skResult, # special 'result' variable skProc, # a proc skMethod, # a method - skIterator, # an iterator + skIterator, # an inline iterator + skClosureIterator, # a resumable closure iterator skConverter, # a type converter skMacro, # a macro skTemplate, # a template; currently also misused for user-defined @@ -485,8 +486,8 @@ type TSymKinds* = set[TSymKind] const - routineKinds* = {skProc, skMethod, skIterator, skConverter, - skMacro, skTemplate} + routineKinds* = {skProc, skMethod, skIterator, skClosureIterator, + skConverter, skMacro, skTemplate} tfIncompleteStruct* = tfVarargs skError* = skUnknown @@ -820,8 +821,8 @@ type # the poor naming choices in the standard library. const - OverloadableSyms* = {skProc, skMethod, skIterator, skConverter, - skModule, skTemplate, skMacro} + OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator, + skConverter, skModule, skTemplate, skMacro} GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody, tyGenericParam} @@ -843,7 +844,8 @@ const tyTuple, tySequence} NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence, tyProc, tyString, tyError} - ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, + ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, + skIterator, skClosureIterator, skMacro, skTemplate, skConverter, skEnumField, skLet, skStub} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, @@ -869,7 +871,10 @@ const nkStrKinds* = {nkStrLit..nkTripleStrLit} skLocalVars* = {skVar, skLet, skForVar, skParam, skResult} - skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter} + skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skClosureIterator, + skMethod, skConverter} + + skIterators* = {skIterator, skClosureIterator} lfFullExternalName* = lfParamCopy # \ # only used when 'gCmd == cmdPretty': Indicates that the symbol has been @@ -1479,8 +1484,7 @@ proc originatingModule*(s: PSym): PSym = while result.kind != skModule: result = result.owner proc isRoutine*(s: PSym): bool {.inline.} = - result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod, - skConverter} + result = s.kind in skProcKinds proc hasPattern*(s: PSym): bool {.inline.} = result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 8cb423688..2f88ecab5 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1803,7 +1803,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: genProc(p.module, sym) putLocIntoDest(p, d, sym.loc) - of skProc, skConverter, skIterator: + of skProc, skConverter, skIterators: genProc(p.module, sym) if sym.loc.r == nil or sym.loc.t == nil: internalError(n.info, "expr: proc not init " & sym.name.s) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 4576a54b5..990153fc7 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -863,7 +863,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope = res.add(t.sons[i].strVal) of nkSym: var sym = t.sons[i].sym - if sym.kind in {skProc, skIterator, skMethod}: + if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}: var a: TLoc initLocExpr(p, t.sons[i], a) res.add(rdLoc(a).ropeToStr) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 673e888fe..c856c6df0 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -69,7 +69,7 @@ proc mangleName(s: PSym): PRope = if result == nil: if gCmd == cmdCompileToLLVM: case s.kind - of skProc, skMethod, skConverter, skConst, skIterator: + of skProc, skMethod, skConverter, skConst, skIterators: result = ~"@" of skVar, skForVar, skResult, skLet: if sfGlobal in s.flags: result = ~"@" diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e8739d20e..683aed069 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -705,7 +705,7 @@ proc cgsym(m: BModule, name: string): PRope = var sym = magicsys.getCompilerProc(name) if sym != nil: case sym.kind - of skProc, skMethod, skConverter, skIterator: genProc(m, sym) + of skProc, skMethod, skConverter, skIterators: genProc(m, sym) of skVar, skResult, skLet: genVarPrototype(m, sym) of skType: discard getTypeDesc(m, sym.typ) else: internalError("cgsym: " & name) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index c05b55184..d8c439c9c 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -366,7 +366,7 @@ proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode = proc genSection(d: PDoc, kind: TSymKind) = const sectionNames: array[skModule..skTemplate, string] = [ "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods", - "Iterators", "Converters", "Macros", "Templates" + "Iterators", "Iterators", "Converters", "Macros", "Templates" ] if d.section[kind] == nil: return var title = sectionNames[kind].toRope diff --git a/compiler/importer.nim b/compiler/importer.nim index 078a90c98..7a73f2bbf 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -103,7 +103,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = internalError(n.info, "importSymbol: 2") # for an enumeration we have to add all identifiers case s.kind - of skProc, skMethod, skIterator, skMacro, skTemplate, skConverter: + of skProcKinds: # for a overloadable syms add all overloaded routines var it: TIdentIter var e = initIdentIter(it, fromMod.tab, s.name) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 6e0bd5e44..0cbd4c364 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1154,7 +1154,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope = proc isIndirect(v: PSym): bool = result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and - v.kind notin {skProc, skConverter, skMethod, skIterator} + v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator} proc genVarInit(p: PProc, v: PSym, n: PNode) = var diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 3738f89b2..67cd364dc 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -232,9 +232,9 @@ proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext = initIdNodeTable(result.localsToAccess) initIdTable(result.localsToEnv) initIdTable(result.lambdasToEnv) - result.isIter = fn.kind == skIterator and fn.typ.callConv == ccClosure + result.isIter = fn.kind == skClosureIterator if result.isIter: initIterContext(result, fn) - + proc newInnerContext(fn: PSym): PInnerContext = new(result) result.fn = fn @@ -292,8 +292,7 @@ proc newCall(a, b: PSym): PNode = result.add newSymNode(b) proc isInnerProc(s, outerProc: PSym): bool {.inline.} = - result = (s.kind in {skProc, skMethod, skConverter} or - s.kind == skIterator and s.typ.callConv == ccClosure) and + result = s.kind in {skProc, skMethod, skConverter, skClosureIterator} and s.skipGenericOwner == outerProc #s.typ.callConv == ccClosure @@ -653,7 +652,7 @@ proc outerProcSons(o: POuterContext, n: PNode) = proc liftIterSym*(n: PNode): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) let iter = n.sym - assert iter.kind == skIterator + assert iter.kind == skClosureIterator result = newNodeIT(nkStmtListExpr, n.info, n.typ) @@ -679,7 +678,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = var closure = PEnv(idTableGet(o.lambdasToEnv, local)) - if local.kind == skIterator and local.typ.callConv == ccClosure: + if local.kind == skClosureIterator: # consider: [i1, i2, i1] Since we merged the iterator's closure # with the captured owning variables, we need to generate the # closure generation code again: @@ -843,10 +842,10 @@ proc liftForLoop*(body: PNode): PNode = # static binding? var env: PSym - if call[0].kind == nkSym and call[0].sym.kind == skIterator: + if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator: # createClosure() let iter = call[0].sym - assert iter.kind == skIterator + assert iter.kind == skClosureIterator env = copySym(getHiddenParam(iter)) var v = newNodeI(nkVarSection, body.info) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 93a7b7c72..18b47a7bb 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -109,7 +109,7 @@ type proc getSymRepr*(s: PSym): string = case s.kind - of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s) + of skProc, skMethod, skConverter, skIterators: result = getProcHeader(s) else: result = s.name.s proc ensureNoMissingOrUnusedSymbols(scope: PScope) = diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 4304e5fb7..e0123c658 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -772,6 +772,17 @@ proc implictPragmas*(c: PContext, sym: PSym, n: PNode, addToLib(lib, sym) if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s) +proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = + if n == nil or n.sons == nil: + return false + + for p in n.sons: + var key = if p.kind == nkExprColonExpr: p[0] else: p + if key.kind == nkIdent and whichKeyword(key.ident) == pragma: + return true + + return false + proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if n == nil: return for i in countup(0, sonsLen(n) - 1): diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0cd27a443..2e5def75a 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -64,7 +64,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, errors[errors.len - 1].add("\n " & err) if z.state == csMatch: # little hack so that iterators are preferred over everything else: - if sym.kind == skIterator: inc(z.exactMatches, 200) + if sym.kind in skIterators: inc(z.exactMatches, 200) case best.state of csEmpty, csNoMatch: best = z of csMatch: @@ -289,8 +289,9 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = newNodeI(a.kind, n.info) for i in countup(0, len(a)-1): var candidate = a.sons[i].sym - if candidate.kind in {skProc, skMethod, skConverter, skIterator}: - # if suffices that the candidate has the proper number of generic + if candidate.kind in {skProc, skMethod, skConverter, + skIterator, skClosureIterator}: + # it suffices that the candidate has the proper number of generic # type parameters: if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1: result.add(explicitGenericSym(c, n, candidate)) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index df58c896f..d942aa41e 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -274,7 +274,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt; addSonSkipIntLit(result, intType) # basetype of range proc markIndirect*(c: PContext, s: PSym) {.inline.} = - if s.kind in {skProc, skConverter, skMethod, skIterator}: + if s.kind in {skProc, skConverter, skMethod, skIterator, skClosureIterator}: incl(s.flags, sfAddrTaken) # XXX add to 'c' for global analysis diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 34d58118d..ebdd79a8c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -662,11 +662,11 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = if flags*{efInTypeof, efWantIterator} != {}: # consider: 'for x in pReturningArray()' --> we don't want the restriction - # to 'skIterator' anymore; skIterator is preferred in sigmatch already for - # typeof support. + # to 'skIterators' anymore; skIterators are preferred in sigmatch already + # for typeof support. # for ``type(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, - {skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}) + {skProc, skMethod, skConverter, skMacro, skTemplate}+skIterators) else: result = semOverloadedCall(c, n, nOrig, {skProc, skMethod, skConverter, skMacro, skTemplate}) @@ -679,7 +679,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, case callee.kind of skMacro, skTemplate: discard else: - if (callee.kind == skIterator) and (callee.id == c.p.owner.id): + if (callee.kind in skIterators) and (callee.id == c.p.owner.id): localError(n.info, errRecursiveDependencyX, callee.name.s) if sfNoSideEffect notin callee.flags: if {sfImportc, sfSideEffect} * callee.flags != {}: @@ -1193,7 +1193,7 @@ proc semReturn(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or - (c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure): + c.p.owner.kind == skClosureIterator: if n.sons[0].kind != nkEmpty: # transform ``return expr`` to ``result = expr; return`` if c.p.resultSym != nil: @@ -1258,7 +1258,7 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = proc semYield(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) - if c.p.owner == nil or c.p.owner.kind != skIterator: + if c.p.owner == nil or c.p.owner.kind notin skIterators: localError(n.info, errYieldNotAllowedHere) elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: localError(n.info, errYieldNotAllowedInTryStmt) @@ -1266,9 +1266,11 @@ proc semYield(c: PContext, n: PNode): PNode = n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: var restype = c.p.owner.typ.sons[0] if restype != nil: - n.sons[0] = fitNode(c, restype.base, n.sons[0]) + let adjustedRes = if c.p.owner.kind == skIterator: restype.base + else: restype + n.sons[0] = fitNode(c, adjustedRes, n.sons[0]) if n.sons[0].typ == nil: internalError(n.info, "semYield") - semYieldVarResult(c, n, restype) + semYieldVarResult(c, n, adjustedRes) else: localError(n.info, errCannotReturnExpr) elif c.p.owner.typ.sons[0] != nil: @@ -1828,7 +1830,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = var s = lookUp(c, n) semCaptureSym(s, c.p.owner) result = semSym(c, n, s, flags) - if s.kind in {skProc, skMethod, skIterator, skConverter}: + if s.kind in {skProc, skMethod, skConverter}+skIterators: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) if result.kind == nkSym: @@ -1918,7 +1920,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = localError(n.info, errUseQualifier, s.name.s) elif s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) - of skProc, skMethod, skConverter, skIterator: + of skProc, skMethod, skConverter, skIterators: if s.magic == mNone: result = semDirectOp(c, n, flags) else: result = semMagic(c, n, s, flags) else: @@ -1942,7 +1944,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkBracketExpr: checkMinSonsLen(n, 1) var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared}) - if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}: + if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators: # type parameters: partial generic specialization n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) result = explicitGenericInstantiation(c, n, s) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index b21d851c9..ffc1a43b2 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -42,7 +42,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode = of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of skProc, skMethod, skIterator, skConverter: + of skProc, skMethod, skIterators, skConverter: result = symChoice(c, n, s, scOpen) of skTemplate: if macroToExpand(s): @@ -141,7 +141,7 @@ proc semGenericStmt(c: PContext, n: PNode, # symbol lookup ... of skUnknown, skParam: # Leave it as an identifier. - of skProc, skMethod, skIterator, skConverter: + of skProc, skMethod, skIterators, skConverter: result.sons[0] = symChoice(c, n.sons[0], s, scOption) first = 1 of skGenericParam: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b11f45b38..83450aa07 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -76,8 +76,8 @@ proc performProcvarCheck(c: PContext, n: PNode, s: PSym) = proc semProcvarCheck(c: PContext, n: PNode) = let n = n.skipConv - if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator, - skConverter}: + if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter, + skIterator, skClosureIterator}: performProcvarCheck(c, n, n.sym) proc semProc(c: PContext, n: PNode): PNode @@ -665,7 +665,7 @@ proc semFor(c: PContext, n: PNode): PNode = # first class iterator: result = semForVars(c, n) elif call.kind notin nkCallKinds or call.sons[0].kind != nkSym or - call.sons[0].sym.kind != skIterator: + call.sons[0].sym.kind notin skIterators: if length == 3: n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2]) elif length == 4: @@ -998,8 +998,7 @@ proc activate(c: PContext, n: PNode) = discard proc maybeAddResult(c: PContext, s: PSym, n: PNode) = - if s.typ.sons[0] != nil and - (s.kind != skIterator or s.typ.callConv == ccClosure): + if s.typ.sons[0] != nil and s.kind != skIterator: addResult(c, s.typ.sons[0], n.info, s.kind) addResultNode(c, n) @@ -1074,12 +1073,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, rawAddSon(s.typ, nil) if n.sons[patternPos].kind != nkEmpty: n.sons[patternPos] = semPattern(c, n.sons[patternPos]) - if s.kind == skIterator: + if s.kind in skIterators: s.typ.flags.incl(tfIterator) var proto = searchForProc(c, s.scope, s) - if proto == nil: - if s.kind == skIterator and isAnon: s.typ.callConv = ccClosure + if proto == nil: + if s.kind == skClosureIterator: s.typ.callConv = ccClosure else: s.typ.callConv = lastOptionEntry(c).defaultCC # add it here, so that recursive procs are possible: if sfGenSym in s.flags: discard @@ -1139,7 +1138,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[bodyPos] = transformBody(c.module, semBody, s) popProcCon(c) else: - if s.typ.sons[0] != nil and kind != skIterator: + if s.typ.sons[0] != nil and kind notin skIterators: addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) var toBind = initIntSet() n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind) @@ -1166,7 +1165,10 @@ proc determineType(c: PContext, s: PSym) = discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType) proc semIterator(c: PContext, n: PNode): PNode = - result = semProcAux(c, n, skIterator, iteratorPragmas) + let kind = if hasPragma(n[pragmasPos], wClosure) or + n[namePos].kind == nkEmpty: skClosureIterator + else: skIterator + result = semProcAux(c, n, kind, iteratorPragmas) var s = result.sons[namePos].sym var t = s.typ if t.sons[0] == nil and s.typ.callConv != ccClosure: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 5abc3ef33..31624a97f 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -171,7 +171,7 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of skProc, skMethod, skIterator, skConverter, skTemplate, skMacro: + of OverloadableSyms: result = symChoice(c, n, s, scOpen) of skGenericParam: result = newSymNodeTypeDesc(s, n.info) @@ -348,7 +348,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkMethodDef: result = semRoutineInTemplBody(c, n, skMethod) of nkIteratorDef: - result = semRoutineInTemplBody(c, n, skIterator) + result = semRoutineInTemplBody(c, n, n[namePos].sym.kind) of nkTemplateDef: result = semRoutineInTemplBody(c, n, skTemplate) of nkMacroDef: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 9c6f5114f..5291dbb10 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -953,6 +953,23 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = let typ = semTypeNode(c, n, nil) result.sons.safeAdd(typ) +proc semProcTypeWithScope(c: PContext, n: PNode, + prev: PType, kind: TSymKind): PType = + checkSonsLen(n, 2) + openScope(c) + result = semProcTypeNode(c, n.sons[0], nil, prev, kind) + # dummy symbol for `pragma`: + var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c) + s.typ = result + if n.sons[1].kind == nkEmpty or n.sons[1].len == 0: + if result.callConv == ccDefault: + result.callConv = ccClosure + #Message(n.info, warnImplicitClosure, renderTree(n)) + else: + pragma(c, s, n.sons[1], procTypePragmas) + when useEffectSystem: setEffectsForProcType(result, n.sons[1]) + closeScope(c) + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil if gCmd == cmdIdeTools: suggestExpr(c, n) @@ -1075,27 +1092,18 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = var base = semTypeNode(c, n.sons[0], nil) result.rawAddSon(base) result.flags.incl tfHasStatic - of nkProcTy, nkIteratorTy: + of nkIteratorTy: + if n.sonsLen == 0: + result = newConstraint(c, tyIter) + else: + result = semProcTypeWithScope(c, n, prev, skClosureIterator) + result.flags.incl(tfIterator) + result.callConv = ccClosure + of nkProcTy: if n.sonsLen == 0: result = newConstraint(c, tyProc) else: - checkSonsLen(n, 2) - openScope(c) - result = semProcTypeNode(c, n.sons[0], nil, prev, skProc) - # dummy symbol for `pragma`: - var s = newSymS(skProc, newIdentNode(getIdent("dummy"), n.info), c) - s.typ = result - if n.sons[1].kind == nkEmpty or n.sons[1].len == 0: - if result.callConv == ccDefault: - result.callConv = ccClosure - #Message(n.info, warnImplicitClosure, renderTree(n)) - else: - pragma(c, s, n.sons[1], procTypePragmas) - when useEffectSystem: setEffectsForProcType(result, n.sons[1]) - closeScope(c) - if n.kind == nkIteratorTy: - result.flags.incl(tfIterator) - result.callConv = ccClosure + result = semProcTypeWithScope(c, n, prev, skProc) of nkEnumTy: result = semEnum(c, n, prev) of nkType: result = n.typ of nkStmtListType: result = semStmtListType(c, n, prev) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ce6120f08..6a3c9267e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1142,7 +1142,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, z.calleeSym = m.calleeSym var best = -1 for i in countup(0, sonsLen(arg) - 1): - if arg.sons[i].sym.kind in {skProc, skIterator, skMethod, skConverter}: + if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators: copyCandidate(z, m) var r = typeRel(z, f, arg.sons[i].typ) if r != isNone: diff --git a/compiler/transf.nim b/compiler/transf.nim index 0911950d0..f4b716c5b 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -113,7 +113,7 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode = result[1] = ri proc transformSymAux(c: PTransf, n: PNode): PNode = - #if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure: + #if n.sym.kind == skClosureIterator: # return liftIterSym(n) var b: PNode var tc = c.transCon @@ -434,7 +434,6 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = var length = sonsLen(n) var call = n.sons[length - 2] if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or - call.sons[0].typ.callConv == ccClosure or call.sons[0].sym.kind != skIterator: n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode return lambdalifting.liftForLoop(n).PTransNode @@ -454,7 +453,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode = var newC = newTransCon(getCurrOwner(c)) newC.forStmt = n newC.forLoopBody = loopBody - if iter.kind != skIterator: internalError(call.info, "transformFor") + internalAssert iter.kind == skIterator # generate access statements for the parameters (unless they are constant) pushTransCon(c, newC) for i in countup(1, sonsLen(call) - 1): @@ -741,7 +740,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = var c = openTransf(module, "") result = processTransf(c, n, prc) result = liftLambdas(prc, result) - #if prc.kind == skIterator and prc.typ.callConv == ccClosure: + #if prc.kind == skClosureIterator: # result = lambdalifting.liftIterator(prc, result) incl(result.flags, nfTransf) when useEffectSystem: trackProc(prc, result) diff --git a/compiler/types.nim b/compiler/types.nim index edf5ab47b..8cf91da8b 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1247,7 +1247,7 @@ proc computeSize(typ: PType): BiggestInt = proc getReturnType*(s: PSym): PType = # Obtains the return type of a iterator/proc/macro/template - assert s.kind in {skProc, skTemplate, skMacro, skIterator} + assert s.kind in skProcKinds result = s.typ.sons[0] proc getSize(typ: PType): BiggestInt = diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 591c5ade8..b4d73c0aa 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1311,7 +1311,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = case s.kind of skVar, skForVar, skTemp, skLet, skParam, skResult: genRdVar(c, n, dest, flags) - of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator: + of skProc, skConverter, skMacro, skTemplate, skMethod, skIterators: # 'skTemplate' is only allowed for 'getAst' support: if sfImportc in s.flags: c.importcSym(n.info, s) genLit(c, n, dest) diff --git a/doc/idetools.txt b/doc/idetools.txt index c1eba9e5f..d4f0f077d 100644 --- a/doc/idetools.txt +++ b/doc/idetools.txt @@ -279,8 +279,8 @@ skForVar col 7: "" -skIterator ----------- +skIterator, skClosureIterator +----------------------------- The fourth column will be the empty string if the iterator is being defined, since at that point in the file the parser hasn't processed diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d14822974..db2bbc5a4 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -88,7 +88,7 @@ type nskUnknown, nskConditional, nskDynLib, nskParam, nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet, nskConst, nskResult, - nskProc, nskMethod, nskIterator, + nskProc, nskMethod, nskIterator, nskClosureIterator, nskConverter, nskMacro, nskTemplate, nskField, nskEnumField, nskForVar, nskLabel, nskStub diff --git a/tests/exprs/texprstmt.nim b/tests/exprs/texprstmt.nim index b32394d8d..355da2407 100644 --- a/tests/exprs/texprstmt.nim +++ b/tests/exprs/texprstmt.nim @@ -1,6 +1,6 @@ discard """ line: 10 - errormsg: "value returned by statement has to be discarded" + errormsg: "value of type 'string' has to be discarded" """ # bug #578 diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim index 578749caf..9f0d0a74b 100644 --- a/tests/iter/tanoniter1.nim +++ b/tests/iter/tanoniter1.nim @@ -8,7 +8,7 @@ discard """ """ proc factory(a, b: int): iterator (): int = - iterator foo(): int = + iterator foo(): int {.closure.} = var x = a while x <= b: yield x From 249dd7027302c913513cf577beed83512236fc57 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 6 Mar 2014 23:03:02 +0200 Subject: [PATCH 132/249] test cases for the new handling of iterators by the `is` operator --- compiler/semexprs.nim | 5 ----- compiler/sigmatch.nim | 12 +++++++++--- tests/types/tisopr.nim | 28 +++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ebdd79a8c..203a51816 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -323,11 +323,6 @@ proc isOpImpl(c: PContext, n: PNode): PNode = result = newIntNode(nkIntLit, ord(t.kind == tyProc and t.callConv == ccClosure and tfIterator notin t.flags)) - of "iterator": - let t = skipTypes(t1, abstractRange) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator in t.flags)) else: var t2 = n[2].typ.skipTypes({tyTypeDesc}) let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info), diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6a3c9267e..c0898ef26 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -410,7 +410,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = return isNone when useEffectSystem: if not compatibleEffects(f, a): return isNone - of tyNil: result = f.allowsNil + of tyNil: + result = f.allowsNil + of tyIter: + if tfIterator in f.flags: result = typeRel(c, f.base, a.base) else: discard proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = @@ -923,8 +926,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isNone of tyIter: - if a.kind == f.kind: result = typeRel(c, f.base, a.base) - else: result = isNone + if a.kind == tyIter or + (a.kind == tyProc and tfIterator in a.flags): + result = typeRel(c, f.base, a.base) + else: + result = isNone of tyStmt: result = isGeneric diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 6d3c51749..f173b93f4 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -1,5 +1,8 @@ discard """ - output: "true true false yes" + output: '''true +true +false +yes''' """ proc IsVoid[T](): string = @@ -11,3 +14,26 @@ proc IsVoid[T](): string = const x = int is int echo x, " ", float is float, " ", float is string, " ", IsVoid[void]() +template yes(e: expr): stmt = + static: assert e + +template no(e: expr): stmt = + static: assert(not e) + +var s = @[1, 2, 3] + +yes s.items is iterator +no s.items is proc + +yes s.items is iterator: int +no s.items is iterator: float + +yes s.items is iterator: TNumber +no s.items is iterator: object + +type + Iter[T] = iterator: T + +yes s.items is Iter[TNumber] +no s.items is Iter[float] + From a88a6358478165aae7681ac651220ce03d97bb30 Mon Sep 17 00:00:00 2001 From: ReneSac Date: Thu, 6 Mar 2014 18:18:25 -0300 Subject: [PATCH 133/249] Added comment explaining 'assert_numbits' error. --- lib/nimbase.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/nimbase.h b/lib/nimbase.h index f73dca190..1100e084b 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -373,5 +373,8 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } # define GC_GUARD #endif +/* Test to see if nimrod and the C compiler agrees on the size of a pointer. + On disagreement, your C compiler will say something like: + "error: 'assert_numbits' declared as an array with a negative size" */ typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1]; #endif From ee1b0d8c6784c30307bcddc7ab8ace0bba35e853 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 6 Mar 2014 23:20:36 +0200 Subject: [PATCH 134/249] fix #587 --- compiler/semtypes.nim | 6 +++++ doc/manual.txt | 2 +- tests/metatype/udtcmanual.nim | 43 +++++++++++++++++++++++++++++++++++ tests/types/tisopr.nim | 7 ++---- 4 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 tests/metatype/udtcmanual.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5291dbb10..a619de7ff 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -711,6 +711,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, for i in 0 .. paramType.sonsLen - 2: result.rawAddSon newTypeS(tyAnything, c) # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) + + if paramType.lastSon.kind == tyUserTypeClass: + result.kind = tyUserTypeClassInst + result.rawAddSon paramType.lastSon + return addImplicitGeneric(result) + result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) diff --git a/doc/manual.txt b/doc/manual.txt index f3602dc58..427fa2eb7 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -3441,7 +3441,7 @@ Declarative type classes are written in the following form: c.len is ordinal items(c) is iterator for value in c: - type(value) is T + value.type is T The type class will be matched if: diff --git a/tests/metatype/udtcmanual.nim b/tests/metatype/udtcmanual.nim new file mode 100644 index 000000000..f22bd6ac6 --- /dev/null +++ b/tests/metatype/udtcmanual.nim @@ -0,0 +1,43 @@ +discard """ + output: '''1 +2 +3 +4 +5 +6 +a +b +t +e +s +t +''' +""" + +template accept(e: expr) = + static: assert compiles(e) + +template reject(e: expr) = + static: assert(not compiles(e)) + +type + Container[T] = generic C + C.len is Ordinal + items(c) is iterator + for value in C: + value.type is T + +proc takesIntContainer(c: Container[int]) = + for e in c: echo e + +takesIntContainer(@[1, 2, 3]) +reject takesIntContainer(@["x", "y"]) + +proc takesContainer(c: Container) = + for e in c: echo e + +takesContainer(@[4, 5, 6]) +takesContainer(@["a", "b"]) +takesContainer "test" +reject takesContainer(10) + diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index f173b93f4..3c2b9ee5e 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -1,11 +1,8 @@ discard """ - output: '''true -true -false -yes''' + output: '''true true false yes''' """ -proc IsVoid[T](): string = +proc IsVoid[T](): string = when T is void: result = "yes" else: From 3ae21ea48e5f61ccf757bc3588c3124e2b2899b3 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Fri, 7 Mar 2014 00:09:23 +0100 Subject: [PATCH 135/249] Better names. --- lib/system.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 171c7b6b8..de918c78b 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1195,7 +1195,7 @@ when not defined(nimrodVM): ## be freed with ``dealloc``. ## The allocated memory belongs to its allocating thread! ## Use `reallocShared` to reallocate from a shared heap. - proc reallocType*[T](p: ptr T, newSize: int): ptr T {.inline.} = + proc resize*[T](p: ptr T, newSize: int): ptr T {.inline.} = ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not @@ -1244,7 +1244,7 @@ when not defined(nimrodVM): ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** ## ``reallocShared`` calls ``deallocShared(p)``. In other cases the ## block has to be freed with ``deallocShared``. - proc reallocSharedType*[T](p: ptr T, newSize: int): ptr T {.inline.} = + proc resizeShared*[T](p: ptr T, newSize: int): ptr T {.inline.} = ## grows or shrinks a given memory block on the heap. If p is **nil** ## then a new memory block is returned. In either way the block has at ## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is From 90e326504b077bc0a233642e121cd8ea3155d637 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Fri, 7 Mar 2014 00:09:43 +0100 Subject: [PATCH 136/249] Update test with new names. --- tests/system/alloc.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/system/alloc.nim b/tests/system/alloc.nim index 665b448ac..3a10a1a39 100644 --- a/tests/system/alloc.nim +++ b/tests/system/alloc.nim @@ -16,7 +16,7 @@ assert cast[ptr array[4, int]](x)[3] == 0 x = cast[ptr int](x.realloc(2)) assert x != nil -x = x.reallocType(4) +x = x.resize(4) assert x != nil x.dealloc() @@ -37,9 +37,9 @@ assert cast[ptr array[3, int]](x)[2] == 0 x = cast[ptr int](reallocShared(x, 2)) assert x != nil -x = reallocType(x, 12) +x = resize(x, 12) assert x != nil -x = reallocSharedType(x, 1) +x = resizeShared(x, 1) assert x != nil x.deallocShared() From 5e78baa061f36900b28018b6b0c47e4f45dc92cf Mon Sep 17 00:00:00 2001 From: EXetoC Date: Fri, 7 Mar 2014 00:34:44 +0100 Subject: [PATCH 137/249] Use separate names for type-based allocation procs rather than overloading. --- lib/system.nim | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index de918c78b..0ae141241 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1164,13 +1164,13 @@ when not defined(nimrodVM): ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! ## Use `allocShared` to allocate from a shared heap. - proc alloc*(T: typedesc, size = 1): ptr T {.inline.} = + proc create*(T: typedesc, size = 1): ptr T {.inline.} = ## allocates a new memory block with at least ``T.sizeof * size`` - ## bytes. The block has to be freed with ``realloc(block, 0)`` or - ## ``dealloc(block)``. The block is not initialized, so reading + ## bytes. The block has to be freed with ``resize(block, 0)`` or + ## ``free(block)``. The block is not initialized, so reading ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! - ## Use `allocShared` to allocate from a shared heap. + ## Use `createShared` to allocate from a shared heap. cast[ptr T](alloc(T.sizeof * size)) proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].} ## allocates a new memory block with at least ``size`` bytes. The @@ -1179,13 +1179,13 @@ when not defined(nimrodVM): ## containing zero, so it is somewhat safer than ``alloc``. ## The allocated memory belongs to its allocating thread! ## Use `allocShared0` to allocate from a shared heap. - proc alloc0*(T: typedesc, size = 1): ptr T {.inline.} = + proc create0*(T: typedesc, size = 1): ptr T {.inline.} = ## allocates a new memory block with at least ``T.sizeof * size`` - ## bytes. The block has to be freed with ``realloc(block, 0)`` or - ## ``dealloc(block)``. The block is initialized with all bytes - ## containing zero, so it is somewhat safer than ``alloc``. + ## bytes. The block has to be freed with ``resize(block, 0)`` or + ## ``free(block)``. The block is initialized with all bytes + ## containing zero, so it is somewhat safer than ``create``. ## The allocated memory belongs to its allocating thread! - ## Use `allocShared0` to allocate from a shared heap. + ## Use `createShared0` to allocate from a shared heap. cast[ptr T](alloc0(T.sizeof * size)) proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].} ## grows or shrinks a given memory block. If p is **nil** then a new @@ -1199,10 +1199,10 @@ when not defined(nimrodVM): ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not - ## **nil** ``realloc`` calls ``dealloc(p)``. In other cases the block - ## has to be freed with ``dealloc``. The allocated memory belongs to + ## **nil** ``resize`` calls ``free(p)``. In other cases the block + ## has to be freed with ``free``. The allocated memory belongs to ## its allocating thread! - ## Use `reallocShared` to reallocate from a shared heap. + ## Use `resizeShared` to reallocate from a shared heap. cast[ptr T](realloc(p, T.sizeof * newSize)) proc dealloc*(p: pointer) {.noconv, rtl, tags: [].} ## frees the memory allocated with ``alloc``, ``alloc0`` or @@ -1212,16 +1212,18 @@ when not defined(nimrodVM): ## or other memory may be corrupted. ## The freed memory must belong to its allocating thread! ## Use `deallocShared` to deallocate from a shared heap. + proc free*[T](p: ptr T) {.inline.} = + dealloc(p) proc allocShared*(size: int): pointer {.noconv, rtl.} ## allocates a new memory block on the shared heap with at ## least ``size`` bytes. The block has to be freed with ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## is not initialized, so reading from it before writing to it is ## undefined behaviour! - proc allocShared*(T: typedesc, size: int): ptr T {.inline.} = + proc createShared*(T: typedesc, size: int): ptr T {.inline.} = ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with - ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block + ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block ## is not initialized, so reading from it before writing to it is ## undefined behaviour! cast[ptr T](allocShared(T.sizeof * size)) @@ -1231,13 +1233,13 @@ when not defined(nimrodVM): ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``allocShared``. - proc allocShared0*(T: typedesc, size: int): ptr T {.inline.} = + proc createShared0*(T: typedesc, size: int): ptr T {.inline.} = ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with - ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. + ## ``resizeShared(block, 0)`` or ``freeShared(block)``. ## The block is initialized with all bytes - ## containing zero, so it is somewhat safer than ``allocShared``. - cast[ptr T](allocShared(T.sizeof * size)) + ## containing zero, so it is somewhat safer than ``createShared``. + cast[ptr T](allocShared0(T.sizeof * size)) proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.} ## grows or shrinks a given memory block on the heap. If p is **nil** ## then a new memory block is returned. In either way the block has at @@ -1248,8 +1250,8 @@ when not defined(nimrodVM): ## grows or shrinks a given memory block on the heap. If p is **nil** ## then a new memory block is returned. In either way the block has at ## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is - ## not **nil** ``reallocShared`` calls ``deallocShared(p)``. In other - ## cases the block has to be freed with ``deallocShared``. + ## not **nil** ``resizeShared`` calls ``freeShared(p)``. In other + ## cases the block has to be freed with ``freeShared``. cast[ptr T](reallocShared(p, T.sizeof * newSize)) proc deallocShared*(p: pointer) {.noconv, rtl.} ## frees the memory allocated with ``allocShared``, ``allocShared0`` or @@ -1257,6 +1259,13 @@ when not defined(nimrodVM): ## free the memory a leak occurs; if one tries to access freed ## memory (or just freeing it twice!) a core dump may happen ## or other memory may be corrupted. + proc freeShared*[T](p: ptr T) {.inline.} = + ## frees the memory allocated with ``createShared``, ``createShared0`` or + ## ``resizeShared``. This procedure is dangerous! If one forgets to + ## free the memory a leak occurs; if one tries to access freed + ## memory (or just freeing it twice!) a core dump may happen + ## or other memory may be corrupted. + deallocShared(p) proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.} ## swaps the values `a` and `b`. This is often more efficient than From 61e5435081069792dd027a1f127cba836af1ed20 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Fri, 7 Mar 2014 00:36:58 +0100 Subject: [PATCH 138/249] Update unit test. --- tests/system/alloc.nim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/system/alloc.nim b/tests/system/alloc.nim index 3a10a1a39..f8e14ac04 100644 --- a/tests/system/alloc.nim +++ b/tests/system/alloc.nim @@ -3,11 +3,11 @@ var x: ptr int x = cast[ptr int](alloc(7)) assert x != nil -x = alloc(int, 3) +x = create(int, 3) assert x != nil x.dealloc() -x = alloc0(int, 4) +x = create0(int, 4) assert cast[ptr array[4, int]](x)[0] == 0 assert cast[ptr array[4, int]](x)[1] == 0 assert cast[ptr array[4, int]](x)[2] == 0 @@ -18,28 +18,28 @@ assert x != nil x = x.resize(4) assert x != nil -x.dealloc() +x.free() x = cast[ptr int](allocShared(100)) assert x != nil deallocShared(x) -x = allocShared(int, 3) +x = createShared(int, 3) assert x != nil -x.deallocShared() +x.freeShared() -x = allocShared0(int, 3) +x = createShared0(int, 3) assert x != nil assert cast[ptr array[3, int]](x)[0] == 0 assert cast[ptr array[3, int]](x)[1] == 0 assert cast[ptr array[3, int]](x)[2] == 0 -x = cast[ptr int](reallocShared(x, 2)) +x = cast[ptr int](x.resizeShared(2)) assert x != nil -x = resize(x, 12) +x = x.resize(12) assert x != nil -x = resizeShared(x, 1) +x = x.resizeShared(1) assert x != nil -x.deallocShared() +x.freeShared() From 5b7e44f0aaab8e47e11de029924439175f4ad0bd Mon Sep 17 00:00:00 2001 From: EXetoC Date: Fri, 7 Mar 2014 01:00:18 +0100 Subject: [PATCH 139/249] create -> createU, create0 -> create. --- lib/system.nim | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 0ae141241..953aa3bce 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1164,13 +1164,13 @@ when not defined(nimrodVM): ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! ## Use `allocShared` to allocate from a shared heap. - proc create*(T: typedesc, size = 1): ptr T {.inline.} = + proc createU*(T: typedesc, size = 1): ptr T {.inline.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or ## ``free(block)``. The block is not initialized, so reading ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! - ## Use `createShared` to allocate from a shared heap. + ## Use `createSharedU` to allocate from a shared heap. cast[ptr T](alloc(T.sizeof * size)) proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].} ## allocates a new memory block with at least ``size`` bytes. The @@ -1179,13 +1179,13 @@ when not defined(nimrodVM): ## containing zero, so it is somewhat safer than ``alloc``. ## The allocated memory belongs to its allocating thread! ## Use `allocShared0` to allocate from a shared heap. - proc create0*(T: typedesc, size = 1): ptr T {.inline.} = + proc create*(T: typedesc, size = 1): ptr T {.inline.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or ## ``free(block)``. The block is initialized with all bytes - ## containing zero, so it is somewhat safer than ``create``. + ## containing zero, so it is somewhat safer than ``createU``. ## The allocated memory belongs to its allocating thread! - ## Use `createShared0` to allocate from a shared heap. + ## Use `createShared` to allocate from a shared heap. cast[ptr T](alloc0(T.sizeof * size)) proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].} ## grows or shrinks a given memory block. If p is **nil** then a new @@ -1220,7 +1220,7 @@ when not defined(nimrodVM): ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## is not initialized, so reading from it before writing to it is ## undefined behaviour! - proc createShared*(T: typedesc, size: int): ptr T {.inline.} = + proc createSharedU*(T: typedesc, size: int): ptr T {.inline.} = ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block @@ -1233,12 +1233,12 @@ when not defined(nimrodVM): ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``allocShared``. - proc createShared0*(T: typedesc, size: int): ptr T {.inline.} = + proc createShared*(T: typedesc, size: int): ptr T {.inline.} = ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with ## ``resizeShared(block, 0)`` or ``freeShared(block)``. ## The block is initialized with all bytes - ## containing zero, so it is somewhat safer than ``createShared``. + ## containing zero, so it is somewhat safer than ``createSharedU``. cast[ptr T](allocShared0(T.sizeof * size)) proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.} ## grows or shrinks a given memory block on the heap. If p is **nil** @@ -1260,7 +1260,7 @@ when not defined(nimrodVM): ## memory (or just freeing it twice!) a core dump may happen ## or other memory may be corrupted. proc freeShared*[T](p: ptr T) {.inline.} = - ## frees the memory allocated with ``createShared``, ``createShared0`` or + ## frees the memory allocated with ``createShared``, ``createSharedU`` or ## ``resizeShared``. This procedure is dangerous! If one forgets to ## free the memory a leak occurs; if one tries to access freed ## memory (or just freeing it twice!) a core dump may happen From c7b83d2c44626cd90d0d83ed2684c120e1490283 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Fri, 7 Mar 2014 01:09:16 +0100 Subject: [PATCH 140/249] Update unit test. --- tests/system/alloc.nim | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/system/alloc.nim b/tests/system/alloc.nim index f8e14ac04..7abefec2a 100644 --- a/tests/system/alloc.nim +++ b/tests/system/alloc.nim @@ -2,20 +2,20 @@ var x: ptr int x = cast[ptr int](alloc(7)) assert x != nil - -x = create(int, 3) +x = cast[ptr int](x.realloc(2)) assert x != nil x.dealloc() -x = create0(int, 4) +x = createU(int, 3) +assert x != nil +x.free() + +x = create(int, 4) assert cast[ptr array[4, int]](x)[0] == 0 assert cast[ptr array[4, int]](x)[1] == 0 assert cast[ptr array[4, int]](x)[2] == 0 assert cast[ptr array[4, int]](x)[3] == 0 -x = cast[ptr int](x.realloc(2)) -assert x != nil - x = x.resize(4) assert x != nil x.free() @@ -24,22 +24,29 @@ x = cast[ptr int](allocShared(100)) assert x != nil deallocShared(x) -x = createShared(int, 3) +x = createSharedU(int, 3) assert x != nil x.freeShared() -x = createShared0(int, 3) +x = createShared(int, 3) assert x != nil assert cast[ptr array[3, int]](x)[0] == 0 assert cast[ptr array[3, int]](x)[1] == 0 assert cast[ptr array[3, int]](x)[2] == 0 +assert x != nil x = cast[ptr int](x.resizeShared(2)) assert x != nil +x.freeShared() +x = create(int, 10) +assert x != nil x = x.resize(12) assert x != nil +x.dealloc() +x = createShared(int, 1) +assert x != nil x = x.resizeShared(1) assert x != nil x.freeShared() From 3147f7f6c2c8fc62d04d5b449df093f113935487 Mon Sep 17 00:00:00 2001 From: EXetoC Date: Fri, 7 Mar 2014 01:32:07 +0100 Subject: [PATCH 141/249] Use 'Natural' or 'Positive' instead of 'int' where applicable. --- lib/system.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 953aa3bce..24ad50f97 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1164,7 +1164,7 @@ when not defined(nimrodVM): ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! ## Use `allocShared` to allocate from a shared heap. - proc createU*(T: typedesc, size = 1): ptr T {.inline.} = + proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or ## ``free(block)``. The block is not initialized, so reading @@ -1179,7 +1179,7 @@ when not defined(nimrodVM): ## containing zero, so it is somewhat safer than ``alloc``. ## The allocated memory belongs to its allocating thread! ## Use `allocShared0` to allocate from a shared heap. - proc create*(T: typedesc, size = 1): ptr T {.inline.} = + proc create*(T: typedesc, size = 1.Positive): ptr T {.inline.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or ## ``free(block)``. The block is initialized with all bytes @@ -1195,7 +1195,7 @@ when not defined(nimrodVM): ## be freed with ``dealloc``. ## The allocated memory belongs to its allocating thread! ## Use `reallocShared` to reallocate from a shared heap. - proc resize*[T](p: ptr T, newSize: int): ptr T {.inline.} = + proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline.} = ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not @@ -1220,7 +1220,7 @@ when not defined(nimrodVM): ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## is not initialized, so reading from it before writing to it is ## undefined behaviour! - proc createSharedU*(T: typedesc, size: int): ptr T {.inline.} = + proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline.} = ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block @@ -1233,7 +1233,7 @@ when not defined(nimrodVM): ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``allocShared``. - proc createShared*(T: typedesc, size: int): ptr T {.inline.} = + proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} = ## allocates a new memory block on the shared heap with at ## least ``T.sizeof * size`` bytes. The block has to be freed with ## ``resizeShared(block, 0)`` or ``freeShared(block)``. @@ -1246,7 +1246,7 @@ when not defined(nimrodVM): ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** ## ``reallocShared`` calls ``deallocShared(p)``. In other cases the ## block has to be freed with ``deallocShared``. - proc resizeShared*[T](p: ptr T, newSize: int): ptr T {.inline.} = + proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline.} = ## grows or shrinks a given memory block on the heap. If p is **nil** ## then a new memory block is returned. In either way the block has at ## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is From 91d842e1ec070a9ab7f883820bd6244526f5d622 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Mar 2014 22:25:05 +0100 Subject: [PATCH 142/249] implements strongSpaces parsing mode --- compiler/canonicalizer.nim | 6 ++-- compiler/lexer.nim | 16 ++++++++++ compiler/parser.nim | 65 ++++++++++++++++++++++++++------------ compiler/pragmas.nim | 10 ++---- compiler/syntaxes.nim | 21 ++++++------ doc/manual.txt | 46 +++++++++++++++++++++++++-- todo.txt | 1 - web/news.txt | 1 + 8 files changed, 123 insertions(+), 43 deletions(-) diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim index 07e932b28..3bc4eb029 100644 --- a/compiler/canonicalizer.nim +++ b/compiler/canonicalizer.nim @@ -81,7 +81,7 @@ proc hashSym(c: var MD5Context, s: PSym) = proc hashTree(c: var MD5Context, n: PNode) = if n == nil: - c &= "noTreeKind" + c &= "\255" return var k = n.kind md5Update(c, cast[cstring](addr(k)), 1) @@ -107,7 +107,7 @@ proc hashTree(c: var MD5Context, n: PNode) = proc hashType(c: var MD5Context, t: PType) = # modelled after 'typeToString' if t == nil: - c &= "noTypeKind" + c &= "\254" return var k = t.kind @@ -168,7 +168,7 @@ proc canonConst(n: PNode): TUid = c.hashType(n.typ) md5Final(c, MD5Digest(result)) -proc canonSym(s: PSym): TUid +proc canonSym(s: PSym): TUid = var c: MD5Context md5Init(c) c.hashSym(s) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 0e7df13cd..9c6c5e22f 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -110,6 +110,8 @@ type fNumber*: BiggestFloat # the parsed floating point literal base*: TNumericalBase # the numerical base; only valid for int # or float literals + strongSpaceA*: int8 # leading spaces of an operator + strongSpaceB*: int8 # trailing spaces of an operator literal*: string # the parsed (string) literal; and # documentation comments are here too line*, col*: int @@ -119,6 +121,7 @@ type indentAhead*: int # if > 0 an indendation has already been read # this is needed because scanning comments # needs so much look-ahead + strongSpaces*: bool var gLinesCompiled*: int # all lines that have been compiled @@ -183,6 +186,7 @@ proc initToken*(L: var TToken) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 + L.strongSpaceA = 0 L.literal = "" L.fNumber = 0.0 L.base = base10 @@ -192,6 +196,7 @@ proc fillToken(L: var TToken) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 + L.strongSpaceA = 0 setLen(L.literal, 0) L.fNumber = 0.0 L.base = base10 @@ -634,6 +639,14 @@ proc getOperator(L: var TLexer, tok: var TToken) = h = h !& ord(c) inc(pos) endOperator(L, tok, pos, h) + # advance pos but don't store it in L.bufpos so the next token (which might + # be an operator too) gets the preceeding spaces: + tok.strongSpaceB = 0 + while buf[pos] == ' ': + inc pos + inc tok.strongSpaceB + if buf[pos] in {CR, LF, nimlexbase.EndOfFile}: + tok.strongSpaceB = -1 proc scanComment(L: var TLexer, tok: var TToken) = var pos = L.bufpos @@ -677,10 +690,12 @@ proc scanComment(L: var TLexer, tok: var TToken) = proc skip(L: var TLexer, tok: var TToken) = var pos = L.bufpos var buf = L.buf + tok.strongSpaceA = 0 while true: case buf[pos] of ' ': inc(pos) + inc(tok.strongSpaceA) of Tabulator: lexMessagePos(L, errTabulatorsAreNotAllowed, pos) inc(pos) @@ -691,6 +706,7 @@ proc skip(L: var TLexer, tok: var TToken) = while buf[pos] == ' ': inc(pos) inc(indent) + tok.strongSpaceA = 0 if buf[pos] > ' ': tok.indent = indent break diff --git a/compiler/parser.nim b/compiler/parser.nim index 5a5bfb574..cfba89f4a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -38,7 +38,6 @@ type inSemiStmtList: int proc parseAll*(p: var TParser): PNode -proc openParser*(p: var TParser, filename: string, inputstream: PLLStream) proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode # implements an iterator. Returns the next top-level statement or @@ -50,7 +49,6 @@ proc parseString*(s: string, filename: string = "", line: int = 0): PNode # correct error messages referring to the original source. # helpers for the other parsers -proc getPrecedence*(tok: TToken): int proc isOperator*(tok: TToken): bool proc getTok*(p: var TParser) proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "") @@ -77,14 +75,17 @@ proc parseCase(p: var TParser): PNode proc getTok(p: var TParser) = rawGetTok(p.lex, p.tok) -proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream) = +proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream, + strongSpaces=false) = initToken(p.tok) openLexer(p.lex, fileIdx, inputStream) getTok(p) # read the first token p.firstTok = true + p.strongSpaces = strongSpaces -proc openParser*(p: var TParser, filename: string, inputStream: PLLStream) = - openParser(p, filename.fileInfoIdx, inputstream) +proc openParser*(p: var TParser, filename: string, inputStream: PLLStream, + strongSpaces=false) = + openParser(p, filename.fileInfoIdx, inputstream, strongSpaces) proc closeParser(p: var TParser) = closeLexer(p.lex) @@ -193,34 +194,52 @@ proc isSigilLike(tok: TToken): bool {.inline.} = proc isLeftAssociative(tok: TToken): bool {.inline.} = result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^' -proc getPrecedence(tok: TToken): int = +proc getPrecedence(tok: TToken, strongSpaces: bool): int = + template considerStrongSpaces(x): expr = + x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0) + case tok.tokType of tkOpr: let L = tok.ident.s.len let relevantChar = relevantOprChar(tok.ident) - template considerAsgn(value: expr) = - result = if tok.ident.s[L-1] == '=': 1 else: value + template considerAsgn(value: expr) = + result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value) case relevantChar of '$', '^': considerAsgn(10) of '*', '%', '/', '\\': considerAsgn(9) - of '~': result = 8 + of '~': result = considerStrongSpaces(8) of '+', '-', '|': considerAsgn(8) of '&': considerAsgn(7) - of '=', '<', '>', '!': result = 5 + of '=', '<', '>', '!': result = considerStrongSpaces(5) of '.': considerAsgn(6) - of '?': result = 2 + of '?': result = considerStrongSpaces(2) else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5 - of tkDotDot: result = 6 + of tkDotDot: result = considerStrongSpaces(6) of tkAnd: result = 4 of tkOr, tkXor: result = 3 - else: result = - 10 - -proc isOperator(tok: TToken): bool = - result = getPrecedence(tok) >= 0 + else: result = -10 + +proc isOperator(tok: TToken): bool = + tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, + tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} + +proc isUnary(p: TParser): bool = + p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and + p.tok.strongSpaceB == 0 and + p.tok.strongSpaceA > 0 + +proc checkBinary(p: TParser) {.inline.} = + # we don't check '..' here as that's too annoying + if p.strongSpaces and p.tok.tokType == tkOpr: + if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB: + parMessage(p, errGenerated, "number of spaces around '$#' not consistent"% + prettyTok(p.tok)) + elif p.tok.strongSpaceA notin {0,1,2,4,8}: + parMessage(p, errGenerated, "number of spaces must be 0,1,2,4 or 8") #| module = stmt ^* (';' / IND{=}) #| @@ -650,6 +669,7 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = while p.tok.indent < 0: case p.tok.tokType of tkParLe: + if p.strongSpaces and p.tok.strongSpaceA > 0: break result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: result.kind = nkObjConstr @@ -664,8 +684,10 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = result = dotExpr(p, result) result = parseGStrLit(p, result) of tkBracketLe: + if p.strongSpaces and p.tok.strongSpaceA > 0: break result = namedParams(p, result, nkBracketExpr, tkBracketRi) of tkCurlyLe: + if p.strongSpaces and p.tok.strongSpaceA > 0: break result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast: if p.inPragma == 0: @@ -695,10 +717,11 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = result = primary(p, mode) # expand while operators have priorities higher than 'limit' - var opPrec = getPrecedence(p.tok) + var opPrec = getPrecedence(p.tok, p.strongSpaces) let modeB = if mode == pmTypeDef: pmTypeDesc else: mode # the operator itself must not start on a new line: - while opPrec >= limit and p.tok.indent < 0: + while opPrec >= limit and p.tok.indent < 0 and not isUnary(p): + checkBinary(p) var leftAssoc = ord(isLeftAssociative(p.tok)) var a = newNodeP(nkInfix, p) var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: @@ -710,7 +733,7 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = addSon(a, result) addSon(a, b) result = a - opPrec = getPrecedence(p.tok) + opPrec = getPrecedence(p.tok, p.strongSpaces) proc simpleExpr(p: var TParser, mode = pmNormal): PNode = result = simpleExprAux(p, -1, mode) @@ -1933,7 +1956,9 @@ proc parseString(s: string, filename: string = "", line: int = 0): PNode = stream.lineOffset = line var parser: TParser - openParser(parser, filename, stream) + # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong + # spaces... + openParser(parser, filename, stream, false) result = parser.parseAll closeParser(parser) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index bf3564016..f5d69a01c 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -97,8 +97,6 @@ proc makeExternImport(s: PSym, extname: string) = incl(s.flags, sfImportc) excl(s.flags, sfForward) -const invalidIdentChars = AllChars - IdentChars - proc validateExternCName(s: PSym, info: TLineInfo) = ## Validates that the symbol name in s.loc.r is a valid C identifier. ## @@ -106,16 +104,14 @@ proc validateExternCName(s: PSym, info: TLineInfo) = ## starting with a number. If the check fails, a generic error will be ## displayed to the user. let target = ropeToStr(s.loc.r) - if target.len < 1 or (not (target[0] in IdentStartChars)) or - (not target.allCharsInSet(IdentChars)): + if target.len < 1 or target[0] notin IdentStartChars or + not target.allCharsInSet(IdentChars): localError(info, errGenerated, "invalid exported symbol") proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = setExternName(s, extname) - case gCmd - of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC: + if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}: validateExternCName(s, info) - else: discard incl(s.flags, sfExportc) proc processImportCompilerProc(s: PSym, extname: string) = diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 7c44ec0b4..478c2a837 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -17,14 +17,15 @@ type TFilterKind* = enum filtNone, filtTemplate, filtReplace, filtStrip TParserKind* = enum - skinStandard, skinBraces, skinEndX + skinStandard, skinStrongSpaces, skinBraces, skinEndX const - parserNames*: array[TParserKind, string] = ["standard", "braces", "endx"] - filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", - "strip"] + parserNames*: array[TParserKind, string] = ["standard", "strongspaces", + "braces", "endx"] + filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", + "strip"] -type +type TParsers*{.final.} = object skin*: TParserKind parser*: TParser @@ -54,7 +55,7 @@ proc parseFile(fileIdx: int32): PNode = proc parseAll(p: var TParsers): PNode = case p.skin - of skinStandard: + of skinStandard, skinStrongSpaces: result = parser.parseAll(p.parser) of skinBraces: result = pbraces.parseAll(p.parser) @@ -65,7 +66,7 @@ proc parseAll(p: var TParsers): PNode = proc parseTopLevelStmt(p: var TParsers): PNode = case p.skin - of skinStandard: + of skinStandard, skinStrongSpaces: result = parser.parseTopLevelStmt(p.parser) of skinBraces: result = pbraces.parseTopLevelStmt(p.parser) @@ -170,7 +171,9 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) = else: s = inputstream case p.skin of skinStandard, skinBraces, skinEndX: - parser.openParser(p.parser, fileIdx, s) + parser.openParser(p.parser, fileIdx, s, false) + of skinStrongSpaces: + parser.openParser(p.parser, fileIdx, s, true) -proc closeParsers(p: var TParsers) = +proc closeParsers(p: var TParsers) = parser.closeParser(p.parser) diff --git a/doc/manual.txt b/doc/manual.txt index 98219360e..1c6cf6c1d 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -480,8 +480,8 @@ precedence and associativity; this is useful for meta programming. Associativity ------------- -All binary operators are left-associative, except binary operators whose -relevant char is ``^``. +Binary operators whose relevant character is ``^`` are right-associative, all +other binary operators are left-associative. Precedence ---------- @@ -508,7 +508,7 @@ Precedence level Operators Relevant char 7 ``+ -`` ``+ ~ |`` OP7 6 ``&`` ``&`` OP6 5 ``..`` ``.`` OP5 - 4 ``== <= < >= > != in not_in is isnot not of`` ``= < > !`` OP4 + 4 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP4 3 ``and`` OP3 2 ``or xor`` OP2 1 ``@ : ?`` OP1 @@ -516,6 +516,46 @@ Precedence level Operators Relevant char ================ =============================================== ================== =============== +Strong spaces +------------- + +The number of spaces preceeding a non-keyword operator affects precedence +if the experimental parser directive ``#!strongSpaces`` is used. Indentation +is not used to determine the number of spaces. If 2 or more operators have the +same number of preceeding spaces the precedence table applies, so ``1 + 3 * 4`` +is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``: + +.. code-block:: nimrod + #! strongSpaces + if foo+4 * 4 == 8 and b&c | 9 ++ + bar: + echo "" + # is parsed as + if ((foo+4)*4 == 8) and (((b&c) | 9) ++ bar): echo "" + + +Furthermore whether an operator is used a prefix operator is affected by the +number of spaces: + +.. code-block:: nimrod + #! strongSpaces + echo $foo + # is parsed as + echo($foo) + +This also affects whether ``[]``, ``{}``, ``()`` are parsed as constructors +or as accessors: + +.. code-block:: nimrod + #! strongSpaces + echo (1,2) + # is parsed as + echo((1,2)) + + +Grammar +------- + The grammar's start symbol is ``module``. .. include:: grammar.txt diff --git a/todo.txt b/todo.txt index a67ff5172..51f883d1d 100644 --- a/todo.txt +++ b/todo.txt @@ -32,7 +32,6 @@ version 0.9.x - ensure (ref T)(a, b) works as a type conversion and type constructor - optimize 'genericReset'; 'newException' leads to code bloat - stack-less GC -- implement strongSpaces:on - make '--implicitStatic:on' the default - implicit deref for parameter matching diff --git a/web/news.txt b/web/news.txt index 0001cece7..83863cdd9 100644 --- a/web/news.txt +++ b/web/news.txt @@ -82,6 +82,7 @@ News - The *command syntax* is supported in a lot more contexts. - Anonymous iterators are now supported and iterators can capture variables of an outer proc. + - The experimental ``strongSpaces`` parsing mode has been implemented. Tools improvements From 71de5ea909ea16463a985f077a960153f9adc9db Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Fri, 7 Mar 2014 17:50:57 -0500 Subject: [PATCH 143/249] Tester now outputs all the test results of the current test run to json. --- tests/testament/htmlgen.nim | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim index 74d8811b8..93e3a4270 100644 --- a/tests/testament/htmlgen.nim +++ b/tests/testament/htmlgen.nim @@ -174,6 +174,10 @@ proc generateJson*(filename: string, commit: int) = on A.name = B.name and A.category = B.category where A.[commit] = ? and B.[commit] = ? and A.machine = ? and A.result != B.result""" + selResults = """select + name, category, target, action, result, expected, given + from TestResult + where [commit] = ?""" var db = open(connection="testament.db", user="testament", password="", database="testament") let lastCommit = db.getCommit(commit) @@ -189,6 +193,21 @@ proc generateJson*(filename: string, commit: int) = outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#""" % data) + let results = newJArray() + for row in db.rows(sql(selResults), lastCommit): + echo(repr(row)) + var obj = newJObject() + obj["name"] = %row[0] + obj["category"] = %row[1] + obj["target"] = %row[2] + obj["action"] = %row[3] + obj["result"] = %row[4] + obj["expected"] = %row[5] + obj["given"] = %row[6] + results.add(obj) + outfile.writeln(""", "results": """) + outfile.write(results.pretty) + if not previousCommit.isNil: let diff = newJArray() From 5f19af9e01bf30b1b28c410377f919e02fc6f1cc Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Sat, 8 Mar 2014 14:30:05 -0500 Subject: [PATCH 144/249] Removed debug code --- tests/testament/htmlgen.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim index 93e3a4270..89d56c693 100644 --- a/tests/testament/htmlgen.nim +++ b/tests/testament/htmlgen.nim @@ -195,7 +195,6 @@ proc generateJson*(filename: string, commit: int) = let results = newJArray() for row in db.rows(sql(selResults), lastCommit): - echo(repr(row)) var obj = newJObject() obj["name"] = %row[0] obj["category"] = %row[1] From 085b339b8b12267cb8ed555979db368e151c9ca4 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 8 Mar 2014 22:57:06 +0200 Subject: [PATCH 145/249] implements higher-order inline iterators and return type inference for iterators --- compiler/msgs.nim | 6 +++-- compiler/semexprs.nim | 16 ++++++++++++-- compiler/seminst.nim | 2 +- compiler/semstmts.nim | 18 ++++++++------- compiler/semtypes.nim | 28 ++++++++++++++++++----- compiler/semtypinst.nim | 7 +++++- compiler/sigmatch.nim | 19 +++++++++++++--- compiler/transf.nim | 12 +++++----- tests/iter/tchainediterators.nim | 38 ++++++++++++++++++++++++++++++++ 9 files changed, 119 insertions(+), 27 deletions(-) create mode 100644 tests/iter/tchainediterators.nim diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 66763e7f5..5bc490d14 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -96,8 +96,8 @@ type errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errMacroBodyDependsOnGenericTypes, errDestructorNotGenericEnough, - - errXExpectsTwoArguments, + errInlineIteratorsAsProcParams, + errXExpectsTwoArguments, errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, @@ -331,6 +331,8 @@ const "because the parameter '$1' has a generic type", errDestructorNotGenericEnough: "Destructor signarue is too specific. " & "A destructor must be associated will all instantiations of a generic type", + errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " & + "templates, macros and other inline iterators", errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsObjectTypes: "\'$1\' expects object types", errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 203a51816..c16ab9b87 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1119,6 +1119,9 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = n.sons[0] = x # 'result[]' --> 'result' n.sons[1] = takeImplicitAddr(c, ri) +template resultTypeIsInferrable(typ: PType): expr = + typ.isMetaType and typ.kind != tyTypeDesc + proc semAsgn(c: PContext, n: PNode): PNode = checkSonsLen(n, 2) var a = n.sons[0] @@ -1170,7 +1173,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: n.typ = enforceVoidContext - if lhs.sym.typ.isMetaType and lhs.sym.typ.kind != tyTypeDesc: + if resultTypeIsInferrable(lhs.sym.typ): if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: internalAssert c.p.resultSym != nil lhs.typ = rhs.typ @@ -1259,12 +1262,21 @@ proc semYield(c: PContext, n: PNode): PNode = localError(n.info, errYieldNotAllowedInTryStmt) elif n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: - var restype = c.p.owner.typ.sons[0] + var iterType = c.p.owner.typ + var restype = iterType.sons[0] if restype != nil: let adjustedRes = if c.p.owner.kind == skIterator: restype.base else: restype n.sons[0] = fitNode(c, adjustedRes, n.sons[0]) if n.sons[0].typ == nil: internalError(n.info, "semYield") + + if resultTypeIsInferrable(adjustedRes): + let inferred = n.sons[0].typ + if c.p.owner.kind == skIterator: + iterType.sons[0].sons[0] = inferred + else: + iterType.sons[0] = inferred + semYieldVarResult(c, n, adjustedRes) else: localError(n.info, errCannotReturnExpr) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 8faf1d21a..4bcfa7f15 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -20,7 +20,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, if a.kind != nkSym: internalError(a.info, "instantiateGenericParamList; no symbol") var q = a.sym - if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses: + if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: continue var s = newSym(skType, q.name, getCurrOwner(), q.info) s.flags = s.flags + {sfUsed, sfFromGeneric} diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a11386966..edce7c9bd 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -661,10 +661,18 @@ proc semFor(c: PContext, n: PNode): PNode = openScope(c) n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator}) var call = n.sons[length-2] - if call.kind in nkCallKinds and call.sons[0].typ.callConv == ccClosure: + let isCallExpr = call.kind in nkCallKinds + if isCallExpr and call.sons[0].sym.magic != mNone: + if call.sons[0].sym.magic == mOmpParFor: + result = semForVars(c, n) + result.kind = nkParForStmt + else: + result = semForFields(c, n, call.sons[0].sym.magic) + elif (isCallExpr and call.sons[0].typ.callConv == ccClosure) or + call.typ.kind == tyIter: # first class iterator: result = semForVars(c, n) - elif call.kind notin nkCallKinds or call.sons[0].kind != nkSym or + elif not isCallExpr or call.sons[0].kind != nkSym or call.sons[0].sym.kind notin skIterators: if length == 3: n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2]) @@ -673,12 +681,6 @@ proc semFor(c: PContext, n: PNode): PNode = else: localError(n.sons[length-2].info, errIteratorExpected) result = semForVars(c, n) - elif call.sons[0].sym.magic != mNone: - if call.sons[0].sym.magic == mOmpParFor: - result = semForVars(c, n) - result.kind = nkParForStmt - else: - result = semForFields(c, n, call.sons[0].sym.magic) else: result = semForVars(c, n) # propagate any enforced VoidContext: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a619de7ff..875631505 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -721,7 +721,16 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = addImplicitGeneric(result) - + + of tyIter: + if paramType.callConv == ccInline: + if procKind notin {skTemplate, skMacro, skIterator}: + localError(info, errInlineIteratorsAsProcParams) + if paramType.len == 1: + let lifted = liftingWalk(paramType.base) + if lifted != nil: paramType.sons[0] = lifted + result = addImplicitGeneric(paramType) + of tyGenericInst: if paramType.lastSon.kind == tyUserTypeClass: var cp = copyType(paramType, getCurrOwner(), false) @@ -852,7 +861,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if lifted != nil: r = lifted r.flags.incl tfRetType r = skipIntLit(r) - if kind == skIterator: r = newTypeWithSons(c, tyIter, @[r]) + if kind == skIterator: + # see tchainediterators + # in cases like iterator foo(it: iterator): type(it) + # we don't need to change the return type to iter[T] + if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r]) result.sons[0] = r res.typ = r @@ -984,7 +997,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkTypeOfExpr: # for ``type(countup(1,3))``, see ``tests/ttoseq``. checkSonsLen(n, 1) - result = semExprWithType(c, n.sons[0], {efInTypeof}).typ.skipTypes({tyIter}) + let typExpr = semExprWithType(c, n.sons[0], {efInTypeof}) + result = typExpr.typ.skipTypes({tyIter}) of nkPar: if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) else: @@ -1103,8 +1117,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = newConstraint(c, tyIter) else: result = semProcTypeWithScope(c, n, prev, skClosureIterator) - result.flags.incl(tfIterator) - result.callConv = ccClosure + if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline): + result.kind = tyIter + result.callConv = ccInline + else: + result.flags.incl(tfIterator) + result.callConv = ccClosure of nkProcTy: if n.sonsLen == 0: result = newConstraint(c, tyProc) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 22edc6e32..46ec31d90 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -305,6 +305,11 @@ proc skipIntLiteralParams(t: PType) = if skipped != p: t.sons[i] = skipped if i > 0: t.n.sons[i].sym.typ = skipped + + # when the typeof operator is used on a static input + # param, the results gets infected with static as well: + if t.sons[0] != nil and t.sons[0].kind == tyStatic: + t.sons[0] = t.sons[0].base proc propagateFieldFlags(t: PType, n: PNode) = # This is meant for objects and tuples @@ -323,7 +328,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = t if t == nil: return - if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses: + if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses: let lookup = PType(idTableGet(cl.typeMap, t)) if lookup != nil: return lookup diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index c0898ef26..19f10def8 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1014,6 +1014,10 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, result.typ = getInstantiatedType(c, arg, m, base(f)) m.baseTypeMatch = true +proc isInlineIterator*(t: PType): bool = + result = t.kind == tyIter or + (t.kind == tyBuiltInTypeClass and t.base.kind == tyIter) + proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, argSemantized, argOrig: PNode): PNode = var @@ -1021,7 +1025,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, arg = argSemantized argType = argType c = m.c - + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to @@ -1060,7 +1064,14 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, return arg.typ.n else: return argOrig - + + if r != isNone and f.isInlineIterator: + var inlined = newTypeS(tyStatic, c) + inlined.sons = @[argType] + inlined.n = argSemantized + put(m.bindings, f, inlined) + return argSemantized + case r of isConvertible: inc(m.convMatches) @@ -1188,7 +1199,9 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = # a.typ == nil is valid result = a elif a.typ.isNil: - result = c.semOperand(c, a, {efDetermineType}) + let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator} + else: {efDetermineType} + result = c.semOperand(c, a, flags) else: result = a diff --git a/compiler/transf.nim b/compiler/transf.nim index f4b716c5b..9586398c9 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -425,7 +425,7 @@ proc findWrongOwners(c: PTransf, n: PNode) = x.sym.owner.name.s & " " & getCurrOwner(c).name.s) else: for i in 0 .. Date: Sat, 8 Mar 2014 23:27:33 +0200 Subject: [PATCH 146/249] implicit auto return type for inline iterators --- compiler/semtypes.nim | 8 ++++++-- doc/manual.txt | 20 +++++++++++++------- tests/iter/tchainediterators.nim | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 875631505..dd73cf01a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -849,9 +849,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, addParamOrResult(c, arg, kind) if gCmd == cmdPretty: checkDef(a.sons[j], arg) - + var r: PType if n.sons[0].kind != nkEmpty: - var r = semTypeNode(c, n.sons[0], nil) + r = semTypeNode(c, n.sons[0], nil) + elif kind == skIterator: + r = newTypeS(tyAnything, c) + + if r != nil: # turn explicit 'void' return type into 'nil' because the rest of the # compiler only checks for 'nil': if skipTypes(r, {tyGenericInst}).kind != tyEmpty: diff --git a/doc/manual.txt b/doc/manual.txt index 3c1f7b651..04050a99e 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2822,7 +2822,6 @@ as there are components in the tuple. The i'th iteration variable's type is the type of the i'th component. In other words, implicit tuple unpacking in a for loop context is supported. - Implict items/pairs invocations ------------------------------- @@ -2847,10 +2846,11 @@ First class iterators There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators. An `inline iterator`:idx: is an iterator that's always inlined by the compiler leading to zero overhead for the abstraction, but may result in a heavy -increase in code size. Inline iterators are second class -citizens; one cannot pass them around like first class procs. +increase in code size. Inline iterators are second class citizens; +They can be passed as parameters only to other inlining code facilities like +templates, macros and other inline iterators. -In contrast to that, a `closure iterator`:idx: can be passed around: +In contrast to that, a `closure iterator`:idx: can be passed around more freely: .. code-block:: nimrod iterator count0(): int {.closure.} = @@ -2873,9 +2873,7 @@ Closure iterators have other restrictions than inline iterators: 1. ``yield`` in a closure iterator can not occur in a ``try`` statement. 2. For now, a closure iterator cannot be evaluated at compile time. 3. ``return`` is allowed in a closure iterator (but rarely useful). -4. Since closure iterators can be used as a collaborative tasking - system, ``void`` is a valid return type for them. -5. Both inline and closure iterators cannot be recursive. +4. Both inline and closure iterators cannot be recursive. Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly default to being inline, but that this may change in future versions of the @@ -2937,6 +2935,14 @@ parameters of an outer factory proc: for f in foo(): echo f +Implicit return type +-------------------- + +Since inline interators must always produce values that will be consumed in +a for loop, the compiler will implicity use the ``auto`` return type if no +type is given by the user. In contrast, since closure iterators can be used +as a collaborative tasking system, ``void`` is a valid return type for them. + Type sections ============= diff --git a/tests/iter/tchainediterators.nim b/tests/iter/tchainediterators.nim index 010703cb5..18d096761 100644 --- a/tests/iter/tchainediterators.nim +++ b/tests/iter/tchainediterators.nim @@ -12,7 +12,7 @@ iterator gaz(it: iterator{.inline.}): type(it) = for x in it: yield x*2 -iterator baz(it: iterator{.inline.}): auto = +iterator baz(it: iterator{.inline.}) = for x in gaz(it): yield x*2 From 7b055388b7acfc7b7db11dd8102a0de23e3547ae Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Sun, 9 Mar 2014 11:47:22 +0100 Subject: [PATCH 147/249] raise exceptions through newException --- lib/pure/net.nim | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ab9c3ac23..82b24585f 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -139,7 +139,7 @@ proc parseIPv4Address(address_str: string): TIpAddress = for i in 0 .. high(address_str): if address_str[i] in strutils.Digits: # Character is a number currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) - if currentByte > 255'u16: raise new EInvalidValue + if currentByte > 255'u16: raise newException(EInvalidValue, "Invalid IP Address") seperatorValid = true elif address_str[i] == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: @@ -149,7 +149,7 @@ proc parseIPv4Address(address_str: string): TIpAddress = byteCount.inc seperatorValid = false else: - raise new EInvalidValue # Invalid character + raise newException(EInvalidValue, "Invalid IP Address") # Invalid character if byteCount != 3 or not seperatorValid: raise new EInvalidValue @@ -159,7 +159,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = ## Parses IPv6 adresses ## Raises EInvalidValue on errors result.family = IpAddressFamily.IPv6 - if address_str.len < 2: raise new EInvalidValue + if address_str.len < 2: raise newException(EInvalidValue, "Invalid IP Address") var groupCount = 0 @@ -173,26 +173,26 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str: if c == ':': - if not seperatorValid: raise new EInvalidValue + if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address") if lastWasColon: - if dualColonGroup != -1: raise new EInvalidValue + if dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address") dualColonGroup = groupCount seperatorValid = false elif i != 0 and i != high(address_str): - if groupCount >= 8: raise new EInvalidValue + if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) currentShort = 0 groupCount.inc() if dualColonGroup != -1: seperatorValid = false elif i == 0: # only valid if address starts with :: - if address_str[1] != ':': raise new EInvalidValue + if address_str[1] != ':': raise newException(EInvalidValue, "Invalid IP Address") else: # i == high(address_str) - only valid if address ends with :: - if address_str[high(address_str)-1] != ':': raise new EInvalidValue + if address_str[high(address_str)-1] != ':': raise newException(EInvalidValue, "Invalid IP Address") lastWasColon = true currentGroupStart = i + 1 elif c == '.': # Switch to parse IPv4 mode - if i < 3 or not seperatorValid or groupCount >= 7: raise new EInvalidValue + if i < 3 or not seperatorValid or groupCount >= 7: raise newException(EInvalidValue, "Invalid IP Address") v4StartPos = currentGroupStart currentShort = 0 seperatorValid = false @@ -204,16 +204,16 @@ proc parseIPv6Address(address_str: string): TIpAddress = currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10 else: # Upper case hex currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 - if currentShort > 65535'u32: raise new EInvalidValue + if currentShort > 65535'u32: raise newException(EInvalidValue, "Invalid IP Address") lastWasColon = false seperatorValid = true else: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff if seperatorValid: # Copy remaining data - if groupCount >= 8: raise new EInvalidValue + if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) groupCount.inc() @@ -221,7 +221,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str[v4StartPos..high(address_str)]: if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) - if currentShort > 255'u32: raise new EInvalidValue + if currentShort > 255'u32: raise newException(EInvalidValue, "Invalid IP Address") seperatorValid = true elif c == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: @@ -231,7 +231,7 @@ proc parseIPv6Address(address_str: string): TIpAddress = byteCount.inc() seperatorValid = false else: # Invalid character - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") if byteCount != 3 or not seperatorValid: raise new EInvalidValue @@ -240,23 +240,23 @@ proc parseIPv6Address(address_str: string): TIpAddress = # Shift and fill zeros in case of :: if groupCount > 8: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") elif groupCount < 8: # must fill - if dualColonGroup == -1: raise new EInvalidValue + if dualColonGroup == -1: raise newException(EInvalidValue, "Invalid IP Address") var toFill = 8 - groupCount # The number of groups to fill var toShift = groupCount - dualColonGroup # Nr of known groups after :: for i in 0..2*toShift-1: # shift result.address_v6[15-i] = result.address_v6[groupCount*2-i-1] for i in 0..2*toFill-1: # fill with 0s result.address_v6[dualColonGroup*2+i] = 0 - elif dualColonGroup != -1: raise new EInvalidValue + elif dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address") proc parseIpAddress*(address_str: string): TIpAddress = ## Parses an IP address ## Raises EInvalidValue on error if address_str == nil: - raise new EInvalidValue + raise newException(EInvalidValue, "IP Address string is nil") if address_str.contains(':'): return parseIPv6Address(address_str) else: From 4b09a897583a38bea144e86b8b99b1347b525280 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 9 Mar 2014 14:02:01 +0200 Subject: [PATCH 148/249] fix #966 test for ``map`` defined as inline iterator --- compiler/semcall.nim | 12 +++++++++--- tests/iter/titerable.nim | 26 ++++++++++++++++++++++++++ tests/overload/tissue966.nim | 12 ++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 tests/iter/titerable.nim create mode 100644 tests/overload/tissue966.nim diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 2e5def75a..5d480bc98 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) = # fail fast: globalError(n.info, errTypeMismatch, "") var result = msgKindToString(errTypeMismatch) - add(result, describeArgs(c, n, 1 + ord(nfDotField in n.flags))) + add(result, describeArgs(c, n, 1)) add(result, ')') var candidates = "" @@ -114,7 +114,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, var errors: seq[string] var usedSyms: seq[PNode] - + template pickBest(headSymbol: expr) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, filter, result, alt, errors) @@ -166,7 +166,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, n.sons[0..1] = [callOp, n[1], calleeName] orig.sons[0..1] = [callOp, orig[1], calleeName] pickBest(callOp) - + if overloadsState == csEmpty and result.state == csEmpty: localError(n.info, errUndeclaredIdentifier, considerAcc(f).s) return @@ -175,9 +175,15 @@ proc resolveOverloads(c: PContext, n, orig: PNode, localError(n.info, errExprXCannotBeCalled, renderTree(n, {renderNoComments})) else: + if {nfDotField, nfDotSetter} * n.flags != {}: + # clean up the inserted ops + n.sons.delete(2) + n.sons[0] = f + errors = @[] pickBest(f) notFoundError(c, n, errors) + return if alt.state == csMatch and cmpCandidates(result, alt) == 0 and diff --git a/tests/iter/titerable.nim b/tests/iter/titerable.nim new file mode 100644 index 000000000..3ec79f68d --- /dev/null +++ b/tests/iter/titerable.nim @@ -0,0 +1,26 @@ +discard """ + output: '''2 +4 +6 +4 +8 +12 +''' +""" + +iterator map[T, U](s: iterator:T{.inline.}, f: proc(x: T): U): U = + for e in s: yield f(e) + +template toSeq(s: expr): expr = + var res = newSeq[type(s)](0) + for e in s: res.add(e) + res + +var s1 = @[1, 2, 3] +for x in map(s1.items, proc (a:int): int = a*2): + echo x + +var s2 = toSeq(map(s1.items, proc (a:int): int = a*4)) +for x in s2: + echo x + diff --git a/tests/overload/tissue966.nim b/tests/overload/tissue966.nim new file mode 100644 index 000000000..53ec2f108 --- /dev/null +++ b/tests/overload/tissue966.nim @@ -0,0 +1,12 @@ +discard """ + msg: 'type mismatch: got (PTest)' +""" + +type + PTest = ref object + +proc test(x: PTest, y: int) = nil + +var buf: PTest +buf.test() + From a0975269564f2474953d2b12aaaab4ba71cec664 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 9 Mar 2014 13:49:38 +0000 Subject: [PATCH 149/249] Fixes to asyncio2 on Linux. --- lib/posix/epoll.nim | 2 +- lib/pure/asyncio2.nim | 28 ++++++++++++++-------------- lib/pure/selectors.nim | 26 ++++++++++++++++++-------- lib/pure/sockets2.nim | 22 ++++++++++++++++++++++ tests/async/tasyncawait.nim | 10 +++++++++- 5 files changed, 64 insertions(+), 24 deletions(-) diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index 366521551..57a2f001f 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -36,7 +36,7 @@ type epoll_data* {.importc: "union epoll_data", header: "", pure, final.} = object # TODO: This is actually a union. #thePtr* {.importc: "ptr".}: pointer - fd*: cint # \ + fd* {.importc: "fd".}: cint # \ #u32*: uint32 #u64*: uint64 diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 12d4cb5a3..60d489dda 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -473,7 +473,6 @@ else: proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) = assert sock in p.selector - echo("Update: ", events) if events == {}: discard p.selector.unregister(sock) else: @@ -499,23 +498,25 @@ else: for info in p.selector.select(timeout): let data = PData(info.key.data) assert data.sock == info.key.fd - echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events) if EvRead in info.events: - var newReadCBs: seq[TCallback] = @[] - for cb in data.readCBs: + # Callback may add items to ``data.readCBs`` which causes issues if + # we are iterating over ``data.readCBs`` at the same time. We therefore + # make a copy to iterate over. + let currentCBs = data.readCBs + data.readCBs = @[] + for cb in currentCBs: if not cb(data.sock): # Callback wants to be called again. - newReadCBs.add(cb) - data.readCBs = newReadCBs + data.readCBs.add(cb) if EvWrite in info.events: - var newWriteCBs: seq[TCallback] = @[] - for cb in data.writeCBs: + let currentCBs = data.writeCBs + data.writeCBs = @[] + for cb in currentCBs: if not cb(data.sock): # Callback wants to be called again. - newWriteCBs.add(cb) - data.writeCBs = newWriteCBs + data.writeCBs.add(cb) var newEvents: set[TEvent] if data.readCBs.len != 0: newEvents = {EvRead} @@ -615,7 +616,6 @@ else: retFuture.complete(0) addWrite(p, socket, cb) return retFuture - proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): PFuture[tuple[address: string, client: TSocketHandle]] = @@ -854,7 +854,7 @@ when isMainModule: sock.setBlocking false - when false: + when true: # Await tests proc main(p: PDispatcher): PFuture[int] {.async.} = discard await p.connect(sock, "irc.freenode.net", TPort(6667)) @@ -880,7 +880,7 @@ when isMainModule: else: - when false: + when true: var f = p.connect(sock, "irc.freenode.org", TPort(6667)) f.callback = @@ -919,4 +919,4 @@ when isMainModule: - \ No newline at end of file + diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 6482a01a6..e086ee3ab 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -10,11 +10,13 @@ # TODO: Docs. import tables, os, unsigned, hashes +import sockets2 when defined(linux): import posix, epoll elif defined(windows): import winlean proc hash*(x: TSocketHandle): THash {.borrow.} +proc `$`*(x: TSocketHandle): string {.borrow.} type TEvent* = enum @@ -31,7 +33,7 @@ when defined(linux) or defined(nimdoc): type PSelector* = ref object epollFD: cint - events: array[64, ptr epoll_event] + events: array[64, epoll_event] fds: TTable[TSocketHandle, PSelectorKey] proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event = @@ -66,17 +68,25 @@ when defined(linux) or defined(nimdoc): var event = createEventStruct(events, fd) s.fds[fd].events = events - echo("About to update") if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: + if OSLastError().cint == ENOENT: + # Socket has been closed. Epoll automatically removes disconnected + # sockets. + s.fds.del(fd) + osError("Socket has been disconnected") + OSError(OSLastError()) - echo("finished updating") result = s.fds[fd] proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = if not s.fds.hasKey(fd): raise newException(EInvalidValue, "File descriptor not found.") if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: - OSError(OSLastError()) + if osLastError().cint == ENOENT: + # Socket has been closed. Epoll automatically removes disconnected + # sockets so its already been removed. + else: + OSError(OSLastError()) result = s.fds[fd] s.fds.del(fd) @@ -92,21 +102,21 @@ when defined(linux) or defined(nimdoc): ## on the ``fd``. result = @[] - let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint) + let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint) if evNum < 0: OSError(OSLastError()) if evNum == 0: return @[] for i in 0 .. Date: Sun, 9 Mar 2014 16:04:58 +0200 Subject: [PATCH 150/249] few more fixes for static params in macros; new failing test cases for static evaluation --- compiler/semtypes.nim | 25 +++++++++++++------ tests/vm/tstaticprintseq.nim | 48 ++++++++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index dd73cf01a..299a3a9b9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -602,15 +602,24 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = incl(result.flags, tfFinal) proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = - if kind == skMacro and param.typ.kind notin {tyTypeDesc, tyStatic}: - # within a macro, every param has the type PNimrodNode! - # and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}: - let nn = getSysSym"PNimrodNode" - var a = copySym(param) - a.typ = nn.typ - if sfGenSym notin a.flags: addDecl(c, a) + template addDecl(x) = + if sfGenSym notin x.flags: addDecl(c, x) + + if kind == skMacro: + if param.typ.kind == tyTypeDesc: + addDecl(param) + elif param.typ.kind == tyStatic: + var a = copySym(param) + a.typ = param.typ.base + addDecl(a) + else: + # within a macro, every param has the type PNimrodNode! + let nn = getSysSym"PNimrodNode" + var a = copySym(param) + a.typ = nn.typ + addDecl(a) else: - if sfGenSym notin param.flags: addDecl(c, param) + addDecl(param) let typedescId = getIdent"typedesc" diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim index 99a56d161..f7ed1e2bb 100644 --- a/tests/vm/tstaticprintseq.nim +++ b/tests/vm/tstaticprintseq.nim @@ -4,18 +4,52 @@ discard """ 3 1 2 +3 +1 +2 +3 +1 +2 3''' """ -const s = @[1,2,3] +when false: + const s = @[1,2,3] -macro foo: stmt = - for e in s: - echo e + macro foo: stmt = + for e in s: + echo e -foo() + foo() + + static: + for e in s: + echo e + + macro bar(x: static[seq[int]]): stmt = + for e in x: + echo e + + bar s + bar(@[1, 2, 3]) + +type + TData = tuple + letters: seq[string] + numbers: seq[int] + +const data: TData = (@["aa", "bb"], @[11, 22]) static: - for e in s: - echo e + for x in data.letters: + echo x + var m = data + for x in m.letters: + echo x + +macro ff(d: static[TData]): stmt = + for x in d.letters: + echo x + +ff(data) From eebe0035b5d16d82a3806ff6c3d29935e771226b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 9 Mar 2014 17:06:29 +0200 Subject: [PATCH 151/249] teach opcLdObj about nkExprColonExpr --- compiler/vm.nim | 4 ++- tests/static/tstaticparammacro.nim | 52 ++++++++++++++++++++++++++++++ tests/vm/tstaticprintseq.nim | 36 ++++++++++----------- 3 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 tests/static/tstaticparammacro.nim diff --git a/compiler/vm.nim b/compiler/vm.nim index f9b143bce..2a80106a5 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -425,7 +425,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkNode) let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit}: - regs[ra].node = src.sons[rc] + let n = src.sons[rc] + regs[ra].node = if n.kind == nkExprColonExpr: n[1] + else: n else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrObj: diff --git a/tests/static/tstaticparammacro.nim b/tests/static/tstaticparammacro.nim new file mode 100644 index 000000000..7fb9e2014 --- /dev/null +++ b/tests/static/tstaticparammacro.nim @@ -0,0 +1,52 @@ +discard """ + msg: '''letters +aa +bb +numbers +11 +22 +AST a +[(11, 22), (33, 44)] +AST b +(e: [55, 66], f: [77, 88]) +55 +''' +""" + +import macros + +type + TConfig = tuple + letters: seq[string] + numbers:seq[int] + +const data: Tconfig = (@["aa", "bb"], @[11, 22]) + +macro mymacro(data: static[TConfig]): stmt = + echo "letters" + for s in items(data.letters): + echo s + echo "numbers" + for n in items(data.numbers): + echo n + +mymacro(data) + +type + Ta = seq[tuple[c:int, d:int]] + Tb = tuple[e:seq[int], f:seq[int]] + +const + a : Ta = @[(11, 22), (33, 44)] + b : Tb = (@[55,66], @[77, 88]) + +macro mA(data: static[Ta]): stmt = + echo "AST a \n", repr(data) + +macro mB(data: static[Tb]): stmt = + echo "AST b \n", repr(data) + echo data.e[0] + +mA(a) +mB(b) + diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim index f7ed1e2bb..4575f3af1 100644 --- a/tests/vm/tstaticprintseq.nim +++ b/tests/vm/tstaticprintseq.nim @@ -10,28 +10,31 @@ discard """ 3 1 2 -3''' +3 +aa +bb +aa +bb''' """ -when false: - const s = @[1,2,3] +const s = @[1,2,3] - macro foo: stmt = - for e in s: - echo e +macro foo: stmt = + for e in s: + echo e - foo() +foo() - static: - for e in s: - echo e +static: + for e in s: + echo e - macro bar(x: static[seq[int]]): stmt = - for e in x: - echo e +macro bar(x: static[seq[int]]): stmt = + for e in x: + echo e - bar s - bar(@[1, 2, 3]) +bar s +bar(@[1, 2, 3]) type TData = tuple @@ -41,9 +44,6 @@ type const data: TData = (@["aa", "bb"], @[11, 22]) static: - for x in data.letters: - echo x - var m = data for x in m.letters: echo x From 752352a6834dad94e0687b2518240e8425e6bcac Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 9 Mar 2014 20:47:05 +0200 Subject: [PATCH 152/249] fix #949 --- compiler/semexprs.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c16ab9b87..d07d695da 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1951,7 +1951,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkBracketExpr: checkMinSonsLen(n, 1) var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared}) - if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators: + if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or + n[0].kind in nkSymChoices: # type parameters: partial generic specialization n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) result = explicitGenericInstantiation(c, n, s) From 29b7104a5330ebcb188243a271b4409e8613837b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 9 Mar 2014 21:04:12 +0200 Subject: [PATCH 153/249] fix #829; (macros.quote) the fix is a little bit peculiar: the inserted call to getAst was getting a false-positive for being a lambda proc, because lambdalifting.isInnerProc takes into account who is the owner of a given symbol: (a nested proc is a callable symbol owned by the enclosing proc) --- compiler/semexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d07d695da..77ac98db7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1358,7 +1358,7 @@ proc expectString(c: PContext, n: PNode): string = localError(n.info, errStringLiteralExpected) proc getMagicSym(magic: TMagic): PSym = - result = newSym(skProc, getIdent($magic), getCurrOwner(), gCodegenLineInfo) + result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo) result.magic = magic proc newAnonSym(kind: TSymKind, info: TLineInfo, From 3dcf735482bed528f80614dca7a096f5e1a3aaa4 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 9 Mar 2014 22:15:11 +0200 Subject: [PATCH 154/249] fix #909 --- compiler/sem.nim | 4 ++++ compiler/semtempl.nim | 4 +++- tests/template/tissue909.nim | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/template/tissue909.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index 5ee46654e..ed4c4b093 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -139,6 +139,10 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = result = n.sym internalAssert sfGenSym in result.flags internalAssert result.kind == kind + # when there is a nested proc inside a template, semtmpl + # will assign a wrong owner during the first pass over the + # template; we must fix it here: see #909 + result.owner = getCurrOwner() else: result = newSym(kind, considerAcc(n), getCurrOwner(), n.info) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 31624a97f..42268d44f 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -348,7 +348,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkMethodDef: result = semRoutineInTemplBody(c, n, skMethod) of nkIteratorDef: - result = semRoutineInTemplBody(c, n, n[namePos].sym.kind) + let kind = if hasPragma(n[pragmasPos], wClosure): skClosureIterator + else: skIterator + result = semRoutineInTemplBody(c, n, kind) of nkTemplateDef: result = semRoutineInTemplBody(c, n, skTemplate) of nkMacroDef: diff --git a/tests/template/tissue909.nim b/tests/template/tissue909.nim new file mode 100644 index 000000000..5b57a3558 --- /dev/null +++ b/tests/template/tissue909.nim @@ -0,0 +1,16 @@ +import macros + +template baz() = + proc bar() = + var x = 5 + iterator foo(): int {.closure.} = + echo x + var y = foo + discard y() + +macro test(): stmt = + result = getAst(baz()) + echo(treeRepr(result)) + +test() +bar() From 45a345e93da9d80d5a7f2aaa5903e9f75c0bf2d6 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 9 Mar 2014 23:38:17 +0200 Subject: [PATCH 155/249] fix #866; generic static params --- compiler/ast.nim | 4 ++++ compiler/astalgo.nim | 8 +++++++- compiler/guards.nim | 4 ---- compiler/semtypes.nim | 35 ++++++++++++++++++++++------------- doc/manual.txt | 4 ++-- 5 files changed, 35 insertions(+), 20 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 9c9dfce9a..f9cab0d88 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -818,6 +818,9 @@ type counter*: int data*: TObjectSeq + TImplication* = enum + impUnknown, impNo, impYes + # BUGFIX: a module is overloadable so that a proc can have the # same name as an imported module. This is necessary because of # the poor naming choices in the standard library. @@ -865,6 +868,7 @@ const nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit, nkHiddenCallConv} + nkLiterals* = {nkCharLit..nkTripleStrLit} nkLambdaKinds* = {nkLambda, nkDo} declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef} procDefs* = nkLambdaKinds + declarativeDefs diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index cce2cc215..36dd7f562 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -50,7 +50,7 @@ proc strTableGet*(t: TStrTable, name: PIdent): PSym type TTabIter*{.final.} = object # consider all fields here private h*: THash # current hash - + proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym proc nextIter*(ti: var TTabIter, tab: TStrTable): PSym # usage: @@ -157,6 +157,12 @@ proc leValue*(a, b: PNode): bool = #InternalError(a.info, "leValue") discard +proc weakLeValue*(a, b: PNode): TImplication = + if a.kind notin nkLiterals or b.kind notin nkLiterals: + result = impUnknown + else: + result = if leValue(a, b): impYes else: impNo + proc lookupInRecord(n: PNode, field: PIdent): PSym = result = nil case n.kind diff --git a/compiler/guards.nim b/compiler/guards.nim index fe868054f..f475f5068 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -274,10 +274,6 @@ proc pred(n: PNode): PNode = else: result = n -type - TImplication* = enum - impUnknown, impNo, impYes - proc impliesEq(fact, eq: PNode): TImplication = let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 299a3a9b9..c441c3c44 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -147,26 +147,35 @@ proc semDistinct(c: PContext, n: PNode, prev: PType): PType = else: result = newConstraint(c, tyDistinct) -proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = +proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = assert isRange(n) checkSonsLen(n, 3) result = newOrPrevType(tyRange, prev, c) result.n = newNodeI(nkRange, n.info) - if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): + if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): localError(n.info, errRangeIsEmpty) - var a = semConstExpr(c, n[1]) - var b = semConstExpr(c, n[2]) - if not sameType(a.typ, b.typ): + + var imm: array[2, PNode] + imm[0] = semExprWithType(c, n[1], {efDetermineType}) + imm[1] = semExprWithType(c, n[2], {efDetermineType}) + + if not sameType(imm[0].typ, imm[1].typ): localError(n.info, errPureTypeMismatch) - elif a.typ.kind notin {tyInt..tyInt64,tyEnum,tyBool,tyChar, - tyFloat..tyFloat128,tyUInt8..tyUInt32}: + elif not imm[0].typ.isOrdinalType: localError(n.info, errOrdinalTypeExpected) - elif enumHasHoles(a.typ): - localError(n.info, errEnumXHasHoles, a.typ.sym.name.s) - elif not leValue(a, b): localError(n.info, errRangeIsEmpty) - addSon(result.n, a) - addSon(result.n, b) - addSonSkipIntLit(result, b.typ) + elif enumHasHoles(imm[0].typ): + localError(n.info, errEnumXHasHoles, imm[0].typ.sym.name.s) + + for i in 0..1: + if hasGenericArguments(imm[i]): + result.n.addSon makeStaticExpr(c, imm[i]) + else: + result.n.addSon semConstExpr(c, imm[i]) + + if weakLeValue(result.n[0], result.n[1]) == impNo: + localError(n.info, errRangeIsEmpty) + + addSonSkipIntLit(result, imm[0].typ) proc semRange(c: PContext, n: PNode, prev: PType): PType = result = nil diff --git a/doc/manual.txt b/doc/manual.txt index 748d41d4b..c85998741 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -4062,8 +4062,8 @@ Static params can also appear in the signatures of generic types: AffineTransform2D[T] = Matrix[3, 3, T] AffineTransform3D[T] = Matrix[4, 4, T] - AffineTransform3D[float] # OK - AffineTransform2D[string] # Error, `string` is not a `Number` + var m1: AffineTransform3D[float] # OK + var m2: AffineTransform2D[string] # Error, `string` is not a `Number` typedesc From 9fb07555721d53139f7275c7f8f7143f5c3c8a01 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 10 Mar 2014 00:29:15 +0200 Subject: [PATCH 156/249] further improvements for static generic params; activate the hardest test cases --- compiler/sem.nim | 3 ++- compiler/semtypes.nim | 25 ++++++++++++++----------- tests/generics/tgenericshardcases.nim | 18 ++++++++++++------ tests/metatype/tstaticparams.nim | 22 +++++++++++++++++----- 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index ed4c4b093..c8228618b 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -198,7 +198,8 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = result = semExprWithType(c, evaluated) else: result = evaluated - semmacrosanity.annotateType(result, eOrig.typ) + let expectedType = eOrig.typ.skipTypes({tyStatic}) + semmacrosanity.annotateType(result, expectedType) else: result = semExprWithType(c, evaluated) #result = fitNode(c, e.typ, result) inlined with special case: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index c441c3c44..f91222477 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -155,27 +155,30 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): localError(n.info, errRangeIsEmpty) - var imm: array[2, PNode] - imm[0] = semExprWithType(c, n[1], {efDetermineType}) - imm[1] = semExprWithType(c, n[2], {efDetermineType}) + var range: array[2, PNode] + range[0] = semExprWithType(c, n[1], {efDetermineType}) + range[1] = semExprWithType(c, n[2], {efDetermineType}) - if not sameType(imm[0].typ, imm[1].typ): + var rangeT: array[2, PType] + for i in 0..1: rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit + + if not sameType(rangeT[0], rangeT[1]): localError(n.info, errPureTypeMismatch) - elif not imm[0].typ.isOrdinalType: + elif not rangeT[0].isOrdinalType: localError(n.info, errOrdinalTypeExpected) - elif enumHasHoles(imm[0].typ): - localError(n.info, errEnumXHasHoles, imm[0].typ.sym.name.s) + elif enumHasHoles(rangeT[0]): + localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s) for i in 0..1: - if hasGenericArguments(imm[i]): - result.n.addSon makeStaticExpr(c, imm[i]) + if hasGenericArguments(range[i]): + result.n.addSon makeStaticExpr(c, range[i]) else: - result.n.addSon semConstExpr(c, imm[i]) + result.n.addSon semConstExpr(c, range[i]) if weakLeValue(result.n[0], result.n[1]) == impNo: localError(n.info, errRangeIsEmpty) - addSonSkipIntLit(result, imm[0].typ) + addSonSkipIntLit(result, rangeT[0]) proc semRange(c: PContext, n: PNode, prev: PType): PType = result = nil diff --git a/tests/generics/tgenericshardcases.nim b/tests/generics/tgenericshardcases.nim index 2ef63bc20..e3b805db6 100644 --- a/tests/generics/tgenericshardcases.nim +++ b/tests/generics/tgenericshardcases.nim @@ -14,7 +14,8 @@ macro selectType(a, b: typedesc): typedesc = type Foo[T] = object data1: array[T.high, int] - data2: array[typeNameLen(T), float] # data3: array[0..T.typeNameLen, selectType(float, int)] + data2: array[typeNameLen(T), float] + data3: array[0..T.typeNameLen, selectType(float, int)] MyEnum = enum A, B, C, D @@ -27,10 +28,15 @@ echo high(f1.data2) # (MyEnum.len = 6) - 1 == 5 echo high(f2.data1) # 127 - 1 == 126 echo high(f2.data2) # int8.len - 1 == 3 -#static: -# assert high(f1.data1) == ord(D) -# assert high(f1.data2) == 6 # length of MyEnum +static: + assert high(f1.data1) == ord(C) + assert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high -# assert high(f2.data1) == 127 -# assert high(f2.data2) == 4 # length of int8 + assert high(f2.data1) == 126 + assert high(f2.data2) == 3 + + assert high(f1.data3) == 6 # length of MyEnum + assert high(f2.data3) == 4 # length of int8 + + assert f2.data3[0] is float diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim index b1377443b..0597325c3 100644 --- a/tests/metatype/tstaticparams.nim +++ b/tests/metatype/tstaticparams.nim @@ -1,6 +1,6 @@ discard """ file: "tstaticparams.nim" - output: "abracadabra\ntest\n3" + output: "abracadabra\ntest\n3\n15" """ type @@ -11,8 +11,8 @@ type data: array[I, T] TA1[T; I: static[int]] = array[I, T] - # TA2[T; I: static[int]] = array[0..I, T] - # TA3[T; I: static[int]] = array[I-1, T] + TA2[T; I: static[int]] = array[0..I, T] + TA3[T; I: static[int]] = array[I-1, T] proc takeFoo(x: TFoo) = echo "abracadabra" @@ -26,6 +26,18 @@ echo high(y.data) var t1: TA1[float, 1] - # t2: TA2[string, 4] - # t3: TA3[int, 10] + t2: TA2[string, 4] + t3: TA3[int, 10] + +# example from the manual: +type + Matrix[M,N: static[int]; T] = array[0..(M*N - 1), T] + # Note how `Number` is just a type constraint here, while + # `static[int]` requires us to supply a compile-time int value + + AffineTransform2D[T] = Matrix[3, 3, T] + AffineTransform3D[T] = Matrix[4, 4, T] + +var m: AffineTransform3D[float] +echo high(m) From d5798b43dec547f372eb49d5a8848a9970b12260 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 10 Mar 2014 01:57:08 +0200 Subject: [PATCH 157/249] fix #986 --- compiler/semexprs.nim | 34 ++++++++++++++++++++------------ compiler/semtypinst.nim | 4 +++- tests/metatype/tstaticparams.nim | 6 +++++- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 77ac98db7..f2c5e2854 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -885,7 +885,7 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent, of nkSym: if r.sym.name.id == field.id: result = r.sym else: illFormedAst(n) - + proc makeDeref(n: PNode): PNode = var t = skipTypes(n.typ, {tyGenericInst}) result = n @@ -899,6 +899,21 @@ proc makeDeref(n: PNode): PNode = addSon(result, a) t = skipTypes(t.sons[0], {tyGenericInst}) +proc readTypeParameter(c: PContext, ty: PType, + paramName: PIdent, info: TLineInfo): PNode = + internalAssert ty.kind == tyGenericInst + let ty = ty.skipGenericAlias + let tbody = ty.sons[0] + for s in countup(0, tbody.len-2): + let tParam = tbody.sons[s] + if tParam.sym.name == paramName: + let rawTyp = ty.sons[s + 1] + if rawTyp.kind == tyStatic: + return rawTyp.n + else: + let foundTyp = makeTypeDesc(c, rawTyp) + return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info) + proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if it's not a built-in field access checkSonsLen(n, 2) @@ -932,18 +947,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = markUsed(n, f) return of tyGenericInst: - assert ty.sons[0].kind == tyGenericBody - let tbody = ty.sons[0] - for s in countup(0, tbody.len-2): - let tParam = tbody.sons[s] - if tParam.sym.name == i: - let rawTyp = ty.sons[s + 1] - if rawTyp.kind == tyStatic: - return rawTyp.n - else: - let foundTyp = makeTypeDesc(c, rawTyp) - return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info) - return + return readTypeParameter(c, ty, i, n.info) of tyObject, tyTuple: if ty.n.kind == nkRecList: for field in ty.n.sons: @@ -990,6 +994,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = n.typ = f.typ result = n + # we didn't find any field, let's look for a generic param + if result == nil and n.sons[0].typ.kind == tyGenericInst: + result = readTypeParameter(c, n.sons[0].typ, i, n.info) + proc dotTransformation(c: PContext, n: PNode): PNode = if isSymChoice(n.sons[1]): result = newNodeI(nkDotCall, n.info) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 46ec31d90..4229f4797 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -181,7 +181,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode = of nkStaticExpr: var n = prepareNode(cl, n) n = reResolveCallsWithTypedescParams(cl, n) - result = cl.c.semExpr(cl.c, n) + result = if cl.allowMetaTypes: n + else: cl.c.semExpr(cl.c, n) else: var length = sonsLen(n) if length > 0: @@ -341,6 +342,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = replaceTypeVarsT(cl, lastSon(t)) of tyFromExpr: + if cl.allowMetaTypes: return var n = prepareNode(cl, t.n) n = cl.c.semConstExpr(cl.c, n) if n.typ.kind == tyTypeDesc: diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim index 0597325c3..e76dae23c 100644 --- a/tests/metatype/tstaticparams.nim +++ b/tests/metatype/tstaticparams.nim @@ -1,6 +1,6 @@ discard """ file: "tstaticparams.nim" - output: "abracadabra\ntest\n3\n15" + output: "abracadabra\ntest\n3\n15\4" """ type @@ -41,3 +41,7 @@ type var m: AffineTransform3D[float] echo high(m) +proc getRows(mtx: Matrix): int = + result = mtx.M + +echo getRows(m) From d4714ed14ee6bc2b2c58dc3e6a60246a76d4132f Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Mar 2014 08:46:41 +0100 Subject: [PATCH 158/249] minor additions to the manual --- doc/manual.txt | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index 8c8db7658..04c69cf94 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -79,8 +79,21 @@ program execution. Unless explicitly classified, an error is a static error. A `checked runtime error`:idx: is an error that the implementation detects and reports at runtime. The method for reporting such errors is via *raising -exceptions*. However, the implementation provides a means to disable these -runtime checks. See the section pragmas_ for details. +exceptions* or *dying with a fatal error*. However, the implementation +provides a means to disable these runtime checks. See the section pragmas_ +for details. + +Wether a checked runtime error results in an exception or in a fatal error at +runtime is implementation specific. Thus the following program is always +invalid: + +.. code-block:: nimrod + var a: array[0..1, char] + let i = 5 + try: + a[i] = 'N' + except EInvalidIndex: + echo "invalid index" An `unchecked runtime error`:idx: is an error that is not guaranteed to be detected, and can cause the subsequent behavior of the computation to @@ -522,7 +535,7 @@ Strong spaces The number of spaces preceeding a non-keyword operator affects precedence if the experimental parser directive ``#!strongSpaces`` is used. Indentation is not used to determine the number of spaces. If 2 or more operators have the -same number of preceeding spaces the precedence table applies, so ``1 + 3 * 4`` +same number of preceding spaces the precedence table applies, so ``1 + 3 * 4`` is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``: .. code-block:: nimrod @@ -3481,7 +3494,7 @@ Declarative type classes are written in the following form: c.len is ordinal items(c) is iterator for value in c: - value.type is T + type(value) is T The type class will be matched if: From 85fe5e1940efbde41ac88113e8cc6a954b0414b9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 10 Mar 2014 13:04:22 +0200 Subject: [PATCH 159/249] fix #988 trivial crash caused due to unchecked iteration over an empty reclist --- compiler/semexprs.nim | 7 ++++--- compiler/semtypinst.nim | 5 ++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f2c5e2854..62a816a50 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -931,7 +931,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = var ty = n.sons[0].typ var f: PSym = nil result = nil - if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.base.kind != tyNone: + if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): if ty.kind == tyTypeDesc: ty = ty.base case ty.kind of tyEnum: @@ -940,7 +940,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = f = getSymFromList(ty.n, i) if f != nil: break ty = ty.sons[0] # enum inheritance - if f != nil: + if f != nil: result = newSymNode(f) result.info = n.info result.typ = ty @@ -950,7 +950,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = return readTypeParameter(c, ty, i, n.info) of tyObject, tyTuple: if ty.n.kind == nkRecList: - for field in ty.n.sons: + for field in ty.n: if field.sym.name == i: n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ]) n.typ.n = copyTree(n) @@ -962,6 +962,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # XXX: This is probably not relevant any more # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": ty = n.sons[0].typ + return nil ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef}) var check: PNode = nil diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 4229f4797..80e2aa284 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -320,9 +320,8 @@ proc propagateFieldFlags(t: PType, n: PNode) = of nkSym: propagateToOwner(t, n.sym.typ) of nkRecList, nkRecCase, nkOfBranch, nkElse: - if n.sons != nil: - for son in n.sons: - propagateFieldFlags(t, son) + for son in n: + propagateFieldFlags(t, son) else: discard proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = From 1546d210c5739213ebf93eb0cb624ca7a9fe9ad6 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 10 Mar 2014 18:13:48 +0200 Subject: [PATCH 160/249] fix #971 --- lib/nimbase.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/nimbase.h b/lib/nimbase.h index 1100e084b..b16b27b2c 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -314,6 +314,9 @@ static unsigned long nimNaN[2]={0xffffffff, 0x7fffffff}; # define INF INFINITY # elif defined(HUGE_VAL) # define INF HUGE_VAL +# elif defined(_MSC_VER) +# include +# define INF (DBL_MAX+DBL_MAX) # else # define INF (1.0 / 0.0) # endif From 121553d1a69c4d175259944f6bdc9f9dcfda9cdd Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Mar 2014 17:32:50 +0100 Subject: [PATCH 161/249] osproc compiles again for haiku --- lib/pure/osproc.nim | 85 ++++++++++++++++++---------------- tests/parser/tstrongspaces.nim | 52 +++++++++++++++++++++ todo.txt | 1 - 3 files changed, 96 insertions(+), 42 deletions(-) create mode 100644 tests/parser/tstrongspaces.nim diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 582b3c960..5d6848565 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -606,7 +606,8 @@ elif not defined(useNimRtl): optionPoParentStreams: bool optionPoStdErrToStdOut: bool - proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} + when not defined(useFork): + proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].} {.push stacktrace: off, profiler: off.} proc startProcessAfterFork(data: ptr TStartProcessData) {. @@ -664,7 +665,8 @@ elif not defined(useNimRtl): data.workingDir = workingDir - when defined(posix_spawn) and not defined(useFork) and not defined(useClone) and not defined(linux): + when defined(posix_spawn) and not defined(useFork) and + not defined(useClone) and not defined(linux): pid = startProcessAuxSpawn(data) else: pid = startProcessAuxFork(data) @@ -694,55 +696,56 @@ elif not defined(useNimRtl): discard close(pStdin[readIdx]) discard close(pStdout[writeIdx]) - proc startProcessAuxSpawn(data: TStartProcessData): TPid = - var attr: Tposix_spawnattr - var fops: Tposix_spawn_file_actions + when not defined(useFork): + proc startProcessAuxSpawn(data: TStartProcessData): TPid = + var attr: Tposix_spawnattr + var fops: Tposix_spawn_file_actions - template chck(e: expr) = - if e != 0'i32: osError(osLastError()) + template chck(e: expr) = + if e != 0'i32: osError(osLastError()) - chck posix_spawn_file_actions_init(fops) - chck posix_spawnattr_init(attr) + chck posix_spawn_file_actions_init(fops) + chck posix_spawnattr_init(attr) - var mask: Tsigset - chck sigemptyset(mask) - chck posix_spawnattr_setsigmask(attr, mask) - chck posix_spawnattr_setpgroup(attr, 0'i32) + var mask: Tsigset + chck sigemptyset(mask) + chck posix_spawnattr_setsigmask(attr, mask) + chck posix_spawnattr_setpgroup(attr, 0'i32) - chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or - POSIX_SPAWN_SETSIGMASK or - POSIX_SPAWN_SETPGROUP) + chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or + POSIX_SPAWN_SETSIGMASK or + POSIX_SPAWN_SETPGROUP) - if not data.optionPoParentStreams: - chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx]) - chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx) - chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx]) - chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx) - chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx]) - if data.optionPoStdErrToStdOut: - chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2) + if not data.optionPoParentStreams: + chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx]) + chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx) + chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx]) + chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx) + chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx]) + if data.optionPoStdErrToStdOut: + chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2) + else: + chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2) + + var res: cint + # FIXME: chdir is global to process + if data.workingDir.len > 0: + setCurrentDir($data.workingDir) + var pid: TPid + + if data.optionPoUsePath: + res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) else: - chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2) + res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) - var res: cint - # FIXME: chdir is global to process - if data.workingDir.len > 0: - setCurrentDir($data.workingDir) - var pid: TPid - - if data.optionPoUsePath: - res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) - else: - res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) - - discard posix_spawn_file_actions_destroy(fops) - discard posix_spawnattr_destroy(attr) - chck res - return pid + discard posix_spawn_file_actions_destroy(fops) + discard posix_spawnattr_destroy(attr) + chck res + return pid proc startProcessAuxFork(data: TStartProcessData): TPid = if pipe(data.pErrorPipe) != 0: - osError(osLastError()) + osError(osLastError()) finally: discard close(data.pErrorPipe[readIdx]) diff --git a/tests/parser/tstrongspaces.nim b/tests/parser/tstrongspaces.nim new file mode 100644 index 000000000..91506daf0 --- /dev/null +++ b/tests/parser/tstrongspaces.nim @@ -0,0 +1,52 @@ +#! strongSpaces + +discard """ + output: '''35 +77 +(Field0: 1, Field1: 2, Field2: 2) +ha +true +tester args +all +all args +''' +""" + +echo 2+5 * 5 + +let foo = 77 +echo $foo + +echo (1, 2, 2) + +template `&`(a, b: int): expr = a and b +template `|`(a, b: int): expr = a - b +template `++`(a, b: int): expr = a + b == 8009 + +when true: + let b = 66 + let c = 90 + let bar = 8000 + if foo+4 * 4 == 8 and b&c | 9 ++ + bar: + echo "ho" + else: + echo "ha" + + let booA = foo+4 * 4 - b&c | 9 + + bar + # is parsed as + let booB = ((foo+4)*4) - ((b&c) | 9) + bar + + echo booA == booB + + +template `|`(a, b): expr = (if a.len > 0: a else: b) + +const + tester = "tester" + args = "args" + +echo tester & " " & args|"all" +echo "all" | tester & " " & args +echo "all"|tester & " " & args diff --git a/todo.txt b/todo.txt index 51f883d1d..3b085ac91 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ version 0.9.4 ============= -- make testament produce full JSON information - fix gensym capture bug - vm - at least try to get the basic type zoo ops right From 71695ab791687302d4fdd7726616c348f1de7be8 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 10 Mar 2014 20:58:45 +0200 Subject: [PATCH 162/249] fix tsemistatic --- compiler/ast.nim | 5 +---- compiler/msgs.nim | 8 ++++---- compiler/sem.nim | 18 +++++++++++++++--- compiler/semdata.nim | 3 +++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index f9cab0d88..ee0d55920 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1291,7 +1291,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType = proc propagateToOwner*(owner, elem: PType) = const HaveTheirOwnEmpty = {tySequence, tySet} owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta, - tfHasStatic, tfHasGCedMem}) + tfHasGCedMem}) if tfNotNil in elem.flags: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvokation}: owner.flags.incl tfNotNil @@ -1308,9 +1308,6 @@ proc propagateToOwner*(owner, elem: PType) = if elem.kind in tyMetaTypes: owner.flags.incl tfHasMeta - if elem.kind == tyStatic: - owner.flags.incl tfHasStatic - if elem.kind in {tyString, tyRef, tySequence} or elem.kind == tyProc and elem.callConv == ccClosure: owner.flags.incl tfHasGCedMem diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 5bc490d14..c75876843 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -719,19 +719,19 @@ proc handleError(msg: TMsgKind, eh: TErrorHandling, s: string) = writeStackTrace() quit 1 - if msg >= fatalMin and msg <= fatalMax: + if msg >= fatalMin and msg <= fatalMax: quit() - if msg >= errMin and msg <= errMax: + if msg >= errMin and msg <= errMax: inc(gErrorCounter) options.gExitcode = 1'i8 - if gErrorCounter >= gErrorMax: + if gErrorCounter >= gErrorMax: quit() elif eh == doAbort and gCmd != cmdIdeTools: quit() elif eh == doRaise: raiseRecoverableError(s) -proc `==`*(a, b: TLineInfo): bool = +proc `==`*(a, b: TLineInfo): bool = result = a.line == b.line and a.fileIndex == b.fileIndex proc writeContext(lastinfo: TLineInfo) = diff --git a/compiler/sem.nim b/compiler/sem.nim index c8228618b..093fc9452 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -219,14 +219,26 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = result = getConstExpr(c.module, e) if result != nil: return + let oldErrorCount = msgs.gErrorCounter + let oldErrorMax = msgs.gErrorMax + let oldErrorOutputs = errorOutputs + + errorOutputs = {} + msgs.gErrorMax = high(int) + try: result = evalConstExpr(c.module, e) if result == nil or result.kind == nkEmpty: - return nil + result = nil + else: + result = fixupTypeAfterEval(c, result, e) - result = fixupTypeAfterEval(c, result, e) except ERecoverableError: - return nil + result = nil + + msgs.gErrorCounter = oldErrorCount + msgs.gErrorMax = oldErrorMax + errorOutputs = oldErrorOutputs proc semConstExpr(c: PContext, n: PNode): PNode = var e = semExprWithType(c, n) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index d942aa41e..84e017050 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -236,17 +236,20 @@ proc makeAndType*(c: PContext, t1, t2: PType): PType = result.sons = @[t1, t2] propagateToOwner(result, t1) propagateToOwner(result, t2) + result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) proc makeOrType*(c: PContext, t1, t2: PType): PType = result = newTypeS(tyOr, c) result.sons = @[t1, t2] propagateToOwner(result, t1) propagateToOwner(result, t2) + result.flags.incl((t1.flags + t2.flags) * {tfHasStatic}) proc makeNotType*(c: PContext, t1: PType): PType = result = newTypeS(tyNot, c) result.sons = @[t1] propagateToOwner(result, t1) + result.flags.incl(t1.flags * {tfHasStatic}) proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) From 2ce07042fdbe7104694245b56006204ade9df34a Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 11 Mar 2014 21:53:35 +0000 Subject: [PATCH 163/249] tasyncawait now works on Linux. Reworked detection of a file descriptor being closed with epoll (in the case of sockets it is when the remote host disconnects). Ensured that events are only updated when they change. --- lib/pure/asyncio2.nim | 40 +++++++++++++++++------ lib/pure/selectors.nim | 65 ++++++++++++++++++++++--------------- tests/async/tasyncawait.nim | 9 +---- 3 files changed, 69 insertions(+), 45 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 60d489dda..cdb2b3a89 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -479,9 +479,11 @@ else: discard p.selector.update(sock, events) proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = + #echo("addRead") if sock notin p.selector: var data = PData(sock: sock, readCBs: @[cb], writeCBs: @[]) p.selector.register(sock, {EvRead}, data.PObject) + #echo("registered") else: p.selector[sock].data.PData.readCBs.add(cb) p.update(sock, p.selector[sock].events + {EvRead}) @@ -498,7 +500,7 @@ else: for info in p.selector.select(timeout): let data = PData(info.key.data) assert data.sock == info.key.fd - + #echo("In poll ", data.sock.cint) if EvRead in info.events: # Callback may add items to ``data.readCBs`` which causes issues if # we are iterating over ``data.readCBs`` at the same time. We therefore @@ -517,11 +519,17 @@ else: if not cb(data.sock): # Callback wants to be called again. data.writeCBs.add(cb) - - var newEvents: set[TEvent] - if data.readCBs.len != 0: newEvents = {EvRead} - if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite} - p.update(data.sock, newEvents) + + if info.key in p.selector: + var newEvents: set[TEvent] + if data.readCBs.len != 0: newEvents = {EvRead} + if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite} + if newEvents != info.key.events: + echo(info.key.events, " -> ", newEvents) + p.update(data.sock, newEvents) + else: + # FD no longer a part of the selector. Likely been closed + # (e.g. socket disconnected). proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, af = AF_INET): PFuture[int] = @@ -569,6 +577,7 @@ else: result = true let netSize = size - sizeRead let res = recv(sock, addr readBuffer[sizeRead], netSize, flags.cint) + #echo("recv cb res: ", res) if res < 0: let lastError = osLastError() if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: @@ -576,6 +585,7 @@ else: else: result = false # We still want this callback to be called. elif res == 0: + #echo("Disconnected recv: ", sizeRead) # Disconnected if sizeRead == 0: retFuture.complete("") @@ -588,6 +598,7 @@ else: result = false # We want to read all the data requested. else: retFuture.complete(readBuffer) + #echo("Recv cb result: ", result) addRead(p, socket, cb) return retFuture @@ -833,9 +844,13 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} result = "" var c = "" while true: + #echo("1") c = await p.recv(socket, 1) + #echo("Received ", c.len) if c.len == 0: + #echo("returning") return + #echo("2") if c == "\r": c = await p.recv(socket, 1, MSG_PEEK) if c.len > 0 and c == "\L": @@ -845,7 +860,9 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} elif c == "\L": addNLIfEmpty() return + #echo("3") add(result.string, c) + #echo("4") when isMainModule: @@ -859,6 +876,7 @@ when isMainModule: proc main(p: PDispatcher): PFuture[int] {.async.} = discard await p.connect(sock, "irc.freenode.net", TPort(6667)) while true: + echo("recvLine") var line = await p.recvLine(sock) echo("Line is: ", line.repr) if line == "": @@ -882,7 +900,7 @@ when isMainModule: else: when true: - var f = p.connect(sock, "irc.freenode.org", TPort(6667)) + var f = p.connect(sock, "irc.poop.nl", TPort(6667)) f.callback = proc (future: PFuture[int]) = echo("Connected in future!") @@ -898,11 +916,13 @@ when isMainModule: sock.bindAddr(TPort(6667)) sock.listen() proc onAccept(future: PFuture[TSocketHandle]) = - echo "Accepted" - var t = p.send(future.read, "test\c\L") + let client = future.read + echo "Accepted ", client.cint + var t = p.send(client, "test\c\L") t.callback = proc (future: PFuture[int]) = - echo(future.read) + echo("Send: ", future.read) + client.close() var f = p.accept(sock) f.callback = onAccept diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index e086ee3ab..a113e3362 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -10,7 +10,6 @@ # TODO: Docs. import tables, os, unsigned, hashes -import sockets2 when defined(linux): import posix, epoll elif defined(windows): import winlean @@ -41,17 +40,14 @@ when defined(linux) or defined(nimdoc): result.events = EPOLLIN if EvWrite in events: result.events = result.events or EPOLLOUT + result.events = result.events or EPOLLRDHUP result.data.fd = fd.cint proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent], data: PObject): PSelectorKey {.discardable.} = ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent ## ``events``. - if s.fds.hasKey(fd): - raise newException(EInvalidValue, "File descriptor already exists.") - var event = createEventStruct(events, fd) - if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: OSError(OSLastError()) @@ -63,30 +59,18 @@ when defined(linux) or defined(nimdoc): proc update*(s: PSelector, fd: TSocketHandle, events: set[TEvent]): PSelectorKey {.discardable.} = ## Updates the events which ``fd`` wants notifications for. - if not s.fds.hasKey(fd): - raise newException(EInvalidValue, "File descriptor not found.") - var event = createEventStruct(events, fd) - - s.fds[fd].events = events - if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: - if OSLastError().cint == ENOENT: - # Socket has been closed. Epoll automatically removes disconnected - # sockets. - s.fds.del(fd) - osError("Socket has been disconnected") - - OSError(OSLastError()) - result = s.fds[fd] + if s.fds[fd].events != events: + echo("Update ", fd.cint, " to ", events) + var event = createEventStruct(events, fd) + + s.fds[fd].events = events + if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: + OSError(OSLastError()) + result = s.fds[fd] proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = - if not s.fds.hasKey(fd): - raise newException(EInvalidValue, "File descriptor not found.") if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: - if osLastError().cint == ENOENT: - # Socket has been closed. Epoll automatically removes disconnected - # sockets so its already been removed. - else: - OSError(OSLastError()) + OSError(OSLastError()) result = s.fds[fd] s.fds.del(fd) @@ -113,6 +97,14 @@ when defined(linux) or defined(nimdoc): assert selectorKey != nil result.add((selectorKey, evSet)) + if (s.events[i].events and EPOLLHUP) != 0 or + (s.events[i].events and EPOLLRDHUP) != 0: + # fd closed + #echo("fd closed ", s.events[i].data.fd) + s.unregister(s.events[i].data.fd.TSocketHandle) + + #echo("Epoll: ", result[i].key.fd, " ", result[i].events, " ", result[i].key.events) + proc newSelector*(): PSelector = new result result.epollFD = epoll_create(64) @@ -123,7 +115,26 @@ when defined(linux) or defined(nimdoc): proc contains*(s: PSelector, fd: TSocketHandle): bool = ## Determines whether selector contains a file descriptor. - return s.fds.hasKey(fd) + if s.fds.hasKey(fd): + result = true + + # Ensure the underlying epoll instance still contains this fd. + var event = createEventStruct(s.fds[fd].events, fd) + if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: + let err = osLastError() + if err.cint in {ENOENT, EBADF}: + return false + OSError(OSLastError()) + else: + return false + + proc contains*(s: PSelector, key: PSelectorKey): bool = + ## Determines whether selector contains this selector key. More accurate + ## than checking if the file descriptor is in the selector because it + ## ensures that the keys are equal. File descriptors may not always be + ## unique especially when an fd is closed and then a new one is opened, + ## the new one may have the same value. + return key.fd in s and s.fds[key.fd] == key proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey = ## Retrieves the selector key for ``fd``. diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index 9e5d270c3..458efea56 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -15,24 +15,17 @@ const var clientCount = 0 proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} = - echo("entering sendMessages") for i in 0 .. Date: Tue, 11 Mar 2014 23:06:22 +0000 Subject: [PATCH 164/249] EOS exception now contains the error code. --- lib/pure/os.nim | 11 ++++++----- lib/system.nim | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index bfecc569a..faca17e98 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -260,11 +260,12 @@ proc osError*(errorCode: TOSErrorCode) = ## ## If the error code is ``0`` or an error message could not be retrieved, ## the message ``unknown OS error`` will be used. - let msg = osErrorMsg(errorCode) - if msg == "": - raise newException(EOS, "unknown OS error") - else: - raise newException(EOS, msg) + var e: ref EOS; new(e) + e.errorCode = errorCode.int32 + e.msg = osErrorMsg(errorCode) + if e.msg == "": + e.msg = "unknown OS error" + raise e {.push stackTrace:off.} proc osLastError*(): TOSErrorCode = diff --git a/lib/system.nim b/lib/system.nim index 24ad50f97..41624bb05 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -260,6 +260,7 @@ type ## system raises. EIO* = object of ESystem ## raised if an IO error occured. EOS* = object of ESystem ## raised if an operating system service failed. + errorCode*: int32 ## OS-defined error code describing this error. EInvalidLibrary* = object of EOS ## raised if a dynamic library ## could not be loaded. EResourceExhausted* = object of ESystem ## raised if a resource request From d97a397139a6e7fdae6f3ec887ec5d76ef681b2c Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 12 Mar 2014 20:42:36 +0000 Subject: [PATCH 165/249] Fixed tasyncawait on Windows. Implicit registration of an fd now only occurs when a new socket is created (in socket() or accept()). This makes the implementation much simpler, changes to the linux version will follow. --- lib/pure/asyncio2.nim | 40 ++++++++++++++++++++++++++++--------- lib/windows/winlean.nim | 1 + tests/async/tasyncawait.nim | 4 ++-- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index cdb2b3a89..c15415130 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -94,7 +94,8 @@ proc failed*[T](future: PFuture[T]): bool = # TODO: Get rid of register. Do it implicitly. when defined(windows) or defined(nimdoc): - import winlean + import winlean, sets, hashes + #from hashes import THash type TCompletionKey = dword @@ -105,7 +106,7 @@ when defined(windows) or defined(nimdoc): PDispatcher* = ref object ioPort: THandle - hasHandles: bool + handles: TSet[TSocketHandle] TCustomOverlapped = object Internal*: DWORD @@ -117,21 +118,31 @@ when defined(windows) or defined(nimdoc): PCustomOverlapped = ptr TCustomOverlapped + proc hash(x: TSocketHandle): THash {.borrow.} + proc newDispatcher*(): PDispatcher = ## Creates a new Dispatcher instance. new result result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) + result.handles = initSet[TSocketHandle]() proc register*(p: PDispatcher, sock: TSocketHandle) = ## Registers ``sock`` with the dispatcher ``p``. if CreateIOCompletionPort(sock.THandle, p.ioPort, cast[TCompletionKey](sock), 1) == 0: OSError(OSLastError()) - p.hasHandles = true + p.handles.incl(sock) + + proc verifyPresence(p: PDispatcher, sock: TSocketHandle) = + ## Ensures that socket has been registered with the dispatcher. + if sock notin p.handles: + raise newException(EInvalidValue, + "Operation performed on a socket which has not been registered with" & + " the dispatcher yet.") proc poll*(p: PDispatcher, timeout = 500) = ## Waits for completion events and processes them. - if not p.hasHandles: + if p.handles.len == 0: raise newException(EInvalidValue, "No handles registered in dispatcher.") let llTimeout = @@ -237,7 +248,7 @@ when defined(windows) or defined(nimdoc): ## ## Returns a ``PFuture`` which will complete when the connection succeeds ## or an error occurs. - + verifyPresence(p, socket) var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed. # Apparently ``ConnectEx`` expects the socket to be initially bound: var saddr: Tsockaddr_in @@ -298,7 +309,7 @@ when defined(windows) or defined(nimdoc): ## recv operation then the future may complete with only a part of the ## requested data read. If socket is disconnected and no data is available ## to be read then the future will complete with a value of ``""``. - + verifyPresence(p, socket) var retFuture = newFuture[string]() var dataBuf: TWSABuf @@ -354,6 +365,7 @@ when defined(windows) or defined(nimdoc): proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. + verifyPresence(p, socket) var retFuture = newFuture[int]() var dataBuf: TWSABuf @@ -390,7 +402,9 @@ when defined(windows) or defined(nimdoc): ## Accepts a new connection. Returns a future containing the client socket ## corresponding to that connection and the remote address of the client. ## The future will complete when the connection is successfully accepted. - + ## + ## The resulting client socket is automatically registered to dispatcher. + verifyPresence(p, socket) var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]() var clientSock = socket() @@ -416,6 +430,7 @@ when defined(windows) or defined(nimdoc): dwLocalAddressLength, dwRemoteAddressLength, addr LocalSockaddr, addr localLen, addr RemoteSockaddr, addr remoteLen) + p.register(clientSock) # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186 retFuture.complete( (address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr), @@ -452,6 +467,13 @@ when defined(windows) or defined(nimdoc): return retFuture + proc socket*(disp: PDispatcher, domain: TDomain = AF_INET, + typ: TType = SOCK_STREAM, + protocol: TProtocol = IPPROTO_TCP): TSocketHandle = + ## Creates a new socket and registers it with the dispatcher implicitly. + result = socket() + disp.register(result) + initAll() else: import selectors @@ -871,7 +893,7 @@ when isMainModule: sock.setBlocking false - when true: + when false: # Await tests proc main(p: PDispatcher): PFuture[int] {.async.} = discard await p.connect(sock, "irc.freenode.net", TPort(6667)) @@ -898,7 +920,7 @@ when isMainModule: else: - when true: + when false: var f = p.connect(sock, "irc.poop.nl", TPort(6667)) f.callback = diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 74ef9c9ec..4d87cf4b2 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -456,6 +456,7 @@ var SO_DONTLINGER* {.importc, header: "Winsock2.h".}: cint SO_EXCLUSIVEADDRUSE* {.importc, header: "Winsock2.h".}: cint # disallow local address reuse + SO_ERROR* {.importc, header: "Winsock2.h".}: cint proc `==`*(x, y: TSocketHandle): bool {.borrow.} diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index 458efea56..fea0783a0 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -20,7 +20,7 @@ proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.asyn proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} = for i in 0 .. Date: Wed, 12 Mar 2014 23:19:40 +0000 Subject: [PATCH 166/249] Fix compilation on linux. --- lib/pure/asyncio2.nim | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index c15415130..c37370b7b 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -132,6 +132,7 @@ when defined(windows) or defined(nimdoc): cast[TCompletionKey](sock), 1) == 0: OSError(OSLastError()) p.handles.incl(sock) + # TODO: fd closure detection, we need to remove the fd from handles set proc verifyPresence(p: PDispatcher, sock: TSocketHandle) = ## Ensures that socket has been registered with the dispatcher. @@ -471,7 +472,7 @@ when defined(windows) or defined(nimdoc): typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP): TSocketHandle = ## Creates a new socket and registers it with the dispatcher implicitly. - result = socket() + result = socket(domain, typ, protocol) disp.register(result) initAll() @@ -495,28 +496,29 @@ else: proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) = assert sock in p.selector - if events == {}: - discard p.selector.unregister(sock) - else: - discard p.selector.update(sock, events) + discard p.selector.update(sock, events) + + proc register(p: PDispatcher, sock: TSocketHandle) = + var data = PData(sock: sock, readCBs: @[], writeCBs: @[]) + p.selector.register(sock, {}, data.PObject) + + proc socket*(disp: PDispatcher, domain: TDomain = AF_INET, + typ: TType = SOCK_STREAM, + protocol: TProtocol = IPPROTO_TCP): TSocketHandle = + result = socket(domain, typ, protocol) + disp.register(result) proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = - #echo("addRead") if sock notin p.selector: - var data = PData(sock: sock, readCBs: @[cb], writeCBs: @[]) - p.selector.register(sock, {EvRead}, data.PObject) - #echo("registered") - else: - p.selector[sock].data.PData.readCBs.add(cb) - p.update(sock, p.selector[sock].events + {EvRead}) + raise newException(EInvalidValue, "File descriptor not registered.") + p.selector[sock].data.PData.readCBs.add(cb) + p.update(sock, p.selector[sock].events + {EvRead}) proc addWrite(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = if sock notin p.selector: - var data = PData(sock: sock, readCBs: @[], writeCBs: @[cb]) - p.selector.register(sock, {EvWrite}, data.PObject) - else: - p.selector[sock].data.PData.writeCBs.add(cb) - p.update(sock, p.selector[sock].events + {EvWrite}) + raise newException(EInvalidValue, "File descriptor not registered.") + p.selector[sock].data.PData.writeCBs.add(cb) + p.update(sock, p.selector[sock].events + {EvWrite}) proc poll*(p: PDispatcher, timeout = 500) = for info in p.selector.select(timeout): @@ -667,6 +669,7 @@ else: else: retFuture.fail(newException(EOS, osErrorMsg(lastError))) else: + p.register(client) retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client)) addRead(p, socket, cb) return retFuture From 2fc84325c7cfba82c54c652a5f1a795845b169a9 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 13 Mar 2014 02:52:40 +0100 Subject: [PATCH 167/249] should fix the nkExprColonExpr bug in the vm --- compiler/evalffi.nim | 2 +- compiler/vm.nim | 3 +-- compiler/vmgen.nim | 22 +++++++++++++++------- lib/pure/dynlib.nim | 2 +- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 54be0ccb2..db78da714 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -151,7 +151,7 @@ proc getField(n: PNode; position: int): PSym = else: discard proc packObject(x: PNode, typ: PType, res: pointer) = - InternalAssert x.kind in {nkObjConstr, nkPar} + internalAssert x.kind in {nkObjConstr, nkPar} # compute the field's offsets: discard typ.getSize for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): diff --git a/compiler/vm.nim b/compiler/vm.nim index 2a80106a5..5c8e533f1 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -426,8 +426,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit}: let n = src.sons[rc] - regs[ra].node = if n.kind == nkExprColonExpr: n[1] - else: n + regs[ra].node = n else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcWrObj: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index b1a751723..0fc71189d 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -308,12 +308,20 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = c.gen(n.sons[2], dest) c.patch(L1) -proc nilLiteral(n: PNode): PNode = - result = n +proc canonConst(n: PNode): PNode = + if n.kind == nkExprColonExpr: + result = n.sons[1] + elif n.hasSubnodeWith(nkExprColonExpr): + result = n.copyNode + newSeq(result.sons, n.len) + for i in 0.. Date: Thu, 13 Mar 2014 12:04:25 +0100 Subject: [PATCH 168/249] Provide zlib version explicit on Linux. - workaround for Gentoo (#987), - also, it's a good practice to provide ABI version in calls to dlopen, so if zlib ever changes ABI, wrappers won't break siliently. --- lib/wrappers/zip/zlib.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wrappers/zip/zlib.nim b/lib/wrappers/zip/zlib.nim index cd3a765c1..f505b95a7 100644 --- a/lib/wrappers/zip/zlib.nim +++ b/lib/wrappers/zip/zlib.nim @@ -7,7 +7,7 @@ when defined(windows): elif defined(macosx): const libz = "libz.dylib" else: - const libz = "libz.so" + const libz = "libz.so.1" type Uint* = int32 From dd18c0ddd50f1e1f60a5f4337772198af77aebfa Mon Sep 17 00:00:00 2001 From: Grzegorz Adam Hankiewicz Date: Thu, 13 Mar 2014 19:56:11 +0100 Subject: [PATCH 169/249] Adds example to likely/unlikely docstring. Refs #983. --- lib/system.nim | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 41624bb05..be57ba192 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2364,12 +2364,32 @@ when not defined(JS): #and not defined(NimrodVM): when not defined(NimrodVM): proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.} - ## can be used to mark a condition to be likely. This is a hint for the - ## optimizer. + ## Hints the optimizer that `val` is likely going to be true. + ## + ## You can use this proc to decorate a branch condition. On certain + ## platforms this can help the processor predict better which branch is + ## going to be run. Example: + ## + ## .. code-block:: nimrod + ## for value in inputValues: + ## if likely(value <= 100): + ## process(value) + ## else: + ## echo "Value too big!" proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.} - ## can be used to mark a condition to be unlikely. This is a hint for the - ## optimizer. + ## Hints the optimizer that `val` is likely going to be false. + ## + ## You can use this proc to decorate a branch condition. On certain + ## platforms this can help the processor predict better which branch is + ## going to be run. Example: + ## + ## .. code-block:: nimrod + ## for value in inputValues: + ## if unlikely(value > 100): + ## echo "Value too big!" + ## else: + ## process(value) proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = ## retrieves the raw proc pointer of the closure `x`. This is From 3e056afb1c481d35403c93bc17307b46be5e20f2 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 13 Mar 2014 22:29:48 +0100 Subject: [PATCH 170/249] fixes #993 --- compiler/semtempl.nim | 8 +++++++- lib/pure/parsesql.nim | 2 +- tests/template/tissue993.nim | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 tests/template/tissue993.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 42268d44f..363c5246f 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -155,7 +155,11 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = of nkPragmaExpr: x = x[0] of nkIdent: break else: illFormedAst(x) - c.toInject.incl(x.ident.id) + let ident = getIdentNode(c, x) + if not isTemplParam(c, ident): + c.toInject.incl(x.ident.id) + else: + replaceIdentBySym(n, ident) else: let ident = getIdentNode(c, n) if not isTemplParam(c, ident): @@ -359,6 +363,8 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = semRoutineInTemplBody(c, n, skConverter) of nkPragmaExpr: result.sons[0] = semTemplBody(c, n.sons[0]) + of nkPostfix: + result.sons[1] = semTemplBody(c, n.sons[1]) of nkPragma: discard else: diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index 3f9686e1e..bd8836f7c 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -267,7 +267,7 @@ proc getSymbol(c: var TSqlLexer, tok: var TToken) = while true: add(tok.literal, buf[pos]) Inc(pos) - if not (buf[pos] in {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}): + if buf[pos] notin {'a'..'z','A'..'Z','0'..'9','_','$', '\128'..'\255'}: break c.bufpos = pos tok.kind = tkIdentifier diff --git a/tests/template/tissue993.nim b/tests/template/tissue993.nim new file mode 100644 index 000000000..d39f43942 --- /dev/null +++ b/tests/template/tissue993.nim @@ -0,0 +1,21 @@ + +type pnode* = ref object of tobject + +template litNode (name, ty): stmt = + type name* = ref object of PNode + val*: ty +litNode PIntNode, int + +import json + +template withKey*(j: PJsonNode; key: string; varname: expr; + body:stmt): stmt {.immediate.} = + if j.hasKey(key): + let varname{.inject.}= j[key] + block: + body + +var j = parsejson("{\"zzz\":1}") +withkey(j, "foo", x): + echo(x) + From 5e47c18e3f7023f1c73efb0e1e2e7f8713e47b83 Mon Sep 17 00:00:00 2001 From: flaviut Date: Fri, 14 Mar 2014 14:18:38 -0400 Subject: [PATCH 171/249] Add token position to `nimrod scan` A block of the format "linenum:col\t' is added to the start of each token that is printed. --- compiler/lexer.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 0e7df13cd..4285a090d 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -173,6 +173,7 @@ proc prettyTok*(tok: TToken): string = else: result = tokToStr(tok) proc printTok*(tok: TToken) = + write(stdout, tok.line, ":", tok.col, "\t") write(stdout, TokTypeToStr[tok.tokType]) write(stdout, " ") writeln(stdout, tokToStr(tok)) From 1a75b17cd03caac6b26588a0572c1032b024f1c8 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 14 Mar 2014 18:46:08 +0000 Subject: [PATCH 172/249] File descriptors are now removed from fds list explicitly in close(). Fixes tasyncawait on linux. --- lib/pure/asyncio2.nim | 6 +++++- lib/pure/selectors.nim | 36 +++++++++++++++++------------------- tests/async/tasyncawait.nim | 6 +++--- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index c37370b7b..0ea4d7e79 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -508,6 +508,10 @@ else: result = socket(domain, typ, protocol) disp.register(result) + proc close*(disp: PDispatcher, sock: TSocketHandle) = + sock.close() + disp.selector.unregister(sock) + proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = if sock notin p.selector: raise newException(EInvalidValue, "File descriptor not registered.") @@ -892,7 +896,7 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} when isMainModule: var p = newDispatcher() - var sock = socket() + var sock = p.socket() sock.setBlocking false diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index a113e3362..085344e3e 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -60,7 +60,6 @@ when defined(linux) or defined(nimdoc): events: set[TEvent]): PSelectorKey {.discardable.} = ## Updates the events which ``fd`` wants notifications for. if s.fds[fd].events != events: - echo("Update ", fd.cint, " to ", events) var event = createEventStruct(events, fd) s.fds[fd].events = events @@ -70,7 +69,9 @@ when defined(linux) or defined(nimdoc): proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: - OSError(OSLastError()) + let err = OSLastError() + if err.cint notin {ENOENT, EBADF}: # TODO: Why do we sometimes get an EBADF? Is this normal? + OSError(err) result = s.fds[fd] s.fds.del(fd) @@ -78,6 +79,15 @@ when defined(linux) or defined(nimdoc): if s.epollFD.close() != 0: OSError(OSLastError()) dealloc(addr s.events) # TODO: Test this + proc epollHasFd(s: PSelector, fd: TSocketHandle): bool = + result = true + var event = createEventStruct(s.fds[fd].events, fd) + if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: + let err = osLastError() + if err.cint in {ENOENT, EBADF}: + return false + OSError(OSLastError()) + proc select*(s: PSelector, timeout: int): seq[TReadyInfo] = ## ## The ``events`` field of the returned ``key`` contains the original events @@ -85,24 +95,19 @@ when defined(linux) or defined(nimdoc): ## of the ``TReadyInfo`` tuple which determines which events are ready ## on the ``fd``. result = @[] - let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint) if evNum < 0: OSError(OSLastError()) if evNum == 0: return @[] for i in 0 .. Date: Fri, 14 Mar 2014 20:18:51 +0000 Subject: [PATCH 173/249] Fixes compilation of asyncio2 on Windows. Added a close(PDispatcher, TSocketHandle) implementation which unregisters the handle. --- lib/pure/asyncio2.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 0ea4d7e79..43247ba7e 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -132,7 +132,6 @@ when defined(windows) or defined(nimdoc): cast[TCompletionKey](sock), 1) == 0: OSError(OSLastError()) p.handles.incl(sock) - # TODO: fd closure detection, we need to remove the fd from handles set proc verifyPresence(p: PDispatcher, sock: TSocketHandle) = ## Ensures that socket has been registered with the dispatcher. @@ -475,6 +474,11 @@ when defined(windows) or defined(nimdoc): result = socket(domain, typ, protocol) disp.register(result) + proc close*(disp: PDispatcher, socket: TSocketHandle) = + ## Closes a socket and ensures that it is unregistered. + socket.close() + disp.handles.excl(socket) + initAll() else: import selectors From 95e4ab99e7830877fd67844b69640e1e31b705e4 Mon Sep 17 00:00:00 2001 From: Matthias Einwag Date: Fri, 14 Mar 2014 22:17:44 +0100 Subject: [PATCH 174/249] Make the IP address exceptions more detailed --- lib/pure/net.nim | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 82b24585f..9ee98cbe6 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -139,20 +139,21 @@ proc parseIPv4Address(address_str: string): TIpAddress = for i in 0 .. high(address_str): if address_str[i] in strutils.Digits: # Character is a number currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) - if currentByte > 255'u16: raise newException(EInvalidValue, "Invalid IP Address") + if currentByte > 255'u16: + raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") seperatorValid = true elif address_str[i] == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") result.address_v4[byteCount] = cast[uint8](currentByte) currentByte = 0 byteCount.inc seperatorValid = false else: - raise newException(EInvalidValue, "Invalid IP Address") # Invalid character + raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") if byteCount != 3 or not seperatorValid: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") result.address_v4[byteCount] = cast[uint8](currentByte) proc parseIPv6Address(address_str: string): TIpAddress = @@ -173,22 +174,24 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str: if c == ':': - if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address") + if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid seperator") if lastWasColon: - if dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address") + if dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address. Address contains more than one \"::\" seperator") dualColonGroup = groupCount seperatorValid = false elif i != 0 and i != high(address_str): - if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address") + if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) currentShort = 0 groupCount.inc() if dualColonGroup != -1: seperatorValid = false elif i == 0: # only valid if address starts with :: - if address_str[1] != ':': raise newException(EInvalidValue, "Invalid IP Address") + if address_str[1] != ':': + raise newException(EInvalidValue, "Invalid IP Address. Address may not start with \":\"") else: # i == high(address_str) - only valid if address ends with :: - if address_str[high(address_str)-1] != ':': raise newException(EInvalidValue, "Invalid IP Address") + if address_str[high(address_str)-1] != ':': + raise newException(EInvalidValue, "Invalid IP Address. Address may not end with \":\"") lastWasColon = true currentGroupStart = i + 1 elif c == '.': # Switch to parse IPv4 mode @@ -204,16 +207,17 @@ proc parseIPv6Address(address_str: string): TIpAddress = currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10 else: # Upper case hex currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 - if currentShort > 65535'u32: raise newException(EInvalidValue, "Invalid IP Address") + if currentShort > 65535'u32: + raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") lastWasColon = false seperatorValid = true else: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff if seperatorValid: # Copy remaining data - if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address") + if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) groupCount.inc() @@ -221,35 +225,36 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str[v4StartPos..high(address_str)]: if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) - if currentShort > 255'u32: raise newException(EInvalidValue, "Invalid IP Address") + if currentShort > 255'u32: + raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") seperatorValid = true elif c == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) currentShort = 0 byteCount.inc() seperatorValid = false else: # Invalid character - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") if byteCount != 3 or not seperatorValid: - raise new EInvalidValue + raise newException(EInvalidValue, "Invalid IP Address") result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort) groupCount += 2 # Shift and fill zeros in case of :: if groupCount > 8: - raise newException(EInvalidValue, "Invalid IP Address") + raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") elif groupCount < 8: # must fill - if dualColonGroup == -1: raise newException(EInvalidValue, "Invalid IP Address") + if dualColonGroup == -1: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too few groups") var toFill = 8 - groupCount # The number of groups to fill var toShift = groupCount - dualColonGroup # Nr of known groups after :: for i in 0..2*toShift-1: # shift result.address_v6[15-i] = result.address_v6[groupCount*2-i-1] for i in 0..2*toFill-1: # fill with 0s result.address_v6[dualColonGroup*2+i] = 0 - elif dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address") + elif dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") proc parseIpAddress*(address_str: string): TIpAddress = From de64e8ec22717af899c115ac9756eadd9d578dcb Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 15 Mar 2014 00:09:33 +0000 Subject: [PATCH 175/249] PFuture[void] now works. Return types can also be completely ommitted. PFuture[void] will then be implicitly deduced. --- lib/pure/asyncio2.nim | 82 ++++++++++++++++++++++++------------- tests/async/tasyncawait.nim | 22 +++++----- 2 files changed, 65 insertions(+), 39 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 43247ba7e..eb31eca13 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -43,6 +43,14 @@ proc complete*[T](future: PFuture[T], val: T) = if future.cb != nil: future.cb() +proc complete*(future: PFuture[void]) = + ## Completes a void ``future``. + assert(not future.finished, "Future already finished, cannot finish twice.") + assert(future.error == nil) + future.finished = true + if future.cb != nil: + future.cb() + proc fail*[T](future: PFuture[T], error: ref EBase) = ## Completes ``future`` with ``error``. assert(not future.finished, "Future already finished, cannot finish twice.") @@ -76,7 +84,8 @@ proc read*[T](future: PFuture[T]): T = ## If the result of the future is an error then that error will be raised. if future.finished: if future.error != nil: raise future.error - return future.value + when T isnot void: + return future.value else: # TODO: Make a custom exception type for this? raise newException(EInvalidValue, "Future still in progress.") @@ -243,13 +252,13 @@ when defined(windows) or defined(nimdoc): RemoteSockaddr, RemoteSockaddrLength) proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, - af = AF_INET): PFuture[int] = + af = AF_INET): PFuture[void] = ## Connects ``socket`` to server at ``address:port``. ## ## Returns a ``PFuture`` which will complete when the connection succeeds ## or an error occurs. verifyPresence(p, socket) - var retFuture = newFuture[int]()# TODO: Change to void when that regression is fixed. + var retFuture = newFuture[void]() # Apparently ``ConnectEx`` expects the socket to be initially bound: var saddr: Tsockaddr_in saddr.sin_family = int16(toInt(af)) @@ -271,7 +280,7 @@ when defined(windows) or defined(nimdoc): proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): - retFuture.complete(0) + retFuture.complete() else: retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) @@ -281,7 +290,7 @@ when defined(windows) or defined(nimdoc): if ret: # Request to connect completed immediately. success = true - retFuture.complete(0) + retFuture.complete() # We don't deallocate ``ol`` here because even though this completed # immediately poll will still be notified about its completion and it will # free ``ol``. @@ -362,11 +371,11 @@ when defined(windows) or defined(nimdoc): # free ``ol``. return retFuture - proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = + proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[void] = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. verifyPresence(p, socket) - var retFuture = newFuture[int]() + var retFuture = newFuture[void]() var dataBuf: TWSABuf dataBuf.buf = data @@ -378,7 +387,7 @@ when defined(windows) or defined(nimdoc): proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): - retFuture.complete(0) + retFuture.complete() else: retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) @@ -391,7 +400,7 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(EOS, osErrorMsg(err))) dealloc(ol) else: - retFuture.complete(0) + retFuture.complete() # We don't deallocate ``ol`` here because even though this completed # immediately poll will still be notified about its completion and it will # free ``ol``. @@ -564,12 +573,12 @@ else: # (e.g. socket disconnected). proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, - af = AF_INET): PFuture[int] = - var retFuture = newFuture[int]() + af = AF_INET): PFuture[void] = + var retFuture = newFuture[void]() proc cb(sock: TSocketHandle): bool = # We have connected. - retFuture.complete(0) + retFuture.complete() return true var aiList = getAddrInfo(address, port, af) @@ -581,7 +590,7 @@ else: if ret == 0: # Request to connect completed immediately. success = true - retFuture.complete(0) + retFuture.complete() break else: lastError = osLastError() @@ -635,8 +644,8 @@ else: addRead(p, socket, cb) return retFuture - proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] = - var retFuture = newFuture[int]() + proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[void] = + var retFuture = newFuture[void]() var written = 0 @@ -656,7 +665,7 @@ else: if res != netSize: result = false # We still have data to send. else: - retFuture.complete(0) + retFuture.complete() addWrite(p, socket, cb) return retFuture @@ -789,12 +798,17 @@ macro async*(prc: stmt): stmt {.immediate.} = hint("Processing " & prc[0].getName & " as an async proc.") + let returnType = prc[3][0] + var subtypeName = "" # Verify that the return type is a PFuture[T] - if prc[3][0].kind == nnkIdent: - error("Expected return type of 'PFuture' got '" & $prc[3][0] & "'") - elif prc[3][0].kind == nnkBracketExpr: - if $prc[3][0][0] != "PFuture": - error("Expected return type of 'PFuture' got '" & $prc[3][0][0] & "'") + if returnType.kind == nnkIdent: + error("Expected return type of 'PFuture' got '" & $returnType & "'") + elif returnType.kind == nnkBracketExpr: + if $returnType[0] != "PFuture": + error("Expected return type of 'PFuture' got '" & $returnType[0] & "'") + subtypeName = $returnType[1].ident + elif returnType.kind == nnkEmpty: + subtypeName = "void" # TODO: Why can't I use genSym? I get illegal capture errors for Syms. # TODO: It seems genSym is broken. Change all usages back to genSym when fixed @@ -807,20 +821,24 @@ macro async*(prc: stmt): stmt {.immediate.} = newVarStmt(retFutureSym, newCall( newNimNode(nnkBracketExpr).add( - newIdentNode("newFuture"), - prc[3][0][1])))) # Get type from return type of this proc. - + newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`. + newIdentNode(subtypeName))))) # Get type from return type of this proc + echo(treeRepr(outerProcBody)) # -> iterator nameIter(): PFutureBase {.closure.} = # -> var result: T # -> # -> complete(retFuture, result) var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter") var procBody = prc[6].processBody(retFutureSym) - procBody.insert(0, newNimNode(nnkVarSection).add( - newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T - procBody.add( - newCall(newIdentNode("complete"), - retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) + if subtypeName != "void": + procBody.insert(0, newNimNode(nnkVarSection).add( + newIdentDefs(newIdentNode("result"), returnType[1]))) # -> var result: T + procBody.add( + newCall(newIdentNode("complete"), + retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) + else: + # -> complete(retFuture) + procBody.add(newCall(newIdentNode("complete"), retFutureSym)) var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")], procBody, nnkIteratorDef) @@ -855,6 +873,12 @@ macro async*(prc: stmt): stmt {.immediate.} = for i in 0 .. Date: Sat, 15 Mar 2014 10:37:14 +0200 Subject: [PATCH 176/249] grammar fix: allow free-standing expressions such as type(foo) is bar --- compiler/parser.nim | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index cfba89f4a..4ef5a300c 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -713,9 +713,11 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = break proc primary(p: var TParser, mode: TPrimaryMode): PNode +proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = - result = primary(p, mode) +proc parseOperators(p: var TParser, headNode: PNode, + limit: int, mode: TPrimaryMode): PNode = + result = headNode # expand while operators have priorities higher than 'limit' var opPrec = getPrecedence(p.tok, p.strongSpaces) let modeB = if mode == pmTypeDef: pmTypeDesc else: mode @@ -734,6 +736,10 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = addSon(a, b) result = a opPrec = getPrecedence(p.tok, p.strongSpaces) + +proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = + result = primary(p, mode) + result = parseOperators(p, result, limit, mode) proc simpleExpr(p: var TParser, mode = pmNormal): PNode = result = simpleExprAux(p, -1, mode) @@ -1501,7 +1507,7 @@ proc parseSection(p: var TParser, kind: TNodeKind, defparser: TDefParser): PNode = #| section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED) result = newNodeP(kind, p) - getTok(p) + if kind != nkTypeSection: getTok(p) skipComment(p, result) if realInd(p): withInd(p): @@ -1862,7 +1868,16 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkMacro: result = parseRoutine(p, nkMacroDef) of tkTemplate: result = parseRoutine(p, nkTemplateDef) of tkConverter: result = parseRoutine(p, nkConverterDef) - of tkType: result = parseSection(p, nkTypeSection, parseTypeDef) + of tkType: + getTok(p) + if p.tok.tokType == tkParLe: + getTok(p) + result = newNodeP(nkTypeOfExpr, p) + result.addSon(primary(p, pmTypeDesc)) + eat(p, tkParRi) + result = parseOperators(p, result, -1, pmNormal) + else: + result = parseSection(p, nkTypeSection, parseTypeDef) of tkConst: result = parseSection(p, nkConstSection, parseConstant) of tkLet: result = parseSection(p, nkLetSection, parseVariable) of tkWhen: result = parseIfOrWhen(p, nkWhenStmt) From 7080d02af47cdc459aa441baa76d06f32af2f1c3 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Mar 2014 10:50:14 +0200 Subject: [PATCH 177/249] allow multi-line expressions using the dot operator this enables parsing of expressions like: ``` Nimrod s.items .map(proc (x): auto = x * 2) .filter do (x) -> auto: return x % 2 == 0 .group_by((x) => x.length) ``` --- compiler/lexer.nim | 9 +++++++-- compiler/parser.nim | 16 ++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 9c6c5e22f..be152eb55 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -101,6 +101,7 @@ type base10, # base10 is listed as the first element, # so that it is the correct default value base2, base8, base16 + TToken* = object # a Nimrod token tokType*: TTokType # the type of the token indent*: int # the indentation; != -1 if the token has been @@ -121,8 +122,9 @@ type indentAhead*: int # if > 0 an indendation has already been read # this is needed because scanning comments # needs so much look-ahead + currLineIndent*: int strongSpaces*: bool - + var gLinesCompiled*: int # all lines that have been compiled @@ -206,6 +208,7 @@ proc openLexer(lex: var TLexer, fileIdx: int32, inputstream: PLLStream) = openBaseLexer(lex, inputstream) lex.fileIdx = fileidx lex.indentAhead = - 1 + lex.currLineIndent = 0 inc(lex.lineNumber, inputstream.lineOffset) proc closeLexer(lex: var TLexer) = @@ -709,6 +712,7 @@ proc skip(L: var TLexer, tok: var TToken) = tok.strongSpaceA = 0 if buf[pos] > ' ': tok.indent = indent + L.currLineIndent = indent break else: break # EndOfFile also leaves the loop @@ -718,6 +722,7 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = fillToken(tok) if L.indentAhead >= 0: tok.indent = L.indentAhead + L.currLineIndent = L.indentAhead L.indentAhead = -1 else: tok.indent = -1 @@ -827,5 +832,5 @@ proc rawGetTok(L: var TLexer, tok: var TToken) = tok.tokType = tkInvalid lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')') inc(L.bufpos) - + dummyIdent = getIdent("") diff --git a/compiler/parser.nim b/compiler/parser.nim index 4ef5a300c..060629518 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -658,7 +658,7 @@ proc namedParams(p: var TParser, callee: PNode, exprColonEqExprListAux(p, endTok, result) proc parseMacroColon(p: var TParser, x: PNode): PNode -proc primarySuffix(p: var TParser, r: PNode): PNode = +proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| | doBlocks #| | '.' optInd ('type' | 'addr' | symbol) generalizedLit? @@ -666,7 +666,8 @@ proc primarySuffix(p: var TParser, r: PNode): PNode = #| | '{' optInd indexExprList optPar '}' #| | &( '`'|IDENT|literal|'cast') expr # command syntax result = r - while p.tok.indent < 0: + while p.tok.indent < 0 or + (p.tok.tokType == tkDot and p.tok.indent >= baseIndent): case p.tok.tokType of tkParLe: if p.strongSpaces and p.tok.strongSpaceA > 0: break @@ -1007,8 +1008,9 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = optInd(p, a) if isSigil: #XXX prefix operators + let baseInd = p.lex.currLineIndent addSon(result, primary(p, pmSkipSuffix)) - result = primarySuffix(p, result) + result = primarySuffix(p, result, baseInd) else: addSon(result, primary(p, pmNormal)) return @@ -1071,9 +1073,10 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = optInd(p, result) addSon(result, primary(p, pmNormal)) else: + let baseInd = p.lex.currLineIndent result = identOrLiteral(p, mode) if mode != pmSkipSuffix: - result = primarySuffix(p, result) + result = primarySuffix(p, result, baseInd) proc parseTypeDesc(p: var TParser): PNode = #| typeDesc = simpleExpr @@ -1901,7 +1904,7 @@ proc parseStmt(p: var TParser): PNode = if p.tok.indent < 0 or p.tok.indent == p.currInd: discard else: break else: - if p.tok.indent > p.currInd: + if p.tok.indent > p.currInd and p.tok.tokType != tkDot: parMessage(p, errInvalidIndentation) break if p.tok.tokType in {tkCurlyRi, tkParRi, tkCurlyDotRi, tkBracketRi}: @@ -1928,7 +1931,8 @@ proc parseStmt(p: var TParser): PNode = else: result = newNodeP(nkStmtList, p) while true: - if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) + if p.tok.indent >= 0: + parMessage(p, errInvalidIndentation) let a = simpleStmt(p) if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) result.add(a) From cf8fe16a48d79d149f6ce85e229d23c1513c2497 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Mar 2014 11:23:46 +0200 Subject: [PATCH 178/249] fix #715 again the regression was caused by the introduction of "generic" lambdas --- compiler/msgs.nim | 6 ++++++ compiler/semdata.nim | 2 +- compiler/semexprs.nim | 7 ++++++- compiler/semstmts.nim | 4 +++- tests/generics/tbadgenericlambda.nim | 7 +++++++ tests/generics/tgenericlambda.nim | 7 ++++++- 6 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 tests/generics/tbadgenericlambda.nim diff --git a/compiler/msgs.nim b/compiler/msgs.nim index c75876843..3e5a56e1e 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -106,6 +106,8 @@ type errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, errXCannotBeClosure, errXMustBeCompileTime, errCannotInferTypeOfTheLiteral, + errCannotInferReturnType, + errGenericLambdaNotAllowed, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -355,6 +357,10 @@ const errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXMustBeCompileTime: "'$1' can only be used in compile-time context", errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1", + errCannotInferReturnType: "cannot infer the return type of the proc", + errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & + "it is used as an operand to another routine and the types " & + "of the generic paramers can be infered from the expected signature.", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 84e017050..088b93fae 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -42,7 +42,7 @@ type TExprFlag* = enum efLValue, efWantIterator, efInTypeof, efWantStmt, efDetermineType, - efAllowDestructor, efWantValue + efAllowDestructor, efWantValue, efOperand TExprFlags* = set[TExprFlag] PContext* = ref TContext diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 62a816a50..ba2847835 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -21,7 +21,7 @@ proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # same as 'semExprWithType' but doesn't check for proc vars - result = semExpr(c, n, flags) + result = semExpr(c, n, flags + {efOperand}) if result.kind == nkEmpty: # do not produce another redundant error message: #raiseRecoverableError("") @@ -1218,6 +1218,7 @@ proc semReturn(c: PContext, n: PNode): PNode = proc semProcBody(c: PContext, n: PNode): PNode = openScope(c) + result = semExpr(c, n) if c.p.resultSym != nil and not isEmptyType(result.typ): # transform ``expr`` to ``result = expr``, but not if the expr is already @@ -1241,6 +1242,10 @@ proc semProcBody(c: PContext, n: PNode): PNode = result = semAsgn(c, a) else: discardCheck(c, result) + + if c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: + localError(c.p.resultSym.info, errCannotInferReturnType) + closeScope(c) proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index edce7c9bd..15bfaab10 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -944,13 +944,15 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) #if efDetermineType notin flags: # XXX not good enough; see tnamedparamanonproc.nim - if n.sons[genericParamsPos].kind == nkEmpty: + if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags): pushProcCon(c, s) addResult(c, s.typ.sons[0], n.info, skProc) let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) n.sons[bodyPos] = transformBody(c.module, semBody, s) addResultNode(c, n) popProcCon(c) + elif efOperand notin flags: + localError(n.info, errGenericLambdaNotAllowed) sideEffectsCheck(c, s) else: localError(n.info, errImplOfXexpected, s.name.s) diff --git a/tests/generics/tbadgenericlambda.nim b/tests/generics/tbadgenericlambda.nim new file mode 100644 index 000000000..5e406cacc --- /dev/null +++ b/tests/generics/tbadgenericlambda.nim @@ -0,0 +1,7 @@ +discard """ + msg: "nested proc can have generic parameters only when" + line: 6 +""" + +let x = proc (x, y): auto = x + y + diff --git a/tests/generics/tgenericlambda.nim b/tests/generics/tgenericlambda.nim index f7aafe1d9..eb6ada3e5 100644 --- a/tests/generics/tgenericlambda.nim +++ b/tests/generics/tgenericlambda.nim @@ -1,5 +1,5 @@ discard """ - output: "10\n10\n1\n2\n3" + output: "10\n10\n1\n2\n3\n15" """ proc test(x: proc (a, b: int): int) = @@ -16,3 +16,8 @@ proc foreach[T](s: seq[T], body: proc(x: T)) = foreach(@[1,2,3]) do (x): echo x +proc foo = + let x = proc (a, b: int): auto = a + b + echo x(5, 10) + +foo() From 4d2c948281ee1ad38f8098d1b454dc4787441e8b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Mar 2014 12:04:57 +0200 Subject: [PATCH 179/249] fix #1001 --- compiler/lambdalifting.nim | 2 +- tests/metatype/tusertypeclasses.nim | 1 + tests/metatype/udtcmanual.nim | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 67cd364dc..4bc8eff86 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -518,7 +518,7 @@ proc searchForInnerProcs(o: POuterContext, n: PNode) = else: internalError(it.info, "transformOuter") of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkClosure: + nkClosure, nkTypeSection: # don't recurse here: # XXX recurse here and setup 'up' pointers discard diff --git a/tests/metatype/tusertypeclasses.nim b/tests/metatype/tusertypeclasses.nim index 5b04c490f..eb6597e4d 100644 --- a/tests/metatype/tusertypeclasses.nim +++ b/tests/metatype/tusertypeclasses.nim @@ -34,6 +34,7 @@ type T.x y(T) intval T.y + let y = intval(T.y) proc y(x: TObj): int = 10 diff --git a/tests/metatype/udtcmanual.nim b/tests/metatype/udtcmanual.nim index f22bd6ac6..dd44298dc 100644 --- a/tests/metatype/udtcmanual.nim +++ b/tests/metatype/udtcmanual.nim @@ -25,7 +25,7 @@ type C.len is Ordinal items(c) is iterator for value in C: - value.type is T + type(value) is T proc takesIntContainer(c: Container[int]) = for e in c: echo e From 37b199d34ae198872f061d9a4155539138b34c87 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Mar 2014 19:23:20 +0200 Subject: [PATCH 180/249] first phase of tyGenericParam reforms needed for static params --- compiler/ast.nim | 13 +++++++++- compiler/semexprs.nim | 4 +-- compiler/seminst.nim | 9 +++---- compiler/semtypes.nim | 54 +++++++++++++++++++++++++++-------------- compiler/semtypinst.nim | 14 ++--------- compiler/sigmatch.nim | 32 ++++++++++++++++++------ 6 files changed, 79 insertions(+), 47 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index ee0d55920..55de43dd1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -382,6 +382,10 @@ type # sons[0]: type of containing object or tuple # sons[1]: field type # .n: nkDotExpr storing the field name + +static: + # remind us when TTypeKind stops to fit in a single 64-bit word + assert TTypeKind.high.ord <= 63 const tyPureObject* = tyTuple @@ -448,6 +452,10 @@ type tfHasStatic tfGenericTypeParam tfImplicitTypeParam + tfWildcard # consider a proc like foo[T, I](x: Type[T, I]) + # T and I here can bind to both typedesc and static types + # before this is determined, we'll consider them to be a + # wildcard type. TTypeFlags* = set[TTypeFlag] @@ -693,7 +701,7 @@ type TSym* {.acyclic.} = object of TIdObj # proc and type instantiations are cached in the generic symbol case kind*: TSymKind - of skType: + of skType, skGenericParam: typeInstCache*: seq[PType] typScope*: PScope of routineKinds: @@ -1495,6 +1503,9 @@ proc hasPattern*(s: PSym): bool {.inline.} = iterator items*(n: PNode): PNode = for i in 0.. = nkNone and n.kind <= nkNilLit diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ba2847835..133b4ac1e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -117,8 +117,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = elif s.ast != nil: result = semExpr(c, s.ast) else: - internalError(n.info, "no default for") - result = emptyNode + n.typ = s.typ + return n of skType: markUsed(n, s) result = newSymNode(s, n.info) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 4bcfa7f15..dca9e3378 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -15,8 +15,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, if n.kind != nkGenericParams: internalError(n.info, "instantiateGenericParamList; no generic params") newSeq(entry.concreteTypes, n.len) - for i in countup(0, n.len - 1): - var a = n.sons[i] + for i, a in n.pairs: if a.kind != nkSym: internalError(a.info, "instantiateGenericParamList; no symbol") var q = a.sym @@ -149,7 +148,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = # no need to instantiate generic templates/macros: if fn.kind in {skTemplate, skMacro}: return fn - # generates an instantiated proc if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep") inc(c.instCounter) @@ -183,9 +181,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) if isNil(n.sons[bodyPos]): n.sons[bodyPos] = copyTree(fn.getBody) - if fn.kind != skTemplate: - instantiateBody(c, n, result) - sideEffectsCheck(c, result) + instantiateBody(c, n, result) + sideEffectsCheck(c, result) paramsTypeCheck(c, result.typ) else: result = oldPrc diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f91222477..6e47af837 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -203,6 +203,14 @@ proc nMinusOne(n: PNode): PNode = newSymNode(getSysMagic("<", mUnaryLt)), n]) +proc makeRangeWithStaticExpr(c: PContext, n: PNode): PType = + let intType = getSysType tyInt + result = newTypeS(tyRange, c) + result.sons = @[intType] + result.n = newNode(nkRange, n.info, @[ + newIntTypeNode(nkIntLit, 0, intType), + makeStaticExpr(c, n.nMinusOne)]) + proc semArray(c: PContext, n: PNode, prev: PType): PType = var indx, base: PType result = newOrPrevType(tyArray, prev, c) @@ -220,7 +228,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = internalAssert c.inGenericContext > 0 if not isOrdinalType(e.typ.lastSon): localError(n[1].info, errOrdinalTypeExpected) - indx = e.typ + indx = makeRangeWithStaticExpr(c, e) elif e.kind in nkCallKinds and hasGenericArguments(e): if not isOrdinalType(e.typ): localError(n[1].info, errOrdinalTypeExpected) @@ -229,12 +237,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = # We are going to construct a range type that will be # properly filled-out in semtypinst (see how tyStaticExpr # is handled there). - let intType = getSysType(tyInt) - indx = newTypeS(tyRange, c) - indx.sons = @[intType] - indx.n = newNode(nkRange, n.info, @[ - newIntTypeNode(nkIntLit, 0, intType), - makeStaticExpr(c, e.nMinusOne)]) + indx = makeRangeWithStaticExpr(c, e) else: indx = e.typ.skipTypes({tyTypeDesc}) addSonSkipIntLit(result, indx) @@ -283,6 +286,18 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = result = result.typ.sym.copySym result.typ = copyType(result.typ, result.typ.owner, true) result.typ.flags.incl tfUnresolved + + if result.kind == skGenericParam: + if result.typ.kind == tyGenericParam and result.typ.len == 0 and + tfWildcard in result.typ.flags: + # collapse the wild-card param to a type + result.kind = skType + result.typ.flags.excl tfWildcard + return + else: + localError(n.info, errTypeExpected) + return errorSym(c, n) + if result.kind != skType: # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter @@ -737,7 +752,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result.kind = tyUserTypeClassInst result.rawAddSon paramType.lastSon return addImplicitGeneric(result) - + result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) @@ -787,11 +802,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = addImplicitGeneric(newTypeS(tyAnything, c)) of tyGenericParam: - if tfGenericTypeParam in paramType.flags and false: - if paramType.sonsLen > 0: - result = liftingWalk(paramType.lastSon) - else: - result = addImplicitGeneric(newTypeS(tyAnything, c)) + if tfWildcard in paramType.flags: + paramType.flags.excl tfWildcard + paramType.sym.kind = skType else: discard @@ -804,8 +817,8 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = else: result = semTypeNode(c, n, nil) -proc semProcTypeNode(c: PContext, n, genericParams: PNode, - prev: PType, kind: TSymKind): PType = +proc semProcTypeNode(c: PContext, n, genericParams: PNode, + prev: PType, kind: TSymKind): PType = var res: PNode cl: TIntSet @@ -894,6 +907,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result.sons[0] = r res.typ = r + if genericParams != nil: + for n in genericParams: + if tfUnresolved in n.sym.typ.flags: + n.sym.kind = skType + n.sym.typ.flags.excl tfWildcard + proc semStmtListType(c: PContext, n: PNode, prev: PType): PType = checkMinSonsLen(n, 1) var length = sonsLen(n) @@ -944,7 +963,6 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = localError(n.info, errNoGenericParamsAllowedForX, s.name.s) return newOrPrevType(tyError, prev, c) else: - var m = newCandidate(c, s, n) matches(c, n, copyTree(n), m) @@ -1262,6 +1280,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = if typ == nil: typ = newTypeS(tyGenericParam, c) + if father == nil: typ.flags.incl tfWildcard typ.flags.incl tfGenericTypeParam @@ -1272,8 +1291,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = # type for each generic param. the index # of the parameter will be stored in the # attached symbol. - var s = case finalType.kind - of tyStatic: + var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) else: newSymG(skType, a.sons[j], c).linkTo(finalType) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 80e2aa284..f8cddd781 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -394,21 +394,11 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = propagateToOwner(result, result.sons[i]) result.n = replaceTypeVarsN(cl, result.n) - - # XXX: This is not really needed? - # if result.kind in GenericTypes: - # localError(cl.info, errCannotInstantiateX, typeToString(t, preferName)) - + case result.kind of tyArray: let idx = result.sons[0] - if idx.kind == tyStatic: - if idx.n == nil: - let lookup = lookupTypeVar(cl, idx) - internalAssert lookup != nil - idx.n = lookup.n - - result.sons[0] = makeRangeType(cl.c, 0, idx.n.intVal - 1, idx.n.info) + internalAssert idx.kind != tyStatic of tyObject, tyTuple: propagateFieldFlags(result, result.n) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 19f10def8..049c9520b 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -620,8 +620,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: fRange = prev result = typeRel(c, f.sons[1], a.sons[1]) - if result < isGeneric: result = isNone - elif lengthOrd(fRange) != lengthOrd(a): result = isNone + if result < isGeneric: + result = isNone + elif lengthOrd(fRange) != lengthOrd(a): + result = isNone else: discard of tyOpenArray, tyVarargs: case a.kind @@ -867,7 +869,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # any value" and what we need is "match any type", which can be encoded # by a tyTypeDesc params. Unfortunately, this requires more substantial # changes in semtypinst and elsewhere. - if a.kind == tyTypeDesc: + if a.kind == tyTypeDesc or tfWildcard in a.flags: if f.sonsLen == 0: result = isGeneric else: @@ -883,11 +885,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isGeneric if result == isGeneric: - var concrete = concreteType(c, a) - if concrete == nil: - result = isNone + var concrete = a + if tfWildcard in a.flags: + a.sym.kind = skType + a.flags.excl tfWildcard else: - if doBind: put(c.bindings, f, concrete) + concrete = concreteType(c, a) + if concrete == nil: + return isNone + if doBind: + put(c.bindings, f, concrete) elif a.kind == tyEmpty: result = isGeneric elif x.kind == tyGenericParam: @@ -1025,11 +1032,20 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, arg = argSemantized argType = argType c = m.c - + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to # make sure that static types enter here + + # XXX: weaken tyGenericParam and call it tyGenericPlaceholder + # and finally start using tyTypedesc for generic types properly. + if argType.kind == tyGenericParam and tfWildcard in argType.flags: + argType.assignType(f) + argType.flags.incl tfUnresolved + # put(m.bindings, f, argType) + return argSemantized + var evaluated = c.semTryConstExpr(c, arg) if evaluated != nil: arg.typ = newTypeS(tyStatic, c) From 27c2c1e75ce9f7bf21dcbbb4625f494a6ac5837e Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Mar 2014 19:32:49 +0200 Subject: [PATCH 181/249] fix #996 --- compiler/ast.nim | 8 +++++--- tests/metatype/tstaticparams.nim | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 55de43dd1..a48ba9dc5 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -398,7 +398,7 @@ const tyUserTypeClass, tyUserTypeClassInst, tyAnd, tyOr, tyNot, tyAnything} - tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses + tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses type TTypeKinds* = set[TTypeKind] @@ -969,7 +969,9 @@ var emptyNode* = newNode(nkEmpty) # There is a single empty node that is shared! Do not overwrite it! proc isMetaType*(t: PType): bool = - return t.kind in tyMetaTypes or tfHasMeta in t.flags + return t.kind in tyMetaTypes or + (t.kind == tyStatic and t.n == nil) or + tfHasMeta in t.flags proc linkTo*(t: PType, s: PSym): PType {.discardable.} = t.sym = s @@ -1313,7 +1315,7 @@ proc propagateToOwner*(owner, elem: PType) = if tfShared in elem.flags: owner.flags.incl tfHasShared - if elem.kind in tyMetaTypes: + if elem.isMetaType: owner.flags.incl tfHasMeta if elem.kind in {tyString, tyRef, tySequence} or diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim index e76dae23c..fa162f4e8 100644 --- a/tests/metatype/tstaticparams.nim +++ b/tests/metatype/tstaticparams.nim @@ -14,6 +14,9 @@ type TA2[T; I: static[int]] = array[0..I, T] TA3[T; I: static[int]] = array[I-1, T] + TObj = object + x: TA3[int, 3] + proc takeFoo(x: TFoo) = echo "abracadabra" echo TFoo.Val @@ -28,6 +31,7 @@ var t1: TA1[float, 1] t2: TA2[string, 4] t3: TA3[int, 10] + t4: TObj # example from the manual: type From 7dcf6ff50b03dfd54968383ad5a4258f040eec1b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 15 Mar 2014 19:43:50 +0200 Subject: [PATCH 182/249] fix #997 --- compiler/sigmatch.nim | 13 +++++++------ tests/metatype/tstaticparams.nim | 9 ++++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 049c9520b..433455365 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1046,12 +1046,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, # put(m.bindings, f, argType) return argSemantized - var evaluated = c.semTryConstExpr(c, arg) - if evaluated != nil: - arg.typ = newTypeS(tyStatic, c) - arg.typ.sons = @[evaluated.typ] - arg.typ.n = evaluated - argType = arg.typ + if argType.kind != tyStatic: + var evaluated = c.semTryConstExpr(c, arg) + if evaluated != nil: + arg.typ = newTypeS(tyStatic, c) + arg.typ.sons = @[evaluated.typ] + arg.typ.n = evaluated + argType = arg.typ var a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor}) diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim index fa162f4e8..d14de7d65 100644 --- a/tests/metatype/tstaticparams.nim +++ b/tests/metatype/tstaticparams.nim @@ -1,6 +1,6 @@ discard """ file: "tstaticparams.nim" - output: "abracadabra\ntest\n3\n15\4" + output: "abracadabra\ntest\n3\n15\n4\n2" """ type @@ -49,3 +49,10 @@ proc getRows(mtx: Matrix): int = result = mtx.M echo getRows(m) + +# issue 997 +type TTest[T: static[int], U: static[int]] = array[0..T*U, int] +type TTestSub[N: static[int]] = TTest[1, N] + +var x: TTestSub[2] +echo x.high From ac271e76b18110bea8046af64ceccd6b804978dd Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 16 Mar 2014 11:39:53 +0200 Subject: [PATCH 183/249] more robust handling of proc signatures containing inter-param type references --- compiler/semexprs.nim | 13 ++++--- compiler/seminst.nim | 69 +++++++++++++++++++++++++++++------ compiler/semtypes.nim | 22 +++++++---- compiler/semtypinst.nim | 10 ++--- compiler/sigmatch.nim | 13 ++++++- tests/generics/tsigtypeop.nim | 9 +++++ 6 files changed, 106 insertions(+), 30 deletions(-) create mode 100644 tests/generics/tsigtypeop.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 133b4ac1e..5a12156ec 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -899,10 +899,13 @@ proc makeDeref(n: PNode): PNode = addSon(result, a) t = skipTypes(t.sons[0], {tyGenericInst}) -proc readTypeParameter(c: PContext, ty: PType, +const tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} + +proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = - internalAssert ty.kind == tyGenericInst - let ty = ty.skipGenericAlias + let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias + else: (internalAssert typ.kind == tyCompositeTypeClass; typ.sons[1]) + let tbody = ty.sons[0] for s in countup(0, tbody.len-2): let tParam = tbody.sons[s] @@ -946,7 +949,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result.typ = ty markUsed(n, f) return - of tyGenericInst: + of tyTypeParamsHolders: return readTypeParameter(c, ty, i, n.info) of tyObject, tyTuple: if ty.n.kind == nkRecList: @@ -996,7 +999,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we didn't find any field, let's look for a generic param - if result == nil and n.sons[0].typ.kind == tyGenericInst: + if result == nil and n.sons[0].typ.kind in tyTypeParamsHolders: result = readTypeParameter(c, n.sons[0].typ, i, n.info) proc dotTransformation(c: PContext, n: PNode): PNode = diff --git a/compiler/seminst.nim b/compiler/seminst.nim index dca9e3378..3d3227e4e 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -85,19 +85,21 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) = proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) +proc addProcDecls(c: PContext, fn: PSym) = + # get the proc itself in scope (e.g. for recursion) + addDecl(c, fn) + + for i in 1 .. Date: Sun, 16 Mar 2014 13:19:07 +0200 Subject: [PATCH 184/249] use g++ and clang++ automatically when compiling to C++ --- compiler/extccomp.nim | 52 +++++++++++++++++++++++++------------------ compiler/msgs.nim | 2 ++ 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 12761f1d4..a7ee7f7cc 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -16,7 +16,7 @@ import type TSystemCC* = enum ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc, - ccTcc, ccPcc, ccUcc, ccIcl, ccGpp + ccTcc, ccPcc, ccUcc, ccIcl TInfoCCProp* = enum # properties of the C compiler: hasSwitchRange, # CC allows ranges in switch statements (GNU C) hasComputedGoto, # CC has computed goto (GNU C extension) @@ -33,11 +33,12 @@ type optSpeed: string, # the options for optimization for speed optSize: string, # the options for optimization for size compilerExe: string, # the compiler's executable + cppCompiler: string, # name of the C++ compiler's executable (if supported) compileTmpl: string, # the compile command template buildGui: string, # command to build a GUI application buildDll: string, # command to build a shared library buildLib: string, # command to build a static library - linkerExe: string, # the linker's executable + linkerExe: string, # the linker's executable (if not matching compiler's) linkTmpl: string, # command to link files to produce an exe includeCmd: string, # command to add an include dir linkDirCmd: string, # command to add a lib dir @@ -63,11 +64,12 @@ compiler gcc: optSpeed: " -O3 -ffast-math ", optSize: " -Os -ffast-math ", compilerExe: "gcc", + cppCompiler: "g++", compileTmpl: "-c $options $include -o $objfile $file", buildGui: " -mwindows", buildDll: " -shared", buildLib: "ar rcs $libfile $objfiles", - linkerExe: "gcc", + linkerExe: "", linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options", includeCmd: " -I", linkDirCmd: " -L", @@ -77,32 +79,21 @@ compiler gcc: asmStmtFrmt: "asm($1);$n", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, hasNakedAttribute}) - -compiler gpp: - result = gcc() - - result.name = "gpp" - result.compilerExe = "g++" - result.linkerExe = "g++" - - result.buildDll = " -mdll" - # XXX: Hmm, I'm keeping this from the previos version, - # but my gcc doesn't even have such an option (is this mingw?) compiler llvmGcc: result = gcc() result.name = "llvm_gcc" result.compilerExe = "llvm-gcc" + result.cppCompiler = "llvm-g++" result.buildLib = "llvm-ar rcs $libfile $objfiles" - result.linkerExe = "llvm-gcc" compiler clang: result = llvmGcc() result.name = "clang" result.compilerExe = "clang" - result.linkerExe = "clang" + result.cppCompiler = "clang++" compiler vcc: result = ( @@ -111,6 +102,7 @@ compiler vcc: optSpeed: " /Ogityb2 /G7 /arch:SSE2 ", optSize: " /O1 /G7 ", compilerExe: "cl", + cppCompiler: "cl", compileTmpl: "/c $options $include /Fo$objfile $file", buildGui: " /link /SUBSYSTEM:WINDOWS ", buildDll: " /LD", @@ -131,7 +123,7 @@ compiler icl: result = vcc() else: result = gcc() - + result.name = "icl" result.compilerExe = "icl" result.linkerExe = "icl" @@ -143,6 +135,7 @@ compiler lcc: optSpeed: " -O -p6 ", optSize: " -O -p6 ", compilerExe: "lcc", + cppCompiler: "", compileTmpl: "$options $include -Fo$objfile $file", buildGui: " -subsystem windows", buildDll: " -dll", @@ -164,6 +157,7 @@ compiler bcc: optSpeed: " -O2 -6 ", optSize: " -O1 -6 ", compilerExe: "bcc32", + cppCompiler: "", compileTmpl: "-c $options $include -o$objfile $file", buildGui: " -tW", buildDll: " -tWD", @@ -185,6 +179,7 @@ compiler dmc: optSpeed: " -ff -o -6 ", optSize: " -ff -o -6 ", compilerExe: "dmc", + cppCompiler: "", compileTmpl: "-c $options $include -o$objfile $file", buildGui: " -L/exet:nt/su:windows", buildDll: " -WD", @@ -206,6 +201,7 @@ compiler wcc: optSpeed: " -ox -on -6 -d0 -fp6 -zW ", optSize: "", compilerExe: "wcl386", + cppCompiler: "", compileTmpl: "-c $options $include -fo=$objfile $file", buildGui: " -bw", buildDll: " -bd", @@ -227,6 +223,7 @@ compiler tcc: optSpeed: "", optSize: "", compilerExe: "tcc", + cppCompiler: "", compileTmpl: "-c $options $include -o $objfile $file", buildGui: "UNAVAILABLE!", buildDll: " -shared", @@ -249,6 +246,7 @@ compiler pcc: optSpeed: " -Ox ", optSize: " -Os ", compilerExe: "cc", + cppCompiler: "", compileTmpl: "-c $options $include -Fo$objfile $file", buildGui: " -SUBSYSTEM:WINDOWS", buildDll: " -DLL", @@ -270,6 +268,7 @@ compiler ucc: optSpeed: " -O3 ", optSize: " -O1 ", compilerExe: "cc", + cppCompiler: "", compileTmpl: "-c $options $include -o $objfile $file", buildGui: "", buildDll: " -shared ", @@ -297,8 +296,7 @@ const tcc(), pcc(), ucc(), - icl(), - gpp()] + icl()] const hExt* = ".h" @@ -471,11 +469,21 @@ proc needsExeExt(): bool {.inline.} = result = (optGenScript in gGlobalOptions and targetOS == osWindows) or (platform.hostOS == osWindows) +proc getCompilerExe(compiler: TSystemCC): string = + result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler + else: CC[compiler].compilerExe + if result.len == 0: + rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name) + +proc getLinkerExe(compiler: TSystemCC): string = + result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe + else: compiler.getCompilerExe + proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = var c = cCompiler var options = cFileSpecificOptions(cfilename) var exe = getConfigVar(c, ".exe") - if exe.len == 0: exe = CC[c].compilerExe + if exe.len == 0: exe = c.getCompilerExe if needsExeExt(): exe = addFileExt(exe, "exe") if optGenDynLib in gGlobalOptions and @@ -493,7 +501,7 @@ proc getCompileCFileCmd*(cfilename: string, isExternal = false): string = compilePattern = joinPath(ccompilerpath, exe) else: includeCmd = "" - compilePattern = CC[c].compilerExe + compilePattern = c.getCompilerExe var cfile = if noAbsolutePaths(): extractFilename(cfilename) else: cfilename @@ -600,7 +608,7 @@ proc callCCompiler*(projectfile: string) = if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd) else: var linkerExe = getConfigVar(c, ".linkerexe") - if len(linkerExe) == 0: linkerExe = CC[c].linkerExe + if len(linkerExe) == 0: linkerExe = c.getLinkerExe if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe") if noAbsolutePaths(): linkCmd = quoteShell(linkerExe) else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe)) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 3e5a56e1e..cdafc997b 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -108,6 +108,7 @@ type errCannotInferTypeOfTheLiteral, errCannotInferReturnType, errGenericLambdaNotAllowed, + errCompilerDoesntSupportTarget, errUser, warnCannotOpenFile, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, @@ -361,6 +362,7 @@ const errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & "it is used as an operand to another routine and the types " & "of the generic paramers can be infered from the expected signature.", + errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]", From f0953db3ba59f2e23df2fb7932c672f5020db5fb Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 16 Mar 2014 17:12:30 +0200 Subject: [PATCH 185/249] handle arbitrary expressions dependent on static input params in proc signatures --- compiler/ast.nim | 3 ++- compiler/ccgutils.nim | 5 +++-- compiler/semexprs.nim | 14 +++++++++++++- compiler/semtypes.nim | 8 +++++--- compiler/semtypinst.nim | 9 ++++++--- compiler/sigmatch.nim | 8 ++++++-- compiler/types.nim | 11 +++++++---- tests/effects/teffects1.nim | 2 +- tests/generics/tlateboundstatic.nim | 16 ++++++++++++++++ tests/overload/tissue966.nim | 2 +- 10 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 tests/generics/tlateboundstatic.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index a48ba9dc5..1dff21503 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -432,8 +432,9 @@ type tfFromGeneric, # type is an instantiation of a generic; this is needed # because for instantiations of objects, structural # type equality has to be used - tfUnresolved, # marks unresolved typedesc params: e.g. + tfUnresolved, # marks unresolved typedesc/static params: e.g. # proc foo(T: typedesc, list: seq[T]): var T + # proc foo(L: static[int]): array[L, int] tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) tfCapturesEnv, # whether proc really captures some environment diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index da1673ca4..1d8f0158b 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -87,9 +87,10 @@ proc getUniqueType*(key: PType): PType = gCanonicalTypes[k] = key result = key of tyTypeDesc, tyTypeClasses, tyGenericParam, - tyFromExpr, tyStatic, tyFieldAccessor: + tyFromExpr, tyFieldAccessor: internalError("GetUniqueType") - of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, tyConst, tyIter: + of tyGenericInst, tyDistinct, tyOrdinal, tyMutable, + tyConst, tyIter, tyStatic: result = getUniqueType(lastSon(key)) of tyArrayConstr, tyGenericInvokation, tyGenericBody, tyOpenArray, tyArray, tySet, tyRange, tyTuple, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5a12156ec..2c7408047 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -612,7 +612,19 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = if result.isNil: result = n else: return result result.typ = semfold.getIntervalType(callee.magic, call) - + + block maybeLabelAsStatic: + # XXX: temporary work-around needed for tlateboundstatic. + # This is certainly not correct, but it will get the job + # done until we have a more robust infrastructure for + # implicit statics. + if n.len > 1: + for i in 1 .. Date: Sun, 16 Mar 2014 18:34:37 +0200 Subject: [PATCH 186/249] make some tests green --- compiler/lookups.nim | 5 ++++- compiler/semexprs.nim | 10 ++++++++-- compiler/seminst.nim | 16 +++++++++------- compiler/semtypes.nim | 7 ++++--- compiler/sigmatch.nim | 9 ++++++--- compiler/vmgen.nim | 2 +- ...bindtypedesc.nim => tinvalidbindtypedesc.nim} | 0 tests/metatype/tstaticparams.nim | 4 ++-- tests/metatype/tusertypeclasses.nim | 6 +++--- tests/stdlib/tpegs.nim | 2 +- tests/testament/tester.nim | 3 ++- tests/vm/twrongconst.nim | 7 +++---- tests/vm/twrongwhen.nim | 4 ++-- 13 files changed, 45 insertions(+), 30 deletions(-) rename tests/bind/{tbindtypedesc.nim => tinvalidbindtypedesc.nim} (100%) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 8239f2a47..60125177c 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -127,7 +127,10 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) = elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options: # BUGFIX: check options in s! if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}: - message(s.info, hintXDeclaredButNotUsed, getSymRepr(s)) + # XXX: implicit type params are currently skTypes + # maybe they can be made skGenericParam as well. + if s.typ != nil and tfImplicitTypeParam notin s.typ.flags: + message(s.info, hintXDeclaredButNotUsed, getSymRepr(s)) s = nextIter(it, scope.symbols) proc wrongRedefinition*(info: TLineInfo, s: string) = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2c7408047..7e141cb24 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -325,8 +325,13 @@ proc isOpImpl(c: PContext, n: PNode): PNode = tfIterator notin t.flags)) else: var t2 = n[2].typ.skipTypes({tyTypeDesc}) + # XXX: liftParamType started to perform addDecl + # we could do that instead in semTypeNode by snooping for added + # gnrc. params, then it won't be necessary to open a new scope here + openScope(c) let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info), t2, ":anon", n.info) + closeScope(c) if lifted != nil: t2 = lifted var m: TCandidate initCandidate(c, m, t2) @@ -1257,8 +1262,9 @@ proc semProcBody(c: PContext, n: PNode): PNode = result = semAsgn(c, a) else: discardCheck(c, result) - - if c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: + + if c.p.owner.kind notin {skMacro, skTemplate} and + c.p.resultSym != nil and c.p.resultSym.typ.isMetaType: localError(c.p.resultSym.info, errCannotInferReturnType) closeScope(c) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 3d3227e4e..a5149a842 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -117,12 +117,9 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) = var oldPrc = c.generics[i].inst.sym pushInfoContext(oldPrc.info) openScope(c) - pushProcCon(c, oldPrc) - addProcDecls(c, oldPrc) var n = oldPrc.ast n.sons[bodyPos] = copyTree(s.getBody) instantiateBody(c, n, oldPrc) - popProcCon(c) closeScope(c) popInfoContext() @@ -164,10 +161,11 @@ proc instantiateProcType(c: PContext, pt: TIdTable, # at this point semtypinst have to become part of sem, because it # will need to use openScope, addDecl, etc # + addDecl(c, prc) + pushInfoContext(info) var cl = initTypeVars(c, pt, info) var result = instCopyType(cl, prc.typ) - addDecl(c, prc) let originalParams = result.n result.n = originalParams.shallowCopy @@ -175,10 +173,14 @@ proc instantiateProcType(c: PContext, pt: TIdTable, result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) propagateToOwner(result, result.sons[i]) let param = replaceTypeVarsN(cl, originalParams[i]) - internalAssert param.kind == nkSym result.n.sons[i] = param - addDecl(c, param.sym) - + if param.kind == nkSym: + # XXX: this won't be true for void params + # implement pass-through of void params and + # the "sort by distance to point" container + param.sym.owner = prc + addDecl(c, param.sym) + result.sons[0] = replaceTypeVarsT(cl, result.sons[0]) result.n.sons[0] = originalParams[0].copyTree diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 4e97a6744..06fcd74b4 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -220,7 +220,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = else: let e = semExprWithType(c, n.sons[1], {efDetermineType}) if e.typ.kind == tyFromExpr: - indx = e.typ + indx = makeRangeWithStaticExpr(c, e.typ.n) elif e.kind in {nkIntLit..nkUInt64Lit}: indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) elif e.kind == nkSym and e.typ.kind == tyStatic: @@ -783,7 +783,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = paramType result.lastSon.shouldHaveMeta - let liftBody = liftingWalk(paramType.lastSon) + let liftBody = liftingWalk(paramType.lastSon, true) if liftBody != nil: result = liftBody result.shouldHaveMeta @@ -795,7 +795,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, let expanded = instGenericContainer(c, info, paramType, allowMetaTypes = true) - result = liftingWalk(expanded) + result = liftingWalk(expanded, true) of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true)) @@ -964,6 +964,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = elif s.typ.kind != tyGenericBody: #we likely got code of the form TypeA[TypeB] where TypeA is #not generic. + debug s.typ localError(n.info, errNoGenericParamsAllowedForX, s.name.s) return newOrPrevType(tyError, prev, c) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index b87d27cb4..662268380 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -522,8 +522,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = template bindingRet(res) = when res == isGeneric: - let bound = aOrig.skipTypes({tyRange}).skipIntLit - put(c.bindings, f, bound) + if doBind: + let bound = aOrig.skipTypes({tyRange}).skipIntLit + if doBind: put(c.bindings, f, bound) return res template considerPreviousT(body: stmt) {.immediate.} = @@ -869,7 +870,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # any value" and what we need is "match any type", which can be encoded # by a tyTypeDesc params. Unfortunately, this requires more substantial # changes in semtypinst and elsewhere. - if a.kind == tyTypeDesc or tfWildcard in a.flags: + if tfWildcard in a.flags: + result = isGeneric + elif a.kind == tyTypeDesc: if f.sonsLen == 0: result = isGeneric else: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 0fc71189d..d3eda5db3 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1569,7 +1569,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "xmlConstructor": + #if s.name.s == "foo": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc diff --git a/tests/bind/tbindtypedesc.nim b/tests/bind/tinvalidbindtypedesc.nim similarity index 100% rename from tests/bind/tbindtypedesc.nim rename to tests/bind/tinvalidbindtypedesc.nim diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim index d14de7d65..6d7c569e0 100644 --- a/tests/metatype/tstaticparams.nim +++ b/tests/metatype/tstaticparams.nim @@ -54,5 +54,5 @@ echo getRows(m) type TTest[T: static[int], U: static[int]] = array[0..T*U, int] type TTestSub[N: static[int]] = TTest[1, N] -var x: TTestSub[2] -echo x.high +var z: TTestSub[2] +echo z.high diff --git a/tests/metatype/tusertypeclasses.nim b/tests/metatype/tusertypeclasses.nim index eb6597e4d..a5d575dbf 100644 --- a/tests/metatype/tusertypeclasses.nim +++ b/tests/metatype/tusertypeclasses.nim @@ -26,7 +26,7 @@ foo 10 foo "test" foo(@[TObj(x: 10), TObj(x: 20)]) -proc intval(x: int) = discard +proc intval(x: int): int = 10 # check real and virtual fields type @@ -34,8 +34,8 @@ type T.x y(T) intval T.y - let y = intval(T.y) - + let z = intval(T.y) + proc y(x: TObj): int = 10 proc testFoo(x: TFoo) = discard diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index 7775091a1..6e488bab4 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -72,7 +72,7 @@ type rule: TNode ## the rule that the symbol refers to TNode {.final, shallow.} = object case kind: TPegKind - of pkEmpty..pkWhitespace: discard + of pkEmpty..pkWhitespace: nil of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index fac97cf2a..d32269ad6 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -109,6 +109,7 @@ proc addResult(r: var TResults, test: TTest, expected = expected, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) + echo " [", $success, "]" proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = if strip(expected.msg) notin strip(given.msg): @@ -146,7 +147,7 @@ proc testSpec(r: var TResults, test: TTest) = # major entry point for a single test let tname = test.name.addFileExt(".nim") inc(r.total) - echo extractFilename(tname) + stdout.write extractFilename(tname) var expected = parseSpec(tname) if expected.err == reIgnored: r.addResult(test, "", "", reIgnored) diff --git a/tests/vm/twrongconst.nim b/tests/vm/twrongconst.nim index e5b8a15bd..5c0c80f9f 100644 --- a/tests/vm/twrongconst.nim +++ b/tests/vm/twrongconst.nim @@ -1,10 +1,9 @@ discard """ - output: "Error: cannot evaluate at compile time: x" - line: 10 + errormsg: "cannot evaluate at compile time: x" + line: 9 """ -var x: array[100, char] +var x: array[100, char] template Foo : expr = x[42] - const myConst = foo diff --git a/tests/vm/twrongwhen.nim b/tests/vm/twrongwhen.nim index 085bb6fb6..d67e42883 100644 --- a/tests/vm/twrongwhen.nim +++ b/tests/vm/twrongwhen.nim @@ -1,9 +1,9 @@ discard """ - output: "Error: cannot evaluate at compile time: x" + errormsg: "cannot evaluate at compile time: x" line: 7 """ -proc bla(x:int) = +proc bla(x:int) = when x == 0: echo "oops" else: From 26890b6525f12f89c2a93c1cde1c444e6af2e9d7 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 16 Mar 2014 20:52:09 +0200 Subject: [PATCH 187/249] make some tests red :) (i.e. print failure details while running the tester) --- tests/bind/tinvalidbindtypedesc.nim | 1 - tests/effects/teffects1.nim | 2 +- tests/overload/tissue966.nim | 2 +- tests/testament/tester.nim | 11 ++++++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/bind/tinvalidbindtypedesc.nim b/tests/bind/tinvalidbindtypedesc.nim index d6fbae537..7d97d2e0d 100644 --- a/tests/bind/tinvalidbindtypedesc.nim +++ b/tests/bind/tinvalidbindtypedesc.nim @@ -1,6 +1,5 @@ discard """ line: 11 - file: "tbindtypedesc.nim" errormsg: "type mismatch: got (typedesc[float], string)" """ diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index 2cf3d0483..0014cff46 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -1,5 +1,5 @@ discard """ - line: 1912 + line: 1913 file: "system.nim" errormsg: "can raise an unlisted exception: ref EIO" """ diff --git a/tests/overload/tissue966.nim b/tests/overload/tissue966.nim index 59c43c8f9..2911348cf 100644 --- a/tests/overload/tissue966.nim +++ b/tests/overload/tissue966.nim @@ -1,5 +1,5 @@ discard """ - msg: "type mismatch: got (PTest)" + errormsg: "type mismatch: got (PTest)" """ type diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index d32269ad6..923cd7518 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -11,7 +11,7 @@ import parseutils, strutils, pegs, os, osproc, streams, parsecfg, json, - marshal, backend, parseopt, specs, htmlgen, browsers + marshal, backend, parseopt, specs, htmlgen, browsers, terminal const resultsFile = "testresults.html" @@ -109,7 +109,12 @@ proc addResult(r: var TResults, test: TTest, expected = expected, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) - echo " [", $success, "]" + if success notin {reSuccess, reIgnored}: + styledEcho styleBright, fgRed, "^^^ [", $success, "]" + styledEcho styleDim, "EXPECTED:" + echo expected + styledEcho styleDim, "GIVEN:" + echo given proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) = if strip(expected.msg) notin strip(given.msg): @@ -147,7 +152,7 @@ proc testSpec(r: var TResults, test: TTest) = # major entry point for a single test let tname = test.name.addFileExt(".nim") inc(r.total) - stdout.write extractFilename(tname) + echo extractFilename(tname) var expected = parseSpec(tname) if expected.err == reIgnored: r.addResult(test, "", "", reIgnored) From b3da0ab88824c0f59f8f6829d1641aebbc82042b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 16 Mar 2014 21:44:21 +0200 Subject: [PATCH 188/249] fix #1007 --- compiler/semtypes.nim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 06fcd74b4..2f1532e6a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -945,6 +945,11 @@ proc semGenericParamInInvokation(c: PContext, n: PNode): PType = result = semTypeNode(c, n, nil) proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = + if s.typ == nil: + localError(n.info, "cannot instantiate the '$1' $2" % + [s.name.s, ($s.kind).substr(2).toLower]) + return newOrPrevType(tyError, prev, c) + result = newOrPrevType(tyGenericInvokation, prev, c) addSonSkipIntLit(result, s.typ) @@ -954,17 +959,13 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = rawAddSon(result, typ) else: addSonSkipIntLit(result, typ) - if s.typ == nil: - localError(n.info, errCannotInstantiateX, s.name.s) - return newOrPrevType(tyError, prev, c) - elif s.typ.kind == tyForward: + if s.typ.kind == tyForward: for i in countup(1, sonsLen(n)-1): var elem = semGenericParamInInvokation(c, n.sons[i]) addToResult(elem) elif s.typ.kind != tyGenericBody: #we likely got code of the form TypeA[TypeB] where TypeA is #not generic. - debug s.typ localError(n.info, errNoGenericParamsAllowedForX, s.name.s) return newOrPrevType(tyError, prev, c) else: From 1e45bb79ab4beb7a027b403bd78e879975b09963 Mon Sep 17 00:00:00 2001 From: Audun Wilhelmsen Date: Tue, 18 Mar 2014 21:11:57 +0100 Subject: [PATCH 189/249] Added support for {.packed.} pragma on objects Added tests for packed and union pragmas --- compiler/ast.nim | 1 + compiler/ccgtypes.nim | 25 ++++++++++++------ compiler/extccomp.nim | 20 +++++++++++++++ compiler/pragmas.nim | 6 ++++- compiler/semexprs.nim | 2 +- compiler/semtypes.nim | 2 +- compiler/wordrecg.nim | 4 +-- tests/objects/tobjpragma.nim | 49 ++++++++++++++++++++++++++++++++++++ 8 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 tests/objects/tobjpragma.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 1dff21503..00fe3b246 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -450,6 +450,7 @@ type tfHasMeta, # type contains "wildcard" sub-types such as generic params # or other type classes tfHasGCedMem, # type contains GC'ed memory + tfPacked tfHasStatic tfGenericTypeParam tfImplicitTypeParam diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 321462044..319d6a5c5 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -464,24 +464,33 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope, check: var TIntSet): PRope = # declare the record: var hasField = false - let aStruct = structOrUnion(typ) + + var attribute: PRope = + if tfPacked in typ.flags: toRope(CC[ccompiler].packedPragma) + else: nil + + result = ropecg(m, CC[ccompiler].structStmtFmt, + [structOrUnion(typ), name, attribute]) + if typ.kind == tyObject: + if typ.sons[0] == nil: if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags: - result = ropecg(m, "$1 $2 {$n", [aStruct, name]) + appcg(m, result, " {$n", []) else: - result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name]) + appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute]) hasField = true elif gCmd == cmdCompileToCpp: - result = ropecg(m, "$1 $2 : public $3 {$n", - [aStruct, name, getTypeDescAux(m, typ.sons[0], check)]) + appcg(m, result, " : public $1 {$n", + [getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - result = ropecg(m, "$1 $2 {$n $3 Sup;$n", - [aStruct, name, getTypeDescAux(m, typ.sons[0], check)]) + appcg(m, result, " {$n $1 Sup;$n", + [getTypeDescAux(m, typ.sons[0], check)]) hasField = true else: - result = ropef("$1 $2 {$n", [aStruct, name]) + appf(result, " {$n", [name]) + var desc = getRecordFields(m, typ, check) if (desc == nil) and not hasField: appf(result, "char dummy;$n", []) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index a7ee7f7cc..02e15f232 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -47,6 +47,8 @@ type pic: string, # command for position independent code # used on some platforms asmStmtFrmt: string, # format of ASM statement + structStmtFmt: string, # Format for struct statement + packedPragma: string, # Attribute/pragma to make struct packed (1-byte aligned) props: TInfoCCProps] # properties of the C compiler @@ -77,6 +79,8 @@ compiler gcc: debug: "", pic: "-fPIC", asmStmtFrmt: "asm($1);$n", + structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name + packedPragma: "__attribute__((__packed__))", props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm, hasNakedAttribute}) @@ -115,6 +119,8 @@ compiler vcc: debug: " /GZ /Zi ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$3$#1 $2", + packedPragma: "#pragma pack(1)", props: {hasCpp, hasAssume, hasNakedDeclspec}) compiler icl: @@ -148,6 +154,8 @@ compiler lcc: debug: " -g5 ", pic: "", asmStmtFrmt: "_asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {}) compiler bcc: @@ -170,6 +178,8 @@ compiler bcc: debug: "", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {hasCpp}) compiler dmc: @@ -192,6 +202,8 @@ compiler dmc: debug: " -g ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$3$n$1 $2", + packedPragma: "#pragma pack(1)", props: {hasCpp}) compiler wcc: @@ -214,6 +226,8 @@ compiler wcc: debug: " -d2 ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {hasCpp}) compiler tcc: @@ -236,6 +250,8 @@ compiler tcc: debug: " -g ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {hasSwitchRange, hasComputedGoto}) compiler pcc: @@ -259,6 +275,8 @@ compiler pcc: debug: " -Zi ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {}) compiler ucc: @@ -281,6 +299,8 @@ compiler ucc: debug: "", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", + structStmtFmt: "$1 $2", + packedPragma: "", # XXX: not supported yet props: {}) const diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index fba4e7657..efd06eef4 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -52,7 +52,7 @@ const typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, - wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion} + wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportCpp, wImportObjC, wError} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -655,6 +655,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.flags, sfThread) incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) + of wPacked: + noVal(it) + if sym.typ == nil: invalidPragma(it) + else: incl(sym.typ.flags, tfPacked) of wHint: message(it.info, hintUser, expectStrLit(c, it)) of wWarning: message(it.info, warnUser, expectStrLit(c, it)) of wError: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 7e141cb24..a6e2eaa23 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -921,7 +921,7 @@ const tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias - else: (internalAssert typ.kind == tyCompositeTypeClass; typ.sons[1]) + else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1]) let tbody = ty.sons[0] for s in countup(0, tbody.len-2): diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 2f1532e6a..f0ef43be4 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -204,7 +204,7 @@ proc nMinusOne(n: PNode): PNode = n]) proc makeRangeWithStaticExpr(c: PContext, n: PNode): PType = - let intType = getSysType tyInt + let intType = getSysType(tyInt) result = newTypeS(tyRange, c) result.sons = @[intType] result.n = newNode(nkRange, n.info, @[ diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index f4a0c59f4..9fdb3bac6 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -72,7 +72,7 @@ type wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast, wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch, wThis, wThrow, wTrue, wTypedef, wTypeid, wTypename, - wUnion, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t, + wUnion, wPacked, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t, wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept, wThread_local, wStatic_assert, wChar16_t, wChar32_t, @@ -155,7 +155,7 @@ const "private", "protected", "public", "register", "reinterpret_cast", "short", "signed", "sizeof", "static_cast", "struct", "switch", "this", "throw", "true", "typedef", "typeid", - "typename", "union", "unsigned", "virtual", "void", "volatile", + "typename", "union", "packed", "unsigned", "virtual", "void", "volatile", "wchar_t", "alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept", diff --git a/tests/objects/tobjpragma.nim b/tests/objects/tobjpragma.nim new file mode 100644 index 000000000..f9fbd5e40 --- /dev/null +++ b/tests/objects/tobjpragma.nim @@ -0,0 +1,49 @@ +discard """ + file: "tobjpragma.nim" + output: '''2 +3 +9 +257 +1 +2 +3''' +""" + +# Test + +type + Foo {.packed.} = object + a: int8 + b: int8 + + Bar {.packed.} = object + a: int8 + b: int16 + + Daz {.packed.} = object + a: int32 + b: int8 + c: int32 + + +var f = Foo(a: 1, b: 1) +var b: Bar +var d: Daz + +echo sizeof(f) +echo sizeof(b) +echo sizeof(d) +echo (cast[ptr int16](f.addr)[]) + +type + Union {.union.} = object + a: int8 + b: int8 + +var u: Union +u.a = 1 +echo u.b +u.a = 2 +echo u.b +u.b = 3 +echo u.a From ca228e46b56d98204f8976009a6f4f827008df53 Mon Sep 17 00:00:00 2001 From: Audun Wilhelmsen Date: Tue, 18 Mar 2014 21:23:35 +0100 Subject: [PATCH 190/249] Fix compilation error when using packed pragma with vcc. --- compiler/extccomp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 02e15f232..f0e5dad11 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -119,7 +119,7 @@ compiler vcc: debug: " /GZ /Zi ", pic: "", asmStmtFrmt: "__asm{$n$1$n}$n", - structStmtFmt: "$3$#1 $2", + structStmtFmt: "$3$n$1 $2", packedPragma: "#pragma pack(1)", props: {hasCpp, hasAssume, hasNakedDeclspec}) From 4b7655fd10d81ea50d0af41152df07ec21b05232 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 19 Mar 2014 02:52:48 +0200 Subject: [PATCH 191/249] reference implementation of a vector swizzle library This also provides the initial steps towards support for type class "filtered" type inference fixes an "ordinal type expected" ICE, related to the use of static params --- compiler/ast.nim | 2 + compiler/sem.nim | 3 + compiler/semcall.nim | 19 ++++++- compiler/semdata.nim | 21 +++++++ compiler/semexprs.nim | 43 ++++++++++----- compiler/semstmts.nim | 15 ++--- compiler/semtypes.nim | 55 ++++++++++--------- compiler/sigmatch.nim | 33 +++++++++-- compiler/types.nim | 1 + tests/bind/tinvalidbindtypedesc.nim | 2 +- tests/metatype/swizzle.nim | 79 +++++++++++++++++++++++++++ tests/metatype/typeclassinference.nim | 10 ++++ 12 files changed, 225 insertions(+), 58 deletions(-) create mode 100644 tests/metatype/swizzle.nim create mode 100644 tests/metatype/typeclassinference.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 00fe3b246..ae1b34fd9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -435,6 +435,8 @@ type tfUnresolved, # marks unresolved typedesc/static params: e.g. # proc foo(T: typedesc, list: seq[T]): var T # proc foo(L: static[int]): array[L, int] + # can be attached to ranges to indicate that the range + # depends on unresolved static params. tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) tfCapturesEnv, # whether proc really captures some environment diff --git a/compiler/sem.nim b/compiler/sem.nim index 093fc9452..c35cff027 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -66,6 +66,9 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = result = copyTree(arg) result.typ = formal +proc inferWithMetatype(c: PContext, formal: PType, + arg: PNode, coerceDistincts = false): PNode + var commonTypeBegin = PType(kind: tyExpr) proc commonType*(x, y: PType): PType = diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 5d480bc98..b994a94d7 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -228,12 +228,25 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = if m.genericConverter and result != nil: instGenericConvertersArg(c, result, m) -proc convertTo*(c: PContext, f: PType, n: PNode): PNode = +proc inferWithMetatype(c: PContext, formal: PType, + arg: PNode, coerceDistincts = false): PNode = var m: TCandidate - initCandidate(c, m, f) - result = paramTypesMatch(m, f, n.typ, n, nil) + initCandidate(c, m, formal) + m.coerceDistincts = coerceDistincts + result = paramTypesMatch(m, formal, arg.typ, arg, nil) if m.genericConverter and result != nil: instGenericConvertersArg(c, result, m) + if result != nil: + # This almost exactly replicates the steps taken by the compiler during + # param matching. It performs an embarassing ammount of back-and-forth + # type jugling, but it's the price to pay for consistency and correctness + result.typ = generateTypeInstance(c, m.bindings, arg.info, + formal.skipTypes({tyCompositeTypeClass})) + else: + typeMismatch(arg, formal, arg.typ) + # error correction: + result = copyTree(arg) + result.typ = formal proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = assert x.state == csMatch diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 088b93fae..5ec66b51c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -251,6 +251,27 @@ proc makeNotType*(c: PContext, t1: PType): PType = propagateToOwner(result, t1) result.flags.incl(t1.flags * {tfHasStatic}) +proc nMinusOne*(n: PNode): PNode = + result = newNode(nkCall, n.info, @[ + newSymNode(getSysMagic("<", mUnaryLt)), + n]) + +# Remember to fix the procs below this one when you make changes! +proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = + let intType = getSysType tyInt + result = newTypeS(tyRange, c) + result.sons = @[intType] + result.n = newNode(nkRange, n.info, @[ + newIntTypeNode(nkIntLit, 0, intType), + makeStaticExpr(c, n.nMinusOne)]) + +template rangeHasStaticIf*(t: PType): bool = + # this accepts the ranges's node + t.n[1].kind == nkStaticExpr + +template getStaticTypeFromRange*(t: PType): PType = + t.n[1][0][1].typ + proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a6e2eaa23..b8c4f3297 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -121,6 +121,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = return n of skType: markUsed(n, s) + if s.typ.kind == tyStatic and s.typ.n != nil: + return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) else: @@ -191,15 +193,35 @@ proc isCastable(dst, src: PType): bool = proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices +proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = + # XXX: liftParamType started to perform addDecl + # we could do that instead in semTypeNode by snooping for added + # gnrc. params, then it won't be necessary to open a new scope here + openScope(c) + var lifted = liftParamType(c, skType, newNodeI(nkArgList, info), + t, ":anon", info) + closeScope(c) + if lifted != nil: t = lifted + proc semConv(c: PContext, n: PNode): PNode = if sonsLen(n) != 2: localError(n.info, errConvNeedsOneArg) return n + result = newNodeI(nkConv, n.info) - result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst}) - addSon(result, copyTree(n.sons[0])) - addSon(result, semExprWithType(c, n.sons[1])) - var op = result.sons[1] + var targetType = semTypeNode(c, n.sons[0], nil) + maybeLiftType(targetType, c, n[0].info) + result.addSon copyTree(n.sons[0]) + var op = semExprWithType(c, n.sons[1]) + + if targetType.isMetaType: + let final = inferWithMetatype(c, targetType, op, true) + result.addSon final + result.typ = final.typ + return + + result.typ = targetType + addSon(result, op) if not isSymChoice(op): let status = checkConvertible(c, result.typ, op.typ) @@ -221,7 +243,7 @@ proc semConv(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(op) - 1): let it = op.sons[i] let status = checkConvertible(c, result.typ, it.typ) - if status == convOK: + if status in {convOK, convNotNeedeed}: markUsed(n, it.sym) markIndirect(c, it.sym) return it @@ -325,14 +347,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode = tfIterator notin t.flags)) else: var t2 = n[2].typ.skipTypes({tyTypeDesc}) - # XXX: liftParamType started to perform addDecl - # we could do that instead in semTypeNode by snooping for added - # gnrc. params, then it won't be necessary to open a new scope here - openScope(c) - let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info), - t2, ":anon", n.info) - closeScope(c) - if lifted != nil: t2 = lifted + maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) let match = typeRel(m, t2, t1) != isNone @@ -1202,7 +1217,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: n.typ = enforceVoidContext - if resultTypeIsInferrable(lhs.sym.typ): + if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: internalAssert c.p.resultSym != nil lhs.typ = rhs.typ diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 15bfaab10..38854bc8a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -12,9 +12,6 @@ var enforceVoidContext = PType(kind: tyStmt) -proc semCommand(c: PContext, n: PNode): PNode = - result = semExprNoType(c, n) - proc semDiscard(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) @@ -133,6 +130,7 @@ proc fixNilType(n: PNode) = n.typ = nil proc discardCheck(c: PContext, result: PNode) = + if c.inTypeClass > 0: return if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: if result.kind == nkNilLit: result.typ = nil @@ -143,11 +141,6 @@ proc discardCheck(c: PContext, result: PNode) = while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil - elif c.inTypeClass > 0: - if result.typ.kind == tyBool: - let verdict = semConstExpr(c, result) - if verdict.intVal == 0: - localError(result.info, "type class predicate failed") elif result.typ.kind != tyError and gCmd != cmdInteractive: if result.typ.kind == tyNil: fixNilType(result) @@ -1324,13 +1317,17 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = return else: n.sons[i] = semExpr(c, n.sons[i]) + if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool: + let verdict = semConstExpr(c, n[i]) + if verdict.intVal == 0: + localError(result.info, "type class predicate failed") if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): voidContext = true n.typ = enforceVoidContext if i == last and (length == 1 or efWantValue in flags): n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr - elif i != last or voidContext or c.inTypeClass > 0: + elif i != last or voidContext: discardCheck(c, n.sons[i]) else: n.typ = n.sons[i].typ diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f0ef43be4..269676624 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -198,19 +198,6 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = localError(n.info, errXExpectsOneTypeParam, "range") result = newOrPrevType(tyError, prev, c) -proc nMinusOne(n: PNode): PNode = - result = newNode(nkCall, n.info, @[ - newSymNode(getSysMagic("<", mUnaryLt)), - n]) - -proc makeRangeWithStaticExpr(c: PContext, n: PNode): PType = - let intType = getSysType(tyInt) - result = newTypeS(tyRange, c) - result.sons = @[intType] - result.n = newNode(nkRange, n.info, @[ - newIntTypeNode(nkIntLit, 0, intType), - makeStaticExpr(c, n.nMinusOne)]) - proc semArray(c: PContext, n: PNode, prev: PType): PType = var indx, base: PType result = newOrPrevType(tyArray, prev, c) @@ -229,6 +216,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = if not isOrdinalType(e.typ.lastSon): localError(n[1].info, errOrdinalTypeExpected) indx = makeRangeWithStaticExpr(c, e) + indx.flags.incl tfUnresolved elif e.kind in nkCallKinds and hasGenericArguments(e): if not isOrdinalType(e.typ): localError(n[1].info, errOrdinalTypeExpected) @@ -628,17 +616,28 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if base == nil and tfInheritable notin result.flags: incl(result.flags, tfFinal) +proc findEnforcedStaticType(t: PType): PType = + # This handles types such as `static[T] and Foo`, + # which are subset of `static[T]`, hence they could + # be treated in the same way + if t.kind == tyStatic: return t + if t.kind == tyAnd: + for s in t.sons: + let t = findEnforcedStaticType(s) + if t != nil: return t + proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = template addDecl(x) = if sfGenSym notin x.flags: addDecl(c, x) if kind == skMacro: - if param.typ.kind == tyTypeDesc: - addDecl(param) - elif param.typ.kind == tyStatic: + let staticType = findEnforcedStaticType(param.typ) + if staticType != nil: var a = copySym(param) - a.typ = param.typ.base + a.typ = staticType.base addDecl(a) + elif param.typ.kind == tyTypeDesc: + addDecl(param) else: # within a macro, every param has the type PNimrodNode! let nn = getSysSym"PNimrodNode" @@ -944,14 +943,18 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType = proc semGenericParamInInvokation(c: PContext, n: PNode): PType = result = semTypeNode(c, n, nil) -proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = +proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: localError(n.info, "cannot instantiate the '$1' $2" % [s.name.s, ($s.kind).substr(2).toLower]) return newOrPrevType(tyError, prev, c) + var t = s.typ + if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: + t = t.base + result = newOrPrevType(tyGenericInvokation, prev, c) - addSonSkipIntLit(result, s.typ) + addSonSkipIntLit(result, t) template addToResult(typ) = if typ.isNil: @@ -959,23 +962,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = rawAddSon(result, typ) else: addSonSkipIntLit(result, typ) - if s.typ.kind == tyForward: + if t.kind == tyForward: for i in countup(1, sonsLen(n)-1): var elem = semGenericParamInInvokation(c, n.sons[i]) addToResult(elem) - elif s.typ.kind != tyGenericBody: + return + elif t.kind != tyGenericBody: #we likely got code of the form TypeA[TypeB] where TypeA is #not generic. localError(n.info, errNoGenericParamsAllowedForX, s.name.s) return newOrPrevType(tyError, prev, c) else: - var m = newCandidate(c, s, n) + var m = newCandidate(c, t) matches(c, n, copyTree(n), m) if m.state != csMatch: - var err = "cannot instantiate " & typeToString(s.typ) & "\n" & + var err = "cannot instantiate " & typeToString(t) & "\n" & "got: (" & describeArgs(c, n) & ")\n" & - "but expected: (" & describeArgs(c, s.typ.n, 0) & ")" + "but expected: (" & describeArgs(c, t.n, 0) & ")" localError(n.info, errGenerated, err) return newOrPrevType(tyError, prev, c) @@ -987,7 +991,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = addToResult(typ) if isConcrete: - if s.ast == nil: + if s.ast == nil and s.typ.kind != tyCompositeTypeClass: + # XXX: What kind of error is this? is it still relevant? localError(n.info, errCannotInstantiateX, s.name.s) result = newOrPrevType(tyError, prev, c) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 662268380..56490dfe3 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -40,6 +40,8 @@ type proxyMatch*: bool # to prevent instantiations genericConverter*: bool # true if a generic converter needs to # be instantiated + coerceDistincts*: bool # this is an explicit coercion that can strip away + # a distrinct type typedescMatched: bool inheritancePenalty: int # to prefer closest father object type errors*: seq[string] # additional clarifications to be displayed to the @@ -114,6 +116,9 @@ proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = initCandidate(ctx, result, callee, binding, calleeScope) +proc newCandidate*(ctx: PContext, callee: PType): TCandidate = + initCandidate(ctx, result, callee) + proc copyCandidate(a: var TCandidate, b: TCandidate) = a.c = b.c a.exactMatches = b.exactMatches @@ -460,7 +465,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, if param.kind == nkVarTy: dummyName = param[0] - dummyType = makeVarType(c, a) + dummyType = if a.kind != tyVar: makeVarType(c, a) + else: a else: dummyName = param dummyType = a @@ -470,7 +476,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, dummyParam.typ = dummyType addDecl(c, dummyParam) - var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false) + var checkedBody = c.semTryExpr(c, body.n[3].copyTree, bufferErrors = false) m.errors = bufferedMsgs clearBufferedMsgs() if checkedBody == nil: return isNone @@ -623,6 +629,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, f.sons[1], a.sons[1]) if result < isGeneric: result = isNone + elif tfUnresolved in fRange.flags and + rangeHasStaticIf(fRange): + # This is a range from an array instantiated with a generic + # static param. We must extract the static param here and bind + # it to the size of the currently supplied array. + var + rangeStaticT = fRange.getStaticTypeFromRange + replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType]) + inputUpperBound = a.sons[0].n[1].intVal + # we must correct for the off-by-one discrepancy between + # ranges and static params: + replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1) + put(c.bindings, rangeStaticT, replacementT) + result = isGeneric elif lengthOrd(fRange) != lengthOrd(a): result = isNone else: discard @@ -686,6 +706,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isSubtype of tyDistinct: if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual + elif c.coerceDistincts: result = typeRel(c, f.base, a) of tySet: if a.kind == tySet: if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): @@ -848,7 +869,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyUserTypeClass, tyUserTypeClassInst: considerPreviousT: - result = matchUserTypeClass(c.c, c, f, a) + result = matchUserTypeClass(c.c, c, f, aOrig) if result == isGeneric: put(c.bindings, f, a) @@ -863,7 +884,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyGenericParam: var x = PType(idTableGet(c.bindings, f)) if x == nil: - if c.calleeSym != nil and c.calleeSym.kind == skType and + if c.calleeSym != nil and c.callee.kind == tyGenericBody and f.kind == tyGenericParam and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match @@ -1051,7 +1072,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, # XXX: When implicit statics are the default # this will be done earlier - we just have to # make sure that static types enter here - + # XXX: weaken tyGenericParam and call it tyGenericPlaceholder # and finally start using tyTypedesc for generic types properly. if argType.kind == tyGenericParam and tfWildcard in argType.flags: @@ -1060,7 +1081,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, return argSemantized if argType.kind == tyStatic: - if m.calleeSym.kind == skType: + if m.callee.kind == tyGenericBody: result = newNodeI(nkType, argOrig.info) result.typ = makeTypeFromExpr(c, arg) return diff --git a/compiler/types.nim b/compiler/types.nim index 89b15c4a8..d7410c67a 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -436,6 +436,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyStatic: internalAssert t.len > 0 result = "static[" & typeToString(t.sons[0]) & "]" + if t.n != nil: result.add "(" & renderTree(t.n) & ")" of tyUserTypeClass: internalAssert t.sym != nil and t.sym.owner != nil return t.sym.owner.name.s diff --git a/tests/bind/tinvalidbindtypedesc.nim b/tests/bind/tinvalidbindtypedesc.nim index 7d97d2e0d..5b2f51110 100644 --- a/tests/bind/tinvalidbindtypedesc.nim +++ b/tests/bind/tinvalidbindtypedesc.nim @@ -1,5 +1,5 @@ discard """ - line: 11 + line: 10 errormsg: "type mismatch: got (typedesc[float], string)" """ diff --git a/tests/metatype/swizzle.nim b/tests/metatype/swizzle.nim new file mode 100644 index 000000000..ce18fa234 --- /dev/null +++ b/tests/metatype/swizzle.nim @@ -0,0 +1,79 @@ +discard """ + output: '''3 +[1, 3] +[2, 1, 2] +''' +""" + +import macros, strutils + +template accept(e: expr) = + static: assert(compiles(e)) + +template reject(e: expr) = + static: assert(not compiles(e)) + +proc swizzleIdx(c: char): int = + return case c + of 'x': 0 + of 'y': 1 + of 'z': 2 + of 'w': 3 + of 'r': 0 + of 'g': 1 + of 'b': 2 + of 'a': 3 + else: 0 + +proc isSwizzle(s: string): bool = + template trySet(name, set) = + block search: + for c in s: + if c notin set: + break search + return true + + trySet coords, {'x', 'y', 'z', 'w'} + trySet colors, {'r', 'g', 'b', 'a'} + + return false + +type + StringIsSwizzle = generic value + value.isSwizzle + + SwizzleStr = static[string] and StringIsSwizzle + +proc foo(x: SwizzleStr) = + echo "sw" + +accept foo("xx") +reject foo("xe") + +type + Vec[N: static[int]; T] = array[N, T] + + +proc card(x: Vec): int = x.N +proc `$`(x: Vec): string = x.repr.strip + +macro `.`(x: Vec, swizzle: SwizzleStr): expr = + var + cardinality = swizzle.len + values = newNimNode(nnkBracket) + v = genSym() + + for c in swizzle: + values.add newNimNode(nnkBracketExpr).add( + v, c.swizzleIdx.newIntLitNode) + + return quote do: + let `v` = `x` + Vec[`cardinality`, `v`.T](`values`) + +var z = Vec([1, 2, 3]) + +echo z.card +echo z.xz +echo z.yxy + diff --git a/tests/metatype/typeclassinference.nim b/tests/metatype/typeclassinference.nim new file mode 100644 index 000000000..72b5aca96 --- /dev/null +++ b/tests/metatype/typeclassinference.nim @@ -0,0 +1,10 @@ +import typetraits + +type + Vec[N: static[int]; T] = distinct array[N, T] + +var x = Vec([1, 2, 3]) + +static: + assert x.type.name == "Vec[static[int](3), int]" + From d508384d39461966ee247a01ad637bc57d3f62af Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 19 Mar 2014 22:35:31 +0200 Subject: [PATCH 192/249] fix #1013 --- compiler/sigmatch.nim | 14 ++++++++++---- tests/matrix/issue1013.nim | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 tests/matrix/issue1013.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 56490dfe3..c09964638 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -12,7 +12,7 @@ import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, - magicsys, condsyms, idents, lexer, options, parampatterns, strutils + magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees when not defined(noDocgen): import docgen @@ -973,11 +973,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # fix the expression, so it contains the already instantiated types let instantiated = replaceTypesInBody(c.c, c.bindings, f.n) let reevaluted = c.c.semExpr(c.c, instantiated) - if reevaluted.typ.kind != tyTypeDesc: + case reevaluted.typ.kind + of tyTypeDesc: + result = typeRel(c, a, reevaluted.typ.base) + of tyStatic: + result = typeRel(c, a, reevaluted.typ.base) + if result != isNone and reevaluted.typ.n != nil: + if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n): + result = isNone + else: localError(f.n.info, errTypeExpected) result = isNone - else: - result = typeRel(c, a, reevaluted.typ.base) else: internalAssert false diff --git a/tests/matrix/issue1013.nim b/tests/matrix/issue1013.nim new file mode 100644 index 000000000..7d3d52f85 --- /dev/null +++ b/tests/matrix/issue1013.nim @@ -0,0 +1,23 @@ +import typetraits + +template reject(e: expr) = + static: assert(not compiles(e)) + +type + TMatrix[T; M, N: static[int]] = array[M*N, T] + +proc `*`[T; R, N, C](a: TMatrix[T, R, N], b: TMatrix[T, N, C]): TMatrix[T, R, C] = + discard + +var m1: TMatrix[int, 6, 4] +var m2: TMatrix[int, 4, 3] +var m3: TMatrix[int, 3, 3] + +var m4 = m1*m2 +static: assert m4.M == 6 and m4.N == 3 + +reject m1 * m3 # not compatible + +var m5 = m2 * m3 +static: assert high(m5) == 11 # 4*3 - 1 + From 8a0509b3b255c2aef10b9db468810d98ae0d4215 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 20 Mar 2014 01:15:20 +0200 Subject: [PATCH 193/249] implements ``distinct with/without X, Y`` This still doesn't work quite right, because some common operations like array indexing lay completely outside the scope/symbol lookup system - they are not even magics. --- compiler/ast.nim | 6 +++++- compiler/parser.nim | 44 +++++++++++++++++++++++++++---------------- compiler/renderer.nim | 15 ++++++++++++--- compiler/semtypes.nim | 11 +++++------ compiler/sigmatch.nim | 23 +++++++++++++++++++++- 5 files changed, 72 insertions(+), 27 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index ae1b34fd9..30778c02d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -188,6 +188,10 @@ type nkStmtListType, # a statement list ending in a type; for macros nkBlockType, # a statement block ending in a type; for macros # types as syntactic trees: + + nkWith, # distinct with `foo` + nkWithout, # distinct without `foo` + nkTypeOfExpr, # type(1+2) nkObjectTy, # object body nkTupleTy, # tuple body @@ -418,7 +422,7 @@ type nfExplicitCall # x.y() was used instead of x.y nfExprCall # this is an attempt to call a regular expression nfIsRef # this node is a 'ref' node; used for the VM - + TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23) tfVarargs, # procedure has C styled varargs diff --git a/compiler/parser.nim b/compiler/parser.nim index 060629518..d59e013d7 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -67,7 +67,7 @@ proc optPar*(p: var TParser) proc optInd*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode) proc setBaseFlags*(n: PNode, base: TNumericalBase) -proc parseSymbol*(p: var TParser): PNode +proc parseSymbol*(p: var TParser, allowNil = false): PNode proc parseTry(p: var TParser): PNode proc parseCase(p: var TParser): PNode # implementation @@ -273,7 +273,7 @@ proc colcom(p: var TParser, n: PNode) = eat(p, tkColon) skipComment(p, n) -proc parseSymbol(p: var TParser): PNode = +proc parseSymbol(p: var TParser, allowNil = false): PNode = #| symbol = '`' (KEYW|IDENT|operator|'(' ')'|'[' ']'|'{' '}'|'='|literal)+ '`' #| | IDENT case p.tok.tokType @@ -312,9 +312,13 @@ proc parseSymbol(p: var TParser): PNode = break eat(p, tkAccent) else: - parMessage(p, errIdentifierExpected, p.tok) - getTok(p) # BUGFIX: We must consume a token here to prevent endless loops! - result = ast.emptyNode + if allowNil and p.tok.tokType == tkNil: + result = newNodeP(nkNilLit, p) + getTok(p) + else: + parMessage(p, errIdentifierExpected, p.tok) + getTok(p) # BUGFIX: We must consume a token here to prevent endless loops! + result = ast.emptyNode proc indexExpr(p: var TParser): PNode = #| indexExpr = expr @@ -964,14 +968,30 @@ proc isExprStart(p: TParser): bool = tkTuple, tkObject, tkType, tkWhen, tkCase, tkShared: result = true else: result = false - -proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, - mode: TPrimaryMode): PNode = + +proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) = + while true: + var s = parseSymbol(p, allowNil) + if s.kind == nkEmpty: break + addSon(result, s) + if p.tok.tokType != tkComma: break + getTok(p) + optInd(p, s) + +proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, + mode: TPrimaryMode): PNode = result = newNodeP(kind, p) getTok(p) optInd(p, result) if not isOperator(p.tok) and isExprStart(p): addSon(result, primary(p, mode)) + if kind == nkDistinctTy and p.tok.tokType in {tkWith, tkWithout}: + let nodeKind = if p.tok.tokType == tkWith: nkWith + else: nkWithout + getTok(p) + let list = newNodeP(nodeKind, p) + result.addSon list + parseSymbolList(p, list, allowNil = true) proc parseExpr(p: var TParser): PNode = #| expr = (ifExpr @@ -988,7 +1008,6 @@ proc parseExpr(p: var TParser): PNode = proc parseEnum(p: var TParser): PNode proc parseObject(p: var TParser): PNode -proc parseDistinct(p: var TParser): PNode proc parseTypeClass(p: var TParser): PNode proc primary(p: var TParser, mode: TPrimaryMode): PNode = @@ -1743,13 +1762,6 @@ proc parseTypeClass(p: var TParser): PNode = else: addSon(result, parseStmt(p)) -proc parseDistinct(p: var TParser): PNode = - #| distinct = 'distinct' optInd typeDesc - result = newNodeP(nkDistinctTy, p) - getTok(p) - optInd(p, result) - addSon(result, parseTypeDesc(p)) - proc parseTypeDef(p: var TParser): PNode = #| typeDef = identWithPragma genericParamList? '=' optInd typeDefAux #| indAndComment? diff --git a/compiler/renderer.nim b/compiler/renderer.nim index d68cb91c0..2d2310914 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -424,8 +424,11 @@ proc lsub(n: PNode): int = of nkRefTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ref") of nkPtrTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("ptr") of nkVarTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + len("var") - of nkDistinctTy: result = (if n.len > 0: lsub(n.sons[0])+1 else: 0) + - len("Distinct") + of nkDistinctTy: + result = len("distinct") + (if n.len > 0: lsub(n.sons[0])+1 else: 0) + if n.len > 1: + result += (if n[1].kind == nkWith: len("_with_") else: len("_without_")) + result += lcomma(n[1]) of nkStaticTy: result = (if n.len > 0: lsub(n.sons[0]) else: 0) + len("static[]") of nkTypeDef: result = lsons(n) + 3 @@ -1020,9 +1023,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = else: put(g, tkVar, "var") of nkDistinctTy: - if sonsLen(n) > 0: + if n.len > 0: putWithSpace(g, tkDistinct, "distinct") gsub(g, n.sons[0]) + if n.len > 1: + if n[1].kind == nkWith: + putWithSpace(g, tkWith, " with") + else: + putWithSpace(g, tkWithout, " without") + gcomma(g, n[1]) else: put(g, tkDistinct, "distinct") of nkTypeDef: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 269676624..c53dc0f7d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -139,13 +139,12 @@ proc semVarType(c: PContext, n: PNode, prev: PType): PType = addSonSkipIntLit(result, base) else: result = newConstraint(c, tyVar) - + proc semDistinct(c: PContext, n: PNode, prev: PType): PType = - if sonsLen(n) == 1: - result = newOrPrevType(tyDistinct, prev, c) - addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) - else: - result = newConstraint(c, tyDistinct) + if n.len == 0: return newConstraint(c, tyDistinct) + result = newOrPrevType(tyDistinct, prev, c) + addSonSkipIntLit(result, semTypeNode(c, n.sons[0], nil)) + if n.len > 1: result.n = n[1] proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = assert isRange(n) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index c09964638..ae31f1630 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -491,6 +491,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, return isGeneric +proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool = + if rules.kind == nkWith: + for r in rules: + if r.considerAcc == callIdent: return true + return false + else: + for r in rules: + if r.considerAcc == callIdent: return false + return true + +proc maybeSkipDistinct(t: PType, callee: PSym): PType = + if t != nil and t.kind == tyDistinct and t.n != nil and + shouldSkipDistinct(t.n, callee.name): + result = t.base + else: + result = t + proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = # typeRel can be used to establish various relationships between types: # @@ -518,7 +535,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = assert(aOrig != nil) # var and static arguments match regular modifier-free types - let a = aOrig.skipTypes({tyStatic, tyVar}) + let a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) + # XXX: Theoretically, maybeSkipDistinct could be called before we even + # start the param matching process. This could be done in `prepareOperand` + # for example, but unfortunately `prepareOperand` is not called in certain + # situation when nkDotExpr are rotated to nkDotCalls if a.kind == tyGenericInst and skipTypes(f, {tyVar}).kind notin { From b2aa70c6efd650d2187945424353cbd8ec412364 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 20 Mar 2014 22:27:49 +0200 Subject: [PATCH 194/249] fix #1018 Previously, generic types originating from the same body symbol were compared structurally. The bug illustrates a case, where such a comparison is not correct. The new approach follows the established method used in typeRel. --- compiler/types.nim | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index d7410c67a..ae31d24de 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -830,6 +830,12 @@ proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool = result = sameTypeOrNilAux(a.sons[i], b.sons[i], c) if not result: return +proc isGenericAlias*(t: PType): bool = + return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst + +proc skipGenericAlias*(t: PType): PType = + return if t.isGenericAlias: t.lastSon else: t + proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = template cycleCheck() = # believe it or not, the direct check for ``containsOrIncl(c, a, b)`` @@ -860,6 +866,19 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of dcEqOrDistinctOf: while a.kind == tyDistinct: a = a.sons[0] if a.kind != b.kind: return false + + if x.kind == tyGenericInst: + let + lhs = x.skipGenericAlias + rhs = y.skipGenericAlias + if rhs.kind != tyGenericInst or lhs.base != rhs.base: + return false + for i in 1 .. lhs.len - 2: + let ff = rhs.sons[i] + let aa = lhs.sons[i] + if not sameTypeAux(ff, aa, c): return false + return true + case a.kind of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, tyInt..tyBigNum, tyStmt, tyExpr: @@ -884,8 +903,6 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of tyTuple: cycleCheck() result = sameTuple(a, b, c) and sameFlags(a, b) - of tyGenericInst: - result = sameTypeAux(lastSon(a), lastSon(b), c) of tyTypeDesc: if c.cmp == dcEqIgnoreDistinct: result = false elif ExactTypeDescValues in c.flags: @@ -912,6 +929,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameTypeOrNilAux(a.sons[0], b.sons[0], c) and sameValue(a.n.sons[0], b.n.sons[0]) and sameValue(a.n.sons[1], b.n.sons[1]) + of tyGenericInst: discard of tyNone: result = false proc sameBackendType*(x, y: PType): bool = @@ -1005,12 +1023,6 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]], a = a.sons[i] result = a.kind == last -proc isGenericAlias*(t: PType): bool = - return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst - -proc skipGenericAlias*(t: PType): PType = - return if t.isGenericAlias: t.lastSon else: t - proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, flags: TTypeAllowedFlags = {}): bool = assert(kind in {skVar, skLet, skConst, skParam, skResult}) From f49f2f38f01b4ac0ada06bef48bf30bc0c910ce1 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 20 Mar 2014 23:12:24 +0200 Subject: [PATCH 195/249] fix the handling of "msg" tests --- tests/testament/specs.nim | 6 +++++- tests/testament/tester.nim | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index f7982127f..65e17a453 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -114,7 +114,11 @@ proc parseSpec*(filename: string): TSpec = result.substr = true of "exitcode": discard parseInt(e.value, result.exitCode) - of "errormsg", "msg": + of "msg": + result.msg = e.value + if result.action != actionRun: + result.action = actionCompile + of "errormsg": result.msg = e.value result.action = actionReject of "disabled": diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 923cd7518..6655b1b79 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -110,7 +110,7 @@ proc addResult(r: var TResults, test: TTest, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) if success notin {reSuccess, reIgnored}: - styledEcho styleBright, fgRed, "^^^ [", $success, "]" + styledEcho styleBright, name, fgRed, " [", $success, "]" styledEcho styleDim, "EXPECTED:" echo expected styledEcho styleDim, "GIVEN:" From 8ba062d952f1bb4e8330bb7f5f04c2cd949e29b3 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 20 Mar 2014 23:24:25 +0200 Subject: [PATCH 196/249] fix #1016 --- compiler/sigmatch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ae31f1630..b9fb0c957 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -905,7 +905,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyGenericParam: var x = PType(idTableGet(c.bindings, f)) if x == nil: - if c.calleeSym != nil and c.callee.kind == tyGenericBody and + if c.callee.kind == tyGenericBody and f.kind == tyGenericParam and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match From 09bda74e7d3cbc5e606be6c4b6c68230013948e3 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 20 Mar 2014 23:45:31 +0200 Subject: [PATCH 197/249] fix #1015 --- compiler/semexprs.nim | 11 ++++++++--- tests/metatype/tusertypeclasses2.nim | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 tests/metatype/tusertypeclasses2.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b8c4f3297..b3b757640 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -931,7 +931,9 @@ proc makeDeref(n: PNode): PNode = addSon(result, a) t = skipTypes(t.sons[0], {tyGenericInst}) -const tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} +const + tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} + tyDotOpTransparent = {tyVar, tyPtr, tyRef} proc readTypeParameter(c: PContext, typ: PType, paramName: PIdent, info: TLineInfo): PNode = @@ -968,6 +970,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone): if ty.kind == tyTypeDesc: ty = ty.base + ty = ty.skipTypes(tyDotOpTransparent) case ty.kind of tyEnum: # look up if the identifier belongs to the enum: @@ -1031,8 +1034,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we didn't find any field, let's look for a generic param - if result == nil and n.sons[0].typ.kind in tyTypeParamsHolders: - result = readTypeParameter(c, n.sons[0].typ, i, n.info) + if result == nil: + let t = n.sons[0].typ.skipTypes(tyDotOpTransparent) + if t.kind in tyTypeParamsHolders: + result = readTypeParameter(c, t, i, n.info) proc dotTransformation(c: PContext, n: PNode): PNode = if isSymChoice(n.sons[1]): diff --git a/tests/metatype/tusertypeclasses2.nim b/tests/metatype/tusertypeclasses2.nim new file mode 100644 index 000000000..77c70d7a6 --- /dev/null +++ b/tests/metatype/tusertypeclasses2.nim @@ -0,0 +1,24 @@ +type + hasFieldX = generic z + z.x is int + + obj_x = object + x: int + + ref_obj_x = ref object + x: int + + ref_to_obj_x = ref obj_x + + p_o_x = ptr obj_x + v_o_x = var obj_x + +template check(x) = + static: assert(x) + +check obj_x is hasFieldX +check ref_obj_x is hasFieldX +check ref_to_obj_x is hasFieldX +check p_o_x is hasFieldX +check v_o_x is hasFieldX + From b59fba584521a0a9d4fe97ee4a5d608ec19aedc4 Mon Sep 17 00:00:00 2001 From: Audun Wilhelmsen Date: Thu, 20 Mar 2014 23:11:33 +0100 Subject: [PATCH 198/249] Added documentation for packed pragma --- doc/manual.txt | 5 +++++ web/news.txt | 1 + 2 files changed, 6 insertions(+) diff --git a/doc/manual.txt b/doc/manual.txt index ab1badaf3..9497e957d 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -5390,6 +5390,11 @@ checked. **Future directions**: GC'ed memory should be allowed in unions and the GC should scan unions conservatively. +Packed pragma +------------- +The `packed`:idx: pragma can be applied to any ``object`` type. It ensures that the fields of an object is packed back-to-back in memory. It is useful to store packets or messages from/to network or hardware drivers, and for interoperability with C. Combining packed pragma with inheritance is not defined, and it should not be used with GC'ed memory (ref's). + +**Future directions**: Using GC'ed memory in packed pragma will result in compile-time error. Usage with inheretinence should be defined and documented. Unchecked pragma ---------------- diff --git a/web/news.txt b/web/news.txt index 83863cdd9..7f5526665 100644 --- a/web/news.txt +++ b/web/news.txt @@ -61,6 +61,7 @@ News evaluation. - ``--gc:none`` produces warnings when code uses the GC. - A ``union`` pragma for better C interoperability is now supported. + - A ``packed`` pragma to control the memory packing/alignment of fields in an object. - Arrays can be annotated to be ``unchecked`` for easier low level manipulations of memory. From 4f91dd40ed78ece0c41a63ad27e10127098e1d2b Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 21 Mar 2014 00:27:28 +0200 Subject: [PATCH 199/249] msg -> errmsg --- tests/array/tarrayplus.nim | 2 +- tests/generics/tbadgenericlambda.nim | 2 +- tests/generics/tmetafield.nim | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/array/tarrayplus.nim b/tests/array/tarrayplus.nim index 8c7452e85..9e08bbb0a 100644 --- a/tests/array/tarrayplus.nim +++ b/tests/array/tarrayplus.nim @@ -1,5 +1,5 @@ discard """ - msg: "type mismatch: got (array[0..2, float], array[0..1, float])" + errmsg: "type mismatch: got (array[0..2, float], array[0..1, float])" """ proc `+`*[R, T] (v1, v2: array[R, T]): array[R, T] = diff --git a/tests/generics/tbadgenericlambda.nim b/tests/generics/tbadgenericlambda.nim index 5e406cacc..38e7f6cd7 100644 --- a/tests/generics/tbadgenericlambda.nim +++ b/tests/generics/tbadgenericlambda.nim @@ -1,5 +1,5 @@ discard """ - msg: "nested proc can have generic parameters only when" + errmsg: "nested proc can have generic parameters only when" line: 6 """ diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim index 42353006d..8e7f26549 100644 --- a/tests/generics/tmetafield.nim +++ b/tests/generics/tmetafield.nim @@ -1,8 +1,8 @@ discard """ cmd: "nimrod check $# $#" - msg: "'proc' is not a concrete type" - msg: "'Foo' is not a concrete type." - msg: "invalid type: 'TBaseMed'" + errmsg: "'proc' is not a concrete type" + errmsg: "'Foo' is not a concrete type." + errmsg: "invalid type: 'TBaseMed'" """ type From 1b423139e6b9997bdda97e32d1575df41f62cd0a Mon Sep 17 00:00:00 2001 From: Audun Wilhelmsen Date: Thu, 20 Mar 2014 23:47:50 +0100 Subject: [PATCH 200/249] Fixed typo and line breaks in docs --- doc/manual.txt | 9 +++++++-- web/news.txt | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index 9497e957d..08ed6f8d1 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -5392,9 +5392,14 @@ should scan unions conservatively. Packed pragma ------------- -The `packed`:idx: pragma can be applied to any ``object`` type. It ensures that the fields of an object is packed back-to-back in memory. It is useful to store packets or messages from/to network or hardware drivers, and for interoperability with C. Combining packed pragma with inheritance is not defined, and it should not be used with GC'ed memory (ref's). +The `packed`:idx: pragma can be applied to any ``object`` type. It ensures +that the fields of an object is packed back-to-back in memory. It is useful +to store packets or messages from/to network or hardware drivers, and for +interoperability with C. Combining packed pragma with inheritance is not +defined, and it should not be used with GC'ed memory (ref's). -**Future directions**: Using GC'ed memory in packed pragma will result in compile-time error. Usage with inheretinence should be defined and documented. +**Future directions**: Using GC'ed memory in packed pragma will result in +compile-time error. Usage with inheritance should be defined and documented. Unchecked pragma ---------------- diff --git a/web/news.txt b/web/news.txt index 7f5526665..9c84b5490 100644 --- a/web/news.txt +++ b/web/news.txt @@ -61,7 +61,8 @@ News evaluation. - ``--gc:none`` produces warnings when code uses the GC. - A ``union`` pragma for better C interoperability is now supported. - - A ``packed`` pragma to control the memory packing/alignment of fields in an object. + - A ``packed`` pragma to control the memory packing/alignment of fields in + an object. - Arrays can be annotated to be ``unchecked`` for easier low level manipulations of memory. From 2a182f55aeb22149d72f7f3e2e0c96019b8dcd17 Mon Sep 17 00:00:00 2001 From: Audun Wilhelmsen Date: Fri, 21 Mar 2014 18:10:27 +0100 Subject: [PATCH 201/249] Fixed typo in docs --- doc/manual.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.txt b/doc/manual.txt index 08ed6f8d1..c4b08a942 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -5393,7 +5393,7 @@ should scan unions conservatively. Packed pragma ------------- The `packed`:idx: pragma can be applied to any ``object`` type. It ensures -that the fields of an object is packed back-to-back in memory. It is useful +that the fields of an object are packed back-to-back in memory. It is useful to store packets or messages from/to network or hardware drivers, and for interoperability with C. Combining packed pragma with inheritance is not defined, and it should not be used with GC'ed memory (ref's). From 02e97422eda48e16d07adb7e84c8f7ea66ad2926 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Mar 2014 02:43:37 +0100 Subject: [PATCH 202/249] c2nim knows about arrray[N, T] --- compiler/c2nim/cparse.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index 555e1af10..86533fe26 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -624,10 +624,11 @@ proc parseTypeSuffix(p: var TParser, typ: PNode): PNode = # array type: result = newNodeP(nkBracketExpr, p) addSon(result, newIdentNodeP("array", p)) - var r = newNodeP(nkRange, p) - addSon(r, newIntNodeP(nkIntLit, 0, p)) - addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p)) - addSon(result, r) + #var r = newNodeP(nkRange, p) + #addSon(r, newIntNodeP(nkIntLit, 0, p)) + #addSon(r, newBinary("-", index, newIntNodeP(nkIntLit, 1, p), p)) + #addSon(result, r) + addSon(result, index) addSon(result, tmp) else: # pointer type: From 28e375c4f689c07ca15ec46da8e1693490331820 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Mar 2014 02:45:35 +0100 Subject: [PATCH 203/249] improved docs about strongSpaces --- doc/manual.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/manual.txt b/doc/manual.txt index ab1badaf3..a9e6aebb4 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -565,6 +565,11 @@ or as accessors: # is parsed as echo((1,2)) +Only 0, 1, 2, 4 or 8 spaces are allowed to specify precedence and it is +enforced that infix operators have the same amount of spaces before and after +them. This rules does not apply when a newline follows after the operator, +then only the preceding spaces are considered. + Grammar ------- From a431207e528dd0d69026465ea1df27c4e37dee93 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Mar 2014 02:49:51 +0100 Subject: [PATCH 204/249] bugfix: reprEnum for enums with negative values --- lib/system/repr.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index cd3f7c3f4..7c1a68bc7 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -59,7 +59,11 @@ proc reprChar(x: char): string {.compilerRtl.} = proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = # we read an 'int' but this may have been too large, so mask the other bits: - let e = e and (1 shl (typ.size*8)-1) + let e = if typ.size == 1: e and 0xff + elif typ.size == 2: e and 0xffff + else: e + # XXX we need a proper narrowing based on signedness here + #e and ((1 shl (typ.size*8)) - 1) if ntfEnumHole notin typ.flags: if e <% typ.node.len: return $typ.node.sons[e].name From fb5ece805ff5b752ec2e9dc0c49fc34d5abdc6a9 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Mar 2014 02:51:03 +0100 Subject: [PATCH 205/249] added mapMem/unmapMem to memfiles; untested --- lib/pure/memfiles.nim | 45 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 8c404abe8..807f3da43 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2012 Nimrod Contributors +# (c) Copyright 2014 Nimrod Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -34,6 +34,43 @@ type else: handle: cint + +proc mapMem*(m: var TMemFile, mode: TFileMode = fmRead, + mappedSize = -1, offset = 0): pointer = + var readonly = mode == fmRead + when defined(windows): + result = mapViewOfFileEx( + m.mapHandle, + if readonly: FILE_MAP_READ else: FILE_MAP_WRITE, + int32(offset shr 32), + int32(offset and 0xffffffff), + if mappedSize == -1: 0 else: mappedSize, + nil) + if result == nil: + osError(osLastError()) + else: + assert mappedSize > 0 + result = mmap( + nil, + mappedSize, + if readonly: PROT_READ else: PROT_READ or PROT_WRITE, + if readonly: MAP_PRIVATE else: MAP_SHARED, + m.handle, offset) + if result == cast[pointer](MAP_FAILED): + osError(osLastError()) + + +proc unmapMem*(f: var TMemFile, p: pointer, size: int) = + ## unmaps the memory region ``(p, Date: Sat, 22 Mar 2014 02:52:06 +0100 Subject: [PATCH 206/249] fixed tuples in a static context; preparations for correct compile time evaluation of integral ops --- compiler/idgen.nim | 2 +- compiler/vm.nim | 26 +++++++++++++++++++++++++- compiler/vmdef.nim | 1 + compiler/vmgen.nim | 3 ++- doc/c2nim.txt | 2 +- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/compiler/idgen.nim b/compiler/idgen.nim index c4f5f2a9e..d932e3d9d 100644 --- a/compiler/idgen.nim +++ b/compiler/idgen.nim @@ -19,7 +19,7 @@ const when debugIds: import intsets - var usedIds = InitIntSet() + var usedIds = initIntSet() proc registerID*(id: PIdObj) = when debugIds: diff --git a/compiler/vm.nim b/compiler/vm.nim index 5c8e533f1..06f0e8886 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -902,8 +902,32 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let newLen = regs[rb].intVal.int if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess) else: setLen(regs[ra].node.sons, newLen) - of opcSwap, opcReset: + of opcSwap: + let rb = instr.regB + if regs[ra].kind == regs[rb].kind: + case regs[ra].kind + of rkNone: discard + of rkInt: swap regs[ra].intVal, regs[rb].intVal + of rkFloat: swap regs[ra].floatVal, regs[rb].floatVal + of rkNode: swap regs[ra].node, regs[rb].node + of rkRegisterAddr: swap regs[ra].regAddr, regs[rb].regAddr + of rkNodeAddr: swap regs[ra].nodeAddr, regs[rb].nodeAddr + else: + internalError(c.debug[pc], "cannot swap operands") + of opcReset: internalError(c.debug[pc], "too implement") + of opcNarrowS: + decodeBC(rkInt) + let min = -(1 shl (rc-1)) + let max = (1 shl (rc-1))-1 + if regs[rb].intVal >= min and regs[rb].intVal <= max: + regs[ra].intVal = regs[rb].intVal + else: + stackTrace(c, tos, pc, errGenerated, + msgKindToString(errUnhandledExceptionX) % "value out of range") + of opcNarrowU: + decodeBC(rkInt) + regs[ra].intVal = regs[rb].intVal and ((1'i64 shl rc)-1) of opcIsNil: decodeB(rkInt) regs[ra].intVal = ord(regs[rb].node.kind == nkNilLit) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 102fc3024..7725d79b8 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -64,6 +64,7 @@ type opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcSwap, opcIsNil, opcOf, opcIs, opcSubStr, opcConv, opcCast, opcQuit, opcReset, + opcNarrowS, opcNarrowU, opcAddStrCh, opcAddStrStr, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 0fc71189d..5e51ecf4c 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1226,7 +1226,8 @@ proc genVarSection(c: PCtx; n: PNode) = if s.position == 0: if sfImportc in s.flags: c.importcSym(a.info, s) else: - let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast + let sa = if s.ast.isNil: getNullValue(s.typ, a.info) + else: canonConst(s.ast) c.globals.add(sa) s.position = c.globals.len if a.sons[2].kind == nkEmpty: diff --git a/doc/c2nim.txt b/doc/c2nim.txt index 7dec8b995..6788ef569 100644 --- a/doc/c2nim.txt +++ b/doc/c2nim.txt @@ -74,7 +74,7 @@ Is translated into: printf("%s\x0A", x) else: template OUT*(x: expr): stmt = - nil + discard As can been seen from the example, C's macros with parameters are mapped to Nimrod's templates. This mapping is the best one can do, but it is of course From 5f478e5aa125289efa232cd57043b8ee31aad562 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Mar 2014 11:50:42 +0100 Subject: [PATCH 207/249] fixes #1010 --- compiler/msgs.nim | 2 +- compiler/vm.nim | 47 +++++++++++++++++++----------------- compiler/vmgen.nim | 8 +++--- tests/vm/tstaticprintseq.nim | 22 ++++++++++++++++- 4 files changed, 51 insertions(+), 28 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index cdafc997b..a63fbca7f 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -202,7 +202,7 @@ const errXExpectsArrayType: "\'$1\' expects an array type", errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet", errExprXAmbiguous: "expression '$1' ambiguous in this context", - errConstantDivisionByZero: "constant division by zero", + errConstantDivisionByZero: "division by zero", errOrdinalTypeExpected: "ordinal type expected", errOrdinalOrFloatTypeExpected: "ordinal or float type expected", errOverOrUnderflow: "over- or underflow", diff --git a/compiler/vm.nim b/compiler/vm.nim index 06f0e8886..f093527f8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -10,6 +10,8 @@ ## This file implements the new evaluation engine for Nimrod code. ## An instruction is 1-3 int32s in memory, it is a register based VM. +const debugEchoCode = false + import ast except getstr import @@ -328,6 +330,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = proc compile(c: PCtx, s: PSym): int = result = vmgen.genProc(c, s) + when debugEchoCode: c.echoCode result #c.echoCode proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = @@ -381,18 +384,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNodeToReg: let ra = instr.regA let rb = instr.regB - assert regs[rb].kind == rkNode - let nb = regs[rb].node - case nb.kind - of nkCharLit..nkInt64Lit: - ensureKind(rkInt) - regs[ra].intVal = nb.intVal - of nkFloatLit..nkFloat64Lit: - ensureKind(rkFloat) - regs[ra].floatVal = nb.floatVal + # opcDeref might already have loaded it into a register. XXX Let's hope + # this is still correct this way: + if regs[rb].kind != rkNode: + regs[ra] = regs[rb] else: - ensureKind(rkNode) - regs[ra].node = nb + assert regs[rb].kind == rkNode + let nb = regs[rb].node + case nb.kind + of nkCharLit..nkInt64Lit: + ensureKind(rkInt) + regs[ra].intVal = nb.intVal + of nkFloatLit..nkFloat64Lit: + ensureKind(rkFloat) + regs[ra].floatVal = nb.floatVal + else: + ensureKind(rkNode) + regs[ra].node = nb of opcLdArr: # a = b[c] decodeBC(rkNode) @@ -520,10 +528,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = regs[rb].intVal * regs[rc].intVal of opcDivInt: decodeBC(rkInt) - regs[ra].intVal = regs[rb].intVal div regs[rc].intVal + if regs[rc].intVal == 0: stackTrace(c, tos, pc, errConstantDivisionByZero) + else: regs[ra].intVal = regs[rb].intVal div regs[rc].intVal of opcModInt: decodeBC(rkInt) - regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal + if regs[rc].intVal == 0: stackTrace(c, tos, pc, errConstantDivisionByZero) + else: regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal of opcAddFloat: decodeBC(rkFloat) regs[ra].floatVal = regs[rb].floatVal + regs[rc].floatVal @@ -1174,12 +1184,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.strVal = typ.typeToString(preferExported) inc pc -proc fixType(result, n: PNode) {.inline.} = - # XXX do it deeply for complex values; there seems to be no simple - # solution except to check it deeply here. - #if result.typ.isNil: result.typ = n.typ - discard - proc execute(c: PCtx, start: int): PNode = var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) @@ -1198,8 +1202,6 @@ proc evalExpr*(c: PCtx, n: PNode): PNode = let start = genExpr(c, n) assert c.code[start].opcode != opcEof result = execute(c, start) - if not result.isNil: - fixType(result, n) # for now we share the 'globals' environment. XXX Coming soon: An API for # storing&loading the 'globals' environment to get what a component system @@ -1243,11 +1245,11 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode = let start = genExpr(c, n, requiresValue = mode!=emStaticStmt) if c.code[start].opcode == opcEof: return emptyNode assert c.code[start].opcode != opcEof + when debugEchoCode: c.echoCode start var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil) newSeq(tos.slots, c.prc.maxSlots) #for i in 0 .. Date: Sat, 22 Mar 2014 21:18:56 +0100 Subject: [PATCH 208/249] fixes #1023 --- compiler/vmgen.nim | 51 +++++++++++++++++++++++++----------------- tests/vm/tquadplus.nim | 17 ++++++++++++++ todo.txt | 3 ++- 3 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 tests/vm/tquadplus.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 0a0fcaebb..80c07f39e 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -465,6 +465,15 @@ proc needsAsgnPatch(n: PNode): bool = n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal) +proc genField(n: PNode): TRegister = + if n.kind != nkSym or n.sym.kind != skField: + internalError(n.info, "no field symbol") + let s = n.sym + if s.position > high(result): + internalError(n.info, + "too large offset! cannot generate code for: " & s.name.s) + result = s.position + proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = case le.kind of nkBracketExpr: @@ -477,10 +486,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) = # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] let dest = c.genx(left.sons[0], {gfAddrOf}) - let idx = c.genx(left.sons[1]) + let idx = genField(left.sons[1]) c.gABC(left, opcWrObj, dest, idx, value) c.freeTemp(dest) - c.freeTemp(idx) of nkDerefExpr, nkHiddenDeref: let dest = c.genx(le.sons[0], {gfAddrOf}) c.gABC(le, opcWrDeref, dest, value) @@ -1023,7 +1031,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = # XXX field checks here let left = if le.kind == nkDotExpr: le else: le.sons[0] let dest = c.genx(left.sons[0], {gfAddrOf}) - let idx = c.genx(left.sons[1]) + let idx = genField(left.sons[1]) let tmp = c.genx(ri) c.gABC(left, opcWrObj, dest, idx, tmp) c.freeTemp(tmp) @@ -1108,11 +1116,12 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = else: c.gABx(n, opcLdGlobal, dest, s.position) else: - if s.kind == skForVar and c.mode == emRepl: c.setSlot s + if s.kind == skForVar and c.mode == emRepl: c.setSlot(s) if s.position > 0 or (s.position == 0 and s.kind in {skParam,skResult}): if dest < 0: dest = s.position + ord(s.kind == skParam) + internalAssert(c.prc.slots[dest].kind < slotSomeTemp) else: # we need to generate an assignment: genAsgn(c, dest, n, c.prc.slots[dest].kind >= slotSomeTemp) @@ -1120,8 +1129,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = # see tests/t99bott for an example that triggers it: cannotEval(n) -proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; - flags: TGenFlags) = +proc genArrAccess2(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; + flags: TGenFlags) = let a = c.genx(n.sons[0], flags) let b = c.genx(n.sons[1], {}) if dest < 0: dest = c.getTemp(n.typ) @@ -1136,18 +1145,28 @@ proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; c.freeTemp(b) proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = - genAccess(c, n, dest, opcLdObj, flags) + let a = c.genx(n.sons[0], flags) + let b = genField(n.sons[1]) + if dest < 0: dest = c.getTemp(n.typ) + if gfAddrOf notin flags and fitsRegister(n.typ): + var cc = c.getTemp(n.typ) + c.gABC(n, opcLdObj, cc, a, b) + c.gABC(n, opcNodeToReg, dest, cc) + c.freeTemp(cc) + else: + c.gABC(n, opcLdObj, dest, a, b) + c.freeTemp(a) proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = # XXX implement field checks! - genAccess(c, n.sons[0], dest, opcLdObj, flags) + genObjAccess(c, n.sons[0], dest, flags) proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = if n.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { tyString, tyCString}: - genAccess(c, n, dest, opcLdStrIdx, {}) + genArrAccess2(c, n, dest, opcLdStrIdx, {}) else: - genAccess(c, n, dest, opcLdArr, flags) + genArrAccess2(c, n, dest, opcLdArr, flags) proc getNullValueAux(obj: PNode, result: PNode) = case obj.kind @@ -1304,11 +1323,10 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = for i in 1.. high(dest): - internalError(n.info, - "too large offset! cannot generate code for: " & s.name.s) - dest = s.position of skType: genTypeLit(c, s.typ, dest) else: diff --git a/tests/vm/tquadplus.nim b/tests/vm/tquadplus.nim new file mode 100644 index 000000000..552e8fef7 --- /dev/null +++ b/tests/vm/tquadplus.nim @@ -0,0 +1,17 @@ +# bug #1023 + +discard """ + output: "1 == 1" +""" + +type Quadruple = tuple[a, b, c, d: int] + +proc `+`(s, t: Quadruple): Quadruple = + (a: s.a + t.a, b: s.b + t.b, c: s.c + t.c, d: s.d + t.d) + +const + A = (a: 0, b: -1, c: 0, d: 1) + B = (a: 0, b: -2, c: 1, d: 0) + C = A + B + +echo C.d, " == ", (A+B).d diff --git a/todo.txt b/todo.txt index 3b085ac91..921862d43 100644 --- a/todo.txt +++ b/todo.txt @@ -6,7 +6,6 @@ version 0.9.4 - at least try to get the basic type zoo ops right - optimize opcAsgnStr - fix GC issues -- test and fix showoff Bugs @@ -27,6 +26,8 @@ version 0.9.x - memory manager: add a measure of fragmentation - implement 'bits' pragmas +- we need a magic thisModule symbol +- provide --nilChecks:on|off - fix closures/lambdalifting - ensure (ref T)(a, b) works as a type conversion and type constructor - optimize 'genericReset'; 'newException' leads to code bloat From b76d2eac7f551cafa0ac797330e3bd0650ab26d9 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Mar 2014 22:59:23 +0100 Subject: [PATCH 209/249] fixes #1006 --- compiler/c2nim/cparse.nim | 3 ++- compiler/semgnrc.nim | 2 ++ doc/manual.txt | 17 ++++++++++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim index 86533fe26..52d50ca39 100644 --- a/compiler/c2nim/cparse.nim +++ b/compiler/c2nim/cparse.nim @@ -1279,7 +1279,8 @@ proc leftBindingPower(p : var TParser, tok : ref TToken) : int = of pxComma: return 10 # throw == 20 - of pxAsgn, pxPlusAsgn, pxMinusAsgn, pxStarAsgn, pxSlashAsgn, pxModAsgn, pxShlAsgn, pxShrAsgn, pxAmpAsgn, pxHatAsgn, pxBarAsgn: + of pxAsgn, pxPlusAsgn, pxMinusAsgn, pxStarAsgn, pxSlashAsgn, pxModAsgn, + pxShlAsgn, pxShrAsgn, pxAmpAsgn, pxHatAsgn, pxBarAsgn: return 30 of pxConditional: return 40 diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index ffc1a43b2..2de397e25 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -94,6 +94,8 @@ proc semGenericStmt(c: PContext, n: PNode, let luf = if withinMixin notin flags: {checkUndeclared} else: {} var s = qualifiedLookUp(c, n, luf) if s != nil: result = semGenericStmtSymbol(c, n, s) + else: + # XXX for example: ``result.add`` -- ``add`` needs to be looked up here... of nkEmpty, nkSym..nkNilLit: # see tests/compile/tgensymgeneric.nim: diff --git a/doc/manual.txt b/doc/manual.txt index 5c4aa2c17..c2acd3e6a 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -499,18 +499,21 @@ other binary operators are left-associative. Precedence ---------- -For operators that are not keywords the precedence is determined by the -following rules: +Unary operators always bind stronger than any binary +operator: ``$a + b`` is ``($a) + b`` and not ``$(a + b)``. + +If an unary operator's relevant character is ``@`` it is a `sigil-like`:idx: +operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed +as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``. + + +For binary operators that are not keywords the precedence is determined by the +following rules: If the operator ends with ``=`` and its relevant character is none of ``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which has the lowest precedence. -If the operator's relevant character is ``@`` it is a `sigil-like`:idx: -operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed -as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``. - - Otherwise precedence is determined by the relevant character. ================ =============================================== ================== =============== From a4f30c36822511ce4460a5d23b2ec82e36f5e796 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Mar 2014 23:02:01 +0100 Subject: [PATCH 210/249] fixes wrong commit --- compiler/semgnrc.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 2de397e25..ffc1a43b2 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -94,8 +94,6 @@ proc semGenericStmt(c: PContext, n: PNode, let luf = if withinMixin notin flags: {checkUndeclared} else: {} var s = qualifiedLookUp(c, n, luf) if s != nil: result = semGenericStmtSymbol(c, n, s) - else: - # XXX for example: ``result.add`` -- ``add`` needs to be looked up here... of nkEmpty, nkSym..nkNilLit: # see tests/compile/tgensymgeneric.nim: From ca6b4bf0f9a8fc99247fc3147dd9760a1bde6f47 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 15 Mar 2014 14:11:29 +0000 Subject: [PATCH 211/249] Asyncio2 doc improvements. Changed recvLine's behaviour. recvLine had an edge case where it would return a partial line if the socket were to be disconnected in the middle of a line being read. The behaviour now is that the data is simply lost as it likely will be unparsable by the protocol anyway. --- lib/pure/asyncio2.nim | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index eb31eca13..c5371adf0 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -93,18 +93,15 @@ proc read*[T](future: PFuture[T]): T = proc finished*[T](future: PFuture[T]): bool = ## Determines whether ``future`` has completed. ## - ## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish. + ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. future.finished proc failed*[T](future: PFuture[T]): bool = ## Determines whether ``future`` completed with an error. future.error != nil -# TODO: Get rid of register. Do it implicitly. - when defined(windows) or defined(nimdoc): import winlean, sets, hashes - #from hashes import THash type TCompletionKey = dword @@ -794,6 +791,9 @@ proc getName(node: PNimrodNode): string {.compileTime.} = assert false macro async*(prc: stmt): stmt {.immediate.} = + ## Macro which processes async procedures into the appropriate + ## iterators and yield statements. + expectKind(prc, nnkProcDef) hint("Processing " & prc[0].getName & " as an async proc.") @@ -893,6 +893,10 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} ## will be set to it. ## ## If the socket is disconnected, ``line`` will be set to ``""``. + ## + ## If the socket is disconnected in the middle of a line (before ``\r\L`` + ## is read) then line will be set to ``""``. + ## The partial line **will be lost**. template addNLIfEmpty(): stmt = if result.len == 0: @@ -901,13 +905,9 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} result = "" var c = "" while true: - #echo("1") c = await p.recv(socket, 1) - #echo("Received ", c.len) if c.len == 0: - #echo("returning") - return - #echo("2") + return "" if c == "\r": c = await p.recv(socket, 1, MSG_PEEK) if c.len > 0 and c == "\L": @@ -917,9 +917,7 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} elif c == "\L": addNLIfEmpty() return - #echo("3") add(result.string, c) - #echo("4") when isMainModule: From 5bf45d92854dd9b95fc35c6d2a646c70432825b8 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 16 Mar 2014 14:55:39 +0000 Subject: [PATCH 212/249] Clean up IP address code in net module. --- lib/pure/net.nim | 82 +++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 9ee98cbe6..c3e14e52d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -17,43 +17,45 @@ type IPv4 ## IPv4 address TIpAddress* = object ## stores an arbitrary IP address - case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6) + case family*: IpAddressFamily ## the type of the IP address (IPv4 or IPv6) of IpAddressFamily.IPv6: - address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in case of IPv6 + address_v6*: array[0..15, uint8] ## Contains the IP address in bytes in + ## case of IPv6 of IpAddressFamily.IPv4: - address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in case of IPv4 + address_v4*: array[0..3, uint8] ## Contains the IP address in bytes in + ## case of IPv4 proc IPv4_any*(): TIpAddress = ## Returns the IPv4 any address, which can be used to listen on all available ## network adapters result = TIpAddress( family: IpAddressFamily.IPv4, - address_v4: [0'u8, 0'u8, 0'u8, 0'u8]) + address_v4: [0'u8, 0, 0, 0]) proc IPv4_loopback*(): TIpAddress = ## Returns the IPv4 loopback address (127.0.0.1) result = TIpAddress( family: IpAddressFamily.IPv4, - address_v4: [127'u8, 0'u8, 0'u8, 1'u8]) + address_v4: [127'u8, 0, 0, 1]) proc IPv4_broadcast*(): TIpAddress = ## Returns the IPv4 broadcast address (255.255.255.255) result = TIpAddress( family: IpAddressFamily.IPv4, - address_v4: [255'u8, 255'u8, 255'u8, 255'u8]) + address_v4: [255'u8, 255, 255, 255]) proc IPv6_any*(): TIpAddress = ## Returns the IPv6 any address (::0), which can be used ## to listen on all available network adapters result = TIpAddress( family: IpAddressFamily.IPv6, - address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8]) + address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) proc IPv6_loopback*(): TIpAddress = ## Returns the IPv6 loopback address (::1) result = TIpAddress( family: IpAddressFamily.IPv6, - address_v6: [0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,0'u8,1'u8]) + address_v6: [0'u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]) proc `==`*(lhs, rhs: TIpAddress): bool = ## Compares two IpAddresses for Equality. Returns two if the addresses are equal @@ -138,19 +140,23 @@ proc parseIPv4Address(address_str: string): TIpAddress = for i in 0 .. high(address_str): if address_str[i] in strutils.Digits: # Character is a number - currentByte = currentByte * 10 + cast[uint16](ord(address_str[i]) - ord('0')) + currentByte = currentByte * 10 + + cast[uint16](ord(address_str[i]) - ord('0')) if currentByte > 255'u16: - raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") + raise newException(EInvalidValue, + "Invalid IP Address. Value is out of range") seperatorValid = true elif address_str[i] == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: - raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") + raise newException(EInvalidValue, + "Invalid IP Address. The address consists of too many groups") result.address_v4[byteCount] = cast[uint8](currentByte) currentByte = 0 byteCount.inc seperatorValid = false else: - raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") + raise newException(EInvalidValue, + "Invalid IP Address. Address contains an invalid character") if byteCount != 3 or not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address") @@ -160,7 +166,8 @@ proc parseIPv6Address(address_str: string): TIpAddress = ## Parses IPv6 adresses ## Raises EInvalidValue on errors result.family = IpAddressFamily.IPv6 - if address_str.len < 2: raise newException(EInvalidValue, "Invalid IP Address") + if address_str.len < 2: + raise newException(EInvalidValue, "Invalid IP Address") var groupCount = 0 @@ -174,13 +181,19 @@ proc parseIPv6Address(address_str: string): TIpAddress = for i,c in address_str: if c == ':': - if not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid seperator") + if not seperatorValid: + raise newException(EInvalidValue, + "Invalid IP Address. Address contains an invalid seperator") if lastWasColon: - if dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address. Address contains more than one \"::\" seperator") + if dualColonGroup != -1: + raise newException(EInvalidValue, + "Invalid IP Address. Address contains more than one \"::\" seperator") dualColonGroup = groupCount seperatorValid = false elif i != 0 and i != high(address_str): - if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") + if groupCount >= 8: + raise newException(EInvalidValue, + "Invalid IP Address. The address consists of too many groups") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) currentShort = 0 @@ -188,14 +201,17 @@ proc parseIPv6Address(address_str: string): TIpAddress = if dualColonGroup != -1: seperatorValid = false elif i == 0: # only valid if address starts with :: if address_str[1] != ':': - raise newException(EInvalidValue, "Invalid IP Address. Address may not start with \":\"") + raise newException(EInvalidValue, + "Invalid IP Address. Address may not start with \":\"") else: # i == high(address_str) - only valid if address ends with :: if address_str[high(address_str)-1] != ':': - raise newException(EInvalidValue, "Invalid IP Address. Address may not end with \":\"") + raise newException(EInvalidValue, + "Invalid IP Address. Address may not end with \":\"") lastWasColon = true currentGroupStart = i + 1 elif c == '.': # Switch to parse IPv4 mode - if i < 3 or not seperatorValid or groupCount >= 7: raise newException(EInvalidValue, "Invalid IP Address") + if i < 3 or not seperatorValid or groupCount >= 7: + raise newException(EInvalidValue, "Invalid IP Address") v4StartPos = currentGroupStart currentShort = 0 seperatorValid = false @@ -208,16 +224,20 @@ proc parseIPv6Address(address_str: string): TIpAddress = else: # Upper case hex currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10 if currentShort > 65535'u32: - raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") + raise newException(EInvalidValue, + "Invalid IP Address. Value is out of range") lastWasColon = false seperatorValid = true else: - raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") + raise newException(EInvalidValue, + "Invalid IP Address. Address contains an invalid character") if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff if seperatorValid: # Copy remaining data - if groupCount >= 8: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") + if groupCount >= 8: + raise newException(EInvalidValue, + "Invalid IP Address. The address consists of too many groups") result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8) result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) groupCount.inc() @@ -226,7 +246,8 @@ proc parseIPv6Address(address_str: string): TIpAddress = if c in strutils.Digits: # Character is a number currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) if currentShort > 255'u32: - raise newException(EInvalidValue, "Invalid IP Address. Value is out of range") + raise newException(EInvalidValue, + "Invalid IP Address. Value is out of range") seperatorValid = true elif c == '.': # IPv4 address separator if not seperatorValid or byteCount >= 3: @@ -236,7 +257,8 @@ proc parseIPv6Address(address_str: string): TIpAddress = byteCount.inc() seperatorValid = false else: # Invalid character - raise newException(EInvalidValue, "Invalid IP Address. Address contains an invalid character") + raise newException(EInvalidValue, + "Invalid IP Address. Address contains an invalid character") if byteCount != 3 or not seperatorValid: raise newException(EInvalidValue, "Invalid IP Address") @@ -245,17 +267,21 @@ proc parseIPv6Address(address_str: string): TIpAddress = # Shift and fill zeros in case of :: if groupCount > 8: - raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") + raise newException(EInvalidValue, + "Invalid IP Address. The address consists of too many groups") elif groupCount < 8: # must fill - if dualColonGroup == -1: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too few groups") + if dualColonGroup == -1: + raise newException(EInvalidValue, + "Invalid IP Address. The address consists of too few groups") var toFill = 8 - groupCount # The number of groups to fill var toShift = groupCount - dualColonGroup # Nr of known groups after :: for i in 0..2*toShift-1: # shift result.address_v6[15-i] = result.address_v6[groupCount*2-i-1] for i in 0..2*toFill-1: # fill with 0s result.address_v6[dualColonGroup*2+i] = 0 - elif dualColonGroup != -1: raise newException(EInvalidValue, "Invalid IP Address. The address consists of too many groups") - + elif dualColonGroup != -1: + raise newException(EInvalidValue, + "Invalid IP Address. The address consists of too many groups") proc parseIpAddress*(address_str: string): TIpAddress = ## Parses an IP address From 2ce9f1c77f9aa4504c55c75c57e74a5ad840916d Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 22 Mar 2014 15:42:40 +0000 Subject: [PATCH 213/249] Copied most blocking socket operations to the net module. --- lib/pure/asyncio2.nim | 4 +- lib/pure/net.nim | 831 +++++++++++++++++++++++++++++++++++- lib/pure/sockets2.nim | 201 ++++++++- tests/async/tasyncawait.nim | 17 +- 4 files changed, 1020 insertions(+), 33 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index c5371adf0..fbb02c37c 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -9,7 +9,7 @@ import os, oids, tables, strutils, macros -import sockets2, net +import sockets2 ## Asyncio2 ## -------- @@ -772,7 +772,7 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = else: discard of nnkDiscardStmt: # discard await x - if node[0][0].ident == !"await": + if node[0][0].kind == nnkIdent and node[0][0].ident == !"await": var dummy = newNimNode(nnkStmtList) createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy) else: discard diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c3e14e52d..45883166b 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -9,7 +9,8 @@ ## This module implements a high-level cross-platform sockets interface. -import sockets2, os, strutils, unsigned +{.deadCodeElim: on.} +import sockets2, os, strutils, unsigned, parseutils, times type IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address @@ -293,14 +294,232 @@ proc parseIpAddress*(address_str: string): TIpAddress = else: return parseIPv4Address(address_str) +when defined(ssl): + import openssl + +# Note: The enumerations are mapped to Window's constants. + +when defined(ssl): + type + ESSL* = object of ESynch + + TSSLCVerifyMode* = enum + CVerifyNone, CVerifyPeer + + TSSLProtVersion* = enum + protSSLv2, protSSLv3, protTLSv1, protSSLv23 + + PSSLContext* = distinct PSSLCTX + + TSSLAcceptResult* = enum + AcceptNoClient = 0, AcceptNoHandshake, AcceptSuccess + +const + BufferSize*: int = 4000 ## size of a buffered socket's buffer type - TSocket* = TSocketHandle + TSocketImpl = object ## socket type + fd: TSocketHandle + case isBuffered: bool # determines whether this socket is buffered. + of true: + buffer: array[0..BufferSize, char] + currPos: int # current index in buffer + bufLen: int # current length of buffer + of false: nil + when defined(ssl): + case isSsl: bool + of true: + sslHandle: PSSL + sslContext: PSSLContext + sslNoHandshake: bool # True if needs handshake. + sslHasPeekChar: bool + sslPeekChar: char + of false: nil + + PSocket* = ref TSocketImpl -proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {. + TSOBool* = enum ## Boolean socket options. + OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive, + OptOOBInline, OptReuseAddr + + TReadLineResult* = enum ## result for readLineAsync + ReadFullLine, ReadPartialLine, ReadDisconnected, ReadNone + + ETimeout* = object of ESynch + +proc newSocket(fd: TSocketHandle, isBuff: bool): PSocket = + assert fd != osInvalidSocket + new(result) + result.fd = fd + result.isBuffered = isBuff + if isBuff: + result.currPos = 0 + +proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, + protocol: TProtocol = IPPROTO_TCP, buffered = true): PSocket = + ## Creates a new socket. + ## + ## If an error occurs EOS will be raised. + let fd = sockets2.socket(domain, typ, protocol) + if fd == osInvalidSocket: + osError(osLastError()) + result = newSocket(fd, buffered) + +when defined(ssl): + CRYPTO_malloc_init() + SslLibraryInit() + SslLoadErrorStrings() + ErrLoadBioStrings() + OpenSSL_add_all_algorithms() + + proc SSLError(s = "") = + if s != "": + raise newException(ESSL, s) + let err = ErrPeekLastError() + if err == 0: + raise newException(ESSL, "No error reported.") + if err == -1: + OSError(OSLastError()) + var errStr = ErrErrorString(err, nil) + raise newException(ESSL, $errStr) + + # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html + proc loadCertificates(ctx: PSSL_CTX, certFile, keyFile: string) = + if certFile != "" and not existsFile(certFile): + raise newException(system.EIO, "Certificate file could not be found: " & certFile) + if keyFile != "" and not existsFile(keyFile): + raise newException(system.EIO, "Key file could not be found: " & keyFile) + + if certFile != "": + var ret = SSLCTXUseCertificateChainFile(ctx, certFile) + if ret != 1: + SSLError() + + # TODO: Password? www.rtfm.com/openssl-examples/part1.pdf + if keyFile != "": + if SSL_CTX_use_PrivateKey_file(ctx, keyFile, + SSL_FILETYPE_PEM) != 1: + SSLError() + + if SSL_CTX_check_private_key(ctx) != 1: + SSLError("Verification of private key file failed.") + + proc newContext*(protVersion = ProtSSLv23, verifyMode = CVerifyPeer, + certFile = "", keyFile = ""): PSSLContext = + ## Creates an SSL context. + ## + ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 are + ## are available with the addition of ``ProtSSLv23`` which allows for + ## compatibility with all of them. + ## + ## There are currently only two options for verify mode; + ## one is ``CVerifyNone`` and with it certificates will not be verified + ## the other is ``CVerifyPeer`` and certificates will be verified for + ## it, ``CVerifyPeer`` is the safest choice. + ## + ## The last two parameters specify the certificate file path and the key file + ## path, a server socket will most likely not work without these. + ## Certificates can be generated using the following command: + ## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``. + var newCTX: PSSL_CTX + case protVersion + of protSSLv23: + newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. + of protSSLv2: + when not defined(linux): + newCTX = SSL_CTX_new(SSLv2_method()) + else: + SSLError() + of protSSLv3: + newCTX = SSL_CTX_new(SSLv3_method()) + of protTLSv1: + newCTX = SSL_CTX_new(TLSv1_method()) + + if newCTX.SSLCTXSetCipherList("ALL") != 1: + SSLError() + case verifyMode + of CVerifyPeer: + newCTX.SSLCTXSetVerify(SSLVerifyPeer, nil) + of CVerifyNone: + newCTX.SSLCTXSetVerify(SSLVerifyNone, nil) + if newCTX == nil: + SSLError() + + discard newCTX.SSLCTXSetMode(SSL_MODE_AUTO_RETRY) + newCTX.loadCertificates(certFile, keyFile) + return PSSLContext(newCTX) + + proc wrapSocket*(ctx: PSSLContext, socket: PSocket) = + ## Wraps a socket in an SSL context. This function effectively turns + ## ``socket`` into an SSL socket. + ## + ## **Disclaimer**: This code is not well tested, may be very unsafe and + ## prone to security vulnerabilities. + + socket.isSSL = true + socket.sslContext = ctx + socket.sslHandle = SSLNew(PSSLCTX(socket.sslContext)) + socket.sslNoHandshake = false + socket.sslHasPeekChar = false + if socket.sslHandle == nil: + SSLError() + + if SSLSetFd(socket.sslHandle, socket.fd) != 1: + SSLError() + +proc socketError*(socket: PSocket, err: int = -1, async = false) = + ## Raises an EOS error based on the error code returned by ``SSLGetError`` + ## (for SSL sockets) and ``osLastError`` otherwise. + ## + ## If ``async`` is ``True`` no error will be thrown in the case when the + ## error was caused by no data being available to be read. + ## + ## If ``err`` is not lower than 0 no exception will be raised. + when defined(ssl): + if socket.isSSL: + if err <= 0: + var ret = SSLGetError(socket.sslHandle, err.cint) + case ret + of SSL_ERROR_ZERO_RETURN: + SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: + if async: + return + else: SSLError("Not enough data on socket.") + of SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_READ: + if async: + return + else: SSLError("Not enough data on socket.") + of SSL_ERROR_WANT_X509_LOOKUP: + SSLError("Function for x509 lookup has been called.") + of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: + SSLError() + else: SSLError("Unknown Error") + + if err == -1 and not (when defined(ssl): socket.isSSL else: false): + let lastError = osLastError() + if async: + when defined(windows): + if lastError.int32 == WSAEWOULDBLOCK: + return + else: osError(lastError) + else: + if lastError.int32 == EAGAIN or lastError.int32 == EWOULDBLOCK: + return + else: osError(lastError) + else: osError(lastError) + +proc listen*(socket: PSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} = + ## Marks ``socket`` as accepting connections. + ## ``Backlog`` specifies the maximum length of the + ## queue of pending connections. + ## + ## Raises an EOS error upon failure. + if listen(socket.fd, backlog) < 0'i32: osError(osLastError()) + +proc bindAddr*(socket: PSocket, port = TPort(0), address = "") {. tags: [FReadIO].} = - - ## binds an address/port number to a socket. + ## Binds an address/port number to a socket. ## Use address string in dotted decimal form like "a.b.c.d" ## or leave "" for any address. @@ -312,27 +531,599 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {. name.sin_family = toInt(AF_INET) name.sin_port = htons(int16(port)) name.sin_addr.s_addr = htonl(INADDR_ANY) - if bindAddr(socket, cast[ptr TSockAddr](addr(name)), + if bindAddr(socket.fd, cast[ptr TSockAddr](addr(name)), sizeof(name).TSocklen) < 0'i32: osError(osLastError()) else: var aiList = getAddrInfo(address, port, AF_INET) - if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32: + if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32: dealloc(aiList) osError(osLastError()) dealloc(aiList) -proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} = - ## Sets blocking mode on socket - when defined(Windows): - var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking - if ioctlsocket(s, FIONBIO, addr(mode)) == -1: - osError(osLastError()) - else: # BSD sockets - var x: int = fcntl(s, F_GETFL, 0) - if x == -1: - osError(osLastError()) +proc acceptAddr*(server: PSocket, client: var PSocket, address: var string) {. + tags: [FReadIO].} = + ## Blocks until a connection is being made from a client. When a connection + ## is made sets ``client`` to the client socket and ``address`` to the address + ## of the connecting client. + ## This function will raise EOS if an error occurs. + ## + ## The resulting client will inherit any properties of the server socket. For + ## example: whether the socket is buffered or not. + ## + ## **Note**: ``client`` must be initialised (with ``new``), this function + ## makes no effort to initialise the ``client`` variable. + assert(client != nil) + var sockAddress: Tsockaddr_in + var addrLen = sizeof(sockAddress).TSocklen + var sock = accept(server.fd, cast[ptr TSockAddr](addr(sockAddress)), + addr(addrLen)) + + if sock == osInvalidSocket: + let err = osLastError() + osError(err) + else: + client.fd = sock + client.isBuffered = server.isBuffered + + # Handle SSL. + when defined(ssl): + if server.isSSL: + # We must wrap the client sock in a ssl context. + + server.sslContext.wrapSocket(client) + let ret = SSLAccept(client.sslHandle) + socketError(client, ret, false) + + # Client socket is set above. + address = $inet_ntoa(sockAddress.sin_addr) + +when false: #defined(ssl): + proc acceptAddrSSL*(server: PSocket, client: var PSocket, + address: var string): TSSLAcceptResult {. + tags: [FReadIO].} = + ## This procedure should only be used for non-blocking **SSL** sockets. + ## It will immediately return with one of the following values: + ## + ## ``AcceptSuccess`` will be returned when a client has been successfully + ## accepted and the handshake has been successfully performed between + ## ``server`` and the newly connected client. + ## + ## ``AcceptNoHandshake`` will be returned when a client has been accepted + ## but no handshake could be performed. This can happen when the client + ## connects but does not yet initiate a handshake. In this case + ## ``acceptAddrSSL`` should be called again with the same parameters. + ## + ## ``AcceptNoClient`` will be returned when no client is currently attempting + ## to connect. + template doHandshake(): stmt = + when defined(ssl): + if server.isSSL: + client.setBlocking(false) + # We must wrap the client sock in a ssl context. + + if not client.isSSL or client.sslHandle == nil: + server.sslContext.wrapSocket(client) + let ret = SSLAccept(client.sslHandle) + while ret <= 0: + let err = SSLGetError(client.sslHandle, ret) + if err != SSL_ERROR_WANT_ACCEPT: + case err + of SSL_ERROR_ZERO_RETURN: + SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + of SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, + SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT: + client.sslNoHandshake = true + return AcceptNoHandshake + of SSL_ERROR_WANT_X509_LOOKUP: + SSLError("Function for x509 lookup has been called.") + of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: + SSLError() + else: + SSLError("Unknown error") + client.sslNoHandshake = false + + if client.isSSL and client.sslNoHandshake: + doHandshake() + return AcceptSuccess else: - var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK - if fcntl(s, F_SETFL, mode) == -1: - osError(osLastError()) + acceptAddrPlain(AcceptNoClient, AcceptSuccess): + doHandshake() + +proc accept*(server: PSocket, client: var PSocket) {.tags: [FReadIO].} = + ## Equivalent to ``acceptAddr`` but doesn't return the address, only the + ## socket. + ## + ## **Note**: ``client`` must be initialised (with ``new``), this function + ## makes no effort to initialise the ``client`` variable. + + var addrDummy = "" + acceptAddr(server, client, addrDummy) + +proc close*(socket: PSocket) = + ## Closes a socket. + socket.fd.close() + when defined(ssl): + if socket.isSSL: + let res = SSLShutdown(socket.sslHandle) + if res == 0: + if SSLShutdown(socket.sslHandle) != 1: + socketError(socket) + elif res != 1: + socketError(socket) + +proc toCInt(opt: TSOBool): cint = + case opt + of OptAcceptConn: SO_ACCEPTCONN + of OptBroadcast: SO_BROADCAST + of OptDebug: SO_DEBUG + of OptDontRoute: SO_DONTROUTE + of OptKeepAlive: SO_KEEPALIVE + of OptOOBInline: SO_OOBINLINE + of OptReuseAddr: SO_REUSEADDR + +proc getSockOpt*(socket: PSocket, opt: TSOBool, level = SOL_SOCKET): bool {. + tags: [FReadIO].} = + ## Retrieves option ``opt`` as a boolean value. + var res = getsockoptint(socket.fd, cint(level), toCInt(opt)) + result = res != 0 + +proc setSockOpt*(socket: PSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {. + tags: [FWriteIO].} = + ## Sets option ``opt`` to a boolean value specified by ``value``. + var valuei = cint(if value: 1 else: 0) + setsockoptint(socket.fd, cint(level), toCInt(opt), valuei) + +proc connect*(socket: PSocket, address: string, port = TPort(0), + af: TDomain = AF_INET) {.tags: [FReadIO].} = + ## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a + ## host name. If ``address`` is a host name, this function will try each IP + ## of that host name. ``htons`` is already performed on ``port`` so you must + ## not do it. + ## + ## If ``socket`` is an SSL socket a handshake will be automatically performed. + var aiList = getAddrInfo(address, port, af) + # try all possibilities: + var success = false + var lastError: TOSErrorCode + var it = aiList + while it != nil: + if connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) == 0'i32: + success = true + break + else: lastError = osLastError() + it = it.ai_next + + dealloc(aiList) + if not success: osError(lastError) + + when defined(ssl): + if socket.isSSL: + let ret = SSLConnect(socket.sslHandle) + socketError(socket, ret) + +when defined(ssl): + proc handshake*(socket: PSocket): bool {.tags: [FReadIO, FWriteIO].} = + ## This proc needs to be called on a socket after it connects. This is + ## only applicable when using ``connectAsync``. + ## This proc performs the SSL handshake. + ## + ## Returns ``False`` whenever the socket is not yet ready for a handshake, + ## ``True`` whenever handshake completed successfully. + ## + ## A ESSL error is raised on any other errors. + result = true + if socket.isSSL: + var ret = SSLConnect(socket.sslHandle) + if ret <= 0: + var errret = SSLGetError(socket.sslHandle, ret) + case errret + of SSL_ERROR_ZERO_RETURN: + SSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") + of SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT, + SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE: + return false + of SSL_ERROR_WANT_X509_LOOKUP: + SSLError("Function for x509 lookup has been called.") + of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: + SSLError() + else: + SSLError("Unknown Error") + socket.sslNoHandshake = false + else: + SSLError("Socket is not an SSL socket.") + + proc gotHandshake*(socket: PSocket): bool = + ## Determines whether a handshake has occurred between a client (``socket``) + ## and the server that ``socket`` is connected to. + ## + ## Throws ESSL if ``socket`` is not an SSL socket. + if socket.isSSL: + return not socket.sslNoHandshake + else: + SSLError("Socket is not an SSL socket.") + +proc hasDataBuffered*(s: PSocket): bool = + ## Determines whether a socket has data buffered. + result = false + if s.isBuffered: + result = s.bufLen > 0 and s.currPos != s.bufLen + + when defined(ssl): + if s.isSSL and not result: + result = s.sslHasPeekChar + +proc select(readfd: PSocket, timeout = 500): int = + ## Used for socket operation timeouts. + if readfd.hasDataBuffered: + return 1 + + var fds = @[readFd.fd] + result = select(fds, timeout) + +proc readIntoBuf(socket: PSocket, flags: int32): int = + result = 0 + when defined(ssl): + if socket.isSSL: + result = SSLRead(socket.sslHandle, addr(socket.buffer), int(socket.buffer.high)) + else: + result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags) + else: + result = recv(socket.fd, addr(socket.buffer), cint(socket.buffer.high), flags) + if result <= 0: + socket.bufLen = 0 + socket.currPos = 0 + return result + socket.bufLen = result + socket.currPos = 0 + +template retRead(flags, readBytes: int) {.dirty.} = + let res = socket.readIntoBuf(flags.int32) + if res <= 0: + if readBytes > 0: + return readBytes + else: + return res + +proc recv*(socket: PSocket, data: pointer, size: int): int {.tags: [FReadIO].} = + ## Receives data from a socket. + ## + ## **Note**: This is a low-level function, you may be interested in the higher + ## level versions of this function which are also named ``recv``. + if size == 0: return + if socket.isBuffered: + if socket.bufLen == 0: + retRead(0'i32, 0) + + var read = 0 + while read < size: + if socket.currPos >= socket.bufLen: + retRead(0'i32, read) + + let chunk = min(socket.bufLen-socket.currPos, size-read) + var d = cast[cstring](data) + copyMem(addr(d[read]), addr(socket.buffer[socket.currPos]), chunk) + read.inc(chunk) + socket.currPos.inc(chunk) + + result = read + else: + when defined(ssl): + if socket.isSSL: + if socket.sslHasPeekChar: + copyMem(data, addr(socket.sslPeekChar), 1) + socket.sslHasPeekChar = false + if size-1 > 0: + var d = cast[cstring](data) + result = SSLRead(socket.sslHandle, addr(d[1]), size-1) + 1 + else: + result = 1 + else: + result = SSLRead(socket.sslHandle, data, size) + else: + result = recv(socket.fd, data, size.cint, 0'i32) + else: + result = recv(socket.fd, data, size.cint, 0'i32) + +proc waitFor(socket: PSocket, waited: var float, timeout, size: int, + funcName: string): int {.tags: [FTime].} = + ## determines the amount of characters that can be read. Result will never + ## be larger than ``size``. For unbuffered sockets this will be ``1``. + ## For buffered sockets it can be as big as ``BufferSize``. + ## + ## If this function does not determine that there is data on the socket + ## within ``timeout`` ms, an ETimeout error will be raised. + result = 1 + if size <= 0: assert false + if timeout == -1: return size + if socket.isBuffered and socket.bufLen != 0 and socket.bufLen != socket.currPos: + result = socket.bufLen - socket.currPos + result = min(result, size) + else: + if timeout - int(waited * 1000.0) < 1: + raise newException(ETimeout, "Call to '" & funcName & "' timed out.") + + when defined(ssl): + if socket.isSSL: + if socket.hasDataBuffered: + # sslPeekChar is present. + return 1 + let sslPending = SSLPending(socket.sslHandle) + if sslPending != 0: + return sslPending + + var startTime = epochTime() + let selRet = select(socket, timeout - int(waited * 1000.0)) + if selRet < 0: osError(osLastError()) + if selRet != 1: + raise newException(ETimeout, "Call to '" & funcName & "' timed out.") + waited += (epochTime() - startTime) + +proc recv*(socket: PSocket, data: pointer, size: int, timeout: int): int {. + tags: [FReadIO, FTime].} = + ## overload with a ``timeout`` parameter in miliseconds. + var waited = 0.0 # number of seconds already waited + + var read = 0 + while read < size: + let avail = waitFor(socket, waited, timeout, size-read, "recv") + var d = cast[cstring](data) + result = recv(socket, addr(d[read]), avail) + if result == 0: break + if result < 0: + return result + inc(read, result) + + result = read + +proc recv*(socket: PSocket, data: var string, size: int, timeout = -1): int = + ## Higher-level version of ``recv``. + ## + ## When 0 is returned the socket's connection has been closed. + ## + ## This function will throw an EOS exception when an error occurs. A value + ## lower than 0 is never returned. + ## + ## A timeout may be specified in miliseconds, if enough data is not received + ## within the time specified an ETimeout exception will be raised. + ## + ## **Note**: ``data`` must be initialised. + data.setLen(size) + result = recv(socket, cstring(data), size, timeout) + if result < 0: + data.setLen(0) + socket.socketError(result) + data.setLen(result) + +proc peekChar(socket: PSocket, c: var char): int {.tags: [FReadIO].} = + if socket.isBuffered: + result = 1 + if socket.bufLen == 0 or socket.currPos > socket.bufLen-1: + var res = socket.readIntoBuf(0'i32) + if res <= 0: + result = res + + c = socket.buffer[socket.currPos] + else: + when defined(ssl): + if socket.isSSL: + if not socket.sslHasPeekChar: + result = SSLRead(socket.sslHandle, addr(socket.sslPeekChar), 1) + socket.sslHasPeekChar = true + + c = socket.sslPeekChar + return + result = recv(socket.fd, addr(c), 1, MSG_PEEK) + +proc readLine*(socket: PSocket, line: var TaintedString, timeout = -1) {. + tags: [FReadIO, FTime].} = + ## Reads a line of data from ``socket``. + ## + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` + ## will be set to it. + ## + ## If the socket is disconnected, ``line`` will be set to ``""``. + ## + ## An EOS exception will be raised in the case of a socket error. + ## + ## A timeout can be specified in miliseconds, if data is not received within + ## the specified time an ETimeout exception will be raised. + + template addNLIfEmpty(): stmt = + if line.len == 0: + line.add("\c\L") + + var waited = 0.0 + + setLen(line.string, 0) + while true: + var c: char + discard waitFor(socket, waited, timeout, 1, "readLine") + var n = recv(socket, addr(c), 1) + if n < 0: socket.socketError() + elif n == 0: return + if c == '\r': + discard waitFor(socket, waited, timeout, 1, "readLine") + n = peekChar(socket, c) + if n > 0 and c == '\L': + discard recv(socket, addr(c), 1) + elif n <= 0: socket.socketError() + addNLIfEmpty() + return + elif c == '\L': + addNLIfEmpty() + return + add(line.string, c) + +proc recvFrom*(socket: PSocket, data: var string, length: int, + address: var string, port: var TPort, flags = 0'i32): int {. + tags: [FReadIO].} = + ## Receives data from ``socket``. This function should normally be used with + ## connection-less sockets (UDP sockets). + ## + ## If an error occurs an EOS exception will be raised. Otherwise the return + ## value will be the length of data received. + ## + ## **Warning:** This function does not yet have a buffered implementation, + ## so when ``socket`` is buffered the non-buffered implementation will be + ## used. Therefore if ``socket`` contains something in its buffer this + ## function will make no effort to return it. + + # TODO: Buffered sockets + data.setLen(length) + var sockAddress: Tsockaddr_in + var addrLen = sizeof(sockAddress).TSocklen + result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint, + cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen)) + + if result != -1: + data.setLen(result) + address = $inet_ntoa(sockAddress.sin_addr) + port = ntohs(sockAddress.sin_port).TPort + else: + osError(osLastError()) + +proc skip*(socket: PSocket, size: int, timeout = -1) = + ## Skips ``size`` amount of bytes. + ## + ## An optional timeout can be specified in miliseconds, if skipping the + ## bytes takes longer than specified an ETimeout exception will be raised. + ## + ## Returns the number of skipped bytes. + var waited = 0.0 + var dummy = alloc(size) + var bytesSkipped = 0 + while bytesSkipped != size: + let avail = waitFor(socket, waited, timeout, size-bytesSkipped, "skip") + bytesSkipped += recv(socket, dummy, avail) + dealloc(dummy) + +proc send*(socket: PSocket, data: pointer, size: int): int {. + tags: [FWriteIO].} = + ## Sends data to a socket. + ## + ## **Note**: This is a low-level version of ``send``. You likely should use + ## the version below. + when defined(ssl): + if socket.isSSL: + return SSLWrite(socket.sslHandle, cast[cstring](data), size) + + when defined(windows) or defined(macosx): + result = send(socket.fd, data, size.cint, 0'i32) + else: + when defined(solaris): + const MSG_NOSIGNAL = 0 + result = send(socket.fd, data, size, int32(MSG_NOSIGNAL)) + +proc send*(socket: PSocket, data: string) {.tags: [FWriteIO].} = + ## sends data to a socket. + let sent = send(socket, cstring(data), data.len) + if sent < 0: + socketError(socket) + + if sent != data.len: + raise newException(EOS, "Could not send all data.") + +proc trySend*(socket: PSocket, data: string): bool {.tags: [FWriteIO].} = + ## Safe alternative to ``send``. Does not raise an EOS when an error occurs, + ## and instead returns ``false`` on failure. + result = send(socket, cstring(data), data.len) == data.len + +proc sendTo*(socket: PSocket, address: string, port: TPort, data: pointer, + size: int, af: TDomain = AF_INET, flags = 0'i32): int {. + tags: [FWriteIO].} = + ## This proc sends ``data`` to the specified ``address``, + ## which may be an IP address or a hostname, if a hostname is specified + ## this function will try each IP of that hostname. + ## + ## + ## **Note:** You may wish to use the high-level version of this function + ## which is defined below. + ## + ## **Note:** This proc is not available for SSL sockets. + var aiList = getAddrInfo(address, port, af) + + # try all possibilities: + var success = false + var it = aiList + while it != nil: + result = sendto(socket.fd, data, size.cint, flags.cint, it.ai_addr, + it.ai_addrlen.TSocklen) + if result != -1'i32: + success = true + break + it = it.ai_next + + dealloc(aiList) + +proc sendTo*(socket: PSocket, address: string, port: TPort, + data: string): int {.tags: [FWriteIO].} = + ## This proc sends ``data`` to the specified ``address``, + ## which may be an IP address or a hostname, if a hostname is specified + ## this function will try each IP of that hostname. + ## + ## This is the high-level version of the above ``sendTo`` function. + result = socket.sendTo(address, port, cstring(data), data.len) + +proc connectAsync(socket: PSocket, name: string, port = TPort(0), + af: TDomain = AF_INET) {.tags: [FReadIO].} = + ## A variant of ``connect`` for non-blocking sockets. + ## + ## This procedure will immediatelly return, it will not block until a connection + ## is made. It is up to the caller to make sure the connection has been established + ## by checking (using ``select``) whether the socket is writeable. + ## + ## **Note**: For SSL sockets, the ``handshake`` procedure must be called + ## whenever the socket successfully connects to a server. + var aiList = getAddrInfo(name, port, af) + # try all possibilities: + var success = false + var lastError: TOSErrorCode + var it = aiList + while it != nil: + var ret = connect(socket.fd, it.ai_addr, it.ai_addrlen.TSocklen) + if ret == 0'i32: + success = true + break + else: + lastError = osLastError() + when defined(windows): + # Windows EINTR doesn't behave same as POSIX. + if lastError.int32 == WSAEWOULDBLOCK: + success = true + break + else: + if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS: + success = true + break + + it = it.ai_next + + dealloc(aiList) + if not success: osError(lastError) + +proc connect*(socket: PSocket, address: string, port = TPort(0), timeout: int, + af: TDomain = AF_INET) {.tags: [FReadIO, FWriteIO].} = + ## Connects to server as specified by ``address`` on port specified by ``port``. + ## + ## The ``timeout`` paremeter specifies the time in miliseconds to allow for + ## the connection to the server to be made. + socket.fd.setBlocking(false) + + socket.connectAsync(address, port, af) + var s = @[socket.fd] + if selectWrite(s, timeout) != 1: + raise newException(ETimeout, "Call to 'connect' timed out.") + else: + when defined(ssl): + if socket.isSSL: + socket.fd.setBlocking(true) + doAssert socket.handshake() + socket.fd.setBlocking(true) + +proc isSSL*(socket: PSocket): bool = return socket.isSSL + ## Determines whether ``socket`` is a SSL socket. + +proc getFD*(socket: PSocket): TSocketHandle = return socket.fd + ## Returns the socket's file descriptor diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim index 290f414b4..975cc685a 100644 --- a/lib/pure/sockets2.nim +++ b/lib/pure/sockets2.nim @@ -10,6 +10,8 @@ ## This module implements a low-level cross-platform sockets interface. Look ## at the ``net`` module for the higher-level version. +# TODO: Clean up the exports a bit and everything else in general. + import unsigned, os when hostos == "solaris": @@ -17,16 +19,21 @@ when hostos == "solaris": when defined(Windows): import winlean + export WSAEWOULDBLOCK else: import posix - export fcntl, F_GETFL, O_NONBLOCK, F_SETFL + export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, - inet_ntoa, recv, `==`, connect, send, accept + inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto export SO_ERROR, - SOL_SOCKET + SOL_SOCKET, + SOMAXCONN, + SO_ACCEPTCONN, SO_BROADCAST, SO_DEBUG, SO_DONTROUTE, + SO_KEEPALIVE, SO_OOBINLINE, SO_REUSEADDR, + MSG_PEEK type @@ -138,8 +145,6 @@ else: proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP): TSocketHandle = ## Creates a new socket; returns `InvalidSocket` if an error occurs. - - # TODO: The function which will use this will raise EOS. socket(toInt(domain), toInt(typ), toInt(protocol)) proc close*(socket: TSocketHandle) = @@ -154,14 +159,14 @@ proc close*(socket: TSocketHandle) = proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint = result = bindSocket(socket, name, namelen) -proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.tags: [FReadIO].} = +proc listen*(socket: TSocketHandle, backlog = SOMAXCONN): cint {.tags: [FReadIO].} = ## Marks ``socket`` as accepting connections. ## ``Backlog`` specifies the maximum length of the ## queue of pending connections. when defined(windows): - if winlean.listen(socket, cint(backlog)) < 0'i32: osError(osLastError()) + result = winlean.listen(socket, cint(backlog)) else: - if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError()) + result = posix.listen(socket, cint(backlog)) proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM, prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo = @@ -212,6 +217,103 @@ proc htons*(x: int16): int16 = ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. result = sockets2.ntohs(x) +proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} = + ## Searches the database from the beginning and finds the first entry for + ## which the service name specified by ``name`` matches the s_name member + ## and the protocol name specified by ``proto`` matches the s_proto member. + ## + ## On posix this will search through the ``/etc/services`` file. + when defined(Windows): + var s = winlean.getservbyname(name, proto) + else: + var s = posix.getservbyname(name, proto) + if s == nil: raise newException(EOS, "Service not found.") + result.name = $s.s_name + result.aliases = cstringArrayToSeq(s.s_aliases) + result.port = TPort(s.s_port) + result.proto = $s.s_proto + +proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} = + ## Searches the database from the beginning and finds the first entry for + ## which the port specified by ``port`` matches the s_port member and the + ## protocol name specified by ``proto`` matches the s_proto member. + ## + ## On posix this will search through the ``/etc/services`` file. + when defined(Windows): + var s = winlean.getservbyport(ze(int16(port)).cint, proto) + else: + var s = posix.getservbyport(ze(int16(port)).cint, proto) + if s == nil: raise newException(EOS, "Service not found.") + result.name = $s.s_name + result.aliases = cstringArrayToSeq(s.s_aliases) + result.port = TPort(s.s_port) + result.proto = $s.s_proto + +proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} = + ## This function will lookup the hostname of an IP Address. + var myaddr: TInAddr + myaddr.s_addr = inet_addr(ip) + + when defined(windows): + var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, + cint(sockets2.AF_INET)) + if s == nil: osError(osLastError()) + else: + var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen, + cint(posix.AF_INET)) + if s == nil: + raise newException(EOS, $hstrerror(h_errno)) + + result.name = $s.h_name + result.aliases = cstringArrayToSeq(s.h_aliases) + when defined(windows): + result.addrtype = TDomain(s.h_addrtype) + else: + if s.h_addrtype == posix.AF_INET: + result.addrtype = AF_INET + elif s.h_addrtype == posix.AF_INET6: + result.addrtype = AF_INET6 + else: + raise newException(EOS, "unknown h_addrtype") + result.addrList = cstringArrayToSeq(s.h_addr_list) + result.length = int(s.h_length) + +proc getHostByName*(name: string): Thostent {.tags: [FReadIO].} = + ## This function will lookup the IP address of a hostname. + when defined(Windows): + var s = winlean.gethostbyname(name) + else: + var s = posix.gethostbyname(name) + if s == nil: osError(osLastError()) + result.name = $s.h_name + result.aliases = cstringArrayToSeq(s.h_aliases) + when defined(windows): + result.addrtype = TDomain(s.h_addrtype) + else: + if s.h_addrtype == posix.AF_INET: + result.addrtype = AF_INET + elif s.h_addrtype == posix.AF_INET6: + result.addrtype = AF_INET6 + else: + raise newException(EOS, "unknown h_addrtype") + result.addrList = cstringArrayToSeq(s.h_addr_list) + result.length = int(s.h_length) + +proc getSockName*(socket: TSocketHandle): TPort = + ## returns the socket's associated port number. + var name: Tsockaddr_in + when defined(Windows): + name.sin_family = int16(ord(AF_INET)) + else: + name.sin_family = posix.AF_INET + #name.sin_port = htons(cint16(port)) + #name.sin_addr.s_addr = htonl(INADDR_ANY) + var namelen = sizeof(name).TSocklen + if getsockname(socket, cast[ptr TSockAddr](addr(name)), + addr(namelen)) == -1'i32: + osError(osLastError()) + result = TPort(sockets2.ntohs(name.sin_port)) + proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {. tags: [FReadIO].} = ## getsockopt for integer options. @@ -230,6 +332,89 @@ proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {. sizeof(value).TSocklen) < 0'i32: osError(osLastError()) +proc setBlocking*(s: TSocketHandle, blocking: bool) = + ## Sets blocking mode on socket. + ## + ## Raises EOS on error. + when defined(Windows): + var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking + if ioctlsocket(s, FIONBIO, addr(mode)) == -1: + osError(osLastError()) + else: # BSD sockets + var x: int = fcntl(s, F_GETFL, 0) + if x == -1: + osError(osLastError()) + else: + var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK + if fcntl(s, F_SETFL, mode) == -1: + osError(osLastError()) + +proc timeValFromMilliseconds(timeout = 500): Ttimeval = + if timeout != -1: + var seconds = timeout div 1000 + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + +proc createFdSet(fd: var TFdSet, s: seq[TSocketHandle], m: var int) = + FD_ZERO(fd) + for i in items(s): + m = max(m, int(i)) + FD_SET(i, fd) + +proc pruneSocketSet(s: var seq[TSocketHandle], fd: var TFdSet) = + var i = 0 + var L = s.len + while i < L: + if FD_ISSET(s[i], fd) == 0'i32: + s[i] = s[L-1] + dec(L) + else: + inc(i) + setLen(s, L) + +proc select*(readfds: var seq[TSocketHandle], timeout = 500): int = + ## Traditional select function. This function will return the number of + ## sockets that are ready to be read from, written to, or which have errors. + ## If there are none; 0 is returned. + ## ``Timeout`` is in miliseconds and -1 can be specified for no timeout. + ## + ## A socket is removed from the specific ``seq`` when it has data waiting to + ## be read/written to or has errors (``exceptfds``). + var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) + + var rd: TFdSet + var m = 0 + createFdSet((rd), readfds, m) + + if timeout != -1: + result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv))) + else: + result = int(select(cint(m+1), addr(rd), nil, nil, nil)) + + pruneSocketSet(readfds, (rd)) + +proc selectWrite*(writefds: var seq[TSocketHandle], + timeout = 500): int {.tags: [FReadIO].} = + ## When a socket in ``writefds`` is ready to be written to then a non-zero + ## value will be returned specifying the count of the sockets which can be + ## written to. The sockets which can be written to will also be removed + ## from ``writefds``. + ## + ## ``timeout`` is specified in miliseconds and ``-1`` can be specified for + ## an unlimited time. + var tv {.noInit.}: Ttimeval = timeValFromMilliseconds(timeout) + + var wr: TFdSet + var m = 0 + createFdSet((wr), writefds, m) + + if timeout != -1: + result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv))) + else: + result = int(select(cint(m+1), nil, addr(wr), nil, nil)) + + pruneSocketSet(writefds, (wr)) + when defined(Windows): var wsa: TWSADATA if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index 91dfb7932..ae22d8d52 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -3,7 +3,7 @@ discard """ cmd: "nimrod cc --hints:on $# $#" output: "5000" """ -import asyncio2, sockets2, net, strutils +import asyncio2, sockets2, net, strutils, os var disp = newDispatcher() var msgCount = 0 @@ -50,8 +50,19 @@ proc readMessages(disp: PDispatcher, client: TSocketHandle) {.async.} = proc createServer(disp: PDispatcher, port: TPort) {.async.} = var server = disp.socket() #disp.register(server) - server.bindAddr(port) - server.listen() + block: + var name: TSockaddr_in + when defined(windows): + name.sin_family = toInt(AF_INET).int16 + else: + name.sin_family = toInt(AF_INET) + name.sin_port = htons(int16(port)) + name.sin_addr.s_addr = htonl(INADDR_ANY) + if bindAddr(server, cast[ptr TSockAddr](addr(name)), + sizeof(name).TSocklen) < 0'i32: + osError(osLastError()) + + discard server.listen() while true: var client = await disp.accept(server) readMessages(disp, client) From 192e11e7b72470e27bc6bccf1fedbfefc9c4ebd8 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 22 Mar 2014 22:33:02 +0000 Subject: [PATCH 214/249] Many renames. Created high level asyncnet module. --- lib/pure/{asyncio2.nim => asyncdispatch.nim} | 28 +++- lib/pure/asyncnet.nim | 147 +++++++++++++++++++ lib/pure/net.nim | 4 +- lib/pure/{sockets2.nim => rawsockets.nim} | 8 +- 4 files changed, 175 insertions(+), 12 deletions(-) rename lib/pure/{asyncio2.nim => asyncdispatch.nim} (98%) create mode 100644 lib/pure/asyncnet.nim rename lib/pure/{sockets2.nim => rawsockets.nim} (98%) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncdispatch.nim similarity index 98% rename from lib/pure/asyncio2.nim rename to lib/pure/asyncdispatch.nim index fbb02c37c..67361e46c 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncdispatch.nim @@ -9,14 +9,14 @@ import os, oids, tables, strutils, macros -import sockets2 +import rawsockets -## Asyncio2 +## AsyncDispatch ## -------- ## -## This module implements a brand new asyncio module based on Futures. -## IOCP is used under the hood on Windows and the selectors module is used for -## other operating systems. +## This module implements a brand new dispatcher based on Futures. +## On Windows IOCP is used and on other operating systems the selectors module +## is used instead. # -- Futures @@ -27,7 +27,7 @@ type PFuture*[T] = ref object of PFutureBase value: T - error: ref EBase + error*: ref EBase # TODO: This shouldn't be necessary, generics bug? proc newFuture*[T](): PFuture[T] = ## Creates a new future. @@ -90,6 +90,11 @@ proc read*[T](future: PFuture[T]): T = # TODO: Make a custom exception type for this? raise newException(EInvalidValue, "Future still in progress.") +proc readError*[T](future: PFuture[T]): ref EBase = + if future.error != nil: return future.error + else: + raise newException(EInvalidValue, "No error in future.") + proc finished*[T](future: PFuture[T]): bool = ## Determines whether ``future`` has completed. ## @@ -478,6 +483,7 @@ when defined(windows) or defined(nimdoc): protocol: TProtocol = IPPROTO_TCP): TSocketHandle = ## Creates a new socket and registers it with the dispatcher implicitly. result = socket(domain, typ, protocol) + result.setBlocking(false) disp.register(result) proc close*(disp: PDispatcher, socket: TSocketHandle) = @@ -516,6 +522,7 @@ else: typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP): TSocketHandle = result = socket(domain, typ, protocol) + result.setBlocking(false) disp.register(result) proc close*(disp: PDispatcher, sock: TSocketHandle) = @@ -919,6 +926,15 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} return add(result.string, c) +var gDisp*{.threadvar.}: PDispatcher ## Global dispatcher +gDisp = newDispatcher() + +proc runForever*() = + ## Begins a never ending global dispatcher poll loop. + while true: + gDisp.poll() + + when isMainModule: var p = newDispatcher() diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim new file mode 100644 index 000000000..ed8a2fb82 --- /dev/null +++ b/lib/pure/asyncnet.nim @@ -0,0 +1,147 @@ +import asyncdispatch +import rawsockets +import net + +when defined(ssl): + import openssl + +type + TAsyncSocket = object ## socket type + fd: TSocketHandle + case isBuffered: bool # determines whether this socket is buffered. + of true: + buffer: array[0..BufferSize, char] + currPos: int # current index in buffer + bufLen: int # current length of buffer + of false: nil + when defined(ssl): + case isSsl: bool + of true: + sslHandle: PSSL + sslContext: PSSLContext + sslNoHandshake: bool # True if needs handshake. + sslHasPeekChar: bool + sslPeekChar: char + of false: nil + + PAsyncSocket* = ref TAsyncSocket + +# TODO: Save AF, domain etc info and reuse it in procs which need it like connect. + +proc newSocket(fd: TSocketHandle, isBuff: bool): PAsyncSocket = + assert fd != osInvalidSocket + new(result) + result.fd = fd + result.isBuffered = isBuff + if isBuff: + result.currPos = 0 + +proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, + protocol: TProtocol = IPPROTO_TCP, buffered = true): PAsyncSocket = + ## Creates a new asynchronous socket. + result = newSocket(gDisp.socket(domain, typ, protocol), buffered) + +proc connect*(socket: PAsyncSocket, address: string, port: TPort, + af = AF_INET): PFuture[void] = + ## Connects ``socket`` to server at ``address:port``. + ## + ## Returns a ``PFuture`` which will complete when the connection succeeds + ## or an error occurs. + result = gDisp.connect(socket.fd, address, port, af) + +proc recv*(socket: PAsyncSocket, size: int, + flags: int = 0): PFuture[string] = + ## Reads ``size`` bytes from ``socket``. Returned future will complete once + ## all of the requested data is read. If socket is disconnected during the + ## recv operation then the future may complete with only a part of the + ## requested data read. If socket is disconnected and no data is available + ## to be read then the future will complete with a value of ``""``. + result = gDisp.recv(socket.fd, size, flags) + +proc send*(socket: PAsyncSocket, data: string): PFuture[void] = + ## Sends ``data`` to ``socket``. The returned future will complete once all + ## data has been sent. + result = gDisp.send(socket.fd, data) + +proc acceptAddr*(socket: PAsyncSocket): + PFuture[tuple[address: string, client: PAsyncSocket]] = + ## Accepts a new connection. Returns a future containing the client socket + ## corresponding to that connection and the remote address of the client. + ## The future will complete when the connection is successfully accepted. + var retFuture = newFuture[tuple[address: string, client: PAsyncSocket]]() + var fut = gDisp.acceptAddr(socket.fd) + fut.callback = + proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) = + assert future.finished + if future.failed: + retFuture.fail(future.readError) + else: + let resultTup = (future.read.address, + newSocket(future.read.client, socket.isBuffered)) + retFuture.complete(resultTup) + return retFuture + +proc accept*(socket: PAsyncSocket): PFuture[PAsyncSocket] = + ## Accepts a new connection. Returns a future containing the client socket + ## corresponding to that connection. + ## The future will complete when the connection is successfully accepted. + var retFut = newFuture[PAsyncSocket]() + var fut = acceptAddr(socket) + fut.callback = + proc (future: PFuture[tuple[address: string, client: PAsyncSocket]]) = + assert future.finished + if future.failed: + retFut.fail(future.readError) + else: + retFut.complete(future.read.client) + return retFut + +proc recvLine*(socket: PAsyncSocket): PFuture[string] {.async.} = + ## Reads a line of data from ``socket``. Returned future will complete once + ## a full line is read or an error occurs. + ## + ## If a full line is read ``\r\L`` is not + ## added to ``line``, however if solely ``\r\L`` is read then ``line`` + ## will be set to it. + ## + ## If the socket is disconnected, ``line`` will be set to ``""``. + ## + ## If the socket is disconnected in the middle of a line (before ``\r\L`` + ## is read) then line will be set to ``""``. + ## The partial line **will be lost**. + + template addNLIfEmpty(): stmt = + if result.len == 0: + result.add("\c\L") + + result = "" + var c = "" + while true: + c = await recv(socket, 1) + if c.len == 0: + return "" + if c == "\r": + c = await recv(socket, 1, MSG_PEEK) + if c.len > 0 and c == "\L": + discard await recv(socket, 1) + addNLIfEmpty() + return + elif c == "\L": + addNLIfEmpty() + return + add(result.string, c) + +when isMainModule: + proc main() {.async.} = + var sock = AsyncSocket() + await sock.connect("irc.freenode.net", TPort(6667)) + while true: + let line = await sock.recvLine() + if line == "": + echo("Disconnected") + break + else: + echo("Got line: ", line) + main() + runForever() + diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 45883166b..d40f0949b 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -10,7 +10,7 @@ ## This module implements a high-level cross-platform sockets interface. {.deadCodeElim: on.} -import sockets2, os, strutils, unsigned, parseutils, times +import rawsockets, os, strutils, unsigned, parseutils, times type IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address @@ -360,7 +360,7 @@ proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, ## Creates a new socket. ## ## If an error occurs EOS will be raised. - let fd = sockets2.socket(domain, typ, protocol) + let fd = rawsockets.socket(domain, typ, protocol) if fd == osInvalidSocket: osError(osLastError()) result = newSocket(fd, buffered) diff --git a/lib/pure/sockets2.nim b/lib/pure/rawsockets.nim similarity index 98% rename from lib/pure/sockets2.nim rename to lib/pure/rawsockets.nim index 975cc685a..db04f6097 100644 --- a/lib/pure/sockets2.nim +++ b/lib/pure/rawsockets.nim @@ -209,13 +209,13 @@ proc htonl*(x: int32): int32 = ## Converts 32-bit integers from host to network byte order. On machines ## where the host byte order is the same as network byte order, this is ## a no-op; otherwise, it performs a 4-byte swap operation. - result = sockets2.ntohl(x) + result = rawsockets.ntohl(x) proc htons*(x: int16): int16 = ## Converts 16-bit positive integers from host to network byte order. ## On machines where the host byte order is the same as network byte ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. - result = sockets2.ntohs(x) + result = rawsockets.ntohs(x) proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} = ## Searches the database from the beginning and finds the first entry for @@ -256,7 +256,7 @@ proc getHostByAddr*(ip: string): Thostent {.tags: [FReadIO].} = when defined(windows): var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, - cint(sockets2.AF_INET)) + cint(rawsockets.AF_INET)) if s == nil: osError(osLastError()) else: var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).TSocklen, @@ -312,7 +312,7 @@ proc getSockName*(socket: TSocketHandle): TPort = if getsockname(socket, cast[ptr TSockAddr](addr(name)), addr(namelen)) == -1'i32: osError(osLastError()) - result = TPort(sockets2.ntohs(name.sin_port)) + result = TPort(rawsockets.ntohs(name.sin_port)) proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {. tags: [FReadIO].} = From 7a311f93f7c08376416989e2c0a777db9e94dc7b Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 22 Mar 2014 23:48:33 +0100 Subject: [PATCH 215/249] fixes #994 --- compiler/semcall.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index b994a94d7..04d280ce2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -326,7 +326,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = # for borrowing the sym in the symbol table is returned, else nil. # New approach: generate fn(x, y, z) where x, y, z have the proper types # and use the overloading resolution mechanism: - var call = newNode(nkCall) + var call = newNodeI(nkCall, fn.info) var hasDistinct = false call.add(newIdentNode(fn.name, fn.info)) for i in 1.. Date: Sun, 23 Mar 2014 11:39:24 +0000 Subject: [PATCH 216/249] Fixes tasyncawait and the net module on Linux. --- lib/pure/rawsockets.nim | 3 ++- tests/async/tasyncawait.nim | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index db04f6097..de7437420 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -22,7 +22,8 @@ when defined(Windows): export WSAEWOULDBLOCK else: import posix - export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK + export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL, + EINTR, EINPROGRESS export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index ae22d8d52..7f2c1e6cc 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -3,7 +3,7 @@ discard """ cmd: "nimrod cc --hints:on $# $#" output: "5000" """ -import asyncio2, sockets2, net, strutils, os +import asyncdispatch, rawsockets, net, strutils, os var disp = newDispatcher() var msgCount = 0 From 5920edf6e4f44898210bb37a71324537dca9569a Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 23 Mar 2014 17:48:10 +0100 Subject: [PATCH 217/249] fixes #404 --- compiler/lambdalifting.nim | 9 +----- compiler/lowerings.nim | 52 ++++++++++++++++++++++++++++++++++ compiler/transf.nim | 12 +------- compiler/vm.nim | 2 ++ compiler/vmgen.nim | 31 +++++++++++--------- tests/vm/tcompiletimetable.nim | 29 +++++++++++++++++++ 6 files changed, 102 insertions(+), 33 deletions(-) create mode 100644 compiler/lowerings.nim create mode 100644 tests/vm/tcompiletimetable.nim diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 4bc8eff86..6f6942096 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -11,7 +11,7 @@ import intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, - idents, renderer, types, magicsys, rodread + idents, renderer, types, magicsys, rodread, lowerings discard """ The basic approach is that captured vars need to be put on the heap and @@ -536,13 +536,6 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = result.sons[0] = le result.sons[1] = ri -proc addVar*(father, v: PNode) = - var vpart = newNodeI(nkIdentDefs, v.info) - addSon(vpart, v) - addSon(vpart, ast.emptyNode) - addSon(vpart, ast.emptyNode) - addSon(father, vpart) - proc newClosureCreationVar(o: POuterContext; e: PEnv): PSym = result = newSym(skVar, getIdent(envName), o.fn, e.attachedNode.info) incl(result.flags, sfShadowed) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim new file mode 100644 index 000000000..2cf641d93 --- /dev/null +++ b/compiler/lowerings.nim @@ -0,0 +1,52 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2014 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements common simple lowerings. + +const + genPrefix* = ":tmp" # prefix for generated names + +import ast, types, idents, magicsys + +proc newTupleAccess*(tup: PNode, i: int): PNode = + result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes( + abstractInst).sons[i]) + addSon(result, copyTree(tup)) + var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt)) + lit.intVal = i + addSon(result, lit) + +proc addVar*(father, v: PNode) = + var vpart = newNodeI(nkIdentDefs, v.info, 3) + vpart.sons[0] = v + vpart.sons[1] = ast.emptyNode + vpart.sons[2] = ast.emptyNode + addSon(father, vpart) + +proc newAsgnStmt(le, ri: PNode): PNode = + result = newNodeI(nkAsgn, le.info, 2) + result.sons[0] = le + result.sons[1] = ri + +proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode = + assert n.kind == nkVarTuple + let value = n.lastSon + result = newNodeI(nkStmtList, n.info) + + var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info) + temp.typ = skipTypes(value.typ, abstractInst) + incl(temp.flags, sfFromGeneric) + + var v = newNodeI(nkVarSection, value.info) + v.addVar(newSymNode(temp)) + result.add(v) + + result.add newAsgnStmt(newSymNode(temp), value) + for i in 0 .. n.len-3: + result.add newAsgnStmt(n.sons[i], newTupleAccess(value, i)) diff --git a/compiler/transf.nim b/compiler/transf.nim index 9586398c9..7922acbe9 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -20,10 +20,7 @@ import intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread, - lambdalifting, sempass2 - -const - genPrefix* = ":tmp" # prefix for generated names + lambdalifting, sempass2, lowerings # implementation @@ -240,13 +237,6 @@ proc transformLoopBody(c: PTransf, n: PNode): PTransNode = discard c.contSyms.pop() else: result = transform(c, n) - -proc newTupleAccess(tup: PNode, i: int): PNode = - result = newNodeIT(nkBracketExpr, tup.info, tup.typ.sons[i]) - addSon(result, copyTree(tup)) - var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt)) - lit.intVal = i - addSon(result, lit) proc unpackTuple(c: PTransf, n: PNode, father: PTransNode) = # XXX: BUG: what if `n` is an expression with side-effects? diff --git a/compiler/vm.nim b/compiler/vm.nim index f093527f8..70eb6f828 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -486,6 +486,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = regs[rb].intVal + regs[rc].intVal of opcAddImmInt: decodeBImm(rkInt) + #message(c.debug[pc], warnUser, "came here") + #debug regs[rb].node regs[ra].intVal = regs[rb].intVal + imm of opcSubInt: decodeBC(rkInt) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 80c07f39e..82d1e9ab8 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -11,7 +11,7 @@ import unsigned, strutils, ast, astalgo, types, msgs, renderer, vmdef, - trees, intsets, rodread, magicsys, options + trees, intsets, rodread, magicsys, options, lowerings from os import splitFile @@ -636,11 +636,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.genAddSubInt(n, dest, opcAddInt) of mInc, mDec: unused(n, dest) - # XXX generates inefficient code for globals - var d = c.genx(n.sons[1]).TDest - c.genAddSubInt(n, d, if m == mInc: opcAddInt else: opcSubInt) + let opc = if m == mInc: opcAddInt else: opcSubInt + let d = c.genx(n.sons[1]) + if n.sons[2].isInt8Lit: + c.gABI(n, succ(opc), d, d, n.sons[2].intVal) + else: + let tmp = c.genx(n.sons[2]) + c.gABC(n, opc, d, d, tmp) + c.freeTemp(tmp) c.genAsgnPatch(n.sons[1], d) - c.freeTemp(d.TRegister) + c.freeTemp(d) of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest) of mNew, mNewFinalize: unused(n, dest) @@ -1006,6 +1011,10 @@ proc isOwnedBy(a, b: PSym): bool = if a == b: return true a = a.owner +proc getOwner(c: PCtx): PSym = + result = c.prc.sym + if result.isNil: result = c.module + proc checkCanEval(c: PCtx; n: PNode) = # we need to ensure that we don't evaluate 'x' here: # proc foo() = var x ... @@ -1148,7 +1157,7 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let a = c.genx(n.sons[0], flags) let b = genField(n.sons[1]) if dest < 0: dest = c.getTemp(n.typ) - if gfAddrOf notin flags and fitsRegister(n.typ): + if gfAddrOf notin flags and fitsRegister(n.typ.skipTypes({tyVar})): var cc = c.getTemp(n.typ) c.gABC(n, opcLdObj, cc, a, b) c.gABC(n, opcNodeToReg, dest, cc) @@ -1228,16 +1237,10 @@ proc genVarSection(c: PCtx; n: PNode) = if a.kind == nkCommentStmt: continue #assert(a.sons[0].kind == nkSym) can happen for transformed vars if a.kind == nkVarTuple: - let tmp = c.genx(a.lastSon) for i in 0 .. a.len-3: setSlot(c, a[i].sym) - # v = t[i] - var v: TDest = -1 checkCanEval(c, a[i]) - genRdVar(c, a[i], v, {gfAddrOf}) - c.gABC(n, opcWrObj, v, tmp, i) - # XXX globals? - c.freeTemp(tmp) + c.gen(lowerTupleUnpacking(a, c.getOwner)) elif a.sons[0].kind == nkSym: let s = a.sons[0].sym checkCanEval(c, a.sons[0]) @@ -1581,7 +1584,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "foo": + #if s.name.s == "tupleUnpack": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc diff --git a/tests/vm/tcompiletimetable.nim b/tests/vm/tcompiletimetable.nim new file mode 100644 index 000000000..3b40add07 --- /dev/null +++ b/tests/vm/tcompiletimetable.nim @@ -0,0 +1,29 @@ +discard """ + msg: '''2 +3 +4:2 + ''' +""" + +# bug #404 + +import macros, tables + +var ZOOT{.compileTime.} = initTable[int, int](2) +var iii {.compiletime.} = 1 + +macro x:stmt= + zoot[iii] = iii*2 + inc iii + echo iii + +x +x + + +macro tupleUnpack: stmt = + var (y,z) = (4, 2) + echo y, ":", z + +tupleUnpack + From d310b01db1c6f2c8e63a561c40352b17978e1bdb Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 23 Mar 2014 18:24:11 +0000 Subject: [PATCH 218/249] Moved the global dispatcher to asyncdispatch. --- lib/pure/asyncdispatch.nim | 233 ++++++++++++++++++++---------------- lib/pure/asyncnet.nim | 30 +++-- lib/pure/net.nim | 8 +- lib/pure/rawsockets.nim | 2 +- tests/async/tasyncawait.nim | 45 ++++--- 5 files changed, 174 insertions(+), 144 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 67361e46c..4a4ef8bdd 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -111,13 +111,13 @@ when defined(windows) or defined(nimdoc): TCompletionKey = dword TCompletionData* = object - sock: TSocketHandle - cb: proc (sock: TSocketHandle, bytesTransferred: DWORD, + sock: TAsyncFD + cb: proc (sock: TAsyncFD, bytesTransferred: DWORD, errcode: TOSErrorCode) {.closure.} PDispatcher* = ref object ioPort: THandle - handles: TSet[TSocketHandle] + handles: TSet[TAsyncFD] TCustomOverlapped = object Internal*: DWORD @@ -129,30 +129,42 @@ when defined(windows) or defined(nimdoc): PCustomOverlapped = ptr TCustomOverlapped - proc hash(x: TSocketHandle): THash {.borrow.} + TAsyncFD* = distinct int + + proc hash(x: TAsyncFD): THash {.borrow.} + proc `==`*(x: TAsyncFD, y: TAsyncFD): bool {.borrow.} proc newDispatcher*(): PDispatcher = ## Creates a new Dispatcher instance. new result result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) - result.handles = initSet[TSocketHandle]() + result.handles = initSet[TAsyncFD]() - proc register*(p: PDispatcher, sock: TSocketHandle) = - ## Registers ``sock`` with the dispatcher ``p``. + var gDisp{.threadvar.}: PDispatcher ## Global dispatcher + proc getGlobalDispatcher*(): PDispatcher = + ## Retrieves the global thread-local dispatcher. + if gDisp.isNil: gDisp = newDispatcher() + result = gDisp + + proc register*(sock: TAsyncFD) = + ## Registers ``sock`` with the dispatcher. + let p = getGlobalDispatcher() if CreateIOCompletionPort(sock.THandle, p.ioPort, cast[TCompletionKey](sock), 1) == 0: OSError(OSLastError()) p.handles.incl(sock) - proc verifyPresence(p: PDispatcher, sock: TSocketHandle) = + proc verifyPresence(sock: TAsyncFD) = ## Ensures that socket has been registered with the dispatcher. + let p = getGlobalDispatcher() if sock notin p.handles: raise newException(EInvalidValue, "Operation performed on a socket which has not been registered with" & " the dispatcher yet.") - proc poll*(p: PDispatcher, timeout = 500) = + proc poll*(timeout = 500) = ## Waits for completion events and processes them. + let p = getGlobalDispatcher() if p.handles.len == 0: raise newException(EInvalidValue, "No handles registered in dispatcher.") @@ -170,7 +182,7 @@ when defined(windows) or defined(nimdoc): var customOverlapped = cast[PCustomOverlapped](lpOverlapped) if res: # This is useful for ensuring the reliability of the overlapped struct. - assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle + assert customOverlapped.data.sock == lpCompletionKey.TAsyncFD customOverlapped.data.cb(customOverlapped.data.sock, lpNumberOfBytesTransferred, TOSErrorCode(-1)) @@ -178,7 +190,7 @@ when defined(windows) or defined(nimdoc): else: let errCode = OSLastError() if lpOverlapped != nil: - assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle + assert customOverlapped.data.sock == lpCompletionKey.TAsyncFD customOverlapped.data.cb(customOverlapped.data.sock, lpNumberOfBytesTransferred, errCode) dealloc(customOverlapped) @@ -201,7 +213,7 @@ when defined(windows) or defined(nimdoc): addr bytesRet, nil, nil) == 0 proc initAll() = - let dummySock = socket() + let dummySock = newRawSocket() if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX): OSError(OSLastError()) if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX): @@ -253,20 +265,20 @@ when defined(windows) or defined(nimdoc): dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength, RemoteSockaddr, RemoteSockaddrLength) - proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, + proc connect*(socket: TAsyncFD, address: string, port: TPort, af = AF_INET): PFuture[void] = ## Connects ``socket`` to server at ``address:port``. ## ## Returns a ``PFuture`` which will complete when the connection succeeds ## or an error occurs. - verifyPresence(p, socket) + verifyPresence(socket) var retFuture = newFuture[void]() # Apparently ``ConnectEx`` expects the socket to be initially bound: var saddr: Tsockaddr_in saddr.sin_family = int16(toInt(af)) saddr.sin_port = 0 saddr.sin_addr.s_addr = INADDR_ANY - if bindAddr(socket, cast[ptr TSockAddr](addr(saddr)), + if bindAddr(socket.TSocketHandle, cast[ptr TSockAddr](addr(saddr)), sizeof(saddr).TSockLen) < 0'i32: OSError(OSLastError()) @@ -279,7 +291,7 @@ when defined(windows) or defined(nimdoc): # http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): retFuture.complete() @@ -287,8 +299,9 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) - var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint, - nil, 0, nil, cast[POverlapped](ol)) + var ret = connectEx(socket.TSocketHandle, it.ai_addr, + sizeof(TSockAddrIn).cint, nil, 0, nil, + cast[POverlapped](ol)) if ret: # Request to connect completed immediately. success = true @@ -313,14 +326,14 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(EOS, osErrorMsg(lastError))) return retFuture - proc recv*(p: PDispatcher, socket: TSocketHandle, size: int, + proc recv*(socket: TAsyncFD, size: int, flags: int = 0): PFuture[string] = ## Reads ``size`` bytes from ``socket``. Returned future will complete once ## all of the requested data is read. If socket is disconnected during the ## recv operation then the future may complete with only a part of the ## requested data read. If socket is disconnected and no data is available ## to be read then the future will complete with a value of ``""``. - verifyPresence(p, socket) + verifyPresence(socket) var retFuture = newFuture[string]() var dataBuf: TWSABuf @@ -331,7 +344,7 @@ when defined(windows) or defined(nimdoc): var flagsio = flags.dword var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): if bytesCount == 0 and dataBuf.buf[0] == '\0': @@ -344,7 +357,7 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) - let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived, + let ret = WSARecv(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived, addr flagsio, cast[POverlapped](ol), nil) if ret == -1: let err = OSLastError() @@ -373,10 +386,10 @@ when defined(windows) or defined(nimdoc): # free ``ol``. return retFuture - proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[void] = + proc send*(socket: TAsyncFD, data: string): PFuture[void] = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. - verifyPresence(p, socket) + verifyPresence(socket) var retFuture = newFuture[void]() var dataBuf: TWSABuf @@ -386,7 +399,7 @@ when defined(windows) or defined(nimdoc): var bytesReceived, flags: DWord var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): retFuture.complete() @@ -394,7 +407,7 @@ when defined(windows) or defined(nimdoc): retFuture.fail(newException(EOS, osErrorMsg(errcode))) ) - let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived, + let ret = WSASend(socket.TSocketHandle, addr dataBuf, 1, addr bytesReceived, flags, cast[POverlapped](ol), nil) if ret == -1: let err = osLastError() @@ -408,17 +421,17 @@ when defined(windows) or defined(nimdoc): # free ``ol``. return retFuture - proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): - PFuture[tuple[address: string, client: TSocketHandle]] = + proc acceptAddr*(socket: TAsyncFD): + PFuture[tuple[address: string, client: TAsyncFD]] = ## Accepts a new connection. Returns a future containing the client socket ## corresponding to that connection and the remote address of the client. ## The future will complete when the connection is successfully accepted. ## ## The resulting client socket is automatically registered to dispatcher. - verifyPresence(p, socket) - var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]() + verifyPresence(socket) + var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]() - var clientSock = socket() + var clientSock = newRawSocket() if clientSock == OSInvalidSocket: osError(osLastError()) const lpOutputLen = 1024 @@ -441,16 +454,16 @@ when defined(windows) or defined(nimdoc): dwLocalAddressLength, dwRemoteAddressLength, addr LocalSockaddr, addr localLen, addr RemoteSockaddr, addr remoteLen) - p.register(clientSock) + register(clientSock.TAsyncFD) # TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186 retFuture.complete( (address: $inet_ntoa(cast[ptr Tsockaddr_in](remoteSockAddr).sin_addr), - client: clientSock) + client: clientSock.TAsyncFD) ) var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped))) ol.data = TCompletionData(sock: socket, cb: - proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) = + proc (sock: TAsyncFD, bytesCount: DWord, errcode: TOSErrorCode) = if not retFuture.finished: if errcode == TOSErrorCode(-1): completeAccept() @@ -459,7 +472,7 @@ when defined(windows) or defined(nimdoc): ) # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx - let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0], + let ret = acceptEx(socket.TSocketHandle, clientSock, addr lpOutputBuf[0], dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength, @@ -478,73 +491,87 @@ when defined(windows) or defined(nimdoc): return retFuture - proc socket*(disp: PDispatcher, domain: TDomain = AF_INET, + proc newAsyncRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, - protocol: TProtocol = IPPROTO_TCP): TSocketHandle = + protocol: TProtocol = IPPROTO_TCP): TAsyncFD = ## Creates a new socket and registers it with the dispatcher implicitly. - result = socket(domain, typ, protocol) - result.setBlocking(false) - disp.register(result) + result = newRawSocket(domain, typ, protocol).TAsyncFD + result.TSocketHandle.setBlocking(false) + register(result) - proc close*(disp: PDispatcher, socket: TSocketHandle) = + proc close*(socket: TAsyncFD) = ## Closes a socket and ensures that it is unregistered. - socket.close() - disp.handles.excl(socket) + socket.TSocketHandle.close() + getGlobalDispatcher().handles.excl(socket) initAll() else: import selectors from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK type - TCallback = proc (sock: TSocketHandle): bool {.closure.} + TAsyncFD* = distinct cint + TCallback = proc (sock: TAsyncFD): bool {.closure.} PData* = ref object of PObject - sock: TSocketHandle + sock: TAsyncFD readCBs: seq[TCallback] writeCBs: seq[TCallback] PDispatcher* = ref object selector: PSelector + proc `==`*(x, y: TAsyncFD): bool {.borrow.} + proc newDispatcher*(): PDispatcher = new result result.selector = newSelector() - proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) = - assert sock in p.selector - discard p.selector.update(sock, events) + var gDisp{.threadvar.}: PDispatcher ## Global dispatcher + proc getGlobalDispatcher*(): PDispatcher = + if gDisp.isNil: gDisp = newDispatcher() + result = gDisp - proc register(p: PDispatcher, sock: TSocketHandle) = + proc update(sock: TAsyncFD, events: set[TEvent]) = + let p = getGlobalDispatcher() + assert sock.TSocketHandle in p.selector + discard p.selector.update(sock.TSocketHandle, events) + + proc register(sock: TAsyncFD) = + let p = getGlobalDispatcher() var data = PData(sock: sock, readCBs: @[], writeCBs: @[]) - p.selector.register(sock, {}, data.PObject) + p.selector.register(sock.TSocketHandle, {}, data.PObject) - proc socket*(disp: PDispatcher, domain: TDomain = AF_INET, + proc newAsyncRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, - protocol: TProtocol = IPPROTO_TCP): TSocketHandle = - result = socket(domain, typ, protocol) - result.setBlocking(false) - disp.register(result) + protocol: TProtocol = IPPROTO_TCP): TAsyncFD = + result = newRawSocket(domain, typ, protocol).TAsyncFD + result.TSocketHandle.setBlocking(false) + register(result) - proc close*(disp: PDispatcher, sock: TSocketHandle) = - sock.close() - disp.selector.unregister(sock) + proc close*(sock: TAsyncFD) = + let disp = getGlobalDispatcher() + sock.TSocketHandle.close() + disp.selector.unregister(sock.TSocketHandle) - proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = - if sock notin p.selector: + proc addRead(sock: TAsyncFD, cb: TCallback) = + let p = getGlobalDispatcher() + if sock.TSocketHandle notin p.selector: raise newException(EInvalidValue, "File descriptor not registered.") - p.selector[sock].data.PData.readCBs.add(cb) - p.update(sock, p.selector[sock].events + {EvRead}) + p.selector[sock.TSocketHandle].data.PData.readCBs.add(cb) + update(sock, p.selector[sock.TSocketHandle].events + {EvRead}) - proc addWrite(p: PDispatcher, sock: TSocketHandle, cb: TCallback) = - if sock notin p.selector: + proc addWrite(sock: TAsyncFD, cb: TCallback) = + let p = getGlobalDispatcher() + if sock.TSocketHandle notin p.selector: raise newException(EInvalidValue, "File descriptor not registered.") - p.selector[sock].data.PData.writeCBs.add(cb) - p.update(sock, p.selector[sock].events + {EvWrite}) + p.selector[sock.TSocketHandle].data.PData.writeCBs.add(cb) + update(sock, p.selector[sock.TSocketHandle].events + {EvWrite}) - proc poll*(p: PDispatcher, timeout = 500) = + proc poll*(timeout = 500) = + let p = getGlobalDispatcher() for info in p.selector.select(timeout): let data = PData(info.key.data) - assert data.sock == info.key.fd + assert data.sock == info.key.fd.TAsyncFD #echo("In poll ", data.sock.cint) if EvRead in info.events: # Callback may add items to ``data.readCBs`` which causes issues if @@ -570,17 +597,16 @@ else: if data.readCBs.len != 0: newEvents = {EvRead} if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite} if newEvents != info.key.events: - echo(info.key.events, " -> ", newEvents) - p.update(data.sock, newEvents) + update(data.sock, newEvents) else: # FD no longer a part of the selector. Likely been closed # (e.g. socket disconnected). - proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, + proc connect*(socket: TAsyncFD, address: string, port: TPort, af = AF_INET): PFuture[void] = var retFuture = newFuture[void]() - proc cb(sock: TSocketHandle): bool = + proc cb(sock: TAsyncFD): bool = # We have connected. retFuture.complete() return true @@ -590,7 +616,7 @@ else: var lastError: TOSErrorCode var it = aiList while it != nil: - var ret = connect(socket, it.ai_addr, it.ai_addrlen.TSocklen) + var ret = connect(socket.TSocketHandle, it.ai_addr, it.ai_addrlen.TSocklen) if ret == 0: # Request to connect completed immediately. success = true @@ -600,7 +626,7 @@ else: lastError = osLastError() if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS: success = true - addWrite(p, socket, cb) + addWrite(socket, cb) break else: success = false @@ -611,17 +637,18 @@ else: retFuture.fail(newException(EOS, osErrorMsg(lastError))) return retFuture - proc recv*(p: PDispatcher, socket: TSocketHandle, size: int, + proc recv*(socket: TAsyncFD, size: int, flags: int = 0): PFuture[string] = var retFuture = newFuture[string]() var readBuffer = newString(size) var sizeRead = 0 - proc cb(sock: TSocketHandle): bool = + proc cb(sock: TAsyncFD): bool = result = true let netSize = size - sizeRead - let res = recv(sock, addr readBuffer[sizeRead], netSize, flags.cint) + let res = recv(sock.TSocketHandle, addr readBuffer[sizeRead], netSize, + flags.cint) #echo("recv cb res: ", res) if res < 0: let lastError = osLastError() @@ -645,19 +672,19 @@ else: retFuture.complete(readBuffer) #echo("Recv cb result: ", result) - addRead(p, socket, cb) + addRead(socket, cb) return retFuture - proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[void] = + proc send*(socket: TAsyncFD, data: string): PFuture[void] = var retFuture = newFuture[void]() var written = 0 - proc cb(sock: TSocketHandle): bool = + proc cb(sock: TAsyncFD): bool = result = true let netSize = data.len-written var d = data.cstring - let res = send(sock, addr d[written], netSize, 0.cint) + let res = send(sock.TSocketHandle, addr d[written], netSize, 0.cint) if res < 0: let lastError = osLastError() if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}: @@ -670,18 +697,18 @@ else: result = false # We still have data to send. else: retFuture.complete() - addWrite(p, socket, cb) + addWrite(socket, cb) return retFuture - proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): - PFuture[tuple[address: string, client: TSocketHandle]] = - var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]() - proc cb(sock: TSocketHandle): bool = + proc acceptAddr*(socket: TAsyncFD): + PFuture[tuple[address: string, client: TAsyncFD]] = + var retFuture = newFuture[tuple[address: string, client: TAsyncFD]]() + proc cb(sock: TAsyncFD): bool = result = true var sockAddress: Tsockaddr_in var addrLen = sizeof(sockAddress).TSocklen - var client = accept(sock, cast[ptr TSockAddr](addr(sockAddress)), - addr(addrLen)) + var client = accept(sock.TSocketHandle, + cast[ptr TSockAddr](addr(sockAddress)), addr(addrLen)) if client == osInvalidSocket: let lastError = osLastError() assert lastError.int32 notin {EWOULDBLOCK, EAGAIN} @@ -690,19 +717,19 @@ else: else: retFuture.fail(newException(EOS, osErrorMsg(lastError))) else: - p.register(client) - retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client)) - addRead(p, socket, cb) + register(client.TAsyncFD) + retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client.TAsyncFD)) + addRead(socket, cb) return retFuture -proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] = +proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] = ## Accepts a new connection. Returns a future containing the client socket ## corresponding to that connection. ## The future will complete when the connection is successfully accepted. - var retFut = newFuture[TSocketHandle]() - var fut = p.acceptAddr(socket) + var retFut = newFuture[TAsyncFD]() + var fut = acceptAddr(socket) fut.callback = - proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) = + proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) = assert future.finished if future.failed: retFut.fail(future.error) @@ -891,7 +918,7 @@ macro async*(prc: stmt): stmt {.immediate.} = echo(toStrLit(result)) -proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} = +proc recvLine*(socket: TAsyncFD): PFuture[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## @@ -912,28 +939,24 @@ proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} result = "" var c = "" while true: - c = await p.recv(socket, 1) + c = await recv(socket, 1) if c.len == 0: return "" if c == "\r": - c = await p.recv(socket, 1, MSG_PEEK) + c = await recv(socket, 1, MSG_PEEK) if c.len > 0 and c == "\L": - discard await p.recv(socket, 1) + discard await recv(socket, 1) addNLIfEmpty() return elif c == "\L": addNLIfEmpty() return - add(result.string, c) - -var gDisp*{.threadvar.}: PDispatcher ## Global dispatcher -gDisp = newDispatcher() + add(result, c) proc runForever*() = ## Begins a never ending global dispatcher poll loop. while true: - gDisp.poll() - + poll() when isMainModule: diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index ed8a2fb82..451ef25ac 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -1,3 +1,11 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# import asyncdispatch import rawsockets import net @@ -7,7 +15,7 @@ when defined(ssl): type TAsyncSocket = object ## socket type - fd: TSocketHandle + fd: TAsyncFD case isBuffered: bool # determines whether this socket is buffered. of true: buffer: array[0..BufferSize, char] @@ -28,18 +36,18 @@ type # TODO: Save AF, domain etc info and reuse it in procs which need it like connect. -proc newSocket(fd: TSocketHandle, isBuff: bool): PAsyncSocket = - assert fd != osInvalidSocket +proc newSocket(fd: TAsyncFD, isBuff: bool): PAsyncSocket = + assert fd != osInvalidSocket.TAsyncFD new(result) result.fd = fd result.isBuffered = isBuff if isBuff: result.currPos = 0 -proc AsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, +proc newAsyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP, buffered = true): PAsyncSocket = ## Creates a new asynchronous socket. - result = newSocket(gDisp.socket(domain, typ, protocol), buffered) + result = newSocket(newAsyncRawSocket(domain, typ, protocol), buffered) proc connect*(socket: PAsyncSocket, address: string, port: TPort, af = AF_INET): PFuture[void] = @@ -47,7 +55,7 @@ proc connect*(socket: PAsyncSocket, address: string, port: TPort, ## ## Returns a ``PFuture`` which will complete when the connection succeeds ## or an error occurs. - result = gDisp.connect(socket.fd, address, port, af) + result = connect(socket.fd, address, port, af) proc recv*(socket: PAsyncSocket, size: int, flags: int = 0): PFuture[string] = @@ -56,12 +64,12 @@ proc recv*(socket: PAsyncSocket, size: int, ## recv operation then the future may complete with only a part of the ## requested data read. If socket is disconnected and no data is available ## to be read then the future will complete with a value of ``""``. - result = gDisp.recv(socket.fd, size, flags) + result = recv(socket.fd, size, flags) proc send*(socket: PAsyncSocket, data: string): PFuture[void] = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. - result = gDisp.send(socket.fd, data) + result = send(socket.fd, data) proc acceptAddr*(socket: PAsyncSocket): PFuture[tuple[address: string, client: PAsyncSocket]] = @@ -69,9 +77,9 @@ proc acceptAddr*(socket: PAsyncSocket): ## corresponding to that connection and the remote address of the client. ## The future will complete when the connection is successfully accepted. var retFuture = newFuture[tuple[address: string, client: PAsyncSocket]]() - var fut = gDisp.acceptAddr(socket.fd) + var fut = acceptAddr(socket.fd) fut.callback = - proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) = + proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) = assert future.finished if future.failed: retFuture.fail(future.readError) @@ -133,7 +141,7 @@ proc recvLine*(socket: PAsyncSocket): PFuture[string] {.async.} = when isMainModule: proc main() {.async.} = - var sock = AsyncSocket() + var sock = newAsyncSocket() await sock.connect("irc.freenode.net", TPort(6667)) while true: let line = await sock.recvLine() diff --git a/lib/pure/net.nim b/lib/pure/net.nim index d40f0949b..2461ece1b 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -347,7 +347,7 @@ type ETimeout* = object of ESynch -proc newSocket(fd: TSocketHandle, isBuff: bool): PSocket = +proc createSocket(fd: TSocketHandle, isBuff: bool): PSocket = assert fd != osInvalidSocket new(result) result.fd = fd @@ -355,15 +355,15 @@ proc newSocket(fd: TSocketHandle, isBuff: bool): PSocket = if isBuff: result.currPos = 0 -proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, +proc newSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP, buffered = true): PSocket = ## Creates a new socket. ## ## If an error occurs EOS will be raised. - let fd = rawsockets.socket(domain, typ, protocol) + let fd = newRawSocket(domain, typ, protocol) if fd == osInvalidSocket: osError(osLastError()) - result = newSocket(fd, buffered) + result = createSocket(fd, buffered) when defined(ssl): CRYPTO_malloc_init() diff --git a/lib/pure/rawsockets.nim b/lib/pure/rawsockets.nim index de7437420..aeaa7f3b5 100644 --- a/lib/pure/rawsockets.nim +++ b/lib/pure/rawsockets.nim @@ -143,7 +143,7 @@ else: result = cint(ord(p)) -proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, +proc newRawSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM, protocol: TProtocol = IPPROTO_TCP): TSocketHandle = ## Creates a new socket; returns `InvalidSocket` if an error occurs. socket(toInt(domain), toInt(typ), toInt(protocol)) diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index 7f2c1e6cc..7e6270247 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -5,7 +5,6 @@ discard """ """ import asyncdispatch, rawsockets, net, strutils, os -var disp = newDispatcher() var msgCount = 0 const @@ -14,31 +13,31 @@ const var clientCount = 0 -proc sendMessages(disp: PDispatcher, client: TSocketHandle) {.async.} = +proc sendMessages(client: TAsyncFD) {.async.} = for i in 0 .. Date: Sun, 23 Mar 2014 18:28:05 -0400 Subject: [PATCH 219/249] added lowerBound function to algorithm library --- lib/pure/algorithm.nim | 24 ++++++++++++++++++++++++ tests/stdlib/talgorithm.nim | 3 +++ 2 files changed, 27 insertions(+) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 921c659de..b6a97b5da 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -55,6 +55,30 @@ proc smartBinarySearch*[T](a: openArray[T], key: T): int = const onlySafeCode = true +proc lowerBound*[T](a: openarray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = + ## same as binarySearch except that if key is not in `a` then this + ## returns the location where `key` would be if it were. In other + ## words if you have a sorted sequence and you call insert(thing, elm, lowerBound(thing, elm)) + ## the sequence will still be sorted + ## + ## `cmp` is the comparator function to use, the expected return values are the same as + ## that of system.cmp + result = a.low + var pos = result + var count, step: int + count = a.high - a.low + 1 + while count != 0: + pos = result + step = count div 2 + pos += step + if cmp(a[pos], key) < 0: + pos.inc + result = pos + count -= step + 1 + else: + count = step + +proc lowerBound*[T](a: openarray[T], key: T): int = lowerBound(a, key, system.cmp[T]) proc merge[T](a, b: var openArray[T], lo, m, hi: int, cmp: proc (x, y: T): int {.closure.}, order: TSortOrder) = template `<-` (a, b: expr) = diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 7ab652c82..3ca425fbc 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -6,3 +6,6 @@ doAssert product(@[@[1,2]]) == @[@[1,2]], "a simple case of one element" doAssert product(@[@[1,2], @[3,4]]) == @[@[2,4],@[1,4],@[2,3],@[1,3]], "two elements" doAssert product(@[@[1,2], @[3,4], @[5,6]]) == @[@[2,4,6],@[1,4,6],@[2,3,6],@[1,3,6], @[2,4,5],@[1,4,5],@[2,3,5],@[1,3,5]], "three elements" doAssert product(@[@[1,2], @[]]) == newSeq[seq[int]](), "two elements, but one empty" +doAssert lowerBound([1,2,4], 3, system.cmp[int]) == 2 +doAssert lowerBound([1,2,2,3], 4, system.cmp[int]) == 4 +doAssert lowerBound([1,2,3,10], 11) == 4 \ No newline at end of file From 976fb18a8f99adb9ccec5a248fcf36be7d07388f Mon Sep 17 00:00:00 2001 From: Charlie Barto Date: Sun, 23 Mar 2014 18:30:54 -0400 Subject: [PATCH 220/249] made the default comparator for lowerBound unqualified, so the user can customize via two phase lookup --- lib/pure/algorithm.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index b6a97b5da..fb0ba5355 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -78,7 +78,7 @@ proc lowerBound*[T](a: openarray[T], key: T, cmp: proc(x,y: T): int {.closure.}) else: count = step -proc lowerBound*[T](a: openarray[T], key: T): int = lowerBound(a, key, system.cmp[T]) +proc lowerBound*[T](a: openarray[T], key: T): int = lowerBound(a, key, cmp[T]) proc merge[T](a, b: var openArray[T], lo, m, hi: int, cmp: proc (x, y: T): int {.closure.}, order: TSortOrder) = template `<-` (a, b: expr) = From ee1192679ebdfa58d812576c3a959656e9ebe097 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 24 Mar 2014 08:43:57 +0100 Subject: [PATCH 221/249] VM: endless loop prevention --- compiler/vm.nim | 21 +- compiler/vmdef.nim | 7 +- compiler/vmgen.nim | 14 +- lib/wrappers/mongo.nim | 1204 ---------------------------------------- 4 files changed, 33 insertions(+), 1213 deletions(-) delete mode 100644 lib/wrappers/mongo.nim diff --git a/compiler/vm.nim b/compiler/vm.nim index 70eb6f828..f8b32bac1 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -68,7 +68,9 @@ proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: TMsgKind, arg = "") = msgWriteln("stack trace: (most recent call last)") stackTraceAux(c, tos, pc) - localError(c.debug[pc], msg, arg) + # XXX test if we want 'globalError' for every mode + if c.mode == emRepl: globalError(c.debug[pc], msg, arg) + else: localError(c.debug[pc], msg, arg) proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, errUnhandledExceptionX, @@ -333,6 +335,16 @@ proc compile(c: PCtx, s: PSym): int = when debugEchoCode: c.echoCode result #c.echoCode +template handleJmpBack() {.dirty.} = + if c.loopIterations <= 0: + if allowInfiniteLoops in c.features: + c.loopIterations = MaxLoopIterations + else: + msgWriteln("stack trace: (most recent call last)") + stackTraceAux(c, tos, pc) + globalError(c.debug[pc], errTooManyIterations) + dec(c.loopIterations) + proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos @@ -735,6 +747,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = globalError(c.debug[pc], errGenerated, "VM not built with FFI support") elif prc.kind != skTemplate: let newPc = compile(c, prc) + # tricky: a recursion is also a jump back, so we use the same + # logic as for loops: + if newPc < pc: handleJmpBack() #echo "new pc ", newPc, " calling: ", prc.name.s var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) newSeq(newFrame.slots, prc.offset) @@ -778,6 +793,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # jump Bx let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' inc pc, rbx + of opcJmpBack: + let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' + inc pc, rbx + handleJmpBack() of opcBranch: # we know the next instruction is a 'fjmp': let branch = c.constants[instr.regBx-wordExcess] diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 7725d79b8..90ff8e29f 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -16,6 +16,9 @@ const byteExcess* = 128 # we use excess-K for immediates wordExcess* = 32768 + MaxLoopIterations* = 500_000 # max iterations of all loops + + type TRegister* = range[0..255] TDest* = range[-1 .. 255] @@ -110,6 +113,7 @@ type opcTJmp, # jump Bx if A != 0 opcFJmp, # jump Bx if A == 0 opcJmp, # jump Bx + opcJmpBack, # jump Bx; resulting from a while loop opcBranch, # branch for 'case' opcTry, opcExcept, @@ -182,6 +186,7 @@ type mode*: TEvalMode features*: TSandboxFlags traceActive*: bool + loopIterations*: int TPosition* = distinct int @@ -190,7 +195,7 @@ type proc newCtx*(module: PSym): PCtx = PCtx(code: @[], debug: @[], globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], - prc: PProc(blocks: @[]), module: module) + prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations) proc refresh*(c: PCtx, module: PSym) = c.module = module diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 82d1e9ab8..a3af54145 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -92,10 +92,10 @@ proc genLabel(c: PCtx): TPosition = result = TPosition(c.code.len) #c.jumpTargets.incl(c.code.len) -proc jmpBack(c: PCtx, n: PNode, opc: TOpcode, p = TPosition(0)) = +proc jmpBack(c: PCtx, n: PNode, p = TPosition(0)) = let dist = p.int - c.code.len internalAssert(-0x7fff < dist and dist < 0x7fff) - gABx(c, n, opc, 0, dist) + gABx(c, n, opcJmpBack, 0, dist) proc patch(c: PCtx, p: TPosition) = # patch with current index @@ -229,20 +229,20 @@ proc genWhile(c: PCtx; n: PNode) = withBlock(nil): if isTrue(n.sons[0]): c.gen(n.sons[1]) - c.jmpBack(n, opcJmp, L1) + c.jmpBack(n, L1) elif isNotOpr(n.sons[0]): var tmp = c.genx(n.sons[0].sons[1]) let L2 = c.xjmp(n, opcTJmp, tmp) c.freeTemp(tmp) c.gen(n.sons[1]) - c.jmpBack(n, opcJmp, L1) + c.jmpBack(n, L1) c.patch(L2) else: var tmp = c.genx(n.sons[0]) let L2 = c.xjmp(n, opcFJmp, tmp) c.freeTemp(tmp) c.gen(n.sons[1]) - c.jmpBack(n, opcJmp, L1) + c.jmpBack(n, L1) c.patch(L2) proc genBlock(c: PCtx; n: PNode; dest: var TDest) = @@ -1518,7 +1518,7 @@ proc optimizeJumps(c: PCtx; start: int) = var d = i + c.code[i].jmpDiff for iters in countdown(maxIterations, 0): case c.code[d].opcode - of opcJmp: + of opcJmp, opcJmpBack: d = d + c.code[d].jmpDiff of opcTJmp, opcFJmp: if c.code[d].regA != reg: break @@ -1536,7 +1536,7 @@ proc optimizeJumps(c: PCtx; start: int) = else: break if d != i + c.code[i].jmpDiff: c.finalJumpTarget(i, d - i) - of opcJmp: + of opcJmp, opcJmpBack: var d = i + c.code[i].jmpDiff var iters = maxIterations while c.code[d].opcode == opcJmp and iters > 0: diff --git a/lib/wrappers/mongo.nim b/lib/wrappers/mongo.nim deleted file mode 100644 index 098b4f4d3..000000000 --- a/lib/wrappers/mongo.nim +++ /dev/null @@ -1,1204 +0,0 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module is a wrapper for the `mongodb`:idx: client C library. -## It allows you to connect to a mongo-server instance, send commands and -## receive replies. - -# -# Copyright 2009-2011 10gen Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import oids, times - -{.deadCodeElim: on.} - -when defined(windows): - const - mongodll* = "mongoc.dll" - bsondll* = "bson.dll" -elif defined(macosx): - const - mongodll* = "libmongoc.dylib" - bsondll* = "libbson.dylib" -else: - const - mongodll* = "libmongoc.so" - bsondll* = "libbson.so" - -# -# This package supports both compile-time and run-time determination of CPU -# byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be -# compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is -# defined as non-zero, the code will be compiled to run only on big-endian -# CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to -# run on either big- or little-endian CPUs, but will run slightly less -# efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. -# - -type - Tmd5_state*{.pure, final.} = object - count*: array[0..2 - 1, int32] # message length in bits, lsw first - abcd*: array[0..4 - 1, int32] # digest buffer - buf*: array[0..64 - 1, byte] # accumulate block - - -proc sock_init*(): cint{.stdcall, importc: "mongo_sock_init", dynlib: mongodll.} -const - OK* = 0 - ERROR* = - 1 - SIZE_OVERFLOW* = 1 - defaultHost* = "127.0.0.1" - defaultPort* = 27017 - -type - TValidity* = enum ## validity - VALID = 0, ## BSON is valid and UTF-8 compliant. - NOT_UTF8 = (1 shl 1), ## A key or a string is not valid UTF-8. - FIELD_HAS_DOT = (1 shl 2), ## Warning: key contains '.' character. - FIELD_INIT_DOLLAR = (1 shl 3), ## Warning: key starts with '$' character. - ALREADY_FINISHED = (1 shl 4) ## Trying to modify a finished BSON object. - TBinarySubtype* = enum - BIN_BINARY = 0, BIN_FUNC = 1, BIN_BINARY_OLD = 2, BIN_UUID = 3, BIN_MD5 = 5, - BIN_USER = 128 - TBsonKind* {.size: sizeof(cint).} = enum - bkEOO = 0, - bkDOUBLE = 1, - bkSTRING = 2, - bkOBJECT = 3, - bkARRAY = 4, - bkBINDATA = 5, - bkUNDEFINED = 6, - bkOID = 7, - bkBOOL = 8, - bkDATE = 9, - bkNULL = 10, - bkREGEX = 11, - bkDBREF = 12, #*< Deprecated. - bkCODE = 13, - bkSYMBOL = 14, - bkCODEWSCOPE = 15, - bkINT = 16, - bkTIMESTAMP = 17, - bkLONG = 18 - TBsonBool* = cint - TIter* {.pure, final.} = object - cur*: cstring - first*: TBsonBool - - TBson* {.pure, final.} = object - data*: cstring - cur*: cstring - dataSize*: cint - finished*: TBsonBool - ownsData*: TBsonBool - err*: cint - stackSize*: cint - stackPos*: cint - stackPtr*: ptr csize - stack*: array[0..32 - 1, csize] - - TDate* = int64 - -# milliseconds since epoch UTC - -type - TTimestamp*{.pure, final.} = object ## a timestamp - i*: cint # increment - t*: cint # time in seconds - -proc create*(): ptr TBson{.stdcall, importc: "bson_create", dynlib: bsondll.} -proc dispose*(b: ptr TBson){.stdcall, importc: "bson_dispose", dynlib: bsondll.} - -proc size*(b: var TBson): cint {.stdcall, importc: "bson_size", dynlib: bsondll.} - ## Size of a BSON object. - -proc bufferSize*(b: var TBson): cint{.stdcall, importc: "bson_buffer_size", - dynlib: bsondll.} - ## Buffer size of a BSON object. - -proc print*(b: var TBson){.stdcall, importc: "bson_print", dynlib: bsondll.} - ## Print a string representation of a BSON object. - -proc print*(TBson: cstring, depth: cint) {.stdcall, - importc: "bson_print_raw", dynlib: bsondll.} - ## Print a string representation of a BSON object up to `depth`. - - -proc data*(b: var TBson): cstring{.stdcall, importc: "bson_data", - dynlib: bsondll.} - ## Return a pointer to the raw buffer stored by this bson object. - -proc find*(it: var TIter, obj: var TBson, name: cstring): TBsonKind {.stdcall, - importc: "bson_find", dynlib: bsondll.} - ## Advance `it` to the named field. `obj` is the BSON object to use. - ## `name` is the name of the field to find. Returns the type of the found - ## object or ``bkEOO`` if it is not found. - -proc createIter*(): ptr TIter{.stdcall, importc: "bson_iterator_create", - dynlib: bsondll.} -proc dispose*(a2: ptr TIter){.stdcall, importc: "bson_iterator_dispose", - dynlib: bsondll.} - -proc initIter*(b: var TBson): TIter = - ## Initialize a bson iterator from the value `b`. - proc iterator_init(i: var TIter, b: var TBson){.stdcall, - importc: "bson_iterator_init", dynlib: bsondll.} - - iterator_init(result, b) - -proc fromBuffer*(i: var TIter, buffer: cstring) {.stdcall, - importc: "bson_iterator_from_buffer", dynlib: bsondll.} - ## Initialize a bson iterator from a cstring buffer. Note - ## that this is mostly used internally. - -proc more*(i: var TIter): bool = - ## Check to see if the bson_iterator has more data. - proc iterator_more(i: var TIter): TBsonBool{.stdcall, - importc: "bson_iterator_more", dynlib: bsondll.} - result = iterator_more(i) != 0'i32 - -proc next*(i: var TIter): TBsonKind {.stdcall, - importc: "bson_iterator_next", dynlib: bsondll.} - ## Point the iterator at the next BSON object. - -proc kind*(i: var TIter): TBsonKind{.stdcall, - importc: "bson_iterator_type", dynlib: bsondll.} - ## Get the type of the BSON object currently pointed to by the iterator. - -proc key*(i: var TIter): cstring{.stdcall, - importc: "bson_iterator_key", dynlib: bsondll.} - ## Get the key of the BSON object currently pointed to by the iterator. - -proc value*(i: var TIter): cstring{.stdcall, - importc: "bson_iterator_value", dynlib: bsondll.} - ## Get the value of the BSON object currently pointed to by the iterator. - -proc floatVal*(i: var TIter): float {.stdcall, - importc: "bson_iterator_double", dynlib: bsondll.} - ## Get the double value of the BSON object currently pointed to by the - ## iterator. - -proc intVal*(i: var TIter): cint{.stdcall, importc: "bson_iterator_int", - dynlib: bsondll.} - ## Get the int value of the BSON object currently pointed to by the iterator. - -proc int64Val*(i: var TIter): int64{.stdcall, - importc: "bson_iterator_long", dynlib: bsondll.} - ## Get the long value of the BSON object currently pointed to by the iterator. - -proc timestamp*(i: var TIter): Ttimestamp {.stdcall, - importc: "bson_iterator_timestamp", dynlib: bsondll.} - # return the bson timestamp as a whole or in parts - -proc timestampTime*(i: var TIter): cint {.stdcall, - importc: "bson_iterator_timestamp_time", dynlib: bsondll.} - # return the bson timestamp as a whole or in parts -proc timestampIncrement*(i: var TIter): cint{.stdcall, - importc: "bson_iterator_timestamp_increment", dynlib: bsondll.} - # return the bson timestamp as a whole or in parts - -proc boolVal*(i: var TIter): TBsonBool{.stdcall, - importc: "bson_iterator_bool", dynlib: bsondll.} - ## Get the boolean value of the BSON object currently pointed to by - ## the iterator. - ## - ## | false: boolean false, 0 in any type, or null - ## | true: anything else (even empty strings and objects) - -proc floatRaw*(i: var TIter): cdouble{.stdcall, - importc: "bson_iterator_double_raw", dynlib: bsondll.} - ## Get the double value of the BSON object currently pointed to by the - ## iterator. Assumes the correct type is used. - -proc intRaw*(i: var TIter): cint{.stdcall, - importc: "bson_iterator_int_raw", dynlib: bsondll.} - ## Get the int value of the BSON object currently pointed to by the - ## iterator. Assumes the correct type is used. - -proc int64Raw*(i: var TIter): int64{.stdcall, - importc: "bson_iterator_long_raw", dynlib: bsondll.} - ## Get the long value of the BSON object currently pointed to by the - ## iterator. Assumes the correct type is used. - -proc boolRaw*(i: var TIter): TBsonBool{.stdcall, - importc: "bson_iterator_bool_raw", dynlib: bsondll.} - ## Get the bson_bool_t value of the BSON object currently pointed to by the - ## iterator. Assumes the correct type is used. - -proc oidVal*(i: var TIter): ptr TOid {.stdcall, - importc: "bson_iterator_oid", dynlib: bsondll.} - ## Get the bson_oid_t value of the BSON object currently pointed to by the - ## iterator. - -proc strVal*(i: var TIter): cstring {.stdcall, - importc: "bson_iterator_string", dynlib: bsondll.} - ## Get the string value of the BSON object currently pointed to by the - ## iterator. - -proc strLen*(i: var TIter): cint {.stdcall, - importc: "bson_iterator_string_len", dynlib: bsondll.} - ## Get the string length of the BSON object currently pointed to by the - ## iterator. - -proc code*(i: var TIter): cstring {.stdcall, - importc: "bson_iterator_code", dynlib: bsondll.} - ## Get the code value of the BSON object currently pointed to by the - ## iterator. Works with bson_code, bson_codewscope, and BSON_STRING - ## returns ``nil`` for everything else. - -proc codeScope*(i: var TIter, scope: var TBson) {.stdcall, - importc: "bson_iterator_code_scope", dynlib: bsondll.} - ## Calls bson_empty on scope if not a bson_codewscope - -proc date*(i: var TIter): Tdate {.stdcall, - importc: "bson_iterator_date", dynlib: bsondll.} - ## Get the date value of the BSON object currently pointed to by the - ## iterator. - -proc time*(i: var TIter): TTime {.stdcall, - importc: "bson_iterator_time_t", dynlib: bsondll.} - ## Get the time value of the BSON object currently pointed to by the - ## iterator. - -proc binLen*(i: var TIter): cint {.stdcall, - importc: "bson_iterator_bin_len", dynlib: bsondll.} - ## Get the length of the BSON binary object currently pointed to by the - ## iterator. - -proc binType*(i: var TIter): char {.stdcall, - importc: "bson_iterator_bin_type", dynlib: bsondll.} - ## Get the type of the BSON binary object currently pointed to by the - ## iterator. - -proc binData*(i: var TIter): cstring {.stdcall, - importc: "bson_iterator_bin_data", dynlib: bsondll.} - ## Get the value of the BSON binary object currently pointed to by the - ## iterator. - -proc regex*(i: var TIter): cstring {.stdcall, - importc: "bson_iterator_regex", dynlib: bsondll.} - ## Get the value of the BSON regex object currently pointed to by the - ## iterator. - -proc regexOpts*(i: var TIter): cstring {.stdcall, - importc: "bson_iterator_regex_opts", dynlib: bsondll.} - ## Get the options of the BSON regex object currently pointed to by the - ## iterator. - -proc subobject*(i: var TIter, sub: var TBson) {.stdcall, - importc: "bson_iterator_subobject", dynlib: bsondll.} - ## Get the BSON subobject currently pointed to by the - ## iterator. - -proc subiterator*(i: var TIter, sub: var TIter) {.stdcall, - importc: "bson_iterator_subiterator", dynlib: bsondll.} - ## Get a bson_iterator that on the BSON subobject. - - -# ---------------------------- -# BUILDING -# ---------------------------- - -proc init*(b: var TBson) {.stdcall, importc: "bson_init", dynlib: bsondll.} - ## Initialize a new bson object. If not created - ## with bson_new, you must initialize each new bson - ## object using this function. - ## - ## When finished, you must pass the bson object to bson_destroy(). - -proc init*(b: var TBson, data: cstring): cint {.stdcall, - importc: "bson_init_data", dynlib: bsondll.} - ## Initialize a BSON object, and point its data - ## pointer to the provided `data`. - ## Returns OK or ERROR. - -proc initFinished*(b: var TBson, data: cstring): cint {.stdcall, - importc: "bson_init_finished_data", dynlib: bsondll.} - -proc initSize*(b: var TBson, size: cint) {.stdcall, importc: "bson_init_size", - dynlib: bsondll.} - ## Initialize a BSON object, and set its buffer to the given size. - ## Returns OK or ERROR. - -proc ensureSpace*(b: var TBson, bytesNeeded: cint): cint {.stdcall, - importc: "bson_ensure_space", dynlib: bsondll.} - ## Grow a bson object. `bytesNeeded` is the additional number of bytes needed. - -proc finish*(b: var TBson): cint{.stdcall, importc: "bson_finish", - dynlib: bsondll, discardable.} - ## Finalize a bson object. Returns the standard error code. - ## To deallocate memory, call destroy on the bson object. - -proc destroy*(b: var TBson){.stdcall, importc: "bson_destroy", dynlib: bsondll.} - ## Destroy a bson object. - -proc empty*(obj: var TBson) {.stdcall, importc: "bson_empty", - dynlib: bsondll.} - ## Sets a pointer to a static empty BSON object. - ## `obj` is the BSON object to initialize. - -proc copy*(outp, inp: var TBson): cint{.stdcall, importc: "bson_copy", - dynlib: bsondll.} - ## Make a complete copy of the a BSON object. - ## The source bson object must be in a finished - ## state; otherwise, the copy will fail. - -proc add*(b: var TBson, name: cstring, oid: TOid) = - ## adds an OID to `b`. - proc appendOid(b: var TBson, name: cstring, oid: ptr TOid): cint {.stdcall, - importc: "bson_append_oid", dynlib: bsondll.} - - var oid = oid - discard appendOid(b, name, addr(oid)) - -proc add*(b: var TBson, name: cstring, i: cint): cint{.stdcall, - importc: "bson_append_int", dynlib: bsondll, discardable.} - ## Append an int to a bson. - -proc add*(b: var TBson, name: cstring, i: int64): cint{.stdcall, - importc: "bson_append_long", dynlib: bsondll, discardable.} - ## Append an long to a bson. - -proc add*(b: var TBson, name: cstring, d: float): cint{.stdcall, - importc: "bson_append_double", dynlib: bsondll, discardable.} - ## Append an double to a bson. - -proc add*(b: var TBson, name: cstring, str: cstring): cint {.stdcall, - importc: "bson_append_string", dynlib: bsondll, discardable.} - ## Append a string to a bson. - -proc add*(b: var TBson, name: cstring, str: cstring, len: cint): cint{. - stdcall, importc: "bson_append_string_n", dynlib: bsondll, discardable.} - ## Append len bytes of a string to a bson. - -proc add*(b: var TBson, name: cstring, str: string) = - ## Append a Nimrod string `str` to a bson. - discard add(b, name, str, str.len.cint) - -proc addSymbol*(b: var TBson, name: cstring, str: cstring): cint{.stdcall, - importc: "bson_append_symbol", dynlib: bsondll, discardable.} - ## Append a symbol to a bson. - -proc addSymbol*(b: var TBson, name: cstring, str: cstring, len: cint): cint{. - stdcall, importc: "bson_append_symbol_n", dynlib: bsondll, discardable.} - ## Append len bytes of a symbol to a bson. - -proc addCode*(b: var TBson, name: cstring, str: cstring): cint{.stdcall, - importc: "bson_append_code", dynlib: bsondll, discardable.} - ## Append code to a bson. - -proc addCode*(b: var TBson, name: cstring, str: cstring, len: cint): cint{. - stdcall, importc: "bson_append_code_n", dynlib: bsondll, discardable.} - ## Append len bytes of code to a bson. - -proc addCode*(b: var TBson, name: cstring, code: cstring, - scope: var TBson): cint{.stdcall, - importc: "bson_append_code_w_scope", dynlib: bsondll, discardable.} - ## Append code to a bson with scope. - -proc addCode*(b: var TBson, name: cstring, code: cstring, - size: cint, scope: var TBson): cint{.stdcall, - importc: "bson_append_code_w_scope_n", dynlib: bsondll, discardable.} - ## Append len bytes of code to a bson with scope. - -proc addBinary*(b: var TBson, name: cstring, typ: char, str: cstring, - len: cint): cint{.stdcall, importc: "bson_append_binary", - dynlib: bsondll, discardable.} - ## Append binary data to a bson. - -proc addBinary*(b: var TBson, name: cstring, data: string) = - ## Append binary data to a bson. - addBinary(b, name, '\5', data, data.len.cint) - -proc addBool*(b: var TBson, name: cstring, v: TBsonBool): cint{.stdcall, - importc: "bson_append_bool", dynlib: bsondll, discardable.} - ## Append a bson_bool_t to a bson. - -proc addNull*(b: var TBson, name: cstring): cint {.stdcall, - importc: "bson_append_null", dynlib: bsondll, discardable.} - ## Append a null value to a bson. - -proc addUndefined*(b: var TBson, name: cstring): cint{.stdcall, - importc: "bson_append_undefined", dynlib: bsondll, discardable.} - ## Append an undefined value to a bson. - -proc addRegex*(b: var TBson, name: cstring, pattern: cstring, opts: cstring): cint{. - stdcall, importc: "bson_append_regex", dynlib: bsondll, discardable.} - ## Append a regex value to a bson. - -proc add*(b: var TBson, name: cstring, TBson: var TBson): cint {.stdcall, - importc: "bson_append_bson", dynlib: bsondll, discardable.} - ## Append bson data to a bson. - -proc addElement*(b: var TBson, name_or_null: cstring, elem: var TIter): cint{. - stdcall, importc: "bson_append_element", dynlib: bsondll, discardable.} - ## Append a BSON element to a bson from the current point of an iterator. - -proc addTimestamp*(b: var TBson, name: cstring, ts: var TTimestamp): cint{. - stdcall, importc: "bson_append_timestamp", dynlib: bsondll, discardable.} - ## Append a bson_timestamp_t value to a bson. - -proc addTimestamp2*(b: var TBson, name: cstring, time: cint, increment: cint): cint{. - stdcall, importc: "bson_append_timestamp2", dynlib: bsondll, discardable.} -proc addDate*(b: var TBson, name: cstring, millis: TDate): cint{.stdcall, - importc: "bson_append_date", dynlib: bsondll, discardable.} - ## Append a bson_date_t value to a bson. - -proc addTime*(b: var TBson, name: cstring, secs: TTime): cint{.stdcall, - importc: "bson_append_time_t", dynlib: bsondll, discardable.} - ## Append a time_t value to a bson. - -proc addStartObject*(b: var TBson, name: cstring): cint {.stdcall, - importc: "bson_append_start_object", dynlib: bsondll, discardable.} - ## Start appending a new object to a bson. - -proc addStartArray*(b: var TBson, name: cstring): cint {.stdcall, - importc: "bson_append_start_array", dynlib: bsondll, discardable.} - ## Start appending a new array to a bson. - -proc addFinishObject*(b: var TBson): cint {.stdcall, - importc: "bson_append_finish_object", dynlib: bsondll, discardable.} - ## Finish appending a new object or array to a bson. - -proc addFinishArray*(b: var TBson): cint {.stdcall, - importc: "bson_append_finish_array", dynlib: bsondll, discardable.} - ## Finish appending a new object or array to a bson. This - ## is simply an alias for bson_append_finish_object. - -proc numstr*(str: cstring, i: cint){.stdcall, importc: "bson_numstr", - dynlib: bsondll.} -proc incnumstr*(str: cstring){.stdcall, importc: "bson_incnumstr", - dynlib: bsondll.} - -type - TErrHandler* = proc (errmsg: cstring){. - stdcall.} ## an error handler. Error handlers shouldn't return! - -proc setBsonErrHandler*(func: TErrHandler): TErrHandler {.stdcall, - importc: "set_bson_err_handler", dynlib: bsondll.} - ## Set a function for error handling. - ## Returns the old error handling function, or nil. - -proc fatal*(ok: cint){.stdcall, importc: "bson_fatal", dynlib: bsondll.} - ## does nothing if ok != 0. Exit fatally. - -proc fatal*(ok: cint, msg: cstring){.stdcall, importc: "bson_fatal_msg", - dynlib: bsondll.} - ## Exit fatally with an error message. - -proc builderError*(b: var TBson){.stdcall, importc: "bson_builder_error", - dynlib: bsondll.} - ## Invoke the error handler, but do not exit. - -proc int64ToDouble*(i64: int64): cdouble {.stdcall, - importc: "bson_int64_to_double", dynlib: bsondll.} - ## Cast an int64_t to double. This is necessary for embedding in - ## certain environments. - -const - MAJOR* = 0 - MINOR* = 4 - PATCH* = 0 - -type - TError*{.size: sizeof(cint).} = enum ## connection errors - CONN_SUCCESS = 0, ## Connection success! - CONN_NO_SOCKET, ## Could not create a socket. - CONN_FAIL, ## An error occured while calling connect(). - CONN_ADDR_FAIL, ## An error occured while calling getaddrinfo(). - CONN_NOT_MASTER, ## Warning: connected to a non-master node (read-only). - CONN_BAD_SET_NAME, ## Given rs name doesn't match this replica set. - CONN_NO_PRIMARY, ## Can't find primary in replica set. Connection closed. - IO_ERROR, ## An error occurred while reading or writing on the socket. - READ_SIZE_ERROR, ## The response is not the expected length. - COMMAND_FAILED, ## The command returned with 'ok' value of 0. - BSON_INVALID, ## BSON not valid for the specified op. - BSON_NOT_FINISHED ## BSON object has not been finished. - TCursorError*{.size: sizeof(cint).} = enum ## cursor error - CURSOR_EXHAUSTED, ## The cursor has no more results. - CURSOR_INVALID, ## The cursor has timed out or is not recognized. - CURSOR_PENDING, ## Tailable cursor still alive but no data. - CURSOR_QUERY_FAIL, ## The server returned an '$err' object, indicating query failure. - ## See conn.lasterrcode and conn.lasterrstr for details. - CURSOR_BSON_ERROR ## Something is wrong with the BSON provided. See conn.err - ## for details. - TCursorFlags* = enum ## cursor flags - CURSOR_MUST_FREE = 1, ## mongo_cursor_destroy should free cursor. - CURSOR_QUERY_SENT = (1 shl 1) ## Initial query has been sent. - TindexOpts* = enum - INDEX_UNIQUE = (1 shl 0), INDEX_DROP_DUPS = (1 shl 2), - INDEX_BACKGROUND = (1 shl 3), INDEX_SPARSE = (1 shl 4) - TupdateOpts* = enum - UPDATE_UPSERT = 0x00000001, - UPDATE_MULTI = 0x00000002, - UPDATE_BASIC = 0x00000004 - TCursorOpts* = enum - TAILABLE = (1 shl 1), ## Create a tailable cursor. - SLAVE_OK = (1 shl 2), ## Allow queries on a non-primary node. - NO_CURSOR_TIMEOUT = (1 shl 4), ## Disable cursor timeouts. - AWAIT_DATA = (1 shl 5), ## Momentarily block for more data. - EXHAUST = (1 shl 6), ## Stream in multiple 'more' packages. - PARTIAL = (1 shl 7) ## Allow reads even if a shard is down. - Toperations* = enum - OP_MSG = 1000, OP_UPDATE = 2001, OP_INSERT = 2002, OP_QUERY = 2004, - OP_GET_MORE = 2005, OP_DELETE = 2006, OP_KILL_CURSORS = 2007 - THeader* {.pure, final.} = object - len*: cint - id*: cint - responseTo*: cint - op*: cint - - TMessage* {.pure, final.} = object - head*: Theader - data*: char - - TReplyFields*{.pure, final.} = object - flag*: cint # FIX THIS COMMENT non-zero on failure - cursorID*: int64 - start*: cint - num*: cint - - TReply*{.pure, final.} = object - head*: Theader - fields*: Treply_fields - objs*: char - - THostPort*{.pure, final.} = object - host*: array[0..255 - 1, char] - port*: cint - next*: ptr THostPort - - TReplset*{.pure, final.} = object ## replset - seeds*: ptr THostPort ## List of seeds provided by the user. - hosts*: ptr THostPort ## List of host/ports given by the replica set - name*: cstring ## Name of the replica set. - primary_connected*: TBsonBool ## Primary node connection status. - - TWriteConcern*{.pure, final.} = object ## mongo_write_concern - w*: cint - wtimeout*: cint - j*: cint - fsync*: cint - mode*: cstring - cmd*: TBSon - - TMongo*{.pure, final.} = object ## mongo - primary*: ptr THostPort ## Primary connection info. - replset*: ptr TReplSet ## replset object if connected to a replica set. - sock*: cint ## Socket file descriptor. - flags*: cint ## Flags on this connection object. - conn_timeout_ms*: cint ## Connection timeout in milliseconds. - op_timeout_ms*: cint ## Read and write timeout in milliseconds. - max_bson_size*: cint ## Largest BSON object allowed on this connection. - connected*: TBsonBool ## Connection status. - write_concern*: TWriteConcern ## The default write concern. - err*: TError ## Most recent driver error code. - errcode*: cint ## Most recent errno or WSAGetLastError(). - errstr*: array[0..128 - 1, char] ## String version of most recent driver error code. - lasterrcode*: cint ## getlasterror code given by the server on error. - lasterrstr*: array[0..128 - 1, char] ## getlasterror string generated by server. - - TCursor*{.pure, final.} = object ## cursor - reply*: ptr TReply ## reply is owned by cursor - conn*: ptr TMongo ## connection is *not* owned by cursor - ns*: cstring ## owned by cursor - flags*: cint ## Flags used internally by this drivers. - seen*: cint ## Number returned so far. - current*: TBson ## This cursor's current bson object. - err*: TCursorError ## Errors on this cursor. - query*: ptr TBson ## Bitfield containing cursor options. - fields*: ptr TBson ## Bitfield containing cursor options. - options*: cint ## Bitfield containing cursor options. - limit*: cint ## Bitfield containing cursor options. - skip*: cint ## Bitfield containing cursor options. - - -# Connection API - -proc createMongo*(): ptr TMongo{.stdcall, importc: "mongo_create", dynlib: mongodll.} -proc dispose*(conn: ptr TMongo){.stdcall, importc: "mongo_dispose", - dynlib: mongodll.} -proc getErr*(conn: var TMongo): cint{.stdcall, importc: "mongo_get_err", - dynlib: mongodll.} -proc isConnected*(conn: var TMongo): cint{.stdcall, - importc: "mongo_is_connected", dynlib: mongodll.} -proc getOpTimeout*(conn: var TMongo): cint{.stdcall, - importc: "mongo_get_op_timeout", dynlib: mongodll.} -proc getPrimary*(conn: var TMongo): cstring{.stdcall, - importc: "mongo_get_primary", dynlib: mongodll.} -proc getSocket*(conn: var TMongo): cint {.stdcall, importc: "mongo_get_socket", - dynlib: mongodll.} -proc getHostCount*(conn: var TMongo): cint{.stdcall, - importc: "mongo_get_host_count", dynlib: mongodll.} -proc getHost*(conn: var TMongo, i: cint): cstring {.stdcall, - importc: "mongo_get_host", dynlib: mongodll.} -proc createCursor*(): ptr TCursor{.stdcall, importc: "mongo_cursor_create", - dynlib: mongodll.} -proc dispose*(cursor: ptr TCursor){.stdcall, - importc: "mongo_cursor_dispose", dynlib: mongodll.} -proc getServerErr*(conn: var TMongo): cint{.stdcall, - importc: "mongo_get_server_err", dynlib: mongodll.} -proc getServerErrString*(conn: var TMongo): cstring{.stdcall, - importc: "mongo_get_server_err_string", dynlib: mongodll.} - -proc init*(conn: var TMongo){.stdcall, importc: "mongo_init", dynlib: mongodll.} - ## Initialize a new mongo connection object. You must initialize each mongo - ## object using this function. - ## When finished, you must pass this object to ``destroy``. - -proc connect*(conn: var TMongo, host: cstring = defaultHost, - port: cint = defaultPort): cint {.stdcall, - importc: "mongo_connect", dynlib: mongodll, deprecated.} - ## Connect to a single MongoDB server. -proc client*(conn: var TMongo, host: cstring = defaultHost, - port: cint = defaultPort): cint {.stdcall, - importc: "mongo_client", dynlib: mongodll.} - ## Connect to a single MongoDB server. - -proc replsetInit*(conn: var TMongo, name: cstring){.stdcall, - importc: "mongo_replset_init", dynlib: mongodll.} - ## Set up this connection object for connecting to a replica set. - ## To connect, pass the object to replsetConnect. - ## `name` is the name of the replica set to connect to. - -proc replsetAddSeed*(conn: var TMongo, host: cstring = defaultHost, - port: cint = defaultPort){.stdcall, - importc: "mongo_replset_add_seed", dynlib: mongodll.} - ## Add a seed node to the replica set connection object. - ## You must specify at least one seed node before connecting - ## to a replica set. - -proc parseHost*(hostString: cstring, hostPort: var ThostPort){.stdcall, - importc: "mongo_parse_host", dynlib: mongodll.} - ## Utility function for converting a host-port string to a mongo_host_port. - ## `hostString` is a string containing either a host or a host and port - ## separated by a colon. - ## `hostPort` is the mongo_host_port object to write the result to. - -proc replsetConnect*(conn: var TMongo): cint{.stdcall, - importc: "mongo_replset_connect", dynlib: mongodll.} - ## Connect to a replica set. - ## Before passing a connection object to this function, you must already - ## have called setReplset and replsetAddSeed. - -proc setOpTimeout*(conn: var TMongo, millis: cint): cint{.stdcall, - importc: "mongo_set_op_timeout", dynlib: mongodll.} - ## Set a timeout for operations on this connection. This - ## is a platform-specific feature, and only work on Unix-like - ## systems. You must also compile for linux to support this. - -proc checkConnection*(conn: var TMongo): cint {.stdcall, - importc: "mongo_check_connection", dynlib: mongodll.} - ## Ensure that this connection is healthy by performing - ## a round-trip to the server. - ## Returns OK if connected; otherwise ERROR. - -proc reconnect*(conn: var TMongo): cint {.stdcall, importc: "mongo_reconnect", - dynlib: mongodll.} - ## Try reconnecting to the server using the existing connection settings. - ## This function will disconnect the current socket. If you've authenticated, - ## you'll need to re-authenticate after calling this function. - -proc disconnect*(conn: var TMongo){.stdcall, importc: "mongo_disconnect", - dynlib: mongodll.} - ## Close the current connection to the server. After calling - ## this function, you may call reconnect with the same - ## connection object. - -proc destroy*(conn: var TMongo){.stdcall, importc: "mongo_destroy", - dynlib: mongodll.} - ## Close any existing connection to the server and free all allocated - ## memory associated with the conn object. - ## You must always call this function when finished with the connection - ## object. - -proc insert*(conn: var TMongo, ns: cstring, data: var TBson, - custom_write_concern: ptr TWriteConcern): cint{.stdcall, - importc: "mongo_insert", dynlib: mongodll, discardable.} - ## Insert a BSON document into a MongoDB server. This function - ## will fail if the supplied BSON struct is not UTF-8 or if - ## the keys are invalid for insert (contain '.' or start with '$'). - -proc insertBatch*(conn: var TMongo, ns: cstring, - data: ptr ptr TBson, num: cint): cint{. - stdcall, importc: "mongo_insert_batch", dynlib: mongodll, discardable.} - ## Insert a batch of BSON documents into a MongoDB server. This function - ## will fail if any of the documents to be inserted is invalid. - ## `num` is the number of documents in data. - -proc update*(conn: var TMongo, ns: cstring, cond, op: var TBson, - flags: cint): cint{.stdcall, importc: "mongo_update", - dynlib: mongodll, discardable.} - ## Update a document in a MongoDB server. - ## - ## | conn a mongo object. - ## | ns the namespace. - ## | cond the bson update query. - ## | op the bson update data. - ## | flags flags for the update. - ## | returns OK or ERROR with error stored in conn object. - -proc remove*(conn: var TMongo, namespace: cstring, cond: var TBson): cint{.stdcall, - importc: "mongo_remove", dynlib: mongodll.} - ## Remove a document from a MongoDB server. - ## - ## | conn a mongo object. - ## | ns the namespace. - ## | cond the bson query. - ## | returns OK or ERROR with error stored in conn object. - -proc find*(conn: var TMongo, namespace: cstring, query, fields: var TBson, - limit, skip: cint, options: cint): ptr TCursor{.stdcall, - importc: "mongo_find", dynlib: mongodll.} - ## Find documents in a MongoDB server. - ## - ## | conn a mongo object. - ## | ns the namespace. - ## | query the bson query. - ## | fields a bson document of fields to be returned. - ## | limit the maximum number of documents to return. - ## | skip the number of documents to skip. - ## | options A bitfield containing cursor options. - ## | returns A cursor object allocated on the heap or nil if - ## an error has occurred. For finer-grained error checking, - ## use the cursor builder API instead. - -proc init*(cursor: var TCursor, conn: var TMongo, namespace: cstring){.stdcall, - importc: "mongo_cursor_init", dynlib: mongodll.} - ## Initalize a new cursor object. - ## - ## The namespace is represented as the database - ## name and collection name separated by a dot. e.g., "test.users". - -proc setQuery*(cursor: var TCursor, query: var TBson) {.stdcall, - importc: "mongo_cursor_set_query", dynlib: mongodll.} - ## Set the bson object specifying this cursor's query spec. If - ## your query is the empty bson object "{}", then you need not - ## set this value. - ## - ## `query` is a bson object representing the query spec. This may - ## be either a simple query spec or a complex spec storing values for - ## $query, $orderby, $hint, and/or $explain. See - ## http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol for details. - -proc setFields*(cursor: var TCursor, fields: var TBson){.stdcall, - importc: "mongo_cursor_set_fields", dynlib: mongodll.} - ## Set the fields to return for this cursor. If you want to return - ## all fields, you need not set this value. - ## `fields` is a bson object representing the fields to return. - ## See http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields. - -proc setSkip*(cursor: var TCursor, skip: cint){.stdcall, - importc: "mongo_cursor_set_skip", dynlib: mongodll.} - ## Set the number of documents to skip. - -proc setLimit*(cursor: var TCursor, limit: cint){.stdcall, - importc: "mongo_cursor_set_limit", dynlib: mongodll.} - ## Set the number of documents to return. - -proc setOptions*(cursor: var TCursor, options: cint){.stdcall, - importc: "mongo_cursor_set_options", dynlib: mongodll.} - ## Set any of the available query options (e.g., TAILABLE). - ## See `TCursorOpts` for available constants. - -proc data*(cursor: var TCursor): cstring {.stdcall, - importc: "mongo_cursor_data", dynlib: mongodll.} - ## Return the current BSON object data as a ``cstring``. This is useful - ## for creating bson iterators. - -proc bson*(cursor: var TCursor): ptr TBson{.stdcall, - importc: "mongo_cursor_bson", dynlib: mongodll.} - ## Return the current BSON object. - -proc next*(cursor: var TCursor): cint {.stdcall, - importc: "mongo_cursor_next", dynlib: mongodll.} - ## Iterate the cursor, returning the next item. When successful, - ## the returned object will be stored in cursor.current; - -proc destroy*(cursor: var TCursor): cint {.stdcall, - importc: "mongo_cursor_destroy", dynlib: mongodll, discardable.} - ## Destroy a cursor object. When finished with a cursor, you - ## must pass it to this function. - -proc findOne*(conn: var TMongo, namespace: cstring, query: var TBson, - fields: var TBson, outp: var TBson): cint{.stdcall, - importc: "mongo_find_one", dynlib: mongodll.} - ## Find a single document in a MongoDB server. - ## - ## | conn a mongo object. - ## | ns the namespace. - ## | query the bson query. - ## | fields a bson document of the fields to be returned. - ## | outp a bson document in which to put the query result. - ## outp can be nil if you don't care about results. Useful for commands. - -proc count*(conn: var TMongo, db: cstring, coll: cstring, query: var TBson): cdouble{. - stdcall, importc: "mongo_count", dynlib: mongodll.} - ## Count the number of documents in a collection matching a query. - ## - ## | conn a mongo object. - ## | db the db name. - ## | coll the collection name. - ## | query the BSON query. - ## | returns the number of matching documents. If the command fails, - ## ERROR is returned. - -proc createIndex*(conn: var TMongo, namespace: cstring, key: var TBson, - options: cint, outp: var TBson): cint {.stdcall, - importc: "mongo_create_index", dynlib: mongodll.} - ## Create a compouned index. - ## - ## | conn a mongo object. - ## | ns the namespace. - ## | data the bson index data. - ## | options a bitfield for setting index options. Possibilities include - ## INDEX_UNIQUE, INDEX_DROP_DUPS, INDEX_BACKGROUND, - ## and INDEX_SPARSE. - ## | out a bson document containing errors, if any. - ## | returns MONGO_OK if index is created successfully; otherwise, MONGO_ERROR. - -proc createSimpleIndex*(conn: var TMongo, namespace, field: cstring, - options: cint, outp: var TBson): TBsonBool {.stdcall, - importc: "mongo_create_simple_index", dynlib: mongodll.} - ## Create an index with a single key. - ## - ## | conn a mongo object. - ## | ns the namespace. - ## | field the index key. - ## | options index options. - ## | out a BSON document containing errors, if any. - ## | returns true if the index was created. - - -# ---------------------------- -# COMMANDS -# ---------------------------- - - -proc runCommand*(conn: var TMongo, db: cstring, command: var TBson, - outp: var TBson): cint{.stdcall, importc: "mongo_run_command", - dynlib: mongodll.} - ## Run a command on a MongoDB server. - ## - ## | conn a mongo object. - ## | db the name of the database. - ## | command the BSON command to run. - ## | out the BSON result of the command. - ## | returns OK if the command ran without error. - -proc simpleIntCommand*(conn: var TMongo, db: cstring, cmd: cstring, arg: cint, - outp: var TBson): cint{.stdcall, - importc: "mongo_simple_int_command", dynlib: mongodll.} - ## Run a command that accepts a simple string key and integer value. - ## - ## | conn a mongo object. - ## | db the name of the database. - ## | cmd the command to run. - ## | arg the integer argument to the command. - ## | out the BSON result of the command. - ## | returns OK or an error code. - -proc simpleStrCommand*(conn: var TMongo, db: cstring, cmd: cstring, - arg: cstring, outp: var TBson): cint{.stdcall, - importc: "mongo_simple_str_command", dynlib: mongodll.} - ## Run a command that accepts a simple string key and value. - ## - ## | conn a mongo object. - ## | db the name of the database. - ## | cmd the command to run. - ## | arg the string argument to the command. - ## | out the BSON result of the command. - ## | returns true if the command ran without error. - -proc cmdDropDb*(conn: var TMongo, db: cstring): cint{.stdcall, - importc: "mongo_cmd_drop_db", dynlib: mongodll.} - ## Drop a database. - ## - ## | conn a mongo object. - ## | db the name of the database to drop. - ## | returns OK or an error code. - -proc cmdDropCollection*(conn: var TMongo, db: cstring, collection: cstring, - outp: var TBson): cint{.stdcall, - importc: "mongo_cmd_drop_collection", dynlib: mongodll.} - ## Drop a collection. - ## - ## | conn a mongo object. - ## | db the name of the database. - ## | collection the name of the collection to drop. - ## | out a BSON document containing the result of the command. - ## | returns true if the collection drop was successful. - -proc cmdAddUser*(conn: var TMongo, db: cstring, user: cstring, pass: cstring): cint{. - stdcall, importc: "mongo_cmd_add_user", dynlib: mongodll.} - ## Add a database user. - ## - ## | conn a mongo object. - ## | db the database in which to add the user. - ## | user the user name - ## | pass the user password - ## | returns OK or ERROR. - -proc cmdAuthenticate*(conn: var TMongo, db: cstring, user: cstring, - pass: cstring): cint{.stdcall, - importc: "mongo_cmd_authenticate", dynlib: mongodll.} - ## Authenticate a user. - ## - ## | conn a mongo object. - ## | db the database to authenticate against. - ## | user the user name to authenticate. - ## | pass the user's password. - ## | returns OK on sucess and ERROR on failure. - -proc cmdIsMaster*(conn: var TMongo, outp: var TBson): TBsonBool {.stdcall, - importc: "mongo_cmd_ismaster", dynlib: mongodll.} - ## Check if the current server is a master. - ## - ## | conn a mongo object. - ## | outp a BSON result of the command. - ## | returns true if the server is a master. - -proc cmdGetLastError*(conn: var TMongo, db: cstring, outp: var TBson): cint{. - stdcall, importc: "mongo_cmd_get_last_error", dynlib: mongodll.} - ## Get the error for the last command with the current connection. - ## - ## | conn a mongo object. - ## | db the name of the database. - ## | outp a BSON object containing the error details. - ## | returns OK or ERROR - -proc cmdGetPrevError*(conn: var TMongo, db: cstring, outp: var TBson): cint{. - stdcall, importc: "mongo_cmd_get_prev_error", dynlib: mongodll.} - ## Get the most recent error with the current connection. - ## - ## | conn a mongo object. - ## | db the name of the database. - ## | outp a BSON object containing the error details. - ## | returns OK or ERROR. - -proc cmdResetError*(conn: var TMongo, db: cstring){.stdcall, - importc: "mongo_cmd_reset_error", dynlib: mongodll.} - ## Reset the error state for the connection. `db` is the name of the database. - -# gridfs.h - -const - DEFAULT_CHUNK_SIZE* = 262144 - -type - TOffset* = int64 - -# A GridFS represents a single collection of GridFS files in the database. - -type - TGridfs*{.pure, final.} = object - client*: ptr TMongo ## The client to db-connection. - dbname*: cstring ## The root database name - prefix*: cstring ## The prefix of the GridFS's collections, - ## default is nil - files_ns*: cstring ## The namespace where the file's metadata - ## is stored - chunks_ns*: cstring ## The namespace where the files's data is - ## stored in chunks - -# A GridFile is a single GridFS file. - -type - TGridFile*{.pure, final.} = object - gfs*: ptr TGridfs ## GridFS where the GridFile is located - meta*: ptr TBson ## GridFile's bson object where all - ## its metadata is located - pos*: TOffset ## position is the offset in the file - id*: TOid ## files_id of the gridfile - remote_name*: cstring ## name of the gridfile as a string - content_type*: cstring ## gridfile's content type - length*: TOffset ## length of this gridfile - chunk_num*: cint ## number of the current chunk being written to - pending_data*: cstring ## buffer storing data still to be - ## written to chunks - pending_len*: cint ## length of pending_data buffer - - -proc createGridfs*(): ptr TGridfs{.stdcall, importc: "gridfs_create", dynlib: mongodll.} -proc dispose*(gfs: ptr TGridfs){.stdcall, importc: "gridfs_dispose", - dynlib: mongodll.} -proc createGridfile*(): ptr TGridFile{.stdcall, importc: "gridfile_create", - dynlib: mongodll.} -proc dispose*(gf: ptr TGridFile){.stdcall, importc: "gridfile_dispose", - dynlib: mongodll.} -proc getDescriptor*(gf: var TGridFile, outp: var TBson){.stdcall, - importc: "gridfile_get_descriptor", dynlib: mongodll.} - - -proc init*(client: var TMongo, dbname: cstring, prefix: cstring, - gfs: var TGridfs): cint{.stdcall, importc: "gridfs_init", - dynlib: mongodll.} - ## Initializes a GridFS object - ## - ## | client - db connection - ## | dbname - database name - ## | prefix - collection prefix, default is fs if NULL or empty - ## | gfs - the GridFS object to initialize - ## | returns - OK or ERROR. - -proc destroy*(gfs: var TGridfs){.stdcall, importc: "gridfs_destroy", - dynlib: mongodll.} - ## Destroys a GridFS object. Call this when finished with the object. - -proc writerInit*(gfile: var TGridFile, gfs: var TGridfs, remote_name: cstring, - content_type: cstring){.stdcall, - importc: "gridfile_writer_init", dynlib: mongodll.} - ## Initializes a gridfile for writing incrementally with ``writeBuffer``. - ## Once initialized, you can write any number of buffers with ``writeBuffer``. - ## When done, you must call ``writerDone`` to save the file metadata. - -proc writeBuffer*(gfile: var TGridFile, data: cstring, length: TOffset){. - stdcall, importc: "gridfile_write_buffer", dynlib: mongodll.} - ## Write to a GridFS file incrementally. You can call this function any number - ## of times with a new buffer each time. This allows you to effectively - ## stream to a GridFS file. When finished, be sure to call ``writerDone``. - -proc writerDone*(gfile: var TGridFile): cint{.stdcall, - importc: "gridfile_writer_done", dynlib: mongodll.} - ## Signal that writing of this gridfile is complete by - ## writing any buffered chunks along with the entry in the - ## files collection. Returns OK or ERROR. - -proc storeBuffer*(gfs: var TGridfs, data: cstring, length: TOffset, - remotename: cstring, contenttype: cstring): cint{.stdcall, - importc: "gridfs_store_buffer", dynlib: mongodll.} - ## Store a buffer as a GridFS file. - ## - ## | gfs - the working GridFS - ## | data - pointer to buffer to store in GridFS - ## | length - length of the buffer - ## | remotename - filename for use in the database - ## | contenttype - optional MIME type for this object - ## | returns - MONGO_OK or MONGO_ERROR. - -proc storeFile*(gfs: var TGridfs, filename: cstring, remotename: cstring, - contenttype: cstring): cint{.stdcall, - importc: "gridfs_store_file", dynlib: mongodll.} - ## Open the file referenced by filename and store it as a GridFS file. - ## - ## | gfs - the working GridFS - ## | filename - local filename relative to the process - ## | remotename - optional filename for use in the database - ## | contenttype - optional MIME type for this object - ## | returns - OK or ERROR. - -proc removeFilename*(gfs: var TGridfs, filename: cstring){.stdcall, - importc: "gridfs_remove_filename", dynlib: mongodll.} - ## Removes the files referenced by filename from the db. - -proc findQuery*(gfs: var TGridfs, query: var TBson, gfile: var TGridFile): cint{. - stdcall, importc: "gridfs_find_query", dynlib: mongodll.} - ## Find the first file matching the provided query within the - ## GridFS files collection, and return the file as a GridFile. - ## Returns OK if successful, ERROR otherwise. - -proc findFilename*(gfs: var TGridfs, filename: cstring, gfile: var TGridFile): cint{. - stdcall, importc: "gridfs_find_filename", dynlib: mongodll.} - ## Find the first file referenced by filename within the GridFS - ## and return it as a GridFile. Returns OK or ERROR. - -proc init*(gfs: var TGridfs, meta: var TBson, gfile: var TGridFile): cint{. - stdcall, importc: "gridfile_init", dynlib: mongodll.} - ## Initializes a GridFile containing the GridFS and file bson. - -proc destroy*(gfile: var TGridFile){.stdcall, importc: "gridfile_destroy", - dynlib: mongodll.} - ## Destroys the GridFile. - -proc exists*(gfile: var TGridFile): TBsonBool{.stdcall, - importc: "gridfile_exists", dynlib: mongodll.} - ## Returns whether or not the GridFile exists. - -proc getFilename*(gfile: var TGridFile): cstring{.stdcall, - importc: "gridfile_get_filename", dynlib: mongodll.} - ## Returns the filename of GridFile. - -proc getChunksize*(gfile: var TGridFile): cint{.stdcall, - importc: "gridfile_get_chunksize", dynlib: mongodll.} - ## Returns the size of the chunks of the GridFile. - -proc getContentlength*(gfile: var TGridFile): TOffset{.stdcall, - importc: "gridfile_get_contentlength", dynlib: mongodll.} - ## Returns the length of GridFile's data. - -proc getContenttype*(gfile: var TGridFile): cstring{.stdcall, - importc: "gridfile_get_contenttype", dynlib: mongodll.} - ## Returns the MIME type of the GridFile (nil if no type specified). - -proc getUploaddate*(gfile: var TGridFile): Tdate{.stdcall, - importc: "gridfile_get_uploaddate", dynlib: mongodll.} - ## Returns the upload date of GridFile. - -proc getMd5*(gfile: var TGridFile): cstring {.stdcall, - importc: "gridfile_get_md5", dynlib: mongodll.} - ## Returns the MD5 of GridFile. - -proc getField*(gfile: var TGridFile, name: cstring): cstring{.stdcall, - importc: "gridfile_get_field", dynlib: mongodll.} - ## Returns the field in GridFile specified by name. Returns the data of the - ## field specified (nil if none exists). - -proc getBoolean*(gfile: var TGridFile, name: cstring): TBsonBool{.stdcall, - importc: "gridfile_get_boolean", dynlib: mongodll.} - ## Returns a boolean field in GridFile specified by name. - -proc getMetadata*(gfile: var TGridFile, outp: var TBson){.stdcall, - importc: "gridfile_get_metadata", dynlib: mongodll.} - ## Returns the metadata of GridFile (an empty bson is returned if none - ## exists). - -proc getNumchunks*(gfile: var TGridFile): cint{.stdcall, - importc: "gridfile_get_numchunks", dynlib: mongodll.} - ## Returns the number of chunks in the GridFile. - -proc getChunk*(gfile: var TGridFile, n: cint, outp: var TBson){.stdcall, - importc: "gridfile_get_chunk", dynlib: mongodll.} - ## Returns chunk `n` of GridFile. - -proc getChunks*(gfile: var TGridFile, start: cint, size: cint): ptr TCursor{. - stdcall, importc: "gridfile_get_chunks", dynlib: mongodll.} - ## Returns a mongo_cursor of `size` chunks starting with chunk `start`. - ## The cursor must be destroyed after use. - -proc writeFile*(gfile: ptr TGridFile, stream: TFile): TOffset{.stdcall, - importc: "gridfile_write_file", dynlib: mongodll.} - ## Writes the GridFile to a stream. - -proc read*(gfile: var TGridFile, size: TOffset, buf: cstring): TOffset{.stdcall, - importc: "gridfile_read", dynlib: mongodll.} - ## Reads length bytes from the GridFile to a buffer - ## and updates the position in the file. - ## (assumes the buffer is large enough) - ## (if size is greater than EOF gridfile_read reads until EOF). - ## Returns the number of bytes read. - -proc seek*(gfile: var TGridFile, offset: TOffset): TOffset{.stdcall, - importc: "gridfile_seek", dynlib: mongodll.} - ## Updates the position in the file - ## (If the offset goes beyond the contentlength, - ## the position is updated to the end of the file.) - ## Returns the offset location From e8c50640d7a4d386197b8508c82b26f5563060cb Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 24 Mar 2014 12:17:22 +0200 Subject: [PATCH 222/249] failing test case preventing the use of --gc:v2: tsymchoicefield --- lib/core/macros.nim | 6 +++--- tests/template/tsymchoicefield.nim | 12 ++++++++++++ tests/vm/tstaticprintseq.nim | 11 +++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 tests/template/tsymchoicefield.nim diff --git a/lib/core/macros.nim b/lib/core/macros.nim index db2bbc5a4..8ccad8fe3 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -48,17 +48,17 @@ type nnkYieldStmt, nnkTryStmt, nnkFinally, nnkRaiseStmt, nnkReturnStmt, nnkBreakStmt, nnkContinueStmt, nnkBlockStmt, nnkStaticStmt, nnkDiscardStmt, nnkStmtList, - nnkImportStmt, nnkImportExceptStmt, nnkExportStmt, nnkExportExceptStmt, nnkFromStmt, nnkIncludeStmt, - nnkBindStmt, nnkMixinStmt, nnkUsingStmt, nnkCommentStmt, nnkStmtListExpr, nnkBlockExpr, - nnkStmtListType, nnkBlockType, nnkTypeOfExpr, nnkObjectTy, + nnkStmtListType, nnkBlockType, + nnkWith, nnkWithout, + nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkRefTy, nnkPtrTy, nnkVarTy, diff --git a/tests/template/tsymchoicefield.nim b/tests/template/tsymchoicefield.nim new file mode 100644 index 000000000..ab05500bf --- /dev/null +++ b/tests/template/tsymchoicefield.nim @@ -0,0 +1,12 @@ +type Foo = object + len: int + +var f = Foo(len: 40) + +template getLen(f: Foo): expr = f.len + +echo f.getLen +# This fails, because `len` gets the nkOpenSymChoice +# treatment inside the template early pass and then +# it can't be recognized as a field anymore + diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim index 1fe65cca5..3dfd0048d 100644 --- a/tests/vm/tstaticprintseq.nim +++ b/tests/vm/tstaticprintseq.nim @@ -13,6 +13,8 @@ discard """ 3 aa bb +11 +22 aa bb 24''' @@ -45,9 +47,11 @@ type const data: TData = (@["aa", "bb"], @[11, 22]) static: - var m = data - for x in m.letters: - echo x + var m1 = data + for x in m1.letters: echo x + + var m2: TData = data + for x in m2.numbers: echo x macro ff(d: static[TData]): stmt = for x in d.letters: @@ -55,7 +59,6 @@ macro ff(d: static[TData]): stmt = ff(data) - # bug #1010 proc `*==`(x: var int, y: int) {.inline, noSideEffect.} = From 907e1b80b2a3a64cbd60f98c221cd662988d74aa Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 24 Mar 2014 17:29:31 +0100 Subject: [PATCH 223/249] VM: attempt to get integer ops right --- compiler/vm.nim | 14 +++++------ compiler/vmgen.nim | 62 ++++++++++++++++++++++++++++++++++------------ todo.txt | 4 +-- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index f8b32bac1..8fea6b293 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -948,17 +948,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcReset: internalError(c.debug[pc], "too implement") of opcNarrowS: - decodeBC(rkInt) - let min = -(1 shl (rc-1)) - let max = (1 shl (rc-1))-1 - if regs[rb].intVal >= min and regs[rb].intVal <= max: - regs[ra].intVal = regs[rb].intVal - else: + decodeB(rkInt) + let min = -(1 shl (rb-1)) + let max = (1 shl (rb-1))-1 + if regs[ra].intVal < min or regs[ra].intVal > max: stackTrace(c, tos, pc, errGenerated, msgKindToString(errUnhandledExceptionX) % "value out of range") of opcNarrowU: - decodeBC(rkInt) - regs[ra].intVal = regs[rb].intVal and ((1'i64 shl rc)-1) + decodeB(rkInt) + regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1) of opcIsNil: decodeB(rkInt) regs[ra].intVal = ord(regs[rb].node.kind == nkNilLit) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a3af54145..59d3d2495 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -543,6 +543,30 @@ proc genBinaryABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp) c.freeTemp(tmp2) +proc genNarrow(c: PCtx; n: PNode; dest: TDest) = + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + # uint is uint64 in the VM, we we only need to mask the result for + # other unsigned types: + if t.kind in {tyUInt8..tyUInt32}: + c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + elif t.kind in {tyInt8..tyInt32}: + c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) + +proc genNarrowU(c: PCtx; n: PNode; dest: TDest) = + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + # uint is uint64 in the VM, we we only need to mask the result for + # other unsigned types: + if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}: + c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + +proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = + genBinaryABC(c, n, dest, opc) + genNarrow(c, n, dest) + +proc genBinaryABCnarrowU(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = + genBinaryABC(c, n, dest, opc) + genNarrowU(c, n, dest) + proc genSetType(c: PCtx; n: PNode; dest: TRegister) = let t = skipTypes(n.typ, abstractInst-{tyTypeDesc}) if t.kind == tySet: @@ -604,6 +628,7 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.freeTemp(tmp) else: genBinaryABC(c, n, dest, opc) + c.genNarrow(n, dest) proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = let tmp = c.genx(arg) @@ -644,6 +669,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = let tmp = c.genx(n.sons[2]) c.gABC(n, opc, d, d, tmp) c.freeTemp(tmp) + c.genNarrow(n.sons[1], d) c.genAsgnPatch(n.sons[1], d) c.freeTemp(d) of mOrd, mChr, mArrToSeq: c.gen(n.sons[1], dest) @@ -678,23 +704,23 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(d) c.freeTemp(tmp) of mCard: genCard(c, n, dest) - of mMulI, mMulI64: genBinaryABC(c, n, dest, opcMulInt) - of mDivI, mDivI64: genBinaryABC(c, n, dest, opcDivInt) - of mModI, mModI64: genBinaryABC(c, n, dest, opcModInt) + of mMulI, mMulI64: genBinaryABCnarrow(c, n, dest, opcMulInt) + of mDivI, mDivI64: genBinaryABCnarrow(c, n, dest, opcDivInt) + of mModI, mModI64: genBinaryABCnarrow(c, n, dest, opcModInt) of mAddF64: genBinaryABC(c, n, dest, opcAddFloat) of mSubF64: genBinaryABC(c, n, dest, opcSubFloat) of mMulF64: genBinaryABC(c, n, dest, opcMulFloat) of mDivF64: genBinaryABC(c, n, dest, opcDivFloat) - of mShrI, mShrI64: genBinaryABC(c, n, dest, opcShrInt) - of mShlI, mShlI64: genBinaryABC(c, n, dest, opcShlInt) - of mBitandI, mBitandI64: genBinaryABC(c, n, dest, opcBitandInt) - of mBitorI, mBitorI64: genBinaryABC(c, n, dest, opcBitorInt) - of mBitxorI, mBitxorI64: genBinaryABC(c, n, dest, opcBitxorInt) - of mAddU: genBinaryABC(c, n, dest, opcAddu) - of mSubU: genBinaryABC(c, n, dest, opcSubu) - of mMulU: genBinaryABC(c, n, dest, opcMulu) - of mDivU: genBinaryABC(c, n, dest, opcDivu) - of mModU: genBinaryABC(c, n, dest, opcModu) + of mShrI, mShrI64: genBinaryABCnarrowU(c, n, dest, opcShrInt) + of mShlI, mShlI64: genBinaryABCnarrowU(c, n, dest, opcShlInt) + of mBitandI, mBitandI64: genBinaryABCnarrowU(c, n, dest, opcBitandInt) + of mBitorI, mBitorI64: genBinaryABCnarrowU(c, n, dest, opcBitorInt) + of mBitxorI, mBitxorI64: genBinaryABCnarrowU(c, n, dest, opcBitxorInt) + of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu) + of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu) + of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu) + of mDivU: genBinaryABCnarrowU(c, n, dest, opcDivu) + of mModU: genBinaryABCnarrowU(c, n, dest, opcModu) of mEqI, mEqI64, mEqB, mEqEnum, mEqCh: genBinaryABC(c, n, dest, opcEqInt) of mLeI, mLeI64, mLeEnum, mLeCh, mLeB: @@ -708,12 +734,16 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu) of mEqProc, mEqRef, mEqUntracedRef, mEqCString: genBinaryABC(c, n, dest, opcEqRef) - of mXor: genBinaryABC(c, n, dest, opcXor) + of mXor: genBinaryABCnarrowU(c, n, dest, opcXor) of mNot: genUnaryABC(c, n, dest, opcNot) - of mUnaryMinusI, mUnaryMinusI64: genUnaryABC(c, n, dest, opcUnaryMinusInt) + of mUnaryMinusI, mUnaryMinusI64: + genUnaryABC(c, n, dest, opcUnaryMinusInt) + genNarrow(c, n, dest) of mUnaryMinusF64: genUnaryABC(c, n, dest, opcUnaryMinusFloat) of mUnaryPlusI, mUnaryPlusI64, mUnaryPlusF64: gen(c, n.sons[1], dest) - of mBitnotI, mBitnotI64: genUnaryABC(c, n, dest, opcBitnotInt) + of mBitnotI, mBitnotI64: + genUnaryABC(c, n, dest, opcBitnotInt) + genNarrowU(c, n, dest) of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64, mToU8, mToU16, mToU32, mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, diff --git a/todo.txt b/todo.txt index 921862d43..129f15ebc 100644 --- a/todo.txt +++ b/todo.txt @@ -2,9 +2,6 @@ version 0.9.4 ============= - fix gensym capture bug -- vm - - at least try to get the basic type zoo ops right - - optimize opcAsgnStr - fix GC issues @@ -39,6 +36,7 @@ version 0.9.x - ``=`` should be overloadable; requires specialization for ``=``; general lift mechanism in the compiler is already implemented for 'fields' - built-in 'getImpl' +- VM: optimize opcAsgnStr - change comment handling in the AST; that's lots of work as c2nim and pas2nim make use of the fact every node can have a comment! From 87557920778e861b3ec897aa7cfd632676ec80ee Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 25 Mar 2014 01:25:17 +0100 Subject: [PATCH 224/249] mongodb wrapper properly removed from core --- lib/impure/db_mongo.nim | 227 ---------------------------------------- web/nimrod.ini | 4 +- 2 files changed, 2 insertions(+), 229 deletions(-) delete mode 100644 lib/impure/db_mongo.nim diff --git a/lib/impure/db_mongo.nim b/lib/impure/db_mongo.nim deleted file mode 100644 index dc8a808f2..000000000 --- a/lib/impure/db_mongo.nim +++ /dev/null @@ -1,227 +0,0 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements a higher level wrapper for `mongodb`:idx:. Example: -## -## .. code-block:: nimrod -## -## import mongo, db_mongo, oids, json -## -## var conn = db_mongo.open() -## -## # construct JSON data: -## var data = %{"a": %13, "b": %"my string value", -## "inner": %{"i": %71} } -## -## var id = insertID(conn, "test.test", data) -## -## for v in find(conn, "test.test", "this.a == 13"): -## print v -## -## delete(conn, "test.test", id) -## close(conn) - -import mongo, oids, json - -type - EDb* = object of EIO ## exception that is raised if a database error occurs - TDbConn* = TMongo ## a database connection; alias for ``TMongo`` - - FDb* = object of FIO ## effect that denotes a database operation - FReadDb* = object of FDB ## effect that denotes a read operation - FWriteDb* = object of FDB ## effect that denotes a write operation - -proc dbError*(db: TDbConn, msg: string) {.noreturn.} = - ## raises an EDb exception with message `msg`. - var e: ref EDb - new(e) - if db.errstr[0] != '\0': - e.msg = $db.errstr - else: - e.msg = $db.err & " " & msg - raise e - -proc close*(db: var TDbConn) {.tags: [FDB].} = - ## closes the database connection. - disconnect(db) - destroy(db) - -proc open*(host: string = defaultHost, port: int = defaultPort): TDbConn {. - tags: [FDB].} = - ## opens a database connection. Raises `EDb` if the connection could not - ## be established. - init(result) - - let x = client(result, host, port.cint) - if x != 0'i32: - dbError(result, "cannot open: " & host) - -proc jsonToBSon(b: var TBSon, key: string, j: PJsonNode) = - case j.kind - of JString: - add(b, key, j.str) - of JInt: - add(b, key, j.num) - of JFloat: - add(b, key, j.fnum) - of JBool: - addBool(b, key, ord(j.bval)) - of JNull: - addNull(b, key) - of JObject: - addStartObject(b, key) - for k, v in items(j.fields): - jsonToBSon(b, k, v) - addFinishObject(b) - of JArray: - addStartArray(b, key) - for i, e in pairs(j.elems): - jsonToBSon(b, $i, e) - addFinishArray(b) - -proc jsonToBSon*(j: PJsonNode, oid: TOid): TBSon = - ## converts a JSON value into the BSON format. The result must be - ## ``destroyed`` explicitely! - init(result) - assert j.kind == JObject - add(result, "_id", oid) - for key, val in items(j.fields): - jsonToBSon(result, key, val) - finish(result) - -proc `[]`*(obj: var TBSon, fieldname: cstring): TBSon = - ## retrieves the value belonging to `fieldname`. Raises `EInvalidKey` if - ## the attribute does not exist. - var it = initIter(obj) - let res = find(it, result, fieldname) - if res == bkEOO: - raise newException(EInvalidIndex, "key not in object") - -proc getId*(obj: var TBSon): TOid = - ## retrieves the ``_id`` attribute of `obj`. - var it = initIter(obj) - var b: TBSon - let res = find(it, b, "_id") - if res == bkOID: - result = oidVal(it)[] - else: - raise newException(EInvalidIndex, "_id not in object") - -proc insertId*(db: var TDbConn, namespace: string, data: PJsonNode): TOid {. - tags: [FWriteDb].} = - ## converts `data` to BSON format and inserts it in `namespace`. Returns - ## the generated OID for the ``_id`` field. - result = genOid() - var x = jsonToBSon(data, result) - insert(db, namespace, x, nil) - destroy(x) - -proc insert*(db: var TDbConn, namespace: string, data: PJsonNode) {. - tags: [FWriteDb].} = - ## converts `data` to BSON format and inserts it in `namespace`. - discard InsertID(db, namespace, data) - -proc update*(db: var TDbConn, namespace: string, obj: var TBSon) {. - tags: [FReadDB, FWriteDb].} = - ## updates `obj` in `namespace`. - var cond: TBson - init(cond) - cond.add("_id", getId(obj)) - finish(cond) - update(db, namespace, cond, obj, ord(UPDATE_UPSERT)) - destroy(cond) - -proc update*(db: var TDbConn, namespace: string, oid: TOid, obj: PJsonNode) {. - tags: [FReadDB, FWriteDb].} = - ## updates the data with `oid` to have the new data `obj`. - var a = jsonToBSon(obj, oid) - Update(db, namespace, a) - destroy(a) - -proc delete*(db: var TDbConn, namespace: string, oid: TOid) {. - tags: [FWriteDb].} = - ## Deletes the object belonging to `oid`. - var cond: TBson - init(cond) - cond.add("_id", oid) - finish(cond) - discard remove(db, namespace, cond) - destroy(cond) - -proc delete*(db: var TDbConn, namespace: string, obj: var TBSon) {. - tags: [FWriteDb].} = - ## Deletes the object `obj`. - delete(db, namespace, getId(obj)) - -iterator find*(db: var TDbConn, namespace: string): var TBSon {. - tags: [FReadDB].} = - ## iterates over any object in `namespace`. - var cursor: TCursor - init(cursor, db, namespace) - while next(cursor) == mongo.OK: - yield bson(cursor)[] - destroy(cursor) - -iterator find*(db: var TDbConn, namespace: string, - query, fields: var TBSon): var TBSon {.tags: [FReadDB].} = - ## yields the `fields` of any document that suffices `query`. - var cursor = find(db, namespace, query, fields, 0'i32, 0'i32, 0'i32) - if cursor != nil: - while next(cursor[]) == mongo.OK: - yield bson(cursor[])[] - destroy(cursor[]) - -proc setupFieldnames(fields: varargs[string]): TBSon = - init(result) - for x in fields: add(result, x, 1'i32) - finish(result) - -iterator find*(db: var TDbConn, namespace: string, - query: var TBSon, fields: varargs[string]): var TBSon {. - tags: [FReadDB].} = - ## yields the `fields` of any document that suffices `query`. If `fields` - ## is ``[]`` the whole document is yielded. - var f = setupFieldnames(fields) - var cursor = find(db, namespace, query, f, 0'i32, 0'i32, 0'i32) - if cursor != nil: - while next(cursor[]) == mongo.OK: - yield bson(cursor[])[] - destroy(cursor[]) - destroy(f) - -proc setupQuery(query: string): TBSon = - init(result) - add(result, "$where", query) - finish(result) - -iterator find*(db: var TDbConn, namespace: string, - query: string, fields: varargs[string]): var TBSon {. - tags: [FReadDB].} = - ## yields the `fields` of any document that suffices `query`. If `fields` - ## is ``[]`` the whole document is yielded. - var f = setupFieldnames(fields) - var q = setupQuery(query) - var cursor = find(db, namespace, q, f, 0'i32, 0'i32, 0'i32) - if cursor != nil: - while next(cursor[]) == mongo.OK: - yield bson(cursor[])[] - destroy(cursor[]) - destroy(q) - destroy(f) - -when false: - # this doesn't work this way; would require low level hacking - iterator fieldPairs*(obj: var TBSon): tuple[key: cstring, value: TBSon] = - ## iterates over `obj` and yields all (key, value)-Pairs. - var it = initIter(obj) - var v: TBSon - while next(it) != bkEOO: - let key = key(it) - discard init(v, value(it)) - yield (key, v) diff --git a/web/nimrod.ini b/web/nimrod.ini index 71e36dcdc..0ebc4b089 100644 --- a/web/nimrod.ini +++ b/web/nimrod.ini @@ -49,7 +49,7 @@ srcdoc2: "pure/parseopt;pure/parseopt2;pure/hashes;pure/strtabs;pure/lexbase" srcdoc2: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql" srcdoc2: "pure/streams;pure/terminal;pure/cgi;impure/web;pure/unicode" srcdoc2: "impure/zipfiles;pure/htmlgen;pure/parseutils;pure/browsers" -srcdoc2: "impure/db_postgres;impure/db_mysql;impure/db_sqlite;impure/db_mongo" +srcdoc2: "impure/db_postgres;impure/db_mysql;impure/db_sqlite" srcdoc2: "pure/httpserver;pure/httpclient;pure/smtp;impure/ssl;pure/fsmonitor" srcdoc2: "pure/ropes;pure/unidecode/unidecode;pure/xmldom;pure/xmldomparser" srcdoc2: "pure/xmlparser;pure/htmlparser;pure/xmltree;pure/colors;pure/mimetypes" @@ -72,7 +72,7 @@ webdoc: "wrappers/libuv;wrappers/joyent_http_parser" webdoc: "posix/posix;wrappers/odbcsql;impure/dialogs" webdoc: "wrappers/zip/zlib;wrappers/zip/libzip" -webdoc: "wrappers/libsvm.nim;wrappers/mongo.nim" +webdoc: "wrappers/libsvm.nim" webdoc: "windows" webdoc: "wrappers/readline/readline;wrappers/readline/history" webdoc: "wrappers/readline/rltypedefs" From 9e66d988e7fc1f33b11a0b93be484361390bd095 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 25 Mar 2014 01:26:32 +0100 Subject: [PATCH 225/249] mongodb wrapper properly removed from core --- doc/lib.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/lib.txt b/doc/lib.txt index 3214cdae2..ca2539631 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -386,9 +386,6 @@ Database support A higher level SQLite database wrapper. The same interface is implemented for other databases too. -* `db_mongo `_ - A higher level **mongodb** wrapper. - Other ----- @@ -503,8 +500,6 @@ Database support Contains a wrapper for the mySQL API. * `sqlite3 `_ Contains a wrapper for SQLite 3 API. -* `mongodb `_ - Lower level wrapper for the **mongodb** client C library. * `odbcsql `_ interface to the ODBC driver. * `sphinx `_ From c58ec376b6db3891924188f35d5c83578658048a Mon Sep 17 00:00:00 2001 From: Jimmie Houchin Date: Tue, 25 Mar 2014 17:48:27 -0500 Subject: [PATCH 226/249] fixed parens around getSysType arguments --- compiler/semdata.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 5ec66b51c..b46d83a92 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -258,7 +258,7 @@ proc nMinusOne*(n: PNode): PNode = # Remember to fix the procs below this one when you make changes! proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = - let intType = getSysType tyInt + let intType = getSysType(tyInt) result = newTypeS(tyRange, c) result.sons = @[intType] result.n = newNode(nkRange, n.info, @[ From d15788d00a4007a80de4427f84129abe47fa4f11 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 26 Mar 2014 01:00:34 +0100 Subject: [PATCH 227/249] added #903 to the test suite --- compiler/vm.nim | 6 +++++- compiler/vmdef.nim | 1 + compiler/vmgen.nim | 2 +- tests/vm/tcompiletimetable.nim | 27 ++++++++++++++++++++++++--- todo.txt | 2 +- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 8fea6b293..268289aca 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -448,7 +448,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let n = src.sons[rc] regs[ra].node = n else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, errNilAccess) of opcWrObj: # a.b = c decodeBC(rkNode) @@ -902,6 +902,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regBx - wordExcess - 1 ensureKind(rkNode) regs[ra].node = c.globals.sons[rb] + of opcLdGlobalAddr: + let rb = instr.regBx - wordExcess - 1 + ensureKind(rkNodeAddr) + regs[ra].nodeAddr = addr(c.globals.sons[rb]) of opcRepr: decodeB(rkNode) createStr regs[ra] diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 90ff8e29f..d0c38a2ad 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -126,6 +126,7 @@ type opcLdConst, # dest = constants[Bx] opcAsgnConst, # dest = copy(constants[Bx]) opcLdGlobal, # dest = globals[Bx] + opcLdGlobalAddr, # dest = addr(globals[Bx]) opcLdImmInt, # dest = immediate value opcNBindSym, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 59d3d2495..e627fee48 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1614,7 +1614,7 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "tupleUnpack": + #if s.name.s == "addStuff": # echo renderTree(body) # c.echoCode(result) c.prc = oldPrc diff --git a/tests/vm/tcompiletimetable.nim b/tests/vm/tcompiletimetable.nim index 3b40add07..f1d3ecd4e 100644 --- a/tests/vm/tcompiletimetable.nim +++ b/tests/vm/tcompiletimetable.nim @@ -12,13 +12,13 @@ import macros, tables var ZOOT{.compileTime.} = initTable[int, int](2) var iii {.compiletime.} = 1 -macro x:stmt= +macro zoo:stmt= zoot[iii] = iii*2 inc iii echo iii -x -x +zoo +zoo macro tupleUnpack: stmt = @@ -27,3 +27,24 @@ macro tupleUnpack: stmt = tupleUnpack +# bug #903 + +import strtabs + +var x {.compileTime.}: PStringTable + +macro addStuff(stuff, body: expr): stmt {.immediate.} = + result = newNimNode(nnkStmtList) + + if x.isNil: + x = newStringTable(modeStyleInsensitive) + x[$stuff] = "" + +macro dump(): stmt = + result = newNimNode(nnkStmtList) + for y in x.keys: echo "Got ", y + +addStuff("Hey"): echo "Hey" +addStuff("Hi"): echo "Hi" +dump() + diff --git a/todo.txt b/todo.txt index 129f15ebc..91fa0cd6a 100644 --- a/todo.txt +++ b/todo.txt @@ -154,7 +154,7 @@ Optimizations ============= - optimize 'if' with a constant condition --> necessary in frontend for better - dead code elimination + dead code elimination; also necessary to prevent ``if c > 0: 1 div c`` - escape analysis for string/seq seems to be easy to do too; even further write barrier specialization - inlining of first class functions From cdb4d83eadef07c3ba07b1a2d408548df1b3326b Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 26 Mar 2014 01:27:22 +0100 Subject: [PATCH 228/249] implemented 'borrow dot' feature for distinct types --- compiler/ast.nim | 4 +++- compiler/pragmas.nim | 19 +++++++++++++++---- compiler/semexprs.nim | 10 +++++----- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 30778c02d..dd6f47ed1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -424,7 +424,7 @@ type nfIsRef # this node is a 'ref' node; used for the VM TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 23) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 28) tfVarargs, # procedure has C styled varargs tfNoSideEffect, # procedure type does not allow side effects tfFinal, # is the object final? @@ -464,6 +464,8 @@ type # T and I here can bind to both typedesc and static types # before this is determined, we'll consider them to be a # wildcard type. + tfGuarded # guarded pointer + tfBorrowDot # distinct type borrows '.' TTypeFlags* = set[TTypeFlag] diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index efd06eef4..b5dead5b2 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -52,7 +52,8 @@ const typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, - wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked} + wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, + wBorrow} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportCpp, wImportObjC, wError} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -511,6 +512,13 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) = else: invalidPragma(n) +proc typeBorrow(sym: PSym, n: PNode) = + if n.kind == nkExprColonExpr: + let it = n.sons[1] + if it.kind != nkAccQuoted: + localError(n.info, "a type can only borrow `.` for now") + incl(sym.typ.flags, tfBorrowDot) + proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, validPragmas: TSpecialWords): bool = var it = n.sons[i] @@ -631,9 +639,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, noVal(it) if sym.typ == nil: invalidPragma(it) else: incl(sym.typ.flags, tfVarargs) - of wBorrow: - noVal(it) - incl(sym.flags, sfBorrow) + of wBorrow: + if sym.kind == skType: + typeBorrow(sym, it) + else: + noVal(it) + incl(sym.flags, sfBorrow) of wFinal: noVal(it) if sym.typ == nil: invalidPragma(it) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b3b757640..70c57b15e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1001,15 +1001,15 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": ty = n.sons[0].typ return nil - ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) var check: PNode = nil - if ty.kind == tyObject: - while true: + if ty.kind == tyObject: + while true: check = nil f = lookupInRecordAndBuildCheck(c, n, ty.n, i, check) - if f != nil: break - if ty.sons[0] == nil: break + if f != nil: break + if ty.sons[0] == nil: break ty = skipTypes(ty.sons[0], {tyGenericInst}) if f != nil: if fieldVisible(c, f): From 7874c0efcb2437c378c973a51de88f3ebc7e4d74 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 26 Mar 2014 08:42:27 +0100 Subject: [PATCH 229/249] borrow dots for distinct types documented --- doc/manual.txt | 22 ++++++++++++++++++++++ tests/distinct/tborrowdot.nim | 13 +++++++++++++ todo.txt | 1 - 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/distinct/tborrowdot.nim diff --git a/doc/manual.txt b/doc/manual.txt index c2acd3e6a..6526ba2cb 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1569,6 +1569,28 @@ currency. This can be solved with templates_. defineCurrency(TEuro, int) +The borrow pragma can also be used to annotate the distinct type to allow +certain builtin operations to be lifted: + +.. code-block:: nimrod + type + Foo = object + a, b: int + s: string + + Bar {.borrow: `.`.} = distinct Foo + + var bb: ref Bar + new bb + # field access now valid + bb.a = 90 + bb.s = "abc" + +Currently only the dot accessor can be borrowed in this way. + + + + Void type --------- diff --git a/tests/distinct/tborrowdot.nim b/tests/distinct/tborrowdot.nim new file mode 100644 index 000000000..820ee3b71 --- /dev/null +++ b/tests/distinct/tborrowdot.nim @@ -0,0 +1,13 @@ + +type + Foo = object + a, b: int + s: string + + Bar {.borrow: `.`.} = distinct Foo + +var bb: ref Bar +new bb +bb.a = 90 +bb.s = "abc" + diff --git a/todo.txt b/todo.txt index 91fa0cd6a..7a315545e 100644 --- a/todo.txt +++ b/todo.txt @@ -4,7 +4,6 @@ version 0.9.4 - fix gensym capture bug - fix GC issues - Bugs ==== From 4ed1ac0078ce6ef9ffebccdefb3023aa034a41ab Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 26 Mar 2014 10:31:16 +0000 Subject: [PATCH 230/249] Move asyncdispatch tests to asyncnet. --- lib/pure/asyncdispatch.nim | 77 ------------------------- lib/pure/asyncnet.nim | 108 ++++++++++++++++++++++++------------ lib/pure/net.nim | 30 +++++----- tests/async/tasyncawait.nim | 2 - 4 files changed, 89 insertions(+), 128 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 4a4ef8bdd..a3a4099e0 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -957,80 +957,3 @@ proc runForever*() = ## Begins a never ending global dispatcher poll loop. while true: poll() - -when isMainModule: - - var p = newDispatcher() - var sock = p.socket() - sock.setBlocking false - - - when false: - # Await tests - proc main(p: PDispatcher): PFuture[int] {.async.} = - discard await p.connect(sock, "irc.freenode.net", TPort(6667)) - while true: - echo("recvLine") - var line = await p.recvLine(sock) - echo("Line is: ", line.repr) - if line == "": - echo "Disconnected" - break - - proc peekTest(p: PDispatcher): PFuture[int] {.async.} = - discard await p.connect(sock, "localhost", TPort(6667)) - while true: - var line = await p.recv(sock, 1, MSG_PEEK) - var line2 = await p.recv(sock, 1) - echo(line.repr) - echo(line2.repr) - echo("---") - if line2 == "": break - sleep(500) - - var f = main(p) - - - else: - when false: - - var f = p.connect(sock, "irc.poop.nl", TPort(6667)) - f.callback = - proc (future: PFuture[int]) = - echo("Connected in future!") - echo(future.read) - for i in 0 .. 50: - var recvF = p.recv(sock, 10) - recvF.callback = - proc (future: PFuture[string]) = - echo("Read ", future.read.len, ": ", future.read.repr) - - else: - - sock.bindAddr(TPort(6667)) - sock.listen() - proc onAccept(future: PFuture[TSocketHandle]) = - let client = future.read - echo "Accepted ", client.cint - var t = p.send(client, "test\c\L") - t.callback = - proc (future: PFuture[int]) = - echo("Send: ", future.read) - client.close() - - var f = p.accept(sock) - f.callback = onAccept - - var f = p.accept(sock) - f.callback = onAccept - - while true: - p.poll() - - - - - - - - diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 451ef25ac..de5d84a53 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -14,32 +14,17 @@ when defined(ssl): import openssl type - TAsyncSocket = object ## socket type - fd: TAsyncFD - case isBuffered: bool # determines whether this socket is buffered. - of true: - buffer: array[0..BufferSize, char] - currPos: int # current index in buffer - bufLen: int # current length of buffer - of false: nil - when defined(ssl): - case isSsl: bool - of true: - sslHandle: PSSL - sslContext: PSSLContext - sslNoHandshake: bool # True if needs handshake. - sslHasPeekChar: bool - sslPeekChar: char - of false: nil - + # TODO: I would prefer to just do: + # PAsyncSocket* {.borrow: `.`.} = distinct PAsyncSocket. But that doesn't work. + TAsyncSocket {.borrow: `.`.} = distinct TSocketImpl PAsyncSocket* = ref TAsyncSocket # TODO: Save AF, domain etc info and reuse it in procs which need it like connect. proc newSocket(fd: TAsyncFD, isBuff: bool): PAsyncSocket = assert fd != osInvalidSocket.TAsyncFD - new(result) - result.fd = fd + new(result.PSocket) + result.fd = fd.TSocketHandle result.isBuffered = isBuff if isBuff: result.currPos = 0 @@ -55,7 +40,7 @@ proc connect*(socket: PAsyncSocket, address: string, port: TPort, ## ## Returns a ``PFuture`` which will complete when the connection succeeds ## or an error occurs. - result = connect(socket.fd, address, port, af) + result = connect(socket.fd.TAsyncFD, address, port, af) proc recv*(socket: PAsyncSocket, size: int, flags: int = 0): PFuture[string] = @@ -64,12 +49,12 @@ proc recv*(socket: PAsyncSocket, size: int, ## recv operation then the future may complete with only a part of the ## requested data read. If socket is disconnected and no data is available ## to be read then the future will complete with a value of ``""``. - result = recv(socket.fd, size, flags) + result = recv(socket.fd.TAsyncFD, size, flags) proc send*(socket: PAsyncSocket, data: string): PFuture[void] = ## Sends ``data`` to ``socket``. The returned future will complete once all ## data has been sent. - result = send(socket.fd, data) + result = send(socket.fd.TAsyncFD, data) proc acceptAddr*(socket: PAsyncSocket): PFuture[tuple[address: string, client: PAsyncSocket]] = @@ -77,7 +62,7 @@ proc acceptAddr*(socket: PAsyncSocket): ## corresponding to that connection and the remote address of the client. ## The future will complete when the connection is successfully accepted. var retFuture = newFuture[tuple[address: string, client: PAsyncSocket]]() - var fut = acceptAddr(socket.fd) + var fut = acceptAddr(socket.fd.TAsyncFD) fut.callback = proc (future: PFuture[tuple[address: string, client: TAsyncFD]]) = assert future.finished @@ -139,17 +124,72 @@ proc recvLine*(socket: PAsyncSocket): PFuture[string] {.async.} = return add(result.string, c) +proc bindAddr*(socket: PAsyncSocket, port = TPort(0), address = "") = + ## Binds ``address``:``port`` to the socket. + ## + ## If ``address`` is "" then ADDR_ANY will be bound. + socket.PSocket.bindAddr(port, address) + +proc listen*(socket: PAsyncSocket, backlog = SOMAXCONN) = + ## Marks ``socket`` as accepting connections. + ## ``Backlog`` specifies the maximum length of the + ## queue of pending connections. + ## + ## Raises an EOS error upon failure. + socket.PSocket.listen(backlog) + +proc close*(socket: PAsyncSocket) = + ## Closes the socket. + socket.fd.TAsyncFD.close() + # TODO SSL + when isMainModule: - proc main() {.async.} = + type + TestCases = enum + HighClient, LowClient, LowServer + + const test = LowServer + + when test == HighClient: + proc main() {.async.} = + var sock = newAsyncSocket() + await sock.connect("irc.freenode.net", TPort(6667)) + while true: + let line = await sock.recvLine() + if line == "": + echo("Disconnected") + break + else: + echo("Got line: ", line) + main() + elif test == LowClient: var sock = newAsyncSocket() - await sock.connect("irc.freenode.net", TPort(6667)) - while true: - let line = await sock.recvLine() - if line == "": - echo("Disconnected") - break - else: - echo("Got line: ", line) - main() + var f = connect(sock, "irc.freenode.net", TPort(6667)) + f.callback = + proc (future: PFuture[void]) = + echo("Connected in future!") + for i in 0 .. 50: + var recvF = recv(sock, 10) + recvF.callback = + proc (future: PFuture[string]) = + echo("Read ", future.read.len, ": ", future.read.repr) + elif test == LowServer: + var sock = newAsyncSocket() + sock.bindAddr(TPort(6667)) + sock.listen() + proc onAccept(future: PFuture[PAsyncSocket]) = + let client = future.read + echo "Accepted ", client.fd.cint + var t = send(client, "test\c\L") + t.callback = + proc (future: PFuture[void]) = + echo("Send") + client.close() + + var f = accept(sock) + f.callback = onAccept + + var f = accept(sock) + f.callback = onAccept runForever() diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 2461ece1b..43642c7b9 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -318,22 +318,22 @@ const BufferSize*: int = 4000 ## size of a buffered socket's buffer type - TSocketImpl = object ## socket type - fd: TSocketHandle - case isBuffered: bool # determines whether this socket is buffered. + TSocketImpl* = object ## socket type + fd*: TSocketHandle + case isBuffered*: bool # determines whether this socket is buffered. of true: - buffer: array[0..BufferSize, char] - currPos: int # current index in buffer - bufLen: int # current length of buffer + buffer*: array[0..BufferSize, char] + currPos*: int # current index in buffer + bufLen*: int # current length of buffer of false: nil when defined(ssl): - case isSsl: bool + case isSsl*: bool of true: - sslHandle: PSSL - sslContext: PSSLContext - sslNoHandshake: bool # True if needs handshake. - sslHasPeekChar: bool - sslPeekChar: char + sslHandle*: PSSL + sslContext*: PSSLContext + sslNoHandshake*: bool # True if needs handshake. + sslHasPeekChar*: bool + sslPeekChar*: char of false: nil PSocket* = ref TSocketImpl @@ -519,9 +519,9 @@ proc listen*(socket: PSocket, backlog = SOMAXCONN) {.tags: [FReadIO].} = proc bindAddr*(socket: PSocket, port = TPort(0), address = "") {. tags: [FReadIO].} = - ## Binds an address/port number to a socket. - ## Use address string in dotted decimal form like "a.b.c.d" - ## or leave "" for any address. + ## Binds ``address``:``port`` to the socket. + ## + ## If ``address`` is "" then ADDR_ANY will be bound. if address == "": var name: TSockaddr_in diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index 7e6270247..bd722842f 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -21,7 +21,6 @@ proc launchSwarm(port: TPort) {.async.} = for i in 0 .. Date: Wed, 26 Mar 2014 16:22:34 +0100 Subject: [PATCH 231/249] fixes #992 --- compiler/parser.nim | 2 ++ tests/exprs/tstmtexprs.nim | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/compiler/parser.nim b/compiler/parser.nim index d59e013d7..07f5c9de9 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -525,6 +525,8 @@ proc parsePar(p: var TParser): PNode = asgn.sons[0] = a asgn.sons[1] = b result.add(asgn) + if p.tok.tokType == tkSemiColon: + semiStmtList(p, result) elif p.tok.tokType == tkSemiColon: # stmt context: result.add(a) diff --git a/tests/exprs/tstmtexprs.nim b/tests/exprs/tstmtexprs.nim index 816e58cb1..ed0066287 100644 --- a/tests/exprs/tstmtexprs.nim +++ b/tests/exprs/tstmtexprs.nim @@ -86,3 +86,9 @@ proc parseResponse(): PJsonNode = if (var n=result["key2"]; n != nil): excMsg &= n.str raise newException(ESynch, excMsg) + + + +#bug #992 +var se = @[1,2] +let b = (se[1] = 1; 1) From 783087fd57e265c06e5081073391058c0e28864f Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 26 Mar 2014 16:43:32 +0000 Subject: [PATCH 232/249] Implemented async for httpclient. --- lib/pure/asyncdispatch.nim | 8 +- lib/pure/asyncnet.nim | 2 +- lib/pure/httpclient.nim | 239 +++++++++++++++++++++++++++++++++---- lib/pure/net.nim | 2 +- 4 files changed, 221 insertions(+), 30 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index a3a4099e0..72d2fc4e4 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -766,7 +766,7 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0]) result.add newNimNode(nnkYieldStmt).add(newNilLit()) of nnkCommand: - if node[0].ident == !"await": + if node[0].kind == nnkIdent and node[0].ident == !"await": case node[1].kind of nnkIdent: # await x @@ -782,7 +782,7 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = node[1][0].ident == !"await": # foo await x var newCommand = node - createVar("future" & $node[0].ident, node[1][0], newCommand[1]) + createVar("future" & $node[0].toStrLit, node[1][1], newCommand[1]) result.add newCommand of nnkVarSection, nnkLetSection: @@ -801,7 +801,7 @@ proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = if node[1][0].ident == !"await": # x = await y var newAsgn = node - createVar("future" & $node[0].ident, node[1][1], newAsgn[1]) + createVar("future" & $node[0].toStrLit, node[1][1], newAsgn[1]) result.add newAsgn else: discard of nnkDiscardStmt: @@ -857,7 +857,7 @@ macro async*(prc: stmt): stmt {.immediate.} = newNimNode(nnkBracketExpr).add( newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`. newIdentNode(subtypeName))))) # Get type from return type of this proc - echo(treeRepr(outerProcBody)) + # -> iterator nameIter(): PFutureBase {.closure.} = # -> var result: T # -> diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index de5d84a53..24651b08c 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -15,7 +15,7 @@ when defined(ssl): type # TODO: I would prefer to just do: - # PAsyncSocket* {.borrow: `.`.} = distinct PAsyncSocket. But that doesn't work. + # PAsyncSocket* {.borrow: `.`.} = distinct PSocket. But that doesn't work. TAsyncSocket {.borrow: `.`.} = distinct TSocketImpl PAsyncSocket* = ref TAsyncSocket diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index bb9835fe7..62d9bea7c 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -76,6 +76,8 @@ ## currently only basic authentication is supported. import sockets, strutils, parseurl, parseutils, strtabs, base64 +import asyncnet, asyncdispatch +import rawsockets type TResponse* = tuple[ @@ -286,16 +288,16 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "", add(headers, "\c\L") var s = socket() - var port = TPort(80) + var port = sockets.TPort(80) if r.scheme == "https": when defined(ssl): sslContext.wrapSocket(s) - port = TPort(443) + port = sockets.TPort(443) else: raise newException(EHttpRequestErr, "SSL support is not available. Cannot connect over SSL.") if r.port != "": - port = TPort(r.port.parseInt) + port = sockets.TPort(r.port.parseInt) if timeout == -1: s.connect(r.hostname, port) @@ -413,28 +415,217 @@ proc downloadFile*(url: string, outputFilename: string, else: fileError("Unable to open file") +proc generateHeaders(r: TURL, httpMethod: THttpMethod, + headers: PStringTable): string = + result = substr($httpMethod, len("http")) + # TODO: Proxies + result.add(" /" & r.path & r.query) + result.add(" HTTP/1.1\c\L") + + add(result, "Host: " & r.hostname & "\c\L") + add(result, "Connection: Keep-Alive\c\L") + for key, val in headers: + add(result, key & ": " & val & "\c\L") + + add(result, "\c\L") + +type + PAsyncHttpClient = ref object + socket: PAsyncSocket + connected: bool + currentURL: TURL ## Where we are currently connected. + headers: PStringTable + userAgent: string + +proc newAsyncHttpClient*(): PAsyncHttpClient = + new result + result.socket = newAsyncSocket() + result.headers = newStringTable(modeCaseInsensitive) + result.userAgent = defUserAgent + +proc parseChunks(client: PAsyncHttpClient): PFuture[string] {.async.} = + result = "" + var ri = 0 + while true: + var chunkSize = 0 + var chunkSizeStr = await client.socket.recvLine() + var i = 0 + if chunkSizeStr == "": + httpError("Server terminated connection prematurely") + while true: + case chunkSizeStr[i] + of '0'..'9': + chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) + of 'a'..'f': + chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10) + of 'A'..'F': + chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) + of '\0': + break + of ';': + # http://tools.ietf.org/html/rfc2616#section-3.6.1 + # We don't care about chunk-extensions. + break + else: + httpError("Invalid chunk size: " & chunkSizeStr) + inc(i) + if chunkSize <= 0: break + result.add await recv(client.socket, chunkSize) + discard await recv(client.socket, 2) # Skip \c\L + # Trailer headers will only be sent if the request specifies that we want + # them: http://tools.ietf.org/html/rfc2616#section-3.6.1 + +proc parseBody(client: PAsyncHttpClient, + headers: PStringTable): PFuture[string] {.async.} = + result = "" + if headers["Transfer-Encoding"] == "chunked": + result = await parseChunks(client) + else: + # -REGION- Content-Length + # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.3 + var contentLengthHeader = headers["Content-Length"] + if contentLengthHeader != "": + var length = contentLengthHeader.parseint() + result = await client.socket.recv(length) + if result == "": + httpError("Got disconnected while trying to recv body.") + else: + # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO + + # -REGION- Connection: Close + # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5 + if headers["Connection"] == "close": + var buf = "" + while True: + buf = await client.socket.recv(4000) + if buf == "": break + result.add(buf) + +proc parseResponse(client: PAsyncHttpClient, + getBody: bool): PFuture[TResponse] {.async.} = + var parsedStatus = false + var linei = 0 + var fullyRead = false + var line = "" + result.headers = newStringTable(modeCaseInsensitive) + while True: + linei = 0 + line = await client.socket.recvLine() + if line == "": break # We've been disconnected. + if line == "\c\L": + fullyRead = true + break + if not parsedStatus: + # Parse HTTP version info and status code. + var le = skipIgnoreCase(line, "HTTP/", linei) + if le <= 0: httpError("invalid http version") + inc(linei, le) + le = skipIgnoreCase(line, "1.1", linei) + if le > 0: result.version = "1.1" + else: + le = skipIgnoreCase(line, "1.0", linei) + if le <= 0: httpError("unsupported http version") + result.version = "1.0" + inc(linei, le) + # Status code + linei.inc skipWhitespace(line, linei) + result.status = line[linei .. -1] + parsedStatus = true + else: + # Parse headers + var name = "" + var le = parseUntil(line, name, ':', linei) + if le <= 0: httpError("invalid headers") + inc(linei, le) + if line[linei] != ':': httpError("invalid headers") + inc(linei) # Skip : + + result.headers[name] = line[linei.. -1].strip() + if not fullyRead: + httpError("Connection was closed before full request has been made") + if getBody: + result.body = await parseBody(client, result.headers) + else: + result.body = "" + +proc close*(client: PAsyncHttpClient) = + ## Closes any connections held by the HttpClient. + if client.connected: + client.socket.close() + client.connected = false + #client.socket = newAsyncSocket() + +proc newConnection(client: PAsyncHttpClient, url: TURL) {.async.} = + if not client.connected or client.currentURL.hostname != url.hostname or + client.currentURL.scheme != url.scheme: + if client.connected: client.close() + if url.scheme == "https": + assert false, "TODO SSL" + + # TODO: I should be able to write 'net.TPort' here... + let port = + if url.port == "": rawsockets.TPort(80) + else: rawsockets.TPort(url.port.parseInt) + + await client.socket.connect(url.hostname, port) + client.currentURL = url + +proc request*(client: PAsyncHttpClient, url: string, httpMethod = httpGET, + body = ""): PFuture[TResponse] {.async.} = + let r = parseUrl(url) + await newConnection(client, r) + + if not client.headers.hasKey("user-agent") and client.userAgent != "": + client.headers["User-Agent"] = client.userAgent + + var headers = generateHeaders(r, httpMethod, client.headers) + + await client.socket.send(headers) + if body != "": + await client.socket.send(body) + + result = await parseResponse(client, httpMethod != httpHEAD) when isMainModule: - #downloadFile("http://force7.de/nimrod/index.html", "nimrodindex.html") - #downloadFile("http://www.httpwatch.com/", "ChunkTest.html") - #downloadFile("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com", - # "validator.html") + when true: + # Async + proc main() {.async.} = + var client = newAsyncHttpClient() + var resp = await client.request("http://picheta.me") + + echo("Got response: ", resp.status) + echo("Body:\n") + echo(resp.body) - #var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com& - # charset=%28detect+automatically%29&doctype=Inline&group=0") - - var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L" - var body: string = "--xyz\c\L" - # soap 1.2 output - body.add("Content-Disposition: form-data; name=\"output\"\c\L") - body.add("\c\Lsoap12\c\L") - - # html - body.add("--xyz\c\L") - body.add("Content-Disposition: form-data; name=\"uploaded_file\";" & - " filename=\"test.html\"\c\L") - body.add("Content-Type: text/html\c\L") - body.add("\c\L

test

\c\L") - body.add("--xyz--") + #var resp1 = await client.request("http://freenode.net") + #echo("Got response: ", resp1.status) - echo(postContent("http://validator.w3.org/check", headers, body)) + var resp2 = await client.request("http://picheta.me/aasfasgf.html") + echo("Got response: ", resp2.status) + main() + runForever() + + else: + #downloadFile("http://force7.de/nimrod/index.html", "nimrodindex.html") + #downloadFile("http://www.httpwatch.com/", "ChunkTest.html") + #downloadFile("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com", + # "validator.html") + + #var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com& + # charset=%28detect+automatically%29&doctype=Inline&group=0") + + var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L" + var body: string = "--xyz\c\L" + # soap 1.2 output + body.add("Content-Disposition: form-data; name=\"output\"\c\L") + body.add("\c\Lsoap12\c\L") + + # html + body.add("--xyz\c\L") + body.add("Content-Disposition: form-data; name=\"uploaded_file\";" & + " filename=\"test.html\"\c\L") + body.add("Content-Type: text/html\c\L") + body.add("\c\L

test

\c\L") + body.add("--xyz--") + + echo(postContent("http://validator.w3.org/check", headers, body)) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 43642c7b9..4afb5c6ab 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -11,7 +11,7 @@ {.deadCodeElim: on.} import rawsockets, os, strutils, unsigned, parseutils, times - +export TPort type IpAddressFamily* {.pure.} = enum ## Describes the type of an IP address IPv6, ## IPv6 address From 78cc4de9a1d8db66d997e5ba02c6ce208637d70c Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 26 Mar 2014 20:31:18 +0100 Subject: [PATCH 233/249] fixes #1025; don't know what this breaks --- compiler/ast.nim | 2 +- compiler/semexprs.nim | 5 ++--- compiler/semtempl.nim | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index dd6f47ed1..0652bd3e7 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1043,7 +1043,7 @@ proc discardSons(father: PNode) = father.sons = nil when defined(useNodeIds): - const nodeIdToDebug = 612777 # 612794 + const nodeIdToDebug* = 482228 # 612794 #612840 # 612905 # 614635 # 614637 # 614641 # 423408 #429107 # 430443 # 441048 # 441090 # 441153 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 70c57b15e..1fd9075e8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1481,9 +1481,8 @@ proc processQuotations(n: var PNode, op: string, elif n.kind == nkAccQuoted and op == "``": returnQuote n[0] - if not n.isAtom: - for i in 0 .. Date: Wed, 26 Mar 2014 22:56:56 +0100 Subject: [PATCH 234/249] fixes tmemit regression --- compiler/semtempl.nim | 14 ++++++++++++++ tests/macros/tmemit.nim | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 407da8bdb..b71198119 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -232,6 +232,18 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind) = for j in countup(0, L-3): addLocalDecl(c, a.sons[j], symKind) +proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode = + result = n + if n.kind == nkIdent: + let s = qualifiedLookUp(c.c, n, {}) + if s != nil: + if s.owner == c.owner and s.kind == skParam: + incl(s.flags, sfUsed) + result = newSymNode(s, n.info) + else: + for i in 0 .. Date: Wed, 26 Mar 2014 18:54:34 -0700 Subject: [PATCH 235/249] Calling randomize() again within 1 second will now provide a different seed --- lib/pure/math.nim | 4 +++- tests/stdlib/tmath.nim | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 062cfae25..94570fc68 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -20,6 +20,8 @@ when defined(Posix) and not defined(haiku): {.passl: "-lm".} +import times + const PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number) E* = 2.71828182845904523536028747 ## Euler's number @@ -201,7 +203,7 @@ when not defined(JS): result = drand48() * max proc randomize() = - randomize(gettime(nil)) + randomize(cast[int](epochTime())) proc randomize(seed: int) = srand(cint(seed)) diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index a86a3b84c..fc9486093 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -23,6 +23,13 @@ suite "random int": rand = random(100..1000) check rand < 1000 check rand >= 100 + test "randomize() again gives new numbers": + randomize() + var rand1 = random(1000000) + randomize() + var rand2 = random(1000000) + check rand1 != rand2 + suite "random float": test "there might be some randomness": @@ -45,3 +52,10 @@ suite "random float": rand = random(100.0..1000.0) check rand < 1000.0 check rand >= 100.0 + test "randomize() again gives new numbers": + randomize() + var rand1:float = random(1000000.0) + randomize() + var rand2:float = random(1000000.0) + check rand1 != rand2 + From 491291ae24fe5901f4be918cbce2d6c21a0ae0e9 Mon Sep 17 00:00:00 2001 From: Charlie Barto Date: Thu, 27 Mar 2014 00:21:19 -0400 Subject: [PATCH 236/249] added usage example for lower bound --- lib/pure/algorithm.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index fb0ba5355..49d1ac972 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -63,6 +63,11 @@ proc lowerBound*[T](a: openarray[T], key: T, cmp: proc(x,y: T): int {.closure.}) ## ## `cmp` is the comparator function to use, the expected return values are the same as ## that of system.cmp + ## + ## example: + ## `var arr = @[1,2,3,5,6,7,8,9]` + ## `arr.insert(4, arr.lowerBound(4))` + ## after running the above arr is `[1,2,3,4,5,6,7,8,9]` result = a.low var pos = result var count, step: int From 3365b42bbe5e09f8e8a739597ae5e49e2ff2c259 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Mar 2014 19:20:59 +0100 Subject: [PATCH 237/249] fixes #1009 --- compiler/vm.nim | 2 +- compiler/vmgen.nim | 9 ++++++++- tests/vm/trgba.nim | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/vm/trgba.nim diff --git a/compiler/vm.nim b/compiler/vm.nim index 268289aca..0d5386502 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -318,7 +318,7 @@ proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = of tyFloat..tyFloat64: dest.intVal = system.toInt(src.floatVal) else: - dest.intVal = src.intVal and ((1 shl desttyp.size)-1) + dest.intVal = src.intVal and ((1 shl (desttyp.size*8))-1) of tyFloat..tyFloat64: if dest.kind != rkFloat: myreset(dest); dest.kind = rkFloat diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index e627fee48..3c0f8dbc9 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -41,7 +41,14 @@ proc codeListing(c: PCtx, result: var string, start=0) = let x = c.code[i] let opc = opcode(x) - if opc < firstABxInstr: + if opc in {opcConv, opcCast}: + let y = c.code[i+1] + let z = c.code[i+2] + result.addf("\t$#\tr$#, r$#, $#, $#", ($opc).substr(3), x.regA, x.regB, + c.types[y.regBx-wordExcess].typeToString, + c.types[z.regBx-wordExcess].typeToString) + inc i, 2 + elif opc < firstABxInstr: result.addf("\t$#\tr$#, r$#, r$#", ($opc).substr(3), x.regA, x.regB, x.regC) elif opc in relativeJumps: diff --git a/tests/vm/trgba.nim b/tests/vm/trgba.nim new file mode 100644 index 000000000..ec79f6aca --- /dev/null +++ b/tests/vm/trgba.nim @@ -0,0 +1,36 @@ +discard """ + output: '''[127, 127, 0, 255] +[127, 127, 0, 255] +''' +""" + +#bug #1009 +type + TAggRgba8* = array[4, byte] + +template R*(self: TAggRgba8): Byte = self[0] +template G*(self: TAggRgba8): Byte = self[1] +template B*(self: TAggRgba8): Byte = self[2] +template A*(self: TAggRgba8): Byte = self[3] + +template `R=`*(self: TAggRgba8, val: Byte) = + self[0] = val +template `G=`*(self: TAggRgba8, val: Byte) = + self[1] = val +template `B=`*(self: TAggRgba8, val: Byte) = + self[2] = val +template `A=`*(self: TAggRgba8, val: Byte) = + self[3] = val + +proc ABGR* (val: int| int64): TAggRgba8 = + var V = val + result.R = V and 0xFF + V = V shr 8 + result.G = V and 0xFF + V = V shr 8 + result.B = V and 0xFF + result.A = (V shr 8) and 0xFF + +const + c1 = ABGR(0xFF007F7F) +echo ABGR(0xFF007F7F).repr, c1.repr From 76ef7529575d9cff2cb97117fe9b000f2af928bd Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Mar 2014 19:43:17 +0100 Subject: [PATCH 238/249] made some tests green --- tests/showoff/tdrdobbs_examples.nim | 2 +- tests/stdlib/tmongo.nim | 16 ---------------- tests/threads/tthreadanalysis.nim | 4 +++- tests/vm/trgba.nim | 2 +- 4 files changed, 5 insertions(+), 19 deletions(-) delete mode 100644 tests/stdlib/tmongo.nim diff --git a/tests/showoff/tdrdobbs_examples.nim b/tests/showoff/tdrdobbs_examples.nim index d1e0585d2..0b3d86a05 100644 --- a/tests/showoff/tdrdobbs_examples.nim +++ b/tests/showoff/tdrdobbs_examples.nim @@ -1,7 +1,7 @@ discard """ output: '''108 11 -1 1936 -4.000000000000002-e001 +4.0000000000000002e-001 true truefalse''' """ diff --git a/tests/stdlib/tmongo.nim b/tests/stdlib/tmongo.nim deleted file mode 100644 index 1c4c0f4e6..000000000 --- a/tests/stdlib/tmongo.nim +++ /dev/null @@ -1,16 +0,0 @@ - -import mongo, db_mongo, oids, json - -var conn = db_mongo.open() - -var data = %{"a": %13, "b": %"my string value", - "inner": %{"i": %71} } - -var id = insertID(conn, "test.test", data) - -for v in find(conn, "test.test", "this.a == 13"): - print v - -delete(conn, "test.test", id) - -close(conn) diff --git a/tests/threads/tthreadanalysis.nim b/tests/threads/tthreadanalysis.nim index 4edd51025..3a46cd185 100644 --- a/tests/threads/tthreadanalysis.nim +++ b/tests/threads/tthreadanalysis.nim @@ -1,5 +1,7 @@ discard """ outputsub: "101" + msg: "Warning: write to foreign heap" + line: 37 cmd: "nimrod cc --hints:on --threads:on $# $#" """ @@ -8,7 +10,7 @@ import os var thr: array [0..5, TThread[tuple[a, b: int]]] -proc doNothing() = nil +proc doNothing() = discard type PNode = ref TNode diff --git a/tests/vm/trgba.nim b/tests/vm/trgba.nim index ec79f6aca..b1d94702f 100644 --- a/tests/vm/trgba.nim +++ b/tests/vm/trgba.nim @@ -6,7 +6,7 @@ discard """ #bug #1009 type - TAggRgba8* = array[4, byte] + TAggRgba8* = array[4, Byte] template R*(self: TAggRgba8): Byte = self[0] template G*(self: TAggRgba8): Byte = self[1] From de5d18188fd6cba580c5d80f1787a2c9dde9fc71 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Mar 2014 19:59:31 +0100 Subject: [PATCH 239/249] ENDB: got rid of deprecated 'nil' stmt --- lib/system/endb.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/system/endb.nim b/lib/system/endb.nim index 2d6a25824..74d3c37ac 100644 --- a/lib/system/endb.nim +++ b/lib/system/endb.nim @@ -179,7 +179,7 @@ proc scanAndAppendWord(src: cstring, a: var TStaticStr, start: int): int = while True: case src[result] of 'a'..'z', '0'..'9': add(a, src[result]) - of '_': nil # just skip it + of '_': discard # just skip it of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a'))) else: break inc(result) @@ -203,7 +203,7 @@ proc scanNumber(src: cstring, a: var int, start: int): int = while true: case src[result] of '0'..'9': a = a * 10 + ord(src[result]) - ord('0') - of '_': nil # skip underscores (nice for long line numbers) + of '_': discard # skip underscores (nice for long line numbers) else: break inc(result) @@ -524,7 +524,7 @@ proc lineHookImpl() {.nimcall.} = of dbBreakpoints: # debugger is only interested in breakpoints checkForBreakpoint() - else: nil + else: discard proc watchpointHookImpl(name: cstring) {.nimcall.} = dbgWriteStackTrace(framePtr) From 76dfa611ed480f81b33bf3966c2dddc7cc09159f Mon Sep 17 00:00:00 2001 From: Charlie Barto Date: Thu, 27 Mar 2014 15:13:10 -0400 Subject: [PATCH 240/249] fixed doc comment --- lib/pure/algorithm.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 49d1ac972..37fbc948c 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -64,10 +64,11 @@ proc lowerBound*[T](a: openarray[T], key: T, cmp: proc(x,y: T): int {.closure.}) ## `cmp` is the comparator function to use, the expected return values are the same as ## that of system.cmp ## - ## example: - ## `var arr = @[1,2,3,5,6,7,8,9]` - ## `arr.insert(4, arr.lowerBound(4))` - ## after running the above arr is `[1,2,3,4,5,6,7,8,9]` + ## example:: + ## + ## var arr = @[1,2,3,5,6,7,8,9] + ## arr.insert(4, arr.lowerBound(4)) + ## `after running the above arr is `[1,2,3,4,5,6,7,8,9]` result = a.low var pos = result var count, step: int From 31cfed0aa894caeb2dcd6d68f6465d62b0271044 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 27 Mar 2014 21:24:41 +0100 Subject: [PATCH 241/249] fixes a typo --- compiler/pragmas.nim | 8 ++++---- compiler/semstmts.nim | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b5dead5b2..692f4bac6 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -770,8 +770,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, else: invalidPragma(it) else: processNote(c, it) -proc implictPragmas*(c: PContext, sym: PSym, n: PNode, - validPragmas: TSpecialWords) = +proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, + validPragmas: TSpecialWords) = if sym != nil and sym.kind != skModule: var it = POptionEntry(c.optionStack.head) while it != nil: @@ -782,7 +782,7 @@ proc implictPragmas*(c: PContext, sym: PSym, n: PNode, internalError(n.info, "implicitPragmas") it = it.next.POptionEntry - if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: + if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: localError(n.info, errDynlibRequiresExportc) var lib = POptionEntry(c.optionStack.tail).dynlib if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and @@ -806,4 +806,4 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = if n == nil: return for i in countup(0, sonsLen(n) - 1): if singlePragma(c, sym, n, i, validPragmas): break - implictPragmas(c, sym, n, validPragmas) + implicitPragmas(c, sym, n, validPragmas) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 38854bc8a..2a6e797da 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1088,7 +1088,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if n.sons[pragmasPos].kind != nkEmpty: pragma(c, s, n.sons[pragmasPos], validPragmas) else: - implictPragmas(c, s, n, validPragmas) + implicitPragmas(c, s, n, validPragmas) else: if n.sons[pragmasPos].kind != nkEmpty: localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProc) From 23d581d3771658f924c37345738ce58db8a8669e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 27 Mar 2014 21:52:28 +0000 Subject: [PATCH 242/249] Added test for issue #911. --- tests/macros/tgensym.nim | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 tests/macros/tgensym.nim diff --git a/tests/macros/tgensym.nim b/tests/macros/tgensym.nim new file mode 100644 index 000000000..629a6097e --- /dev/null +++ b/tests/macros/tgensym.nim @@ -0,0 +1,62 @@ +import rawsockets, asyncdispatch, macros +var p = newDispatcher() +var sock = newAsyncRawSocket() + +proc convertReturns(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} = + case node.kind + of nnkReturnStmt: + result = newCall(newIdentNode("complete"), retFutureSym, node[0]) + else: + result = node + for i in 0 .. var retFuture = newFuture[T]() + var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture") + outerProcBody.add( + newVarStmt(retFutureSym, + newCall( + newNimNode(nnkBracketExpr).add( + newIdentNode("newFuture"), + prc[3][0][1])))) # Get type from return type of this proc. + + # -> iterator nameIter(): PFutureBase {.closure.} = + # Changing this line to: newIdentNode($prc[0].ident & "Iter") # will make it work. + var iteratorNameSym = genSym(nskIterator, $prc[0].ident & "Iter") + var procBody = prc[6].convertReturns(retFutureSym) + + var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")], + procBody, nnkIteratorDef) + closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure")) + outerProcBody.add(closureIterator) + + # -> var nameIterVar = nameIter + # -> var first = nameIterVar() + var varNameIterSym = newIdentNode($prc[0].ident & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar") + var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym) + outerProcBody.add varNameIter + var varFirstSym = genSym(nskVar, "first") + var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym)) + outerProcBody.add varFirst + + + result = prc + + # Remove the 'closure' pragma. + for i in 0 .. Date: Fri, 28 Mar 2014 08:40:15 +0100 Subject: [PATCH 243/249] fixes #911 --- compiler/semstmts.nim | 6 ++++++ tests/macros/tgensym.nim | 1 + todo.txt | 1 - 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2a6e797da..c31a8a06d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1038,6 +1038,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, return else: s = n[namePos].sym + s.owner = getCurrOwner() typeIsDetermined = s.typ == nil s.ast = n s.scope = c.currentScope @@ -1165,6 +1166,11 @@ proc semIterator(c: PContext, n: PNode): PNode = let kind = if hasPragma(n[pragmasPos], wClosure) or n[namePos].kind == nkEmpty: skClosureIterator else: skIterator + # gensym'ed iterator? + if n[namePos].kind == nkSym: + # gensym'ed iterators might need to become closure iterators: + n[namePos].sym.owner = getCurrOwner() + n[namePos].sym.kind = kind result = semProcAux(c, n, kind, iteratorPragmas) var s = result.sons[namePos].sym var t = s.typ diff --git a/tests/macros/tgensym.nim b/tests/macros/tgensym.nim index 629a6097e..3f4140ff4 100644 --- a/tests/macros/tgensym.nim +++ b/tests/macros/tgensym.nim @@ -28,6 +28,7 @@ macro async2(prc: stmt): stmt {.immediate.} = # -> iterator nameIter(): PFutureBase {.closure.} = # Changing this line to: newIdentNode($prc[0].ident & "Iter") # will make it work. var iteratorNameSym = genSym(nskIterator, $prc[0].ident & "Iter") + #var iteratorNameSym = newIdentNode($prc[0].ident & "Iter") var procBody = prc[6].convertReturns(retFutureSym) var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")], diff --git a/todo.txt b/todo.txt index 7a315545e..0e61a069b 100644 --- a/todo.txt +++ b/todo.txt @@ -1,7 +1,6 @@ version 0.9.4 ============= -- fix gensym capture bug - fix GC issues Bugs From 2958adc47f201bde20deeae1f701029d3dd026ff Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 28 Mar 2014 18:15:51 +0100 Subject: [PATCH 244/249] tester outputs full test path --- tests/testament/htmlgen.nim | 3 ++- todo.txt | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim index 89d56c693..b91475aee 100644 --- a/tests/testament/htmlgen.nim +++ b/tests/testament/htmlgen.nim @@ -175,7 +175,8 @@ proc generateJson*(filename: string, commit: int) = where A.[commit] = ? and B.[commit] = ? and A.machine = ? and A.result != B.result""" selResults = """select - name, category, target, action, result, expected, given + category || '/' || target || '/' || name, + category, target, action, result, expected, given from TestResult where [commit] = ?""" var db = open(connection="testament.db", user="testament", password="", diff --git a/todo.txt b/todo.txt index 0e61a069b..982acfb62 100644 --- a/todo.txt +++ b/todo.txt @@ -19,6 +19,9 @@ Bugs version 0.9.x ============= +- pragmas need 'bindSym' support +- pragmas need re-work: 'push' is dangerous, 'hasPragma' does not work + reliably with user-defined pragmas - memory manager: add a measure of fragmentation - implement 'bits' pragmas - we need a magic thisModule symbol From b6d5f18c9345c0000bf4f87dc455b3aa3946811d Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 29 Mar 2014 13:50:07 +0100 Subject: [PATCH 245/249] iterators check seqs/strings are not resized during iteration --- lib/system.nim | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index be57ba192..047b998c3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1457,20 +1457,6 @@ iterator items*[IX, T](a: array[IX, T]): T {.inline.} = if i >= high(IX): break inc(i) -iterator items*[T](a: seq[T]): T {.inline.} = - ## iterates over each item of `a`. - var i = 0 - while i < len(a): - yield a[i] - inc(i) - -iterator items*(a: string): char {.inline.} = - ## iterates over each item of `a`. - var i = 0 - while i < len(a): - yield a[i] - inc(i) - iterator items*[T](a: set[T]): T {.inline.} = ## iterates over each element of `a`. `items` iterates only over the ## elements that are really in the set (and not over the ones the set is @@ -2687,6 +2673,24 @@ template doAssert*(cond: bool, msg = "") = if not cond: raiseAssert(astToStr(cond) & ' ' & msg) +iterator items*[T](a: seq[T]): T {.inline.} = + ## iterates over each item of `a`. + var i = 0 + let L = len(a) + while i < L: + yield a[i] + inc(i) + assert(len(a) == L, "seq modified while iterating over it") + +iterator items*(a: string): char {.inline.} = + ## iterates over each item of `a`. + var i = 0 + let L = len(a) + while i < L: + yield a[i] + inc(i) + assert(len(a) == L, "string modified while iterating over it") + when not defined(nimhygiene): {.pragma: inject.} From f50575d147132dc6d8d368b7c43f8da81326c814 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 29 Mar 2014 14:47:01 +0100 Subject: [PATCH 246/249] fixes #932 --- compiler/lambdalifting.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 6f6942096..6da156ba6 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -356,7 +356,10 @@ proc captureVar(o: POuterContext, i: PInnerContext, local: PSym, # it's in some upper environment: access = indirectAccess(access, addDep(e, it, i.fn), info) access = indirectAccess(access, local, info) - incl(o.capturedVars, local.id) + if o.isIter: + if not containsOrIncl(o.capturedVars, local.id): addField(o.tup, local) + else: + incl(o.capturedVars, local.id) idNodeTablePut(i.localsToAccess, local, access) proc interestingVar(s: PSym): bool {.inline.} = From 227c54c786f8fd58b4d4d88a9a492e3aef93f03b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 29 Mar 2014 21:28:01 +0000 Subject: [PATCH 247/249] Replaced idents with genSym in asyncdispatch. --- lib/pure/asyncdispatch.nim | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 72d2fc4e4..344652c9f 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -752,7 +752,7 @@ template createVar(futSymName: string, asyncProc: PNimrodNode, valueReceiver: expr) {.immediate, dirty.} = # TODO: Used template here due to bug #926 result = newNimNode(nnkStmtList) - var futSym = newIdentNode(futSymName) #genSym(nskVar, "future") + var futSym = genSym(nskVar, "future") result.add newVarStmt(futSym, asyncProc) # -> var future = y result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future.read @@ -843,14 +843,11 @@ macro async*(prc: stmt): stmt {.immediate.} = subtypeName = $returnType[1].ident elif returnType.kind == nnkEmpty: subtypeName = "void" - - # TODO: Why can't I use genSym? I get illegal capture errors for Syms. - # TODO: It seems genSym is broken. Change all usages back to genSym when fixed var outerProcBody = newNimNode(nnkStmtList) # -> var retFuture = newFuture[T]() - var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture") + var retFutureSym = genSym(nskVar, "retFuture") outerProcBody.add( newVarStmt(retFutureSym, newCall( @@ -862,7 +859,7 @@ macro async*(prc: stmt): stmt {.immediate.} = # -> var result: T # -> # -> complete(retFuture, result) - var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter") + var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter") var procBody = prc[6].processBody(retFutureSym) if subtypeName != "void": procBody.insert(0, newNimNode(nnkVarSection).add( @@ -881,7 +878,7 @@ macro async*(prc: stmt): stmt {.immediate.} = # -> var nameIterVar = nameIter # -> var first = nameIterVar() - var varNameIterSym = newIdentNode($prc[0].getName & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar") + var varNameIterSym = genSym(nskVar, $prc[0].getName & "IterVar") var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym) outerProcBody.add varNameIter var varFirstSym = genSym(nskVar, "first") From aae774a2799232941be4791a40e1913507281e8e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 29 Mar 2014 22:01:20 +0000 Subject: [PATCH 248/249] Fix docgen. --- lib/pure/asyncdispatch.nimrod.cfg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 lib/pure/asyncdispatch.nimrod.cfg diff --git a/lib/pure/asyncdispatch.nimrod.cfg b/lib/pure/asyncdispatch.nimrod.cfg new file mode 100644 index 000000000..e88f8eec3 --- /dev/null +++ b/lib/pure/asyncdispatch.nimrod.cfg @@ -0,0 +1,3 @@ +@if nimdoc: + --os:linux +@end From 44ee8aecfd70d1d381b5eed5ae52b01fae04452b Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 30 Mar 2014 22:40:09 +0200 Subject: [PATCH 249/249] disable extern name checking as it breaks building of nimrtl.dll --- compiler/pragmas.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 692f4bac6..14d155539 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -111,8 +111,9 @@ proc validateExternCName(s: PSym, info: TLineInfo) = proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = setExternName(s, extname) - if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}: - validateExternCName(s, info) + # XXX to fix make it work with nimrtl. + #if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}: + # validateExternCName(s, info) incl(s.flags, sfExportc) proc processImportCompilerProc(s: PSym, extname: string) =