Merge branch 'devel' into faster-nimsuggest

This commit is contained in:
Andreas Rumpf
2017-03-14 10:18:20 +01:00
32 changed files with 474 additions and 260 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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]

View File

@@ -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

View File

@@ -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\'",

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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"

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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,

17
tests/errmsgs/t5167_1.nim Normal file
View File

@@ -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

12
tests/errmsgs/t5167_2.nim Normal file
View File

@@ -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)

25
tests/errmsgs/t5167_3.nim Normal file
View File

@@ -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

20
tests/errmsgs/t5167_4.nim Normal file
View File

@@ -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

25
tests/errmsgs/t5167_5.nim Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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"))

View File

@@ -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:

View File

@@ -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"

View File

@@ -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)

14
tools/nimgrind.supp Normal file
View File

@@ -0,0 +1,14 @@
{
markstackandregisters_Cond
Memcheck:Cond
...
fun:markStackAndRegisters*
...
}
{
markstackandregisters_Value8
Memcheck:Value8
...
fun:markStackAndRegisters*
...
}

View File

@@ -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
-----------------