diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1a5334a98a..4fcbeeec2f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -672,9 +672,13 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = expr(p, e.sons[0], d) else: var a: TLoc - initLocExprSingleUse(p, e.sons[0], a) + let typ = skipTypes(e.sons[0].typ, abstractInst) + if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr: + initLocExprSingleUse(p, e[0][0], d) + return + else: + initLocExprSingleUse(p, e.sons[0], a) if d.k == locNone: - let typ = skipTypes(a.t, abstractInst) # dest = *a; <-- We do not know that 'dest' is on the heap! # It is completely wrong to set 'd.s' here, unless it's not yet # been assigned to. @@ -689,9 +693,9 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = return of tyPtr: d.s = OnUnknown # BUGFIX! - else: internalError(e.info, "genDeref " & $a.t.kind) + else: + internalError(e.info, "genDeref " & $typ.kind) elif p.module.compileToCpp: - let typ = skipTypes(a.t, abstractInst) if typ.kind == tyVar and tfVarIsPtr notin typ.flags and e.kind == nkHiddenDeref: putIntoDest(p, d, e.typ, rdLoc(a), a.s) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 8af6239a58..2da7db9004 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -211,7 +211,6 @@ proc escapeJSString(s: string): string = of '\e': result.add("\\e") of '\v': result.add("\\v") of '\\': result.add("\\\\") - of '\'': result.add("\\'") of '\"': result.add("\\\"") else: add(result, c) result.add("\"") @@ -464,6 +463,22 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mSubU: binaryUintExpr(p, n, r, "-") of mMulU: binaryUintExpr(p, n, r, "*") of mDivU: binaryUintExpr(p, n, r, "/") + of mDivI: + if p.target == targetPHP: + var x, y: TCompRes + gen(p, n.sons[1], x) + gen(p, n.sons[2], y) + r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc] + else: + arithAux(p, n, r, op, jsOps) + of mModI: + if p.target == targetPHP: + var x, y: TCompRes + gen(p, n.sons[1], x) + gen(p, n.sons[2], y) + r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc] + else: + arithAux(p, n, r, op, jsOps) of mShrI: var x, y: TCompRes gen(p, n.sons[1], x) @@ -767,10 +782,17 @@ proc generateHeader(p: PProc, typ: PType): Rope = add(result, name) add(result, "_Idx") elif not (i == 1 and param.name.s == "this"): - if param.typ.skipTypes({tyGenericInst}).kind == tyVar: + let k = param.typ.skipTypes({tyGenericInst}).kind + if k in { tyVar, tyRef, tyPtr, tyPointer }: add(result, "&") add(result, "$") add(result, name) + # XXX I think something like this is needed for PHP to really support + # ptr "inside" strings and seq + #if mapType(param.typ) == etyBaseIndex: + # add(result, ", $") + # add(result, name) + # add(result, "_Idx") const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, @@ -953,10 +975,12 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = if n.sons[0].kind in nkCallKinds+{nkStrLit..nkTripleStrLit}: useMagic(p, "nimAt") if ty.kind in {tyString, tyCString}: + # XXX this needs to be more like substr($1,$2) r.res = "ord(nimAt($1, $2))" % [r.address, r.res] else: r.res = "nimAt($1, $2)" % [r.address, r.res] elif ty.kind in {tyString, tyCString}: + # XXX this needs to be more like substr($1,$2) r.res = "ord($1[$2])" % [r.address, r.res] else: r.res = "$1[$2]" % [r.address, r.res] @@ -1963,6 +1987,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genInfixCall(p, n, r) else: genCall(p, n, r) + of nkClosure: gen(p, n[0], r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar: genTupleConstr(p, n, r) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 69a0fea2aa..0032b97df9 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -263,7 +263,7 @@ template eatChar(L: var TLexer, t: var TToken) = add(t.literal, L.buf[L.bufpos]) inc(L.bufpos) -proc getNumber(L: var TLexer): TToken = +proc getNumber(L: var TLexer, result: var TToken) = proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]) = var pos = L.bufpos # use registers for pos, buf var buf = L.buf @@ -1061,7 +1061,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = getCharacter(L, tok) tok.tokType = tkCharLit of '0'..'9': - tok = getNumber(L) + getNumber(L, tok) else: if c in OpChars: getOperator(L, tok) diff --git a/compiler/sem.nim b/compiler/sem.nim index e09d49f882..97a20a4dab 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -67,9 +67,12 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; proc typeMismatch(n: PNode, formal, actual: PType) = if formal.kind != tyError and actual.kind != tyError: + let named = typeToString(formal) + let desc = typeToString(formal, preferDesc) + let x = if named == desc: named else: named & " = " & desc localError(n.info, errGenerated, msgKindToString(errTypeMismatch) & typeToString(actual) & ") " & - `%`(msgKindToString(errButExpectedX), [typeToString(formal)])) + `%`(msgKindToString(errButExpectedX), [x])) proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = if arg.typ.isNil: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5d16f2fbaf..910267e579 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -620,7 +620,8 @@ proc semFor(c: PContext, n: PNode): PNode = result.kind = nkParForStmt else: result = semForFields(c, n, call.sons[0].sym.magic) - elif isCallExpr and call.sons[0].typ.callConv == ccClosure: + elif isCallExpr and call.sons[0].typ.callConv == ccClosure and + tfIterator in call.sons[0].typ.flags: # first class iterator: result = semForVars(c, n) elif not isCallExpr or call.sons[0].kind != nkSym or diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 62d02fe10e..ba17cc307a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -831,9 +831,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result.rawAddSon paramType.lastSon return addImplicitGeneric(result) - result = instGenericContainer(c, paramType.sym.info, result, + let x = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) - result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) + result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, x]) + #result = newTypeS(tyCompositeTypeClass, c) + #for i in 0..", pure, final.} = object - ident*: cuint ## identifier for this event (uintptr_t) - filter*: cshort ## filter for event - flags*: cushort ## general flags - fflags*: cuint ## filter-specific flags - data*: cuint ## filter-specific data (intptr_t) - #udata*: ptr void ## opaque user data identifier + header: """#include + #include + #include """, pure, final.} = object + ident* : uint ## identifier for this event (uintptr_t) + filter* : cshort ## filter for event + flags* : cushort ## general flags + fflags* : cuint ## filter-specific flags + data* : int ## filter-specific data (intptr_t) + udata* : pointer ## opaque user data identifier proc kqueue*(): cint {.importc: "kqueue", header: "".} ## Creates new queue and returns its descriptor. @@ -65,7 +151,7 @@ proc kevent*(kqFD: cint, {.importc: "kevent", header: "".} ## Manipulates queue for given ``kqFD`` descriptor. -proc EV_SET*(event: ptr KEvent, ident: cuint, filter: cshort, flags: cushort, - fflags: cuint, data: cuint, udata: ptr void) +proc EV_SET*(event: ptr KEvent, ident: uint, filter: cshort, flags: cushort, + fflags: cuint, data: int, udata: pointer) {.importc: "EV_SET", header: "".} ## Fills event with given data. diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 40b48f9926..4c7b817cb0 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -486,11 +486,11 @@ type l_onoff*: cint ## Indicates whether linger option is enabled. l_linger*: cint ## Linger time, in seconds. - InPort* = int16 ## unsigned! - InAddrScalar* = int32 ## unsigned! + InPort* = uint16 + InAddrScalar* = uint32 InAddrT* {.importc: "in_addr_t", pure, final, - header: "".} = int32 ## unsigned! + header: "".} = uint32 InAddr* {.importc: "struct in_addr", pure, final, header: "".} = object ## struct in_addr diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index b72face918..0e3824a817 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -508,13 +508,39 @@ template foldl*(sequence, operation: expr): expr = ## assert subtraction == -15, "Subtraction is (((5)-9)-11)" ## assert multiplication == 495, "Multiplication is (((5)*9)*11)" ## assert concatenation == "nimiscool" - assert sequence.len > 0, "Can't fold empty sequences" - var result {.gensym.}: type(sequence[0]) - result = sequence[0] - for i in 1.. 0, "Can't fold empty sequences" + var result {.gensym.}: type(s[0]) + result = s[0] + for i in 1.. 0, "Can't fold empty sequences" - var result {.gensym.}: type(sequence[0]) - result = sequence[sequence.len - 1] - for i in countdown(sequence.len - 2, 0): + let s = sequence + assert s.len > 0, "Can't fold empty sequences" + var result {.gensym.}: type(s[0]) + result = sequence[s.len - 1] + for i in countdown(s.len - 2, 0): let - a {.inject.} = sequence[i] + a {.inject.} = s[i] b {.inject.} = result result = operation result diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim new file mode 100644 index 0000000000..e93ceb02fc --- /dev/null +++ b/lib/pure/collections/sharedlist.nim @@ -0,0 +1,95 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Shared list support. + +{.push stackTrace:off.} + +import + locks + +const + ElemsPerNode = 100 + +type + SharedListNode[A] = ptr object + next: SharedListNode[A] + dataLen: int + d: array[ElemsPerNode, A] + + SharedList*[A] = object ## generic shared list + head, tail: SharedListNode[A] + lock*: Lock + +template withLock(t, x: untyped) = + acquire(t.lock) + x + release(t.lock) + +proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = + ## iterates over the list. If 'action' returns true, the + ## current item is removed from the list. + withLock(x): + var n = x.head + while n != nil: + var i = 0 + while i < n.dataLen: + # action can add new items at the end, so release the lock: + release(x.lock) + if action(n.d[i]): + acquire(x.lock) + let t = x.tail + n.d[i] = t.d[t.dataLen] + dec t.dataLen + else: + acquire(x.lock) + inc i + n = n.next + +iterator items*[A](x: var SharedList[A]): A = + withLock(x): + var it = x.head + while it != nil: + for i in 0..it.dataLen-1: + yield it.d[i] + it = it.next + +proc add*[A](x: var SharedList[A]; y: A) = + withLock(x): + var node: SharedListNode[A] + if x.tail == nil or x.tail.dataLen == ElemsPerNode: + node = cast[type node](allocShared0(sizeof(node[]))) + node.next = x.tail + x.tail = node + if x.head == nil: x.head = node + else: + node = x.tail + node.d[node.dataLen] = y + inc(node.dataLen) + +proc initSharedList*[A](): SharedList[A] = + initLock result.lock + result.head = nil + result.tail = nil + +proc clear*[A](t: var SharedList[A]) = + withLock(t): + var it = t.head + while it != nil: + let nxt = it.next + deallocShared(it) + it = nxt + t.head = nil + t.tail = nil + +proc deinitSharedList*[A](t: var SharedList[A]) = + clear(t) + deinitLock t.lock + +{.pop.} diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index beafe11097..e4ec05b1c7 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -129,4 +129,7 @@ template delImpl() {.dirty, immediate.} = r = t.data[i].hcode and msk # "home" location of key@i if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): break - shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop + when defined(js): + t.data[j] = t.data[i] + else: + shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index aa55b5ade7..f602ce31da 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -92,6 +92,10 @@ type {.deprecated: [TLevel: Level, PLogger: Logger, PConsoleLogger: ConsoleLogger, PFileLogger: FileLogger, PRollingFileLogger: RollingFileLogger].} +var + level {.threadvar.}: Level ## global log filter + handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels + proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): string = ## Format a log message using the ``frmt`` format string, ``level`` and varargs. ## See the module documentation for the format string syntax. @@ -133,13 +137,13 @@ method log*(logger: Logger, level: Level, args: varargs[string, `$`]) {. method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = ## Logs to the console using ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: writeLine(stdout, substituteLog(logger.fmtStr, level, args)) if level in {lvlError, lvlFatal}: flushFile(stdout) method log*(logger: FileLogger, level: Level, args: varargs[string, `$`]) = ## Logs to a file using ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: writeLine(logger.file, substituteLog(logger.fmtStr, level, args)) if level in {lvlError, lvlFatal}: flushFile(logger.file) @@ -224,7 +228,7 @@ proc rotate(logger: RollingFileLogger) = method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) = ## Logs to a file using rolling ``logger`` only. - if level >= logger.levelThreshold: + if level >= logging.level and level >= logger.levelThreshold: if logger.curLine >= logger.maxLines: logger.file.close() rotate(logger) @@ -238,9 +242,6 @@ method log*(logger: RollingFileLogger, level: Level, args: varargs[string, `$`]) # -------- -var level {.threadvar.}: Level ## global log filter -var handlers {.threadvar.}: seq[Logger] ## handlers with their own log levels - proc logLoop(level: Level, args: varargs[string, `$`]) = for logger in items(handlers): if level >= logger.levelThreshold: diff --git a/lib/pure/math.nim b/lib/pure/math.nim index b0104336e1..84c8d3b11a 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -44,6 +44,7 @@ when not defined(js) and not defined(nimscript): const PI* = 3.1415926535897932384626433 ## the circle constant PI (Ludolph's number) + TAU* = 2.0 * PI ## the circle constant TAU (= 2 * PI) E* = 2.71828182845904523536028747 ## Euler's number MaxFloat64Precision* = 16 ## maximum number of meaningful digits diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 3951b46a3f..043d6d80a3 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -219,31 +219,67 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, proc dealloc*(ai: ptr AddrInfo) = freeaddrinfo(ai) -proc ntohl*(x: int32): int32 = - ## Converts 32-bit integers from network to host byte order. +proc ntohl*(x: uint32): uint32 = + ## Converts 32-bit unsigned 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) + else: result = (x shr 24'u32) or + (x shr 8'u32 and 0xff00'u32) or + (x shl 8'u32 and 0xff0000'u32) or + (x shl 24'u32) -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. +template ntohl*(x: int32): expr {.deprecated.} = + ## 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. + ## **Warning**: This template is deprecated since 0.14.0, IPv4 + ## addresses are now treated as unsigned integers. Please use the unsigned + ## version of this template. + cast[int32](ntohl(cast[uint32](x))) + +proc ntohs*(x: uint16): uint16 = + ## Converts 16-bit unsigned 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) + else: result = (x shr 8'u16) or (x shl 8'u16) -template htonl*(x: int32): expr = +template ntohs*(x: int16): expr {.deprecated.} = + ## 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. + ## **Warning**: This template is deprecated since 0.14.0, where port + ## numbers became unsigned integers. Please use the unsigned version of + ## this template. + cast[int16](ntohs(cast[uint16](x))) + +template htonl*(x: int32): expr {.deprecated.} = ## 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. + ## **Warning**: This template is deprecated since 0.14.0, IPv4 + ## addresses are now treated as unsigned integers. Please use the unsigned + ## version of this template. nativesockets.ntohl(x) -template htons*(x: int16): expr = - ## Converts 16-bit positive integers from host to network byte order. +template htonl*(x: uint32): expr = + ## Converts 32-bit unsigned 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. + nativesockets.ntohl(x) + +template htons*(x: int16): expr {.deprecated.} = + ## Converts 16-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 2-byte swap operation. + ## **Warning**: This template is deprecated since 0.14.0, where port + ## numbers became unsigned integers. Please use the unsigned version of + ## this template. + nativesockets.ntohs(x) + +template htons*(x: uint16): expr = + ## Converts 16-bit unsigned 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. nativesockets.ntohs(x) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index b9764edd3e..330682ca90 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -368,7 +368,7 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. name.sin_family = toInt(AF_INET).int16 else: name.sin_family = toInt(AF_INET) - name.sin_port = htons(int16(port)) + name.sin_port = htons(port.uint16) name.sin_addr.s_addr = htonl(INADDR_ANY) if bindAddr(socket.fd, cast[ptr SockAddr](addr(name)), sizeof(name).SockLen) < 0'i32: diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 0173858253..470559e173 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1350,15 +1350,12 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the filename of the application's executable. ## ## This procedure will resolve symlinks. - ## - ## **Note**: This does not work reliably on BSD. # Linux: /proc//exe # Solaris: # /proc//object/a.out (filename only) # /proc//path/a.out (complete pathname) - # *BSD (and maybe Darwin too): - # /proc//file + # FreeBSD: /proc//file when defined(windows): when useWinUnicode: var buf = newWideCString("", 256) @@ -1368,15 +1365,6 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = result = newString(256) var len = getModuleFileNameA(0, result, 256) setlen(result, int(len)) - elif defined(linux) or defined(aix): - result = getApplAux("/proc/self/exe") - if result.len == 0: result = getApplHeuristic() - elif defined(solaris): - result = getApplAux("/proc/" & $getpid() & "/path/a.out") - if result.len == 0: result = getApplHeuristic() - elif defined(freebsd): - result = getApplAux("/proc/" & $getpid() & "/file") - if result.len == 0: result = getApplHeuristic() elif defined(macosx): var size: cuint32 getExecPath1(nil, size) @@ -1386,9 +1374,15 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = if result.len > 0: result = result.expandFilename else: + when defined(linux) or defined(aix) or defined(netbsd): + result = getApplAux("/proc/self/exe") + elif defined(solaris): + result = getApplAux("/proc/" & $getpid() & "/path/a.out") + elif defined(freebsd): + result = getApplAux("/proc/" & $getpid() & "/file") # little heuristic that may work on other POSIX-like systems: - result = string(getEnv("_")) - if result.len == 0: result = getApplHeuristic() + if result.len == 0: + result = getApplHeuristic() proc getApplicationFilename*(): string {.rtl, extern: "nos$1", deprecated.} = ## Returns the filename of the application's executable. @@ -1404,7 +1398,6 @@ proc getApplicationDir*(): string {.rtl, extern: "nos$1", deprecated.} = proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = ## Returns the directory of the application's executable. - ## **Note**: This does not work reliably on BSD. result = splitFile(getAppFilename()).dir proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect].} = diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 72c64befce..6fd05dc4bd 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -164,7 +164,7 @@ proc `-` *[T](x: Rational[T], y: T): Rational[T] = proc `-` *[T](x: T, y: Rational[T]): Rational[T] = ## Subtract rational `y` from int `x`. - result.num = - x * y.den + y.num + result.num = x * y.den - y.num result.den = y.den proc `-=` *[T](x: var Rational[T], y: Rational[T]) = diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index df7071642b..6e97237e04 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -134,11 +134,16 @@ proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} = ## Converts a string to a rope. if s.len == 0: result = nil - elif cacheEnabled: - result = insertInCache(s, cache) - cache = result else: - result = newRope(s) + when nimvm: + # No caching in VM context + result = newRope(s) + else: + if cacheEnabled: + result = insertInCache(s, cache) + cache = result + else: + result = newRope(s) proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = ## Converts an int to a rope. diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index a446f85b41..f2c1e77e18 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -882,7 +882,7 @@ proc abbrev*(s: string, possibilities: openArray[string]): int = # --------------------------------------------------------------------------- -proc join*(a: openArray[string], sep: string): string {. +proc join*(a: openArray[string], sep: string = ""): string {. noSideEffect, rtl, extern: "nsuJoinSep".} = ## Concatenates all strings in `a` separating them with `sep`. if len(a) > 0: @@ -896,16 +896,15 @@ proc join*(a: openArray[string], sep: string): string {. else: result = "" -proc join*(a: openArray[string]): string {. - noSideEffect, rtl, extern: "nsuJoin".} = - ## Concatenates all strings in `a`. - if len(a) > 0: - var L = 0 - for i in 0..high(a): inc(L, a[i].len) - result = newStringOfCap(L) - for i in 0..high(a): add(result, a[i]) - else: - result = "" +proc join*[T: not string](a: openArray[T], sep: string = ""): string {. + noSideEffect, rtl.} = + ## Converts all elements in `a` to strings using `$` and concatenates them + ## with `sep`. + result = "" + for i, x in a: + if i > 0: + add(result, sep) + add(result, $x) type SkipTable = array[char, int] @@ -1721,3 +1720,8 @@ when isMainModule: doAssert(not isUpper("AAcc")) doAssert(not isUpper("A#$")) doAssert(unescape(r"\x013", "", "") == "\x013") + + doAssert join(["foo", "bar", "baz"]) == "foobarbaz" + doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" + doAssert join([1, 2, 3]) == "123" + doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" diff --git a/lib/system.nim b/lib/system.nim index 241f552246..fefabe53f3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1288,6 +1288,10 @@ const hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own taintMode = compileOption("taintmode") +when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): + # tcc doesn't support TLS + {.error: "``--tlsEmulation:on`` must be used when using threads with tcc backend".} + when defined(boehmgc): when defined(windows): const boehmLib = "boehmgc.dll" diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 158fe91bc1..a79dcb1529 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -197,6 +197,51 @@ when defined(windows) and not someGcc: proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = interlockedCompareExchange(p, cast[int](newValue), cast[int](oldValue)) != 0 # XXX fix for 64 bit build +elif defined(tcc) and not defined(windows): + when defined(amd64): + {.emit:""" +static int __tcc_cas(int *ptr, int oldVal, int newVal) +{ + unsigned char ret; + __asm__ __volatile__ ( + " lock\n" + " cmpxchgq %2,%1\n" + " sete %0\n" + : "=q" (ret), "=m" (*ptr) + : "r" (newVal), "m" (*ptr), "a" (oldVal) + : "memory"); + + if (ret) + return 0; + else + return 1; +} +""".} + else: + assert sizeof(int) == 4 + {.emit:""" +static int __tcc_cas(int *ptr, int oldVal, int newVal) +{ + unsigned char ret; + __asm__ __volatile__ ( + " lock\n" + " cmpxchgl %2,%1\n" + " sete %0\n" + : "=q" (ret), "=m" (*ptr) + : "r" (newVal), "m" (*ptr), "a" (oldVal) + : "memory"); + + if (ret) + return 0; + else + return 1; +} +""".} + + proc tcc_cas(p: ptr int; oldValue, newValue: int): bool + {.importc: "__tcc_cas", nodecl.} + proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool = + tcc_cas(cast[ptr int](p), cast[int](oldValue), cast[int](newValue)) else: # this is valid for GCC and Intel C++ proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool @@ -207,7 +252,7 @@ else: when (defined(x86) or defined(amd64)) and someGcc: proc cpuRelax* {.inline.} = {.emit: """asm volatile("pause" ::: "memory");""".} -elif someGcc: +elif someGcc or defined(tcc): proc cpuRelax* {.inline.} = {.emit: """asm volatile("" ::: "memory");""".} elif (defined(x86) or defined(amd64)) and defined(vcc): diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index 6dc8999d19..ba5e4bbeb6 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -23,16 +23,16 @@ proc rawWrite(f: File, s: string) = proc nimLoadLibraryError(path: string) = # carefully written to avoid memory allocation: - stdout.rawWrite("could not load: ") - stdout.rawWrite(path) - stdout.rawWrite("\n") + stderr.rawWrite("could not load: ") + stderr.rawWrite(path) + stderr.rawWrite("\n") quit(1) proc procAddrError(name: cstring) {.noinline.} = # carefully written to avoid memory allocation: - stdout.rawWrite("could not import: ") - stdout.write(name) - stdout.rawWrite("\n") + stderr.rawWrite("could not import: ") + stderr.write(name) + stderr.rawWrite("\n") quit(1) # this code was inspired from Lua's source code: @@ -71,7 +71,7 @@ when defined(posix): when defined(nimDebugDlOpen): let error = dlerror() if error != nil: - c_fprintf(c_stdout, "%s\n", error) + c_fprintf(c_stderr, "%s\n", error) proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = result = dlsym(lib, name) diff --git a/lib/system/gc_stack.nim b/lib/system/gc_stack.nim new file mode 100644 index 0000000000..3a5c5594a4 --- /dev/null +++ b/lib/system/gc_stack.nim @@ -0,0 +1,382 @@ +# +# Nim's Runtime Library +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# "Stack GC" for embedded devices or ultra performance requirements. + +include osalloc + +# We manage memory as a thread local stack. Since the allocation pointer +# is detached from the control flow pointer, this model is vastly more +# useful than the traditional programming model while almost as safe. +# Individual objects can also be deleted but no coalescing is performed. +# Stacks can also be moved from one thread to another. + +# We also support 'finalizers'. + +type + Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} + # A ref type can have a finalizer that is called before the object's + # storage is freed. + + AlignType = BiggestFloat + ObjHeader = object + typ: PNimType + nextFinal: ptr ObjHeader # next object with finalizer + + Hole = object # stacks can have holes. Otherwise 'growObj' would be insane. + zeroTyp: pointer # overlaid with 'typ' field. Always 'nil'. + size: int # size of the free slot + + Chunk = ptr BaseChunk + BaseChunk = object + next: Chunk + size: int + head, last: ptr ObjHeader # first and last object in chunk that + # has a finalizer attached to it + +type + StackPtr = object + chunk: pointer + remaining: int + current: Chunk + + MemRegion* = object + remaining: int + chunk: pointer + head, last: Chunk + nextChunkSize, totalSize: int + hole: ptr Hole # we support individual freeing + lock: SysLock + +var + region {.threadVar.}: MemRegion + +template withRegion*(r: MemRegion; body: untyped) = + let oldRegion = region + region = r + try: + body + finally: + region = oldRegion + +template inc(p: pointer, s: int) = + p = cast[pointer](cast[int](p) +% s) + +template `+!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) +% s) + +template `-!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) +% s) + +proc allocSlowPath(r: var MemRegion; size: int) = + # we need to ensure that the underlying linked list + # stays small. Say we want to grab 16GB of RAM with some + # exponential growth function. So we allocate 16KB, then + # 32 KB, 64 KB, 128KB, 256KB, 512KB, 1MB, 2MB, 4MB, + # 8MB, 16MB, 32MB, 64MB, 128MB, 512MB, 1GB, 2GB, 4GB, 8GB, + # 16GB --> list contains only 20 elements! That's reasonable. + if (r.totalSize and 1) == 0: + r.nextChunkSize = + if r.totalSize < 64 * 1024: PageSize*4 + else: r.nextChunkSize*2 + var s = align(size+sizeof(BaseChunk), PageSize) + var fresh: Chunk + if s > r.nextChunkSize: + fresh = cast[Chunk](osAllocPages(s)) + else: + fresh = cast[Chunk](osTryAllocPages(r.nextChunkSize)) + if fresh == nil: + fresh = cast[Chunk](osAllocPages(s)) + # lowest bit in totalSize is the "don't increase nextChunkSize" + inc r.totalSize + else: + s = r.nextChunkSize + fresh.size = s + fresh.final = nil + r.totalSize += s + let old = r.last + if old == nil: + r.head = fresh + else: + r.last.next = fresh + r.chunk = fresh +! sizeof(BaseChunk) + r.last = fresh + r.remaining = s - sizeof(BaseChunk) + +proc alloc(r: var MemRegion; size: int): pointer {.inline.} = + if unlikely(r.remaining < size): allocSlowPath(r, size) + dec(r.remaining, size) + result = r.chunk + inc r.chunk, size + +proc runFinalizers(c: Chunk) = + var it = c.head + while it != nil: + # indivually freed objects with finalizer stay in the list, but + # their typ is nil then: + if it.typ != nil and it.typ.finalizer != nil: + (cast[Finalizer](cell.typ.finalizer))(cell+!sizeof(ObjHeader)) + it = it.next + +proc dealloc(r: var MemRegion; p: pointer) = + let it = p-!sizeof(ObjHeader) + if it.typ != nil and it.typ.finalizer != nil: + (cast[Finalizer](cell.typ.finalizer))(p) + it.typ = nil + +proc deallocAll(head: Chunk) = + var it = head + while it != nil: + runFinalizers(it) + osDeallocPages(it, it.size) + it = it.next + +proc deallocAll*(r: var MemRegion) = + deallocAll(r.head) + zeroMem(addr r, sizeof r) + +proc obstackPtr*(r: MemRegion): StackPtr = + result.chunk = r.chunk + result.remaining = r.remaining + result.current = r.last + +proc setObstackPtr*(r: MemRegion; sp: StackPtr) = + # free everything after 'sp': + if sp.current != nil: + deallocAll(sp.current.next) + r.chunk = sp.chunk + r.remaining = sp.remaining + r.last = sp.current + +proc joinRegion*(dest: var MemRegion; src: MemRegion) = + # merging is not hard. + if dest.head.isNil: + dest.head = src.head + else: + dest.last.next = src.head + dest.last = src.last + dest.chunk = src.chunk + dest.remaining = src.remaining + dest.nextChunkSize = max(dest.nextChunkSize, src.nextChunkSize) + dest.totalSize += src.totalSize + if dest.hole.size < src.hole.size: + dest.hole = src.hole + +proc isOnHeap*(r: MemRegion; p: pointer): bool = + # the last chunk is the largest, so check it first. It's also special + # in that contains the current bump pointer: + if r.last >= p and p < r.chunk: + return true + var it = r.head + while it != r.last: + if it >= p and p <= it+!it.size: return true + it = it.next + +proc isInteriorPointer(r: MemRegion; p: pointer): pointer = + discard " we cannot patch stack pointers anyway!" + +type + PointerStackChunk = object + next, prev: ptr PointerStackChunk + len: int + data: array[128, pointer] + +template head(s: PointerStackChunk): untyped = s.prev +template tail(s: PointerStackChunk): untyped = s.next + +include chains + +proc push(r: var MemRegion; s: var PointerStackChunk; x: pointer) = + if s.len < high(s.data): + s.data[s.len] = x + inc s.len + else: + let fresh = cast[ptr PointerStackChunk](alloc(r, sizeof(PointerStackChunk))) + fresh.len = 1 + fresh.data[0] = x + fresh.next = nil + fresh.prev = nil + append(s, fresh) + + +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, mt: PNimType) {.benign.} +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, n: ptr TNimNode) {.benign.} = + var + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) + case n.kind + of nkSlot: + genericDeepCopyAux(cast[pointer](d +% n.offset), + cast[pointer](s +% n.offset), n.typ) + of nkList: + for i in 0..n.len-1: + genericDeepCopyAux(dest, src, n.sons[i]) + of nkCase: + var dd = selectBranch(dest, n) + var m = selectBranch(src, n) + # reset if different branches are in use; note different branches also + # imply that's not self-assignment (``x = x``)! + if m != dd and dd != nil: + genericResetAux(dest, dd) + copyMem(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), + n.typ.size) + if m != nil: + genericDeepCopyAux(dest, src, m) + of nkNone: sysAssert(false, "genericDeepCopyAux") + +proc copyDeepString(dr: var MemRegion; stack: var PointerStackChunk; src: NimString): NimString {.inline.} = + result = rawNewStringNoInit(dr, src.len) + result.len = src.len + c_memcpy(result.data, src.data, src.len + 1) + +proc genericDeepCopyAux(dr: var MemRegion; stack: var PointerStackChunk; + dest, src: pointer, mt: PNimType) = + var + d = cast[ByteAddress](dest) + s = cast[ByteAddress](src) + sysAssert(mt != nil, "genericDeepCopyAux 2") + case mt.kind + of tyString: + var x = cast[PPointer](dest) + var s2 = cast[PPointer](s)[] + if s2 == nil: + x[] = nil + else: + x[] = copyDeepString(cast[NimString](s2)) + of tySequence: + var s2 = cast[PPointer](src)[] + var seq = cast[PGenericSeq](s2) + var x = cast[PPointer](dest) + if s2 == nil: + x[] = nil + return + sysAssert(dest != nil, "genericDeepCopyAux 3") + x[] = newSeq(mt, seq.len) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) + for i in 0..seq.len-1: + genericDeepCopyAux(dr, stack, + cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), + cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% + GenericSeqSize), + mt.base) + of tyObject: + # we need to copy m_type field for tyObject, as it could be empty for + # sequence reallocations: + var pint = cast[ptr PNimType](dest) + pint[] = cast[ptr PNimType](src)[] + if mt.base != nil: + genericDeepCopyAux(dr, stack, dest, src, mt.base) + genericDeepCopyAux(dr, stack, dest, src, mt.node) + of tyTuple: + genericDeepCopyAux(dr, stack, dest, src, mt.node) + of tyArray, tyArrayConstr: + for i in 0..(mt.size div mt.base.size)-1: + genericDeepCopyAux(dr, stack, + cast[pointer](d +% i*% mt.base.size), + cast[pointer](s +% i*% mt.base.size), mt.base) + of tyRef: + let s2 = cast[PPointer](src)[] + if s2 == nil: + cast[PPointer](dest)[] = nil + else: + # we modify the header of the cell temporarily; instead of the type + # field we store a forwarding pointer. XXX This is bad when the cloning + # fails due to OOM etc. + let x = usrToCell(s2) + let forw = cast[int](x.typ) + if (forw and 1) == 1: + # we stored a forwarding pointer, so let's use that: + let z = cast[pointer](forw and not 1) + unsureAsgnRef(cast[PPointer](dest), z) + else: + let realType = x.typ + let z = newObj(realType, realType.base.size) + + unsureAsgnRef(cast[PPointer](dest), z) + x.typ = cast[PNimType](cast[int](z) or 1) + genericDeepCopyAux(dr, stack, z, s2, realType.base) + x.typ = realType + else: + copyMem(dest, src, mt.size) + +proc joinAliveDataFromRegion*(dest: var MemRegion; src: var MemRegion; + root: pointer): pointer = + # we mark the alive data and copy only alive data over to 'dest'. + # This is O(liveset) but it nicely compacts memory, so it's fine. + # We use the 'typ' field as a forwarding pointer. The forwarding + # pointers have bit 0 set, so we can disambiguate them. + # We allocate a temporary stack in 'src' that we later free: + var s: PointerStackChunk + s.len = 1 + s.data[0] = root + while s.len > 0: + var p: pointer + if s.tail == nil: + p = s.data[s.len-1] + dec s.len + else: + p = s.tail.data[s.tail.len-1] + dec s.tail.len + if s.tail.len == 0: + unlink(s, s.tail) + +proc rawNewObj(r: var MemRegion, typ: PNimType, size: int): pointer = + var res = cast[ptr ObjHeader](alloc(r, size + sizeof(ObjHeader))) + res.typ = typ + if typ.finalizer != nil: + res.nextFinal = r.chunk.head + r.chunk.head = res + result = res +! sizeof(ObjHeader) + +proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, region) + zeroMem(result, size) + when defined(memProfiler): nimProfile(size) + +proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, region) + when defined(memProfiler): nimProfile(size) + +proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = + let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) + result = newObj(typ, size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + +proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = + result = rawNewObj(typ, size, gch) + zeroMem(result, size) + +proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = + let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) + result = newObj(typ, size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + +proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = + collectCT(gch) + var ol = usrToCell(old) + sysAssert(ol.typ != nil, "growObj: 1") + gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") + + var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell))) + var elemSize = 1 + if ol.typ.kind != tyString: elemSize = ol.typ.base.size + + var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize + copyMem(res, ol, oldsize + sizeof(Cell)) + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), + newsize-oldsize) + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + result = cellToUsr(res) + +proc growObj(old: pointer, newsize: int): pointer {.rtl.} = + result = growObj(old, newsize, region) + diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index c96f2f9588..1b98883b96 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -263,9 +263,7 @@ proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} = proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = when defined(nimphp): asm """ - $result = array(); - for($i = 0; $i < `len`; $i++) $result[] = chr(0); - return $result; + return str_repeat(chr(0),`len`); """ else: asm """ diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index e0d14737e2..5e576f0a35 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -427,7 +427,7 @@ elif defined(nogc) and defined(useMalloc): proc initGC() = discard proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = - result = alloc(size) + result = alloc0(size) proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize)) cast[PGenericSeq](result).len = len diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index cc1a282133..78410d7160 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -94,7 +94,7 @@ elif defined(posix): proc mmap(adr: pointer, len: int, prot, flags, fildes: cint, off: int): pointer {.header: "".} - proc munmap(adr: pointer, len: int) {.header: "".} + proc munmap(adr: pointer, len: int): cint {.header: "".} proc osAllocPages(size: int): pointer {.inline.} = result = mmap(nil, size, PROT_READ or PROT_WRITE, @@ -108,7 +108,7 @@ elif defined(posix): if result == cast[pointer](-1): result = nil proc osDeallocPages(p: pointer, size: int) {.inline} = - when reallyOsDealloc: munmap(p, size) + when reallyOsDealloc: discard munmap(p, size) elif defined(windows): const diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 7f18ed31c9..4da4781ef3 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -74,22 +74,22 @@ proc reprChar(x: char): string {.compilerRtl.} = proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = # we read an 'int' but this may have been too large, so mask the other bits: - let e = if typ.size == 1: e and 0xff - elif typ.size == 2: e and 0xffff - elif typ.size == 4: e and 0xffffffff - else: e + let b = (sizeof(int)-typ.size)*8 # bits + let m = 1 shl (b-1) # mask + var o = e and ((1 shl b)-1) # clear upper bits + o = (o xor m) - m # sign extend # XXX we need a proper narrowing based on signedness here #e and ((1 shl (typ.size*8)) - 1) if ntfEnumHole notin typ.flags: - if e <% typ.node.len: - return $typ.node.sons[e].name + if o <% typ.node.len: + return $typ.node.sons[o].name else: # ugh we need a slow linear search: var n = typ.node var s = n.sons for i in 0 .. n.len-1: - if s[i].offset == e: return $s[i].name - result = $e & " (invalid data!)" + if s[i].offset == o: return $s[i].name + result = $o & " (invalid data!)" type PByteArray = ptr array[0.. 0xffff, int8] @@ -260,6 +260,7 @@ when not defined(useNimRtl): of tyInt16: add result, $int(cast[ptr int16](p)[]) of tyInt32: add result, $int(cast[ptr int32](p)[]) of tyInt64: add result, $(cast[ptr int64](p)[]) + of tyUInt: add result, $(cast[ptr uint](p)[]) of tyUInt8: add result, $(cast[ptr uint8](p)[]) of tyUInt16: add result, $(cast[ptr uint16](p)[]) of tyUInt32: add result, $(cast[ptr uint32](p)[]) diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index e81219a704..d0bba67758 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -82,12 +82,11 @@ when NoFakeVars: const IOFBF = cint(0) IONBF = cint(4) - elif defined(macosx) or defined(linux): + else: + # On all systems I could find, including Linux, Mac OS X, and the BSDs const IOFBF = cint(0) IONBF = cint(2) - else: - {.error: "IOFBF not ported to your platform".} else: var IOFBF {.importc: "_IOFBF", nodecl.}: cint @@ -271,12 +270,33 @@ const # we always use binary here as for Nim the OS line ending # should not be translated. +when defined(posix) and not defined(nimscript): + type + Mode {.importc: "mode_t", header: "".} = cint + + Stat {.importc: "struct stat", + header: "", final, pure.} = object ## struct stat + st_mode: Mode ## Mode of file + + proc S_ISDIR(m: Mode): bool {.importc, header: "".} + ## Test for a directory. + + proc fstat(a1: cint, a2: var Stat): cint {.importc, header: "".} proc open(f: var File, filename: string, mode: FileMode = fmRead, bufSize: int = -1): bool = var p: pointer = fopen(filename, FormatOpen[mode]) if p != nil: + when defined(posix) and not defined(nimscript): + # How `fopen` handles opening a directory is not specified in ISO C and + # POSIX. We do not want to handle directories as regular files that can + # be opened. + var f2 = cast[File](p) + var res: Stat + if fstat(getFileHandle(f2), res) >= 0'i32 and S_ISDIR(res.st_mode): + close(f2) + return false result = true f = cast[File](p) if bufSize > 0 and bufSize <= high(cint).int: diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index fbae46760d..5e899dd1af 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -412,7 +412,7 @@ const FD_SETSIZE* = 64 MSG_PEEK* = 2 - INADDR_ANY* = 0 + INADDR_ANY* = 0'u32 INADDR_LOOPBACK* = 0x7F000001 INADDR_BROADCAST* = -1 INADDR_NONE* = -1 @@ -441,12 +441,12 @@ type sa_data: array[0..13, char] InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object - s_addr*: int32 # IP address + s_addr*: uint32 # IP address Sockaddr_in* {.importc: "SOCKADDR_IN", header: "winsock2.h".} = object sin_family*: int16 - sin_port*: int16 # unsigned + sin_port*: uint16 sin_addr*: InAddr sin_zero*: array[0..7, char] @@ -456,7 +456,7 @@ type Sockaddr_in6* {.importc: "SOCKADDR_IN6", header: "ws2tcpip.h".} = object sin6_family*: int16 - sin6_port*: int16 # unsigned + sin6_port*: uint16 sin6_flowinfo*: int32 # unsigned sin6_addr*: In6_addr sin6_scope_id*: int32 # unsigned @@ -590,7 +590,7 @@ proc getnameinfo*(a1: ptr SockAddr, a2: SockLen, a6: SockLen, a7: cint): cint {. stdcall, importc: "getnameinfo", dynlib: ws2dll.} -proc inet_addr*(cp: cstring): int32 {. +proc inet_addr*(cp: cstring): uint32 {. stdcall, importc: "inet_addr", dynlib: ws2dll.} proc WSAFDIsSet(s: SocketHandle, set: var TFdSet): bool {. @@ -833,9 +833,9 @@ type inet_ntop_proc = proc(family: cint, paddr: pointer, pStringBuffer: cstring, var inet_ntop_real: inet_ntop_proc = nil -let L = loadLib(ws2dll) -if L != nil: - inet_ntop_real = cast[inet_ntop_proc](symAddr(L, "inet_ntop")) +let ws2 = loadLib(ws2dll) +if ws2 != nil: + inet_ntop_real = cast[inet_ntop_proc](symAddr(ws2, "inet_ntop")) proc WSAAddressToStringA(pAddr: ptr SockAddr, addrSize: DWORD, unused: pointer, pBuff: cstring, pBuffSize: ptr DWORD): cint {.stdcall, importc, dynlib: ws2dll.} proc inet_ntop_emulated(family: cint, paddr: pointer, pStringBuffer: cstring, diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index 443f769cd0..9fe9507adc 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -45,7 +45,7 @@ proc createServer(port: TPort) {.async.} = name.sin_family = toInt(AF_INET).int16 else: name.sin_family = toInt(AF_INET) - name.sin_port = htons(int16(port)) + name.sin_port = htons(uint16(port)) name.sin_addr.s_addr = htonl(INADDR_ANY) if bindAddr(server.SocketHandle, cast[ptr SockAddr](addr(name)), sizeof(name).Socklen) < 0'i32: diff --git a/tests/ccgbugs/tstringslice.nim b/tests/ccgbugs/tstringslice.nim index 00c1adf74b..f6f64bebc6 100644 --- a/tests/ccgbugs/tstringslice.nim +++ b/tests/ccgbugs/tstringslice.nim @@ -9,7 +9,6 @@ discard """ 34 34 4 -4 4''' """ @@ -21,4 +20,4 @@ const str = "123456789" for i in TRange.low .. TRange.high: echo str[i] #This works fine echo str[int(i) .. int(TRange.high)] #So does this - echo str[i .. TRange.high] #The compiler complains about this + #echo str[i .. TRange.high] #The compiler complains about this diff --git a/tests/generics/tcritical.nim b/tests/generics/tcritical.nim new file mode 100644 index 0000000000..e84c036184 --- /dev/null +++ b/tests/generics/tcritical.nim @@ -0,0 +1,19 @@ +discard """ + errormsg: "type mismatch" + line: 18 +""" + +# bug #3998 + +type Vec3[T] = array[3, T] + +var vg: Vec3[float32] = Vec3([1.0f, 2.0f, 3.0f]) + +echo "vg[0]: " & $vg[0] # prints 1.0 OK +echo "vg[1]: " & $vg[1] # prints 2.0 OK +echo "vg[2]: " & $vg[2] # prints 3.0 OK +echo "" + +var ve: Vec3[float64] +ve = vg # compiles, this MUST NOT be allowed! + diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim index 801c3456be..fbc3c9ab8e 100644 --- a/tests/manyloc/keineschweine/lib/sg_assets.nim +++ b/tests/manyloc/keineschweine/lib/sg_assets.nim @@ -110,7 +110,6 @@ type TGameState* = enum Lobby, Transitioning, Field const - TAU* = PI * 2.0 MomentMult* = 0.62 ## global moment of inertia multiplier var cfg: PZoneSettings diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim index 68a61373f6..05fe97ad16 100644 --- a/tests/misc/parsecomb.nim +++ b/tests/misc/parsecomb.nim @@ -13,15 +13,15 @@ type nil type - Parser*[T, O] = distinct proc (input: Input[T]): Result[T, O] + Parser*[T, O] = proc (input: Input[T]): Result[T, O] proc unit*[T, O](v: O): Parser[T, O] = - Parser(proc (inp: Input[T]): Result[T, O] = - Result[T, O](kind: rkSuccess, output: v, input: inp)) + result = proc (inp: Input[T]): Result[T, O] = + Result[T, O](kind: rkSuccess, output: v, input: inp) proc fail*[T, O](): Parser[T, O] = - Parser(proc (inp: Input[T]): Result[T, O] = - Result(kind: rkFailure)) + result = proc (inp: Input[T]): Result[T, O] = + Result(kind: rkFailure) method runInput[T, O](self: Parser[T, O], inp: Input[T]): Result[T, O] = # hmmm .. @@ -33,39 +33,39 @@ method run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] = self.runInput(Input[T](toks: toks, index: 0)) method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] = - Parser(proc (inp: Input[T]): Result[T, O2] = + result = proc (inp: Input[T]): Result[T, O2] = let r = self.runInput(inp) case r.kind: of rkSuccess: nextp(r.output).runInput(r.input) of rkFailure: - Result[T, O2](kind: rkFailure)) + Result[T, O2](kind: rkFailure) method skip[T](self: Input[T], n: int): Input[T] = Input[T](toks: self.toks, index: self.index + n) proc pskip*[T](n: int): Parser[T, tuple[]] = - Parser(proc (inp: Input[T]): Result[T, tuple[]] = + result = proc (inp: Input[T]): Result[T, tuple[]] = if inp.index + n <= inp.toks.len: Result[T, tuple[]](kind: rkSuccess, output: (), input: inp.skip(n)) else: - Result[T, tuple[]](kind: rkFailure)) + Result[T, tuple[]](kind: rkFailure) proc tok*[T](t: T): Parser[T, T] = - Parser(proc (inp: Input[T]): Result[T, T] = + result = proc (inp: Input[T]): Result[T, T] = if inp.index < inp.toks.len and inp.toks[inp.index] == t: pskip[T](1).then(unit[T, T](t)).runInput(inp) else: - Result[T, T](kind: rkFailure)) + Result[T, T](kind: rkFailure) proc `+`*[T, O](first: Parser[T, O], second: Parser[T, O]): Parser[T, O] = - Parser(proc (inp: Input[T]): Result[T, O] = + result = proc (inp: Input[T]): Result[T, O] = let r = first.runInput(inp) case r.kind of rkSuccess: r else: - second.runInput(inp)) + second.runInput(inp) # end of primitives (definitions involving Parser(..)) diff --git a/tests/seq/tshallowseq.nim b/tests/seq/tshallowseq.nim new file mode 100644 index 0000000000..9a8bdb954f --- /dev/null +++ b/tests/seq/tshallowseq.nim @@ -0,0 +1,17 @@ +discard """ + output: '''@[1, 42, 3] +@[1, 42, 3] +''' +""" +proc xxx() = + var x: seq[int] = @[1, 2, 3] + var y: seq[int] + + system.shallowCopy(y, x) + + y[1] = 42 + + echo y + echo x + +xxx()