diff options
-rw-r--r-- | compiler/ast.nim | 2 | ||||
-rw-r--r-- | compiler/extccomp.nim | 2 | ||||
-rw-r--r-- | compiler/msgs.nim | 55 | ||||
-rw-r--r-- | compiler/parser.nim | 15 | ||||
-rw-r--r-- | compiler/sem.nim | 3 | ||||
-rw-r--r-- | compiler/semdata.nim | 4 | ||||
-rw-r--r-- | compiler/semexprs.nim | 30 | ||||
-rw-r--r-- | compiler/semstmts.nim | 39 | ||||
-rw-r--r-- | compiler/semtypes.nim | 3 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 68 | ||||
-rw-r--r-- | doc/gc.txt | 2 | ||||
-rw-r--r-- | doc/lib.txt | 2 | ||||
-rw-r--r-- | doc/manual.txt | 31 | ||||
-rw-r--r-- | doc/tut1.txt | 8 | ||||
-rw-r--r-- | lib/packages/docutils/rst.nim | 10 | ||||
-rw-r--r-- | lib/pure/httpserver.nim | 5 | ||||
-rw-r--r-- | lib/pure/irc.nim | 8 | ||||
-rw-r--r-- | lib/pure/oids.nim | 6 | ||||
-rw-r--r-- | lib/pure/sockets.nim | 3 | ||||
-rw-r--r-- | lib/pure/times.nim | 3 | ||||
-rw-r--r-- | lib/pure/unicode.nim | 30 | ||||
-rw-r--r-- | lib/system/alloc.nim | 15 | ||||
-rw-r--r-- | lib/system/gc.nim | 21 | ||||
-rw-r--r-- | lib/system/gc2.nim | 3 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 1 | ||||
-rw-r--r-- | tests/run/tstaticparams.nim | 9 | ||||
-rw-r--r-- | tests/run/tusertypeclasses.nim | 28 |
27 files changed, 275 insertions, 131 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 2a7d8a551..5a5d87d06 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -712,7 +712,6 @@ type # -1 means that the size is unkwown align*: int # the type's alignment requirements loc*: TLoc - testeeName*: PIdent # the test variable in user-defined type classes TPair*{.final.} = object key*, val*: PObject @@ -1088,7 +1087,6 @@ proc assignType(dest, src: PType) = dest.size = src.size dest.align = src.align dest.destructor = src.destructor - dest.testeeName = src.testeeName # this fixes 'type TLock = TSysLock': if src.sym != nil: if dest.sym != nil: diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index d7f3386e3..13eb972f6 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -583,7 +583,7 @@ proc CallCCompiler*(projectfile: string) = else: rawMessage(errGenerated, " execution of an external program failed; " & "rerun with --parallelBuild:1 to see the error message") - if optNoLinking notin gGlobalOptions and cmds.len > 0: + if optNoLinking notin gGlobalOptions: # call the linker: var it = PStrEntry(toLink.head) var objfiles = "" diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 5363442b4..895ba71f3 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -436,7 +436,14 @@ type # only 8 bytes. line*, col*: int16 fileIndex*: int32 - + + TErrorOutput* = enum + eStdOut + eStdErr + eInMemory + + TErrorOutputs* = set[TErrorOutput] + ERecoverableError* = object of EInvalidValue ESuggestDone* = object of EBase @@ -534,13 +541,27 @@ var gHintCounter*: int = 0 gWarnCounter*: int = 0 gErrorMax*: int = 1 # stop after gErrorMax errors - gSilence*: int # == 0 if we produce any output at all when useCaas: var stdoutSocket*: TSocket +proc UnknownLineInfo*(): TLineInfo = + result.line = int16(-1) + result.col = int16(-1) + result.fileIndex = -1 + +var + msgContext: seq[TLineInfo] = @[] + lastError = UnknownLineInfo() + bufferedMsgs*: seq[string] + + errorOutputs* = {eStdOut, eStdErr} + +proc clearBufferedMsgs* = + bufferedMsgs = nil + proc SuggestWriteln*(s: string) = - if gSilence == 0: + if eStdOut in errorOutputs: when useCaas: if isNil(stdoutSocket): Writeln(stdout, s) else: @@ -548,6 +569,9 @@ proc SuggestWriteln*(s: string) = stdoutSocket.send(s & "\c\L") else: Writeln(stdout, s) + + if eInMemory in errorOutputs: + bufferedMsgs.safeAdd(s) proc SuggestQuit*() = if not isServing: @@ -570,14 +594,6 @@ const RawWarningFormat* = "Warning: $1" RawHintFormat* = "Hint: $1" -proc UnknownLineInfo*(): TLineInfo = - result.line = int16(-1) - result.col = int16(-1) - result.fileIndex = -1 - -var - msgContext: seq[TLineInfo] = @[] - proc getInfoContextLen*(): int = return msgContext.len proc setInfoContextLen*(L: int) = setLen(msgContext, L) @@ -642,14 +658,18 @@ proc addCheckpoint*(filename: string, line: int) = proc OutWriteln*(s: string) = ## Writes to stdout. Always. - if gSilence == 0: Writeln(stdout, s) + if eStdOut in errorOutputs: Writeln(stdout, s) proc MsgWriteln*(s: string) = ## Writes to stdout. If --stdout option is given, writes to stderr instead. - if gSilence == 0: - if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return - if optStdout in gGlobalOptions: Writeln(stderr, s) - else: Writeln(stdout, s) + if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return + + if optStdout in gGlobalOptions: + if eStdErr in errorOutputs: Writeln(stderr, s) + else: + if eStdOut in errorOutputs: Writeln(stdout, s) + + if eInMemory in errorOutputs: bufferedMsgs.safeAdd(s) proc coordToStr(coord: int): string = if coord == -1: result = "???" @@ -736,9 +756,6 @@ proc rawMessage*(msg: TMsgKind, args: openarray[string]) = proc rawMessage*(msg: TMsgKind, arg: string) = rawMessage(msg, [arg]) -var - lastError = UnknownLineInfo() - proc writeSurroundingSrc(info: TLineInfo) = const indent = " " MsgWriteln(indent & info.sourceLine.ropeToStr) diff --git a/compiler/parser.nim b/compiler/parser.nim index e8439466a..fd51b04ec 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1624,10 +1624,23 @@ proc parseObject(p: var TParser): PNode = return addSon(result, parseObjectPart(p)) +proc parseTypeClassParam(p: var TParser): PNode = + if p.tok.tokType == tkVar: + getTok(p) + result = newNode(nkVarTy) + result.addSon(p.parseSymbol) + else: + result = p.parseSymbol + proc parseTypeClass(p: var TParser): PNode = result = newNodeP(nkTypeClassTy, p) getTok(p) - addSon(result, p.parseSymbol) + var args = newNode(nkArgList) + addSon(result, args) + addSon(args, p.parseTypeClassParam) + while p.tok.TokType == tkComma: + getTok(p) + addSon(args, p.parseTypeClassParam) if p.tok.tokType == tkCurlyDotLe and p.validInd: addSon(result, parsePragma(p)) else: diff --git a/compiler/sem.nim b/compiler/sem.nim index 71951dd3f..ea53afbeb 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -36,7 +36,8 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) proc addParams(c: PContext, n: PNode, kind: TSymKind) proc maybeAddResult(c: PContext, s: PSym, n: PNode) proc instGenericContainer(c: PContext, n: PNode, header: PType): PType -proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode +proc tryExpr(c: PContext, n: PNode, + flags: TExprFlags = {}, bufferErrors = false): PNode proc fixImmediateParams(n: PNode): PNode proc activate(c: PContext, n: PNode) proc semQuoteAst(c: PContext, n: PNode): PNode diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 121bf297d..d02359d4c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -60,6 +60,7 @@ type threadEntries*: TSymSeq # list of thread entries to check AmbiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot # store this info in the syms themselves!) + InTypeClass*: int # > 0 if we are in a user-defined type class InGenericContext*: int # > 0 if we are in a generic type InUnrolledContext*: int # > 0 if we are unrolling a loop InCompilesContext*: int # > 0 if we are in a ``compiles`` magic @@ -72,7 +73,8 @@ type libs*: TLinkedList # all libs used by this module semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} - semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} + semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {}, + bufferErrors = false): PNode {.nimcall.} semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.} semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet semOverloadedCall*: proc (c: PContext, n, nOrig: PNode, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 47e07d402..337224aef 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -299,7 +299,7 @@ proc semOf(c: PContext, n: PNode): PNode = n.typ = getSysType(tyBool) result = n -proc IsOpImpl(c: PContext, n: PNode): PNode = +proc isOpImpl(c: PContext, n: PNode): PNode = InternalAssert n.sonsLen == 3 and n[1].kind == nkSym and n[1].sym.kind == skType and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} @@ -321,10 +321,19 @@ proc IsOpImpl(c: PContext, n: PNode): PNode = else: var match: bool let t2 = n[2].typ - if t2.kind == tyTypeClass: + case t2.kind + of tyTypeClass: var m: TCandidate InitCandidate(m, t2) match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil + of tyOrdinal: + var m: TCandidate + InitCandidate(m, t2) + match = isOrdinalType(t1) + of tySequence, tyArray, tySet: + var m: TCandidate + InitCandidate(m, t2) + match = typeRel(m, t2, t1) != isNone else: match = sameType(t1, t2) @@ -353,7 +362,7 @@ proc semIs(c: PContext, n: PNode): PNode = let t1 = n[1].typ.sons[0] # BUGFIX: don't evaluate this too early: ``T is void`` - if not containsGenericType(t1): result = IsOpImpl(c, n) + if not containsGenericType(t1): result = isOpImpl(c, n) proc semOpAux(c: PContext, n: PNode) = const flags = {efDetermineType} @@ -763,7 +772,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = analyseIfAddressTakenInCall(c, result) if callee.magic != mNone: result = magicsAfterOverloadResolution(c, result, flags) - result = evalAtCompileTime(c, result) + if c.InTypeClass == 0: + result = evalAtCompileTime(c, result) proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # this seems to be a hotspot in the compiler! @@ -814,7 +824,7 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode = proc semExprNoType(c: PContext, n: PNode): PNode = result = semExpr(c, n, {efWantStmt}) - discardCheck(result) + discardCheck(c, result) proc isTypeExpr(n: PNode): bool = case n.kind @@ -1208,7 +1218,7 @@ proc semProcBody(c: PContext, n: PNode): PNode = a.sons[1] = result result = semAsgn(c, a) else: - discardCheck(result) + discardCheck(c, result) closeScope(c) proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) = @@ -1429,12 +1439,12 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = newNode(nkCall, n.info, quotes)]) result = semExpandToAst(c, result) -proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = +proc tryExpr(c: PContext, n: PNode, + flags: TExprFlags = {}, bufferErrors = false): PNode = # watch out, hacks ahead: let oldErrorCount = msgs.gErrorCounter let oldErrorMax = msgs.gErrorMax inc c.InCompilesContext - inc msgs.gSilence # do not halt after first error: msgs.gErrorMax = high(int) @@ -1443,6 +1453,8 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = openScope(c) let oldOwnerLen = len(gOwners) let oldGenerics = c.generics + let oldErrorOutputs = errorOutputs + errorOutputs = if bufferErrors: {eInMemory} else: {} let oldContextLen = msgs.getInfoContextLen() let oldInGenericContext = c.InGenericContext @@ -1465,7 +1477,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = setlen(gOwners, oldOwnerLen) c.currentScope = oldScope dec c.InCompilesContext - dec msgs.gSilence + errorOutputs = oldErrorOutputs msgs.gErrorCounter = oldErrorCount msgs.gErrorMax = oldErrorMax diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ed6787a16..da8ba50a8 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -132,7 +132,7 @@ proc fixNilType(n: PNode) = for it in n: fixNilType(it) n.typ = nil -proc discardCheck(result: PNode) = +proc discardCheck(c: PContext, result: PNode) = if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: if result.kind == nkNilLit: result.typ = nil @@ -142,6 +142,10 @@ proc discardCheck(result: PNode) = while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil + elif c.InTypeClass > 0 and result.typ.kind == tyBool: + let verdict = semConstExpr(c, result) + if verdict.intVal == 0: + localError(result.info, "type class predicate failed.") elif result.typ.kind != tyError and gCmd != cmdInteractive: if result.typ.kind == tyNil: fixNilType(result) @@ -169,7 +173,7 @@ proc semIf(c: PContext, n: PNode): PNode = typ = commonType(typ, it.sons[0].typ) else: illFormedAst(it) if isEmptyType(typ) or typ.kind == tyNil or not hasElse: - for it in n: discardCheck(it.lastSon) + for it in n: discardCheck(c, it.lastSon) result.kind = nkIfStmt # propagate any enforced VoidContext: if typ == EnforceVoidContext: result.typ = EnforceVoidContext @@ -230,7 +234,7 @@ proc semCase(c: PContext, n: PNode): PNode = localError(n.info, errNotAllCasesCovered) closeScope(c) if isEmptyType(typ) or typ.kind == tyNil or not hasElse: - for i in 1..n.len-1: discardCheck(n.sons[i].lastSon) + for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) # propagate any enforced VoidContext: if typ == EnforceVoidContext: result.typ = EnforceVoidContext @@ -275,8 +279,8 @@ proc semTry(c: PContext, n: PNode): PNode = typ = commonType(typ, a.sons[length-1].typ) dec c.p.inTryStmt if isEmptyType(typ) or typ.kind == tyNil: - discardCheck(n.sons[0]) - for i in 1..n.len-1: discardCheck(n.sons[i].lastSon) + discardCheck(c, n.sons[0]) + for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon) if typ == EnforceVoidContext: result.typ = EnforceVoidContext else: @@ -879,8 +883,9 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = openScope(c) if n.sons[genericParamsPos].kind != nkEmpty: illFormedAst(n) # process parameters: - if n.sons[paramsPos].kind != nkEmpty: - semParamList(c, n.sons[ParamsPos], nil, s) + if n.sons[paramsPos].kind != nkEmpty: + var gp = newNodeI(nkGenericParams, n.info) + semParamList(c, n.sons[ParamsPos], gp, s) ParamsTypeCheck(c, s.typ) else: s.typ = newTypeS(tyProc, c) @@ -1104,24 +1109,24 @@ proc finishMethod(c: PContext, s: PSym) = methodDef(s, false) proc semMethod(c: PContext, n: PNode): PNode = - if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "method") + if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "method") result = semProcAux(c, n, skMethod, methodPragmas) var s = result.sons[namePos].sym - if not isGenericRoutine(s): + if not isGenericRoutine(s) and result.sons[bodyPos].kind != nkEmpty: if hasObjParam(s): - methodDef(s, false) + methodDef(s, fromCache=false) else: - LocalError(n.info, errXNeedsParamObjectType, "method") + localError(n.info, errXNeedsParamObjectType, "method") proc semConverterDef(c: PContext, n: PNode): PNode = - if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "converter") + if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter") checkSonsLen(n, bodyPos + 1) result = semProcAux(c, n, skConverter, converterPragmas) var s = result.sons[namePos].sym var t = s.typ - if t.sons[0] == nil: LocalError(n.info, errXNeedsReturnType, "converter") - if sonsLen(t) != 2: LocalError(n.info, errXRequiresOneArgument, "converter") + if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "converter") + if sonsLen(t) != 2: localError(n.info, errXRequiresOneArgument, "converter") addConverter(c, s) proc semMacroDef(c: PContext, n: PNode): PNode = @@ -1129,9 +1134,9 @@ proc semMacroDef(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skMacro, macroPragmas) var s = result.sons[namePos].sym var t = s.typ - if t.sons[0] == nil: LocalError(n.info, errXNeedsReturnType, "macro") + if t.sons[0] == nil: localError(n.info, errXNeedsReturnType, "macro") if n.sons[bodyPos].kind == nkEmpty: - LocalError(n.info, errImplOfXexpected, s.name.s) + localError(n.info, errImplOfXexpected, s.name.s) proc evalInclude(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) @@ -1221,7 +1226,7 @@ proc semStmtList(c: PContext, n: PNode): PNode = voidContext = true n.typ = EnforceVoidContext if i != last or voidContext: - discardCheck(n.sons[i]) + discardCheck(c, n.sons[i]) else: n.typ = n.sons[i].typ if not isEmptyType(n.typ): diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b9893d037..92f47f585 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -876,8 +876,7 @@ proc freshType(res, prev: PType): PType {.inline.} = proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = # if n.sonsLen == 0: return newConstraint(c, tyTypeClass) result = newOrPrevType(tyTypeClass, prev, c) - result.testeeName = considerAcc(n[0]) - result.n = n[3] + result.n = n let pragmas = n[1] diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 318acc660..00f3b2b10 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -85,6 +85,7 @@ proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode, c.calleeSym = callee c.calleeScope = calleeScope initIdTable(c.bindings) + c.errors = nil if binding != nil and callee.kind in RoutineKinds: var typeParams = callee.ast[genericParamsPos] for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1): @@ -202,7 +203,7 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string = add(result, argTypeToString(arg)) if i != sonsLen(n) - 1: add(result, ", ") -proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation +proc typeRel*(c: var TCandidate, f, a: PType): TTypeRelation proc concreteType(c: TCandidate, t: PType): PType = case t.kind of tyArrayConstr: @@ -750,40 +751,55 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, # pushInfoContext(arg.info) openScope(c) + inc c.InTypeClass - var testee = newSym(skParam, f.testeeName, f.sym, f.sym.info) - testee.typ = a - addDecl(c, testee) + finally: + dec c.InTypeClass + closeScope(c) - for stmt in f.n: - var e = c.semTryExpr(c, copyTree(stmt)) - if e == nil: - let expStr = renderTree(stmt, {renderNoComments}) - m.errors.safeAdd("can't compile " & expStr & " for " & a.typeToString) - return nil - case e.kind - of nkReturnStmt: - nil - of nkTypeSection: nil - of nkConstDef: nil + for param in f.n[0]: + var + dummyName: PNode + dummyType: PType + + if param.kind == nkVarTy: + dummyName = param[0] + dummyType = makeVarType(c, a) else: - if e.typ.kind == tyBool: - let verdict = c.semConstExpr(c, e) - if verdict.intVal == 0: - let expStr = renderTree(stmt, {renderNoComments}) - m.errors.safeAdd(expStr & " doesn't hold for " & a.typeToString) - return nil + dummyName = param + dummyType = a + + InternalAssert dummyName.kind == nkIdent + var dummyParam = newSym(skType, dummyName.ident, f.sym, f.sym.info) + dummyParam.typ = dummyType + addDecl(c, dummyParam) - closeScope(c) + for stmt in f.n[3]: + var e = c.semTryExpr(c, copyTree(stmt), bufferErrors = false) + m.errors = bufferedMsgs + clearBufferedMsgs() + if e == nil: return nil + case e.kind + of nkReturnStmt: nil + of nkTypeSection: nil + of nkConstDef: nil + else: nil + result = arg put(m.bindings, f, a) -proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, +proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, argType: PType, argSemantized, argOrig: PNode): PNode = - var arg = argSemantized - var r: TTypeRelation - let fMaybeExpr = f.skipTypes({tyDistinct}) + var + r: TTypeRelation + arg = argSemantized + + let + a = if c.InTypeClass > 0: argType.skipTypes({tyTypeDesc}) + else: argType + fMaybeExpr = f.skipTypes({tyDistinct}) + case fMaybeExpr.kind of tyExpr: if fMaybeExpr.sonsLen == 0: diff --git a/doc/gc.txt b/doc/gc.txt index 854f9ce2a..13498afaa 100644 --- a/doc/gc.txt +++ b/doc/gc.txt @@ -13,7 +13,7 @@ Introduction This document describes how the GC works and how to tune it for (soft) `realtime systems`:idx:. -The basic algorithm is *Deferrent Reference Counting* with cycle detection. +The basic algorithm is *Deferred Reference Counting* with cycle detection. References on the stack are not counted for better performance (and easier C code generation). The GC **never** scans the whole heap but it may scan the delta-subgraph of the heap that changed since its last run. diff --git a/doc/lib.txt b/doc/lib.txt index 1b004aa9d..92a4a7c83 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -64,6 +64,8 @@ Core Collections and algorithms -------------------------- +* `algorithm <algorithm.html>`_ + Implements some common generic algorithms like sort or binary search. * `tables <tables.html>`_ Nimrod hash table support. Contains tables, ordered tables and count tables. * `sets <sets.html>`_ diff --git a/doc/manual.txt b/doc/manual.txt index c63df0304..dabff3d69 100644 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -3289,27 +3289,36 @@ Declarative type classes are written in the following form: for value in c: type(value) is T - -The identifiers following the `generic` keyword are treated as variables of -the matched type and the body of the type class consists of arbitrary code that -must be valid under these circumstances. - -Specifically, the type class will be matched if: +The type class will be matched if: a) all of the expressions within the body can be compiled for the tested type b) all statically evaluatable boolean expressions in the body must be true -Please note that the ``is`` operator allows you to easily verify the precise type -signatures of the required operations, but since type inference and default -parameters are still applied in the provided block, it's also possible to encode -usage protocols that doesn't reveal implementation details. +The identifiers following the `generic` keyword represent instances of the +currently matched type. These instances can act both as variables of the type, +when used in contexts, where a value is expected, and as the type itself, when +used in a contexts, where a type is expected. + +Please note that the ``is`` operator allows you to easily verify the precise +type signatures of the required operations, but since type inference and +default parameters are still applied in the provided block, it's also possible +to encode usage protocols that doesn't reveal implementation details. + +As a special rule providing further convenience when writing type classes, any +type value appearing in a callable expression will be treated as a variable of +the designated type for overload resolution purposes, unless the type value was +passed in its explicit ``typedesc[T]`` form: + +.. code-block:: nimrod + type + OutputStream = generic S + write(var S, string) Much like generics, the user defined type classes will be instantiated exactly once for each tested type and any static code included within them will also be executed once. - Return Type Inference --------------------- diff --git a/doc/tut1.txt b/doc/tut1.txt index 0cc9b05c1..5c1cdb52e 100644 --- a/doc/tut1.txt +++ b/doc/tut1.txt @@ -610,7 +610,7 @@ allow to silently throw away a return value: discard yes("May I ask a pointless question?") -The return value can be ignored implicitely if the called proc/iterator has +The return value can be ignored implicitly if the called proc/iterator has been declared with the ``discardable`` pragma: .. code-block:: nimrod @@ -1077,7 +1077,7 @@ can also be used to include elements (and ranges of elements): TCharSet = set[char] var x: TCharSet - x = {'a'..'z', '0'..'9'} # This constructs a set that conains the + x = {'a'..'z', '0'..'9'} # This constructs a set that contains the # letters from 'a' to 'z' and the digits # from '0' to '9' @@ -1201,7 +1201,7 @@ to specify a range from zero to the specified index minus one: Sequences --------- `Sequences`:idx: are similar to arrays but of dynamic length which may change -during runtime (like strings). Since sequences are resizeable they are always +during runtime (like strings). Since sequences are resizable they are always allocated on the heap and garbage collected. Sequences are always indexed with an ``int`` starting at position 0. @@ -1547,7 +1547,7 @@ exported symbols. An alternative that only imports listed symbols is the Include statement ----------------- -The `include`:idx: statement does something fundametally different than +The `include`:idx: statement does something fundamentally different than importing a module: it merely includes the contents of a file. The ``include`` statement is useful to split up a large module into several files: diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 91664cd50..6dd407155 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -868,6 +868,12 @@ proc parseLine(p: var TRstParser, father: PRstNode) = case p.tok[p.idx].kind of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father) else: break + +proc parseUntilNewline(p: var TRstParser, father: PRstNode) = + while True: + case p.tok[p.idx].kind + of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father) + of tkEof, tkIndent: break proc parseSection(p: var TRstParser, result: PRstNode) proc parseField(p: var TRstParser): PRstNode = @@ -1078,7 +1084,7 @@ proc parseParagraph(p: var TRstParser, result: PRstNode) = proc parseHeadline(p: var TRstParser): PRstNode = result = newRstNode(rnHeadline) - parseLine(p, result) + parseUntilNewLine(p, result) assert(p.tok[p.idx].kind == tkIndent) assert(p.tok[p.idx + 1].kind == tkAdornment) var c = p.tok[p.idx + 1].symbol[0] @@ -1172,7 +1178,7 @@ proc parseOverline(p: var TRstParser): PRstNode = inc(p.idx, 2) result = newRstNode(rnOverline) while true: - parseLine(p, result) + parseUntilNewline(p, result) if p.tok[p.idx].kind == tkIndent: inc(p.idx) if p.tok[p.idx - 1].ival > currInd(p): diff --git a/lib/pure/httpserver.nim b/lib/pure/httpserver.nim index 043e713a6..901fdc779 100644 --- a/lib/pure/httpserver.nim +++ b/lib/pure/httpserver.nim @@ -401,8 +401,9 @@ proc nextAsync(s: PAsyncHTTPServer) = var value = "" i = header.parseUntil(key, ':') inc(i) # skip : - i += header.skipWhiteSpace(i) - i += header.parseUntil(value, {'\c', '\L'}, i) + if i < header.len: + i += header.skipWhiteSpace(i) + i += header.parseUntil(value, {'\c', '\L'}, i) s.headers[key] = value else: s.client.close() diff --git a/lib/pure/irc.nim b/lib/pure/irc.nim index 74ad7e26a..ee85d9c69 100644 --- a/lib/pure/irc.nim +++ b/lib/pure/irc.nim @@ -98,6 +98,7 @@ type params*: seq[string] ## Parameters of the IRC message origin*: string ## The channel/user that this msg originated from raw*: string ## Raw IRC message + timestamp*: TTime ## UNIX epoch time the message was received proc send*(irc: PIRC, message: string, sendImmediately = false) = ## Sends ``message`` as a raw command. It adds ``\c\L`` for you. @@ -160,9 +161,10 @@ proc isNumber(s: string): bool = result = i == s.len and s.len > 0 proc parseMessage(msg: string): TIRCEvent = - result.typ = EvMsg - result.cmd = MUnknown - result.raw = msg + result.typ = EvMsg + result.cmd = MUnknown + result.raw = msg + result.timestamp = times.getTime() var i = 0 # Process the prefix if msg[i] == ':': diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index 0fd1d8cd2..fbe0dda95 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -52,6 +52,10 @@ proc oidToString*(oid: TOid, str: cstring) = inc(i) str[24] = '\0' +proc `$`*(oid: TOid): string = + result = newString(25) + oidToString(oid, result) + var incr: int fuzz: int32 diff --git a/lib/pure/sockets.nim b/lib/pure/sockets.nim index 99117ea4e..66bb1e6a9 100644 --- a/lib/pure/sockets.nim +++ b/lib/pure/sockets.nim @@ -154,9 +154,8 @@ proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket = proc `==`*(a, b: TPort): bool {.borrow.} ## ``==`` for ports. -proc `$`*(p: TPort): string = +proc `$`*(p: TPort): string {.borrow.} ## returns the port number as a string - result = $ze(int16(p)) proc ntohl*(x: int32): int32 = ## Converts 32-bit integers from network to host byte order. diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 80a5ad8d3..e967ef683 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -246,7 +246,8 @@ proc toSeconds(a: TTimeInfo, interval: TTimeInterval): float = else: curMonth.inc() result += float(newinterv.days * 24 * 60 * 60) - result += float(newinterv.minutes * 60 * 60) + result += float(newinterv.hours * 60 * 60) + result += float(newinterv.minutes * 60) result += float(newinterv.seconds) result += newinterv.miliseconds / 1000 diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 142178a86..4aacb2f71 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -55,31 +55,31 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = result = TRune(ord(s[i])) when doInc: inc(i) elif ord(s[i]) shr 5 == 0b110: - assert(ord(s[i+1]) shr 6 == 0b10) + # assert(ord(s[i+1]) shr 6 == 0b10) result = TRune((ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))) when doInc: inc(i, 2) elif ord(s[i]) shr 4 == 0b1110: - assert(ord(s[i+1]) shr 6 == 0b10) - assert(ord(s[i+2]) shr 6 == 0b10) + # assert(ord(s[i+1]) shr 6 == 0b10) + # assert(ord(s[i+2]) shr 6 == 0b10) result = TRune((ord(s[i]) and ones(4)) shl 12 or (ord(s[i+1]) and ones(6)) shl 6 or (ord(s[i+2]) and ones(6))) when doInc: inc(i, 3) elif ord(s[i]) shr 3 == 0b11110: - assert(ord(s[i+1]) shr 6 == 0b10) - assert(ord(s[i+2]) shr 6 == 0b10) - assert(ord(s[i+3]) shr 6 == 0b10) + # assert(ord(s[i+1]) shr 6 == 0b10) + # assert(ord(s[i+2]) shr 6 == 0b10) + # assert(ord(s[i+3]) shr 6 == 0b10) result = TRune((ord(s[i]) and ones(3)) shl 18 or (ord(s[i+1]) and ones(6)) shl 12 or (ord(s[i+2]) and ones(6)) shl 6 or (ord(s[i+3]) and ones(6))) when doInc: inc(i, 4) elif ord(s[i]) shr 2 == 0b111110: - assert(ord(s[i+1]) shr 6 == 0b10) - assert(ord(s[i+2]) shr 6 == 0b10) - assert(ord(s[i+3]) shr 6 == 0b10) - assert(ord(s[i+4]) shr 6 == 0b10) + # assert(ord(s[i+1]) shr 6 == 0b10) + # assert(ord(s[i+2]) shr 6 == 0b10) + # assert(ord(s[i+3]) shr 6 == 0b10) + # assert(ord(s[i+4]) shr 6 == 0b10) result = TRune((ord(s[i]) and ones(2)) shl 24 or (ord(s[i+1]) and ones(6)) shl 18 or (ord(s[i+2]) and ones(6)) shl 12 or @@ -87,11 +87,11 @@ template fastRuneAt*(s: string, i: int, result: expr, doInc = true) = (ord(s[i+4]) and ones(6))) when doInc: inc(i, 5) elif ord(s[i]) shr 1 == 0b1111110: - assert(ord(s[i+1]) shr 6 == 0b10) - assert(ord(s[i+2]) shr 6 == 0b10) - assert(ord(s[i+3]) shr 6 == 0b10) - assert(ord(s[i+4]) shr 6 == 0b10) - assert(ord(s[i+5]) shr 6 == 0b10) + # assert(ord(s[i+1]) shr 6 == 0b10) + # assert(ord(s[i+2]) shr 6 == 0b10) + # assert(ord(s[i+3]) shr 6 == 0b10) + # assert(ord(s[i+4]) shr 6 == 0b10) + # assert(ord(s[i+5]) shr 6 == 0b10) result = TRune((ord(s[i]) and ones(1)) shl 30 or (ord(s[i+1]) and ones(6)) shl 24 or (ord(s[i+2]) and ones(6)) shl 18 or diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 744c26d36..2bab79212 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -520,11 +520,18 @@ proc allocInv(a: TMemRegion): bool = for s in low(a.freeSmallChunks)..high(a.freeSmallChunks): var c = a.freeSmallChunks[s] while c != nil: - if c.next == c: return false - if c.size != s * MemAlign: return false + if c.next == c: + echo "[SYSASSERT] c.next == c" + return false + if c.size != s * MemAlign: + echo "[SYSASSERT] c.size != s * MemAlign" + return false var it = c.freeList while it != nil: - if it.zeroField != 0: return false + if it.zeroField != 0: + echo "[SYSASSERT] it.zeroField != 0" + cprintf("%ld %p\n", it.zeroField, it) + return false it = it.next c = c.next result = true @@ -591,6 +598,7 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer = add(a, a.root, cast[TAddress](result), cast[TAddress](result)+%size) sysAssert(isAccessible(a, result), "rawAlloc 14") sysAssert(allocInv(a), "rawAlloc: end") + when logAlloc: cprintf("rawAlloc: %ld %p\n", requestedSize, result) proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer = result = rawAlloc(a, requestedSize) @@ -638,6 +646,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) = del(a, a.root, cast[int](addr(c.data))) freeBigChunk(a, c) sysAssert(allocInv(a), "rawDealloc: end") + when logAlloc: cprintf("rawDealloc: %p\n", p) proc isAllocatedPtr(a: TMemRegion, p: pointer): bool = if isAccessible(a, p): diff --git a/lib/system/gc.nim b/lib/system/gc.nim index c5d4d2aa2..48705db96 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -9,7 +9,7 @@ # Garbage Collector # -# The basic algorithm is *Deferrent Reference Counting* with cycle detection. +# The basic algorithm is *Deferred Reference Counting* with cycle detection. # This is achieved by combining a Deutsch-Bobrow garbage collector # together with Christoper's partial mark-sweep garbage collector. # @@ -407,12 +407,17 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} = return add(gch.zct, res) +{.push stackTrace: off, profiler:off.} +proc gcInvariant*(msg: string) = + sysAssert(allocInv(gch.region), msg) +{.pop.} + proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer = # generates a new object and sets its reference counter to 0 + sysAssert(allocInv(gch.region), "rawNewObj begin") acquire(gch) gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") collectCT(gch) - sysAssert(allocInv(gch.region), "rawNewObj begin") var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell))) gcAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT @@ -517,7 +522,9 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer = writeCell("growObj new cell", res) gcTrace(ol, csZctFreed) gcTrace(res, csAllocated) - when reallyDealloc: rawDealloc(gch.region, ol) + when reallyDealloc: + sysAssert(allocInv(gch.region), "growObj before dealloc") + rawDealloc(gch.region, ol) else: sysAssert(ol.typ != nil, "growObj: 5") zeroMem(ol, sizeof(TCell)) @@ -537,7 +544,9 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) = prepareDealloc(c) gcTrace(c, csCycFreed) when logGC: writeCell("cycle collector dealloc cell", c) - when reallyDealloc: rawDealloc(gch.region, c) + when reallyDealloc: + sysAssert(allocInv(gch.region), "free cyclic cell") + rawDealloc(gch.region, c) else: gcAssert(c.typ != nil, "freeCyclicCell") zeroMem(c, sizeof(TCell)) @@ -910,7 +919,9 @@ proc collectZCT(gch: var TGcHeap): bool = # access invalid memory. This is done by prepareDealloc(): prepareDealloc(c) forAllChildren(c, waZctDecRef) - when reallyDealloc: rawDealloc(gch.region, c) + when reallyDealloc: + sysAssert(allocInv(gch.region), "collectZCT: rawDealloc") + rawDealloc(gch.region, c) else: sysAssert(c.typ != nil, "collectZCT 2") zeroMem(c, sizeof(TCell)) diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 05c291371..31c99a601 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -387,8 +387,7 @@ template `--`(rc: TRefCount): expr = rc <% rcIncrement template `--` (rc: TRefCount, heapType: THeapType): expr = - (when heapType == SharedHeap: atomicDec(rc, rcIncrement) <% rcIncrement - else: --rc) + (when heapType == SharedHeap: atomicDec(rc, rcIncrement) <% rcIncrement else: --rc) template doDecRef(cc: PCell, heapType = LocalHeap, diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index c9801abad..118272ee3 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -28,6 +28,7 @@ const reallyOsDealloc = true coalescRight = true coalescLeft = true + logAlloc = false type PPointer = ptr pointer diff --git a/tests/run/tstaticparams.nim b/tests/run/tstaticparams.nim index 232748356..f2d6e1dd6 100644 --- a/tests/run/tstaticparams.nim +++ b/tests/run/tstaticparams.nim @@ -10,6 +10,10 @@ type TBar[T; I: expr[int]] = object data: array[I, T] + TA1[T; I: expr[int]] = array[I, T] + TA2[T; I: expr[int]] = array[0..I, T] + TA3[T; I: expr[int]] = array[I-1, T] + proc takeFoo(x: TFoo) = echo "abracadabra" echo TFoo.Val @@ -20,3 +24,8 @@ takeFoo(x) var y: TBar[float, 4] echo high(y.data) +var + t1: TA1 + t2: TA2 + t3: TA3 + diff --git a/tests/run/tusertypeclasses.nim b/tests/run/tusertypeclasses.nim new file mode 100644 index 000000000..4c2f07b85 --- /dev/null +++ b/tests/run/tusertypeclasses.nim @@ -0,0 +1,28 @@ +discard """ + output: "Sortable\nSortable\nContainer" +""" + +import typetraits + +type + TObj = object + x: int + + Sortable = generic x, y + (x < y) is bool + + ObjectContainer = generic C + C.len is ordinal + for v in items(C): + v.type is tuple|object + +proc foo(c: ObjectContainer) = + echo "Container" + +proc foo(x: Sortable) = + echo "Sortable" + +foo 10 +foo "test" +foo(@[TObj(x: 10), TObj(x: 20)]) + |