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

This commit is contained in:
Araq 2018-02-09 16:38:53 +01:00
commit b565c0234c
64 changed files with 853 additions and 2036 deletions

View file

@ -12,6 +12,15 @@
`getBool`, `getFloat`, `getBiggestInt`. Also `getInt` procedure was added. `getBool`, `getFloat`, `getBiggestInt`. Also `getInt` procedure was added.
- `reExtended` is no longer default for the `re` constructor in the `re` - `reExtended` is no longer default for the `re` constructor in the `re`
module. module.
- `newAsyncSocket` taking an `AsyncFD` now runs `setBlocking(false)` on the
fd.
- The `ReadyKey` type in the selectors module now contains an ``errorCode``
field to help distinguish between ``Event.Error`` events.
- Implemented an `accept` proc that works on a `SocketHandle` in
``nativesockets``.
- Implemented ``getIoHandler`` proc in the ``asyncdispatch`` module that allows
you to retrieve the underlying IO Completion Port or ``Selector[AsyncData]``
object in the specified dispatcher.
- The overloading rules changed slightly so that constrained generics are - The overloading rules changed slightly so that constrained generics are
preferred over unconstrained generics. (Bug #6526) preferred over unconstrained generics. (Bug #6526)
- It is now possible to forward declare object types so that mutually - It is now possible to forward declare object types so that mutually
@ -226,3 +235,17 @@ styledEcho "Red on Green.", resetStyle
- ``\n`` is now only the single line feed character like in most - ``\n`` is now only the single line feed character like in most
other programming languages. The new platform specific newline escape sequence is other programming languages. The new platform specific newline escape sequence is
written as ``\p``. This change only affects the Windows platform. written as ``\p``. This change only affects the Windows platform.
- Type inference for generic type parameters involving numeric types is now symetric. See
[Generic type inference for numeric types](https://nim-lang.org/docs/manual.html#generics-generic-type-inference-fornumeric-types)
for more information.
- The ``deprecated`` pragma now supports a user-definable warning message for procs.
```nim
proc bar {.deprecated: "use foo instead".} =
return
bar()
```
- The ``securehash`` module is now deprecated. Instead import ``std / sha1``.

View file

@ -10,7 +10,7 @@
# abstract syntax tree + symbol table # abstract syntax tree + symbol table
import import
msgs, hashes, nversion, options, strutils, securehash, ropes, idents, msgs, hashes, nversion, options, strutils, std / sha1, ropes, idents,
intsets, idgen intsets, idgen
type type

View file

@ -970,7 +970,8 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)]) addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
discard cgsym(m, "TNimType") discard cgsym(m, "TNimType")
if isDefined("nimTypeNames"): if isDefined("nimTypeNames"):
var typename = typeToString(origType, preferName) var typename = typeToString(if origType.typeInst != nil: origType.typeInst
else: origType, preferName)
if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil: if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
typename = "anon ref object from " & $origType.skipTypes(skipPtrs).sym.info typename = "anon ref object from " & $origType.skipTypes(skipPtrs).sym.info
addf(m.s[cfsTypeInit3], "$1.name = $2;$n", addf(m.s[cfsTypeInit3], "$1.name = $2;$n",

View file

@ -11,7 +11,7 @@
import import
ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
nversion, nimsets, msgs, securehash, bitsets, idents, types, nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
lowerings, semparallel, tables, sets, ndi lowerings, semparallel, tables, sets, ndi
@ -908,7 +908,6 @@ proc addIntTypes(result: var Rope) {.inline.} =
platform.CPU[targetCPU].intSize.rope]) platform.CPU[targetCPU].intSize.rope])
proc getCopyright(cfile: Cfile): Rope = proc getCopyright(cfile: Cfile): Rope =
const copyrightYear = "2017"
if optCompileOnly in gGlobalOptions: if optCompileOnly in gGlobalOptions:
result = ("/* Generated by Nim Compiler v$1 */$N" & result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) " & copyrightYear & " Andreas Rumpf */$N" & "/* (c) " & copyrightYear & " Andreas Rumpf */$N" &

View file

@ -54,7 +54,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
const const
HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" & HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
"Copyright (c) 2006-2017 by Andreas Rumpf\n" "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
const const
Usage = slurp"../doc/basicopt.txt".replace("//", "") Usage = slurp"../doc/basicopt.txt".replace("//", "")

View file

@ -14,7 +14,7 @@
import import
ropes, os, strutils, osproc, platform, condsyms, options, msgs, ropes, os, strutils, osproc, platform, condsyms, options, msgs,
securehash, streams std / sha1, streams
#from debuginfo import writeDebugInfo #from debuginfo import writeDebugInfo

View file

@ -9,7 +9,7 @@
## Module that implements ``gorge`` for the compiler. ## Module that implements ``gorge`` for the compiler.
import msgs, securehash, os, osproc, streams, strutils, options import msgs, std / sha1, os, osproc, streams, strutils, options
proc readOutput(p: Process): (string, int) = proc readOutput(p: Process): (string, int) =
result[0] = "" result[0] = ""

View file

@ -11,7 +11,7 @@
import import
intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
semdata, passes, renderer, modulepaths semdata, passes, renderer, modulepaths, sigmatch
proc evalImport*(c: PContext, n: PNode): PNode proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode
@ -149,7 +149,7 @@ proc myImportModule(c: PContext, n: PNode): PSym =
localError(n.info, errGenerated, "A module cannot import itself") localError(n.info, errGenerated, "A module cannot import itself")
if sfDeprecated in result.flags: if sfDeprecated in result.flags:
message(n.info, warnDeprecated, result.name.s) message(n.info, warnDeprecated, result.name.s)
#suggestSym(n.info, result, false) suggestSym(n.info, result, c.graph.usageSym, false)
proc impMod(c: PContext; it: PNode) = proc impMod(c: PContext; it: PNode) =
let m = myImportModule(c, it) let m = myImportModule(c, it)

View file

@ -31,7 +31,7 @@ implements the required case distinction.
import import
ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options, ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
nversion, nimsets, msgs, securehash, bitsets, idents, types, os, nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os,
times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils, times, ropes, math, passes, ccgutils, wordrecg, renderer, rodread, rodutils,
intsets, cgmeth, lowerings intsets, cgmeth, lowerings
@ -2284,11 +2284,17 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
r.kind = resExpr r.kind = resExpr
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
let f = n.floatVal let f = n.floatVal
if f != f: r.res = rope"NaN" case classify(f)
elif f == 0.0: r.res = rope"0.0" of fcNaN:
elif f == 0.5 * f: r.res = rope"NaN"
if f > 0.0: r.res = rope"Infinity" of fcNegZero:
else: r.res = rope"-Infinity" r.res = rope"-0.0"
of fcZero:
r.res = rope"0.0"
of fcInf:
r.res = rope"Infinity"
of fcNegInf:
r.res = rope"-Infinity"
else: r.res = rope(f.toStrMaxPrecision) else: r.res = rope(f.toStrMaxPrecision)
r.kind = resExpr r.kind = resExpr
of nkCallKinds: of nkCallKinds:
@ -2390,7 +2396,7 @@ proc genHeader(target: TTarget): Rope =
if target == targetJS: if target == targetJS:
result = ( result = (
"/* Generated by the Nim Compiler v$1 */$n" & "/* Generated by the Nim Compiler v$1 */$n" &
"/* (c) 2017 Andreas Rumpf */$n$n" & "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
"var framePtr = null;$n" & "var framePtr = null;$n" &
"var excHandler = 0;$n" & "var excHandler = 0;$n" &
"var lastJSError = null;$n" & "var lastJSError = null;$n" &
@ -2406,7 +2412,7 @@ proc genHeader(target: TTarget): Rope =
else: else:
result = ("<?php$n" & result = ("<?php$n" &
"/* Generated by the Nim Compiler v$1 */$n" & "/* Generated by the Nim Compiler v$1 */$n" &
"/* (c) 2017 Andreas Rumpf */$n$n" & "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
"$$framePtr = null;$n" & "$$framePtr = null;$n" &
"$$excHandler = 0;$n" & "$$excHandler = 0;$n" &
"$$lastJSError = null;$n") % "$$lastJSError = null;$n") %
@ -2464,7 +2470,7 @@ proc genClass(obj: PType; content: Rope; ext: string) =
else: nil else: nil
let result = ("<?php$n" & let result = ("<?php$n" &
"/* Generated by the Nim Compiler v$# */$n" & "/* Generated by the Nim Compiler v$# */$n" &
"/* (c) 2017 Andreas Rumpf */$n$n" & "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" &
"require_once \"nimsystem.php\";$n" & "require_once \"nimsystem.php\";$n" &
"class $#$# {$n$#$n}$n") % "class $#$# {$n$#$n}$n") %
[rope(VersionAsString), cls, extends, content] [rope(VersionAsString), cls, extends, content]

View file

@ -10,7 +10,7 @@
## Implements the module handling, including the caching of modules. ## Implements the module handling, including the caching of modules.
import import
ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options, ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options,
idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs
when false: when false:

View file

@ -370,7 +370,7 @@ const
errXhasSideEffects: "\'$1\' can have side effects", errXhasSideEffects: "\'$1\' can have side effects",
errIteratorExpected: "iterator within for loop context expected", errIteratorExpected: "iterator within for loop context expected",
errLetNeedsInit: "'let' symbol requires an initialization", errLetNeedsInit: "'let' symbol requires an initialization",
errThreadvarCannotInit: "a thread var cannot be initialized explicitly", errThreadvarCannotInit: "a thread var cannot be initialized explicitly; this would only run for the main thread",
errWrongSymbolX: "usage of \'$1\' is a user-defined error", errWrongSymbolX: "usage of \'$1\' is a user-defined error",
errIllegalCaptureX: "illegal capture '$1'", errIllegalCaptureX: "illegal capture '$1'",
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention", errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
@ -1025,6 +1025,9 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string,
handleError(msg, eh, s) handleError(msg, eh, s)
proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") = proc fatal*(info: TLineInfo, msg: TMsgKind, arg = "") =
# this fixes bug #7080 so that it is at least obvious 'fatal'
# was executed.
errorOutputs = {eStdOut, eStdErr}
liMessage(info, msg, arg, doAbort) liMessage(info, msg, arg, doAbort)
proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") = proc globalError*(info: TLineInfo, msg: TMsgKind, arg = "") =

View file

@ -18,6 +18,7 @@ const
newScopeForIf* = true newScopeForIf* = true
useCaas* = not defined(noCaas) useCaas* = not defined(noCaas)
noTimeMachine* = defined(avoidTimeMachine) and defined(macosx) noTimeMachine* = defined(avoidTimeMachine) and defined(macosx)
copyrightYear* = "2018"
type # please make sure we have under 32 options type # please make sure we have under 32 options
# (improves code efficiency a lot!) # (improves code efficiency a lot!)

View file

@ -914,7 +914,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
optInd(p, result) optInd(p, result)
addSon(result, parseTypeDesc(p)) addSon(result, parseTypeDesc(p))
else: else:
addSon(result, ast.emptyNode) addSon(result, newNodeP(nkEmpty, p))
if p.tok.tokType != tkEquals and withBothOptional notin flags: if p.tok.tokType != tkEquals and withBothOptional notin flags:
parMessage(p, errColonOrEqualsExpected, p.tok) parMessage(p, errColonOrEqualsExpected, p.tok)
if p.tok.tokType == tkEquals: if p.tok.tokType == tkEquals:
@ -922,7 +922,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
optInd(p, result) optInd(p, result)
addSon(result, parseExpr(p)) addSon(result, parseExpr(p))
else: else:
addSon(result, ast.emptyNode) addSon(result, newNodeP(nkEmpty, p))
proc parseTuple(p: var TParser, indentAllowed = false): PNode = proc parseTuple(p: var TParser, indentAllowed = false): PNode =
#| inlTupleDecl = 'tuple' #| inlTupleDecl = 'tuple'

View file

@ -667,10 +667,11 @@ proc semCustomPragma(c: PContext, n: PNode): PNode =
else: else:
result = n result = n
result = c.semOverloadedCall(c, result, n, {skTemplate}, {}) let r = c.semOverloadedCall(c, result, n, {skTemplate}, {})
if sfCustomPragma notin result[0].sym.flags: if r.isNil or sfCustomPragma notin r[0].sym.flags:
invalidPragma(n) invalidPragma(n)
else:
result = r
if n.kind == nkIdent: if n.kind == nkIdent:
result = result[0] result = result[0]
elif n.kind == nkExprColonExpr: elif n.kind == nkExprColonExpr:
@ -809,7 +810,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wExplain: of wExplain:
sym.flags.incl sfExplain sym.flags.incl sfExplain
of wDeprecated: of wDeprecated:
if it.kind in nkPragmaCallKinds: deprecatedStmt(c, it) if sym != nil and sym.kind in routineKinds:
if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
incl(sym.flags, sfDeprecated)
elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it)
elif sym != nil: incl(sym.flags, sfDeprecated) elif sym != nil: incl(sym.flags, sfDeprecated)
else: incl(c.module.flags, sfDeprecated) else: incl(c.module.flags, sfDeprecated)
of wVarargs: of wVarargs:

View file

@ -90,7 +90,7 @@
import import
os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
ropes, idents, securehash, idgen, types, rodutils, memfiles, tables ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables
type type
TReasonForRecompile* = enum ## all the reasons that can trigger recompilation TReasonForRecompile* = enum ## all the reasons that can trigger recompilation

View file

@ -8,18 +8,22 @@
# #
## Serialization utilities for the compiler. ## Serialization utilities for the compiler.
import strutils import strutils, math
proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.} proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
if f != f: case classify(f)
of fcNaN:
result = "NAN" result = "NAN"
elif f == 0.0: of fcNegZero:
result = "-0.0" & literalPostfix
of fcZero:
result = "0.0" & literalPostfix result = "0.0" & literalPostfix
elif f == 0.5 * f: of fcInf:
if f > 0.0: result = "INF" result = "INF"
else: result = "-INF" of fcNegInf:
result = "-INF"
else: else:
when defined(nimNoArrayToCstringConversion): when defined(nimNoArrayToCstringConversion):
result = newString(81) result = newString(81)

View file

@ -13,7 +13,7 @@
import import
intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform, intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
condsyms, ropes, idents, securehash, rodread, passes, idgen, condsyms, ropes, idents, std / sha1, rodread, passes, idgen,
rodutils, modulepaths rodutils, modulepaths
from modulegraphs import ModuleGraph from modulegraphs import ModuleGraph

View file

@ -215,12 +215,17 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym allowed: TSymFlags): PSym
proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) = proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind;
let t = typeAllowed(typ, kind) flags: TTypeAllowedFlags = {}) =
let t = typeAllowed(typ, kind, flags)
if t != nil: if t != nil:
if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'") if t == typ:
else: localError(info, "invalid type: '" & typeToString(t) & localError(info, "invalid type: '" & typeToString(typ) &
"' in this context: '" & typeToString(typ) & "'") "' for " & substr($kind, 2).toLowerAscii)
else:
localError(info, "invalid type: '" & typeToString(t) &
"' in this context: '" & typeToString(typ) &
"' for " & substr($kind, 2).toLowerAscii)
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
typeAllowedCheck(typ.n.info, typ, skProc) typeAllowedCheck(typ.n.info, typ, skProc)

View file

@ -106,6 +106,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
errors.safeAdd(CandidateError( errors.safeAdd(CandidateError(
sym: sym, sym: sym,
unmatchedVarParam: int z.mutabilityProblem, unmatchedVarParam: int z.mutabilityProblem,
firstMismatch: z.firstMismatch,
diagnostics: z.diagnostics)) diagnostics: z.diagnostics))
else: else:
# Symbol table has been modified. Restart and pre-calculate all syms # Symbol table has been modified. Restart and pre-calculate all syms
@ -154,7 +155,20 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
else: else:
add(candidates, err.sym.getProcHeader(prefer)) add(candidates, err.sym.getProcHeader(prefer))
add(candidates, "\n") add(candidates, "\n")
if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len: if err.firstMismatch != 0 and n.len > 2:
add(candidates, "first type mismatch at position: " & $err.firstMismatch &
"\nrequired type: ")
if err.firstMismatch < err.sym.typ.len:
candidates.add typeToString(err.sym.typ.sons[err.firstMismatch])
else:
candidates.add "none"
if err.firstMismatch < n.len:
candidates.add "\nbut expression '"
candidates.add renderTree(n[err.firstMismatch])
candidates.add "' is of type: "
candidates.add typeToString(n[err.firstMismatch].typ)
candidates.add "\n"
elif err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len:
add(candidates, "for a 'var' type a variable needs to be passed, but '" & add(candidates, "for a 'var' type a variable needs to be passed, but '" &
renderTree(n[err.unmatchedVarParam]) & "' is immutable\n") renderTree(n[err.unmatchedVarParam]) & "' is immutable\n")
for diag in err.diagnostics: for diag in err.diagnostics:
@ -189,7 +203,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
while symx != nil: while symx != nil:
if symx.kind in routineKinds: if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx, errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0, unmatchedVarParam: 0, firstMismatch: 0,
diagnostics: nil)) diagnostics: nil))
symx = nextOverloadIter(o, c, headSymbol) symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0: if errors.len == 0:
@ -263,9 +277,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if overloadsState == csEmpty and result.state == csEmpty: if overloadsState == csEmpty and result.state == csEmpty:
if nfDotField in n.flags and nfExplicitCall notin n.flags: if nfDotField in n.flags and nfExplicitCall notin n.flags:
localError(n.info, errUndeclaredField, considerQuotedIdent(f).s) localError(n.info, errUndeclaredField, considerQuotedIdent(f, n).s)
else: else:
localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f).s) localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f, n).s)
return return
elif result.state != csMatch: elif result.state != csMatch:
if nfExprCall in n.flags: if nfExprCall in n.flags:

View file

@ -225,6 +225,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mDivF64: of mDivF64:
if getFloat(b) == 0.0: if getFloat(b) == 0.0:
if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n) if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n)
elif getFloat(b).classify == fcNegZero: result = newFloatNodeT(-Inf, n)
else: result = newFloatNodeT(Inf, n) else: result = newFloatNodeT(Inf, n)
else: else:
result = newFloatNodeT(getFloat(a) / getFloat(b), n) result = newFloatNodeT(getFloat(a) / getFloat(b), n)

View file

@ -260,6 +260,10 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = newNodeIT(nkObjConstr, n.info, t) result = newNodeIT(nkObjConstr, n.info, t)
for child in n: result.add child for child in n: result.add child
if t == nil:
localError(n.info, errGenerated, "object constructor needs an object type")
return
t = skipTypes(t, {tyGenericInst, tyAlias, tySink}) t = skipTypes(t, {tyGenericInst, tyAlias, tySink})
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink}) if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink})
if t.kind != tyObject: if t.kind != tyObject:

View file

@ -593,17 +593,14 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) =
notNilCheck(tracked, n, paramType) notNilCheck(tracked, n, paramType)
proc breaksBlock(n: PNode): bool = proc breaksBlock(n: PNode): bool =
case n.kind # sematic check doesn't allow statements after raise, break, return or
of nkStmtList, nkStmtListExpr: # call to noreturn proc, so it is safe to check just the last statements
for c in n: var it = n
if breaksBlock(c): return true while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
of nkBreakStmt, nkReturnStmt, nkRaiseStmt: it = it.lastSon
return true
of nkCallKinds: result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or
if n.sons[0].kind == nkSym and sfNoReturn in n.sons[0].sym.flags: it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
return true
else:
discard
proc trackCase(tracked: PEffects, n: PNode) = proc trackCase(tracked: PEffects, n: PNode) =
track(tracked, n.sons[0]) track(tracked, n.sons[0])
@ -747,7 +744,7 @@ proc track(tracked: PEffects, n: PNode) =
# may not look like an assignment, but it is: # may not look like an assignment, but it is:
let arg = n.sons[1] let arg = n.sons[1]
initVarViaNew(tracked, arg) initVarViaNew(tracked, arg)
if {tfNeedsInit} * arg.typ.lastSon.flags != {}: if arg.typ.len != 0 and {tfNeedsInit} * arg.typ.lastSon.flags != {}:
if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
n[2].intVal == 0: n[2].intVal == 0:
# var s: seq[notnil]; newSeq(s, 0) is a special case! # var s: seq[notnil]; newSeq(s, 0) is a special case!

View file

@ -150,6 +150,9 @@ proc discardCheck(c: PContext, result: PNode) =
while n.kind in skipForDiscardable: n = n.lastSon while n.kind in skipForDiscardable: n = n.lastSon
var s = "expression '" & $n & "' is of type '" & var s = "expression '" & $n & "' is of type '" &
result.typ.typeToString & "' and has to be discarded" result.typ.typeToString & "' and has to be discarded"
if result.info.line != n.info.line or
result.info.fileIndex != n.info.fileIndex:
s.add "; start of expression here: " & $result.info
if result.typ.kind == tyProc: if result.typ.kind == tyProc:
s.add "; for a function call use ()" s.add "; for a function call use ()"
localError(n.info, s) localError(n.info, s)
@ -531,7 +534,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# this can only happen for errornous var statements: # this can only happen for errornous var statements:
if typ == nil: continue if typ == nil: continue
typeAllowedCheck(a.info, typ, symkind) typeAllowedCheck(a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
liftTypeBoundOps(c, typ, a.info) liftTypeBoundOps(c, typ, a.info)
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink}) var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
if a.kind == nkVarTuple: if a.kind == nkVarTuple:

View file

@ -24,7 +24,7 @@ type
CandidateError* = object CandidateError* = object
sym*: PSym sym*: PSym
unmatchedVarParam*: int unmatchedVarParam*, firstMismatch*: int
diagnostics*: seq[string] diagnostics*: seq[string]
CandidateErrors* = seq[CandidateError] CandidateErrors* = seq[CandidateError]
@ -68,6 +68,8 @@ type
# triggered with an idetools command in the # triggered with an idetools command in the
# future. # future.
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
firstMismatch*: int # position of the first type mismatch for
# better error messages
TTypeRelFlag* = enum TTypeRelFlag* = enum
trDontBind trDontBind
@ -913,6 +915,25 @@ proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
else: else:
return false return false
proc maxNumericType(prev, candidate: PType): PType =
let c = candidate.skipTypes({tyRange})
template greater(s) =
if c.kind in s: result = c
case prev.kind
of tyInt: greater({tyInt64})
of tyInt8: greater({tyInt, tyInt16, tyInt32, tyInt64})
of tyInt16: greater({tyInt, tyInt32, tyInt64})
of tyInt32: greater({tyInt64})
of tyUInt: greater({tyUInt64})
of tyUInt8: greater({tyUInt, tyUInt16, tyUInt32, tyUInt64})
of tyUInt16: greater({tyUInt, tyUInt32, tyUInt64})
of tyUInt32: greater({tyUInt64})
of tyFloat32: greater({tyFloat64, tyFloat128})
of tyFloat64: greater({tyFloat128})
else: discard
proc typeRelImpl(c: var TCandidate, f, aOrig: PType, proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
flags: TTypeRelFlags = {}): TTypeRelation = flags: TTypeRelFlags = {}): TTypeRelation =
# typeRel can be used to establish various relationships between types: # typeRel can be used to establish various relationships between types:
@ -1601,10 +1622,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
result = isGeneric result = isGeneric
elif x.kind == tyGenericParam: elif x.kind == tyGenericParam:
result = isGeneric result = isGeneric
else:
# Special type binding rule for numeric types.
# See section "Generic type inference for numeric types" of the
# manual for further details:
let rebinding = maxNumericType(x.skipTypes({tyRange}), a)
if rebinding != nil:
put(c, f, rebinding)
result = isGeneric
else: else:
result = typeRel(c, x, a) # check if it fits result = typeRel(c, x, a) # check if it fits
if result > isGeneric: result = isGeneric if result > isGeneric: result = isGeneric
of tyStatic: of tyStatic:
let prev = PType(idTableGet(c.bindings, f)) let prev = PType(idTableGet(c.bindings, f))
if prev == nil: if prev == nil:
@ -1833,6 +1861,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
var r = typeRel(m, f, a) var r = typeRel(m, f, a)
# This special typing rule for macros and templates is not documented
# anywhere and breaks symmetry. It's hard to get rid of though, my
# custom seqs example fails to compile without this:
if r != isNone and m.calleeSym != nil and if r != isNone and m.calleeSym != nil and
m.calleeSym.kind in {skMacro, skTemplate}: m.calleeSym.kind in {skMacro, skTemplate}:
# XXX: duplicating this is ugly, but we cannot (!) move this # XXX: duplicating this is ugly, but we cannot (!) move this
@ -2220,6 +2251,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
n.sons[a], nOrig.sons[a]) n.sons[a], nOrig.sons[a])
if arg == nil: if arg == nil:
m.state = csNoMatch m.state = csNoMatch
m.firstMismatch = f
return return
if m.baseTypeMatch: if m.baseTypeMatch:
#assert(container == nil) #assert(container == nil)
@ -2274,6 +2306,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
else: else:
# no default value # no default value
m.state = csNoMatch m.state = csNoMatch
m.firstMismatch = f
break break
else: else:
# use default value: # use default value:

View file

@ -33,6 +33,7 @@
# included from sigmatch.nim # included from sigmatch.nim
import algorithm, prefixmatches import algorithm, prefixmatches
from wordrecg import wDeprecated
when defined(nimsuggest): when defined(nimsuggest):
import passes, tables # importer import passes, tables # importer
@ -479,12 +480,23 @@ proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.in
isDecl: isDecl:
suggestResult(symToSuggest(s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) suggestResult(symToSuggest(s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
proc warnAboutDeprecated(info: TLineInfo; s: PSym) =
if s.kind in routineKinds:
let n = s.ast[pragmasPos]
if n.kind != nkEmpty:
for it in n:
if whichPragma(it) == wDeprecated and it.safeLen == 2 and
it[1].kind in {nkStrLit..nkTripleStrLit}:
message(info, warnDeprecated, it[1].strVal & "; " & s.name.s)
return
message(info, warnDeprecated, s.name.s)
proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) = proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) =
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
if s.kind == skEnumField and s.owner != nil: if s.kind == skEnumField and s.owner != nil:
incl(s.owner.flags, sfUsed) incl(s.owner.flags, sfUsed)
if {sfDeprecated, sfError} * s.flags != {}: if {sfDeprecated, sfError} * s.flags != {}:
if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s) if sfDeprecated in s.flags: warnAboutDeprecated(info, s)
if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s) if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s)
when defined(nimsuggest): when defined(nimsuggest):
suggestSym(info, s, usageSym, false) suggestSym(info, s, usageSym, false)

View file

@ -716,7 +716,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
result = transformSons(c, n) result = transformSons(c, n)
if n[0].isInfixAs(): if n[0].isInfixAs():
let excTypeNode = n[0][1] let excTypeNode = n[0][1]
let actions = newTransNode(nkStmtList, n[1].info, 2) let actions = newTransNode(nkStmtListExpr, n[1], 2)
# Generating `let exc = (excType)(getCurrentException())` # Generating `let exc = (excType)(getCurrentException())`
# -> getCurrentException() # -> getCurrentException()
let excCall = PTransNode(callCodegenProc("getCurrentException", ast.emptyNode)) let excCall = PTransNode(callCodegenProc("getCurrentException", ast.emptyNode))

View file

@ -118,7 +118,7 @@ proc isRange*(n: PNode): bool {.inline.} =
result = true result = true
proc whichPragma*(n: PNode): TSpecialWord = proc whichPragma*(n: PNode): TSpecialWord =
let key = if n.kind == nkExprColonExpr: n.sons[0] else: n let key = if n.kind in nkPragmaCallKinds and n.len > 0: n.sons[0] else: n
if key.kind == nkIdent: result = whichKeyword(key.ident) if key.kind == nkIdent: result = whichKeyword(key.ident)
proc unnestStmts(n, result: PNode) = proc unnestStmts(n, result: PNode) =

View file

@ -1058,11 +1058,12 @@ proc commonSuperclass*(a, b: PType): PType =
y = y.sons[0] y = y.sons[0]
type type
TTypeAllowedFlag = enum TTypeAllowedFlag* = enum
taField, taField,
taHeap taHeap,
taConcept
TTypeAllowedFlags = set[TTypeAllowedFlag] TTypeAllowedFlags* = set[TTypeAllowedFlag]
proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
flags: TTypeAllowedFlags = {}): PType flags: TTypeAllowedFlags = {}): PType
@ -1130,7 +1131,10 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
of tyVoid: of tyVoid:
if taField notin flags: result = t if taField notin flags: result = t
of tyTypeClasses: of tyTypeClasses:
if not (tfGenericTypeParam in t.flags or taField notin flags): result = t if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
discard
elif kind notin {skParam, skResult}:
result = t
of tyGenericBody, tyGenericParam, tyGenericInvocation, of tyGenericBody, tyGenericParam, tyGenericInvocation,
tyNone, tyForward, tyFromExpr: tyNone, tyForward, tyFromExpr:
result = t result = t
@ -1178,11 +1182,11 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
result = nil result = nil
of tyUnused, tyOptAsRef: internalError("typeAllowedAux") of tyUnused, tyOptAsRef: internalError("typeAllowedAux")
proc typeAllowed*(t: PType, kind: TSymKind): PType = proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PType =
# returns 'nil' on success and otherwise the part of the type that is # returns 'nil' on success and otherwise the part of the type that is
# wrong! # wrong!
var marker = initIntSet() var marker = initIntSet()
result = typeAllowedAux(marker, t, kind, {}) result = typeAllowedAux(marker, t, kind, flags)
proc align(address, alignment: BiggestInt): BiggestInt = proc align(address, alignment: BiggestInt): BiggestInt =
result = (address + (alignment - 1)) and not (alignment - 1) result = (address + (alignment - 1)) and not (alignment - 1)

View file

@ -384,7 +384,7 @@ Cryptography and Hashing
* `base64 <base64.html>`_ * `base64 <base64.html>`_
This module implements a base64 encoder and decoder. This module implements a base64 encoder and decoder.
* `securehash <securehash.html>`_ * `sha1 <sha1.html>`_
This module implements a sha1 encoder and decoder. This module implements a sha1 encoder and decoder.

View file

@ -63,6 +63,8 @@ The following example shows a generic binary tree can be modelled:
for str in preorder(root): for str in preorder(root):
stdout.writeLine(str) stdout.writeLine(str)
The ``T`` is called a `generic type parameter`:idx:.
Is operator Is operator
----------- -----------
@ -582,31 +584,32 @@ the concept body:
proc log(format: static[string], varargs[distinct StringRef]) proc log(format: static[string], varargs[distinct StringRef])
VTable types ..
------------ VTable types
------------
Concepts allow Nim to define a great number of algorithms, using only Concepts allow Nim to define a great number of algorithms, using only
static polymorphism and without erasing any type information or sacrificing static polymorphism and without erasing any type information or sacrificing
any execution speed. But when polymorphic collections of objects are required, any execution speed. But when polymorphic collections of objects are required,
the user must use one of the provided type erasure techniques - either common the user must use one of the provided type erasure techniques - either common
base types or VTable types. base types or VTable types.
VTable types are represented as "fat pointers" storing a reference to an VTable types are represented as "fat pointers" storing a reference to an
object together with a reference to a table of procs implementing a set of object together with a reference to a table of procs implementing a set of
required operations (the so called vtable). required operations (the so called vtable).
In contrast to other programming languages, the vtable in Nim is stored In contrast to other programming languages, the vtable in Nim is stored
externally to the object, allowing you to create multiple different vtable externally to the object, allowing you to create multiple different vtable
views for the same object. Thus, the polymorphism in Nim is unbounded - views for the same object. Thus, the polymorphism in Nim is unbounded -
any type can implement an unlimited number of protocols or interfaces not any type can implement an unlimited number of protocols or interfaces not
originally envisioned by the type's author. originally envisioned by the type's author.
Any concept type can be turned into a VTable type by using the ``vtref`` Any concept type can be turned into a VTable type by using the ``vtref``
or the ``vtptr`` compiler magics. Under the hood, these magics generate or the ``vtptr`` compiler magics. Under the hood, these magics generate
a converter type class, which converts the regular instances of the matching a converter type class, which converts the regular instances of the matching
types to the corresponding VTable type. types to the corresponding VTable type.
.. code-block:: nim .. code-block:: nim
type type
IntEnumerable = vtref Enumerable[int] IntEnumerable = vtref Enumerable[int]
@ -620,22 +623,22 @@ types to the corresponding VTable type.
proc addStream(o: var MyObject, e: OutputStream.vtref) = proc addStream(o: var MyObject, e: OutputStream.vtref) =
o.streams.add e o.streams.add e
The procs that will be included in the vtable are derived from the concept The procs that will be included in the vtable are derived from the concept
body and include all proc calls for which all param types were specified as body and include all proc calls for which all param types were specified as
concrete types. All such calls should include exactly one param of the type concrete types. All such calls should include exactly one param of the type
matched against the concept (not necessarily in the first position), which matched against the concept (not necessarily in the first position), which
will be considered the value bound to the vtable. will be considered the value bound to the vtable.
Overloads will be created for all captured procs, accepting the vtable type Overloads will be created for all captured procs, accepting the vtable type
in the position of the captured underlying object. in the position of the captured underlying object.
Under these rules, it's possible to obtain a vtable type for a concept with Under these rules, it's possible to obtain a vtable type for a concept with
unbound type parameters or one instantiated with metatypes (type classes), unbound type parameters or one instantiated with metatypes (type classes),
but it will include a smaller number of captured procs. A completely empty but it will include a smaller number of captured procs. A completely empty
vtable will be reported as an error. vtable will be reported as an error.
The ``vtref`` magic produces types which can be bound to ``ref`` types and The ``vtref`` magic produces types which can be bound to ``ref`` types and
the ``vtptr`` magic produced types bound to ``ptr`` types. the ``vtptr`` magic produced types bound to ``ptr`` types.
Symbol lookup in generics Symbol lookup in generics
@ -709,3 +712,30 @@ definition):
But a ``bind`` is rarely useful because symbol binding from the definition But a ``bind`` is rarely useful because symbol binding from the definition
scope is the default. scope is the default.
Generic type inference for numeric types
----------------------------------------
A `numeric`:idx: type is any signed, unsigned integer type, floating point
type or a subrange thereof. Let ``maxNumericType(T1, T2)`` be the "greater"
type of ``T1`` and ``T2``, that is the type that uses more bits. For
example ``maxNumericType(int32, int64) == int64``. ``maxNumericType`` is only
defined for numeric types of the same class (signed, unsigned, floating point).
``maxNumericType`` strips away subranges,
``maxNumericType(subrangeof(int16), int8)`` produces ``int16`` not its
subrange. The definition ``maxNumericType`` is extended to take a variable
number of arguments in the obvious way;
``maxNumericType(x, y, z) == maxNumericType(maxNumericType(x, y), z)``.
A generic type parameter ``T`` that is bound to multiple numeric types ``N1``,
``N2``, ``N3``, ... during type checking is inferred to
be ``maxNumericType(N1, N2, N3, ...)``. This special type inference rule ensures
that the builtin arithmetic operators can be written in an intuitive way:
.. code-block:: nim
proc `@`[T: int|int16|int32](x, y: T): T
4'i32 @ 6'i64 # inferred to be of type ``int64``
4'i64 @ 6'i32 # inferred to be of type ``int64``

View file

@ -181,11 +181,11 @@ Nim exceptions: `FloatInvalidOpError`:idx:, `FloatDivByZeroError`:idx:,
and `FloatInexactError`:idx:. and `FloatInexactError`:idx:.
These exceptions inherit from the `FloatingPointError`:idx: base class. These exceptions inherit from the `FloatingPointError`:idx: base class.
Nim provides the pragmas `NaNChecks`:idx: and `InfChecks`:idx: to control Nim provides the pragmas `nanChecks`:idx: and `infChecks`:idx: to control
whether the IEEE exceptions are ignored or trap a Nim exception: whether the IEEE exceptions are ignored or trap a Nim exception:
.. code-block:: nim .. code-block:: nim
{.NanChecks: on, InfChecks: on.} {.nanChecks: on, infChecks: on.}
var a = 1.0 var a = 1.0
var b = 0.0 var b = 0.0
echo b / b # raises FloatInvalidOpError echo b / b # raises FloatInvalidOpError
@ -195,7 +195,7 @@ In the current implementation ``FloatDivByZeroError`` and ``FloatInexactError``
are never raised. ``FloatOverflowError`` is raised instead of are never raised. ``FloatOverflowError`` is raised instead of
``FloatDivByZeroError``. ``FloatDivByZeroError``.
There is also a `floatChecks`:idx: pragma that is a short-cut for the There is also a `floatChecks`:idx: pragma that is a short-cut for the
combination of ``NaNChecks`` and ``InfChecks`` pragmas. ``floatChecks`` are combination of ``nanChecks`` and ``infChecks`` pragmas. ``floatChecks`` are
turned off as default. turned off as default.
The only operations that are affected by the ``floatChecks`` pragma are The only operations that are affected by the ``floatChecks`` pragma are

View file

@ -1,9 +1,11 @@
The set type models the mathematical notion of a set. The set's basetype can The set type models the mathematical notion of a set. The set's basetype can
only be an ordinal type of a certain size, namely: only be an ordinal type of a certain size, namely:
* ``int8``-``int16`` * ``int8``-``int16``
* ``uint8``/``byte``-``uint16`` * ``uint8``/``byte``-``uint16``
* ``char`` * ``char``
* ``enum`` * ``enum``
or equivalent. The reason is that sets are implemented as high or equivalent. The reason is that sets are implemented as high
performance bit vectors. Attempting to declare a set with a larger type will performance bit vectors. Attempting to declare a set with a larger type will
result in an error: result in an error:

View file

@ -1032,7 +1032,7 @@ procs for these conversions.
Type Conversion Type Conversion
--------------- ---------------
Conversion between basic types is performed by using the Conversion between numerical types is performed by using the
type as a function: type as a function:
.. code-block:: nim .. code-block:: nim

View file

@ -265,9 +265,15 @@ when defined(windows) or defined(nimdoc):
setGlobalDispatcher(newDispatcher()) setGlobalDispatcher(newDispatcher())
result = gDisp result = gDisp
proc getIoHandler*(disp: PDispatcher): Handle =
## Returns the underlying IO Completion Port handle (Windows) or selector
## (Unix) for the specified dispatcher.
return disp.ioPort
proc register*(fd: AsyncFD) = proc register*(fd: AsyncFD) =
## Registers ``fd`` with the dispatcher. ## Registers ``fd`` with the dispatcher.
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
if createIoCompletionPort(fd.Handle, p.ioPort, if createIoCompletionPort(fd.Handle, p.ioPort,
cast[CompletionKey](fd), 1) == 0: cast[CompletionKey](fd), 1) == 0:
raiseOSError(osLastError()) raiseOSError(osLastError())
@ -757,6 +763,9 @@ when defined(windows) or defined(nimdoc):
## Unregisters ``fd``. ## Unregisters ``fd``.
getGlobalDispatcher().handles.excl(fd) getGlobalDispatcher().handles.excl(fd)
proc contains*(disp: PDispatcher, fd: AsyncFD): bool =
return fd in disp.handles
{.push stackTrace:off.} {.push stackTrace:off.}
proc waitableCallback(param: pointer, proc waitableCallback(param: pointer,
timerOrWaitFired: WINBOOL): void {.stdcall.} = timerOrWaitFired: WINBOOL): void {.stdcall.} =
@ -977,7 +986,7 @@ when defined(windows) or defined(nimdoc):
proc newAsyncEvent*(): AsyncEvent = proc newAsyncEvent*(): AsyncEvent =
## Creates a new thread-safe ``AsyncEvent`` object. ## Creates a new thread-safe ``AsyncEvent`` object.
## ##
## New ``AsyncEvent`` object is not automatically registered with # TODO: Why? -- DP ## New ``AsyncEvent`` object is not automatically registered with
## dispatcher like ``AsyncSocket``. ## dispatcher like ``AsyncSocket``.
var sa = SECURITY_ATTRIBUTES( var sa = SECURITY_ATTRIBUTES(
nLength: sizeof(SECURITY_ATTRIBUTES).cint, nLength: sizeof(SECURITY_ATTRIBUTES).cint,
@ -1095,6 +1104,9 @@ else:
setGlobalDispatcher(newDispatcher()) setGlobalDispatcher(newDispatcher())
result = gDisp result = gDisp
proc getIoHandler*(disp: PDispatcher): Selector[AsyncData] =
return disp.selector
proc register*(fd: AsyncFD) = proc register*(fd: AsyncFD) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var data = newAsyncData() var data = newAsyncData()
@ -1111,6 +1123,9 @@ else:
proc unregister*(ev: AsyncEvent) = proc unregister*(ev: AsyncEvent) =
getGlobalDispatcher().selector.unregister(SelectEvent(ev)) getGlobalDispatcher().selector.unregister(SelectEvent(ev))
proc contains*(disp: PDispatcher, fd: AsyncFd): bool =
return fd.SocketHandle in disp.selector
proc addRead*(fd: AsyncFD, cb: Callback) = proc addRead*(fd: AsyncFD, cb: Callback) =
let p = getGlobalDispatcher() let p = getGlobalDispatcher()
var newEvents = {Event.Read} var newEvents = {Event.Read}

View file

@ -85,7 +85,7 @@ proc newAsyncFile*(fd: AsyncFd): AsyncFile =
## Creates `AsyncFile` with a previously opened file descriptor `fd`. ## Creates `AsyncFile` with a previously opened file descriptor `fd`.
new result new result
result.fd = fd result.fd = fd
register(result.fd) register(fd)
proc openAsync*(filename: string, mode = fmRead): AsyncFile = proc openAsync*(filename: string, mode = fmRead): AsyncFile =
## Opens a file specified by the path in ``filename`` using ## Opens a file specified by the path in ``filename`` using
@ -97,16 +97,16 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
when useWinUnicode: when useWinUnicode:
let fd = createFileW(newWideCString(filename), desiredAccess, let fd = createFileW(newWideCString(filename), desiredAccess,
FILE_SHARE_READ, FILE_SHARE_READ,
nil, creationDisposition, flags, 0).AsyncFd nil, creationDisposition, flags, 0)
else: else:
let fd = createFileA(filename, desiredAccess, let fd = createFileA(filename, desiredAccess,
FILE_SHARE_READ, FILE_SHARE_READ,
nil, creationDisposition, flags, 0).AsyncFd nil, creationDisposition, flags, 0)
if fd.Handle == INVALID_HANDLE_VALUE: if fd == INVALID_HANDLE_VALUE:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = newAsyncFile(fd) result = newAsyncFile(fd.AsyncFd)
if mode == fmAppend: if mode == fmAppend:
result.offset = getFileSize(result) result.offset = getFileSize(result)
@ -115,11 +115,11 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
let flags = getPosixFlags(mode) let flags = getPosixFlags(mode)
# RW (Owner), RW (Group), R (Other) # RW (Owner), RW (Group), R (Other)
let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH let perm = S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH
let fd = open(filename, flags, perm).AsyncFD let fd = open(filename, flags, perm)
if fd.cint == -1: if fd == -1:
raiseOSError(osLastError()) raiseOSError(osLastError())
result = newAsyncFile(fd) result = newAsyncFile(fd.AsyncFd)
proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] = proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
## Read ``size`` bytes from the specified file asynchronously starting at ## Read ``size`` bytes from the specified file asynchronously starting at

View file

@ -342,6 +342,7 @@ proc asyncCheck*[T](future: Future[T]) =
## finished with an error. ## finished with an error.
## ##
## This should be used instead of ``discard`` to discard void futures. ## This should be used instead of ``discard`` to discard void futures.
assert(not future.isNil, "Future is nil")
future.callback = future.callback =
proc () = proc () =
if future.failed: if future.failed:

View file

@ -140,9 +140,16 @@ proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
sockType: SockType = SOCK_STREAM, sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket = protocol: Protocol = IPPROTO_TCP, buffered = true): AsyncSocket =
## Creates a new ``AsyncSocket`` based on the supplied params. ## Creates a new ``AsyncSocket`` based on the supplied params.
##
## The supplied ``fd``'s non-blocking state will be enabled implicitly.
##
## **Note**: This procedure will **NOT** register ``fd`` with the global
## async dispatcher. You need to do this manually. If you have used
## ``newAsyncNativeSocket`` to create ``fd`` then it's already registered.
assert fd != osInvalidSocket.AsyncFD assert fd != osInvalidSocket.AsyncFD
new(result) new(result)
result.fd = fd.SocketHandle result.fd = fd.SocketHandle
fd.SocketHandle.setBlocking(false)
result.isBuffered = buffered result.isBuffered = buffered
result.domain = domain result.domain = domain
result.sockType = sockType result.sockType = sockType

View file

@ -141,7 +141,7 @@ template checkFd(s, f) =
if f >= s.maxFD: if f >= s.maxFD:
raiseIOSelectorsError("Maximum number of descriptors is exhausted!") raiseIOSelectorsError("Maximum number of descriptors is exhausted!")
proc registerHandle*[T](s: Selector[T], fd: SocketHandle, proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) = events: set[Event], data: T) =
let fdi = int(fd) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
@ -156,7 +156,7 @@ proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
raiseIOSelectorsError(osLastError()) raiseIOSelectorsError(osLastError())
inc(s.count) inc(s.count)
proc updateHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event]) = proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event]) =
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode, let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error} Event.User, Event.Oneshot, Event.Error}
let fdi = int(fd) let fdi = int(fd)
@ -392,9 +392,19 @@ proc selectInto*[T](s: Selector[T], timeout: int,
let pevents = resTable[i].events let pevents = resTable[i].events
var pkey = addr(s.fds[fdi]) var pkey = addr(s.fds[fdi])
doAssert(pkey.ident != 0) doAssert(pkey.ident != 0)
var rkey = ReadyKey(fd: int(fdi), events: {}) var rkey = ReadyKey(fd: fdi, events: {})
if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0: if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
if (pevents and EPOLLHUP) != 0:
rkey.errorCode = ECONNRESET.OSErrorCode
else:
# Try reading SO_ERROR from fd.
var error: cint
var size = sizeof(error).SockLen
if getsockopt(fdi.SocketHandle, SOL_SOCKET, SO_ERROR, addr(error),
addr(size)) == 0'i32:
rkey.errorCode = error.OSErrorCode
rkey.events.incl(Event.Error) rkey.events.incl(Event.Error)
if (pevents and EPOLLOUT) != 0: if (pevents and EPOLLOUT) != 0:
rkey.events.incl(Event.Write) rkey.events.incl(Event.Write)
@ -482,7 +492,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0) (s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
return s.fds[fd].ident != 0 return s.fds[fd.int].ident != 0
proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd) let fdi = int(fd)
@ -516,3 +526,6 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
body1 body1
else: else:
body2 body2
proc getFd*[T](s: Selector[T]): int =
return s.epollFd.int

View file

@ -217,7 +217,7 @@ else:
raiseIOSelectorsError(osLastError()) raiseIOSelectorsError(osLastError())
s.changes.setLen(0) s.changes.setLen(0)
proc registerHandle*[T](s: Selector[T], fd: SocketHandle, proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) = events: set[Event], data: T) =
let fdi = int(fd) let fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
@ -235,7 +235,7 @@ proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
when not declared(CACHE_EVENTS): when not declared(CACHE_EVENTS):
flushKQueue(s) flushKQueue(s)
proc updateHandle*[T](s: Selector[T], fd: SocketHandle, proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event]) = events: set[Event]) =
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode, let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error} Event.User, Event.Oneshot, Event.Error}
@ -503,6 +503,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
if (kevent.flags and EV_ERROR) != 0: if (kevent.flags and EV_ERROR) != 0:
rkey.events = {Event.Error} rkey.events = {Event.Error}
rkey.errorCode = kevent.data.OSErrorCode
case kevent.filter: case kevent.filter:
of EVFILT_READ: of EVFILT_READ:
@ -569,6 +570,13 @@ proc selectInto*[T](s: Selector[T], timeout: int,
doAssert(true, "Unsupported kqueue filter in the queue!") doAssert(true, "Unsupported kqueue filter in the queue!")
if (kevent.flags and EV_EOF) != 0: if (kevent.flags and EV_EOF) != 0:
if kevent.fflags != 0:
rkey.errorCode = kevent.fflags.OSErrorCode
else:
# This assumes we are dealing with sockets.
# TODO: For future-proofing it might be a good idea to give the
# user access to the raw `kevent`.
rkey.errorCode = ECONNRESET.OSErrorCode
rkey.events.incl(Event.Error) rkey.events.incl(Event.Error)
results[k] = rkey results[k] = rkey
@ -585,7 +593,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0) (s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
return s.fds[fd].ident != 0 return s.fds[fd.int].ident != 0
proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd) let fdi = int(fd)
@ -619,3 +627,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
body1 body1
else: else:
body2 body2
proc getFd*[T](s: Selector[T]): int =
return s.kqFD.int

View file

@ -141,7 +141,7 @@ template checkFd(s, f) =
if f >= s.maxFD: if f >= s.maxFD:
raiseIOSelectorsError("Maximum number of descriptors is exhausted!") raiseIOSelectorsError("Maximum number of descriptors is exhausted!")
proc registerHandle*[T](s: Selector[T], fd: SocketHandle, proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) = events: set[Event], data: T) =
var fdi = int(fd) var fdi = int(fd)
s.checkFd(fdi) s.checkFd(fdi)
@ -149,7 +149,7 @@ proc registerHandle*[T](s: Selector[T], fd: SocketHandle,
setKey(s, fdi, events, 0, data) setKey(s, fdi, events, 0, data)
if events != {}: s.pollAdd(fdi.cint, events) if events != {}: s.pollAdd(fdi.cint, events)
proc updateHandle*[T](s: Selector[T], fd: SocketHandle, proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event]) = events: set[Event]) =
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode, let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error} Event.User, Event.Oneshot, Event.Error}
@ -280,7 +280,7 @@ template isEmpty*[T](s: Selector[T]): bool =
(s.count == 0) (s.count == 0)
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
return s.fds[fd].ident != 0 return s.fds[fd.int].ident != 0
proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
let fdi = int(fd) let fdi = int(fd)
@ -314,3 +314,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
body1 body1
else: else:
body2 body2
proc getFd*[T](s: Selector[T]): int =
return -1

View file

@ -229,7 +229,7 @@ proc delKey[T](s: Selector[T], fd: SocketHandle) =
doAssert(i < FD_SETSIZE, doAssert(i < FD_SETSIZE,
"Descriptor [" & $int(fd) & "] is not registered in the queue!") "Descriptor [" & $int(fd) & "] is not registered in the queue!")
proc registerHandle*[T](s: Selector[T], fd: SocketHandle, proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event], data: T) = events: set[Event], data: T) =
when not defined(windows): when not defined(windows):
let fdi = int(fd) let fdi = int(fd)
@ -255,7 +255,7 @@ proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
IOFD_SET(ev.rsock, addr s.rSet) IOFD_SET(ev.rsock, addr s.rSet)
inc(s.count) inc(s.count)
proc updateHandle*[T](s: Selector[T], fd: SocketHandle, proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event]) = events: set[Event]) =
let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode, let maskEvents = {Event.Timer, Event.Signal, Event.Process, Event.Vnode,
Event.User, Event.Oneshot, Event.Error} Event.User, Event.Oneshot, Event.Error}
@ -453,3 +453,6 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
else: else:
body2 body2
proc getFd*[T](s: Selector[T]): int =
return -1

View file

@ -187,12 +187,12 @@ proc toSockType*(protocol: Protocol): SockType =
proc newNativeSocket*(domain: Domain = AF_INET, proc newNativeSocket*(domain: Domain = AF_INET,
sockType: SockType = SOCK_STREAM, sockType: SockType = SOCK_STREAM,
protocol: Protocol = IPPROTO_TCP): SocketHandle = protocol: Protocol = IPPROTO_TCP): SocketHandle =
## Creates a new socket; returns `InvalidSocket` if an error occurs. ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
socket(toInt(domain), toInt(sockType), toInt(protocol)) socket(toInt(domain), toInt(sockType), toInt(protocol))
proc newNativeSocket*(domain: cint, sockType: cint, proc newNativeSocket*(domain: cint, sockType: cint,
protocol: cint): SocketHandle = protocol: cint): SocketHandle =
## Creates a new socket; returns `InvalidSocket` if an error occurs. ## Creates a new socket; returns `osInvalidSocket` if an error occurs.
## ##
## Use this overload if one of the enums specified above does ## Use this overload if one of the enums specified above does
## not contain what you need. ## not contain what you need.
@ -666,6 +666,19 @@ proc selectWrite*(writefds: var seq[SocketHandle],
pruneSocketSet(writefds, (wr)) pruneSocketSet(writefds, (wr))
proc accept*(fd: SocketHandle): (SocketHandle, string) =
## Accepts a new client connection.
##
## Returns (osInvalidSocket, "") if an error occurred.
var sockAddress: Sockaddr_in
var addrLen = sizeof(sockAddress).SockLen
var sock = accept(fd, cast[ptr SockAddr](addr(sockAddress)),
addr(addrLen))
if sock == osInvalidSocket:
return (osInvalidSocket, "")
else:
return (sock, $inet_ntoa(sockAddress.sin_addr))
when defined(Windows): when defined(Windows):
var wsa: WSAData var wsa: WSAData
if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError()) if wsaStartup(0x0101'i16, addr wsa) != 0: raiseOSError(osLastError())

View file

@ -753,10 +753,8 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
## flag is specified then this error will not be raised and instead ## flag is specified then this error will not be raised and instead
## accept will be called again. ## accept will be called again.
assert(client != nil) assert(client != nil)
var sockAddress: Sockaddr_in let ret = accept(server.fd)
var addrLen = sizeof(sockAddress).SockLen let sock = ret[0]
var sock = accept(server.fd, cast[ptr SockAddr](addr(sockAddress)),
addr(addrLen))
if sock == osInvalidSocket: if sock == osInvalidSocket:
let err = osLastError() let err = osLastError()
@ -764,6 +762,7 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
acceptAddr(server, client, address, flags) acceptAddr(server, client, address, flags)
raiseOSError(err) raiseOSError(err)
else: else:
address = ret[1]
client.fd = sock client.fd = sock
client.isBuffered = server.isBuffered client.isBuffered = server.isBuffered
@ -776,9 +775,6 @@ proc acceptAddr*(server: Socket, client: var Socket, address: var string,
let ret = SSLAccept(client.sslHandle) let ret = SSLAccept(client.sslHandle)
socketError(client, ret, false) socketError(client, ret, false)
# Client socket is set above.
address = $inet_ntoa(sockAddress.sin_addr)
when false: #defineSsl: when false: #defineSsl:
proc acceptAddrSSL*(server: Socket, client: var Socket, proc acceptAddrSSL*(server: Socket, client: var Socket,
address: var string): SSLAcceptResult {. address: var string): SSLAcceptResult {.

View file

@ -139,10 +139,15 @@ proc findExe*(exe: string, followSymlinks: bool = true;
## is added the `ExeExts <#ExeExts>`_ file extensions if it has none. ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none.
## If the system supports symlinks it also resolves them until it ## If the system supports symlinks it also resolves them until it
## meets the actual file. This behavior can be disabled if desired. ## meets the actual file. This behavior can be disabled if desired.
template checkCurrentDir() =
for ext in extensions: for ext in extensions:
result = addFileExt(exe, ext) result = addFileExt(exe, ext)
if existsFile(result): return if existsFile(result): return
var path = string(getEnv("PATH")) when defined(posix):
if '/' in exe: checkCurrentDir()
else:
checkCurrentDir()
let path = string(getEnv("PATH"))
for candidate in split(path, PathSep): for candidate in split(path, PathSep):
when defined(windows): when defined(windows):
var x = (if candidate[0] == '"' and candidate[^1] == '"': var x = (if candidate[0] == '"' and candidate[^1] == '"':

View file

@ -1,195 +1,6 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Nim Contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import strutils
const Sha1DigestSize = 20
type
Sha1Digest = array[0 .. Sha1DigestSize-1, uint8]
SecureHash* = distinct Sha1Digest
# Copyright (c) 2011, Micael Hildenborg
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Micael Hildenborg nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Ported to Nim by Erik O'Leary
type
Sha1State* = array[0 .. 5-1, uint32]
Sha1Buffer = array[0 .. 80-1, uint32]
template clearBuffer(w: Sha1Buffer, len = 16) =
zeroMem(addr(w), len * sizeof(uint32))
proc init*(result: var Sha1State) =
result[0] = 0x67452301'u32
result[1] = 0xefcdab89'u32
result[2] = 0x98badcfe'u32
result[3] = 0x10325476'u32
result[4] = 0xc3d2e1f0'u32
proc innerHash(state: var Sha1State, w: var Sha1Buffer) =
var
a = state[0]
b = state[1]
c = state[2]
d = state[3]
e = state[4]
var round = 0
template rot(value, bits: uint32): uint32 =
(value shl bits) or (value shr (32 - bits))
template sha1(fun, val: uint32) =
let t = rot(a, 5) + fun + e + val + w[round]
e = d
d = c
c = rot(b, 30)
b = a
a = t
template process(body: untyped) =
w[round] = rot(w[round - 3] xor w[round - 8] xor w[round - 14] xor w[round - 16], 1)
body
inc(round)
template wrap(dest, value: untyped) =
let v = dest + value
dest = v
while round < 16:
sha1((b and c) or (not b and d), 0x5a827999'u32)
inc(round)
while round < 20:
process:
sha1((b and c) or (not b and d), 0x5a827999'u32)
while round < 40:
process:
sha1(b xor c xor d, 0x6ed9eba1'u32)
while round < 60:
process:
sha1((b and c) or (b and d) or (c and d), 0x8f1bbcdc'u32)
while round < 80:
process:
sha1(b xor c xor d, 0xca62c1d6'u32)
wrap state[0], a
wrap state[1], b
wrap state[2], c
wrap state[3], d
wrap state[4], e
proc sha1(src: cstring; len: int): Sha1Digest =
#Initialize state
var state: Sha1State
init(state)
#Create w buffer
var w: Sha1Buffer
#Loop through all complete 64byte blocks.
let byteLen = len
let endOfFullBlocks = byteLen - 64
var endCurrentBlock = 0
var currentBlock = 0
while currentBlock <= endOfFullBlocks:
endCurrentBlock = currentBlock + 64
var i = 0
while currentBlock < endCurrentBlock:
w[i] = uint32(src[currentBlock+3]) or
uint32(src[currentBlock+2]) shl 8'u32 or
uint32(src[currentBlock+1]) shl 16'u32 or
uint32(src[currentBlock]) shl 24'u32
currentBlock += 4
inc(i)
innerHash(state, w)
#Handle last and not full 64 byte block if existing
endCurrentBlock = byteLen - currentBlock
clearBuffer(w)
var lastBlockBytes = 0
while lastBlockBytes < endCurrentBlock:
var value = uint32(src[lastBlockBytes + currentBlock]) shl
((3'u32 - uint32(lastBlockBytes and 3)) shl 3)
w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value
inc(lastBlockBytes)
w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or (
0x80'u32 shl ((3'u32 - uint32(lastBlockBytes and 3)) shl 3)
)
if endCurrentBlock >= 56:
innerHash(state, w)
clearBuffer(w)
w[15] = uint32(byteLen) shl 3
innerHash(state, w)
# Store hash in result pointer, and make sure we get in in the correct order
# on both endian models.
for i in 0 .. Sha1DigestSize-1:
result[i] = uint8((int(state[i shr 2]) shr ((3-(i and 3)) * 8)) and 255)
proc sha1(src: string): Sha1Digest =
## Calculate SHA1 from input string
sha1(src, src.len)
proc secureHash*(str: string): SecureHash = SecureHash(sha1(str))
proc secureHashFile*(filename: string): SecureHash = secureHash(readFile(filename))
proc `$`*(self: SecureHash): string =
result = ""
for v in Sha1Digest(self):
result.add(toHex(int(v), 2))
proc parseSecureHash*(hash: string): SecureHash =
for i in 0 ..< Sha1DigestSize:
Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1]))
proc `==`*(a, b: SecureHash): bool =
# Not a constant-time comparison, but that's acceptable in this context
Sha1Digest(a) == Sha1Digest(b)
when isMainModule: ## This module is a deprecated alias for the ``sha1`` module.
let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]") {.deprecated.}
doAssert hash1 == hash1
doAssert parseSecureHash($hash1) == hash1 include "../std/sha1"

View file

@ -54,9 +54,9 @@ when defined(nimdoc):
Timer, ## Timer descriptor is completed Timer, ## Timer descriptor is completed
Signal, ## Signal is raised Signal, ## Signal is raised
Process, ## Process is finished Process, ## Process is finished
Vnode, ## BSD specific file change happens Vnode, ## BSD specific file change
User, ## User event is raised User, ## User event is raised
Error, ## Error happens while waiting, for descriptor Error, ## Error occurred while waiting for descriptor
VnodeWrite, ## NOTE_WRITE (BSD specific, write to file occurred) VnodeWrite, ## NOTE_WRITE (BSD specific, write to file occurred)
VnodeDelete, ## NOTE_DELETE (BSD specific, unlink of file occurred) VnodeDelete, ## NOTE_DELETE (BSD specific, unlink of file occurred)
VnodeExtend, ## NOTE_EXTEND (BSD specific, file extended) VnodeExtend, ## NOTE_EXTEND (BSD specific, file extended)
@ -69,6 +69,8 @@ when defined(nimdoc):
## An object which holds result for descriptor ## An object which holds result for descriptor
fd* : int ## file/socket descriptor fd* : int ## file/socket descriptor
events*: set[Event] ## set of events events*: set[Event] ## set of events
errorCode*: OSErrorCode ## additional error code information for
## Error events
SelectEvent* = object SelectEvent* = object
## An object which holds user defined event ## An object which holds user defined event
@ -79,13 +81,14 @@ when defined(nimdoc):
proc close*[T](s: Selector[T]) = proc close*[T](s: Selector[T]) =
## Closes the selector. ## Closes the selector.
proc registerHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event], proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
data: T) = events: set[Event], data: T) =
## Registers file/socket descriptor ``fd`` to selector ``s`` ## Registers file/socket descriptor ``fd`` to selector ``s``
## with events set in ``events``. The ``data`` is application-defined ## with events set in ``events``. The ``data`` is application-defined
## data, which will be passed when an event is triggered. ## data, which will be passed when an event is triggered.
proc updateHandle*[T](s: Selector[T], fd: SocketHandle, events: set[Event]) = proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
events: set[Event]) =
## Update file/socket descriptor ``fd``, registered in selector ## Update file/socket descriptor ``fd``, registered in selector
## ``s`` with new events set ``event``. ## ``s`` with new events set ``event``.
@ -221,11 +224,15 @@ when defined(nimdoc):
proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
## Determines whether selector contains a file descriptor. ## Determines whether selector contains a file descriptor.
proc getFd*[T](s: Selector[T]): int =
## Retrieves the underlying selector's file descriptor.
##
## For *poll* and *select* selectors ``-1`` is returned.
else: else:
when hasThreadSupport: when hasThreadSupport:
import locks import locks
type type
SharedArray[T] = UncheckedArray[T] SharedArray[T] = UncheckedArray[T]
@ -234,7 +241,6 @@ else:
proc deallocSharedArray[T](sa: ptr SharedArray[T]) = proc deallocSharedArray[T](sa: ptr SharedArray[T]) =
deallocShared(cast[pointer](sa)) deallocShared(cast[pointer](sa))
type type
Event* {.pure.} = enum Event* {.pure.} = enum
Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot, Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot,
@ -247,6 +253,7 @@ else:
ReadyKey* = object ReadyKey* = object
fd* : int fd* : int
events*: set[Event] events*: set[Event]
errorCode*: OSErrorCode
SelectorKey[T] = object SelectorKey[T] = object
ident: int ident: int

View file

@ -178,8 +178,7 @@ proc parseUri*(uri: string, result: var Uri) =
i.inc(2) # Skip // i.inc(2) # Skip //
var authority = "" var authority = ""
i.inc parseUntil(uri, authority, {'/', '?', '#'}, i) i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
if authority == "": if authority.len > 0:
raise newException(ValueError, "Expected authority got nothing.")
parseAuthority(authority, result) parseAuthority(authority, result)
else: else:
result.opaque = true result.opaque = true
@ -466,6 +465,15 @@ when isMainModule:
doAssert test.port == "dom96" doAssert test.port == "dom96"
doAssert test.path == "/packages" doAssert test.path == "/packages"
block:
let str = "file:///foo/bar/baz.txt"
let test = parseUri(str)
doAssert test.scheme == "file"
doAssert test.username == ""
doAssert test.hostname == ""
doAssert test.port == ""
doAssert test.path == "/foo/bar/baz.txt"
# Remove dot segments tests # Remove dot segments tests
block: block:
doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz"

195
lib/std/sha1.nim Normal file
View file

@ -0,0 +1,195 @@
#
#
# The Nim Compiler
# (c) Copyright 2015 Nim Contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
import strutils
const Sha1DigestSize = 20
type
Sha1Digest = array[0 .. Sha1DigestSize-1, uint8]
SecureHash* = distinct Sha1Digest
# Copyright (c) 2011, Micael Hildenborg
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of Micael Hildenborg nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Ported to Nim by Erik O'Leary
type
Sha1State* = array[0 .. 5-1, uint32]
Sha1Buffer = array[0 .. 80-1, uint32]
template clearBuffer(w: Sha1Buffer, len = 16) =
zeroMem(addr(w), len * sizeof(uint32))
proc init*(result: var Sha1State) =
result[0] = 0x67452301'u32
result[1] = 0xefcdab89'u32
result[2] = 0x98badcfe'u32
result[3] = 0x10325476'u32
result[4] = 0xc3d2e1f0'u32
proc innerHash(state: var Sha1State, w: var Sha1Buffer) =
var
a = state[0]
b = state[1]
c = state[2]
d = state[3]
e = state[4]
var round = 0
template rot(value, bits: uint32): uint32 =
(value shl bits) or (value shr (32u32 - bits))
template sha1(fun, val: uint32) =
let t = rot(a, 5) + fun + e + val + w[round]
e = d
d = c
c = rot(b, 30)
b = a
a = t
template process(body: untyped) =
w[round] = rot(w[round - 3] xor w[round - 8] xor w[round - 14] xor w[round - 16], 1)
body
inc(round)
template wrap(dest, value: untyped) =
let v = dest + value
dest = v
while round < 16:
sha1((b and c) or (not b and d), 0x5a827999'u32)
inc(round)
while round < 20:
process:
sha1((b and c) or (not b and d), 0x5a827999'u32)
while round < 40:
process:
sha1(b xor c xor d, 0x6ed9eba1'u32)
while round < 60:
process:
sha1((b and c) or (b and d) or (c and d), 0x8f1bbcdc'u32)
while round < 80:
process:
sha1(b xor c xor d, 0xca62c1d6'u32)
wrap state[0], a
wrap state[1], b
wrap state[2], c
wrap state[3], d
wrap state[4], e
proc sha1(src: cstring; len: int): Sha1Digest =
#Initialize state
var state: Sha1State
init(state)
#Create w buffer
var w: Sha1Buffer
#Loop through all complete 64byte blocks.
let byteLen = len
let endOfFullBlocks = byteLen - 64
var endCurrentBlock = 0
var currentBlock = 0
while currentBlock <= endOfFullBlocks:
endCurrentBlock = currentBlock + 64
var i = 0
while currentBlock < endCurrentBlock:
w[i] = uint32(src[currentBlock+3]) or
uint32(src[currentBlock+2]) shl 8'u32 or
uint32(src[currentBlock+1]) shl 16'u32 or
uint32(src[currentBlock]) shl 24'u32
currentBlock += 4
inc(i)
innerHash(state, w)
#Handle last and not full 64 byte block if existing
endCurrentBlock = byteLen - currentBlock
clearBuffer(w)
var lastBlockBytes = 0
while lastBlockBytes < endCurrentBlock:
var value = uint32(src[lastBlockBytes + currentBlock]) shl
((3'u32 - uint32(lastBlockBytes and 3)) shl 3)
w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or value
inc(lastBlockBytes)
w[lastBlockBytes shr 2] = w[lastBlockBytes shr 2] or (
0x80'u32 shl ((3'u32 - uint32(lastBlockBytes and 3)) shl 3)
)
if endCurrentBlock >= 56:
innerHash(state, w)
clearBuffer(w)
w[15] = uint32(byteLen) shl 3
innerHash(state, w)
# Store hash in result pointer, and make sure we get in in the correct order
# on both endian models.
for i in 0 .. Sha1DigestSize-1:
result[i] = uint8((int(state[i shr 2]) shr ((3-(i and 3)) * 8)) and 255)
proc sha1(src: string): Sha1Digest =
## Calculate SHA1 from input string
sha1(src, src.len)
proc secureHash*(str: string): SecureHash = SecureHash(sha1(str))
proc secureHashFile*(filename: string): SecureHash = secureHash(readFile(filename))
proc `$`*(self: SecureHash): string =
result = ""
for v in Sha1Digest(self):
result.add(toHex(int(v), 2))
proc parseSecureHash*(hash: string): SecureHash =
for i in 0 ..< Sha1DigestSize:
Sha1Digest(result)[i] = uint8(parseHexInt(hash[i*2] & hash[i*2 + 1]))
proc `==`*(a, b: SecureHash): bool =
# Not a constant-time comparison, but that's acceptable in this context
Sha1Digest(a) == Sha1Digest(b)
when isMainModule:
let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]")
doAssert hash1 == hash1
doAssert parseSecureHash($hash1) == hash1

View file

@ -1955,13 +1955,13 @@ const
## that you cannot compare a floating point value to this value ## that you cannot compare a floating point value to this value
## and expect a reasonable result - use the `classify` procedure ## and expect a reasonable result - use the `classify` procedure
## in the module ``math`` for checking for NaN. ## in the module ``math`` for checking for NaN.
NimMajor*: int = 0 NimMajor* {.intdefine.}: int = 0
## is the major number of Nim's version. ## is the major number of Nim's version.
NimMinor*: int = 17 NimMinor* {.intdefine.}: int = 17
## is the minor number of Nim's version. ## is the minor number of Nim's version.
NimPatch*: int = 3 NimPatch* {.intdefine.}: int = 3
## is the patch number of Nim's version. ## is the patch number of Nim's version.
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
@ -3025,9 +3025,9 @@ when not defined(JS): #and not defined(nimscript):
proc endOfFile*(f: File): bool {.tags: [], benign.} proc endOfFile*(f: File): bool {.tags: [], benign.}
## Returns true iff `f` is at the end. ## Returns true iff `f` is at the end.
proc readChar*(f: File): char {.tags: [ReadIOEffect], deprecated.} proc readChar*(f: File): char {.tags: [ReadIOEffect].}
## Reads a single character from the stream `f`. **Deprecated** since ## Reads a single character from the stream `f`. Should not be used in
## version 0.16.2. Use some variant of ``readBuffer`` instead. ## performance sensitive code.
proc flushFile*(f: File) {.tags: [WriteIOEffect].} proc flushFile*(f: File) {.tags: [WriteIOEffect].}
## Flushes `f`'s buffer. ## Flushes `f`'s buffer.
@ -3769,7 +3769,6 @@ proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
# by ``assert``. # by ``assert``.
type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect, type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect,
tags: [].} tags: [].}
{.deprecated: [THide: Hide].}
Hide(raiseAssert)(msg) Hide(raiseAssert)(msg)
template assert*(cond: bool, msg = "") = template assert*(cond: bool, msg = "") =

View file

@ -104,7 +104,7 @@ type
slBitmap: array[RealFli, uint32] slBitmap: array[RealFli, uint32]
matrix: array[RealFli, array[MaxSli, PBigChunk]] matrix: array[RealFli, array[MaxSli, PBigChunk]]
llmem: PLLChunk llmem: PLLChunk
currMem, maxMem, freeMem: int # memory sizes (allocated from OS) currMem, maxMem, freeMem, occ: int # memory sizes (allocated from OS)
lastSize: int # needed for the case that OS gives us pages linearly lastSize: int # needed for the case that OS gives us pages linearly
chunkStarts: IntSet chunkStarts: IntSet
root, deleted, last, freeAvlNodes: PAvlNode root, deleted, last, freeAvlNodes: PAvlNode
@ -421,7 +421,7 @@ const nimMaxHeap {.intdefine.} = 0
proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
when not defined(emscripten): when not defined(emscripten):
if not a.blockChunkSizeIncrease: if not a.blockChunkSizeIncrease:
let usedMem = a.currMem # - a.freeMem let usedMem = a.occ #a.currMem # - a.freeMem
when nimMaxHeap != 0: when nimMaxHeap != 0:
if usedMem > nimMaxHeap * 1024 * 1024: if usedMem > nimMaxHeap * 1024 * 1024:
raiseOutOfMem() raiseOutOfMem()
@ -567,7 +567,6 @@ proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) =
addChunkToMatrix(a, rest) addChunkToMatrix(a, rest)
proc getBigChunk(a: var MemRegion, size: int): PBigChunk = proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
# use first fit for now:
sysAssert(size > 0, "getBigChunk 2") sysAssert(size > 0, "getBigChunk 2")
var size = size # roundup(size, PageSize) var size = size # roundup(size, PageSize)
var fl, sl: int var fl, sl: int
@ -627,6 +626,85 @@ else:
c = c.next c = c.next
result = true result = true
when false:
var
rsizes: array[50_000, int]
rsizesLen: int
proc trackSize(size: int) =
rsizes[rsizesLen] = size
inc rsizesLen
proc untrackSize(size: int) =
for i in 0 .. rsizesLen-1:
if rsizes[i] == size:
rsizes[i] = rsizes[rsizesLen-1]
dec rsizesLen
return
c_fprintf(stdout, "%ld\n", size)
sysAssert(false, "untracked size!")
else:
template trackSize(x) = discard
template untrackSize(x) = discard
when false:
# not yet used by the GCs
proc rawTryAlloc(a: var MemRegion; requestedSize: int): pointer =
sysAssert(allocInv(a), "rawAlloc: begin")
sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
var size = roundup(requestedSize, MemAlign)
inc a.occ, size
trackSize(size)
sysAssert(size >= requestedSize, "insufficient allocated size!")
#c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
if size <= SmallChunkSize-smallChunkOverhead():
# allocate a small block: for small chunks, we use only its next pointer
var s = size div MemAlign
var c = a.freeSmallChunks[s]
if c == nil:
result = nil
else:
sysAssert c.size == size, "rawAlloc 6"
if c.freeList == nil:
sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
"rawAlloc 7")
result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc)
inc(c.acc, size)
else:
result = c.freeList
sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
c.freeList = c.freeList.next
dec(c.free, size)
sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9")
if c.free < size:
listRemove(a.freeSmallChunks[s], c)
sysAssert(allocInv(a), "rawAlloc: end listRemove test")
sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %%
size == 0, "rawAlloc 21")
sysAssert(allocInv(a), "rawAlloc: end small size")
else:
inc size, bigChunkOverhead()
var fl, sl: int
mappingSearch(size, fl, sl)
sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
let c = findSuitableBlock(a, fl, sl)
if c != nil:
removeChunkFromMatrix2(a, c, fl, sl)
if c.size >= size + PageSize:
splitChunk(a, c, size)
# set 'used' to to true:
c.prevSize = 1
incl(a, a.chunkStarts, pageIndex(c))
dec(a.freeMem, size)
result = addr(c.data)
sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13")
sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
if a.root == nil: a.root = getBottom(a)
add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
else:
result = nil
proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
sysAssert(allocInv(a), "rawAlloc: begin") sysAssert(allocInv(a), "rawAlloc: begin")
sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
@ -676,6 +754,8 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %% sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %%
size == 0, "rawAlloc 21") size == 0, "rawAlloc 21")
sysAssert(allocInv(a), "rawAlloc: end small size") sysAssert(allocInv(a), "rawAlloc: end small size")
inc a.occ, size
trackSize(c.size)
else: else:
size = requestedSize + bigChunkOverhead() # roundup(requestedSize+bigChunkOverhead(), PageSize) size = requestedSize + bigChunkOverhead() # roundup(requestedSize+bigChunkOverhead(), PageSize)
# allocate a large block # allocate a large block
@ -687,6 +767,8 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary") sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
if a.root == nil: a.root = getBottom(a) if a.root == nil: a.root = getBottom(a)
add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size) add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
inc a.occ, c.size
trackSize(c.size)
sysAssert(isAccessible(a, result), "rawAlloc 14") sysAssert(isAccessible(a, result), "rawAlloc 14")
sysAssert(allocInv(a), "rawAlloc: end") sysAssert(allocInv(a), "rawAlloc: end")
when logAlloc: cprintf("var pointer_%p = alloc(%ld)\n", result, requestedSize) when logAlloc: cprintf("var pointer_%p = alloc(%ld)\n", result, requestedSize)
@ -703,6 +785,9 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
# `p` is within a small chunk: # `p` is within a small chunk:
var c = cast[PSmallChunk](c) var c = cast[PSmallChunk](c)
var s = c.size var s = c.size
dec a.occ, s
untrackSize(s)
sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %% sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
s == 0, "rawDealloc 3") s == 0, "rawDealloc 3")
var f = cast[ptr FreeCell](p) var f = cast[ptr FreeCell](p)
@ -733,6 +818,9 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
when overwriteFree: c_memset(p, -1'i32, c.size -% bigChunkOverhead()) when overwriteFree: c_memset(p, -1'i32, c.size -% bigChunkOverhead())
# free big chunk # free big chunk
var c = cast[PBigChunk](c) var c = cast[PBigChunk](c)
dec a.occ, c.size
untrackSize(c.size)
sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)"
a.deleted = getBottom(a) a.deleted = getBottom(a)
del(a, a.root, cast[int](addr(c.data))) del(a, a.root, cast[int](addr(c.data)))
freeBigChunk(a, c) freeBigChunk(a, c)
@ -851,7 +939,8 @@ proc deallocOsPages(a: var MemRegion) =
proc getFreeMem(a: MemRegion): int {.inline.} = result = a.freeMem proc getFreeMem(a: MemRegion): int {.inline.} = result = a.freeMem
proc getTotalMem(a: MemRegion): int {.inline.} = result = a.currMem proc getTotalMem(a: MemRegion): int {.inline.} = result = a.currMem
proc getOccupiedMem(a: MemRegion): int {.inline.} = proc getOccupiedMem(a: MemRegion): int {.inline.} =
result = a.currMem - a.freeMem result = a.occ
# a.currMem - a.freeMem
# ---------------------- thread memory region ------------------------------- # ---------------------- thread memory region -------------------------------
@ -893,7 +982,7 @@ template instantiateForRegion(allocator: untyped) =
#sysAssert(result == countFreeMem()) #sysAssert(result == countFreeMem())
proc getTotalMem(): int = return allocator.currMem proc getTotalMem(): int = return allocator.currMem
proc getOccupiedMem(): int = return getTotalMem() - getFreeMem() proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem()
proc getMaxMem*(): int = return getMaxMem(allocator) proc getMaxMem*(): int = return getMaxMem(allocator)
# -------------------- shared heap region ---------------------------------- # -------------------- shared heap region ----------------------------------
@ -944,7 +1033,8 @@ template instantiateForRegion(allocator: untyped) =
sharedMemStatsShared(sharedHeap.currMem) sharedMemStatsShared(sharedHeap.currMem)
proc getOccupiedSharedMem(): int = proc getOccupiedSharedMem(): int =
sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem) sharedMemStatsShared(sharedHeap.occ)
#sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
{.pop.} {.pop.}
{.pop.} {.pop.}

View file

@ -125,7 +125,7 @@ when BitsPerPage mod (sizeof(int)*8) != 0:
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
# forward declarations: # forward declarations:
proc collectCT(gch: var GcHeap) {.benign.} proc collectCT(gch: var GcHeap; size: int) {.benign.}
proc forAllChildren(cell: PCell, op: WalkOp) {.benign.} proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
proc doOperation(p: pointer, op: WalkOp) {.benign.} proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
@ -277,7 +277,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
incTypeSize typ, size incTypeSize typ, size
acquire(gch) acquire(gch)
gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1") gcAssert(typ.kind in {tyRef, tyOptAsRef, tyString, tySequence}, "newObj: 1")
collectCT(gch) collectCT(gch, size + sizeof(Cell))
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
# now it is buffered in the ZCT # now it is buffered in the ZCT
@ -332,7 +332,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
acquire(gch) acquire(gch)
collectCT(gch) collectCT(gch, newsize + sizeof(Cell))
var ol = usrToCell(old) var ol = usrToCell(old)
sysAssert(ol.typ != nil, "growObj: 1") sysAssert(ol.typ != nil, "growObj: 1")
gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
@ -494,8 +494,9 @@ proc collectCTBody(gch: var GcHeap) =
gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold) gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
sysAssert(allocInv(gch.region), "collectCT: end") sysAssert(allocInv(gch.region), "collectCT: end")
proc collectCT(gch: var GcHeap) = proc collectCT(gch: var GcHeap; size: int) =
if getOccupiedMem(gch.region) >= gch.cycleThreshold and gch.recGcLock == 0: if (getOccupiedMem(gch.region) >= gch.cycleThreshold or
size > getFreeMem(gch.region)) and gch.recGcLock == 0:
collectCTBody(gch) collectCTBody(gch)
when not defined(useNimRtl): when not defined(useNimRtl):
@ -530,7 +531,7 @@ when not defined(useNimRtl):
acquire(gch) acquire(gch)
var oldThreshold = gch.cycleThreshold var oldThreshold = gch.cycleThreshold
gch.cycleThreshold = 0 # forces cycle collection gch.cycleThreshold = 0 # forces cycle collection
collectCT(gch) collectCT(gch, 0)
gch.cycleThreshold = oldThreshold gch.cycleThreshold = oldThreshold
release(gch) release(gch)

View file

@ -47,10 +47,22 @@ when not declared(c_fwrite):
# C routine that is used here: # C routine that is used here:
proc c_fread(buf: pointer, size, n: csize, f: File): csize {. proc c_fread(buf: pointer, size, n: csize, f: File): csize {.
importc: "fread", header: "<stdio.h>", tags: [ReadIOEffect].} importc: "fread", header: "<stdio.h>", tags: [ReadIOEffect].}
proc c_fseek(f: File, offset: clong, whence: cint): cint {. when defined(windows):
when not defined(amd64):
proc c_fseek(f: File, offset: int64, whence: cint): cint {.
importc: "fseek", header: "<stdio.h>", tags: [].} importc: "fseek", header: "<stdio.h>", tags: [].}
proc c_ftell(f: File): clong {. proc c_ftell(f: File): int64 {.
importc: "ftell", header: "<stdio.h>", tags: [].} importc: "ftell", header: "<stdio.h>", tags: [].}
else:
proc c_fseek(f: File, offset: int64, whence: cint): cint {.
importc: "_fseeki64", header: "<stdio.h>", tags: [].}
proc c_ftell(f: File): int64 {.
importc: "_ftelli64", header: "<stdio.h>", tags: [].}
else:
proc c_fseek(f: File, offset: int64, whence: cint): cint {.
importc: "fseeko", header: "<stdio.h>", tags: [].}
proc c_ftell(f: File): int64 {.
importc: "ftello", header: "<stdio.h>", tags: [].}
proc c_ferror(f: File): cint {. proc c_ferror(f: File): cint {.
importc: "ferror", header: "<stdio.h>", tags: [].} importc: "ferror", header: "<stdio.h>", tags: [].}
proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize): cint {. proc c_setvbuf(f: File, buf: pointer, mode: cint, size: csize): cint {.
@ -210,12 +222,12 @@ proc readAllBuffer(file: File): string =
result.add(buffer) result.add(buffer)
break break
proc rawFileSize(file: File): int = proc rawFileSize(file: File): int64 =
# this does not raise an error opposed to `getFileSize` # this does not raise an error opposed to `getFileSize`
var oldPos = c_ftell(file) var oldPos = c_ftell(file)
discard c_fseek(file, 0, 2) # seek the end of the file discard c_fseek(file, 0, 2) # seek the end of the file
result = c_ftell(file) result = c_ftell(file)
discard c_fseek(file, clong(oldPos), 0) discard c_fseek(file, oldPos, 0)
proc endOfFile(f: File): bool = proc endOfFile(f: File): bool =
var c = c_fgetc(f) var c = c_fgetc(f)
@ -223,7 +235,7 @@ proc endOfFile(f: File): bool =
return c < 0'i32 return c < 0'i32
#result = c_feof(f) != 0 #result = c_feof(f) != 0
proc readAllFile(file: File, len: int): string = proc readAllFile(file: File, len: int64): string =
# We acquire the filesize beforehand and hope it doesn't change. # We acquire the filesize beforehand and hope it doesn't change.
# Speeds things up. # Speeds things up.
result = newString(len) result = newString(len)
@ -363,7 +375,7 @@ proc open(f: var File, filehandle: FileHandle, mode: FileMode): bool =
result = f != nil result = f != nil
proc setFilePos(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) = proc setFilePos(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) =
if c_fseek(f, clong(pos), cint(relativeTo)) != 0: if c_fseek(f, pos, cint(relativeTo)) != 0:
raiseEIO("cannot set file position") raiseEIO("cannot set file position")
proc getFilePos(f: File): int64 = proc getFilePos(f: File): int64 =

File diff suppressed because it is too large Load diff

View file

@ -95,6 +95,7 @@ This project exists thanks to all the people who contribute. [Read on to find ou
[![Backers on Open Collective](https://opencollective.com/nim/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/nim/sponsors/badge.svg)](#sponsors) [![Backers on Open Collective](https://opencollective.com/nim/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/nim/sponsors/badge.svg)](#sponsors)
[![Setup a bounty via Bountysource][badge-nim-bountysource]][nim-bountysource] [![Setup a bounty via Bountysource][badge-nim-bountysource]][nim-bountysource]
[![Donate Bitcoins][badge-nim-bitcoin]][nim-bitcoin] [![Donate Bitcoins][badge-nim-bitcoin]][nim-bitcoin]
[![Open Source Helpers](https://www.codetriage.com/nim-lang/nim/badges/users.svg)](https://www.codetriage.com/nim-lang/nim)
We welcome all contributions to Nim regardless of how small or large We welcome all contributions to Nim regardless of how small or large
they are. Everything from spelling fixes to new modules to be included in the they are. Everything from spelling fixes to new modules to be included in the

9
tests/ccgbugs/t7079.nim Normal file
View file

@ -0,0 +1,9 @@
discard """
action: run
targets: '''c js'''
"""
import math
let x = -0.0
doAssert classify(x) == fcNegZero
doAssert classify(1 / -0.0) == fcNegInf

View file

@ -1,25 +1,48 @@
discard """ discard """
errormsg: "type mismatch: got (Bar[system.int])" errormsg: "type mismatch: got (Bar[system.int])"
nimout: ''' nimout: '''
t3330.nim(40, 4) Error: type mismatch: got (Bar[system.int]) t3330.nim(63, 4) Error: type mismatch: got (Bar[system.int])
but expected one of: but expected one of:
proc test(foo: Foo[int]) proc test(foo: Foo[int])
t3330.nim(25, 8) Hint: Non-matching candidates for add(k, string, T) t3330.nim(48, 8) Hint: Non-matching candidates for add(k, string, T)
proc add(x: var string; y: string) proc add(x: var string; y: string)
first type mismatch at position: 1
required type: var string
but expression 'k' is of type: Alias
proc add(x: var string; y: char) proc add(x: var string; y: char)
first type mismatch at position: 1
required type: var string
but expression 'k' is of type: Alias
proc add(result: var string; x: int64) proc add(result: var string; x: int64)
first type mismatch at position: 1
required type: var string
but expression 'k' is of type: Alias
proc add(result: var string; x: float) proc add(result: var string; x: float)
first type mismatch at position: 1
required type: var string
but expression 'k' is of type: Alias
proc add(x: var string; y: cstring) proc add(x: var string; y: cstring)
first type mismatch at position: 1
required type: var string
but expression 'k' is of type: Alias
proc add[T](x: var seq[T]; y: openArray[T]) proc add[T](x: var seq[T]; y: openArray[T])
first type mismatch at position: 1
required type: var seq[T]
but expression 'k' is of type: Alias
proc add[T](x: var seq[T]; y: T) proc add[T](x: var seq[T]; y: T)
first type mismatch at position: 1
required type: var seq[T]
but expression 'k' is of type: Alias
t3330.nim(25, 8) template/generic instantiation from here t3330.nim(48, 8) template/generic instantiation from here
t3330.nim(32, 6) Foo: 'bar.value' cannot be assigned to t3330.nim(55, 6) Foo: 'bar.value' cannot be assigned to
t3330.nim(25, 8) template/generic instantiation from here t3330.nim(48, 8) template/generic instantiation from here
t3330.nim(33, 6) Foo: 'bar.x' cannot be assigned to t3330.nim(56, 6) Foo: 'bar.x' cannot be assigned to
'''
expression: test(bar)'''
""" """
type type
Foo[T] = concept k Foo[T] = concept k
add(k, string, T) add(k, string, T)

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 10 line: 10
errormsg: '''expression 'open(f, "arg.txt", fmRead, -1)' is of type 'bool' and has to be discarded''' errormsg: '''expression 'open(f, "arg.txt", fmRead, -1)' is of type 'bool' and has to be discarded; start of expression here: tneedsdiscard.nim(7, 2)'''
""" """
proc p = proc p =

View file

@ -0,0 +1,22 @@
discard """
cmd: "nim check $file"
errormsg: "type mismatch: got (int literal(1), int literal(2), int literal(3))"
nimout: '''
but expected one of:
proc main(a, b, c: string)
first type mismatch at position: 1
required type: string
but expression '1' is of type: int literal(1)
expression: main(1, 2, 3)
'''
"""
const
myconst = "abcdefghijklmnopqrstuvwxyz"
proc main(a, b, c: string) {.deprecated: "use foo " & "instead " & myconst.} =
return
main(1, 2, 3)

View file

@ -0,0 +1,11 @@
discard """
errormsg: "in expression '("
nimout: '''
Error: in expression '(
890)': identifier expected, but found ''
'''
line: 11
"""
var n: int : 890

View file

@ -21,5 +21,13 @@ proc test2() =
testTemplate(Exception) testTemplate(Exception)
doAssert(not declared(foobar)) doAssert(not declared(foobar))
proc testTryAsExpr(i: int) =
let x = try: i
except ValueError as ex:
echo(ex.msg)
-1
test[Exception]() test[Exception]()
test2() test2()
testTryAsExpr(5)

View file

@ -0,0 +1,12 @@
discard """
output: '''int64
int64'''
"""
import typetraits
proc `@`[T: SomeInteger](x, y: T): T = x
echo(type(5'i64 @ 6'i32))
echo(type(5'i32 @ 6'i64))

View file

@ -1,5 +1,5 @@
import import
unittest, osproc, streams, os unittest, osproc, streams, os, strformat
const STRING_DATA = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet." const STRING_DATA = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
const TEST_FILE = "tests/testdata/string.txt" const TEST_FILE = "tests/testdata/string.txt"
@ -23,3 +23,26 @@ suite "io":
test "file": test "file":
check: check:
readFile(TEST_FILE) == STRING_DATA readFile(TEST_FILE) == STRING_DATA
proc verifyFileSize(sz: int64) =
# issue 7121, large file size (2-4GB and >4Gb)
const fn = "tmpfile112358"
let size_in_mb = sz div 1_000_000
when defined(windows):
discard execProcess(&"fsutil file createnew {fn} {sz}" )
else:
discard execProcess(&"dd if=/dev/zero of={fn} bs=1000000 count={size_in_mb}")
doAssert os.getFileSize(fn) == sz # Verify OS filesize by string
var f = open(fn)
doAssert f.getFileSize() == sz # Verify file handle filesize
f.close()
os.removeFile(fn)
#disable tests for automatic testers
#for s in [50_000_000'i64, 3_000_000_000, 5_000_000_000]:
# verifyFileSize(s)

View file

@ -15,7 +15,7 @@ when haveZipLib:
import import
os, osproc, strutils, parseopt, parsecfg, strtabs, streams, debcreation, os, osproc, strutils, parseopt, parsecfg, strtabs, streams, debcreation,
securehash std / sha1
const const
maxOS = 20 # max number of OSes maxOS = 20 # max number of OSes

View file

@ -65,7 +65,7 @@ srcdoc2: "pure/asyncfile;pure/asyncftpclient;pure/lenientops"
srcdoc2: "pure/md5;pure/rationals" srcdoc2: "pure/md5;pure/rationals"
srcdoc2: "posix/posix;pure/distros;pure/oswalkdir" srcdoc2: "posix/posix;pure/distros;pure/oswalkdir"
srcdoc2: "pure/collections/heapqueue" srcdoc2: "pure/collections/heapqueue"
srcdoc2: "pure/fenv;pure/securehash;impure/rdstdin;pure/strformat" srcdoc2: "pure/fenv;std/sha1;impure/rdstdin;pure/strformat"
srcdoc2: "pure/segfaults" srcdoc2: "pure/segfaults"
srcdoc2: "pure/basic2d;pure/basic3d;pure/mersenne;pure/coro;pure/httpcore" srcdoc2: "pure/basic2d;pure/basic3d;pure/mersenne;pure/coro;pure/httpcore"
srcdoc2: "pure/bitops;pure/nimtracker;pure/punycode;pure/volatile;js/asyncjs" srcdoc2: "pure/bitops;pure/nimtracker;pure/punycode;pure/volatile;js/asyncjs"