diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2017-03-14 10:18:20 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-03-14 10:18:20 +0100 |
commit | 4912300ca762f05681d3654631300173e3729920 (patch) | |
tree | 83cf1121f5a676689883a8fdf5c63165f0f5fd12 | |
parent | 3d534375c79d521c9fa747efceb09b5115b94010 (diff) | |
parent | e32f08d05b7e9a7f8cc121f02a3622bf26e29733 (diff) | |
download | Nim-4912300ca762f05681d3654631300173e3729920.tar.gz |
Merge branch 'devel' into faster-nimsuggest
32 files changed, 474 insertions, 260 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 177def594..137cc12f1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -460,6 +460,8 @@ type # proc foo(T: typedesc, list: seq[T]): var T # proc foo(L: static[int]): array[L, int] # can be attached to ranges to indicate that the range + # can be attached to generic procs with free standing + # type parameters: e.g. proc foo[T]() # depends on unresolved static params. tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 226d5ee42..161e4d637 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -67,6 +67,23 @@ proc debug*(n: PSym) {.deprecated.} proc debug*(n: PType) {.deprecated.} proc debug*(n: PNode) {.deprecated.} +template mdbg*: bool {.dirty.} = + when compiles(c.module): + c.module.fileIdx == gProjectMainIdx + elif compiles(m.c.module): + m.c.module.fileIdx == gProjectMainIdx + elif compiles(cl.c.module): + cl.c.module.fileIdx == gProjectMainIdx + elif compiles(p): + when compiles(p.lex): + p.lex.fileIdx == gProjectMainIdx + else: + p.module.module.fileIdx == gProjectMainIdx + elif compiles(L.fileIdx): + L.fileIdx == gProjectMainIdx + else: + false + # --------------------------- ident tables ---------------------------------- proc idTableGet*(t: TIdTable, key: PIdObj): RootRef proc idTableGet*(t: TIdTable, key: int): RootRef diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 318254a80..5bd274a3e 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -80,9 +80,14 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = expectedRegularParams = <s.typ.len givenRegularParams = totalParams - genericParams if givenRegularParams < 0: givenRegularParams = 0 + if totalParams > expectedRegularParams + genericParams: globalError(n.info, errWrongNumberOfArguments) + if totalParams < genericParams: + globalError(n.info, errMissingGenericParamsForTemplate, + n.renderTree) + result = newNodeI(nkArgList, n.info) for i in 1 .. givenRegularParams: result.addSon n.sons[i] diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 2bb228f41..7e54a30e2 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -1080,7 +1080,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = inc(L.bufpos) of '.': when defined(nimsuggest): - if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and + if L.fileIdx == gTrackPos.fileIndex and tok.col == gTrackPos.col and tok.line == gTrackPos.line and gIdeCmd == ideSug: tok.tokType = tkDot L.cursor = CursorPosition.InToken diff --git a/compiler/msgs.nim b/compiler/msgs.nim index b89b4ee93..2db3646b5 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -64,6 +64,8 @@ type errVarForOutParamNeeded, errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, errAmbiguousCallXYZ, errWrongNumberOfArguments, + errWrongNumberOfArgumentsInCall, + errMissingGenericParamsForTemplate, errXCannotBePassedToProcVar, errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, @@ -89,6 +91,7 @@ type errMainModuleMustBeSpecified, errXExpected, errTIsNotAConcreteType, + errCastToANonConcreteType, errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, @@ -107,6 +110,7 @@ type errCannotInferTypeOfTheLiteral, errCannotInferReturnType, errGenericLambdaNotAllowed, + errProcHasNoConcreteType, errCompilerDoesntSupportTarget, errUser, warnCannotOpenFile, @@ -269,6 +273,8 @@ const errButExpectedX: "but expected \'$1\'", errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3", errWrongNumberOfArguments: "wrong number of arguments", + errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'", + errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters", errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar", errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration", errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1", @@ -326,6 +332,7 @@ const errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file", errXExpected: "\'$1\' expected", errTIsNotAConcreteType: "\'$1\' is not a concrete type.", + errCastToANonConcreteType: "cannot cast to a non concrete type: \'$1\'", errInvalidSectionStart: "invalid section start", errGridTableNotImplemented: "grid table is not implemented", errGeneralParseError: "general parse error", @@ -369,6 +376,7 @@ const errGenericLambdaNotAllowed: "A nested proc can have generic parameters only when " & "it is used as an operand to another routine and the types " & "of the generic paramers can be inferred from the expected signature.", + errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errUser: "$1", warnCannotOpenFile: "cannot open \'$1\'", diff --git a/compiler/options.nim b/compiler/options.nim index c6d016095..349f9dae1 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -420,11 +420,6 @@ proc binaryStrSearch*(x: openArray[string], y: string): int = return mid result = - 1 -template nimdbg*: untyped = c.module.fileIdx == gProjectMainIdx -template cnimdbg*: untyped = p.module.module.fileIdx == gProjectMainIdx -template pnimdbg*: untyped = p.lex.fileIdx == gProjectMainIdx -template lnimdbg*: untyped = L.fileIdx == gProjectMainIdx - proc parseIdeCmd*(s: string): IdeCmd = case s: of "sug": ideSug diff --git a/compiler/sem.nim b/compiler/sem.nim index 2ad506b41..6ad77e3fb 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -385,6 +385,13 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, if sym == c.p.owner: globalError(n.info, errRecursiveDependencyX, sym.name.s) + let genericParams = if sfImmediate in sym.flags: 0 + else: sym.ast[genericParamsPos].len + let suppliedParams = max(n.safeLen - 1, 0) + + if suppliedParams < genericParams: + globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree) + #if c.evalContext == nil: # c.evalContext = c.createEvalContext(emStatic) result = evalMacroCall(c.module, c.cache, n, nOrig, sym) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 98667b085..3a43c63b2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -411,6 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = let tm = typeRel(m, formal, arg, true) if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) + newInst.typ.flags.excl tfUnresolved markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(newInst, n.info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3dc174527..8f1362691 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -30,6 +30,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # result = errorNode(c, n) if result.typ != nil: # XXX tyGenericInst here? + if result.typ.kind == tyProc and tfUnresolved in result.typ.flags: + localError(n.info, errProcHasNoConcreteType, n.renderTree) if result.typ.kind == tyVar: result = newDeref(result) elif {efWantStmt, efAllowStmt} * flags != {}: result.typ = newTypeS(tyVoid, c) @@ -218,13 +220,16 @@ proc semConv(c: PContext, n: PNode): PNode = proc semCast(c: PContext, n: PNode): PNode = ## Semantically analyze a casting ("cast[type](param)") checkSonsLen(n, 2) + let targetType = semTypeNode(c, n.sons[0], nil) + let castedExpr = semExprWithType(c, n.sons[1]) + if tfHasMeta in targetType.flags: + localError(n.sons[0].info, errCastToANonConcreteType, $targetType) + if not isCastable(targetType, castedExpr.typ): + localError(n.info, errExprCannotBeCastToX, $targetType) result = newNodeI(nkCast, n.info) - result.typ = semTypeNode(c, n.sons[0], nil) + result.typ = targetType addSon(result, copyTree(n.sons[0])) - addSon(result, semExprWithType(c, n.sons[1])) - if not isCastable(result.typ, result.sons[1].typ): - localError(result.info, errExprCannotBeCastToX, - typeToString(result.typ)) + addSon(result, castedExpr) proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = const diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6d0190257..069ece6a6 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -503,6 +503,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if hasEmpty(typ): localError(def.info, errCannotInferTypeOfTheLiteral, ($typ.kind).substr(2).toLowerAscii) + elif typ.kind == tyProc and tfUnresolved in typ.flags: + localError(def.info, errProcHasNoConcreteType, def.renderTree) else: if symkind == skLet: localError(a.info, errLetNeedsInit) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 83d0c83b2..7877a26a9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1009,8 +1009,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result.sons[0] = r result.n.typ = r - if genericParams != nil: + if genericParams != nil and genericParams.len > 0: for n in genericParams: + if {sfUsed, sfAnon} * n.sym.flags == {}: + result.flags.incl tfUnresolved + if tfWildcard in n.sym.typ.flags: n.sym.kind = skType n.sym.typ.flags.excl tfWildcard diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index f2caab41f..bc9888df9 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1514,6 +1514,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, if arg.sons[i].sym.kind in {skProc, skMethod, skConverter, skIterator}: copyCandidate(z, m) z.callee = arg.sons[i].typ + if tfUnresolved in z.callee.flags: continue z.calleeSym = arg.sons[i].sym #if arg.sons[i].sym.name.s == "cmp": # ggDebug = true @@ -1650,7 +1651,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if a >= formalLen-1 and formal != nil and formal.typ.isVarargsUntyped: incl(marker, formal.position) if container.isNil: - container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info)) + container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info)) setSon(m.call, formal.position + 1, container) else: incrIndexType(container.typ) @@ -1738,7 +1739,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if formal.typ.isVarargsUntyped: if container.isNil: - container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, n.info)) + container = newNodeIT(nkArgList, n.sons[a].info, arrayConstr(c, n.info)) setSon(m.call, formal.position + 1, container) else: incrIndexType(container.typ) diff --git a/compiler/types.nim b/compiler/types.nim index df1d3e3ca..f4ef75094 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -20,6 +20,7 @@ type preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string +template `$`*(typ: PType): string = typeToString(typ) proc base*(t: PType): PType = result = t.sons[0] @@ -547,7 +548,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if prefer != preferExported: result.add("(" & typeToString(t.sons[0]) & ")") of tyProc: - result = if tfIterator in t.flags: "iterator (" else: "proc (" + result = if tfIterator in t.flags: "iterator " else: "proc " + if tfUnresolved in t.flags: result.add "[*missing parameters*]" + result.add "(" for i in countup(1, sonsLen(t) - 1): if t.n != nil and i < t.n.len and t.n[i].kind == nkSym: add(result, t.n[i].sym.name.s) diff --git a/doc/tut1.rst b/doc/tut1.rst index e79214dee..65906376e 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -393,7 +393,7 @@ Since counting up occurs so often in programs, Nim also has a `.. for i in 1..10: ... -Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less then the higher index: +Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less than the higher index: .. code-block:: nim for i in 0..<10: @@ -411,8 +411,8 @@ Other useful iterators for collections (like arrays and sequences) are * ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively) .. code-block:: nim - for indx, itm in ["a","b"].pairs: - echo itm, " at index ", indx + for index, item in ["a","b"].pairs: + echo item, " at index ", index # => a at index 0 # => b at index 1 @@ -489,10 +489,10 @@ Example: else: echo "unknown operating system" -The ``when`` statement is almost identical to the ``if`` statement with some +The ``when`` statement is almost identical to the ``if`` statement, but with these differences: -* Each condition has to be a constant expression since it is evaluated by the +* Each condition must be a constant expression since it is evaluated by the compiler. * The statements within a branch do not open a new scope. * The compiler checks the semantics and produces code *only* for the statements @@ -516,8 +516,8 @@ In Nim there is a distinction between *simple statements* and *complex statements*. *Simple statements* cannot contain other statements: Assignment, procedure calls or the ``return`` statement belong to the simple statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can -contain other statements. To avoid ambiguities, complex statements always have -to be indented, but single simple statements do not: +contain other statements. To avoid ambiguities, complex statements must always +be indented, but single simple statements do not: .. code-block:: nim # no indentation needed for single assignment statement: @@ -586,9 +586,9 @@ false if they answered "no" (or something similar). A ``return`` statement leaves the procedure (and therefore the while loop) immediately. The ``(question: string): bool`` syntax describes that the procedure expects a parameter named ``question`` of type ``string`` and returns a value of type -``bool``. ``Bool`` is a built-in type: the only valid values for ``bool`` are +``bool``. The ``bool`` type is built-in: the only valid values for ``bool`` are ``true`` and ``false``. -The conditions in if or while statements should be of the type ``bool``. +The conditions in if or while statements must be of type ``bool``. Some terminology: in the example ``question`` is called a (formal) *parameter*, ``"Should I..."`` is called an *argument* that is passed to this parameter. @@ -658,8 +658,8 @@ a tuple as a return value instead of using var parameters. Discard statement ----------------- To call a procedure that returns a value just for its side effects and ignoring -its return value, a ``discard`` statement **has** to be used. Nim does not -allow to silently throw away a return value: +its return value, a ``discard`` statement **must** be used. Nim does not +allow silently throwing away a return value: .. code-block:: nim discard yes("May I ask a pointless question?") @@ -708,7 +708,7 @@ The compiler checks that each parameter receives exactly one argument. Default values -------------- To make the ``createWindow`` proc easier to use it should provide `default -values`, these are values that are used as arguments if the caller does not +values`; these are values that are used as arguments if the caller does not specify them: .. code-block:: nim @@ -750,19 +750,19 @@ algorithm. Ambiguous calls are reported as errors. Operators --------- The Nim library makes heavy use of overloading - one reason for this is that -each operator like ``+`` is a just an overloaded proc. The parser lets you +each operator like ``+`` is just an overloaded proc. The parser lets you use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``). An infix operator always receives two arguments, a prefix operator always one. -Postfix operators are not possible, because this would be ambiguous: does +(Postfix operators are not possible, because this would be ambiguous: does ``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means -``(a) @ (@b)``, because there are no postfix operators in Nim. +``(a) @ (@b)``, because there are no postfix operators in Nim.) Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``, operators always consist of these characters: ``+ - * \ / < > = @ $ ~ & % ! ? ^ . |`` User defined operators are allowed. Nothing stops you from defining your own -``@!?+~`` operator, but readability can suffer. +``@!?+~`` operator, but doing so may reduce readability. The operator's precedence is determined by its first character. The details can be found in the manual. @@ -785,7 +785,7 @@ Forward declarations -------------------- Every variable, procedure, etc. needs to be declared before it can be used. -(The reason for this is that it is non-trivial to do better than that in a +(The reason for this is that it is non-trivial to avoid this need in a language that supports meta programming as extensively as Nim does.) However, this cannot be done for mutually recursive procedures: @@ -822,7 +822,7 @@ whose value is then returned implicitly. Iterators ========= -Let's return to the boring counting example: +Let's return to the simple counting example: .. code-block:: nim echo "Counting to ten: " @@ -843,7 +843,7 @@ However, this does not work. The problem is that the procedure should not only ``return``, but return and **continue** after an iteration has finished. This *return and continue* is called a `yield` statement. Now the only thing left to do is to replace the ``proc`` keyword by ``iterator`` -and there it is - our first iterator: +and here it is - our first iterator: .. code-block:: nim iterator countup(a, b: int): int = @@ -856,8 +856,8 @@ Iterators look very similar to procedures, but there are several important differences: * Iterators can only be called from for loops. -* Iterators cannot contain a ``return`` statement and procs cannot contain a - ``yield`` statement. +* Iterators cannot contain a ``return`` statement (and procs cannot contain a + ``yield`` statement). * Iterators have no implicit ``result`` variable. * Iterators do not support recursion. * Iterators cannot be forward declared, because the compiler must be able @@ -866,8 +866,8 @@ important differences: However, you can also use a ``closure`` iterator to get a different set of restrictions. See `first class iterators <manual.html#first-class-iterators>`_ -for details. Iterators can have the same name and parameters as a proc, -essentially they have their own namespace. Therefore it is common practice to +for details. Iterators can have the same name and parameters as a proc, since +essentially they have their own namespaces. Therefore it is common practice to wrap iterators in procs of the same name which accumulate the result of the iterator and return it as a sequence, like ``split`` from the `strutils module <strutils.html>`_. @@ -882,13 +882,13 @@ that are available for them in detail. Booleans -------- -The boolean type is named ``bool`` in Nim and consists of the two +Nim's boolean type is called ``bool`` and consists of the two pre-defined values ``true`` and ``false``. Conditions in while, -if, elif, when statements need to be of type bool. +if, elif, and when statements must be of type bool. The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined -for the bool type. The ``and`` and ``or`` operators perform short-cut -evaluation. Example: +for the bool type. The ``and`` and ``or`` operators perform short-circuit +evaluation. For example: .. code-block:: nim @@ -899,8 +899,8 @@ evaluation. Example: Characters ---------- -The `character type` is named ``char`` in Nim. Its size is one byte. -Thus it cannot represent an UTF-8 character, but a part of it. +The `character type` is called ``char``. Its size is always one byte, so +it cannot represent most UTF-8 characters; but it *can* represent one of the bytes that makes up a multi-byte UTF-8 character. The reason for this is efficiency: for the overwhelming majority of use-cases, the resulting programs will still handle UTF-8 properly as UTF-8 was specially designed for this. @@ -914,11 +914,11 @@ Converting from an integer to a ``char`` is done with the ``chr`` proc. Strings ------- -String variables in Nim are **mutable**, so appending to a string -is quite efficient. Strings in Nim are both zero-terminated and have a -length field. One can retrieve a string's length with the builtin ``len`` +String variables are **mutable**, so appending to a string +is possible, and quite efficient. Strings in Nim are both zero-terminated and have a +length field. A string's length can be retrieved with the builtin ``len`` procedure; the length never counts the terminating zero. Accessing the -terminating zero is no error and often leads to simpler code: +terminating zero is not an error and often leads to simpler code: .. code-block:: nim if s[i] == 'a' and s[i+1] == 'b': @@ -928,15 +928,15 @@ terminating zero is no error and often leads to simpler code: The assignment operator for strings copies the string. You can use the ``&`` operator to concatenate strings and ``add`` to append to a string. -Strings are compared by their lexicographical order. All comparison operators -are available. Per convention, all strings are UTF-8 strings, but this is not +Strings are compared using their lexicographical order. All the comparison operators +are supported. By convention, all strings are UTF-8 encoded, but this is not enforced. For example, when reading strings from binary files, they are merely a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of ``s``, not the i-th *unichar*. String variables are initialized with a special value, called ``nil``. However, most string operations cannot deal with ``nil`` (leading to an exception being -raised) for performance reasons. One should use empty strings ``""`` +raised) for performance reasons. It is best to use empty strings ``""`` rather than ``nil`` as the *empty* value. But ``""`` often creates a string object on the heap, so there is a trade-off to be made here. @@ -947,7 +947,7 @@ Nim has these integer types built-in: ``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``. The default integer type is ``int``. Integer literals can have a *type suffix* -to mark them to be of another integer type: +to specify a non-default integer type: .. code-block:: nim @@ -961,18 +961,18 @@ Most often integers are used for counting objects that reside in memory, so ``int`` has the same size as a pointer. The common operators ``+ - * div mod < <= == != > >=`` are defined for -integers. The ``and or xor not`` operators are defined for integers too and +integers. The ``and or xor not`` operators are also defined for integers, and provide *bitwise* operations. Left bit shifting is done with the ``shl``, right shifting with the ``shr`` operator. Bit shifting operators always treat their arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary multiplication or division can be used. -Unsigned operations all wrap around; they cannot lead to over- or underflow +Unsigned operations all wrap around; they cannot lead to over- or under-flow errors. -`Automatic type conversion`:idx: is performed in expressions where different +Lossless `Automatic type conversion`:idx: is performed in expressions where different kinds of integer types are used. However, if the type conversion -loses information, the `EOutOfRange`:idx: exception is raised (if the error +would cause loss of information, the `EOutOfRange`:idx: exception is raised (if the error cannot be detected at compile time). @@ -981,9 +981,9 @@ Floats Nim has these floating point types built-in: ``float float32 float64``. The default float type is ``float``. In the current implementation, -``float`` is always 64 bit wide. +``float`` is always 64-bits. -Float literals can have a *type suffix* to mark them to be of another float +Float literals can have a *type suffix* to specify a non-default float type: .. code-block:: nim @@ -993,18 +993,18 @@ type: z = 0.0'f64 # z is of type ``float64`` The common operators ``+ - * / < <= == != > >=`` are defined for -floats and follow the IEEE standard. +floats and follow the IEEE-754 standard. Automatic type conversion in expressions with different kinds of floating point types is performed: the smaller type is converted to the larger. Integer -types are **not** converted to floating point types automatically and vice -versa. The `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_ -procs can be used for these conversions. +types are **not** converted to floating point types automatically, nor vice +versa. Use the `toInt <system.html#toInt>`_ and `toFloat <system.html#toFloat>`_ +procs for these conversions. Type Conversion --------------- -Conversion between basic types in nim is performed by using the +Conversion between basic types is performed by using the type as a function: .. code-block:: nim @@ -1019,9 +1019,9 @@ Internal type representation ============================ As mentioned earlier, the built-in `$ <system.html#$>`_ (stringify) operator -turns any basic type into a string, which you can then print to the screen -with the ``echo`` proc. However, advanced types, or types you may define -yourself won't work with the ``$`` operator until you define one for them. +turns any basic type into a string, which you can then print to the console +using the ``echo`` proc. However, advanced types, and your own custom types, +won't work with the ``$`` operator until you define it for them. Sometimes you just want to debug the current value of a complex type without having to write its ``$`` operator. You can use then the `repr <system.html#repr>`_ proc which works with any type and even complex data @@ -1057,16 +1057,16 @@ In Nim new types can be defined within a ``type`` statement: biggestInt = int64 # biggest integer type that is available biggestFloat = float64 # biggest float type that is available -Enumeration and object types cannot be defined on the fly, but only within a +Enumeration and object types may only be defined within a ``type`` statement. Enumerations ------------ -A variable of an enumeration type can only be assigned a value of a -limited set. This set consists of ordered symbols. Each symbol is mapped +A variable of an enumeration type can only be assigned one of the enumeration's specified values. +These values are a set of ordered symbols. Each symbol is mapped to an integer value internally. The first symbol is represented -at runtime by 0, the second by 1 and so on. Example: +at runtime by 0, the second by 1 and so on. For example: .. code-block:: nim @@ -1077,17 +1077,17 @@ at runtime by 0, the second by 1 and so on. Example: var x = south # `x` is of type `Direction`; its value is `south` echo x # writes "south" to `stdout` -All comparison operators can be used with enumeration types. +All the comparison operators can be used with enumeration types. An enumeration's symbol can be qualified to avoid ambiguities: ``Direction.south``. -The ``$`` operator can convert any enumeration value to its name, the ``ord`` -proc to its underlying integer value. +The ``$`` operator can convert any enumeration value to its name, and the ``ord`` +proc can convert it to its underlying integer value. For better interfacing to other programming languages, the symbols of enum types can be assigned an explicit ordinal value. However, the ordinal values -have to be in ascending order. A symbol whose ordinal value is not +must be in ascending order. A symbol whose ordinal value is not explicitly given is assigned the value of the previous symbol + 1. An explicit ordered enum can have *holes*: @@ -1142,8 +1142,8 @@ subrange types (and vice versa) are allowed. The ``system`` module defines the important `Natural <system.html#Natural>`_ type as ``range[0..high(int)]`` (`high <system.html#high>`_ returns the -maximal value). Other programming languages mandate the usage of unsigned -integers for natural numbers. This is often **wrong**: you don't want unsigned +maximal value). Other programming languages may suggest the use of unsigned +integers for natural numbers. This is often **unwise**: you don't want unsigned arithmetic (which wraps around) just because the numbers cannot be negative. Nim's ``Natural`` type helps to avoid this common programming error. @@ -1156,9 +1156,9 @@ Sets Arrays ------ An array is a simple fixed length container. Each element in -the array has the same type. The array's index type can be any ordinal type. +an array has the same type. The array's index type can be any ordinal type. -Arrays can be constructed via ``[]``: +Arrays can be constructed using ``[]``: .. code-block:: nim @@ -1222,7 +1222,7 @@ subdivided in height levels accessed through their integer index: #tower[0][1] = on Note how the built-in ``len`` proc returns only the array's first dimension -length. Another way of defining the ``LightTower`` to show better its +length. Another way of defining the ``LightTower`` to better illustrate its nested nature would be to omit the previous definition of the ``LevelSetting`` type and instead write it embedded directly as the type of the first dimension: @@ -1230,7 +1230,7 @@ type and instead write it embedded directly as the type of the first dimension: type LightTower = array[1..10, array[north..west, BlinkLights]] -It is quite frequent to have arrays start at zero, so there's a shortcut syntax +It is quite common to have arrays start at zero, so there's a shortcut syntax to specify a range from zero to the specified index minus one: .. code-block:: nim @@ -1288,8 +1288,8 @@ value. Here the ``for`` statement is looping over the results from the <system.html>`_ module. Examples: .. code-block:: nim - for i in @[3, 4, 5]: - echo i + for value in @[3, 4, 5]: + echo value # --> 3 # --> 4 # --> 5 @@ -1320,7 +1320,7 @@ type does not matter. fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits' - capitals = ["New York", "London", "Berlin"] # array 'capitals' allows only assignment of three elements + capitals = ["New York", "London", "Berlin"] # array 'capitals' allows assignment of only three elements fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime fruits.add("Mango") @@ -1406,7 +1406,7 @@ the same type and of the same name in the same order. The assignment operator for tuples copies each component. The notation ``t.field`` is used to access a tuple's field. Another notation is -``t[i]`` to access the ``i``'th field. Here ``i`` needs to be a constant +``t[i]`` to access the ``i``'th field. Here ``i`` must be a constant integer. .. code-block:: nim @@ -1449,10 +1449,10 @@ Tuples can be *unpacked* during variable assignment (and only then!). This can be handy to assign directly the fields of the tuples to individually named variables. An example of this is the `splitFile <os.html#splitFile>`_ proc from the `os module <os.html>`_ which returns the directory, name and -extension of a path at the same time. For tuple unpacking to work you have to -use parenthesis around the values you want to assign the unpacking to, +extension of a path at the same time. For tuple unpacking to work you must +use parentheses around the values you want to assign the unpacking to, otherwise you will be assigning the same value to all the individual -variables! Example: +variables! For example: .. code-block:: nim @@ -1494,12 +1494,12 @@ point to and modify the same location in memory. Nim distinguishes between `traced`:idx: and `untraced`:idx: references. Untraced references are also called *pointers*. Traced references point to -objects of a garbage collected heap, untraced references point to -manually allocated objects or to objects somewhere else in memory. Thus +objects in a garbage collected heap, untraced references point to +manually allocated objects or to objects elsewhere in memory. Thus untraced references are *unsafe*. However for certain low-level operations -(accessing the hardware) untraced references are unavoidable. +(e.g., accessing the hardware), untraced references are necessary. -Traced references are declared with the **ref** keyword, untraced references +Traced references are declared with the **ref** keyword; untraced references are declared with the **ptr** keyword. The empty ``[]`` subscript notation can be used to *derefer* a reference, @@ -1520,10 +1520,10 @@ operators perform implicit dereferencing operations for reference types: n.data = 9 # no need to write n[].data; in fact n[].data is highly discouraged! -To allocate a new traced object, the built-in procedure ``new`` has to be used. +To allocate a new traced object, the built-in procedure ``new`` must be used. To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and -``realloc`` can be used. The documentation of the `system <system.html>`_ -module contains further information. +``realloc`` can be used. The `system <system.html>`_ +module's documentation contains further details. If a reference points to *nothing*, it has the value ``nil``. @@ -1555,8 +1555,8 @@ listed in the `manual <manual.html#types-procedural-type>`_. Distinct type ------------- -A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type". -You must EXPLICITLY define all behaviour for the distinct type. +A Distinct type allows for the creation of new type that "does not imply a subtype relationship between it and its base type". +You must **explicitly** define all behaviour for the distinct type. To help with this, both the distinct type and its base type can cast from one type to the other. Examples are provided in the `manual <manual.html#types-distinct-type>`_. @@ -1564,8 +1564,8 @@ Modules ======= Nim supports splitting a program into pieces with a module concept. Each module is in its own file. Modules enable `information hiding`:idx: and -`separate compilation`:idx:. A module may gain access to symbols of another -module by the `import`:idx: statement. Only top-level symbols that are marked +`separate compilation`:idx:. A module may gain access to the symbols of another +module by using the `import`:idx: statement. Only top-level symbols that are marked with an asterisk (``*``) are exported: .. code-block:: nim @@ -1585,7 +1585,7 @@ with an asterisk (``*``) are exported: The above module exports ``x`` and ``*``, but not ``y``. -The top-level statements of a module are executed at the start of the program. +A module's top-level statements are executed at the start of the program. This can be used to initialize complex data structures for example. Each module has a special magic constant ``isMainModule`` that is true if the @@ -1625,8 +1625,8 @@ This is best illustrated by an example: result = x + 1 -A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. If -the symbol is ambiguous, it even *has* to be qualified. A symbol is ambiguous +A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if +a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous if it is defined in two (or more) different modules and both modules are imported by a third one: @@ -1642,7 +1642,7 @@ imported by a third one: # Module C import A, B write(stdout, x) # error: x is ambiguous - write(stdout, A.x) # no error: qualifier used + write(stdout, A.x) # okay: qualifier used var x = 4 write(stdout, x) # not ambiguous: uses the module C's x diff --git a/koch.nim b/koch.nim index 20d01ae98..7c8401c57 100644 --- a/koch.nim +++ b/koch.nim @@ -512,6 +512,14 @@ proc pushCsources() = finally: setCurrentDir(cwd) +proc valgrind(cmd: string) = + exec("nim c " & cmd) + var i = cmd.len-1 + while i >= 0 and cmd[i] != ' ': dec i + let file = if i >= 0: substr(cmd, i+1) else: cmd + let supp = getAppDir() / "tools" / "nimgrind.supp" + exec("valgrind --suppressions=" & supp & " " & changeFileExt(file, "")) + proc showHelp() = quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), CompileDate, CompileTime], QuitSuccess) @@ -548,5 +556,6 @@ of cmdArgument: of "nimsuggest": bundleNimsuggest(buildExe=true) of "tools": buildTools(existsDir(".git")) of "pushcsource", "pushcsources": pushCsources() + of "valgrind": valgrind(op.cmdLineRest) else: showHelp() of cmdEnd: showHelp() diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 45a148fbf..19512d5f4 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -16,9 +16,6 @@ ## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing. ## Anonymous procs can use `the special do notation <manual.html#do-notation>`_ ## which is more convenient in certain situations. -## -## **Note**: This interface will change as soon as the compiler supports -## closures and proper coroutines. include "system/inclrtl" diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index c6322c7bb..b6154d8de 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -83,7 +83,8 @@ proc unmapMem*(f: var MemFile, p: pointer, size: int) = proc open*(filename: string, mode: FileMode = fmRead, - mappedSize = -1, offset = 0, newFileSize = -1): MemFile = + mappedSize = -1, offset = 0, newFileSize = -1, + allowRemap = false): MemFile = ## opens a memory mapped file. If this fails, ``EOS`` is raised. ## ## ``newFileSize`` can only be set if the file does not exist and is opened @@ -95,6 +96,9 @@ proc open*(filename: string, mode: FileMode = fmRead, ## ``offset`` must be multiples of the PAGE SIZE of your OS ## (usually 4K or 8K but is unique to your OS) ## + ## ``allowRemap`` only needs to be true if you want to call ``mapMem`` on + ## the resulting MemFile; else file handles are not kept open. + ## ## Example: ## ## .. code-block:: nim @@ -189,11 +193,14 @@ proc open*(filename: string, mode: FileMode = fmRead, else: result.size = fileSize.int result.wasOpened = true + if not allowRemap and result.fHandle != INVALID_HANDLE_VALUE: + if closeHandle(result.fHandle) == 0: + result.fHandle = INVALID_HANDLE_VALUE else: template fail(errCode: OSErrorCode, msg: expr) = rollback() - if result.handle != 0: discard close(result.handle) + if result.handle != -1: discard close(result.handle) raiseOSError(errCode) var flags = if readonly: O_RDONLY else: O_RDWR @@ -236,6 +243,10 @@ proc open*(filename: string, mode: FileMode = fmRead, if result.mem == cast[pointer](MAP_FAILED): fail(osLastError(), "file mapping failed") + if not allowRemap and result.handle != -1: + if close(result.handle) == 0: + result.handle = -1 + proc close*(f: var MemFile) = ## closes the memory mapped file `f`. All changes are written back to the ## file system, if `f` was opened with write access. @@ -244,15 +255,16 @@ proc close*(f: var MemFile) = var lastErr: OSErrorCode when defined(windows): - if f.fHandle != INVALID_HANDLE_VALUE and f.wasOpened: + if f.wasOpened: error = unmapViewOfFile(f.mem) == 0 lastErr = osLastError() error = (closeHandle(f.mapHandle) == 0) or error - error = (closeHandle(f.fHandle) == 0) or error + if f.fHandle != INVALID_HANDLE_VALUE: + error = (closeHandle(f.fHandle) == 0) or error else: - if f.handle != 0: - error = munmap(f.mem, f.size) != 0 - lastErr = osLastError() + error = munmap(f.mem, f.size) != 0 + lastErr = osLastError() + if f.handle != -1: error = (close(f.handle) != 0) or error f.size = 0 @@ -263,7 +275,7 @@ proc close*(f: var MemFile) = f.mapHandle = 0 f.wasOpened = false else: - f.handle = 0 + f.handle = -1 if error: raiseOSError(lastErr) diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index 87ece2582..71991e35a 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -25,8 +25,8 @@ when not declared(getEnv) or defined(nimscript): WriteEnvEffect* = object of WriteIOEffect ## effect that denotes a write ## to an environment variable - ReadDirEffect* = object of ReadIOEffect ## effect that denotes a write - ## operation to the directory + ReadDirEffect* = object of ReadIOEffect ## effect that denotes a read + ## operation from the directory ## structure WriteDirEffect* = object of WriteIOEffect ## effect that denotes a write ## operation to diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index 87865c005..08e6c8112 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -20,7 +20,8 @@ ## var msg = createMessage("Hello from Nim's SMTP", ## "Hello!.\n Is this awesome or what?", ## @["foo@gmail.com"]) -## var smtpConn = connect("smtp.gmail.com", Port 465, true, true) +## let smtpConn = newSmtp(useSsl = true, debug=true) +## smtpConn.connect("smtp.gmail.com", Port 465) ## smtpConn.auth("username", "password") ## smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg) ## @@ -34,10 +35,6 @@ import asyncnet, asyncdispatch export Port type - Smtp* = object - sock: Socket - debug: bool - Message* = object msgTo: seq[string] msgCc: seq[string] @@ -47,37 +44,29 @@ type ReplyError* = object of IOError - AsyncSmtp* = ref object - sock: AsyncSocket - address: string - port: Port - useSsl: bool + SmtpBase[SocketType] = ref object + sock: SocketType debug: bool + Smtp* = SmtpBase[Socket] + AsyncSmtp* = SmtpBase[AsyncSocket] + {.deprecated: [EInvalidReply: ReplyError, TMessage: Message, TSMTP: Smtp].} -proc debugSend(smtp: Smtp, cmd: string) = +proc debugSend(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} = if smtp.debug: echo("C:" & cmd) - smtp.sock.send(cmd) - -proc debugRecv(smtp: var Smtp): TaintedString = - var line = TaintedString"" - smtp.sock.readLine(line) + await smtp.sock.send(cmd) +proc debugRecv(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} = + result = await smtp.sock.recvLine() if smtp.debug: - echo("S:" & line.string) - return line + echo("S:" & result.string) proc quitExcpt(smtp: Smtp, msg: string) = smtp.debugSend("QUIT") raise newException(ReplyError, msg) -proc checkReply(smtp: var Smtp, reply: string) = - var line = smtp.debugRecv() - if not line.string.startswith(reply): - quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string) - const compiledWithSsl = defined(ssl) when not defined(ssl): @@ -86,63 +75,6 @@ when not defined(ssl): else: let defaultSSLContext = newContext(verifyMode = CVerifyNone) -proc connect*(address: string, port = Port(25), - ssl = false, debug = false, - sslContext = defaultSSLContext): Smtp = - ## Establishes a connection with a SMTP server. - ## May fail with ReplyError or with a socket error. - result.sock = newSocket() - if ssl: - when compiledWithSsl: - sslContext.wrapSocket(result.sock) - else: - raise newException(ESystem, - "SMTP module compiled without SSL support") - result.sock.connect(address, port) - result.debug = debug - - result.checkReply("220") - result.debugSend("HELO " & address & "\c\L") - result.checkReply("250") - -proc auth*(smtp: var Smtp, username, password: string) = - ## Sends an AUTH command to the server to login as the `username` - ## using `password`. - ## May fail with ReplyError. - - smtp.debugSend("AUTH LOGIN\c\L") - smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:" - # i.e "334 VXNlcm5hbWU6" - smtp.debugSend(encode(username) & "\c\L") - smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?) - - smtp.debugSend(encode(password) & "\c\L") - smtp.checkReply("235") # Check whether the authentification was successful. - -proc sendmail*(smtp: var Smtp, fromaddr: string, - toaddrs: seq[string], msg: string) = - ## Sends `msg` from `fromaddr` to `toaddr`. - ## Messages may be formed using ``createMessage`` by converting the - ## Message into a string. - - smtp.debugSend("MAIL FROM:<" & fromaddr & ">\c\L") - smtp.checkReply("250") - for address in items(toaddrs): - smtp.debugSend("RCPT TO:<" & address & ">\c\L") - smtp.checkReply("250") - - # Send the message - smtp.debugSend("DATA " & "\c\L") - smtp.checkReply("354") - smtp.debugSend(msg & "\c\L") - smtp.debugSend(".\c\L") - smtp.checkReply("250") - -proc close*(smtp: Smtp) = - ## Disconnects from the SMTP server and closes the socket. - smtp.debugSend("QUIT\c\L") - smtp.sock.close() - proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], otherHeaders: openarray[tuple[name, value: string]]): Message = ## Creates a new MIME compliant message. @@ -178,81 +110,94 @@ proc `$`*(msg: Message): string = result.add("\c\L") result.add(msg.msgBody) -proc newAsyncSmtp*(address: string, port: Port, useSsl = false, +proc newSmtp*(useSsl = false, debug=false, + sslContext = defaultSslContext): Smtp = + ## Creates a new ``Smtp`` instance. + new result + result.debug = debug + + result.sock = newSocket() + if useSsl: + when compiledWithSsl: + sslContext.wrapSocket(result.sock) + else: + raise newException(SystemError, + "SMTP module compiled without SSL support") + +proc newAsyncSmtp*(useSsl = false, debug=false, sslContext = defaultSslContext): AsyncSmtp = ## Creates a new ``AsyncSmtp`` instance. new result - result.address = address - result.port = port - result.useSsl = useSsl + result.debug = debug result.sock = newAsyncSocket() if useSsl: when compiledWithSsl: sslContext.wrapSocket(result.sock) else: - raise newException(ESystem, + raise newException(SystemError, "SMTP module compiled without SSL support") proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] = var retFuture = newFuture[void]() - var sendFut = smtp.sock.send("QUIT") + var sendFut = smtp.debugSend("QUIT") sendFut.callback = proc () = # TODO: Fix this in async procs. raise newException(ReplyError, msg) return retFuture -proc checkReply(smtp: AsyncSmtp, reply: string) {.async.} = - var line = await smtp.sock.recvLine() - if not line.string.startswith(reply): - await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line.string) +proc checkReply(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} = + var line = await smtp.debugRecv() + if not line.startswith(reply): + await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line) -proc connect*(smtp: AsyncSmtp) {.async.} = +proc connect*(smtp: Smtp | AsyncSmtp, + address: string, port: Port) {.multisync.} = ## Establishes a connection with a SMTP server. ## May fail with ReplyError or with a socket error. - await smtp.sock.connect(smtp.address, smtp.port) + await smtp.sock.connect(address, port) await smtp.checkReply("220") - await smtp.sock.send("HELO " & smtp.address & "\c\L") + await smtp.debugSend("HELO " & address & "\c\L") await smtp.checkReply("250") -proc auth*(smtp: AsyncSmtp, username, password: string) {.async.} = +proc auth*(smtp: Smtp | AsyncSmtp, username, password: string) {.multisync.} = ## Sends an AUTH command to the server to login as the `username` ## using `password`. ## May fail with ReplyError. - await smtp.sock.send("AUTH LOGIN\c\L") + await smtp.debugSend("AUTH LOGIN\c\L") await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:" # i.e "334 VXNlcm5hbWU6" - await smtp.sock.send(encode(username) & "\c\L") + await smtp.debugSend(encode(username) & "\c\L") await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?) - await smtp.sock.send(encode(password) & "\c\L") + await smtp.debugSend(encode(password) & "\c\L") await smtp.checkReply("235") # Check whether the authentification was successful. -proc sendMail*(smtp: AsyncSmtp, fromAddr: string, - toAddrs: seq[string], msg: string) {.async.} = +proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string, + toAddrs: seq[string], msg: string) {.multisync.} = ## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``. ## Messages may be formed using ``createMessage`` by converting the ## Message into a string. - await smtp.sock.send("MAIL FROM:<" & fromAddr & ">\c\L") + await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L") await smtp.checkReply("250") for address in items(toAddrs): - await smtp.sock.send("RCPT TO:<" & address & ">\c\L") + await smtp.debugSend("RCPT TO:<" & address & ">\c\L") await smtp.checkReply("250") # Send the message - await smtp.sock.send("DATA " & "\c\L") + await smtp.debugSend("DATA " & "\c\L") await smtp.checkReply("354") await smtp.sock.send(msg & "\c\L") - await smtp.sock.send(".\c\L") + await smtp.debugSend(".\c\L") await smtp.checkReply("250") -proc close*(smtp: AsyncSmtp) {.async.} = +proc close*(smtp: Smtp | AsyncSmtp) {.multisync.} = ## Disconnects from the SMTP server and closes the socket. - await smtp.sock.send("QUIT\c\L") + await smtp.debugSend("QUIT\c\L") smtp.sock.close() when not defined(testing) and isMainModule: @@ -278,25 +223,24 @@ when not defined(testing) and isMainModule: proc async_test() {.async.} = let client = newAsyncSmtp( - conf["smtphost"], - conf["port"].parseInt.Port, - conf["use_tls"].parseBool + conf["use_tls"].parseBool, + debug=true ) - await client.connect() + await client.connect(conf["smtphost"], conf["port"].parseInt.Port) await client.auth(conf["username"], conf["password"]) await client.sendMail(conf["sender"], @[conf["recipient"]], $msg) await client.close() echo "async email sent" proc sync_test() = - var smtpConn = connect( - conf["smtphost"], - conf["port"].parseInt.Port, + var smtpConn = newSmtp( conf["use_tls"].parseBool, - true, # debug + debug=true ) + smtpConn.connect(conf["smtphost"], conf["port"].parseInt.Port) smtpConn.auth(conf["username"], conf["password"]) - smtpConn.sendmail(conf["sender"], @[conf["recipient"]], $msg) + smtpConn.sendMail(conf["sender"], @[conf["recipient"]], $msg) + smtpConn.close() echo "sync email sent" waitFor async_test() diff --git a/lib/system.nim b/lib/system.nim index 74dca461a..b7e2c6eba 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -417,7 +417,7 @@ type ## Base exception class. ## ## Each exception has to inherit from `Exception`. See the full `exception - ## hierarchy`_. + ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_. parent*: ref Exception ## parent exception (can be used as a stack) name*: cstring ## The exception's name is its Nim identifier. ## This field is filled automatically in the @@ -430,51 +430,51 @@ type SystemError* = object of Exception ## \ ## Abstract class for exceptions that the runtime system raises. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. IOError* = object of SystemError ## \ ## Raised if an IO error occurred. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. EOFError* = object of IOError ## \ ## Raised if an IO "end of file" error occurred. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. OSError* = object of SystemError ## \ ## Raised if an operating system service failed. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. errorCode*: int32 ## OS-defined error code describing this error. LibraryError* = object of OSError ## \ ## Raised if a dynamic library could not be loaded. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ResourceExhaustedError* = object of SystemError ## \ ## Raised if a resource request could not be fulfilled. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ArithmeticError* = object of Exception ## \ ## Raised if any kind of arithmetic error occurred. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. DivByZeroError* = object of ArithmeticError ## \ ## Raised for runtime integer divide-by-zero errors. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. OverflowError* = object of ArithmeticError ## \ ## Raised for runtime integer overflows. ## ## This happens for calculations whose results are too large to fit in the - ## provided bits. See the full `exception hierarchy`_. + ## provided bits. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. AccessViolationError* = object of Exception ## \ ## Raised for invalid memory access errors ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. AssertionError* = object of Exception ## \ ## Raised when assertion is proved wrong. ## ## Usually the result of using the `assert() template <#assert>`_. See the - ## full `exception hierarchy`_. + ## full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ValueError* = object of Exception ## \ ## Raised for string and object conversion errors. KeyError* = object of ValueError ## \ @@ -482,66 +482,66 @@ type ## ## Mostly used by the `tables <tables.html>`_ module, it can also be raised ## by other collection modules like `sets <sets.html>`_ or `strtabs - ## <strtabs.html>`_. See the full `exception hierarchy`_. + ## <strtabs.html>`_. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. OutOfMemError* = object of SystemError ## \ ## Raised for unsuccessful attempts to allocate memory. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. IndexError* = object of Exception ## \ ## Raised if an array index is out of bounds. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FieldError* = object of Exception ## \ ## Raised if a record field is not accessible because its dicriminant's ## value does not fit. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. RangeError* = object of Exception ## \ ## Raised if a range check error occurred. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. StackOverflowError* = object of SystemError ## \ ## Raised if the hardware stack used for subroutine calls overflowed. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ReraiseError* = object of Exception ## \ ## Raised if there is no exception to reraise. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ObjectAssignmentError* = object of Exception ## \ ## Raised if an object gets assigned to its parent's object. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. ObjectConversionError* = object of Exception ## \ ## Raised if an object is converted to an incompatible object type. ## You can use ``of`` operator to check if conversion will succeed. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FloatingPointError* = object of Exception ## \ ## Base class for floating point exceptions. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FloatInvalidOpError* = object of FloatingPointError ## \ ## Raised by invalid operations according to IEEE. ## ## Raised by ``0.0/0.0``, for example. See the full `exception - ## hierarchy`_. + ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FloatDivByZeroError* = object of FloatingPointError ## \ ## Raised by division by zero. ## ## Divisor is zero and dividend is a finite nonzero number. See the full - ## `exception hierarchy`_. + ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FloatOverflowError* = object of FloatingPointError ## \ ## Raised for overflows. ## ## The operation produced a result that exceeds the range of the exponent. - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FloatUnderflowError* = object of FloatingPointError ## \ ## Raised for underflows. ## ## The operation produced a result that is too small to be represented as a - ## normal number. See the full `exception hierarchy`_. + ## normal number. See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. FloatInexactError* = object of FloatingPointError ## \ ## Raised for inexact results. ## @@ -549,11 +549,11 @@ type ## precision -- for example: ``2.0 / 3.0, log(1.1)`` ## ## **NOTE**: Nim currently does not detect these! See the full - ## `exception hierarchy`_. + ## `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. DeadThreadError* = object of Exception ## \ ## Raised if it is attempted to send a message to a dead thread. ## - ## See the full `exception hierarchy`_. + ## See the full `exception hierarchy <manual.html#exception-handling-exception-hierarchy>`_. {.deprecated: [TObject: RootObj, PObject: RootRef, TEffect: RootEffect, FTime: TimeEffect, FIO: IOEffect, FReadIO: ReadIOEffect, diff --git a/tests/errmsgs/t5167_1.nim b/tests/errmsgs/t5167_1.nim new file mode 100644 index 000000000..9f4f208a4 --- /dev/null +++ b/tests/errmsgs/t5167_1.nim @@ -0,0 +1,17 @@ +discard """ +errormsg: "'bar' doesn't have a concrete type, due to unspecified generic parameters." +line: 16 +""" + +proc foo[T]() = + var y1 = foo[string] + var y2 = foo[T] + +proc bar[T]() = + let x = 0 + +let good1 = foo[int] +let good2 = bar[int] + +let err = bar + diff --git a/tests/errmsgs/t5167_2.nim b/tests/errmsgs/t5167_2.nim new file mode 100644 index 000000000..17d96ef47 --- /dev/null +++ b/tests/errmsgs/t5167_2.nim @@ -0,0 +1,12 @@ +discard """ +cmd: "nim c --threads:on $file" +errormsg: "'threadFunc' doesn't have a concrete type, due to unspecified generic parameters." +line: 11 +""" + +proc threadFunc[T]() {.thread.} = + let x = 0 + +var thr: Thread[void] +thr.createThread(threadFunc) + diff --git a/tests/errmsgs/t5167_3.nim b/tests/errmsgs/t5167_3.nim new file mode 100644 index 000000000..2781d3943 --- /dev/null +++ b/tests/errmsgs/t5167_3.nim @@ -0,0 +1,25 @@ +discard """ +cmd: "nim c --threads:on $file" +errormsg: "type mismatch" +line: 24 +""" + +type + TGeneric[T] = object + x: int + +proc foo1[A, B, C, D](x: proc (a: A, b: B, c: C, d: D)) = + echo "foo1" + +proc foo2(x: proc(x: int)) = + echo "foo2" + +# The goal of this test is to verify that none of the generic parameters of the +# proc will be marked as unused. The error message should be "type mismatch" instead +# of "'bar' doesn't have a concrete type, due to unspecified generic parameters". +proc bar[A, B, C, D](x: A, y: seq[B], z: array[4, TGeneric[C]], r: TGeneric[D]) = + echo "bar" + +foo1[int, seq[int], array[4, TGeneric[float]], TGeneric[string]] bar +foo2 bar + diff --git a/tests/errmsgs/t5167_4.nim b/tests/errmsgs/t5167_4.nim new file mode 100644 index 000000000..3d77fae02 --- /dev/null +++ b/tests/errmsgs/t5167_4.nim @@ -0,0 +1,20 @@ +discard """ +errormsg: "type mismatch: got (proc [*missing parameters*](x: int) | proc (x: string){.gcsafe, locks: 0.})" +line: 19 +""" + +type + TGeneric[T] = object + x: int + +proc foo[B](x: int) = + echo "foo1" + +proc foo(x: string) = + echo "foo2" + +proc bar(x: proc (x: int)) = + echo "bar" + +bar foo + diff --git a/tests/errmsgs/t5167_5.nim b/tests/errmsgs/t5167_5.nim new file mode 100644 index 000000000..ab02f29f6 --- /dev/null +++ b/tests/errmsgs/t5167_5.nim @@ -0,0 +1,25 @@ +discard """ +cmd: "nim check $file" +errormsg: "'m' has unspecified generic parameters" +nimout: ''' +t5167_5.nim(20, 9) Error: 't' has unspecified generic parameters +t5167_5.nim(21, 5) Error: 't' has unspecified generic parameters +t5167_5.nim(23, 9) Error: 'm' has unspecified generic parameters +t5167_5.nim(24, 5) Error: 'm' has unspecified generic parameters +''' +""" + +template t[B]() = + echo "foo1" + +macro m[T]: stmt = nil + +proc bar(x: proc (x: int)) = + echo "bar" + +let x = t +bar t + +let y = m +bar m + diff --git a/tests/errmsgs/tnon_concrete_cast.nim b/tests/errmsgs/tnon_concrete_cast.nim new file mode 100644 index 000000000..e4ae890ce --- /dev/null +++ b/tests/errmsgs/tnon_concrete_cast.nim @@ -0,0 +1,47 @@ +discard """ + errormsg: "cannot cast to a non concrete type: 'ptr SomeNumber'" + line: 36 +""" + +# https://github.com/nim-lang/Nim/issues/5428 + +type + MemFile = object + mem: pointer + +proc memfileopen(filename: string, newFileSize: int): MemFile = + # just a memfile mock + return + +type + MyData = object + member1: seq[int] + member2: int + +type + MyReadWrite = object + memfile: MemFile + offset: int + +# Here, SomeNumber is bound to a concrete type, and that's OK +proc write(rw: var MyReadWrite; value: SomeNumber): void = + (cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + rw.offset.uint))[] = value + rw.offset += sizeof(SomeNumber) + +# Here, we try to use SomeNumber without binding it to a type. This should +# produce an error message for now. It's also possible to relax the rules +# and allow for type-class based type inference in such situations. +proc write[T](rw: var MyReadWrite; value: seq[T]): void = + rw.write value.len + let dst = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset)) + let src = cast[pointer](value[0].unsafeAddr) + let size = sizeof(T) * value.len + copyMem(dst, src, size) + rw.offset += size + +proc saveBinFile(arg: var MyData, filename: string): void = + var rw: MyReadWrite + rw.memfile = memfileOpen(filename, newFileSize = rw.offset) + rw.offset = 0 + rw.write arg.member1 + diff --git a/tests/macros/tmacro4.nim b/tests/macros/tmacro4.nim index a56369369..fb07941a9 100644 --- a/tests/macros/tmacro4.nim +++ b/tests/macros/tmacro4.nim @@ -5,7 +5,7 @@ discard """ import macros, strutils -macro test_macro*(n: stmt): stmt {.immediate.} = +macro test_macro*(s: string, n: stmt): stmt {.immediate.} = result = newNimNode(nnkStmtList) var ass : NimNode = newNimNode(nnkAsgn) add(ass, newIdentNode("str")) diff --git a/tests/macros/tquotewords.nim b/tests/macros/tquotewords.nim index 7a575f541..48fcafd62 100644 --- a/tests/macros/tquotewords.nim +++ b/tests/macros/tquotewords.nim @@ -6,7 +6,7 @@ discard """ import macros -macro quoteWords(n: expr): expr {.immediate.} = +macro quoteWords(n: varargs[expr]): expr {.immediate.} = let n = callsite() result = newNimNode(nnkBracket, n) for i in 1..n.len-1: diff --git a/tests/overload/tparam_forwarding.nim b/tests/overload/tparam_forwarding.nim new file mode 100644 index 000000000..c1b276bfc --- /dev/null +++ b/tests/overload/tparam_forwarding.nim @@ -0,0 +1,37 @@ +discard """ +output: '''baz +10 +100 +1000 +a +b +c +''' +""" + +type + Foo = object + x: int + +proc stringVarargs*(strings: varargs[string, `$`]): void = + for s in strings: echo s + +proc fooVarargs*(foos: varargs[Foo]) = + for f in foos: echo f.x + +template templateForwarding*(callable: untyped, + condition: bool, + forwarded: varargs[untyped]): untyped = + if condition: + callable(forwarded) + +proc procForwarding(args: varargs[string]) = + stringVarargs(args) + +templateForwarding stringVarargs, 17 + 4 < 21, "foo", "bar", 100 +templateForwarding stringVarargs, 10 < 21, "baz" + +templateForwarding fooVarargs, "test".len > 3, Foo(x: 10), Foo(x: 100), Foo(x: 1000) + +procForwarding "a", "b", "c" + diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim index 026443e93..665e92e8a 100644 --- a/tests/stdlib/tmemfiles2.nim +++ b/tests/stdlib/tmemfiles2.nim @@ -18,7 +18,7 @@ mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) mm.close() # read, change -mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1) +mm_full = memfiles.open(fn, mode = fmWrite, mappedSize = -1, allowRemap = true) echo "Full read size: ",mm_full.size p = mm_full.mapMem(fmReadWrite, 20, 0) var p2 = cast[cstring](p) diff --git a/tools/nimgrind.supp b/tools/nimgrind.supp new file mode 100644 index 000000000..44499ebc7 --- /dev/null +++ b/tools/nimgrind.supp @@ -0,0 +1,14 @@ +{ + markstackandregisters_Cond + Memcheck:Cond + ... + fun:markStackAndRegisters* + ... +} +{ + markstackandregisters_Value8 + Memcheck:Value8 + ... + fun:markStackAndRegisters* + ... +} diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst index 3f111b503..37137169b 100644 --- a/web/news/e031_version_0_16_2.rst +++ b/web/news/e031_version_0_16_2.rst @@ -23,6 +23,12 @@ Changes affecting backwards compatibility pointer. Now the hash is calculated from the contents of the string, assuming ``cstring`` is a null-terminated string. Equal ``string`` and ``cstring`` values produce an equal hash value. +- Macros accepting `varargs` arguments will now receive a node having the + `nkArgList` node kind. Previous code expecting the node kind to be `nkBracket` + may have to be updated. +- ``memfiles.open`` now closes file handleds/fds by default. Passing + ``allowRemap=true`` to ``memfiles.open`` recovers the old behavior. The old + behavior is only needed to call ``mapMem`` on the resulting ``MemFile``. Library Additions ----------------- |