From 526f9da46368d9f764783696b4e993c8e1e265f4 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 31 Oct 2013 02:45:17 +0000 Subject: [PATCH 001/141] Epoll wrapper + selectors module. --- lib/posix/epoll.nim | 87 ++++++++++++++++ lib/pure/selectors.nim | 228 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 315 insertions(+) create mode 100644 lib/posix/epoll.nim create mode 100644 lib/pure/selectors.nim diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim new file mode 100644 index 0000000000..098838624d --- /dev/null +++ b/lib/posix/epoll.nim @@ -0,0 +1,87 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2013 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +const + EPOLLIN* = 0x00000001 + EPOLLPRI* = 0x00000002 + EPOLLOUT* = 0x00000004 + EPOLLERR* = 0x00000008 + EPOLLHUP* = 0x00000010 + EPOLLRDNORM* = 0x00000040 + EPOLLRDBAND* = 0x00000080 + EPOLLWRNORM* = 0x00000100 + EPOLLWRBAND* = 0x00000200 + EPOLLMSG* = 0x00000400 + EPOLLRDHUP* = 0x00002000 + EPOLLWAKEUP* = 1 shl 29 + EPOLLONESHOT* = 1 shl 30 + EPOLLET* = 1 shl 31 + +# Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). + +const + EPOLL_CTL_ADD* = 1 # Add a file descriptor to the interface. + EPOLL_CTL_DEL* = 2 # Remove a file descriptor from the interface. + EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure. + +type + epoll_data* {.pure, final.} = object + thePtr*: pointer + fd*: cint + u32*: uint32 + u64*: uint64 + + epoll_event* {.pure, final.} = object + events*: uint32 # Epoll events + data*: epoll_data # User data variable + +proc epoll_create*(size: cint): cint {.importc: "epoll_create", + header: "".} + ## Creates an epoll instance. Returns an fd for the new instance. + ## The "size" parameter is a hint specifying the number of file + ## descriptors to be associated with the new instance. The fd + ## returned by epoll_create() should be closed with close(). + +proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1", + header: "".} + ## 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 {. + 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 + ## specific error code ) The "op" parameter is one of the EPOLL_CTL_* + ## constants defined above. The "fd" parameter is the target of the + ## operation. The "event" parameter describes which events the caller + ## is interested in and any associated user data. + +proc epoll_wait*(epfd: cint; events: ptr epoll_event; maxevents: cint; + timeout: cint): cint {.importc: "epoll_wait", + header: "".} + ## Wait for events on an epoll instance "epfd". Returns the number of + ## triggered events returned in "events" buffer. Or -1 in case of + ## error with the "errno" variable set to the specific error code. The + ## "events" parameter is a buffer that will contain triggered + ## events. The "maxevents" is the maximum number of events to be + ## returned ( usually size of "events" ). The "timeout" parameter + ## specifies the maximum wait time in milliseconds (-1 == infinite). + ## + ## This function is a cancellation point and therefore not marked with + ## __THROW. + + +#proc epoll_pwait*(epfd: cint; events: ptr epoll_event; maxevents: cint; +# timeout: cint; ss: ptr sigset_t): cint {. +# importc: "epoll_pwait", header: "".} +# Same as epoll_wait, but the thread's signal mask is temporarily +# and atomically replaced with the one provided as parameter. +# +# This function is a cancellation point and therefore not marked with +# __THROW. diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim new file mode 100644 index 0000000000..613f2d57b4 --- /dev/null +++ b/lib/pure/selectors.nim @@ -0,0 +1,228 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2013 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# TODO: Docs. + +import tables, os, unsigned +when defined(windows): + import winlean +else: + import posix + +type + TEvent* = enum + EvRead, EvWrite + + TSelectorKey* = object + fd: cint + events: set[TEvent] + data: PObject + + TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]] + + PSelector* = ref object of PObject ## Selector interface. + fds*: TTable[cint, TSelectorKey] + registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent], + data: var 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: var 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] = + ## + ## **Note:** For the ``epoll`` implementation the resulting + ## ``TSelectorKey.events`` will not contain the original events. + ## TODO: This breaks what TSelectorKey means... it's not a key anymore. + ## Rename to TSelectorInfo? + + 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: var 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 + type + PEpollSelector = ref object of PSelector + epollFD: cint + + proc esRegister(s: PSelector, fd: cint, events: set[TEvent], + data: var PObject): TSelectorKey = + var es = PEpollSelector(s) + var event: epoll_event + if EvRead in events: + event.events = EPOLLIN + if EvWrite in events: + event.events = event.events or EPOLLOUT + event.data.fd = fd + event.data.thePtr = addr(data) + + if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0: + OSError(OSLastError()) + + result = TSelectorKey(fd: fd, events: events, data: data) + + 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: + 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) + + proc esClose(s: PSelector) = + var es = PEpollSelector(s) + if es.epollFD.close() != 0: OSError(OSLastError()) + + proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] = + result = @[] + var es = PEpollSelector(s) + + var events: array[64, epoll_event] + let evNum = epoll_wait(es.epollFD, addr events[0], 64.cint, timeout.cint) + if evNum < 0: OSError(OSLastError()) + for i in 0 .. 63: + var evSet: set[TEvent] = {} + if (events[i].events and EPOLLIN) == 1: evSet = evSet + {EvRead} + if (events[i].events and EPOLLOUT) == 1: evSet = evSet + {EvWrite} + let selectorKey = TSelectorKey(fd: events[i].data.fd, events: evSet, + data: cast[PObject](events[i].data.thePtr)) + result.add((selectorKey, evSet)) + + proc newEpollSelector*(): PEpollSelector = + new result + result.epollFD = epoll_create(64) + if result.epollFD < 0: + OSError(OSLastError()) + +when isMainModule: + # Select() + import sockets + type + PSockWrapper = ref object of PObject + sock: TSocket + + + var sock = socket() + sock.connect("irc.freenode.net", TPort(6667)) + + var selector = newSelectSelector() + var data = PSockWrapper(sock: sock) + let key = selector.register(sock.getFD.cint, {EvRead, EvWrite}, data) + while true: + let ready = selector.select() + echo ready.len + if ready.len > 0: echo ready[0].repr + + + + + + + + + + \ No newline at end of file From 471c0aa6349b7ad41d70b64ed370106bed9f0d01 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 31 Oct 2013 15:07:14 +0000 Subject: [PATCH 002/141] Epoll now works. --- lib/posix/epoll.nim | 13 ++++---- lib/pure/selectors.nim | 73 +++++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index 098838624d..d50394f604 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -31,13 +31,14 @@ const EPOLL_CTL_MOD* = 3 # Change file descriptor epoll_event structure. type - epoll_data* {.pure, final.} = object - thePtr*: pointer - fd*: cint - u32*: uint32 - u64*: uint64 + epoll_data* {.importc: "union epoll_data", + header: "", pure, final.} = object # TODO: This is actually a union. + thePtr* {.importc: "ptr".}: pointer # \ + #fd*: cint + #u32*: uint32 + #u64*: uint64 - epoll_event* {.pure, final.} = object + epoll_event* {.importc: "struct epoll_event", header: "", pure, final.} = object events*: uint32 # Epoll events data*: epoll_data # User data variable diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 613f2d57b4..83c158da14 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -29,7 +29,7 @@ type PSelector* = ref object of PObject ## Selector interface. fds*: TTable[cint, TSelectorKey] registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent], - data: var PObject): TSelectorKey {.nimcall, tags: [FWriteIO].} + 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.} @@ -38,7 +38,7 @@ template initSelector(r: expr) = new r r.fds = initTable[cint, TSelectorKey]() -proc register*(s: PSelector, fd: cint, events: set[TEvent], data: var PObject): +proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject): TSelectorKey = if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data) @@ -51,10 +51,10 @@ proc unregister*(s: PSelector, fd: cint): TSelectorKey = proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] = ## - ## **Note:** For the ``epoll`` implementation the resulting - ## ``TSelectorKey.events`` will not contain the original events. - ## TODO: This breaks what TSelectorKey means... it's not a key anymore. - ## Rename to TSelectorInfo? + ## 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) @@ -67,7 +67,7 @@ type PSelectSelector* = ref object of PSelector ## Implementation of select() proc ssRegister(s: PSelector, fd: cint, events: set[TEvent], - data: var PObject): TSelectorKey = + 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) @@ -144,19 +144,29 @@ proc newSelectSelector*(): PSelectSelector = when defined(linux): import epoll type - PEpollSelector = ref object of PSelector + PEpollSelector* = ref object of PSelector epollFD: cint + events: array[64, ptr epoll_event] + + 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: var PObject): TSelectorKey = + data: PObject): TSelectorKey = var es = PEpollSelector(s) var event: epoll_event if EvRead in events: event.events = EPOLLIN if EvWrite in events: event.events = event.events or EPOLLOUT - event.data.fd = fd - event.data.thePtr = addr(data) + + 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()) @@ -176,47 +186,58 @@ when defined(linux): proc esClose(s: PSelector) = var es = PEpollSelector(s) if es.epollFD.close() != 0: OSError(OSLastError()) + dealloc(addr es.events) # TODO: Test this proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] = result = @[] var es = PEpollSelector(s) - var events: array[64, epoll_event] - let evNum = epoll_wait(es.epollFD, addr events[0], 64.cint, timeout.cint) + let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint) if evNum < 0: OSError(OSLastError()) - for i in 0 .. 63: + if evNum == 0: return @[] + for i in 0 .. 0: echo ready[0].repr - + if ready.len > 0: echo ready[0].events + i.inc + if i == 6: + selector.close() + break 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 003/141] 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 c7678b2148..f764971c4a 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 a90f0f50cf630a0fd565fd518e8fda4ff353977a Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Mon, 3 Feb 2014 12:40:51 -0600 Subject: [PATCH 004/141] removed explicit return in the documentation --- doc/manual.txt | 2 +- doc/tut2.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index da229d169b..1c410d5a3b 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2498,7 +2498,7 @@ different; for this a special setter syntax is needed: proc host*(s: TSocket): int {.inline.} = ## getter of hostAddr - return s.FHost + s.FHost var s: TSocket diff --git a/doc/tut2.txt b/doc/tut2.txt index 581239cc76..d2ba8b8a9b 100644 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -126,7 +126,7 @@ The syntax for type conversions is ``destination_type(expression_to_convert)`` .. code-block:: nimrod proc getID(x: TPerson): int = - return TStudent(x).id + TStudent(x).id The ``EInvalidObjectConversion`` exception is raised if ``x`` is not a ``TStudent``. From fb85d6062a23bc205e3ae06f152d9a4a718c9349 Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Wed, 5 Feb 2014 13:14:08 -0600 Subject: [PATCH 005/141] tracked down a few more returns --- doc/manual.txt | 31 ++++++++++++++++--------------- doc/tut1.txt | 10 +++++----- doc/tut2.txt | 2 +- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index 1c410d5a3b..3c085b6124 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -1701,11 +1701,11 @@ algorithm returns true: result = isOrdinal(t) or t.kind in {float, float32, float64} proc isExplicitlyConvertible(a, b: PType): bool = + result = false if isImplicitlyConvertible(a, b): return true if typeEqualsOrDistinct(a, b): return true if isIntegralType(a) and isIntegralType(b): return true if isSubtype(a, b) or isSubtype(b, a): return true - return false The convertible relation can be relaxed by a user-defined type `converter`:idx:. @@ -1774,7 +1774,7 @@ Example: .. code-block:: nimrod proc p(x, y: int): int = - return x + y + result = x + y discard p(3, 4) # discard the return value of `p` @@ -1789,7 +1789,7 @@ been declared with the `discardable`:idx: pragma: .. code-block:: nimrod proc p(x, y: int): int {.discardable.} = - return x + y + result = x + y p(3, 4) # now valid @@ -2438,7 +2438,7 @@ A procedure cannot modify its parameters (unless the parameters have the type .. code-block:: nimrod proc `$` (x: int): string = # converts an integer to a string; this is a prefix operator. - return intToStr(x) + result = intToStr(x) Operators with one parameter are prefix operators, operators with two parameters are infix operators. (However, the parser distinguishes these from @@ -2452,7 +2452,7 @@ notation. (Thus an operator can have more than two parameters): .. code-block:: nimrod proc `*+` (a, b, c: int): int = # Multiply and add - return a * b + c + result = a * b + c assert `*+`(3, 4, 6) == `*`(a, `+`(b, c)) @@ -2648,11 +2648,12 @@ return values. This can be done in a cleaner way by returning a tuple: .. code-block:: nimrod proc divmod(a, b: int): tuple[res, remainder: int] = - return (a div b, a mod b) + (a div b, a mod b) var t = divmod(8, 5) + assert t.res == 1 - assert t.remainder = 3 + assert t.remainder == 3 One can use `tuple unpacking`:idx: to access the tuple's fields: @@ -2724,7 +2725,7 @@ dispatch. method eval(e: ref TPlusExpr): int = # watch out: relies on dynamic binding - return eval(e.a) + eval(e.b) + result = eval(e.a) + eval(e.b) proc newLit(x: int): ref TLiteral = new(result) @@ -2923,7 +2924,7 @@ parameters of an outer factory proc: .. code-block:: nimrod proc mycount(a, b: int): iterator (): int = - return iterator (): int = + result = iterator (): int = var x = a while x <= b: yield x @@ -3373,9 +3374,9 @@ module to illustrate this: ## requires `x` and `y` to be of the same tuple type ## generic ``==`` operator for tuples that is lifted from the components ## of `x` and `y`. + result = true for a, b in fields(x, y): - if a != b: return false - return true + if a != b: result = false Alternatively, the ``distinct`` type modifier can be applied to the type class to allow each param matching the type class to bind to a different type. @@ -3997,9 +3998,9 @@ predicate: proc re(pattern: semistatic[string]): TRegEx = when isStatic(pattern): - return precompiledRegex(pattern) + result = precompiledRegex(pattern) else: - return compile(pattern) + result = compile(pattern) Static params can also appear in the signatures of generic types: @@ -4506,7 +4507,7 @@ This is best illustrated by an example: proc p*(x: A.T1): A.T1 = # this works because the compiler has already # added T1 to A's interface symbol table - return x + 1 + result = x + 1 Import statement @@ -5197,7 +5198,7 @@ Example: {.pragma: rtl, importc, dynlib: "client.dll", cdecl.} proc p*(a, b: int): int {.rtl.} = - return a+b + result = a+b In the example a new pragma named ``rtl`` is introduced that either imports a symbol from a dynamic library or exports the symbol for dynamic library diff --git a/doc/tut1.txt b/doc/tut1.txt index b70f40f4af..5a20629a2c 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -690,8 +690,8 @@ Nimrod provides the ability to overload procedures similar to C++: .. code-block:: nimrod proc toString(x: int): string = ... proc toString(x: bool): string = - if x: return "true" - else: return "false" + if x: result = "true" + else: result = "false" echo(toString(13)) # calls the toString(x: int) proc echo(toString(true)) # calls the toString(x: bool) proc @@ -1569,7 +1569,7 @@ This is best illustrated by an example: proc p*(x: A.T1): A.T1 = # this works because the compiler has already # added T1 to A's interface symbol table - return x + 1 + result = x + 1 A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If @@ -1600,11 +1600,11 @@ rules apply: .. code-block:: nimrod # Module A - proc x*(a: int): string = return $a + proc x*(a: int): string = result = $a .. code-block:: nimrod # Module B - proc x*(a: string): string = return $a + proc x*(a: string): string = result = $a .. code-block:: nimrod # Module C diff --git a/doc/tut2.txt b/doc/tut2.txt index d2ba8b8a9b..5f1767428d 100644 --- a/doc/tut2.txt +++ b/doc/tut2.txt @@ -238,7 +238,7 @@ is needed: proc host*(s: TSocket): int {.inline.} = ## getter of hostAddr - return s.FHost + s.FHost var s: TSocket From 38e4dfc164404a601e0556936497e7ee97a88fba Mon Sep 17 00:00:00 2001 From: EXetoC Date: Thu, 6 Feb 2014 14:18:44 +0100 Subject: [PATCH 006/141] Export procs that are useful elsewhere. --- lib/pure/times.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 6186fcad8f..de6c4e4fa2 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -211,7 +211,9 @@ proc initInterval*(miliseconds, seconds, minutes, hours, days, months, result.months = months result.years = years -proc isLeapYear(year: int): bool = +proc isLeapYear*(year: int): bool = + ## returns true if ``year`` is a leap year + if year mod 400 == 0: return true elif year mod 100 == 0: @@ -221,7 +223,9 @@ proc isLeapYear(year: int): bool = else: return false -proc getDaysInMonth(month: TMonth, year: int): int = +proc getDaysInMonth*(month: TMonth, year: int): int = + ## gets the amount of days in a ``month`` of a ``year`` + # http://www.dispersiondesign.com/articles/time/number_of_days_in_a_month case month of mFeb: result = if isLeapYear(year): 29 else: 28 From a087f6057e70e8b7c57ec9a20ac7f7815afe9327 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Feb 2014 17:06:20 +0100 Subject: [PATCH 007/141] bugfix: codegen issue that caused getGMTime() to leak memory --- compiler/ccgexprs.nim | 2 +- doc/manual.txt | 2 ++ tests/gc/gcleak4.nim | 2 +- tests/gc/gcleak5.nim | 25 +++++++++++++++++++++++++ tests/testament/categories.nim | 5 +++++ todo.txt | 2 +- 6 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 tests/gc/gcleak5.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index be47ac0c4a..031ab8d704 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1049,7 +1049,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = app(tmp2.r, field.loc.r) tmp2.k = locTemp tmp2.t = field.loc.t - tmp2.s = OnHeap + tmp2.s = if isRef: OnHeap else: OnStack tmp2.heapRoot = tmp.r expr(p, it.sons[1], tmp2) if d.k == locNone: diff --git a/doc/manual.txt b/doc/manual.txt index da229d169b..520e4f62e9 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -2231,6 +2231,8 @@ Instead of: Using statement --------------- +**Warning**: The ``using`` statement is highly experimental! + The `using statement`:idx: provides syntactic convenience for procs that heavily use a single contextual parameter. When applied to a variable or a constant, it will instruct Nimrod to automatically consider the used symbol as diff --git a/tests/gc/gcleak4.nim b/tests/gc/gcleak4.nim index bd7bded283..6f2b8a1fe6 100644 --- a/tests/gc/gcleak4.nim +++ b/tests/gc/gcleak4.nim @@ -6,7 +6,7 @@ when defined(GC_setMaxPause): GC_setMaxPause 2_000 type - TExpr = object ## abstract base class for an expression + TExpr = object {.inheritable.} ## abstract base class for an expression PLiteral = ref TLiteral TLiteral = object of TExpr x: int diff --git a/tests/gc/gcleak5.nim b/tests/gc/gcleak5.nim new file mode 100644 index 0000000000..b9131051b0 --- /dev/null +++ b/tests/gc/gcleak5.nim @@ -0,0 +1,25 @@ +discard """ + output: "success" +""" + +import os, times + +proc main = + var i = 0 + for ii in 0..50_000: + #while true: + var t = getTime() + var g = t.getGMTime() + #echo isOnStack(addr g) + + if i mod 100 == 0: + let om = getOccupiedMem() + echo "memory: ", om + if om > 100_000: quit "leak" + + inc(i) + sleep(1) + + echo "success" + +main() diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 5dd8414470..f9f5698bbb 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -123,9 +123,14 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "gcleak2" test "gctest" test "gcleak3" + test "gcleak4" + test "gcleak5" test "weakrefs" test "cycleleak" test "closureleak" + test "refarrayleak" + rest "stackrefleak" + # ------------------------- threading tests ----------------------------------- diff --git a/todo.txt b/todo.txt index a9f2e80e5b..738e9b3fad 100644 --- a/todo.txt +++ b/todo.txt @@ -5,7 +5,7 @@ version 0.9.4 - fix macros\tstringinterp.nim - test and fix showoff - test C source code generation -- test and fix closures +- fix closures - test and fix exception handling - implement 'union' and 'bits' pragmas From c3adc19f471ddddf0cab9a92908dcdbede26b3eb Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Feb 2014 18:49:41 +0100 Subject: [PATCH 008/141] tester compiles again --- tests/gc/gcleak5.nim | 2 +- tests/testament/categories.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/gc/gcleak5.nim b/tests/gc/gcleak5.nim index b9131051b0..9e2948729e 100644 --- a/tests/gc/gcleak5.nim +++ b/tests/gc/gcleak5.nim @@ -14,7 +14,7 @@ proc main = if i mod 100 == 0: let om = getOccupiedMem() - echo "memory: ", om + #echo "memory: ", om if om > 100_000: quit "leak" inc(i) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index f9f5698bbb..442dd12123 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -129,7 +129,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "cycleleak" test "closureleak" test "refarrayleak" - rest "stackrefleak" + test "stackrefleak" # ------------------------- threading tests ----------------------------------- From eedf51e9d11e362fc14bc8b33aca58775420576e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 8 Feb 2014 16:08:03 +0000 Subject: [PATCH 009/141] Added new sockets modules: asyncio2, net, and sockets2. --- lib/pure/asyncio2.nim | 469 ++++++++++++++++++++++++++++++++++++++++ lib/pure/net.nim | 40 ++++ lib/pure/sockets2.nim | 202 +++++++++++++++++ lib/windows/winlean.nim | 77 +++++++ 4 files changed, 788 insertions(+) create mode 100644 lib/pure/asyncio2.nim create mode 100644 lib/pure/net.nim create mode 100644 lib/pure/sockets2.nim diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim new file mode 100644 index 0000000000..38fa934526 --- /dev/null +++ b/lib/pure/asyncio2.nim @@ -0,0 +1,469 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import os, oids, tables, strutils + +import winlean + +import sockets2, net + +## Asyncio2 +## -------- +## +## 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. + +# -- Futures + +type + PFuture*[T] = ref object + value: T + finished: bool + error: ref EBase + cb: proc (future: PFuture[T]) {.closure.} + +proc newFuture*[T](): PFuture[T] = + ## Creates a new future. + new(result) + result.finished = false + +proc complete*[T](future: PFuture[T], val: T) = + ## Completes ``future`` with value ``val``. + assert(not future.finished) + assert(future.error == nil) + future.value = val + future.finished = true + if future.cb != nil: + future.cb(future) + +proc fail*[T](future: PFuture[T], error: ref EBase) = + ## Completes ``future`` with ``error``. + assert(not future.finished) + future.finished = true + future.error = error + if future.cb != nil: + future.cb(future) + +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 read*[T](future: PFuture[T]): T = + ## Retrieves the value of ``future``. Future must be finished otherwise + ## this function will fail with a ``EInvalidValue`` exception. + ## + ## 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 + else: + # TODO: Make a custom exception type for this? + raise newException(EInvalidValue, "Future still in progress.") + +proc finished*[T](future: PFuture[T]): bool = + ## Determines whether ``future`` has completed. + ## + ## ``True`` may indicate an error or a value. Use ``hasError`` to distinguish. + future.finished + +proc failed*[T](future: PFuture[T]): bool = + ## Determines whether ``future`` completed with an error. + future.error != nil + +when defined(windows): + type + TCompletionKey = dword + + TCompletionData* = object + sock: TSocketHandle + cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.} + + PDispatcher* = ref object + ioPort: THandle + + TCustomOverlapped = object + Internal*: DWORD + InternalHigh*: DWORD + Offset*: DWORD + OffsetHigh*: DWORD + hEvent*: THANDLE + data*: TCompletionData + + PCustomOverlapped = ptr TCustomOverlapped + + proc newDispatcher*(): PDispatcher = + ## Creates a new Dispatcher instance. + new result + result.ioPort = CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) + + 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()) + + proc poll*(p: PDispatcher, timeout = 500) = + ## Waits for completion events and processes them. + let llTimeout = + if timeout == -1: winlean.INFINITE + else: timeout.int32 + var lpNumberOfBytesTransferred: DWORD + var lpCompletionKey: ULONG + var lpOverlapped: POverlapped + let res = GetQueuedCompletionStatus(p.ioPort, addr lpNumberOfBytesTransferred, + addr lpCompletionKey, addr lpOverlapped, llTimeout).bool + + # http://stackoverflow.com/a/12277264/492186 + # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html + var customOverlapped = cast[PCustomOverlapped](lpOverlapped) + if res: + assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle + + customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1)) + dealloc(customOverlapped) + else: + let errCode = OSLastError() + if lpOverlapped != nil: + assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle + dealloc(customOverlapped) + customOverlapped.data.cb(customOverlapped.data.sock, errCode) + else: + if errCode.int32 == WAIT_TIMEOUT: + # Timed out + discard + else: OSError(errCode) + + var connectExPtr: pointer = nil + var acceptExPtr: pointer = nil + var getAcceptExSockAddrsPtr: pointer = nil + + proc initPointer(s: TSocketHandle, func: var pointer, guid: var TGUID): bool = + # Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c + var bytesRet: DWord + func = nil + result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid, + sizeof(TGUID).dword, addr func, sizeof(pointer).DWORD, + addr bytesRet, nil, nil) == 0 + + proc initAll() = + let dummySock = socket() + if not initPointer(dummySock, connectExPtr, WSAID_CONNECTEX): + OSError(OSLastError()) + if not initPointer(dummySock, acceptExPtr, WSAID_ACCEPTEX): + OSError(OSLastError()) + if not initPointer(dummySock, getAcceptExSockAddrsPtr, WSAID_GETACCEPTEXSOCKADDRS): + OSError(OSLastError()) + + proc connectEx(s: TSocketHandle, name: ptr TSockAddr, namelen: cint, + lpSendBuffer: pointer, dwSendDataLength: dword, + lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool = + if connectExPtr.isNil: raise newException(EInvalidValue, "Need to initialise ConnectEx().") + let func = + cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint, + lpSendBuffer: pointer, dwSendDataLength: dword, + lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr) + + result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent, + lpOverlapped) + + proc acceptEx(listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer, + dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD, + lpOverlapped: POverlapped): bool = + if acceptExPtr.isNil: raise newException(EInvalidValue, "Need to initialise AcceptEx().") + let func = + cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer, + dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD, + lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr) + result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength, + dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived, + lpOverlapped) + + proc getAcceptExSockaddrs(lpOutputBuffer: pointer, + dwReceiveDataLength, dwLocalAddressLength, dwRemoteAddressLength: DWORD, + LocalSockaddr: ptr ptr TSockAddr, LocalSockaddrLength: lpint, + RemoteSockaddr: ptr ptr TSockAddr, RemoteSockaddrLength: lpint) = + if getAcceptExSockAddrsPtr.isNil: + raise newException(EInvalidValue, "Need to initialise getAcceptExSockAddrs().") + + let func = + cast[proc (lpOutputBuffer: pointer, + dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr, + LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr, + RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr) + + func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength, + dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength, + RemoteSockaddr, RemoteSockaddrLength) + + proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort, + af = AF_INET): PFuture[int] = + ## Connects ``socket`` to server at ``address:port``. + ## + ## Returns a ``PFuture`` which will complete when the connection succeeds + ## or an error occurs. + + 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 + 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)), + sizeof(saddr).TSockLen) < 0'i32: + OSError(OSLastError()) + + var aiList = getAddrInfo(address, port, af) + var success = false + var lastError: TOSErrorCode + var it = aiList + while it != nil: + # "the OVERLAPPED structure must remain valid until the I/O completes" + # 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) = + if errcode == TOSErrorCode(-1): + retFuture.complete(0) + else: + retFuture.fail(newException(EOS, osErrorMsg(errcode))) + ) + + var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint, + nil, 0, nil, cast[POverlapped](ol)) + if ret: + # Request to connect completed immediately. + success = true + retFuture.complete(0) + dealloc(ol) + break + else: + lastError = OSLastError() + if lastError.int32 == ERROR_IO_PENDING: + # In this case ``ol`` will be deallocated in ``poll``. + success = true + break + else: + dealloc(ol) + 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): PFuture[string] = + ## Reads ``size`` bytes from ``socket``. Returned future will complete once + ## all of the requested data is read. + + var retFuture = newFuture[string]() + + var dataBuf: TWSABuf + dataBuf.buf = newString(size) + dataBuf.len = size + + var bytesReceived, 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))) + ) + + let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived, + addr flags, cast[POverlapped](ol), nil) + if ret == -1: + let err = OSLastError() + if err.int32 != ERROR_IO_PENDING: + retFuture.fail(newException(EOS, osErrorMsg(err))) + dealloc(ol) + else: + # Request to read completed immediately. + var data = newString(size) + copyMem(addr data[0], addr dataBuf.buf[0], size) + retFuture.complete($data) + dealloc(ol) + return retFuture + + 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. + var retFuture = newFuture[int]() + + var dataBuf: TWSABuf + dataBuf.buf = data + dataBuf.len = data.len + + var bytesReceived, flags: DWord + 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))) + ) + + let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived, + flags, cast[POverlapped](ol), nil) + if ret == -1: + let err = osLastError() + if err.int32 != ERROR_IO_PENDING: + retFuture.fail(newException(EOS, osErrorMsg(err))) + dealloc(ol) + else: + retFuture.complete(0) + dealloc(ol) + return retFuture + + proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): + PFuture[tuple[address: string, client: TSocketHandle]] = + ## 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: TSocketHandle]]() + + var clientSock = socket() + if clientSock == OSInvalidSocket: osError(osLastError()) + + const lpOutputLen = 1024 + var lpOutputBuf = newString(lpOutputLen) + var dwBytesReceived: DWORD + let dwReceiveDataLength = 0.DWORD # We don't want any data to be read. + let dwLocalAddressLength = DWORD(sizeof (TSockaddr_in) + 16) + let dwRemoteAddressLength = DWORD(sizeof(TSockaddr_in) + 16) + + template completeAccept(): stmt {.immediate, dirty.} = + var listenSock = socket + let setoptRet = setsockopt(clientSock, SOL_SOCKET, + SO_UPDATE_ACCEPT_CONTEXT, addr listenSock, + sizeof(listenSock).TSockLen) + if setoptRet != 0: osError(osLastError()) + + var LocalSockaddr, RemoteSockaddr: ptr TSockAddr + var localLen, remoteLen: int32 + getAcceptExSockaddrs(addr lpOutputBuf[0], dwReceiveDataLength, + dwLocalAddressLength, dwRemoteAddressLength, + addr LocalSockaddr, addr localLen, + addr RemoteSockaddr, addr remoteLen) + # 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) + ) + + 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))) + ) + + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx + let ret = acceptEx(socket, clientSock, addr lpOutputBuf[0], + dwReceiveDataLength, + dwLocalAddressLength, + dwRemoteAddressLength, + addr dwBytesReceived, cast[POverlapped](ol)) + + if not ret: + let err = osLastError() + if err.int32 != ERROR_IO_PENDING: + retFuture.fail(newException(EOS, osErrorMsg(err))) + dealloc(ol) + else: + completeAccept() + dealloc(ol) + + 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. + + +when isMainModule: + + var p = newDispatcher() + var sock = socket() + #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) + + 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 + + while true: + p.poll() + echo "polled" + + + + + + + + \ No newline at end of file diff --git a/lib/pure/net.nim b/lib/pure/net.nim new file mode 100644 index 0000000000..bdcae677e8 --- /dev/null +++ b/lib/pure/net.nim @@ -0,0 +1,40 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a high-level cross-platform sockets interface. + +import sockets2, os + +type + TSocket* = TSocketHandle + +proc bindAddr*(socket: TSocket, 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. + + if address == "": + 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(socket, 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: + dealloc(aiList) + osError(osLastError()) + dealloc(aiList) \ No newline at end of file diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim new file mode 100644 index 0000000000..22624bbad5 --- /dev/null +++ b/lib/pure/sockets2.nim @@ -0,0 +1,202 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2014 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements a low-level cross-platform sockets interface. Look +## at the ``net`` module for the higher-level version. + +import unsigned, os + +when hostos == "solaris": + {.passl: "-lsocket -lnsl".} + +when defined(Windows): + import winlean +else: + import posix + +export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, + inet_ntoa + +type + + TPort* = distinct uint16 ## port type + + TDomain* = enum ## domain, which specifies the protocol family of the + ## created socket. Other domains than those that are listed + ## here are unsupported. + AF_UNIX, ## for local socket (using a file). Unsupported on Windows. + AF_INET = 2, ## for network protocol IPv4 or + AF_INET6 = 23 ## for network protocol IPv6. + + TType* = enum ## second argument to `socket` proc + SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets + SOCK_DGRAM = 2, ## datagram service or Datagram Sockets + SOCK_RAW = 3, ## raw protocols atop the network layer. + SOCK_SEQPACKET = 5 ## reliable sequenced packet service + + TProtocol* = enum ## third argument to `socket` proc + IPPROTO_TCP = 6, ## Transmission control protocol. + IPPROTO_UDP = 17, ## User datagram protocol. + IPPROTO_IP, ## Internet protocol. Unsupported on Windows. + IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows. + IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. + IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. + + TServent* {.pure, final.} = object ## information about a service + name*: string + aliases*: seq[string] + port*: TPort + proto*: string + + Thostent* {.pure, final.} = object ## information about a given host + name*: string + aliases*: seq[string] + addrtype*: TDomain + length*: int + addrList*: seq[string] + +when defined(windows): + let + OSInvalidSocket* = winlean.INVALID_SOCKET +else: + let + OSInvalidSocket* = posix.INVALID_SOCKET + +proc `==`*(a, b: TPort): bool {.borrow.} + ## ``==`` for ports. + +proc `$`*(p: TPort): string {.borrow.} + ## returns the port number as a string + +proc toInt*(domain: TDomain): cint + ## Converts the TDomain enum to a platform-dependent ``cint``. + +proc toInt*(typ: TType): cint + ## Converts the TType enum to a platform-dependent ``cint``. + +proc toInt*(p: TProtocol): cint + ## Converts the TProtocol enum to a platform-dependent ``cint``. + +when defined(posix): + proc toInt(domain: TDomain): cint = + case domain + of AF_UNIX: result = posix.AF_UNIX + of AF_INET: result = posix.AF_INET + of AF_INET6: result = posix.AF_INET6 + else: nil + + proc toInt(typ: TType): cint = + case typ + of SOCK_STREAM: result = posix.SOCK_STREAM + of SOCK_DGRAM: result = posix.SOCK_DGRAM + of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET + of SOCK_RAW: result = posix.SOCK_RAW + else: nil + + proc toInt(p: TProtocol): cint = + case p + of IPPROTO_TCP: result = posix.IPPROTO_TCP + of IPPROTO_UDP: result = posix.IPPROTO_UDP + of IPPROTO_IP: result = posix.IPPROTO_IP + of IPPROTO_IPV6: result = posix.IPPROTO_IPV6 + of IPPROTO_RAW: result = posix.IPPROTO_RAW + of IPPROTO_ICMP: result = posix.IPPROTO_ICMP + else: nil + +else: + proc toInt(domain: TDomain): cint = + result = toU16(ord(domain)) + + proc toInt(typ: TType): cint = + result = cint(ord(typ)) + + proc toInt(p: TProtocol): cint = + result = cint(ord(p)) + + +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) = + ## closes a socket. + when defined(windows): + discard winlean.closeSocket(socket) + else: + discard posix.close(socket) + # TODO: These values should not be discarded. An EOS should be raised. + # http://stackoverflow.com/questions/12463473/what-happens-if-you-call-close-on-a-bsd-socket-multiple-times + +proc bindAddr*(socket: TSocketHandle, name: ptr TSockAddr, namelen: TSockLen): cint = + result = bindSocket(socket, name, namelen) + +proc listen*(socket: TSocketHandle, backlog = SOMAXCONN) {.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()) + else: + if posix.listen(socket, cint(backlog)) < 0'i32: osError(osLastError()) + +proc getAddrInfo*(address: string, port: TPort, af: TDomain = AF_INET, typ: TType = SOCK_STREAM, + prot: TProtocol = IPPROTO_TCP): ptr TAddrInfo = + ## + ## + ## **Warning**: The resulting ``ptr TAddrInfo`` must be freed using ``dealloc``! + var hints: TAddrInfo + result = nil + hints.ai_family = toInt(af) + hints.ai_socktype = toInt(typ) + hints.ai_protocol = toInt(prot) + var gaiResult = getAddrInfo(address, $port, addr(hints), result) + if gaiResult != 0'i32: + when defined(windows): + OSError(OSLastError()) + else: + raise newException(EOS, $gai_strerror(gaiResult)) + +proc dealloc*(ai: ptr TAddrInfo) = + freeaddrinfo(ai) + +proc ntohl*(x: int32): int32 = + ## Converts 32-bit integers from network to host 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. + when cpuEndian == bigEndian: result = x + else: result = (x shr 24'i32) or + (x shr 8'i32 and 0xff00'i32) or + (x shl 8'i32 and 0xff0000'i32) or + (x shl 24'i32) + +proc ntohs*(x: int16): int16 = + ## Converts 16-bit integers from network to host 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. + when cpuEndian == bigEndian: result = x + else: result = (x shr 8'i16) or (x shl 8'i16) + +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) + +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) + +when defined(Windows): + var wsa: TWSADATA + if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) \ No newline at end of file diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 5b641185eb..7713d503aa 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -16,8 +16,12 @@ const type THandle* = int LONG* = int32 + ULONG* = int + PULONG* = ptr int WINBOOL* = int32 DWORD* = int32 + PDWORD* = ptr DWORD + LPINT* = ptr int32 HDC* = THandle HGLRC* = THandle @@ -632,3 +636,76 @@ when not useWinUnicode: proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, dynlib: "kernel32", importc: "UnmapViewOfFile".} +type + TOVERLAPPED* {.final, pure.} = object + Internal*: DWORD + InternalHigh*: DWORD + Offset*: DWORD + OffsetHigh*: DWORD + hEvent*: THANDLE + + POVERLAPPED* = ptr TOVERLAPPED + + POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD, + para3: POVERLAPPED){.stdcall.} + + TGUID* {.final, pure.} = object + D1*: int32 + D2*: int16 + D3*: int16 + D4*: array [0..7, int8] + +const + ERROR_IO_PENDING* = 997 + +proc CreateIoCompletionPort*(FileHandle: THANDLE, ExistingCompletionPort: THANDLE, + CompletionKey: DWORD, + NumberOfConcurrentThreads: DWORD): THANDLE{.stdcall, + dynlib: "kernel32", importc: "CreateIoCompletionPort".} + +proc GetQueuedCompletionStatus*(CompletionPort: THandle, + lpNumberOfBytesTransferred: PDWORD, lpCompletionKey: PULONG, + lpOverlapped: ptr POverlapped, + dwMilliseconds: DWORD): WINBOOL{.stdcall, + dynlib: "kernel32", importc: "GetQueuedCompletionStatus".} + +const + IOC_OUT* = 0x40000000 + IOC_IN* = 0x80000000 + IOC_WS2* = 0x08000000 + IOC_INOUT* = IOC_IN or IOC_OUT + +template WSAIORW*(x,y): expr = (IOC_INOUT or x or y) + +const + SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD + SO_UPDATE_ACCEPT_CONTEXT* = 0x700B + +var + WSAID_CONNECTEX*: TGUID = TGUID(D1: 0x25a207b9, D2: 0xddf3'i16, D3: 0x4660, D4: [ + 0x8e'i8, 0xe9'i8, 0x76'i8, 0xe5'i8, 0x8c'i8, 0x74'i8, 0x06'i8, 0x3e'i8]) + WSAID_ACCEPTEX*: TGUID = TGUID(D1: 0xb5367df1'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ + 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) + WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ + 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) + +proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: dword, lpvInBuffer: pointer, + cbInBuffer: dword, lpvOutBuffer: pointer, cbOutBuffer: dword, + lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED, + lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint + {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".} + +type + TWSABuf* {.importc: "WSABUF", header: "winsock2.h".} = object + len*: ULONG + buf*: cstring + +proc WSARecv*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, + bytesReceived, flags: PDWORD, lpOverlapped: POverlapped, + completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. + stdcall, importc: "WSARecv", dynlib: "Ws2_32.dll".} + +proc WSASend*(s: TSocketHandle, buf: ptr TWSABuf, bufCount: DWORD, + bytesSent: PDWord, flags: DWORD, lpOverlapped: POverlapped, + completionProc: POVERLAPPED_COMPLETION_ROUTINE): cint {. + stdcall, importc: "WSASend", dynlib: "Ws2_32.dll".} From 76e8efe36eede6db4c79586919c479a62fdb52dc Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 8 Feb 2014 22:59:40 +0000 Subject: [PATCH 010/141] Added a generic-lacking version of PFuture. --- lib/pure/asyncio2.nim | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim index 38fa934526..cdb4a6f492 100644 --- a/lib/pure/asyncio2.nim +++ b/lib/pure/asyncio2.nim @@ -23,9 +23,12 @@ import sockets2, net # -- Futures type - PFuture*[T] = ref object - value: T + PFutureVoid* = ref object of PObject + cbVoid: proc () {.closure.} finished: bool + + PFuture*[T] = ref object of PFutureVoid + value: T error: ref EBase cb: proc (future: PFuture[T]) {.closure.} @@ -42,6 +45,8 @@ proc complete*[T](future: PFuture[T], val: T) = future.finished = true if future.cb != nil: future.cb(future) + if future.cbVoid != nil: + future.cbVoid() proc fail*[T](future: PFuture[T], error: ref EBase) = ## Completes ``future`` with ``error``. @@ -60,6 +65,17 @@ proc `callback=`*[T](future: PFuture[T], 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() + proc read*[T](future: PFuture[T]): T = ## Retrieves the value of ``future``. Future must be finished otherwise ## this function will fail with a ``EInvalidValue`` exception. From dbc8aa60e30da3f85306455387bb563437215725 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Feb 2014 00:41:53 +0100 Subject: [PATCH 011/141] fixes 'newSeq[T]' instantiation bug --- compiler/semexprs.nim | 12 +++++++++--- compiler/semgnrc.nim | 11 ++++------- compiler/sigmatch.nim | 1 + doc/manual.txt | 2 +- tests/generics/tgenericrefs.nim | 12 ++++++++++++ 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6c76795780..a8a16672d6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1819,6 +1819,10 @@ proc semExport(c: PContext, n: PNode): PNode = c.module.ast.add x result = n +proc setGenericParams(c: PContext, n: PNode) = + for i in 1 .. Date: Sun, 9 Feb 2014 00:58:18 +0100 Subject: [PATCH 012/141] fixes #882; fixes #853 --- compiler/semthreads.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim index f7322db80d..d3426ca3e4 100644 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -358,7 +358,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner = of nkConstSection: result = analyseConstSection(c, n) of nkTypeSection, nkCommentStmt: result = toVoid of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, - nkElifBranch, nkElse, nkExceptBranch, nkOfBranch: + nkElifBranch, nkElse, nkExceptBranch, nkOfBranch, nkFinally: for i in 0 .. Date: Sun, 9 Feb 2014 01:34:17 +0100 Subject: [PATCH 013/141] compiles on sparc again --- lib/system/gc.nim | 2 +- todo.txt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 820093b3e0..b08a6d214f 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -802,7 +802,7 @@ when defined(sparc): # For SPARC architecture. # Addresses decrease as the stack grows. while sp <= max: gcMark(gch, sp[]) - sp = cast[ppointer](cast[TAddress](sp) +% sizeof(pointer)) + sp = cast[PPointer](cast[TAddress](sp) +% sizeof(pointer)) elif defined(ELATE): {.error: "stack marking code is to be written for this architecture".} diff --git a/todo.txt b/todo.txt index 738e9b3fad..0962d172e5 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 -- test C source code generation - fix closures - test and fix exception handling - implement 'union' and 'bits' pragmas From e6a50307bb505304b37147417ba2a93586d59a51 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Feb 2014 02:37:49 +0100 Subject: [PATCH 014/141] fixes #885 --- compiler/lambdalifting.nim | 53 +++++++++++++++++++++----------------- tests/iter/tanoniter1.nim | 4 +-- todo.txt | 7 ++--- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 00fa045562..ebb4aeac4f 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -637,6 +637,22 @@ proc outerProcSons(o: POuterContext, n: PNode) = let x = transformOuterProc(o, n.sons[i]) if x != nil: n.sons[i] = x +proc liftIterSym*(n: PNode): PNode = + # transforms (iter) to (let env = newClosure[iter](); (iter, env)) + let iter = n.sym + assert iter.kind == skIterator + + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + + var env = copySym(getHiddenParam(iter)) + env.kind = skLet + var v = newNodeI(nkVarSection, n.info) + addVar(v, newSymNode(env)) + result.add(v) + # add 'new' statement: + result.add(newCall(getSysSym"internalNew", env)) + result.add makeClosure(iter, env, n.info) + proc transformOuterProc(o: POuterContext, n: PNode): PNode = if n == nil: return nil case n.kind @@ -649,17 +665,22 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = return indirectAccess(newSymNode(o.closureParam), local, n.info) var closure = PEnv(idTableGet(o.lambdasToEnv, local)) - if closure != nil: - # we need to replace the lambda with '(lambda, env)': - if local.kind == skIterator and local.typ.callConv == ccClosure: - # 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: - #if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s) - # XXX why doesn't this work? + + if local.kind == skIterator and local.typ.callConv == ccClosure: + # 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: + if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s) + # XXX why doesn't this work? + if closure.isNil: + return liftIterSym(n) + else: let createdVar = generateIterClosureCreation(o, closure, closure.attachedNode) return makeClosure(local, createdVar, n.info) + + if closure != nil: + # we need to replace the lambda with '(lambda, env)': let a = closure.createdVar if a != nil: @@ -773,22 +794,6 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = # ------------------- iterator transformation -------------------------------- -proc liftIterSym*(n: PNode): PNode = - # transforms (iter) to (let env = newClosure[iter](); (iter, env)) - let iter = n.sym - assert iter.kind == skIterator - - result = newNodeIT(nkStmtListExpr, n.info, n.typ) - - var env = copySym(getHiddenParam(iter)) - env.kind = skLet - var v = newNodeI(nkVarSection, n.info) - addVar(v, newSymNode(env)) - result.add(v) - # add 'new' statement: - result.add(newCall(getSysSym"internalNew", env)) - result.add makeClosure(iter, env, n.info) - proc liftForLoop*(body: PNode): PNode = # problem ahead: the iterator could be invoked indirectly, but then # we don't know what environment to create here: diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim index 9db5ab8eca..578749caf6 100644 --- a/tests/iter/tanoniter1.nim +++ b/tests/iter/tanoniter1.nim @@ -22,11 +22,11 @@ proc factory2(a, b: int): iterator (): int = yield x inc x -let foo = factory 1, 4 +let foo = factory(1, 4) for f in foo(): echo f -let foo2 = factory2 1,2 +let foo2 = factory2(1,2) for f in foo2(): echo f diff --git a/todo.txt b/todo.txt index 0962d172e5..44aa397910 100644 --- a/todo.txt +++ b/todo.txt @@ -4,9 +4,7 @@ version 0.9.4 - fix GC issues - fix macros\tstringinterp.nim - test and fix showoff -- fix closures -- test and fix exception handling -- implement 'union' and 'bits' pragmas +- fix closure iterators Bugs @@ -27,6 +25,9 @@ Bugs 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 e478374e0d16cf42051e753ccdd364aec13cf7d7 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 9 Feb 2014 21:47:46 +0100 Subject: [PATCH 015/141] capturing of an iterator works better --- compiler/lambdalifting.nim | 31 ++++++++++++++++++++++--------- lib/windows/winlean.nim | 4 ++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ebb4aeac4f..3738f89b2d 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -116,7 +116,8 @@ type TDep = tuple[e: PEnv, field: PSym] TEnv {.final.} = object of TObject attachedNode: PNode - createdVar: PSym # if != nil it is a used environment + createdVar: PSym # if != nil it is a used environment + createdVarComesFromIter: bool capturedVars: seq[PSym] # captured variables in this environment deps: seq[TDep] # dependencies up: PEnv @@ -571,7 +572,14 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode = # maybe later: (sfByCopy in local.flags) # add ``env.param = param`` result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) - idNodeTablePut(o.localsToAccess, local, fieldAccess) + # it can happen that we already captured 'local' in some other environment + # then we capture by copy for now. This is not entirely correct but better + # than nothing: + let existing = idNodeTableGet(o.localsToAccess, local) + if existing.isNil: + idNodeTablePut(o.localsToAccess, local, fieldAccess) + else: + result.add(newAsgnStmt(fieldAccess, existing, env.info)) # add support for 'up' references: for e, field in items(scope.deps): # add ``env.up = env2`` @@ -584,14 +592,19 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode = proc generateIterClosureCreation(o: POuterContext; env: PEnv; scope: PNode): PSym = - result = newClosureCreationVar(o, env) - let cc = rawClosureCreation(o, env, result) - var insertPoint = scope.sons[0] - if insertPoint.kind == nkEmpty: scope.sons[0] = cc + if env.createdVarComesFromIter or env.createdVar.isNil: + # we have to create a new closure: + result = newClosureCreationVar(o, env) + let cc = rawClosureCreation(o, env, result) + var insertPoint = scope.sons[0] + if insertPoint.kind == nkEmpty: scope.sons[0] = cc + else: + assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList + for x in cc: insertPoint.add(x) + if env.createdVar == nil: env.createdVar = result else: - assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList - for x in cc: insertPoint.add(x) - if env.createdVar == nil: env.createdVar = result + result = env.createdVar + env.createdVarComesFromIter = true proc interestingIterVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 7713d503aa..6c8fa48823 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -689,8 +689,8 @@ var WSAID_GETACCEPTEXSOCKADDRS*: TGUID = TGUID(D1: 0xb5367df2'i32, D2: 0xcbac'i16, D3: 0x11cf, D4: [ 0x95'i8, 0xca'i8, 0x00'i8, 0x80'i8, 0x5f'i8, 0x48'i8, 0xa1'i8, 0x92'i8]) -proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: dword, lpvInBuffer: pointer, - cbInBuffer: dword, lpvOutBuffer: pointer, cbOutBuffer: dword, +proc WSAIoctl*(s: TSocketHandle, dwIoControlCode: DWORD, lpvInBuffer: pointer, + cbInBuffer: DWORD, lpvOutBuffer: pointer, cbOutBuffer: DWORD, lpcbBytesReturned: PDword, lpOverlapped: POVERLAPPED, lpCompletionRoutine: POVERLAPPED_COMPLETION_ROUTINE): cint {.stdcall, importc: "WSAIoctl", dynlib: "Ws2_32.dll".} From 728be67afb54fc9645ba36edd2b5e430ccf443de Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 9 Feb 2014 21:05:02 +0000 Subject: [PATCH 016/141] 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 6c8fa48823..74ef9c9ec9 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 0779d96a83f847bd1820b95d5fe4bb65ef28d94d Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Sun, 9 Feb 2014 17:04:00 -0600 Subject: [PATCH 017/141] moved the tests for TSet to the correct place --- tests/{sets => stdlib}/testequivalence.nim | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{sets => stdlib}/testequivalence.nim (100%) diff --git a/tests/sets/testequivalence.nim b/tests/stdlib/testequivalence.nim similarity index 100% rename from tests/sets/testequivalence.nim rename to tests/stdlib/testequivalence.nim From 93f2af1cb340eacdffc0c8cd7a9ebe86f6275d02 Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Sun, 9 Feb 2014 17:06:23 -0600 Subject: [PATCH 018/141] fixes #887 --- compiler/ccgexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 031ab8d704..01f23850bd 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1376,7 +1376,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = getTemp(p, getSysType(tyInt), i) # our counter initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - if d.k == locNone: getTemp(p, a.t, d) + if d.k == locNone: getTemp(p, getSysType(tyBool), d) lineF(p, cpsStmts, lookupOpr[op], [rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: From b9c0a4addca20bf730b3481fca71beadaeb512fd Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Feb 2014 00:16:29 +0100 Subject: [PATCH 019/141] nofoward doesn't exist yet; fixes #890 --- doc/manual.txt | 73 ++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/doc/manual.txt b/doc/manual.txt index 16e025ee0e..ca1e370a18 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -5136,51 +5136,54 @@ Example: .. code-block:: nimrod {.deadCodeElim: on.} -NoForward pragma ----------------- -The `noforward`:idx: pragma can be used to turn on and off a special compilation -mode that to large extent eliminates the need for forward declarations. In this -mode, the proc definitions may appear out of order and the compiler will postpone -their semantic analysis and compilation until it actually needs to generate code -using the definitions. In this regard, this mode is similar to the modus operandi -of dynamic scripting languages, where the function calls are not resolved until -the code is executed. Here is the detailed algorithm taken by the compiler: -1. When a callable symbol is first encountered, the compiler will only note the -symbol callable name and it will add it to the appropriate overload set in the -current scope. At this step, it won't try to resolve any of the type expressions -used in the signature of the symbol (so they can refer to other not yet defined -symbols). +.. + NoForward pragma + ---------------- + The `noforward`:idx: pragma can be used to turn on and off a special compilation + mode that to large extent eliminates the need for forward declarations. In this + mode, the proc definitions may appear out of order and the compiler will postpone + their semantic analysis and compilation until it actually needs to generate code + using the definitions. In this regard, this mode is similar to the modus operandi + of dynamic scripting languages, where the function calls are not resolved until + the code is executed. Here is the detailed algorithm taken by the compiler: -2. When a top level call is encountered (usually at the very end of the module), -the compiler will try to determine the actual types of all of the symbols in the -matching overload set. This is a potentially recursive process as the signatures -of the symbols may include other call expressions, whoose types will be resolved -at this point too. + 1. When a callable symbol is first encountered, the compiler will only note the + symbol callable name and it will add it to the appropriate overload set in the + current scope. At this step, it won't try to resolve any of the type expressions + used in the signature of the symbol (so they can refer to other not yet defined + symbols). -3. Finally, after the best overload is picked, the compiler will start compiling -the body of the respective symbol. This in turn will lead the compiler to discover -more call expresions that need to be resolved and steps 2 and 3 will be repeated -as necessary. + 2. When a top level call is encountered (usually at the very end of the module), + the compiler will try to determine the actual types of all of the symbols in the + matching overload set. This is a potentially recursive process as the signatures + of the symbols may include other call expressions, whoose types will be resolved + at this point too. -Please note that if a callable symbol is never used in this scenario, its body -will never be compiled. This is the default behavior leading to best compilation -times, but if exhaustive compilation of all definitions is required, using -``nimrod check`` provides this option as well. + 3. Finally, after the best overload is picked, the compiler will start compiling + the body of the respective symbol. This in turn will lead the compiler to discover + more call expresions that need to be resolved and steps 2 and 3 will be repeated + as necessary. -Example: + Please note that if a callable symbol is never used in this scenario, its body + will never be compiled. This is the default behavior leading to best compilation + times, but if exhaustive compilation of all definitions is required, using + ``nimrod check`` provides this option as well. -.. code-block:: nimrod + Example: - {.noforward: on.} + .. code-block:: nimrod - proc foo(x: int) = - bar x + {.noforward: on.} - proc bar(x: int) = - echo x + proc foo(x: int) = + bar x + + proc bar(x: int) = + echo x + + foo(10) - foo(10) Pragma pragma ------------- From 5b6e42c94f7f5e1699997530e5cd2144a74950a9 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Feb 2014 00:52:01 +0100 Subject: [PATCH 020/141] fixes #889 --- compiler/vmgen.nim | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index d0e8dacf32..a6d044e24d 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -216,10 +216,12 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = internalAssert tmp >= 0 result = TRegister(tmp) -proc clearDest(n: PNode; dest: var TDest) {.inline.} = +proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. # So we only set dest to -1 if 'void': - if n.typ.isNil or n.typ.kind == tyEmpty: dest = -1 + if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty): + c.freeTemp(dest) + dest = -1 proc isNotOpr(n: PNode): bool = n.kind in nkCallKinds and n.sons[0].kind == nkSym and @@ -259,7 +261,7 @@ proc genWhile(c: PCtx; n: PNode) = proc genBlock(c: PCtx; n: PNode; dest: var TDest) = withBlock(n.sons[0].sym): c.gen(n.sons[1], dest) - clearDest(n, dest) + c.clearDest(n, dest) proc genBreak(c: PCtx; n: PNode) = let L1 = c.xjmp(n, opcJmp) @@ -297,14 +299,16 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) = else: c.gen(it.sons[0], tmp) elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false + c.clearDest(n, dest) c.gen(it.sons[1], dest) # then part if i < sonsLen(n)-1: endings.add(c.xjmp(it.sons[1], opcJmp, 0)) c.patch(elsePos) else: + c.clearDest(n, dest) c.gen(it.sons[0], dest) for endPos in endings: c.patch(endPos) - clearDest(n, dest) + c.clearDest(n, dest) proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = # asgn dest, a @@ -385,8 +389,8 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = if i < sonsLen(n)-1: endings.add(c.xjmp(it.lastSon, opcJmp, 0)) c.patch(elsePos) + c.clearDest(n, dest) for endPos in endings: c.patch(endPos) - clearDest(n, dest) proc genType(c: PCtx; typ: PType): int = for i, t in c.types: @@ -400,6 +404,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = var endings: seq[TPosition] = @[] let elsePos = c.xjmp(n, opcTry, 0) c.gen(n.sons[0], dest) + c.clearDest(n, dest) c.patch(elsePos) for i in 1 .. Date: Mon, 10 Feb 2014 09:30:21 +0200 Subject: [PATCH 021/141] 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 3ed00fdb29..8899120526 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 007d17ce3f28dda72f26df4298bba438f76edd15 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 10 Feb 2014 17:12:25 +0100 Subject: [PATCH 022/141] 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 96afc6f4f9..ab09dc8603 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 02f0366cd2..3d0cc21541 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 44aa397910..2e6eb708ba 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 0b5c14962e63f3aca0c7480de2428568b4ba6650 Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Mon, 10 Feb 2014 12:44:44 -0600 Subject: [PATCH 023/141] added `map` to sets --- lib/pure/collections/sets.nim | 4 ++++ tests/stdlib/tsets.nim | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/stdlib/tsets.nim diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index e6ab617e50..12fa2625b2 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -241,3 +241,7 @@ proc `<=`*[A](s, t: TSet[A]): bool = proc `==`*[A](s, t: TSet[A]): bool = s.counter == t.counter and s <= t + +proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[A] = + result = initSet[B]() + for i in data: result.incl(op(i)) diff --git a/tests/stdlib/tsets.nim b/tests/stdlib/tsets.nim new file mode 100644 index 0000000000..a30373d376 --- /dev/null +++ b/tests/stdlib/tsets.nim @@ -0,0 +1,13 @@ +discard """ + output: '''true''' +""" + +import sets +var + a = initSet[int]() + b = initSet[int]() + +for i in 0..5: a.incl(i) +for i in 1..6: b.incl(i) + +echo map(a, proc(x: int): int = x + 1) == b From 56c41ae1401a90188c4238269b9f43abc466aafa Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Mon, 10 Feb 2014 12:45:00 -0600 Subject: [PATCH 024/141] added tests for set --- tests/sets/tsets_lt.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/sets/tsets_lt.nim diff --git a/tests/sets/tsets_lt.nim b/tests/sets/tsets_lt.nim new file mode 100644 index 0000000000..6d0b3a60c3 --- /dev/null +++ b/tests/sets/tsets_lt.nim @@ -0,0 +1,12 @@ +discard """ + output: '''true +true +true''' +""" + +var s, s1: set[char] +s = {'a'..'d'} +s1 = {'a'..'c'} +echo s1 < s +echo s1 * s == {'a'..'c'} +echo s1 <= s From e8c87d070a4fa507208a042e9f02b356eaa567ad Mon Sep 17 00:00:00 2001 From: Simon Hafner Date: Mon, 10 Feb 2014 13:02:47 -0600 Subject: [PATCH 025/141] fixed a bug in `map` for sets --- lib/pure/collections/sets.nim | 4 ++-- tests/stdlib/tsets.nim | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 12fa2625b2..ad3fe72183 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -242,6 +242,6 @@ proc `<=`*[A](s, t: TSet[A]): bool = proc `==`*[A](s, t: TSet[A]): bool = s.counter == t.counter and s <= t -proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[A] = +proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] = result = initSet[B]() - for i in data: result.incl(op(i)) + for item in data: result.incl(op(item)) diff --git a/tests/stdlib/tsets.nim b/tests/stdlib/tsets.nim index a30373d376..656c5b3f2d 100644 --- a/tests/stdlib/tsets.nim +++ b/tests/stdlib/tsets.nim @@ -1,13 +1,17 @@ discard """ - output: '''true''' + output: '''true +true''' """ import sets var a = initSet[int]() b = initSet[int]() + c = initSet[string]() for i in 0..5: a.incl(i) for i in 1..6: b.incl(i) +for i in 0..5: c.incl($i) echo map(a, proc(x: int): int = x + 1) == b +echo map(a, proc(x: int): string = $x) == c From a158053ae9d04ebd882b2c973ddf4a3dd7d4efe8 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 11 Feb 2014 01:14:57 +0200 Subject: [PATCH 026/141] 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 00ac79716a..845d4ae710 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 c9d95e1bf4..0bc52d6b75 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 a8a16672d6..3fe1367ec8 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 335ceafeb2..227228f6e0 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 0000000000..ac445fd32d --- /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 027/141] 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 c4c6ac0719..cd3a765c13 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 028/141] 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 10ac7aaafa..81e7120472 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 029/141] 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 cdb4a6f492..606e4b3497 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 030/141] 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 585ccf8692..d148229740 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 031/141] 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 032/141] 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 657c47b280..cc27d9f36c 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 033/141] 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 eb674a171a..74d8811b87 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 034/141] 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 4d2b3bfb71..dcd44870f0 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 035/141] nil->discard --- tools/nimweb.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 56d6bcadbc..ff343bd2a8 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 036/141] 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 c31eb3121d..93a7b7c722 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 f3c6723829..a3280bb13f 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 dcd44870f0..dcb66ae3e4 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 85692e97b2..1f23787804 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 037/141] 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 a07d912414..73b618f463 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 417b909ae4..c0037935ac 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 038/141] 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 845d4ae710..1406877214 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 0000000000..3ae95c5718 --- /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 039/141] 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 bb70f28b66..75adb1174b 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 040/141] 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 01f23850bd..27f7f4ba5c 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 c92c15fa91..673e888fe5 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 9cd2c0d87f..0e11483435 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 c0037935ac..535424a278 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 041/141] 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 0000000000..1ed1af3b6d --- /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 c0c7f46d74..40877f6382 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 042/141] 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 75adb1174b..89bb92f9a3 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 6df85bbc6a..e0689c4a64 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 043/141] 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 e0689c4a64..9a43c0a7da 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 044/141] 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 607bb074ae..fe868054f6 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 fb266ae3a6..fedf19c798 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 045/141] 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 fedf19c798..a003252772 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 046/141] 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 343f415b37..c05b551844 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 db75cd3c0f..bb9f18dc3c 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 dcb66ae3e4..ce01d36a57 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 047/141] 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 de6c4e4fa2..82ef0d86f3 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 048/141] 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 82ef0d86f3..2fce235e2d 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 049/141] 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 41260b36fc..138df1aecf 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 40877f6382..aa2f6f937d 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 050/141] 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 606e4b3497..1cf2292de0 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 051/141] 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 cd002eef1f..fe724f4ddb 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 ff3324b471..5a5bfb5743 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 6b19dc359e..0cd27a4434 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 3fe1367ec8..30ab344c26 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 0000000000..ce5b3942d7 --- /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 a045eb8803..b28e5c64a4 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 052/141] 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 41260b36fc..e2c4367498 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 053/141] 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 fb05826cb7..791bef823f 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 bb1410d92d..f10c0cc9cd 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 054/141] 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 0bc52d6b75..2345260541 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 30ab344c26..f09ee12956 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 44e414e9c8..184aca4f80 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 227228f6e0..fce0bdf486 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 db75cd3c0f..ef73ea783b 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 4c2f07b853..4c8c0fc56a 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 055/141] 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 1cf2292de0..00cb60d575 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 056/141] 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 95c71c08cc..1edda4aa02 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 057/141] 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 ffab05788f..e4abe11a00 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 1707b75dbe..84b4c4dfb6 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 67473e71fa..f24b0c420a 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 9288963386..a6f8363f61 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 824c1d8c70..8b668325f0 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 cf8ac52421..d9d57b5e51 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 5472b5ae4e..04f9561d30 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 395425b868..d59706865c 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 003ec6e50b..b033b98ecd 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 18fdca3e94..3f27e24f68 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 ec1084105d..b7043f7b78 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 3c543ecfae..963d0ccfb0 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 02d1f77765..a1647000cb 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 f4add6af45..43a260bcab 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 ef25e89139..32d50faaa2 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 a2fa1fa48b..e416edf0a2 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 51598713b7..d14111bb93 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 b3e02fd29c..9afb5018b1 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 7b806f15b9..e370209ed0 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 6008838ff4..71ecb0b48e 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 6a1dae54d3..935b08634a 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 fa9993cc97..ebe577b009 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 bdd8db0f8e..7775091a1f 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 c627e68e8a..1a21a18d1a 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 ba9f38ed90..40ff067655 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 b20e86a68f..28d92402e8 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 058/141] 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 fce0bdf486..8361d4681f 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 059/141] 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 af0d657f1c..443d845f63 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 0e11483435..71479abddd 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 0000000000..76e986787f --- /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 060/141] 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 00cb60d575..d3e4bcc986 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 061/141] 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 0000000000..84a4164d9b --- /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 062/141] fixes #922 --- compiler/vmgen.nim | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a6d044e24d..6fca7f907d 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 063/141] 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 d3e4bcc986..8541b2ba74 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 064/141] 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 61336aa872..2682053611 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 0871b7fb7a..e592b1e81e 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 0000000000..5d2ec9b331 --- /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 065/141] 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 1406877214..09b2511f1b 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 2345260541..df58c896f4 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 f09ee12956..c271911ab6 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 e592b1e81e..ee085a821d 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 73b618f463..f08214f1ed 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 8361d4681f..5fe474ef36 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 55a89920a6..d7148f1106 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 0000000000..a71c592c59 --- /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 5d2ec9b331..42353006d0 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 066/141] fixes #923 --- compiler/vm.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 81e7120472..0929e072bc 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 067/141] 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 6fca7f907d..80cf4a9bf5 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 0000000000..73fcc16ea9 --- /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 068/141] 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 80cf4a9bf5..206cca0d70 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 069/141] 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 b28e5c64a4..c84a1001cb 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 00bc6a1258..e99987903c 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 070/141] 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 c84a1001cb..00ec4bc0cd 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 071/141] 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 e99987903c..1e9673745b 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 072/141] 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 ee085a821d..503ea4bc1a 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 f10c0cc9cd..e5236aaab3 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 073/141] todo.txt changes --- todo.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/todo.txt b/todo.txt index 2e6eb708ba..656bd06fcc 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 074/141] 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 2682053611..0140d1ac4d 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 503ea4bc1a..a9a9079530 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 22624bbad5..f8284b3392 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 075/141] 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 00ec4bc0cd..187797b587 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 1e9673745b..07d7f50439 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 076/141] 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 5fe474ef36..240145118e 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 00b4276032..878356321c 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 077/141] 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 b9a0a4196b..d45e7df619 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 07d7f50439..a989238e13 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 078/141] 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 715214a1a0..5cee279fc8 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 079/141] 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 091079c1c2..08c0b450c7 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 080/141] 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 a989238e13..844ed3033d 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 081/141] 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 184aca4f80..98abaf0059 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 4fc5f479b7..0714f45892 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 a71c592c59..f7aafe1d90 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 0000000000..5f6f727210 --- /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 0000000000..6c77f1f485 --- /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 73a0ec9376..62d37783ce 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 9dd7d51de4..a2934052f9 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 71ecb0b48e..f0417c7ac6 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 bb569e7f87..d319362176 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 082/141] 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 d9ed50cfeb..602c70f4de 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 837bb4f50f..1c8e4516cd 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 53817c5080..f3602dc58c 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 2acb989c5f..27be9f8151 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 d764a66722..d9b3aebac1 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 4fc5f479b7..a19c456f7f 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 656bd06fcc..d3ed9957f5 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 187797b587..f9ac1b66d0 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 083/141] 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 98abaf0059..0eba602cc9 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 f08214f1ed..22edc6e329 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 5ea8cf063f..84527362f0 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 0000000000..b81c897213 --- /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 084/141] 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 aa2f6f937d..93d737aa69 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 085/141] 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 a9a9079530..6c4e29f297 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 4c8c0fc56a..5b04c490f2 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 086/141] 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 2acb989c5f..b683a49551 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 087/141] 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 0140d1ac4d..8c41a8191a 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 d9ed50cfeb..6e21a85b9b 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 088/141] 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 70b617393a..3b1516e17f 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 089/141] 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 fe724f4ddb..bd8bdb30b1 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 27f7f4ba5c..8cb423688c 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 c0fc4131a7..a8a15f5422 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 1afb5961ec..509c815c16 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 4740ddcb3d..925a80832f 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 0929e072bc..8480cd1c6b 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 d3ed9957f5..3985343e05 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 090/141] 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 64c1b717c3..cce2cc215f 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 8480cd1c6b..f17da1c694 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 091/141] 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 f17da1c694..820a2022c7 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 092/141] 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 3b1516e17f..68b1ab2237 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 093/141] 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 8c41a8191a..0140d1ac4d 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 6e21a85b9b..75c16f6cde 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 094/141] 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 d50394f604..3665215510 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 41260b36fc..bb4039c1bf 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 8541b2ba74..12d4cb5a31 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 bdcae677e8..0ec007009c 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 83c158da14..6482a01a6b 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 f8284b3392..031217b904 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 bcaffc2875..bde5bf8c8d 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 095/141] 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 0000000000..05c57c4f5c --- /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 096/141] 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 0000000000..b9f7843f6e --- /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 0000000000..14a2dab927 --- /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 097/141] 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 443d845f63..4576a54b5a 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 71479abddd..0df7bb6dc8 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 098/141] 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 93d737aa69..5c34c5a115 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 099/141] 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 5c34c5a115..0ac01091c1 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 100/141] 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 2682053611..ae673bd0e1 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 b53135a959..036e6cc3c0 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 09b2511f1b..5ee46654e2 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 c271911ab6..5384894906 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 503ea4bc1a..1a2f9a6a64 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 820a2022c7..6277b2dc62 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 ea7c94856a..72689c8795 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 cddda7bd38..b594c00a9e 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 39324e497d..9882ad90d2 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 7009d9a843..4bee45516a 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 101/141] 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 8226b0b046..d9c933939c 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 ad43c69b7d..df1b743ee0 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 27163e2711..0d09d40e3e 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 102/141] 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 79a4de04b1..4e17d3e02b 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 103/141] 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 4e17d3e02b..2a685f3fb4 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 104/141] 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 5057ae558e..87ed23f368 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 0140d1ac4d..b44ca2ff01 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 0eba602cc9..809b804282 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 240145118e..f8e3459df9 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 105/141] bugfix: typo --- compiler/types.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/types.nim b/compiler/types.nim index d7148f1106..e6178f446b 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 106/141] 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 0000000000..fb5b3b9ced --- /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 107/141] 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 c0fc4131a7..946ef1fdaa 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 108/141] 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 6277b2dc62..108979739a 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 b594c00a9e..a9029442ef 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 24f5b2eeee..2a7856b4aa 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 7cbf2eb3dd..fe60dd3ba6 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 109/141] 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 108979739a..0a3c17e8e4 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 110/141] 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 0a3c17e8e4..ed259636e0 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 111/141] 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 ed259636e0..8411c4a4bd 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 112/141] 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 4ca0c79e03..c507f5e1cd 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 113/141] 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 2a685f3fb4..582b3c9604 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 114/141] 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 115/141] 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 8cdccda018..74aaa1a590 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 116/141] 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 87ed23f368..8da753d048 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 117/141] 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 fb5b3b9ced..94cb8e3550 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 118/141] 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 a239d4ef28..0ee108d485 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 8411c4a4bd..f9b143bce8 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 a9029442ef..687db1b18a 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 119/141] 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 72689c8795..30beea29cb 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 687db1b18a..591c5ade84 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 120/141] 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 031217b904..3542a0694f 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 121/141] 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 89bb92f9a3..bfecc569a7 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 122/141] 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 4f642e6260..69d41c5629 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 0000000000..1ed86ca053 --- /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 0000000000..f22598aba6 --- /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 4bee45516a..5cbe2fe8ba 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 123/141] 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 bb018bc1e3..30cc9026b4 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 124/141] 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 442dd12123..0f6c247165 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 e97015946d..f7982127fa 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 125/141] 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 bd8bdb30b1..61b8ca7955 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 8da753d048..c3a28527eb 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 6a14916829..fb19407416 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 69d41c5629..102ebc3865 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 5cbe2fe8ba..974e18360d 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 126/141] 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 63737d583c..b9d6aec7b8 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 127/141] 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 0f6c247165..9dc51567dc 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 128/141] 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 9dc51567dc..bf65d26dee 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 129/141] 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 bf65d26dee..627c3d5ea6 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 f54556518eba317dbe5a4aa86086fa2de8671f7d Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 4 Mar 2014 08:29:41 +0100 Subject: [PATCH 130/141] 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 509c815c16..d68cb91c02 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 131/141] removed dead code --- compiler/ccgmerge.nim | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 5b04f13588..2d27257ced 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 132/141] 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 1129ecbefe..da1673ca40 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 e6178f446b..edf5ab47b1 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 974e18360d..ba062a9495 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 133/141] 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 627c3d5ea6..9bb4838e05 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 134/141] 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 0000000000..99a56d1616 --- /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 aa92669d9219816f9cd9036b4977ddbdd16d3c5d Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Mar 2014 02:14:53 +0100 Subject: [PATCH 135/141] 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 94cb8e3550..07e932b287 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 30beea29cb..102fc30244 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 591c5ade84..1233944362 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 0000000000..085bb6fb6d --- /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 ba062a9495..a13c86b8ff 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 136/141] 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 c8d9e353a9..b2b3ceb6d0 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 c0551ba2f8..a806ccdce6 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 b32394d8d1..355da24073 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 137/141] 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 93a7b7c722..fcb5b67314 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 d0fccdaff8..b3d9c17ce7 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 138/141] 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 3eaef0b355..38e04233fc 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 a066e4e5c5a8ed5967316a08803af61a3dc66d34 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Mar 2014 20:05:05 +0100 Subject: [PATCH 139/141] 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 e4abe11a00..555e1af100 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 140/141] 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 61b8ca7955..93630979b6 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 8cb423688c..40ebcbfa81 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 673e888fe5..156ebee5ec 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 c79fda13e2..ddf2075b4c 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 4304e5fb76..bf3564016d 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 1c8e4516cd..f4a0c59f4d 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 f3602dc58c..98219360e1 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 19d161adf0..f73dca1900 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 a13c86b8ff..a67ff51727 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 141/141] 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 869658727e..5e27bd83aa 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 f9ac1b66d0..0001cece72 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