summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim42
-rw-r--r--compiler/astalgo.nim10
-rw-r--r--compiler/c2nim/cparse.nim29
-rw-r--r--compiler/c2nim/cpp.nim4
-rw-r--r--compiler/canonicalizer.nim416
-rw-r--r--compiler/ccgexprs.nim7
-rw-r--r--compiler/ccgmerge.nim27
-rw-r--r--compiler/ccgstmts.nim51
-rw-r--r--compiler/ccgtypes.nim48
-rw-r--r--compiler/ccgutils.nim1
-rw-r--r--compiler/cgen.nim26
-rw-r--r--compiler/cgendata.nim13
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/docgen.nim12
-rw-r--r--compiler/guards.nim42
-rw-r--r--compiler/importer.nim2
-rw-r--r--compiler/jsgen.nim79
-rw-r--r--compiler/lambdalifting.nim15
-rw-r--r--compiler/lexer.nim16
-rw-r--r--compiler/lookups.nim5
-rw-r--r--compiler/modules.nim9
-rw-r--r--compiler/msgs.nim13
-rw-r--r--compiler/nimeval.nim8
-rw-r--r--compiler/nimrod.nimrod.cfg2
-rw-r--r--compiler/options.nim49
-rw-r--r--compiler/parser.nim67
-rw-r--r--compiler/pas2nim/paslex.nim2
-rw-r--r--compiler/pas2nim/pasparse.nim24
-rw-r--r--compiler/pragmas.nim52
-rw-r--r--compiler/renderer.nim19
-rw-r--r--compiler/rodread.nim2
-rw-r--r--compiler/sem.nim7
-rw-r--r--compiler/semcall.nim45
-rw-r--r--compiler/semdata.nim6
-rw-r--r--compiler/semdestruct.nim67
-rw-r--r--compiler/semexprs.nim101
-rw-r--r--compiler/semfold.nim2
-rw-r--r--compiler/semgnrc.nim4
-rw-r--r--compiler/sempass2.nim41
-rw-r--r--compiler/semstmts.nim139
-rw-r--r--compiler/semtempl.nim4
-rw-r--r--compiler/semtypes.nim79
-rw-r--r--compiler/semtypinst.nim33
-rw-r--r--compiler/sigmatch.nim184
-rw-r--r--compiler/syntaxes.nim21
-rw-r--r--compiler/transf.nim8
-rw-r--r--compiler/types.nim21
-rw-r--r--compiler/vm.nim977
-rw-r--r--compiler/vmdef.nim26
-rw-r--r--compiler/vmgen.nim218
-rw-r--r--compiler/wordrecg.nim6
-rw-r--r--doc/idetools.txt4
-rw-r--r--doc/manual.txt194
-rw-r--r--examples/htmlrefs.nim2
-rw-r--r--examples/htmltitle.nim8
-rw-r--r--koch.nim27
-rw-r--r--lib/core/macros.nim4
-rw-r--r--lib/nimbase.h7
-rw-r--r--lib/packages/docutils/docutils.babel6
-rw-r--r--lib/packages/docutils/highlite.nim7
-rw-r--r--lib/packages/docutils/rst.nim2
-rw-r--r--lib/posix/epoll.nim8
-rw-r--r--lib/posix/linux.nim25
-rw-r--r--lib/posix/posix.nim4
-rw-r--r--lib/pure/asyncio.nim10
-rw-r--r--lib/pure/asyncio2.nim639
-rw-r--r--lib/pure/collections/sets.nim4
-rw-r--r--lib/pure/dynlib.nim10
-rw-r--r--lib/pure/htmlgen.nim11
-rw-r--r--lib/pure/net.nim17
-rw-r--r--lib/pure/nimprof.nim4
-rw-r--r--lib/pure/os.nim8
-rw-r--r--lib/pure/osproc.nim281
-rw-r--r--lib/pure/pegs.nim8
-rw-r--r--lib/pure/selectors.nim355
-rw-r--r--lib/pure/sockets2.nim25
-rw-r--r--lib/pure/times.nim242
-rw-r--r--lib/stdlib.babel6
-rw-r--r--lib/system.nim81
-rw-r--r--lib/system/arithm.nim12
-rw-r--r--lib/system/jssys.nim117
-rw-r--r--lib/windows/winlean.nim6
-rw-r--r--lib/wrappers/zip/zlib.nim1
-rw-r--r--lib/wrappers/zmq.nim7
-rw-r--r--readme.md2
-rw-r--r--tests/actiontable/tactiontable2.nim2
-rw-r--r--tests/ambsym/mambsym1.nim2
-rw-r--r--tests/ambsym/mambsys1.nim2
-rw-r--r--tests/ambsym/mambsys2.nim2
-rw-r--r--tests/async/tasyncawait.nim64
-rw-r--r--tests/casestmt/tcasestm.nim4
-rw-r--r--tests/ccgbugs/tcgbug.nim13
-rw-r--r--tests/collections/tsets.nim17
-rw-r--r--tests/compiles/tcompiles.nim2
-rw-r--r--tests/concurrency/tnodeadlocks.nim2
-rw-r--r--tests/destructor/tdestructor.nim54
-rw-r--r--tests/destructor/tdictdestruct.nim2
-rw-r--r--tests/discard/tdiscardable.nim16
-rw-r--r--tests/discard/tneedsdiscard.nim2
-rw-r--r--tests/effects/teffects6.nim2
-rw-r--r--tests/exception/texceptionbreak.nim45
-rw-r--r--tests/exception/tfinally4.nim40
-rw-r--r--tests/exception/tnestedreturn.nim40
-rw-r--r--tests/exception/tnestedreturn2.nim20
-rw-r--r--tests/exprs/texprstmt.nim2
-rw-r--r--tests/exprs/tifexpr_typeinference.nim20
-rw-r--r--tests/exprs/tstmtexp.nim4
-rw-r--r--tests/generics/tgeneric3.nim6
-rw-r--r--tests/generics/tgenericlambda.nim18
-rw-r--r--tests/generics/tinferredgenericprocs.nim20
-rw-r--r--tests/generics/tmetafield.nim18
-rw-r--r--tests/global/globalaux.nim15
-rw-r--r--tests/global/globalaux2.nim4
-rw-r--r--tests/iter/tanoniter1.nim2
-rw-r--r--tests/lookups/tredef.nim14
-rw-r--r--tests/macros/tmacro5.nim2
-rw-r--r--tests/macros/tvarnimnode.nim19
-rw-r--r--tests/manyloc/argument_parser/argument_parser.nim4
-rw-r--r--tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim7
-rw-r--r--tests/manyloc/keineschweine/dependencies/enet/enet.nim9
-rw-r--r--tests/manyloc/keineschweine/dependencies/sfml/sfml.nim12
-rw-r--r--tests/metatype/tbindtypedesc.nim23
-rw-r--r--tests/metatype/tusertypeclasses.nim14
-rw-r--r--tests/metatype/udtcmanual.nim43
-rw-r--r--tests/method/tmethods1.nim4
-rw-r--r--tests/module/trecinca.nim2
-rw-r--r--tests/module/trecincb.nim2
-rw-r--r--tests/notnil/tnotnil3.nim35
-rw-r--r--tests/overload/toverwr.nim14
-rw-r--r--tests/patterns/targlist.nim2
-rw-r--r--tests/range/tsubrange2.nim2
-rw-r--r--tests/range/tsubrange3.nim2
-rw-r--r--tests/sets/tsets.nim18
-rw-r--r--tests/sets/tsets_lt.nim12
-rw-r--r--tests/specialops/tdotops.nim66
-rw-r--r--tests/stdlib/tircbot.nim6
-rw-r--r--tests/stdlib/tmath2.nim2
-rw-r--r--tests/stdlib/tos.nim2
-rw-r--r--tests/stdlib/tpegs.nim14
-rw-r--r--tests/system/alloc.nim52
-rw-r--r--tests/table/ttableconstr.nim2
-rw-r--r--tests/template/sunset.tmpl (renamed from tests/sunset.tmpl)0
-rw-r--r--tests/template/ttempl5.nim13
-rw-r--r--tests/testament/categories.nim87
-rw-r--r--tests/testament/htmlgen.nim44
-rw-r--r--tests/testament/specs.nim2
-rw-r--r--tests/threads/nimrod.cfg1
-rw-r--r--tests/typerel/tvoid.nim6
-rw-r--r--tests/typerel/typalias.nim2
-rw-r--r--tests/types/tisopr.nim27
-rw-r--r--tests/vm/tstaticprintseq.nim21
-rw-r--r--tests/vm/twrongwhen.nim13
-rw-r--r--todo.txt20
-rw-r--r--tools/nimgrep.nim2
-rw-r--r--tools/nimweb.nim12
-rw-r--r--tools/website.tmpl10
-rw-r--r--web/assets/style.css2
-rw-r--r--web/community.txt58
-rw-r--r--web/news.txt183
-rw-r--r--web/ticker.txt12
160 files changed, 4614 insertions, 1951 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index cd002eef1..9c9dfce9a 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -62,7 +62,7 @@ type
nkTripleStrLit, # a triple string literal """
nkNilLit, # the nil literal
# end of atoms
- nkMetaNode, # difficult to explain; represents itself
+ nkMetaNode_Obsolete, # difficult to explain; represents itself
# (used for macros)
nkDotCall, # used to temporarily flag a nkCall node;
# this is used
@@ -409,7 +409,9 @@ type
# efficiency
nfTransf, # node has been transformed
nfSem # node has been checked for semantics
- nfDelegate # the call can use a delegator
+ nfDotField # the call can use a dot operator
+ nfDotSetter # the call can use a setter dot operarator
+ nfExplicitCall # x.y() was used instead of x.y
nfExprCall # this is an attempt to call a regular expression
nfIsRef # this node is a 'ref' node; used for the VM
@@ -467,7 +469,8 @@ type
skResult, # special 'result' variable
skProc, # a proc
skMethod, # a method
- skIterator, # an iterator
+ skIterator, # an inline iterator
+ skClosureIterator, # a resumable closure iterator
skConverter, # a type converter
skMacro, # a macro
skTemplate, # a template; currently also misused for user-defined
@@ -479,12 +482,15 @@ type
skStub, # symbol is a stub and not yet loaded from the ROD
# file (it is loaded on demand, which may
# mean: never)
+ skPackage # symbol is a package (used for canonicalization)
TSymKinds* = set[TSymKind]
const
- routineKinds* = {skProc, skMethod, skIterator, skConverter,
- skMacro, skTemplate}
+ routineKinds* = {skProc, skMethod, skIterator, skClosureIterator,
+ skConverter, skMacro, skTemplate}
tfIncompleteStruct* = tfVarargs
+ tfUncheckedArray* = tfVarargs
+ tfUnion* = tfNoSideEffect
skError* = skUnknown
# type flags that are essential for type equality:
@@ -817,8 +823,8 @@ type
# the poor naming choices in the standard library.
const
- OverloadableSyms* = {skProc, skMethod, skIterator, skConverter,
- skModule, skTemplate, skMacro}
+ OverloadableSyms* = {skProc, skMethod, skIterator, skClosureIterator,
+ skConverter, skModule, skTemplate, skMacro}
GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody,
tyGenericParam}
@@ -840,10 +846,12 @@ const
tyTuple, tySequence}
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tySequence,
tyProc, tyString, tyError}
- ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
+ ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType,
+ skIterator, skClosureIterator,
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
- nfAllConst, nfDelegate, nfIsRef}
+ nfDotSetter, nfDotField,
+ nfAllConst,nfIsRef}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2
@@ -865,7 +873,10 @@ const
nkStrKinds* = {nkStrLit..nkTripleStrLit}
skLocalVars* = {skVar, skLet, skForVar, skParam, skResult}
- skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skMethod, skConverter}
+ skProcKinds* = {skProc, skTemplate, skMacro, skIterator, skClosureIterator,
+ skMethod, skConverter}
+
+ skIterators* = {skIterator, skClosureIterator}
lfFullExternalName* = lfParamCopy # \
# only used when 'gCmd == cmdPretty': Indicates that the symbol has been
@@ -1044,6 +1055,10 @@ proc newStrNode(kind: TNodeKind, strVal: string): PNode =
result = newNode(kind)
result.strVal = strVal
+proc withInfo*(n: PNode, info: TLineInfo): PNode =
+ n.info = info
+ return n
+
proc newIdentNode(ident: PIdent, info: TLineInfo): PNode =
result = newNode(nkIdent)
result.ident = ident
@@ -1105,10 +1120,6 @@ proc newNodeIT(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
result.info = info
result.typ = typ
-proc newMetaNodeIT*(tree: PNode, info: TLineInfo, typ: PType): PNode =
- result = newNodeIT(nkMetaNode, info, typ)
- result.add(tree)
-
var emptyParams = newNode(nkFormalParams)
emptyParams.addSon(emptyNode)
@@ -1475,8 +1486,7 @@ proc originatingModule*(s: PSym): PSym =
while result.kind != skModule: result = result.owner
proc isRoutine*(s: PSym): bool {.inline.} =
- result = s.kind in {skProc, skTemplate, skMacro, skIterator, skMethod,
- skConverter}
+ result = s.kind in skProcKinds
proc hasPattern*(s: PSym): bool {.inline.} =
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 64c1b717c..cce2cc215 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -337,7 +337,10 @@ proc treeToYamlAux(n: PNode, marker: var TIntSet, indent: int,
appf(result, ",$N$1\"floatVal\": $2",
[istr, toRope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit:
- appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
+ if n.strVal.isNil:
+ appf(result, ",$N$1\"strVal\": null", [istr])
+ else:
+ appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
appf(result, ",$N$1\"sym\": $2",
[istr, symToYamlAux(n.sym, marker, indent + 2, maxRecDepth)])
@@ -407,7 +410,10 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope =
appf(result, ",$N$1\"floatVal\": $2",
[istr, toRope(n.floatVal.toStrMaxPrecision)])
of nkStrLit..nkTripleStrLit:
- appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
+ if n.strVal.isNil:
+ appf(result, ",$N$1\"strVal\": null", [istr])
+ else:
+ appf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
of nkSym:
appf(result, ",$N$1\"sym\": $2_$3",
[istr, toRope(n.sym.name.s), toRope(n.sym.id)])
diff --git a/compiler/c2nim/cparse.nim b/compiler/c2nim/cparse.nim
index ffab05788..555e1af10 100644
--- a/compiler/c2nim/cparse.nim
+++ b/compiler/c2nim/cparse.nim
@@ -540,7 +540,7 @@ proc typeAtom(p: var TParser): PNode =
if p.tok.s == "unsigned":
isUnsigned = true
elif p.tok.s == "signed" or p.tok.s == "int":
- nil
+ discard
else:
add(x, p.tok.s)
getTok(p, nil)
@@ -681,11 +681,6 @@ proc parseField(p: var TParser, kind: TNodeKind): PNode =
else: result = mangledIdent(p.tok.s, p)
getTok(p, result)
-proc takeOnlyFirstField(p: TParser, isUnion: bool): bool =
- # if we generate an interface to a header file, *all* fields can be
- # generated:
- result = isUnion and p.options.header.len == 0
-
proc parseStructBody(p: var TParser, isUnion: bool,
kind: TNodeKind = nkRecList): PNode =
result = newNodeP(kind, p)
@@ -698,8 +693,7 @@ proc parseStructBody(p: var TParser, isUnion: bool,
var i = parseField(p, kind)
t = parseTypeSuffix(p, t)
addSon(def, i, t, ast.emptyNode)
- if not takeOnlyFirstField(p, isUnion) or sonsLen(result) < 1:
- addSon(result, def)
+ addSon(result, def)
if p.tok.xkind != pxComma: break
getTok(p, def)
eat(p, pxSemicolon, lastSon(result))
@@ -710,11 +704,12 @@ proc structPragmas(p: TParser, name: PNode, origName: string): PNode =
result = newNodeP(nkPragmaExpr, p)
addSon(result, exportSym(p, name, origName))
var pragmas = newNodeP(nkPragma, p)
- addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
+ #addSon(pragmas, newIdentNodeP("pure", p), newIdentNodeP("final", p))
if p.options.header.len > 0:
addSon(pragmas, newIdentStrLitPair("importc", origName, p),
newIdentStrLitPair("header", p.options.header, p))
- addSon(result, pragmas)
+ if pragmas.len > 0: addSon(result, pragmas)
+ else: addSon(result, ast.emptyNode)
proc enumPragmas(p: TParser, name: PNode): PNode =
result = newNodeP(nkPragmaExpr, p)
@@ -726,9 +721,13 @@ proc enumPragmas(p: TParser, name: PNode): PNode =
addSon(pragmas, e)
addSon(result, pragmas)
-proc parseStruct(p: var TParser, isUnion: bool): PNode =
+proc parseStruct(p: var TParser, isUnion: bool): PNode =
result = newNodeP(nkObjectTy, p)
- addSon(result, ast.emptyNode, ast.emptyNode) # no pragmas, no inheritance
+ var pragmas = ast.emptyNode
+ if isUnion:
+ pragmas = newNodeP(nkPragma, p)
+ addSon(pragmas, newIdentNodeP("union", p))
+ addSon(result, pragmas, ast.emptyNode) # no inheritance
if p.tok.xkind == pxCurlyLe:
addSon(result, parseStructBody(p, isUnion))
else:
@@ -746,7 +745,7 @@ proc directDeclarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
result = declarator(p, a, ident)
eat(p, pxParRi, result)
else:
- nil
+ discard
return parseTypeSuffix(p, a)
proc declarator(p: var TParser, a: PNode, ident: ptr PNode): PNode =
@@ -1165,7 +1164,7 @@ proc enumSpecifier(p: var TParser): PNode =
proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base
- of base10: nil
+ of base10: discard
of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16)
@@ -1686,7 +1685,7 @@ proc switchStatement(p: var TParser): PNode =
break
of "case", "default":
break
- else: nil
+ else: discard
addSon(result, statement(p))
if sonsLen(result) == 0:
# translate empty statement list to Nimrod's ``nil`` statement
diff --git a/compiler/c2nim/cpp.nim b/compiler/c2nim/cpp.nim
index 1707b75db..84b4c4dfb 100644
--- a/compiler/c2nim/cpp.nim
+++ b/compiler/c2nim/cpp.nim
@@ -103,7 +103,7 @@ proc parseDefBody(p: var TParser, m: var TMacro, params: seq[string]) =
m.body.add(tok)
of pxDirConc:
# just ignore this token: this implements token merging correctly
- nil
+ discard
else:
m.body.add(p.tok)
# we do not want macro expansion here:
@@ -166,7 +166,7 @@ proc parseStmtList(p: var TParser): PNode =
of pxDirectiveParLe, pxDirective:
case p.tok.s
of "else", "endif", "elif": break
- else: nil
+ else: discard
addSon(result, statement(p))
proc eatEndif(p: var TParser) =
diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer.nim
new file mode 100644
index 000000000..3bc4eb029
--- /dev/null
+++ b/compiler/canonicalizer.nim
@@ -0,0 +1,416 @@
+#
+#
+# The Nimrod Compiler
+# (c) Copyright 2014 Andreas Rumpf
+#
+# See the file "copying.txt", included in this
+# distribution, for details about the copyright.
+#
+
+## This module implements the canonalization for the various caching mechanisms.
+
+import strutils, db_sqlite, md5
+
+var db: TDbConn
+
+# We *hash* the relevant information into 128 bit hashes. This should be good
+# enough to prevent any collisions.
+
+type
+ TUid = distinct MD5Digest
+
+# For name mangling we encode these hashes via a variant of base64 (called
+# 'base64a') and prepend the *primary* identifier to ease the debugging pain.
+# So a signature like:
+#
+# proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt)
+#
+# is mangled into:
+# gABI_MTdmOWY5MTQ1MDcyNGQ3ZA
+#
+# This is a good compromise between correctness and brevity. ;-)
+
+const
+ cb64 = [
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
+ "O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
+ "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "_A", "_B"]
+
+proc toBase64a(s: cstring, len: int): string =
+ ## encodes `s` into base64 representation. After `lineLen` characters, a
+ ## `newline` is added.
+ result = newStringOfCap(((len + 2) div 3) * 4)
+ var i = 0
+ while i < s.len - 2:
+ let a = ord(s[i])
+ let b = ord(s[i+1])
+ let c = ord(s[i+2])
+ result.add cb64[a shr 2]
+ result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+ result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
+ result.add cb64[c and 0x3F]
+ inc(i, 3)
+ if i < s.len-1:
+ let a = ord(s[i])
+ let b = ord(s[i+1])
+ result.add cb64[a shr 2]
+ result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
+ result.add cb64[((b and 0x0F) shl 2)]
+ elif i < s.len:
+ let a = ord(s[i])
+ result.add cb64[a shr 2]
+ result.add cb64[(a and 3) shl 4]
+
+proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
+
+proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
+
+proc hashSym(c: var MD5Context, s: PSym) =
+ if sfAnon in s.flags or s.kind == skGenericParam:
+ c &= ":anon"
+ else:
+ var it = s.owner
+ while it != nil:
+ hashSym(c, it)
+ c &= "."
+ it = s.owner
+ c &= s.name.s
+
+proc hashTree(c: var MD5Context, n: PNode) =
+ if n == nil:
+ c &= "\255"
+ return
+ var k = n.kind
+ md5Update(c, cast[cstring](addr(k)), 1)
+ # we really must not hash line information. 'n.typ' is debatable but
+ # shouldn't be necessary for now and avoids potential infinite recursions.
+ case n.kind
+ of nkEmpty, nkNilLit, nkType: discard
+ of nkIdent:
+ c &= n.ident.s
+ of nkSym:
+ hashSym(c, n.sym)
+ of nkCharLit..nkUInt64Lit:
+ var v = n.intVal
+ md5Update(c, cast[cstring](addr(v)), sizeof(v))
+ of nkFloatLit..nkFloat64Lit:
+ var v = n.floatVal
+ md5Update(c, cast[cstring](addr(v)), sizeof(v))
+ of nkStrLit..nkTripleStrLit:
+ c &= n.strVal
+ else:
+ for i in 0.. <n.len: hashTree(c, n.sons[i])
+
+proc hashType(c: var MD5Context, t: PType) =
+ # modelled after 'typeToString'
+ if t == nil:
+ c &= "\254"
+ return
+
+ var k = t.kind
+ md5Update(c, cast[cstring](addr(k)), 1)
+
+ if t.sym != nil and sfAnon notin t.sym.flags:
+ # t.n for literals, but not for e.g. objects!
+ if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
+ c.hashSym(t.sym)
+
+ case t.kind
+ of tyGenericBody, tyGenericInst, tyGenericInvokation:
+ for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvokation)):
+ c.hashType t.sons[i]
+ of tyUserTypeClass:
+ internalAssert t.sym != nil and t.sym.owner != nil
+ c &= t.sym.owner.name.s
+ of tyUserTypeClassInst:
+ let body = t.base
+ c.hashSym body.sym
+ for i in countup(1, sonsLen(t) - 2):
+ c.hashType t.sons[i]
+ of tyFromExpr, tyFieldAccessor:
+ c.hashTree(t.n)
+ of tyArrayConstr:
+ c.hashTree(t.sons[0].n)
+ c.hashType(t.sons[1])
+ of tyTuple:
+ if t.n != nil:
+ assert(sonsLen(t.n) == sonsLen(t))
+ for i in countup(0, sonsLen(t.n) - 1):
+ assert(t.n.sons[i].kind == nkSym)
+ c &= t.n.sons[i].sym.name.s
+ c &= ":"
+ c.hashType(t.sons[i])
+ c &= ","
+ else:
+ for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i]
+ of tyRange:
+ c.hashTree(t.n)
+ c.hashType(t.sons[0])
+ of tyProc:
+ c &= (if tfIterator in t.flags: "iterator " else: "proc ")
+ for i in 0.. <t.len: c.hashType(t.sons[i])
+ md5Update(c, cast[cstring](addr(t.callConv)), 1)
+
+ if tfNoSideEffect in t.flags: c &= ".noSideEffect"
+ if tfThread in t.flags: c &= ".thread"
+ else:
+ for i in 0.. <t.len: c.hashType(t.sons[i])
+ if tfShared in t.flags: c &= "shared"
+ if tfNotNil in t.flags: c &= "not nil"
+
+proc canonConst(n: PNode): TUid =
+ var c: MD5Context
+ md5Init(c)
+ c.hashTree(n)
+ c.hashType(n.typ)
+ md5Final(c, MD5Digest(result))
+
+proc canonSym(s: PSym): TUid =
+ var c: MD5Context
+ md5Init(c)
+ c.hashSym(s)
+ md5Final(c, MD5Digest(result))
+
+proc pushType(w: PRodWriter, t: PType) =
+ # check so that the stack does not grow too large:
+ if iiTableGet(w.index.tab, t.id) == InvalidKey:
+ w.tstack.add(t)
+
+proc pushSym(w: PRodWriter, s: PSym) =
+ # check so that the stack does not grow too large:
+ if iiTableGet(w.index.tab, s.id) == InvalidKey:
+ w.sstack.add(s)
+
+proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
+ result: var string) =
+ if n == nil:
+ # nil nodes have to be stored too:
+ result.add("()")
+ return
+ result.add('(')
+ encodeVInt(ord(n.kind), result)
+ # we do not write comments for now
+ # Line information takes easily 20% or more of the filesize! Therefore we
+ # omit line information if it is the same as the father's line information:
+ if fInfo.fileIndex != n.info.fileIndex:
+ result.add('?')
+ encodeVInt(n.info.col, result)
+ result.add(',')
+ encodeVInt(n.info.line, result)
+ result.add(',')
+ encodeVInt(fileIdx(w, toFilename(n.info)), result)
+ elif fInfo.line != n.info.line:
+ result.add('?')
+ encodeVInt(n.info.col, result)
+ result.add(',')
+ encodeVInt(n.info.line, result)
+ elif fInfo.col != n.info.col:
+ result.add('?')
+ encodeVInt(n.info.col, result)
+ var f = n.flags * PersistentNodeFlags
+ if f != {}:
+ result.add('$')
+ encodeVInt(cast[int32](f), result)
+ if n.typ != nil:
+ result.add('^')
+ encodeVInt(n.typ.id, result)
+ pushType(w, n.typ)
+ case n.kind
+ of nkCharLit..nkInt64Lit:
+ if n.intVal != 0:
+ result.add('!')
+ encodeVBiggestInt(n.intVal, result)
+ of nkFloatLit..nkFloat64Lit:
+ if n.floatVal != 0.0:
+ result.add('!')
+ encodeStr($n.floatVal, result)
+ of nkStrLit..nkTripleStrLit:
+ if n.strVal != "":
+ result.add('!')
+ encodeStr(n.strVal, result)
+ of nkIdent:
+ result.add('!')
+ encodeStr(n.ident.s, result)
+ of nkSym:
+ result.add('!')
+ encodeVInt(n.sym.id, result)
+ pushSym(w, n.sym)
+ else:
+ for i in countup(0, sonsLen(n) - 1):
+ encodeNode(w, n.info, n.sons[i], result)
+ add(result, ')')
+
+proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
+ var oldLen = result.len
+ result.add('<')
+ if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
+ if loc.s != low(loc.s):
+ add(result, '*')
+ encodeVInt(ord(loc.s), result)
+ if loc.flags != {}:
+ add(result, '$')
+ encodeVInt(cast[int32](loc.flags), result)
+ if loc.t != nil:
+ add(result, '^')
+ encodeVInt(cast[int32](loc.t.id), result)
+ pushType(w, loc.t)
+ if loc.r != nil:
+ add(result, '!')
+ encodeStr(ropeToStr(loc.r), result)
+ if loc.a != 0:
+ add(result, '?')
+ encodeVInt(loc.a, result)
+ if oldLen + 1 == result.len:
+ # no data was necessary, so remove the '<' again:
+ setLen(result, oldLen)
+ else:
+ add(result, '>')
+
+proc encodeType(w: PRodWriter, t: PType, result: var string) =
+ if t == nil:
+ # nil nodes have to be stored too:
+ result.add("[]")
+ return
+ # we need no surrounding [] here because the type is in a line of its own
+ if t.kind == tyForward: internalError("encodeType: tyForward")
+ # for the new rodfile viewer we use a preceeding [ so that the data section
+ # can easily be disambiguated:
+ add(result, '[')
+ encodeVInt(ord(t.kind), result)
+ add(result, '+')
+ encodeVInt(t.id, result)
+ if t.n != nil:
+ encodeNode(w, unknownLineInfo(), t.n, result)
+ if t.flags != {}:
+ add(result, '$')
+ encodeVInt(cast[int32](t.flags), result)
+ if t.callConv != low(t.callConv):
+ add(result, '?')
+ encodeVInt(ord(t.callConv), result)
+ if t.owner != nil:
+ add(result, '*')
+ encodeVInt(t.owner.id, result)
+ pushSym(w, t.owner)
+ if t.sym != nil:
+ add(result, '&')
+ encodeVInt(t.sym.id, result)
+ pushSym(w, t.sym)
+ if t.size != - 1:
+ add(result, '/')
+ encodeVBiggestInt(t.size, result)
+ if t.align != 2:
+ add(result, '=')
+ encodeVInt(t.align, result)
+ encodeLoc(w, t.loc, result)
+ for i in countup(0, sonsLen(t) - 1):
+ if t.sons[i] == nil:
+ add(result, "^()")
+ else:
+ add(result, '^')
+ encodeVInt(t.sons[i].id, result)
+ pushType(w, t.sons[i])
+
+proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
+ add(result, '|')
+ encodeVInt(ord(lib.kind), result)
+ add(result, '|')
+ encodeStr(ropeToStr(lib.name), result)
+ add(result, '|')
+ encodeNode(w, info, lib.path, result)
+
+proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
+ if s == nil:
+ # nil nodes have to be stored too:
+ result.add("{}")
+ return
+ # we need no surrounding {} here because the symbol is in a line of its own
+ encodeVInt(ord(s.kind), result)
+ result.add('+')
+ encodeVInt(s.id, result)
+ result.add('&')
+ encodeStr(s.name.s, result)
+ if s.typ != nil:
+ result.add('^')
+ encodeVInt(s.typ.id, result)
+ pushType(w, s.typ)
+ result.add('?')
+ if s.info.col != -1'i16: encodeVInt(s.info.col, result)
+ result.add(',')
+ if s.info.line != -1'i16: encodeVInt(s.info.line, result)
+ result.add(',')
+ encodeVInt(fileIdx(w, toFilename(s.info)), result)
+ if s.owner != nil:
+ result.add('*')
+ encodeVInt(s.owner.id, result)
+ pushSym(w, s.owner)
+ if s.flags != {}:
+ result.add('$')
+ encodeVInt(cast[int32](s.flags), result)
+ if s.magic != mNone:
+ result.add('@')
+ encodeVInt(ord(s.magic), result)
+ if s.options != w.options:
+ result.add('!')
+ encodeVInt(cast[int32](s.options), result)
+ if s.position != 0:
+ result.add('%')
+ encodeVInt(s.position, result)
+ if s.offset != - 1:
+ result.add('`')
+ encodeVInt(s.offset, result)
+ encodeLoc(w, s.loc, result)
+ if s.annex != nil: encodeLib(w, s.annex, s.info, result)
+ if s.constraint != nil:
+ add(result, '#')
+ encodeNode(w, unknownLineInfo(), s.constraint, result)
+ # lazy loading will soon reload the ast lazily, so the ast needs to be
+ # the last entry of a symbol:
+ if s.ast != nil:
+ # we used to attempt to save space here by only storing a dummy AST if
+ # it is not necessary, but Nimrod's heavy compile-time evaluation features
+ # make that unfeasible nowadays:
+ encodeNode(w, s.info, s.ast, result)
+
+
+proc createDb() =
+ db.exec(sql"""
+ create table if not exists Module(
+ id integer primary key,
+ name varchar(256) not null,
+ fullpath varchar(256) not null,
+ interfHash varchar(256) not null,
+ fullHash varchar(256) not null,
+
+ created timestamp not null default (DATETIME('now')),
+ );""")
+
+ db.exec(sql"""
+ create table if not exists Symbol(
+ id integer primary key,
+ module integer not null,
+ name varchar(max) not null,
+ data varchar(max) not null,
+ created timestamp not null default (DATETIME('now')),
+
+ foreign key (module) references module(id)
+ );""")
+
+ db.exec(sql"""
+ create table if not exists Type(
+ id integer primary key,
+ module integer not null,
+ name varchar(max) not null,
+ data varchar(max) not null,
+ created timestamp not null default (DATETIME('now')),
+
+ foreign key (module) references module(id)
+ );""")
+
+
+ #db.exec(sql"""
+ # --create unique index if not exists TsstNameIx on TestResult(name);
+ # """, [])
+
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 01f23850b..a4ba412a4 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -593,7 +593,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
proc genDeref(p: BProc, e: PNode, d: var TLoc) =
var a: TLoc
- if mapType(e.sons[0].typ) == ctArray:
+ if mapType(e.sons[0].typ) in {ctArray, ctPtrToArray}:
# XXX the amount of hacks for C's arrays is incredible, maybe we should
# simply wrap them in a struct? --> Losing auto vectorization then?
expr(p, e.sons[0], d)
@@ -736,7 +736,7 @@ proc genArrayElem(p: BProc, e: PNode, d: var TLoc) =
var ty = skipTypes(skipTypes(a.t, abstractVarRange), abstractPtrs)
var first = intLiteral(firstOrd(ty))
# emit range check:
- if (optBoundsCheck in p.options):
+ if optBoundsCheck in p.options and tfUncheckedArray notin ty.flags:
if not isConstExpr(e.sons[1]):
# semantic pass has already checked for const index expressions
if firstOrd(ty) == 0:
@@ -1803,7 +1803,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
else:
genProc(p.module, sym)
putLocIntoDest(p, d, sym.loc)
- of skProc, skConverter, skIterator:
+ of skProc, skConverter, skIterators:
genProc(p.module, sym)
if sym.loc.r == nil or sym.loc.t == nil:
internalError(n.info, "expr: proc not init " & sym.name.s)
@@ -1915,7 +1915,6 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
internalError(n.info, "expr: proc not init " & sym.name.s)
putLocIntoDest(p, d, sym.loc)
of nkClosure: genClosure(p, n, d)
- of nkMetaNode: expr(p, n.sons[0], d)
of nkEmpty: discard
of nkWhileStmt: genWhileStmt(p, n)
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index 5b04f1358..2d27257ce 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -145,33 +145,6 @@ proc atEndMark(buf: cstring, pos: int): bool =
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
result = s == NimMergeEndMark.len
-when false:
- proc readVerbatimSection(L: var TBaseLexer): PRope =
- var pos = L.bufpos
- var buf = L.buf
- result = newMutableRope(30_000)
- while true:
- case buf[pos]
- of CR:
- pos = nimlexbase.HandleCR(L, pos)
- buf = L.buf
- result.data.add(tnl)
- of LF:
- pos = nimlexbase.HandleLF(L, pos)
- buf = L.buf
- result.data.add(tnl)
- of '\0':
- InternalError("ccgmerge: expected: " & NimMergeEndMark)
- break
- else:
- if atEndMark(buf, pos):
- inc pos, NimMergeEndMark.len
- break
- result.data.add(buf[pos])
- inc pos
- L.bufpos = pos
- freezeMutableRope(result)
-
proc readVerbatimSection(L: var TBaseLexer): PRope =
var pos = L.bufpos
var buf = L.buf
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index af0d657f1..990153fc7 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -65,6 +65,7 @@ proc startBlock(p: BProc, start: TFormatStr = "{$n",
setLen(p.blocks, result + 1)
p.blocks[result].id = p.labels
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
+ p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
proc assignLabel(b: var TBlock): PRope {.inline.} =
b.label = con("LA", b.id.toRope)
@@ -260,37 +261,57 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
else: internalError(n.info, "genIf()")
if sonsLen(n) > 1: fixLabel(p, lend)
-proc blockLeaveActions(p: BProc, howMany: int) =
- var L = p.nestedTryStmts.len
- # danger of endless recursion! we workaround this here by a temp stack
+
+proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
+ # Called by return and break stmts.
+ # Deals with issues faced when jumping out of try/except/finally stmts,
+
var stack: seq[PNode]
- newSeq(stack, howMany)
- for i in countup(1, howMany):
- stack[i-1] = p.nestedTryStmts[L-i]
- setLen(p.nestedTryStmts, L-howMany)
+ newSeq(stack, 0)
var alreadyPoppedCnt = p.inExceptBlock
- for tryStmt in items(stack):
+ for i in countup(1, howManyTrys):
+
if gCmd != cmdCompileToCpp:
+ # Pop safe points generated by try
if alreadyPoppedCnt > 0:
dec alreadyPoppedCnt
else:
linefmt(p, cpsStmts, "#popSafePoint();$n")
+
+ # Pop this try-stmt of the list of nested trys
+ # so we don't infinite recurse on it in the next step.
+ var tryStmt = p.nestedTryStmts.pop
+ stack.add(tryStmt)
+
+ # Find finally-stmt for this try-stmt
+ # and generate a copy of its sons
var finallyStmt = lastSon(tryStmt)
if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0])
+
# push old elements again:
- for i in countdown(howMany-1, 0):
+ for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i])
+
if gCmd != cmdCompileToCpp:
- for i in countdown(p.inExceptBlock-1, 0):
+ # Pop exceptions that was handled by the
+ # except-blocks we are in
+ for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genReturnStmt(p: BProc, t: PNode) =
p.beforeRetNeeded = true
genLineDir(p, t)
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
- blockLeaveActions(p, min(1, p.nestedTryStmts.len))
+ blockLeaveActions(p,
+ howManyTrys = p.nestedTryStmts.len,
+ howManyExcepts = p.inExceptBlock)
+ if (p.finallySafePoints.len > 0):
+ # If we're in a finally block, and we came here by exception
+ # consume it before we return.
+ var safePoint = p.finallySafePoints[p.finallySafePoints.len-1]
+ linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint)
lineFF(p, cpsStmts, "goto BeforeRet;$n", "br label %BeforeRet$n", [])
proc genComputedGoto(p: BProc; n: PNode) =
@@ -450,7 +471,9 @@ proc genBreakStmt(p: BProc, t: PNode) =
if idx < 0 or not p.blocks[idx].isLoop:
internalError(t.info, "no loop to break")
let label = assignLabel(p.blocks[idx])
- blockLeaveActions(p, p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts)
+ blockLeaveActions(p,
+ p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
+ p.inExceptBlock - p.blocks[idx].nestedExceptStmts)
genLineDir(p, t)
lineF(p, cpsStmts, "goto $1;$n", [label])
@@ -827,7 +850,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
discard pop(p.nestedTryStmts)
endBlock(p) # end of else block
if i < length and t.sons[i].kind == nkFinally:
+ p.finallySafePoints.add(safePoint)
exprBlock(p, t.sons[i].sons[0], d)
+ discard pop(p.finallySafePoints)
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint)
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
@@ -838,7 +863,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): PRope =
res.add(t.sons[i].strVal)
of nkSym:
var sym = t.sons[i].sym
- if sym.kind in {skProc, skIterator, skMethod}:
+ if sym.kind in {skProc, skIterator, skClosureIterator, skMethod}:
var a: TLoc
initLocExpr(p, t.sons[i], a)
res.add(rdLoc(a).ropeToStr)
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index c92c15fa9..321462044 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -69,7 +69,7 @@ proc mangleName(s: PSym): PRope =
if result == nil:
if gCmd == cmdCompileToLLVM:
case s.kind
- of skProc, skMethod, skConverter, skConst, skIterator:
+ of skProc, skMethod, skConverter, skConst, skIterators:
result = ~"@"
of skVar, skForVar, skResult, skLet:
if sfGlobal in s.flags: result = ~"@"
@@ -185,7 +185,7 @@ proc mapType(typ: PType): TCTypeKind =
of tyPtr, tyVar, tyRef:
var base = skipTypes(typ.sons[0], typedescInst)
case base.kind
- of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray
+ of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctPtrToArray
else: result = ctPtr
of tyPointer: result = ctPtr
of tySequence: result = ctNimSeq
@@ -376,10 +376,13 @@ proc getTypePre(m: BModule, typ: PType): PRope =
else:
result = getSimpleTypeDesc(m, typ)
if result == nil: result = cacheGetType(m.typeCache, typ)
-
+
+proc structOrUnion(t: PType): PRope =
+ (if tfUnion in t.flags: toRope("union") else: toRope("struct"))
+
proc getForwardStructFormat(): string =
- if gCmd == cmdCompileToCpp: result = "struct $1;$n"
- else: result = "typedef struct $1 $1;$n"
+ if gCmd == cmdCompileToCpp: result = "$1 $2;$n"
+ else: result = "typedef $1 $2 $2;$n"
proc getTypeForward(m: BModule, typ: PType): PRope =
result = cacheGetType(m.forwTypeCache, typ)
@@ -390,7 +393,8 @@ proc getTypeForward(m: BModule, typ: PType): PRope =
of tySequence, tyTuple, tyObject:
result = getTypeName(typ)
if not isImportedType(typ):
- appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
+ appf(m.s[cfsForwardTypes], getForwardStructFormat(),
+ [structOrUnion(typ), result])
idTablePut(m.forwTypeCache, typ, result)
else: internalError("getTypeForward(" & $typ.kind & ')')
@@ -445,7 +449,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
else: ae = sname
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
- appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
+ let fieldType = field.loc.t
+ if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
+ appf(result, "$1 $2[SEQ_DECL_SIZE];$n",
+ [getTypeDescAux(m, fieldType.elemType, check), sname])
+ else:
+ appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname])
else: internalError(n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
@@ -455,23 +464,24 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
check: var TIntSet): PRope =
# declare the record:
var hasField = false
+ let aStruct = structOrUnion(typ)
if typ.kind == tyObject:
if typ.sons[0] == nil:
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
- result = ropecg(m, "struct $1 {$n", [name])
+ result = ropecg(m, "$1 $2 {$n", [aStruct, name])
else:
- result = ropecg(m, "struct $1 {$n#TNimType* m_type;$n", [name])
+ result = ropecg(m, "$1 $2 {$n#TNimType* m_type;$n", [aStruct, name])
hasField = true
elif gCmd == cmdCompileToCpp:
- result = ropecg(m, "struct $1 : public $2 {$n",
- [name, getTypeDescAux(m, typ.sons[0], check)])
+ result = ropecg(m, "$1 $2 : public $3 {$n",
+ [aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true
else:
- result = ropecg(m, "struct $1 {$n $2 Sup;$n",
- [name, getTypeDescAux(m, typ.sons[0], check)])
+ result = ropecg(m, "$1 $2 {$n $3 Sup;$n",
+ [aStruct, name, getTypeDescAux(m, typ.sons[0], check)])
hasField = true
else:
- result = ropef("struct $1 {$n", [name])
+ result = ropef("$1 $2 {$n", [aStruct, name])
var desc = getRecordFields(m, typ, check)
if (desc == nil) and not hasField:
appf(result, "char dummy;$n", [])
@@ -480,8 +490,8 @@ proc getRecordDesc(m: BModule, typ: PType, name: PRope,
app(result, "};" & tnl)
proc getTupleDesc(m: BModule, typ: PType, name: PRope,
- check: var TIntSet): PRope =
- result = ropef("struct $1 {$n", [name])
+ check: var TIntSet): PRope =
+ result = ropef("$1 $2 {$n", [structOrUnion(typ), name])
var desc: PRope = nil
for i in countup(0, sonsLen(typ) - 1):
appf(desc, "$1 Field$2;$n",
@@ -557,7 +567,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
if result == nil:
result = getTypeName(t)
if not isImportedType(t):
- appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
+ appf(m.s[cfsForwardTypes], getForwardStructFormat(),
+ [structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result)
assert(cacheGetType(m.typeCache, t) == nil)
idTablePut(m.typeCache, t, con(result, "*"))
@@ -588,7 +599,8 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
if result == nil:
result = getTypeName(t)
if not isImportedType(t):
- appf(m.s[cfsForwardTypes], getForwardStructFormat(), [result])
+ appf(m.s[cfsForwardTypes], getForwardStructFormat(),
+ [structOrUnion(t), result])
idTablePut(m.forwTypeCache, t, result)
idTablePut(m.typeCache, t, result) # always call for sideeffects:
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 1129ecbef..da1673ca4 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -130,7 +130,6 @@ proc getUniqueType*(key: PType): PType =
idTablePut(gTypeTable[k], key, key)
result = key
of tyProc:
- # tyVar is not 100% correct, but would speeds things up a little:
if key.callConv != ccClosure:
result = key
else:
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 5057ae558..683aed069 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -705,7 +705,7 @@ proc cgsym(m: BModule, name: string): PRope =
var sym = magicsys.getCompilerProc(name)
if sym != nil:
case sym.kind
- of skProc, skMethod, skConverter, skIterator: genProc(m, sym)
+ of skProc, skMethod, skConverter, skIterators: genProc(m, sym)
of skVar, skResult, skLet: genVarPrototype(m, sym)
of skType: discard getTypeDesc(m, sym.typ)
else: internalError("cgsym: " & name)
@@ -761,6 +761,8 @@ proc genProcAux(m: BModule, prc: PSym) =
var returnStmt: PRope = nil
assert(prc.ast != nil)
if sfPure notin prc.flags and prc.typ.sons[0] != nil:
+ if resultPos >= prc.ast.len:
+ internalError(prc.info, "proc has no result symbol")
var res = prc.ast.sons[resultPos].sym # get result symbol
if not isInvalidReturnType(prc.typ.sons[0]):
if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
@@ -962,8 +964,8 @@ proc genMainProc(m: BModule) =
NimMainBody =
"N_CDECL(void, NimMain)(void) {$N" &
"\tPreMain();$N" &
- "$1$N" &
- "}$N"
+ "$1" &
+ "}$N$N"
PosixNimMain =
"int cmdCount;$N" &
@@ -977,20 +979,20 @@ proc genMainProc(m: BModule) =
"\tcmdCount = argc;$N" &
"\tgEnv = env;$N" &
MainProcsWithResult &
- "}$N"
+ "}$N$N"
StandaloneCMain =
"int main(void) {$N" &
MainProcs &
"\treturn 0;$N" &
- "}$N"
+ "}$N$N"
WinNimMain = NimMainBody
WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" &
" HINSTANCE hPrevInstance, $N" &
" LPSTR lpCmdLine, int nCmdShow) {$N" &
- MainProcsWithResult & "}$N"
+ MainProcsWithResult & "}$N$N"
WinNimDllMain = "N_LIB_EXPORT " & NimMainBody
@@ -998,14 +1000,14 @@ proc genMainProc(m: BModule) =
"BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
" LPVOID lpvReserved) {$N" &
"\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
- "\treturn 1;$N}$N"
+ "\treturn 1;$N}$N$N"
PosixNimDllMain = WinNimDllMain
PosixCDllMain =
"void NIM_POSIX_INIT NimMainInit(void) {$N" &
MainProcs &
- "}$N"
+ "}$N$N"
var nimMain, otherMain: TFormatStr
if platform.targetOS == osWindows and
@@ -1034,7 +1036,7 @@ proc genMainProc(m: BModule) =
platform.targetOS == osStandalone: "".toRope
else: ropecg(m, "\t#initStackBottom();$N")
inc(m.labels)
- appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N", [
+ appcg(m, m.s[cfsProcs], "void PreMain() {$N" & PreMainBody & "}$N$N", [
mainDatInit, initStackBottomCall, gBreakpoints, otherModsInit])
appcg(m, m.s[cfsProcs], nimMain, [mainModInit, toRope(m.labels)])
@@ -1042,8 +1044,10 @@ proc genMainProc(m: BModule) =
appcg(m, m.s[cfsProcs], otherMain, [])
proc getSomeInitName(m: PSym, suffix: string): PRope =
+ assert m.kind == skModule
+ assert m.owner.kind == skPackage
if {sfSystemModule, sfMainModule} * m.flags == {}:
- result = m.info.toFullPath.getPackageName.mangle.toRope
+ result = m.owner.name.s.mangle.toRope
result.app m.name.s
result.app suffix
@@ -1188,7 +1192,7 @@ proc nullify[T](arr: var T) =
for i in low(arr)..high(arr):
arr[i] = nil
-proc resetModule*(m: var BModule) =
+proc resetModule*(m: BModule) =
# between two compilations in CAAS mode, we can throw
# away all the data that was written to disk
initLinkedList(m.headerFiles)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 9cd2c0d87..e7d818556 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -41,7 +41,8 @@ type
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
ctFloat, ctFloat32, ctFloat64, ctFloat128,
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
- ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
+ ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
+ ctCString
TCFileSections* = array[TCFileSection, PRope] # represents a generated C file
TCProcSection* = enum # the sections a generated C proc consists of
cpsLocals, # section of local variables for C proc
@@ -57,17 +58,20 @@ type
sections*: TCProcSections # the code beloging
isLoop*: bool # whether block is a loop
nestedTryStmts*: int16 # how many try statements is it nested into
+ nestedExceptStmts*: int16 # how many except statements is it nested into
frameLen*: int16
TCProc{.final.} = object # represents C proc that is currently generated
prc*: PSym # the Nimrod proc that this C proc belongs to
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
threadVarAccessed*: bool # true if the proc already accessed some threadvar
- nestedTryStmts*: seq[PNode] # in how many nested try statements we are
- # (the vars must be volatile then)
+ nestedTryStmts*: seq[PNode] # in how many nested try statements we are
+ # (the vars must be volatile then)
inExceptBlock*: int # are we currently inside an except block?
# leaving such scopes by raise or by return must
# execute any applicable finally blocks
+ finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when
+ # using return in finally statements
labels*: Natural # for generating unique labels in the C proc
blocks*: seq[TBlock] # nested blocks
breakIdx*: int # the block that will be exited
@@ -140,8 +144,9 @@ proc newProc*(prc: PSym, module: BModule): BProc =
else: result.options = gOptions
newSeq(result.blocks, 1)
result.nestedTryStmts = @[]
+ result.finallySafePoints = @[]
-iterator cgenModules*: var BModule =
+iterator cgenModules*: BModule =
for i in 0..high(gModules):
# ultimately, we are iterating over the file ids here.
# some "files" won't have an associated cgen module (like stdin)
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index c79fda13e..ddf2075b4 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -47,6 +47,7 @@ proc initDefines*() =
defineSymbol("nimeffects")
defineSymbol("nimbabel")
defineSymbol("nimcomputedgoto")
+ defineSymbol("nimunion")
# add platform specific symbols:
case targetCPU
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 343f415b3..d8c439c9c 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -42,17 +42,23 @@ proc compilerMsgHandler(filename: string, line, col: int,
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
globalError(newLineInfo(filename, line, col), k, arg)
+proc docgenFindFile(s: string): string {.procvar.} =
+ result = options.findFile(s)
+ if result.len == 0:
+ result = getCurrentDir() / s
+ if not existsFile(result): result = ""
+
proc parseRst(text, filename: string,
line, column: int, hasToc: var bool,
rstOptions: TRstParseOptions): PRstNode =
result = rstParse(text, filename, line, column, hasToc, rstOptions,
- options.findFile, compilerMsgHandler)
+ docgenFindFile, compilerMsgHandler)
proc newDocumentor*(filename: string, config: PStringTable): PDoc =
new(result)
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
options.gConfigVars, filename, {roSupportRawDirective},
- options.findFile, compilerMsgHandler)
+ docgenFindFile, compilerMsgHandler)
result.id = 100
proc dispA(dest: var PRope, xml, tex: string, args: openArray[PRope]) =
@@ -360,7 +366,7 @@ proc generateJson(d: PDoc, n: PNode, jArray: PJsonNode = nil): PJsonNode =
proc genSection(d: PDoc, kind: TSymKind) =
const sectionNames: array[skModule..skTemplate, string] = [
"Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Methods",
- "Iterators", "Converters", "Macros", "Templates"
+ "Iterators", "Iterators", "Converters", "Macros", "Templates"
]
if d.section[kind] == nil: return
var title = sectionNames[kind].toRope
diff --git a/compiler/guards.nim b/compiler/guards.nim
index 607bb074a..fe868054f 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -9,7 +9,7 @@
## This module implements the 'implies' relation for guards.
-import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer
+import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents
const
someEq = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
@@ -69,9 +69,23 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
-proc swapArgs(fact: PNode, newOp: string, m: TMagic): PNode =
+proc getMagicOp(name: string, m: TMagic): PSym =
+ result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
+ result.magic = m
+
+let
+ opLe = getMagicOp("<=", mLeI)
+ opLt = getMagicOp("<", mLtI)
+ opAnd = getMagicOp("and", mAnd)
+ opOr = getMagicOp("or", mOr)
+ opNot = getMagicOp("not", mNot)
+ opIsNil = getMagicOp("isnil", mIsNil)
+ opContains = getMagicOp("contains", mInSet)
+ opEq = getMagicOp("==", mEqI)
+
+proc swapArgs(fact: PNode, newOp: PSym): PNode =
result = newNodeI(nkCall, fact.info, 3)
- result.sons[0] = newSymNode(getSysMagic(newOp, m))
+ result.sons[0] = newSymNode(newOp)
result.sons[1] = fact.sons[2]
result.sons[2] = fact.sons[1]
@@ -82,9 +96,9 @@ proc neg(n: PNode): PNode =
result = n.sons[1]
of someLt:
# not (a < b) == a >= b == b <= a
- result = swapArgs(n, "<=", mLeI)
+ result = swapArgs(n, opLe)
of someLe:
- result = swapArgs(n, "<", mLtI)
+ result = swapArgs(n, opLt)
of mInSet:
if n.sons[1].kind != nkCurly: return nil
let t = n.sons[2].typ.skipTypes(abstractInst)
@@ -110,7 +124,7 @@ proc neg(n: PNode): PNode =
b = n.sons[2].neg
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
- result.sons[0] = newSymNode(getSysMagic("and", mAnd))
+ result.sons[0] = newSymNode(opAnd)
result.sons[1] = a
result.sons[2] = b
elif a != nil:
@@ -120,12 +134,12 @@ proc neg(n: PNode): PNode =
else:
# leave not (a == 4) as it is
result = newNodeI(nkCall, n.info, 2)
- result.sons[0] = newSymNode(getSysMagic("not", mNot))
+ result.sons[0] = newSymNode(opNot)
result.sons[1] = n
proc buildIsNil(arg: PNode): PNode =
result = newNodeI(nkCall, arg.info, 2)
- result.sons[0] = newSymNode(getSysMagic("isNil", mIsNil))
+ result.sons[0] = newSymNode(opIsNil)
result.sons[1] = arg
proc usefulFact(n: PNode): PNode =
@@ -154,7 +168,7 @@ proc usefulFact(n: PNode): PNode =
b = usefulFact(n.sons[2])
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
- result.sons[0] = newSymNode(getSysMagic("and", mAnd))
+ result.sons[0] = newSymNode(opAnd)
result.sons[1] = a
result.sons[2] = b
elif a != nil:
@@ -177,7 +191,7 @@ proc usefulFact(n: PNode): PNode =
b = usefulFact(n.sons[2]).neg
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
- result.sons[0] = newSymNode(getSysMagic("and", mAnd))
+ result.sons[0] = newSymNode(opAnd)
result.sons[1] = a
result.sons[2] = b
result = result.neg
@@ -520,7 +534,7 @@ proc buildOf(it, loc: PNode): PNode =
s.typ = settype(loc)
for i in 0..it.len-2: s.sons[i] = it.sons[i]
result = newNodeI(nkCall, it.info, 3)
- result.sons[0] = newSymNode(getSysMagic("contains", mInSet))
+ result.sons[0] = newSymNode(opContains)
result.sons[1] = s
result.sons[2] = loc
@@ -532,20 +546,20 @@ proc buildElse(n: PNode): PNode =
for j in 0..branch.len-2:
s.add(branch.sons[j])
result = newNodeI(nkCall, n.info, 3)
- result.sons[0] = newSymNode(getSysMagic("contains", mInSet))
+ result.sons[0] = newSymNode(opContains)
result.sons[1] = s
result.sons[2] = n.sons[0]
proc addDiscriminantFact*(m: var TModel, n: PNode) =
var fact = newNodeI(nkCall, n.info, 3)
- fact.sons[0] = newSymNode(getSysMagic("==", mEqI))
+ fact.sons[0] = newSymNode(opEq)
fact.sons[1] = n.sons[0]
fact.sons[2] = n.sons[1]
m.add fact
proc addAsgnFact*(m: var TModel, key, value: PNode) =
var fact = newNodeI(nkCall, key.info, 3)
- fact.sons[0] = newSymNode(getSysMagic("==", mEqI))
+ fact.sons[0] = newSymNode(opEq)
fact.sons[1] = key
fact.sons[2] = value
m.add fact
diff --git a/compiler/importer.nim b/compiler/importer.nim
index 078a90c98..7a73f2bbf 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -103,7 +103,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
internalError(n.info, "importSymbol: 2")
# for an enumeration we have to add all identifiers
case s.kind
- of skProc, skMethod, skIterator, skMacro, skTemplate, skConverter:
+ of skProcKinds:
# for a overloadable syms add all overloaded routines
var it: TIdentIter
var e = initIdentIter(it, fromMod.tab, s.name)
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index c0fc4131a..0cbd4c364 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -275,11 +275,11 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
- ["AddU", "AddU", "AddU($1, $2)", "AddU($1, $2)"], # AddU
- ["SubU", "SubU", "SubU($1, $2)", "SubU($1, $2)"], # SubU
- ["MulU", "MulU", "MulU($1, $2)", "MulU($1, $2)"], # MulU
- ["DivU", "DivU", "DivU($1, $2)", "DivU($1, $2)"], # DivU
- ["ModU", "ModU", "ModU($1, $2)", "ModU($1, $2)"], # ModU
+ ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
+ ["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU
+ ["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU
+ ["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU
+ ["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU
["", "", "($1 == $2)", "($1 == $2)"], # EqI
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
["", "", "($1 < $2)", "($1 < $2)"], # LtI
@@ -289,10 +289,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
- ["LeU", "LeU", "LeU($1, $2)", "LeU($1, $2)"], # LeU
- ["LtU", "LtU", "LtU($1, $2)", "LtU($1, $2)"], # LtU
- ["LeU64", "LeU64", "LeU64($1, $2)", "LeU64($1, $2)"], # LeU64
- ["LtU64", "LtU64", "LtU64($1, $2)", "LtU64($1, $2)"], # LtU64
+ ["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU
+ ["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU
+ ["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64
+ ["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
@@ -309,10 +309,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
["", "", "($1 != $2)", "($1 != $2)"], # Xor
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
- ["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI
- ["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64
- ["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI
- ["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64
+ ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
+ ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
+ ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
+ ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
["", "", "!($1)", "!($1)"], # Not
["", "", "+($1)", "+($1)"], # UnaryPlusI
["", "", "~($1)", "~($1)"], # BitnotI
@@ -327,9 +327,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
- ["ToU8", "ToU8", "ToU8($1)", "ToU8($1)"], # ToU8
- ["ToU16", "ToU16", "ToU16($1)", "ToU16($1)"], # ToU16
- ["ToU32", "ToU32", "ToU32($1)", "ToU32($1)"], # ToU32
+ ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
+ ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
+ ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
["", "", "$1", "$1"], # ToFloat
["", "", "$1", "$1"], # ToBiggestFloat
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
@@ -375,11 +375,11 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxI64
["nimMin", "nimMin", "nimMin($1, $2)", "nimMin($1, $2)"], # MinF64
["nimMax", "nimMax", "nimMax($1, $2)", "nimMax($1, $2)"], # MaxF64
- ["AddU", "AddU", "AddU($1, $2)", "AddU($1, $2)"], # AddU
- ["SubU", "SubU", "SubU($1, $2)", "SubU($1, $2)"], # SubU
- ["MulU", "MulU", "MulU($1, $2)", "MulU($1, $2)"], # MulU
- ["DivU", "DivU", "DivU($1, $2)", "DivU($1, $2)"], # DivU
- ["ModU", "ModU", "ModU($1, $2)", "ModU($1, $2)"], # ModU
+ ["addU", "addU", "addU($1, $2)", "addU($1, $2)"], # addU
+ ["subU", "subU", "subU($1, $2)", "subU($1, $2)"], # subU
+ ["mulU", "mulU", "mulU($1, $2)", "mulU($1, $2)"], # mulU
+ ["divU", "divU", "divU($1, $2)", "divU($1, $2)"], # divU
+ ["modU", "modU", "modU($1, $2)", "modU($1, $2)"], # modU
["", "", "($1 == $2)", "($1 == $2)"], # EqI
["", "", "($1 <= $2)", "($1 <= $2)"], # LeI
["", "", "($1 < $2)", "($1 < $2)"], # LtI
@@ -389,10 +389,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqF64
["", "", "($1 <= $2)", "($1 <= $2)"], # LeF64
["", "", "($1 < $2)", "($1 < $2)"], # LtF64
- ["LeU", "LeU", "LeU($1, $2)", "LeU($1, $2)"], # LeU
- ["LtU", "LtU", "LtU($1, $2)", "LtU($1, $2)"], # LtU
- ["LeU64", "LeU64", "LeU64($1, $2)", "LeU64($1, $2)"], # LeU64
- ["LtU64", "LtU64", "LtU64($1, $2)", "LtU64($1, $2)"], # LtU64
+ ["leU", "leU", "leU($1, $2)", "leU($1, $2)"], # leU
+ ["ltU", "ltU", "ltU($1, $2)", "ltU($1, $2)"], # ltU
+ ["leU64", "leU64", "leU64($1, $2)", "leU64($1, $2)"], # leU64
+ ["ltU64", "ltU64", "ltU64($1, $2)", "ltU64($1, $2)"], # ltU64
["", "", "($1 == $2)", "($1 == $2)"], # EqEnum
["", "", "($1 <= $2)", "($1 <= $2)"], # LeEnum
["", "", "($1 < $2)", "($1 < $2)"], # LtEnum
@@ -409,10 +409,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "($1 == $2)", "($1 == $2)"], # EqCString
["", "", "($1 != $2)", "($1 != $2)"], # Xor
["", "", "($1 == $2)", "($1 == $2)"], # EqProc
- ["NegInt", "", "NegInt($1)", "-($1)"], # UnaryMinusI
- ["NegInt64", "", "NegInt64($1)", "-($1)"], # UnaryMinusI64
- ["AbsInt", "", "AbsInt($1)", "Math.abs($1)"], # AbsI
- ["AbsInt64", "", "AbsInt64($1)", "Math.abs($1)"], # AbsI64
+ ["negInt", "", "negInt($1)", "-($1)"], # UnaryMinusI
+ ["negInt64", "", "negInt64($1)", "-($1)"], # UnaryMinusI64
+ ["absInt", "", "absInt($1)", "Math.abs($1)"], # AbsI
+ ["absInt64", "", "absInt64($1)", "Math.abs($1)"], # AbsI64
["", "", "not ($1)", "not ($1)"], # Not
["", "", "+($1)", "+($1)"], # UnaryPlusI
["", "", "~($1)", "~($1)"], # BitnotI
@@ -427,9 +427,9 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["Ze16ToI64", "Ze16ToI64", "Ze16ToI64($1)", "Ze16ToI64($1)"], # mZe16ToI64
["Ze32ToI64", "Ze32ToI64", "Ze32ToI64($1)", "Ze32ToI64($1)"], # mZe32ToI64
["ZeIToI64", "ZeIToI64", "ZeIToI64($1)", "ZeIToI64($1)"], # mZeIToI64
- ["ToU8", "ToU8", "ToU8($1)", "ToU8($1)"], # ToU8
- ["ToU16", "ToU16", "ToU16($1)", "ToU16($1)"], # ToU16
- ["ToU32", "ToU32", "ToU32($1)", "ToU32($1)"], # ToU32
+ ["toU8", "toU8", "toU8($1)", "toU8($1)"], # toU8
+ ["toU16", "toU16", "toU16($1)", "toU16($1)"], # toU16
+ ["toU32", "toU32", "toU32($1)", "toU32($1)"], # toU32
["", "", "$1", "$1"], # ToFloat
["", "", "$1", "$1"], # ToBiggestFloat
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
@@ -812,8 +812,8 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
if needsNoCopy(y) or noCopyNeeded:
appf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else:
- useMagic(p, "NimCopy")
- appf(p.body, "$1 = NimCopy($2, $3);$n",
+ useMagic(p, "nimCopy")
+ appf(p.body, "$1 = nimCopy($2, $3);$n",
[a.res, b.res, genTypeInfo(p, y.typ)])
of etyBaseIndex:
if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
@@ -1113,8 +1113,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope =
var length = int(lengthOrd(t))
var e = elemType(t)
if length > 32:
- useMagic(p, "ArrayConstr")
- result = ropef("ArrayConstr($1, $2, $3)", [toRope(length),
+ useMagic(p, "arrayConstr")
+ result = ropef("arrayConstr($1, $2, $3)", [toRope(length),
createVar(p, e, false), genTypeInfo(p, e)])
else:
result = toRope("[")
@@ -1154,7 +1154,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): PRope =
proc isIndirect(v: PSym): bool =
result = (sfAddrTaken in v.flags) and (mapType(v.typ) != etyObject) and
- v.kind notin {skProc, skConverter, skMethod, skIterator}
+ v.kind notin {skProc, skConverter, skMethod, skIterator, skClosureIterator}
proc genVarInit(p: PProc, v: PSym, n: PNode) =
var
@@ -1171,8 +1171,8 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
if needsNoCopy(n):
s = a.res
else:
- useMagic(p, "NimCopy")
- s = ropef("NimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)])
+ useMagic(p, "nimCopy")
+ s = ropef("nimCopy($1, $2)", [a.res, genTypeInfo(p, n.typ)])
of etyBaseIndex:
if (a.typ != etyBaseIndex): internalError(n.info, "genVarInit")
if {sfAddrTaken, sfGlobal} * v.flags != {}:
@@ -1600,7 +1600,6 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
if lfNoDecl in s.loc.flags or s.magic != mNone: discard
elif not p.g.generatedSyms.containsOrIncl(s.id):
app(p.locals, genProc(p, s))
- of nkMetaNode: gen(p, n.sons[0], r)
of nkType: r.res = genTypeInfo(p, n.typ)
of nkStmtList, nkStmtListExpr:
# this shows the distinction is nice for backends and should be kept
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 3738f89b2..67cd364dc 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -232,9 +232,9 @@ proc newOuterContext(fn: PSym, up: POuterContext = nil): POuterContext =
initIdNodeTable(result.localsToAccess)
initIdTable(result.localsToEnv)
initIdTable(result.lambdasToEnv)
- result.isIter = fn.kind == skIterator and fn.typ.callConv == ccClosure
+ result.isIter = fn.kind == skClosureIterator
if result.isIter: initIterContext(result, fn)
-
+
proc newInnerContext(fn: PSym): PInnerContext =
new(result)
result.fn = fn
@@ -292,8 +292,7 @@ proc newCall(a, b: PSym): PNode =
result.add newSymNode(b)
proc isInnerProc(s, outerProc: PSym): bool {.inline.} =
- result = (s.kind in {skProc, skMethod, skConverter} or
- s.kind == skIterator and s.typ.callConv == ccClosure) and
+ result = s.kind in {skProc, skMethod, skConverter, skClosureIterator} and
s.skipGenericOwner == outerProc
#s.typ.callConv == ccClosure
@@ -653,7 +652,7 @@ proc outerProcSons(o: POuterContext, n: PNode) =
proc liftIterSym*(n: PNode): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
let iter = n.sym
- assert iter.kind == skIterator
+ assert iter.kind == skClosureIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
@@ -679,7 +678,7 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode =
var closure = PEnv(idTableGet(o.lambdasToEnv, local))
- if local.kind == skIterator and local.typ.callConv == ccClosure:
+ if local.kind == skClosureIterator:
# consider: [i1, i2, i1] Since we merged the iterator's closure
# with the captured owning variables, we need to generate the
# closure generation code again:
@@ -843,10 +842,10 @@ proc liftForLoop*(body: PNode): PNode =
# static binding?
var env: PSym
- if call[0].kind == nkSym and call[0].sym.kind == skIterator:
+ if call[0].kind == nkSym and call[0].sym.kind == skClosureIterator:
# createClosure()
let iter = call[0].sym
- assert iter.kind == skIterator
+ assert iter.kind == skClosureIterator
env = copySym(getHiddenParam(iter))
var v = newNodeI(nkVarSection, body.info)
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 0e7df13cd..9c6c5e22f 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -110,6 +110,8 @@ type
fNumber*: BiggestFloat # the parsed floating point literal
base*: TNumericalBase # the numerical base; only valid for int
# or float literals
+ strongSpaceA*: int8 # leading spaces of an operator
+ strongSpaceB*: int8 # trailing spaces of an operator
literal*: string # the parsed (string) literal; and
# documentation comments are here too
line*, col*: int
@@ -119,6 +121,7 @@ type
indentAhead*: int # if > 0 an indendation has already been read
# this is needed because scanning comments
# needs so much look-ahead
+ strongSpaces*: bool
var gLinesCompiled*: int # all lines that have been compiled
@@ -183,6 +186,7 @@ proc initToken*(L: var TToken) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
+ L.strongSpaceA = 0
L.literal = ""
L.fNumber = 0.0
L.base = base10
@@ -192,6 +196,7 @@ proc fillToken(L: var TToken) =
L.tokType = tkInvalid
L.iNumber = 0
L.indent = 0
+ L.strongSpaceA = 0
setLen(L.literal, 0)
L.fNumber = 0.0
L.base = base10
@@ -634,6 +639,14 @@ proc getOperator(L: var TLexer, tok: var TToken) =
h = h !& ord(c)
inc(pos)
endOperator(L, tok, pos, h)
+ # advance pos but don't store it in L.bufpos so the next token (which might
+ # be an operator too) gets the preceeding spaces:
+ tok.strongSpaceB = 0
+ while buf[pos] == ' ':
+ inc pos
+ inc tok.strongSpaceB
+ if buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
+ tok.strongSpaceB = -1
proc scanComment(L: var TLexer, tok: var TToken) =
var pos = L.bufpos
@@ -677,10 +690,12 @@ proc scanComment(L: var TLexer, tok: var TToken) =
proc skip(L: var TLexer, tok: var TToken) =
var pos = L.bufpos
var buf = L.buf
+ tok.strongSpaceA = 0
while true:
case buf[pos]
of ' ':
inc(pos)
+ inc(tok.strongSpaceA)
of Tabulator:
lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
inc(pos)
@@ -691,6 +706,7 @@ proc skip(L: var TLexer, tok: var TToken) =
while buf[pos] == ' ':
inc(pos)
inc(indent)
+ tok.strongSpaceA = 0
if buf[pos] > ' ':
tok.indent = indent
break
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index c31eb3121..8239f2a47 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -32,6 +32,7 @@ proc considerAcc*(n: PNode): PIdent =
of nkSym: id.add(x.sym.name.s)
else: globalError(n.info, errIdentifierExpected, renderTree(n))
result = getIdent(id)
+ of nkOpenSymChoice, nkClosedSymChoice: result = n.sons[0].sym.name
else:
globalError(n.info, errIdentifierExpected, renderTree(n))
@@ -91,7 +92,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
result.typ = errorType(c)
incl(result.flags, sfDiscardable)
# pretend it's imported from some unknown module to prevent cascading errors:
- if gCmd != cmdInteractive:
+ if gCmd != cmdInteractive and c.inCompilesContext == 0:
c.importTable.addSym(result)
type
@@ -108,7 +109,7 @@ type
proc getSymRepr*(s: PSym): string =
case s.kind
- of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
+ of skProc, skMethod, skConverter, skIterators: result = getProcHeader(s)
else: result = s.name.s
proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 6a1491682..fb1940741 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
-# (c) Copyright 2013 Andreas Rumpf
+# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -20,7 +20,7 @@ type
TModuleInMemory* = object
compiledAt*: float
crc*: TCrc32
- deps*: seq[int32] ## XXX: slurped files are not currently tracked
+ deps*: seq[int32] ## XXX: slurped files are currently not tracked
needsRecompile*: TNeedRecompile
crcStatus*: TCrcStatus
@@ -83,7 +83,7 @@ proc resetAllModules* =
for i in 0..gCompiledModules.high:
if gCompiledModules[i] != nil:
resetModule(i.int32)
-
+ resetPackageCache()
# for m in cgenModules(): echo "CGEN MODULE FOUND"
proc checkDepMem(fileIdx: int32): TNeedRecompile =
@@ -120,8 +120,9 @@ proc newModule(fileIdx: int32): PSym =
if not isNimrodIdentifier(result.name.s):
rawMessage(errInvalidModuleName, result.name.s)
- result.owner = result # a module belongs to itself
result.info = newLineInfo(fileIdx, 1, 1)
+ result.owner = newSym(skPackage, getIdent(getPackageName(filename)), nil,
+ result.info)
result.position = fileIdx
growCache gMemCacheData, fileIdx
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 61336aa87..66763e7f5 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -67,7 +67,7 @@ type
errAmbiguousCallXYZ, errWrongNumberOfArguments,
errXCannotBePassedToProcVar,
errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed,
- errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValue,
+ errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX,
errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice,
errInvalidOrderInArrayConstructor,
errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry,
@@ -88,11 +88,13 @@ type
errTemplateInstantiationTooNested, errInstantiationFrom,
errInvalidIndexValueForTuple, errCommandExpectsFilename,
errMainModuleMustBeSpecified,
- errXExpected,
+ errXExpected,
+ errTIsNotAConcreteType,
errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError,
errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile,
errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitely,
errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
+ errMacroBodyDependsOnGenericTypes,
errDestructorNotGenericEnough,
errXExpectsTwoArguments,
@@ -103,6 +105,7 @@ type
errXhasSideEffects, errIteratorExpected, errLetNeedsInit,
errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX,
errXCannotBeClosure, errXMustBeCompileTime,
+ errCannotInferTypeOfTheLiteral,
errUser,
warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@@ -263,7 +266,7 @@ const
errImplOfXNotAllowed: "implementation of \'$1\' is not allowed",
errImplOfXexpected: "implementation of \'$1\' expected",
errNoSymbolToBorrowFromFound: "no symbol to borrow from found",
- errDiscardValue: "value returned by statement has to be discarded",
+ errDiscardValueX: "value of type '$1' has to be discarded",
errInvalidDiscard: "statement returns no value that can be discarded",
errIllegalConvFromXtoY: "conversion from $1 to $2 is invalid",
errCannotBindXTwice: "cannot bind parameter \'$1\' twice",
@@ -312,6 +315,7 @@ const
errCommandExpectsFilename: "command expects a filename argument",
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
errXExpected: "\'$1\' expected",
+ errTIsNotAConcreteType: "\'$1\' is not a concrete type.",
errInvalidSectionStart: "invalid section start",
errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error",
@@ -323,6 +327,8 @@ const
errInstantiateXExplicitely: "instantiate '$1' explicitely",
errOnlyACallOpCanBeDelegator: "only a call operator can be a delegator",
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
+ errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
+ "because the parameter '$1' has a generic type",
errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
"A destructor must be associated will all instantiations of a generic type",
errXExpectsTwoArguments: "\'$1\' expects two arguments",
@@ -346,6 +352,7 @@ const
errIllegalCaptureX: "illegal capture '$1'",
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
errXMustBeCompileTime: "'$1' can only be used in compile-time context",
+ errCannotInferTypeOfTheLiteral: "cannot infer the type of the $1",
errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\' [CannotOpenFile]",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored [OctalEscape]",
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index a239d4ef2..0ee108d48 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -17,11 +17,11 @@ proc execute*(program: string) =
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
initDefines()
- LoadConfigs(DefaultConfig)
+ loadConfigs(DefaultConfig)
initDefines()
- DefineSymbol("nimrodvm")
- when hasFFI: DefineSymbol("nimffi")
+ defineSymbol("nimrodvm")
+ when hasFFI: defineSymbol("nimffi")
registerPass(verbosePass)
registerPass(semPass)
registerPass(vmPass)
@@ -30,4 +30,4 @@ proc execute*(program: string) =
compileSystemModule()
var m = makeStdinModule()
incl(m.flags, sfMainModule)
- processModule(m, LLStreamOpen(program), nil)
+ processModule(m, llStreamOpen(program), nil)
diff --git a/compiler/nimrod.nimrod.cfg b/compiler/nimrod.nimrod.cfg
index 657c47b28..cc27d9f36 100644
--- a/compiler/nimrod.nimrod.cfg
+++ b/compiler/nimrod.nimrod.cfg
@@ -17,4 +17,6 @@ import:testability
cincludes: "$lib/wrappers/libffi/common"
@end
+define:useStdoutAsStdmsg
+
cs:partial
diff --git a/compiler/options.nim b/compiler/options.nim
index 4f642e626..102ebc386 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -209,21 +209,42 @@ proc getGeneratedPath: string =
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
genSubDir
+template newPackageCache(): expr =
+ newStringTable(when FileSystemCaseSensitive:
+ modeCaseInsensitive
+ else:
+ modeCaseSensitive)
+
+var packageCache = newPackageCache()
+
+proc resetPackageCache*() = packageCache = newPackageCache()
+
+iterator myParentDirs(p: string): string =
+ # XXX os's parentDirs is stupid (multiple yields) and triggers an old bug...
+ var current = p
+ while true:
+ current = current.parentDir
+ if current.len == 0: break
+ yield current
+
proc getPackageName*(path: string): string =
- var q = 1
- var b = 0
- if path[len(path)-1] in {DirSep, AltSep}: q = 2
- for i in countdown(len(path)-q, 0):
- if path[i] in {DirSep, AltSep}:
- if b == 0: b = i
- else:
- let x = path.substr(i+1, b-1)
- case x.normalize
- of "lib", "src", "source", "package", "pckg", "library", "private":
- b = i
- else:
- return x.replace('.', '_')
- result = ""
+ var parents = 0
+ block packageSearch:
+ for d in myParentDirs(path):
+ if packageCache.hasKey(d):
+ #echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
+ return packageCache[d]
+ inc parents
+ for file in walkFiles(d / "*.babel"):
+ result = file.splitFile.name
+ break packageSearch
+ # we also store if we didn't find anything:
+ if result.isNil: result = ""
+ for d in myParentDirs(path):
+ #echo "set cache ", d, " |", result, "|", parents
+ packageCache[d] = result
+ dec parents
+ if parents <= 0: break
proc withPackageName*(path: string): string =
let x = path.getPackageName
diff --git a/compiler/parser.nim b/compiler/parser.nim
index ff3324b47..cfba89f4a 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -38,7 +38,6 @@ type
inSemiStmtList: int
proc parseAll*(p: var TParser): PNode
-proc openParser*(p: var TParser, filename: string, inputstream: PLLStream)
proc closeParser*(p: var TParser)
proc parseTopLevelStmt*(p: var TParser): PNode
# implements an iterator. Returns the next top-level statement or
@@ -50,7 +49,6 @@ proc parseString*(s: string, filename: string = "", line: int = 0): PNode
# correct error messages referring to the original source.
# helpers for the other parsers
-proc getPrecedence*(tok: TToken): int
proc isOperator*(tok: TToken): bool
proc getTok*(p: var TParser)
proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "")
@@ -77,14 +75,17 @@ proc parseCase(p: var TParser): PNode
proc getTok(p: var TParser) =
rawGetTok(p.lex, p.tok)
-proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream) =
+proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream,
+ strongSpaces=false) =
initToken(p.tok)
openLexer(p.lex, fileIdx, inputStream)
getTok(p) # read the first token
p.firstTok = true
+ p.strongSpaces = strongSpaces
-proc openParser*(p: var TParser, filename: string, inputStream: PLLStream) =
- openParser(p, filename.fileInfoIdx, inputstream)
+proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
+ strongSpaces=false) =
+ openParser(p, filename.fileInfoIdx, inputstream, strongSpaces)
proc closeParser(p: var TParser) =
closeLexer(p.lex)
@@ -193,34 +194,52 @@ proc isSigilLike(tok: TToken): bool {.inline.} =
proc isLeftAssociative(tok: TToken): bool {.inline.} =
result = tok.tokType != tkOpr or relevantOprChar(tok.ident) != '^'
-proc getPrecedence(tok: TToken): int =
+proc getPrecedence(tok: TToken, strongSpaces: bool): int =
+ template considerStrongSpaces(x): expr =
+ x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0)
+
case tok.tokType
of tkOpr:
let L = tok.ident.s.len
let relevantChar = relevantOprChar(tok.ident)
- template considerAsgn(value: expr) =
- result = if tok.ident.s[L-1] == '=': 1 else: value
+ template considerAsgn(value: expr) =
+ result = if tok.ident.s[L-1] == '=': 1 else: considerStrongSpaces(value)
case relevantChar
of '$', '^': considerAsgn(10)
of '*', '%', '/', '\\': considerAsgn(9)
- of '~': result = 8
+ of '~': result = considerStrongSpaces(8)
of '+', '-', '|': considerAsgn(8)
of '&': considerAsgn(7)
- of '=', '<', '>', '!': result = 5
+ of '=', '<', '>', '!': result = considerStrongSpaces(5)
of '.': considerAsgn(6)
- of '?': result = 2
+ of '?': result = considerStrongSpaces(2)
else: considerAsgn(2)
of tkDiv, tkMod, tkShl, tkShr: result = 9
of tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs: result = 5
- of tkDotDot: result = 6
+ of tkDotDot: result = considerStrongSpaces(6)
of tkAnd: result = 4
of tkOr, tkXor: result = 3
- else: result = - 10
-
-proc isOperator(tok: TToken): bool =
- result = getPrecedence(tok) >= 0
+ else: result = -10
+
+proc isOperator(tok: TToken): bool =
+ tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
+ tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
+
+proc isUnary(p: TParser): bool =
+ p.strongSpaces and p.tok.tokType in {tkOpr, tkDotDot} and
+ p.tok.strongSpaceB == 0 and
+ p.tok.strongSpaceA > 0
+
+proc checkBinary(p: TParser) {.inline.} =
+ # we don't check '..' here as that's too annoying
+ if p.strongSpaces and p.tok.tokType == tkOpr:
+ if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB:
+ parMessage(p, errGenerated, "number of spaces around '$#' not consistent"%
+ prettyTok(p.tok))
+ elif p.tok.strongSpaceA notin {0,1,2,4,8}:
+ parMessage(p, errGenerated, "number of spaces must be 0,1,2,4 or 8")
#| module = stmt ^* (';' / IND{=})
#|
@@ -281,7 +300,7 @@ proc parseSymbol(p: var TParser): PNode =
add(result, newIdentNodeP(getIdent"{}", p))
getTok(p)
eat(p, tkCurlyRi)
- of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDotDot:
+ of tokKeywordLow..tokKeywordHigh, tkSymbol, tkOpr, tkDot, tkDotDot:
add(result, newIdentNodeP(p.tok.ident, p))
getTok(p)
of tkIntLit..tkCharLit:
@@ -650,6 +669,7 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
while p.tok.indent < 0:
case p.tok.tokType
of tkParLe:
+ if p.strongSpaces and p.tok.strongSpaceA > 0: break
result = namedParams(p, result, nkCall, tkParRi)
if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
result.kind = nkObjConstr
@@ -664,8 +684,10 @@ proc primarySuffix(p: var TParser, r: PNode): PNode =
result = dotExpr(p, result)
result = parseGStrLit(p, result)
of tkBracketLe:
+ if p.strongSpaces and p.tok.strongSpaceA > 0: break
result = namedParams(p, result, nkBracketExpr, tkBracketRi)
of tkCurlyLe:
+ if p.strongSpaces and p.tok.strongSpaceA > 0: break
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast:
if p.inPragma == 0:
@@ -695,10 +717,11 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
result = primary(p, mode)
# expand while operators have priorities higher than 'limit'
- var opPrec = getPrecedence(p.tok)
+ var opPrec = getPrecedence(p.tok, p.strongSpaces)
let modeB = if mode == pmTypeDef: pmTypeDesc else: mode
# the operator itself must not start on a new line:
- while opPrec >= limit and p.tok.indent < 0:
+ while opPrec >= limit and p.tok.indent < 0 and not isUnary(p):
+ checkBinary(p)
var leftAssoc = ord(isLeftAssociative(p.tok))
var a = newNodeP(nkInfix, p)
var opNode = newIdentNodeP(p.tok.ident, p) # skip operator:
@@ -710,7 +733,7 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
addSon(a, result)
addSon(a, b)
result = a
- opPrec = getPrecedence(p.tok)
+ opPrec = getPrecedence(p.tok, p.strongSpaces)
proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
result = simpleExprAux(p, -1, mode)
@@ -1933,7 +1956,9 @@ proc parseString(s: string, filename: string = "", line: int = 0): PNode =
stream.lineOffset = line
var parser: TParser
- openParser(parser, filename, stream)
+ # XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
+ # spaces...
+ openParser(parser, filename, stream, false)
result = parser.parseAll
closeParser(parser)
diff --git a/compiler/pas2nim/paslex.nim b/compiler/pas2nim/paslex.nim
index 67473e71f..f24b0c420 100644
--- a/compiler/pas2nim/paslex.nim
+++ b/compiler/pas2nim/paslex.nim
@@ -342,7 +342,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
h = h +% ord(c)
h = h +% h shl 10
h = h xor (h shr 6)
- of '_': nil
+ of '_': discard
else: break
inc(pos)
h = h +% h shl 3
diff --git a/compiler/pas2nim/pasparse.nim b/compiler/pas2nim/pasparse.nim
index 928896338..a6f8363f6 100644
--- a/compiler/pas2nim/pasparse.nim
+++ b/compiler/pas2nim/pasparse.nim
@@ -335,7 +335,7 @@ proc exprColonEqExprList(p: var TParser, kind, elemKind: TNodeKind,
proc setBaseFlags(n: PNode, base: TNumericalBase) =
case base
- of base10: nil
+ of base10: discard
of base2: incl(n.flags, nfBase2)
of base8: incl(n.flags, nfBase8)
of base16: incl(n.flags, nfBase16)
@@ -466,7 +466,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
eat(p, pxCurlyDirRi)
opNode.ident = getIdent("&")
else:
- nil
+ discard
of pxMinus:
if p.tok.xkind == pxPer:
getTok(p)
@@ -477,7 +477,7 @@ proc lowestExprAux(p: var TParser, v: var PNode, limit: int): TTokKind =
of pxNeq:
opNode.ident = getIdent("!=")
else:
- nil
+ discard
skipCom(p, opNode) # read sub-expression with higher priority
nextop = lowestExprAux(p, v2, opPred)
addSon(node, opNode)
@@ -505,7 +505,7 @@ proc fixExpr(n: PNode): PNode =
(n.sons[2].kind in {nkCharLit, nkStrLit}):
n.sons[0].ident = getIdent("&") # fix operator
else:
- nil
+ discard
if not (n.kind in {nkEmpty..nkNilLit}):
for i in countup(0, sonsLen(n) - 1): result.sons[i] = fixExpr(n.sons[i])
@@ -603,7 +603,7 @@ proc parseStmtList(p: var TParser): PNode =
of pxCurlyDirLe, pxStarDirLe:
if not isHandledDirective(p): break
else:
- nil
+ discard
addSon(result, parseStmt(p))
if sonsLen(result) == 1: result = result.sons[0]
@@ -732,7 +732,7 @@ proc parseRepeat(p: var TParser): PNode =
addSon(b, c)
addSon(a, b)
if b.sons[0].kind == nkIdent and b.sons[0].ident.id == getIdent("false").id:
- nil
+ discard
else:
addSon(s, a)
addSon(result, s)
@@ -840,7 +840,7 @@ proc parseParam(p: var TParser): PNode =
getTok(p)
v = newNodeP(nkVarTy, p)
else:
- nil
+ discard
while true:
case p.tok.xkind
of pxSymbol: a = createIdentNodeP(p.tok.ident, p)
@@ -1133,7 +1133,7 @@ proc parseRecordPart(p: var TParser): PNode =
proc exSymbol(n: var PNode) =
case n.kind
of nkPostfix:
- nil
+ discard
of nkPragmaExpr:
exSymbol(n.sons[0])
of nkIdent, nkAccQuoted:
@@ -1154,7 +1154,7 @@ proc fixRecordDef(n: var PNode) =
for i in countup(0, sonsLen(n) - 1): fixRecordDef(n.sons[i])
of nkIdentDefs:
for i in countup(0, sonsLen(n) - 3): exSymbol(n.sons[i])
- of nkNilLit, nkEmpty: nil
+ of nkNilLit, nkEmpty: discard
else: internalError(n.info, "fixRecordDef(): " & $n.kind)
proc addPragmaToIdent(ident: var PNode, pragma: PNode) =
@@ -1191,7 +1191,7 @@ proc parseRecordBody(p: var TParser, result, definition: PNode) =
if definition != nil: addPragmaToIdent(definition.sons[0], parseCommand(p))
else: internalError(result.info, "anonymous record is not supported")
else:
- nil
+ discard
opt(p, pxSemicolon)
skipCom(p, result)
@@ -1399,7 +1399,7 @@ proc fixVarSection(p: var TParser, counter: PNode) =
proc exSymbols(n: PNode) =
case n.kind
- of nkEmpty..nkNilLit: nil
+ of nkEmpty..nkNilLit: discard
of nkProcDef..nkIteratorDef: exSymbol(n.sons[namePos])
of nkWhenStmt, nkStmtList:
for i in countup(0, sonsLen(n) - 1): exSymbols(n.sons[i])
@@ -1410,7 +1410,7 @@ proc exSymbols(n: PNode) =
exSymbol(n.sons[i].sons[0])
if n.sons[i].sons[2].kind == nkObjectTy:
fixRecordDef(n.sons[i].sons[2])
- else: nil
+ else: discard
proc parseBegin(p: var TParser, result: PNode) =
getTok(p)
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index d9ed50cfe..fba4e7657 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -23,7 +23,7 @@ const
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
- wNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
+ wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wOperator, wDelegator}
converterPragmas* = procPragmas
methodPragmas* = procPragmas
@@ -47,12 +47,12 @@ const
wInjectStmt}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
- wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wNoStackFrame,
+ wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
wRaises, wTags}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
- wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
+ wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
- wInheritable, wGensym, wInject, wRequiresInit}
+ wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
@@ -97,8 +97,21 @@ proc makeExternImport(s: PSym, extname: string) =
incl(s.flags, sfImportc)
excl(s.flags, sfForward)
-proc makeExternExport(s: PSym, extname: string) =
+proc validateExternCName(s: PSym, info: TLineInfo) =
+ ## Validates that the symbol name in s.loc.r is a valid C identifier.
+ ##
+ ## Valid identifiers are those alphanumeric including the underscore not
+ ## starting with a number. If the check fails, a generic error will be
+ ## displayed to the user.
+ let target = ropeToStr(s.loc.r)
+ if target.len < 1 or target[0] notin IdentStartChars or
+ not target.allCharsInSet(IdentChars):
+ localError(info, errGenerated, "invalid exported symbol")
+
+proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
setExternName(s, extname)
+ if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC}:
+ validateExternCName(s, info)
incl(s.flags, sfExportc)
proc processImportCompilerProc(s: PSym, extname: string) =
@@ -515,7 +528,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
if k in validPragmas:
case k
of wExportc:
- makeExternExport(sym, getOptionalStr(c, it, "$1"))
+ makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
incl(sym.flags, sfUsed) # avoid wrong hints
of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
of wImportCompilerProc:
@@ -548,9 +561,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
of wNodecl:
noVal(it)
incl(sym.loc.flags, lfNoDecl)
- of wPure, wNoStackFrame:
+ of wPure, wAsmNoStackFrame:
noVal(it)
- if sym != nil: incl(sym.flags, sfPure)
+ if sym != nil:
+ if k == wPure and sym.kind in routineKinds: invalidPragma(it)
+ else: incl(sym.flags, sfPure)
of wVolatile:
noVal(it)
incl(sym.flags, sfVolatile)
@@ -601,7 +616,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
processDynLib(c, it, sym)
of wCompilerproc:
noVal(it) # compilerproc may not get a string!
- makeExternExport(sym, "$1")
+ makeExternExport(sym, "$1", it.info)
incl(sym.flags, sfCompilerProc)
incl(sym.flags, sfUsed) # suppress all those stupid warnings
registerCompilerProc(sym)
@@ -699,6 +714,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
noVal(it)
if sym.typ == nil: invalidPragma(it)
else: incl(sym.typ.flags, tfIncompleteStruct)
+ of wUnchecked:
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
+ else: incl(sym.typ.flags, tfUncheckedArray)
+ of wUnion:
+ noVal(it)
+ if sym.typ == nil: invalidPragma(it)
+ else: incl(sym.typ.flags, tfUnion)
of wRequiresInit:
noVal(it)
if sym.typ == nil: invalidPragma(it)
@@ -753,6 +776,17 @@ proc implictPragmas*(c: PContext, sym: PSym, n: PNode,
addToLib(lib, sym)
if sym.loc.r == nil: sym.loc.r = toRope(sym.name.s)
+proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
+ if n == nil or n.sons == nil:
+ return false
+
+ for p in n.sons:
+ var key = if p.kind == nkExprColonExpr: p[0] else: p
+ if key.kind == nkIdent and whichKeyword(key.ident) == pragma:
+ return true
+
+ return false
+
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
if n == nil: return
for i in countup(0, sonsLen(n) - 1):
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index 1afb5961e..d68cb91c0 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -558,16 +558,19 @@ proc longMode(n: PNode, start: int = 0, theEnd: int = - 1): bool =
result = true
break
-proc gstmts(g: var TSrcGen, n: PNode, c: TContext) =
- if n.kind == nkEmpty: return
+proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) =
+ if n.kind == nkEmpty: return
if n.kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
- indentNL(g)
- for i in countup(0, sonsLen(n) - 1):
+ if doIndent: indentNL(g)
+ for i in countup(0, sonsLen(n) - 1):
optNL(g)
- gsub(g, n.sons[i])
+ if n.sons[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}:
+ gstmts(g, n.sons[i], c, doIndent=false)
+ else:
+ gsub(g, n.sons[i])
gcoms(g)
- dedent(g)
- else:
+ if doIndent: dedent(g)
+ else:
if rfLongMode in c.flags: indentNL(g)
gsub(g, n)
gcoms(g)
@@ -1268,7 +1271,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkBracketLe, "[")
gcomma(g, n)
put(g, tkBracketRi, "]")
- of nkMetaNode:
+ of nkMetaNode_Obsolete:
put(g, tkParLe, "(META|")
gsub(g, n.sons[0])
put(g, tkParRi, ")")
diff --git a/compiler/rodread.nim b/compiler/rodread.nim
index b53135a95..036e6cc3c 100644
--- a/compiler/rodread.nim
+++ b/compiler/rodread.nim
@@ -890,7 +890,7 @@ proc loadStub*(s: PSym) =
# deactivate the GC here because we do a deep recursion and generate no
# garbage when restoring parts of the object graph anyway.
- # Since we die with internal errors if this fails, so no try-finally is
+ # Since we die with internal errors if this fails, no try-finally is
# necessary.
GC_disable()
rawLoadStub(s)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 00ac79716..5ee46654e 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -120,7 +120,8 @@ proc commonType*(x, y: PType): PType =
if a.kind == tyObject and b.kind == tyObject:
result = commonSuperclass(a, b)
# this will trigger an error later:
- if result.isNil: return x
+ if result.isNil or result == a: return x
+ if result == b: return y
if k != tyNone:
let r = result
result = newType(k, r.owner)
@@ -219,7 +220,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
return nil
result = fixupTypeAfterEval(c, result, e)
- except:
+ except ERecoverableError:
return nil
proc semConstExpr(c: PContext, n: PNode): PNode =
@@ -332,6 +333,8 @@ proc myOpen(module: PSym): PPassContext =
c.semOperand = semOperand
c.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall
+ c.semInferredLambda = semInferredLambda
+ c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode
pushProcCon(c, module)
pushOwner(c.module)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 6b19dc359..2e5def75a 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -64,7 +64,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
errors[errors.len - 1].add("\n " & err)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
- if sym.kind == skIterator: inc(z.exactMatches, 200)
+ if sym.kind in skIterators: inc(z.exactMatches, 200)
case best.state
of csEmpty, csNoMatch: best = z
of csMatch:
@@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
# fail fast:
globalError(n.info, errTypeMismatch, "")
var result = msgKindToString(errTypeMismatch)
- add(result, describeArgs(c, n, 1 + ord(nfDelegate in n.flags)))
+ add(result, describeArgs(c, n, 1 + ord(nfDotField in n.flags)))
add(result, ')')
var candidates = ""
@@ -138,17 +138,35 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
let overloadsState = result.state
if overloadsState != csMatch:
- if nfDelegate in n.flags:
- internalAssert f.kind == nkIdent
- let calleeName = newStrNode(nkStrLit, f.ident.s)
- calleeName.info = n.info
+ if nfDotField in n.flags:
+ internalAssert f.kind == nkIdent and n.sonsLen >= 2
+ let calleeName = newStrNode(nkStrLit, f.ident.s).withInfo(n.info)
- let callOp = newIdentNode(idDelegator, n.info)
- n.sons[0..0] = [callOp, calleeName]
- orig.sons[0..0] = [callOp, calleeName]
-
- pickBest(callOp)
+ # leave the op head symbol empty,
+ # we are going to try multiple variants
+ n.sons[0..1] = [nil, n[1], calleeName]
+ orig.sons[0..1] = [nil, orig[1], calleeName]
+
+ template tryOp(x) =
+ let op = newIdentNode(getIdent(x), n.info)
+ n.sons[0] = op
+ orig.sons[0] = op
+ pickBest(op)
+
+ if nfExplicitCall in n.flags:
+ tryOp ".()"
+
+ if result.state in {csEmpty, csNoMatch}:
+ tryOp "."
+ elif nfDotSetter in n.flags:
+ internalAssert f.kind == nkIdent and n.sonsLen == 3
+ let calleeName = newStrNode(nkStrLit, f.ident.s[0.. -2]).withInfo(n.info)
+ let callOp = newIdentNode(getIdent".=", n.info)
+ n.sons[0..1] = [callOp, n[1], calleeName]
+ orig.sons[0..1] = [callOp, orig[1], calleeName]
+ pickBest(callOp)
+
if overloadsState == csEmpty and result.state == csEmpty:
localError(n.info, errUndeclaredIdentifier, considerAcc(f).s)
return
@@ -271,8 +289,9 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = newNodeI(a.kind, n.info)
for i in countup(0, len(a)-1):
var candidate = a.sons[i].sym
- if candidate.kind in {skProc, skMethod, skConverter, skIterator}:
- # if suffices that the candidate has the proper number of generic
+ if candidate.kind in {skProc, skMethod, skConverter,
+ skIterator, skClosureIterator}:
+ # it suffices that the candidate has the proper number of generic
# type parameters:
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
result.add(explicitGenericSym(c, n, candidate))
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index c9d95e1bf..d942aa41e 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -81,6 +81,9 @@ type
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode {.nimcall.}
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
+ semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
+ semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
+ info: TLineInfo): PSym
includedFiles*: TIntSet # used to detect recursive include files
userPragmas*: TStrTable
evalContext*: PEvalContext
@@ -211,7 +214,6 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType =
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = makeTypeDesc(c, typ)
- rawAddSon(typedesc, newTypeS(tyNone, c))
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
return newSymNode(sym, info)
@@ -272,7 +274,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
addSonSkipIntLit(result, intType) # basetype of range
proc markIndirect*(c: PContext, s: PSym) {.inline.} =
- if s.kind in {skProc, skConverter, skMethod, skIterator}:
+ if s.kind in {skProc, skConverter, skMethod, skIterator, skClosureIterator}:
incl(s.flags, sfAddrTaken)
# XXX add to 'c' for global analysis
diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim
index fb05826cb..791bef823 100644
--- a/compiler/semdestruct.nim
+++ b/compiler/semdestruct.nim
@@ -55,7 +55,9 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) =
useSym(destructableT.destructor),
n.sons[paramsPos][1][0]]))
-proc destroyField(c: PContext, field: PSym, holder: PNode): PNode =
+proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode
+
+proc destroySym(c: PContext, field: PSym, holder: PNode): PNode =
let destructableT = instantiateDestructor(c, field.typ)
if destructableT != nil:
result = newNode(nkCall, field.info, @[
@@ -70,56 +72,49 @@ proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode =
for i in countup(1, n.len - 1):
# of A, B:
var caseBranch = newNode(n[i].kind, n[i].info, n[i].sons[0 .. -2])
- let recList = n[i].lastSon
- var destroyRecList = newNode(nkStmtList, n[i].info, @[])
- template addField(f: expr): stmt =
- let stmt = destroyField(c, f, holder)
- if stmt != nil:
- destroyRecList.addSon(stmt)
- inc nonTrivialFields
-
- case recList.kind
- of nkSym:
- addField(recList.sym)
- of nkRecList:
- for j in countup(0, recList.len - 1):
- addField(recList[j].sym)
+
+ let stmt = destroyFieldOrFields(c, n[i].lastSon, holder)
+ if stmt == nil:
+ caseBranch.addSon(newNode(nkStmtList, n[i].info, @[]))
else:
- internalAssert false
-
- caseBranch.addSon(destroyRecList)
+ caseBranch.addSon(stmt)
+ nonTrivialFields += stmt.len
+
result.addSon(caseBranch)
+
# maybe no fields were destroyed?
if nonTrivialFields == 0:
result = nil
-
+
+proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode =
+ template maybeAddLine(e: expr): stmt =
+ let stmt = e
+ if stmt != nil:
+ if result == nil: result = newNode(nkStmtList)
+ result.addSon(stmt)
+
+ case field.kind
+ of nkRecCase:
+ maybeAddLine destroyCase(c, field, holder)
+ of nkSym:
+ maybeAddLine destroySym(c, field.sym, holder)
+ of nkRecList:
+ for son in field:
+ maybeAddLine destroyFieldOrFields(c, son, holder)
+ else:
+ internalAssert false
+
proc generateDestructor(c: PContext, t: PType): PNode =
## generate a destructor for a user-defined object or tuple type
## returns nil if the destructor turns out to be trivial
- template addLine(e: expr): stmt =
- if result == nil: result = newNode(nkStmtList)
- result.addSon(e)
-
# XXX: This may be true for some C-imported types such as
# Tposix_spawnattr
if t.n == nil or t.n.sons == nil: return
internalAssert t.n.kind == nkRecList
let destructedObj = newIdentNode(destructorParam, unknownLineInfo())
# call the destructods of all fields
- for s in countup(0, t.n.sons.len - 1):
- case t.n.sons[s].kind
- of nkRecCase:
- let stmt = destroyCase(c, t.n.sons[s], destructedObj)
- if stmt != nil: addLine(stmt)
- of nkSym:
- let stmt = destroyField(c, t.n.sons[s].sym, destructedObj)
- if stmt != nil: addLine(stmt)
- else:
- # XXX just skip it for now so that the compiler doesn't crash, but
- # please zahary fix it! arbitrary nesting of nkRecList/nkRecCase is
- # possible. Any thread example seems to trigger this.
- discard
+ result = destroyFieldOrFields(c, t.n, destructedObj)
# base classes' destructors will be automatically called by
# semProcAux for both auto-generated and user-defined destructors
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index a8a16672d..203a51816 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -204,7 +204,14 @@ proc semConv(c: PContext, n: PNode): PNode =
if not isSymChoice(op):
let status = checkConvertible(c, result.typ, op.typ)
case status
- of convOK: discard
+ of convOK:
+ # handle SomeProcType(SomeGenericProc)
+ # XXX: This needs fixing. checkConvertible uses typeRel internally, but
+ # doesn't bother to perform the work done in paramTypeMatchAux/fitNode
+ # so we are redoing the typeRel work here. Why does semConv exist as a
+ # separate proc from fitNode?
+ if op.kind == nkSym and op.sym.isGenericRoutine:
+ result.sons[1] = fitNode(c, result.typ, result.sons[1])
of convNotNeedeed:
message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
of convNotLegal:
@@ -316,11 +323,6 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
t.callConv == ccClosure and
tfIterator notin t.flags))
- of "iterator":
- let t = skipTypes(t1, abstractRange)
- result = newIntNode(nkIntLit, ord(t.kind == tyProc and
- t.callConv == ccClosure and
- tfIterator in t.flags))
else:
var t2 = n[2].typ.skipTypes({tyTypeDesc})
let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info),
@@ -339,22 +341,21 @@ proc semIs(c: PContext, n: PNode): PNode =
result = n
n.typ = getSysType(tyBool)
-
- n.sons[1] = semExprWithType(c, n[1], {efDetermineType})
-
+
+ n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator})
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
let t2 = semTypeNode(c, n[2], nil)
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
- if n[1].typ.kind != tyTypeDesc:
- n.sons[1] = makeTypeSymNode(c, n[1].typ, n[1].info)
- elif n[1].typ.sonsLen == 0:
+ let lhsType = n[1].typ
+ if lhsType.kind != tyTypeDesc:
+ n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info)
+ elif lhsType.base.kind == tyNone:
# this is a typedesc variable, leave for evals
return
- let t1 = n[1].typ.sons[0]
# BUGFIX: don't evaluate this too early: ``T is void``
- if not containsGenericType(t1): result = isOpImpl(c, n)
+ if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n)
proc semOpAux(c: PContext, n: PNode) =
const flags = {efDetermineType}
@@ -635,9 +636,11 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
result = evalStaticExpr(c.module, call, c.p.owner)
if result.isNil:
localError(n.info, errCannotInterpretNodeX, renderTree(call))
+ else: result = fixupTypeAfterEval(c, result, n)
else:
result = evalConstExpr(c.module, call)
if result.isNil: result = n
+ else: result = fixupTypeAfterEval(c, result, n)
#if result != n:
# echo "SUCCESS evaluated at compile time: ", call.renderTree
@@ -647,16 +650,18 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
if result.isNil:
localError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode
+ else:
+ result = fixupTypeAfterEval(c, result, a)
proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags): PNode =
if flags*{efInTypeof, efWantIterator} != {}:
# consider: 'for x in pReturningArray()' --> we don't want the restriction
- # to 'skIterator' anymore; skIterator is preferred in sigmatch already for
- # typeof support.
+ # to 'skIterators' anymore; skIterators are preferred in sigmatch already
+ # for typeof support.
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
result = semOverloadedCall(c, n, nOrig,
- {skProc, skMethod, skConverter, skMacro, skTemplate, skIterator})
+ {skProc, skMethod, skConverter, skMacro, skTemplate}+skIterators)
else:
result = semOverloadedCall(c, n, nOrig,
{skProc, skMethod, skConverter, skMacro, skTemplate})
@@ -669,27 +674,28 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
case callee.kind
of skMacro, skTemplate: discard
else:
- if (callee.kind == skIterator) and (callee.id == c.p.owner.id):
+ if (callee.kind in skIterators) and (callee.id == c.p.owner.id):
localError(n.info, errRecursiveDependencyX, callee.name.s)
if sfNoSideEffect notin callee.flags:
if {sfImportc, sfSideEffect} * callee.flags != {}:
incl(c.p.owner.flags, sfSideEffect)
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode
-proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = nil
checkMinSonsLen(n, 1)
var prc = n.sons[0]
- if n.sons[0].kind == nkDotExpr:
+ if n.sons[0].kind == nkDotExpr:
checkSonsLen(n.sons[0], 2)
n.sons[0] = semFieldAccess(c, n.sons[0])
- if n.sons[0].kind == nkDotCall:
+ if n.sons[0].kind == nkDotCall:
# it is a static call!
result = n.sons[0]
result.kind = nkCall
+ result.flags.incl nfExplicitCall
for i in countup(1, sonsLen(n) - 1): addSon(result, n.sons[i])
return semExpr(c, result, flags)
- else:
+ else:
n.sons[0] = semExpr(c, n.sons[0])
let nOrig = n.copyTree
semOpAux(c, n)
@@ -910,8 +916,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
var ty = n.sons[0].typ
var f: PSym = nil
result = nil
- if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.len == 1:
- if ty.kind == tyTypeDesc: ty = ty.sons[0]
+ if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.base.kind != tyNone:
+ if ty.kind == tyTypeDesc: ty = ty.base
case ty.kind
of tyEnum:
# look up if the identifier belongs to the enum:
@@ -992,7 +998,7 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
else:
var i = considerAcc(n.sons[1])
result = newNodeI(nkDotCall, n.info)
- result.flags.incl nfDelegate
+ result.flags.incl nfDotField
addSon(result, newIdentNode(i, n[1].info))
addSon(result, copyTree(n[0]))
@@ -1075,12 +1081,13 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerAcc(a[1])
- let setterId = newIdentNode(getIdent(id.s & '='), n.info)
+ var setterId = newIdentNode(getIdent(id.s & '='), n.info)
# a[0] is already checked for semantics, that does ``builtinFieldAccess``
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
# nodes?
let aOrig = nOrig[0]
result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])])
+ result.flags.incl nfDotSetter
let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
result = semOverloadedCallAnalyseEffects(c, result, orig, {})
@@ -1181,7 +1188,7 @@ proc semReturn(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
if c.p.owner.kind in {skConverter, skMethod, skProc, skMacro} or
- (c.p.owner.kind == skIterator and c.p.owner.typ.callConv == ccClosure):
+ c.p.owner.kind == skClosureIterator:
if n.sons[0].kind != nkEmpty:
# transform ``return expr`` to ``result = expr; return``
if c.p.resultSym != nil:
@@ -1246,7 +1253,7 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
proc semYield(c: PContext, n: PNode): PNode =
result = n
checkSonsLen(n, 1)
- if c.p.owner == nil or c.p.owner.kind != skIterator:
+ if c.p.owner == nil or c.p.owner.kind notin skIterators:
localError(n.info, errYieldNotAllowedHere)
elif c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline:
localError(n.info, errYieldNotAllowedInTryStmt)
@@ -1254,9 +1261,11 @@ proc semYield(c: PContext, n: PNode): PNode =
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
var restype = c.p.owner.typ.sons[0]
if restype != nil:
- n.sons[0] = fitNode(c, restype, n.sons[0])
+ let adjustedRes = if c.p.owner.kind == skIterator: restype.base
+ else: restype
+ n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
if n.sons[0].typ == nil: internalError(n.info, "semYield")
- semYieldVarResult(c, n, restype)
+ semYieldVarResult(c, n, adjustedRes)
else:
localError(n.info, errCannotReturnExpr)
elif c.p.owner.typ.sons[0] != nil:
@@ -1770,22 +1779,6 @@ proc semBlock(c: PContext, n: PNode): PNode =
closeScope(c)
dec(c.p.nestedBlockCounter)
-proc buildCall(n: PNode): PNode =
- if n.kind == nkDotExpr and n.len == 2:
- # x.y --> y(x)
- result = newNodeI(nkCall, n.info, 2)
- result.sons[0] = n.sons[1]
- result.sons[1] = n.sons[0]
- elif n.kind in nkCallKinds and n.sons[0].kind == nkDotExpr:
- # x.y(a) -> y(x, a)
- let a = n.sons[0]
- result = newNodeI(nkCall, n.info, n.len+1)
- result.sons[0] = a.sons[1]
- result.sons[1] = a.sons[0]
- for i in 1 .. <n.len: result.sons[i+1] = n.sons[i]
- else:
- result = n
-
proc doBlockIsStmtList(n: PNode): bool =
result = n.kind == nkDo and
n[paramsPos].sonsLen == 1 and
@@ -1832,13 +1825,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
var s = lookUp(c, n)
semCaptureSym(s, c.p.owner)
result = semSym(c, n, s, flags)
- if s.kind in {skProc, skMethod, skIterator, skConverter}:
+ if s.kind in {skProc, skMethod, skConverter}+skIterators:
#performProcvarCheck(c, n, s)
result = symChoice(c, n, s, scClosed)
if result.kind == nkSym:
markIndirect(c, result.sym)
- if isGenericRoutine(result.sym):
- localError(n.info, errInstantiateXExplicitely, s.name.s)
+ # if isGenericRoutine(result.sym):
+ # localError(n.info, errInstantiateXExplicitely, s.name.s)
of nkSym:
# because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again!
@@ -1888,13 +1881,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
message(n.info, warnDeprecated, "bind")
result = semExpr(c, n.sons[0], flags)
of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy:
- var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
+ var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter})
result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info)
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1)
- let mode = if nfDelegate in n.flags: {} else: {checkUndeclared}
+ let mode = if nfDotField in n.flags: {} else: {checkUndeclared}
var s = qualifiedLookUp(c, n.sons[0], mode)
if s != nil:
if gCmd == cmdPretty and n.sons[0].kind == nkDotExpr:
@@ -1922,7 +1915,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
localError(n.info, errUseQualifier, s.name.s)
elif s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags)
- of skProc, skMethod, skConverter, skIterator:
+ of skProc, skMethod, skConverter, skIterators:
if s.magic == mNone: result = semDirectOp(c, n, flags)
else: result = semMagic(c, n, s, flags)
else:
@@ -1933,7 +1926,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
# the 'newSeq[T](x)' bug
setGenericParams(c, n.sons[0])
result = semDirectOp(c, n, flags)
- elif isSymChoice(n.sons[0]) or nfDelegate in n.flags:
+ elif isSymChoice(n.sons[0]) or nfDotField in n.flags:
result = semDirectOp(c, n, flags)
else:
result = semIndirectOp(c, n, flags)
@@ -1946,7 +1939,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkBracketExpr:
checkMinSonsLen(n, 1)
var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared})
- if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}:
+ if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators:
# type parameters: partial generic specialization
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
result = explicitGenericInstantiation(c, n, s)
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 4740ddcb3..925a80832 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -537,7 +537,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode =
if result.kind == nkExprColonExpr: result = result.sons[1]
else:
localError(n.info, errIndexOutOfBounds)
- of nkBracket, nkMetaNode:
+ of nkBracket:
if (idx >= 0) and (idx < sonsLen(x)): result = x.sons[int(idx)]
else: localError(n.info, errIndexOutOfBounds)
of nkStrLit..nkTripleStrLit:
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index b21d851c9..ffc1a43b2 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -42,7 +42,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
of skUnknown:
# Introduced in this pass! Leave it as an identifier.
result = n
- of skProc, skMethod, skIterator, skConverter:
+ of skProc, skMethod, skIterators, skConverter:
result = symChoice(c, n, s, scOpen)
of skTemplate:
if macroToExpand(s):
@@ -141,7 +141,7 @@ proc semGenericStmt(c: PContext, n: PNode,
# symbol lookup ...
of skUnknown, skParam:
# Leave it as an identifier.
- of skProc, skMethod, skIterator, skConverter:
+ of skProc, skMethod, skIterators, skConverter:
result.sons[0] = symChoice(c, n.sons[0], s, scOption)
first = 1
of skGenericParam:
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index fb266ae3a..a00325277 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -84,10 +84,10 @@ proc initVar(a: PEffects, n: PNode) =
proc initVarViaNew(a: PEffects, n: PNode) =
if n.kind != nkSym: return
let s = n.sym
- if {tfNeedsInit, tfNotNil} * s.typ.flags == {tfNotNil}:
+ if {tfNeedsInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
# 'x' is not nil, but that doesn't mean it's not nil children
# are initialized:
- initVarViaNew(a, n)
+ initVar(a, n)
proc useVar(a: PEffects, n: PNode) =
let s = n.sym
@@ -466,8 +466,7 @@ proc track(tracked: PEffects, n: PNode) =
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
mergeTags(tracked, effectList.sons[tagEffects], n)
for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i))
- if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize,
- mNewSeq, mShallowCopy}:
+ if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}:
# may not look like an assignment, but it is:
initVarViaNew(tracked, n.sons[1])
for i in 0 .. <safeLen(n):
@@ -477,7 +476,6 @@ proc track(tracked: PEffects, n: PNode) =
if warnProveField in gNotes: checkFieldAccess(tracked.guards, n)
of nkTryStmt: trackTryStmt(tracked, n)
of nkPragma: trackPragmaStmt(tracked, n)
- of nkMacroDef, nkTemplateDef: discard
of nkAsgn, nkFastAsgn:
track(tracked, n.sons[1])
initVar(tracked, n.sons[0])
@@ -527,7 +525,9 @@ proc track(tracked: PEffects, n: PNode) =
if sfDiscriminant in x.sons[0].sym.flags:
addDiscriminantFact(tracked.guards, x)
setLen(tracked.guards, oldFacts)
- of nkTypeSection: discard
+ of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
+ nkMacroDef, nkTemplateDef:
+ discard
else:
for i in 0 .. <safeLen(n): track(tracked, n.sons[i])
@@ -581,22 +581,26 @@ proc setEffectsForProcType*(t: PType, n: PNode) =
if not isNil(tagsSpec):
effects.sons[tagEffects] = tagsSpec
+proc initEffects(effects: PNode; s: PSym; t: var TEffects) =
+ newSeq(effects.sons, effectListLen)
+ effects.sons[exceptionEffects] = newNodeI(nkArgList, s.info)
+ effects.sons[tagEffects] = newNodeI(nkArgList, s.info)
+
+ t.exc = effects.sons[exceptionEffects]
+ t.tags = effects.sons[tagEffects]
+ t.owner = s
+ t.init = @[]
+ t.guards = @[]
+
proc trackProc*(s: PSym, body: PNode) =
var effects = s.typ.n.sons[0]
internalAssert effects.kind == nkEffectList
# effects already computed?
if sfForward in s.flags: return
if effects.len == effectListLen: return
- newSeq(effects.sons, effectListLen)
- effects.sons[exceptionEffects] = newNodeI(nkArgList, body.info)
- effects.sons[tagEffects] = newNodeI(nkArgList, body.info)
var t: TEffects
- t.exc = effects.sons[exceptionEffects]
- t.tags = effects.sons[tagEffects]
- t.owner = s
- t.init = @[]
- t.guards = @[]
+ initEffects(effects, s, t)
track(t, body)
if not isEmptyType(s.typ.sons[0]) and tfNeedsInit in s.typ.sons[0].flags and
@@ -619,3 +623,12 @@ proc trackProc*(s: PSym, body: PNode) =
# after the check, use the formal spec:
effects.sons[tagEffects] = tagsSpec
+proc trackTopLevelStmt*(module: PSym; n: PNode) =
+ if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef,
+ nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
+ return
+ var effects = newNode(nkEffectList, n.info)
+ var t: TEffects
+ initEffects(effects, module, t)
+
+ track(t, n)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 0871b7fb7..a11386966 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -76,8 +76,8 @@ proc performProcvarCheck(c: PContext, n: PNode, s: PSym) =
proc semProcvarCheck(c: PContext, n: PNode) =
let n = n.skipConv
- if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skIterator,
- skConverter}:
+ if n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
+ skIterator, skClosureIterator}:
performProcvarCheck(c, n, n.sym)
proc semProc(c: PContext, n: PNode): PNode
@@ -126,7 +126,7 @@ proc implicitlyDiscardable(n: PNode): bool =
proc fixNilType(n: PNode) =
if isAtom(n):
if n.kind != nkNilLit and n.typ != nil:
- localError(n.info, errDiscardValue)
+ localError(n.info, errDiscardValueX, n.typ.typeToString)
elif n.kind in {nkStmtList, nkStmtListExpr}:
n.kind = nkStmtList
for it in n: fixNilType(it)
@@ -143,10 +143,11 @@ proc discardCheck(c: PContext, result: PNode) =
while n.kind in skipForDiscardable:
n = n.lastSon
n.typ = nil
- elif c.inTypeClass > 0 and result.typ.kind == tyBool:
- let verdict = semConstExpr(c, result)
- if verdict.intVal == 0:
- localError(result.info, "type class predicate failed")
+ elif c.inTypeClass > 0:
+ if result.typ.kind == tyBool:
+ let verdict = semConstExpr(c, result)
+ if verdict.intVal == 0:
+ localError(result.info, "type class predicate failed")
elif result.typ.kind != tyError and gCmd != cmdInteractive:
if result.typ.kind == tyNil:
fixNilType(result)
@@ -154,7 +155,7 @@ proc discardCheck(c: PContext, result: PNode) =
else:
var n = result
while n.kind in skipForDiscardable: n = n.lastSon
- localError(n.info, errDiscardValue)
+ localError(n.info, errDiscardValueX, result.typ.typeToString)
proc semIf(c: PContext, n: PNode): PNode =
result = n
@@ -331,6 +332,7 @@ proc checkNilable(v: PSym) =
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode
result = copyNode(n)
+ var hasCompileTime = false
for i in countup(0, sonsLen(n)-1):
var a = n.sons[i]
if gCmd == cmdIdeTools: suggestStmt(c, a)
@@ -349,7 +351,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# BUGFIX: ``fitNode`` is needed here!
# check type compability between def.typ and typ:
if typ != nil: def = fitNode(c, typ, def)
- else: typ = skipIntLit(def.typ)
+ else:
+ typ = skipIntLit(def.typ)
+ if typ.kind in {tySequence, tyArray, tySet} and
+ typ.lastSon.kind == tyEmpty:
+ localError(def.info, errCannotInferTypeOfTheLiteral,
+ ($typ.kind).substr(2).toLower)
else:
def = ast.emptyNode
if symkind == skLet: localError(a.info, errLetNeedsInit)
@@ -405,7 +412,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
v.typ = tup.sons[j]
b.sons[j] = newSymNode(v)
checkNilable(v)
-
+ if sfCompileTime in v.flags: hasCompileTime = true
+ if hasCompileTime: vm.setupCompileTimeVar(c.module, result)
+
proc semConst(c: PContext, n: PNode): PNode =
result = copyNode(n)
for i in countup(0, sonsLen(n) - 1):
@@ -607,7 +616,8 @@ proc symForVar(c: PContext, n: PNode): PSym =
proc semForVars(c: PContext, n: PNode): PNode =
result = n
var length = sonsLen(n)
- var iter = skipTypes(n.sons[length-2].typ, {tyGenericInst})
+ let iterBase = n.sons[length-2].typ.skipTypes({tyIter})
+ var iter = skipTypes(iterBase, {tyGenericInst})
# length == 3 means that there is one for loop variable
# and thus no tuple unpacking:
if iter.kind != tyTuple or length == 3:
@@ -617,7 +627,7 @@ proc semForVars(c: PContext, n: PNode): PNode =
# BUGFIX: don't use `iter` here as that would strip away
# the ``tyGenericInst``! See ``tests/compile/tgeneric.nim``
# for an example:
- v.typ = n.sons[length-2].typ
+ v.typ = iterBase
n.sons[0] = newSymNode(v)
if sfGenSym notin v.flags: addForVarDecl(c, v)
else:
@@ -655,7 +665,7 @@ proc semFor(c: PContext, n: PNode): PNode =
# first class iterator:
result = semForVars(c, n)
elif call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
- call.sons[0].sym.kind != skIterator:
+ call.sons[0].sym.kind notin skIterators:
if length == 3:
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
elif length == 4:
@@ -764,6 +774,29 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
s.ast = a
popOwner()
+proc checkForMetaFields(n: PNode) =
+ template checkMeta(t) =
+ if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
+ localError(n.info, errTIsNotAConcreteType, t.typeToString)
+
+ case n.kind
+ of nkRecList, nkRecCase:
+ for s in n: checkForMetaFields(s)
+ of nkOfBranch, nkElse:
+ checkForMetaFields(n.lastSon)
+ of nkSym:
+ let t = n.sym.typ
+ case t.kind
+ of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyPtr, tyRef,
+ tyProc, tyGenericInvokation, tyGenericInst:
+ let start = ord(t.kind in {tyGenericInvokation, tyGenericInst})
+ for i in start .. <t.sons.len:
+ checkMeta(t.sons[i])
+ else:
+ checkMeta(t)
+ else:
+ internalAssert false
+
proc typeSectionFinalPass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
@@ -780,6 +813,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
assignType(s.typ, t)
s.typ.id = t.id # same id
checkConstructedType(s.info, s.typ)
+ if s.typ.kind in {tyObject, tyTuple}:
+ checkForMetaFields(s.typ.n)
let aa = a.sons[2]
if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
aa.sons[0].kind == nkObjectTy:
@@ -883,12 +918,19 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
s = n[namePos].sym
pushOwner(s)
openScope(c)
- if n.sons[genericParamsPos].kind != nkEmpty:
- illFormedAst(n) # process parameters:
+ var gp: PNode
+ if n.sons[genericParamsPos].kind != nkEmpty:
+ n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos])
+ gp = n.sons[genericParamsPos]
+ else:
+ gp = newNodeI(nkGenericParams, n.info)
+
if n.sons[paramsPos].kind != nkEmpty:
- var gp = newNodeI(nkGenericParams, n.info)
semParamList(c, n.sons[paramsPos], gp, s)
- paramsTypeCheck(c, s.typ)
+ # paramsTypeCheck(c, s.typ)
+ if sonsLen(gp) > 0 and n.sons[genericParamsPos].kind == nkEmpty:
+ # we have a list of implicit type parameters:
+ n.sons[genericParamsPos] = gp
else:
s.typ = newTypeS(tyProc, c)
rawAddSon(s.typ, nil)
@@ -900,12 +942,13 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
localError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s)
#if efDetermineType notin flags:
# XXX not good enough; see tnamedparamanonproc.nim
- pushProcCon(c, s)
- addResult(c, s.typ.sons[0], n.info, skProc)
- let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
- n.sons[bodyPos] = transformBody(c.module, semBody, s)
- addResultNode(c, n)
- popProcCon(c)
+ if n.sons[genericParamsPos].kind == nkEmpty:
+ pushProcCon(c, s)
+ addResult(c, s.typ.sons[0], n.info, skProc)
+ let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
+ n.sons[bodyPos] = transformBody(c.module, semBody, s)
+ addResultNode(c, n)
+ popProcCon(c)
sideEffectsCheck(c, s)
else:
localError(n.info, errImplOfXexpected, s.name.s)
@@ -913,6 +956,34 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
popOwner()
result.typ = s.typ
+proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
+ var n = n
+
+ n = replaceTypesInBody(c, pt, n)
+ result = n
+
+ n.sons[genericParamsPos] = emptyNode
+ n.sons[paramsPos] = n.typ.n
+
+ openScope(c)
+ var s = n.sons[namePos].sym
+ addParams(c, n.typ.n, skProc)
+ pushProcCon(c, s)
+ addResult(c, n.typ.sons[0], n.info, skProc)
+ let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos]))
+ n.sons[bodyPos] = transformBody(c.module, semBody, n.sons[namePos].sym)
+ addResultNode(c, n)
+ popProcCon(c)
+ closeScope(c)
+
+ s.ast = result
+
+ # alternative variant (not quite working):
+ # var prc = arg[0].sym
+ # let inferred = c.semGenerateInstance(c, prc, m.bindings, arg.info)
+ # result = inferred.ast
+ # result.kind = arg.kind
+
proc activate(c: PContext, n: PNode) =
# XXX: This proc is part of my plan for getting rid of
# forward declarations. stay tuned.
@@ -927,8 +998,7 @@ proc activate(c: PContext, n: PNode) =
discard
proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
- if s.typ.sons[0] != nil and
- (s.kind != skIterator or s.typ.callConv == ccClosure):
+ if s.typ.sons[0] != nil and s.kind != skIterator:
addResult(c, s.typ.sons[0], n.info, s.kind)
addResultNode(c, n)
@@ -1003,12 +1073,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
rawAddSon(s.typ, nil)
if n.sons[patternPos].kind != nkEmpty:
n.sons[patternPos] = semPattern(c, n.sons[patternPos])
- if s.kind == skIterator:
+ if s.kind in skIterators:
s.typ.flags.incl(tfIterator)
var proto = searchForProc(c, s.scope, s)
- if proto == nil:
- if s.kind == skIterator and isAnon: s.typ.callConv = ccClosure
+ if proto == nil:
+ if s.kind == skClosureIterator: s.typ.callConv = ccClosure
else: s.typ.callConv = lastOptionEntry(c).defaultCC
# add it here, so that recursive procs are possible:
if sfGenSym in s.flags: discard
@@ -1068,7 +1138,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
n.sons[bodyPos] = transformBody(c.module, semBody, s)
popProcCon(c)
else:
- if s.typ.sons[0] != nil and kind != skIterator:
+ if s.typ.sons[0] != nil and kind notin skIterators:
addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info))
var toBind = initIntSet()
n.sons[bodyPos] = semGenericStmtScope(c, n.sons[bodyPos], {}, toBind)
@@ -1095,7 +1165,10 @@ proc determineType(c: PContext, s: PSym) =
discard semProcAux(c, s.ast, s.kind, {}, stepDetermineType)
proc semIterator(c: PContext, n: PNode): PNode =
- result = semProcAux(c, n, skIterator, iteratorPragmas)
+ let kind = if hasPragma(n[pragmasPos], wClosure) or
+ n[namePos].kind == nkEmpty: skClosureIterator
+ else: skIterator
+ result = semProcAux(c, n, kind, iteratorPragmas)
var s = result.sons[namePos].sym
var t = s.typ
if t.sons[0] == nil and s.typ.callConv != ccClosure:
@@ -1250,7 +1323,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true
n.typ = enforceVoidContext
- if i == last and efWantValue in flags:
+ if i == last and (length == 1 or efWantValue in flags):
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext or c.inTypeClass > 0:
@@ -1263,8 +1336,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
let (outer, inner) = insertDestructors(c, n.sons[i])
if outer != nil:
n.sons[i] = outer
- for j in countup(i+1, length-1):
- inner.addSon(semStmt(c, n.sons[j]))
+ var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1])
+ inner.addSon(semStmtList(c, rest, flags))
n.sons.setLen(i+1)
return
of LastBlockStmts:
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 5abc3ef33..31624a97f 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -171,7 +171,7 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode =
of skUnknown:
# Introduced in this pass! Leave it as an identifier.
result = n
- of skProc, skMethod, skIterator, skConverter, skTemplate, skMacro:
+ of OverloadableSyms:
result = symChoice(c, n, s, scOpen)
of skGenericParam:
result = newSymNodeTypeDesc(s, n.info)
@@ -348,7 +348,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
of nkMethodDef:
result = semRoutineInTemplBody(c, n, skMethod)
of nkIteratorDef:
- result = semRoutineInTemplBody(c, n, skIterator)
+ result = semRoutineInTemplBody(c, n, n[namePos].sym.kind)
of nkTemplateDef:
result = semRoutineInTemplBody(c, n, skTemplate)
of nkMacroDef:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 44e414e9c..a619de7ff 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -653,6 +653,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name
else: nil
+ template maybeLift(typ: PType): expr =
+ let lifted = liftingWalk(typ)
+ (if lifted != nil: lifted else: typ)
+
template addImplicitGeneric(e: expr): expr =
addImplicitGenericImpl(e, paramTypId)
@@ -663,15 +667,19 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
of tyStatic:
# proc(a: expr{string}, b: expr{nkLambda})
# overload on compile time values and AST trees
- result = addImplicitGeneric(c.newTypeWithSons(tyStatic, paramType.sons))
+ let base = paramType.base.maybeLift
+ if base.isMetaType and procKind == skMacro:
+ localError(info, errMacroBodyDependsOnGenericTypes, paramName)
+ result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base]))
result.flags.incl tfHasStatic
of tyTypeDesc:
if tfUnresolved notin paramType.flags:
# naked typedescs are not bindOnce types
- if paramType.sonsLen == 0 and paramTypId != nil and
+ if paramType.base.kind == tyNone and paramTypId != nil and
paramTypId.id == typedescId.id: paramTypId = nil
- result = addImplicitGeneric(c.newTypeWithSons(tyTypeDesc, paramType.sons))
+ result = addImplicitGeneric(
+ c.newTypeWithSons(tyTypeDesc, @[paramType.base]))
of tyDistinct:
if paramType.sonsLen == 1:
@@ -703,9 +711,14 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
for i in 0 .. paramType.sonsLen - 2:
result.rawAddSon newTypeS(tyAnything, c)
# result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
+
+ if paramType.lastSon.kind == tyUserTypeClass:
+ result.kind = tyUserTypeClassInst
+ result.rawAddSon paramType.lastSon
+ return addImplicitGeneric(result)
+
result = instGenericContainer(c, paramType.sym.info, result,
allowMetaTypes = true)
- result.lastSon.shouldHaveMeta
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
result = addImplicitGeneric(result)
@@ -838,8 +851,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
n.sons[0].info)
if lifted != nil: r = lifted
r.flags.incl tfRetType
- result.sons[0] = skipIntLit(r)
- res.typ = result.sons[0]
+ r = skipIntLit(r)
+ if kind == skIterator: r = newTypeWithSons(c, tyIter, @[r])
+ result.sons[0] = r
+ res.typ = r
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
checkMinSonsLen(n, 1)
@@ -944,6 +959,23 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
let typ = semTypeNode(c, n, nil)
result.sons.safeAdd(typ)
+proc semProcTypeWithScope(c: PContext, n: PNode,
+ prev: PType, kind: TSymKind): PType =
+ checkSonsLen(n, 2)
+ openScope(c)
+ result = semProcTypeNode(c, n.sons[0], nil, prev, kind)
+ # dummy symbol for `pragma`:
+ var s = newSymS(kind, newIdentNode(getIdent("dummy"), n.info), c)
+ s.typ = result
+ if n.sons[1].kind == nkEmpty or n.sons[1].len == 0:
+ if result.callConv == ccDefault:
+ result.callConv = ccClosure
+ #Message(n.info, warnImplicitClosure, renderTree(n))
+ else:
+ pragma(c, s, n.sons[1], procTypePragmas)
+ when useEffectSystem: setEffectsForProcType(result, n.sons[1])
+ closeScope(c)
+
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = nil
if gCmd == cmdIdeTools: suggestExpr(c, n)
@@ -952,7 +984,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkTypeOfExpr:
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
checkSonsLen(n, 1)
- result = semExprWithType(c, n.sons[0], {efInTypeof}).typ
+ result = semExprWithType(c, n.sons[0], {efInTypeof}).typ.skipTypes({tyIter})
of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else:
@@ -1011,7 +1043,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of mOrdinal: result = semOrdinal(c, n, prev)
of mSeq: result = semContainer(c, n, tySequence, "seq", prev)
of mVarargs: result = semVarargs(c, n, prev)
- of mExpr, mTypeDesc:
+ of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil))
+ of mExpr:
result = semTypeNode(c, n.sons[0], nil)
if result != nil:
result = copyType(result, getCurrOwner(), false)
@@ -1030,9 +1063,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
if s.kind != skError: localError(n.info, errTypeExpected)
result = newOrPrevType(tyError, prev, c)
elif s.kind == skParam and s.typ.kind == tyTypeDesc:
- assert s.typ.len > 0
- internalAssert prev == nil
- result = s.typ.sons[0]
+ internalAssert s.typ.base.kind != tyNone and prev == nil
+ result = s.typ.base
elif prev == nil:
result = s.typ
else:
@@ -1066,27 +1098,18 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
var base = semTypeNode(c, n.sons[0], nil)
result.rawAddSon(base)
result.flags.incl tfHasStatic
- of nkProcTy, nkIteratorTy:
+ of nkIteratorTy:
if n.sonsLen == 0:
- result = newConstraint(c, tyProc)
+ result = newConstraint(c, tyIter)
else:
- checkSonsLen(n, 2)
- openScope(c)
- result = semProcTypeNode(c, n.sons[0], nil, prev, skProc)
- # dummy symbol for `pragma`:
- var s = newSymS(skProc, newIdentNode(getIdent("dummy"), n.info), c)
- s.typ = result
- if n.sons[1].kind == nkEmpty or n.sons[1].len == 0:
- if result.callConv == ccDefault:
- result.callConv = ccClosure
- #Message(n.info, warnImplicitClosure, renderTree(n))
- else:
- pragma(c, s, n.sons[1], procTypePragmas)
- when useEffectSystem: setEffectsForProcType(result, n.sons[1])
- closeScope(c)
- if n.kind == nkIteratorTy:
+ result = semProcTypeWithScope(c, n, prev, skClosureIterator)
result.flags.incl(tfIterator)
result.callConv = ccClosure
+ of nkProcTy:
+ if n.sonsLen == 0:
+ result = newConstraint(c, tyProc)
+ else:
+ result = semProcTypeWithScope(c, n, prev, skProc)
of nkEnumTy: result = semEnum(c, n, prev)
of nkType: result = n.typ
of nkStmtListType: result = semStmtListType(c, n, prev)
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index a07d91241..22edc6e32 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -29,6 +29,7 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
localError(info, errVarVarTypeNotAllowed)
elif computeSize(t) == szIllegalRecursion:
localError(info, errIllegalRecursionInTypeX, typeToString(t))
+
when false:
if t.kind == tyObject and t.sons[0] != nil:
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
@@ -153,6 +154,9 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
discard
of nkSym:
result.sym = replaceTypeVarsS(cl, n.sym)
+ if result.sym.typ.kind == tyEmpty:
+ # don't add the 'void' field
+ result = newNode(nkRecList, n.info)
of nkRecWhen:
var branch: PNode = nil # the branch to take
for i in countup(0, sonsLen(n) - 1):
@@ -216,7 +220,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
# is difficult to handle:
var body = t.sons[0]
if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
- var header: PType = nil
+ var header: PType = t
# search for some instantiation here:
if cl.allowMetaTypes:
result = PType(idTableGet(cl.localCache, t))
@@ -228,11 +232,13 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
if x.kind == tyGenericParam:
x = lookupTypeVar(cl, x)
if x != nil:
- if header == nil: header = instCopyType(cl, t)
+ if header == t: header = instCopyType(cl, t)
header.sons[i] = x
propagateToOwner(header, x)
+ else:
+ propagateToOwner(header, x)
- if header != nil:
+ if header != t:
# search again after first pass:
result = searchInstTypes(header)
if result != nil: return
@@ -240,6 +246,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
header = instCopyType(cl, t)
result = newType(tyGenericInst, t.sons[0].owner)
+ result.flags = header.flags
# be careful not to propagate unnecessary flags here (don't use rawAddSon)
result.sons = @[header.sons[0]]
# ugh need another pass for deeply recursive generic types (e.g. PActor)
@@ -406,14 +413,22 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: discard
+proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
+ initIdTable(result.symMap)
+ copyIdTable(result.typeMap, pt)
+ initIdTable(result.localCache)
+ result.info = info
+ result.c = p
+
+proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode): PNode =
+ var cl = initTypeVars(p, pt, n.info)
+ pushInfoContext(n.info)
+ result = replaceTypeVarsN(cl, n)
+ popInfoContext()
+
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType =
- var cl: TReplTypeVars
- initIdTable(cl.symMap)
- copyIdTable(cl.typeMap, pt)
- initIdTable(cl.localCache)
- cl.info = info
- cl.c = p
+ var cl = initTypeVars(p, pt, info)
pushInfoContext(info)
result = replaceTypeVarsT(cl, t)
popInfoContext()
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 335ceafeb..c0898ef26 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -51,6 +51,8 @@ type
isSubtype,
isSubrange, # subrange of the wanted type; no type conversion
# but apart from that counts as ``isSubtype``
+ isInferred, # generic proc was matched against a concrete type
+ isInferredConvertible, # same as above, but requiring proc CC conversion
isGeneric,
isFromIntLit, # conversion *from* int literal; proven safe
isEqual
@@ -338,34 +340,60 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
proc allowsNil(f: PType): TTypeRelation {.inline.} =
result = if tfNotNil notin f.flags: isSubtype else: isNone
-proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
- proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
- result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
+proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
+ result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
+
+proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
+ var f = f
+
+ if a.isMetaType:
+ if f.isMetaType:
+ # We are matching a generic proc (as proc param)
+ # to another generic type appearing in the proc
+ # signature. There is a change that the target
+ # type is already fully-determined, so we are
+ # going to try resolve it
+ f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
+ if f == nil or f.isMetaType:
+ # no luck resolving the type, so the inference fails
+ return isNone
+ let reverseRel = typeRel(c, a, f)
+ if reverseRel == isGeneric:
+ result = isInferred
+ inc c.genericMatches
+ else:
+ result = typeRel(c, f, a)
+ if result <= isSubtype or inconsistentVarTypes(f, a):
+ result = isNone
+
+ if result == isEqual:
+ inc c.exactMatches
+
+proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
case a.kind
of tyProc:
if sonsLen(f) != sonsLen(a): return
- # Note: We have to do unification for the parameters before the
- # return type!
result = isEqual # start with maximum; also correct for no
# params at all
- for i in countup(1, sonsLen(f)-1):
- var m = typeRel(c, f.sons[i], a.sons[i])
- if m <= isSubtype or inconsistentVarTypes(f.sons[i], a.sons[i]):
- return isNone
- else: result = minRel(m, result)
+
+ template checkParam(f, a) =
+ result = minRel(result, procParamTypeRel(c, f, a))
+ if result == isNone: return
+
+ # Note: We have to do unification for the parameters before the
+ # return type!
+ for i in 1 .. <f.sonsLen:
+ checkParam(f.sons[i], a.sons[i])
+
if f.sons[0] != nil:
if a.sons[0] != nil:
- var m = typeRel(c, f.sons[0], a.sons[0])
- # Subtype is sufficient for return types!
- if m < isSubtype or inconsistentVarTypes(f.sons[0], a.sons[0]):
- return isNone
- elif m == isSubtype: result = isConvertible
- else: result = minRel(m, result)
+ checkParam(f.sons[0], a.sons[0])
else:
return isNone
elif a.sons[0] != nil:
return isNone
+
if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
return isNone
elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
@@ -376,12 +404,16 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
elif f.callConv != a.callConv:
# valid to pass a 'nimcall' thingie to 'closure':
if f.callConv == ccClosure and a.callConv == ccDefault:
- result = isConvertible
+ result = if result != isInferred: isConvertible
+ else: isInferredConvertible
else:
return isNone
when useEffectSystem:
if not compatibleEffects(f, a): return isNone
- of tyNil: result = f.allowsNil
+ of tyNil:
+ result = f.allowsNil
+ of tyIter:
+ if tfIterator in f.flags: result = typeRel(c, f.base, a.base)
else: discard
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
@@ -402,18 +434,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
ff, a: PType): TTypeRelation =
- #if f.n == nil:
- # let r = typeRel(m, f, a)
- # return if r == isGeneric: arg else: nil
-
var body = ff.skipTypes({tyUserTypeClassInst})
- # var prev = PType(idTableGet(m.bindings, f))
- # if prev != nil:
- # if sameType(prev, a): return arg
- # else: return nil
-
- # pushInfoContext(arg.info)
openScope(c)
inc c.inTypeClass
@@ -462,7 +484,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
else: discard
return isGeneric
- # put(m.bindings, f, a)
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# typeRel can be used to establish various relationships between types:
@@ -852,7 +873,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else:
internalAssert a.sons != nil and a.sons.len > 0
c.typedescMatched = true
- result = typeRel(c, f.sons[0], a.sons[0])
+ result = typeRel(c, f.base, a.base)
else:
result = isNone
else:
@@ -884,28 +905,40 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyTypeDesc:
var prev = PType(idTableGet(c.bindings, f))
if prev == nil:
- if a.kind == tyTypeDesc:
- if f.sons[0].kind == tyNone:
- result = isGeneric
- else:
- result = typeRel(c, f.sons[0], a.sons[0])
- if result != isNone:
- put(c.bindings, f, a)
+ # proc foo(T: typedesc, x: T)
+ # when `f` is an unresolved typedesc, `a` could be any
+ # type, so we should not perform this check earlier
+ if a.kind != tyTypeDesc: return isNone
+
+ if f.base.kind == tyNone:
+ result = isGeneric
+ else:
+ result = typeRel(c, f.base, a.base)
+
+ if result != isNone:
+ put(c.bindings, f, a)
+ else:
+ if tfUnresolved in f.flags:
+ result = typeRel(c, prev.base, a)
+ elif a.kind == tyTypeDesc:
+ result = typeRel(c, prev.base, a.base)
else:
result = isNone
+
+ of tyIter:
+ if a.kind == tyIter or
+ (a.kind == tyProc and tfIterator in a.flags):
+ result = typeRel(c, f.base, a.base)
else:
- internalAssert prev.sonsLen == 1
- let toMatch = if tfUnresolved in f.flags: a
- else: a.sons[0]
- result = typeRel(c, prev.sons[0], toMatch)
-
+ result = isNone
+
of tyStmt:
result = isGeneric
of tyProxy:
result = isEqual
- else: internalError("typeRel: " & $f.kind)
+ else: internalAssert false
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
var m: TCandidate
@@ -988,7 +1021,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
arg = argSemantized
argType = argType
c = m.c
-
+
if tfHasStatic in fMaybeStatic.flags:
# XXX: When implicit statics are the default
# this will be done earlier - we just have to
@@ -1001,11 +1034,33 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argType = arg.typ
var
- a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc})
+ a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
else: argType
r = typeRel(m, f, a)
+ if r != isNone and m.calleeSym != nil and
+ m.calleeSym.kind in {skMacro, skTemplate}:
+ # XXX: duplicating this is ugly, maybe we should move this
+ # directly into typeRel using return-like templates
+ case r
+ of isConvertible, isIntConv: inc(m.convMatches)
+ of isSubtype, isSubrange: inc(m.subtypeMatches)
+ of isGeneric, isInferred: inc(m.genericMatches)
+ of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
+ of isFromIntLit: inc(m.intConvMatches, 256)
+ of isEqual: inc(m.exactMatches)
+ of isNone: discard
+
+ if f.kind == tyStmt and argOrig.kind == nkDo:
+ return argOrig[bodyPos]
+ elif f.kind == tyTypeDesc:
+ return arg
+ elif f.kind == tyStatic:
+ return arg.typ.n
+ else:
+ return argOrig
+
case r
of isConvertible:
inc(m.convMatches)
@@ -1022,24 +1077,24 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
inc(m.subtypeMatches)
#result = copyTree(arg)
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
- of isGeneric:
+ of isInferred, isInferredConvertible:
inc(m.genericMatches)
- if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
- if f.kind == tyStmt and argOrig.kind == nkDo:
- result = argOrig[bodyPos]
- elif f.kind == tyTypeDesc:
- result = arg
- elif f.kind == tyStatic:
- result = arg.typ.n
- else:
- result = argOrig
+ if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
+ result = c.semInferredLambda(c, m.bindings, arg)
else:
- result = copyTree(arg)
- result.typ = getInstantiatedType(c, arg, m, f)
- # BUG: f may not be the right key!
- if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
- result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
- # BUGFIX: use ``result.typ`` and not `f` here
+ let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
+ result = newSymNode(inferred, arg.info)
+ if r == isInferredConvertible:
+ inc(m.convMatches)
+ result = implicitConv(nkHiddenStdConv, f, result, m, c)
+ of isGeneric:
+ inc(m.genericMatches)
+ result = copyTree(arg)
+ result.typ = getInstantiatedType(c, arg, m, f)
+ # BUG: f may not be the right key!
+ if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
+ result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
+ # BUGFIX: use ``result.typ`` and not `f` here
of isFromIntLit:
# too lazy to introduce another ``*matches`` field, so we conflate
# ``isIntConv`` and ``isIntLit`` here:
@@ -1082,6 +1137,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
# incorrect to simply use the first fitting match. However, to implement
# this correctly is inefficient. We have to copy `m` here to be able to
# roll back the side effects of the unification algorithm.
+
let c = m.c
var x, y, z: TCandidate
initCandidate(c, x, m.callee)
@@ -1092,7 +1148,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
z.calleeSym = m.calleeSym
var best = -1
for i in countup(0, sonsLen(arg) - 1):
- if arg.sons[i].sym.kind in {skProc, skIterator, skMethod, skConverter}:
+ if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
copyCandidate(z, m)
var r = typeRel(z, f, arg.sons[i].typ)
if r != isNone:
@@ -1103,10 +1159,10 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
x.state = csMatch
of csMatch:
var cmp = cmpCandidates(x, z)
- if cmp < 0:
+ if cmp < 0:
best = i
x = z
- elif cmp == 0:
+ elif cmp == 0:
y = z # z is as good as x
if x.state == csEmpty:
result = nil
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 7c44ec0b4..478c2a837 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -17,14 +17,15 @@ type
TFilterKind* = enum
filtNone, filtTemplate, filtReplace, filtStrip
TParserKind* = enum
- skinStandard, skinBraces, skinEndX
+ skinStandard, skinStrongSpaces, skinBraces, skinEndX
const
- parserNames*: array[TParserKind, string] = ["standard", "braces", "endx"]
- filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
- "strip"]
+ parserNames*: array[TParserKind, string] = ["standard", "strongspaces",
+ "braces", "endx"]
+ filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace",
+ "strip"]
-type
+type
TParsers*{.final.} = object
skin*: TParserKind
parser*: TParser
@@ -54,7 +55,7 @@ proc parseFile(fileIdx: int32): PNode =
proc parseAll(p: var TParsers): PNode =
case p.skin
- of skinStandard:
+ of skinStandard, skinStrongSpaces:
result = parser.parseAll(p.parser)
of skinBraces:
result = pbraces.parseAll(p.parser)
@@ -65,7 +66,7 @@ proc parseAll(p: var TParsers): PNode =
proc parseTopLevelStmt(p: var TParsers): PNode =
case p.skin
- of skinStandard:
+ of skinStandard, skinStrongSpaces:
result = parser.parseTopLevelStmt(p.parser)
of skinBraces:
result = pbraces.parseTopLevelStmt(p.parser)
@@ -170,7 +171,9 @@ proc openParsers(p: var TParsers, fileIdx: int32, inputstream: PLLStream) =
else: s = inputstream
case p.skin
of skinStandard, skinBraces, skinEndX:
- parser.openParser(p.parser, fileIdx, s)
+ parser.openParser(p.parser, fileIdx, s, false)
+ of skinStrongSpaces:
+ parser.openParser(p.parser, fileIdx, s, true)
-proc closeParsers(p: var TParsers) =
+proc closeParsers(p: var TParsers) =
parser.closeParser(p.parser)
diff --git a/compiler/transf.nim b/compiler/transf.nim
index deb821eff..f4b716c5b 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -113,7 +113,7 @@ proc newAsgnStmt(c: PTransf, le: PNode, ri: PTransNode): PTransNode =
result[1] = ri
proc transformSymAux(c: PTransf, n: PNode): PNode =
- #if n.sym.kind == skIterator and n.sym.typ.callConv == ccClosure:
+ #if n.sym.kind == skClosureIterator:
# return liftIterSym(n)
var b: PNode
var tc = c.transCon
@@ -434,7 +434,6 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
var length = sonsLen(n)
var call = n.sons[length - 2]
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
- call.sons[0].typ.callConv == ccClosure or
call.sons[0].sym.kind != skIterator:
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
return lambdalifting.liftForLoop(n).PTransNode
@@ -454,7 +453,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
var newC = newTransCon(getCurrOwner(c))
newC.forStmt = n
newC.forLoopBody = loopBody
- if iter.kind != skIterator: internalError(call.info, "transformFor")
+ internalAssert iter.kind == skIterator
# generate access statements for the parameters (unless they are constant)
pushTransCon(c, newC)
for i in countup(1, sonsLen(call) - 1):
@@ -741,7 +740,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode =
var c = openTransf(module, "")
result = processTransf(c, n, prc)
result = liftLambdas(prc, result)
- #if prc.kind == skIterator and prc.typ.callConv == ccClosure:
+ #if prc.kind == skClosureIterator:
# result = lambdalifting.liftIterator(prc, result)
incl(result.flags, nfTransf)
when useEffectSystem: trackProc(prc, result)
@@ -754,6 +753,7 @@ proc transformStmt*(module: PSym, n: PNode): PNode =
result = processTransf(c, n, module)
result = liftLambdasForTopLevel(module, result)
incl(result.flags, nfTransf)
+ when useEffectSystem: trackTopLevelStmt(module, result)
proc transformExpr*(module: PSym, n: PNode): PNode =
if nfTransf in n.flags:
diff --git a/compiler/types.nim b/compiler/types.nim
index db75cd3c0..8cf91da8b 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -431,8 +431,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, typeToString(t.sons[i]))
add(result, ']')
of tyTypeDesc:
- if t.len == 0: result = "typedesc"
- else: result = "typedesc[" & typeToString(t.sons[0]) & "]"
+ if t.base.kind == tyNone: result = "typedesc"
+ else: result = "typedesc[" & typeToString(t.base) & "]"
of tyStatic:
internalAssert t.len > 0
result = "static[" & typeToString(t.sons[0]) & "]"
@@ -452,7 +452,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyProc: "proc"
of tyObject: "object"
of tyTuple: "tuple"
- else: (internalAssert(false); "")
+ of tyOpenArray: "openarray"
+ else: typeToStr[t.base.kind]
of tyUserTypeClassInst:
let body = t.base
result = body.sym.name.s & "["
@@ -463,7 +464,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyAnd:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
of tyOr:
- result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
+ result = typeToString(t.sons[0]) & " or " & typeToString(t.sons[1])
of tyNot:
result = "not " & typeToString(t.sons[0])
of tyExpr:
@@ -857,7 +858,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
if a.kind != b.kind: return false
of dcEqOrDistinctOf:
while a.kind == tyDistinct: a = a.sons[0]
- if a.kind != b.kind: return false
+ if a.kind != b.kind: return false
case a.kind
of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString,
tyInt..tyBigNum, tyStmt, tyExpr:
@@ -1042,7 +1043,8 @@ proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
of tyEmpty:
result = taField in flags
of tyTypeClasses:
- result = true
+ result = tfGenericTypeParam in t.flags or
+ taField notin flags
of tyGenericBody, tyGenericParam, tyGenericInvokation,
tyNone, tyForward, tyFromExpr, tyFieldAccessor:
result = false
@@ -1231,8 +1233,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt =
of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter:
result = computeSizeAux(lastSon(typ), a)
of tyTypeDesc:
- result = if typ.len == 1: computeSizeAux(typ.sons[0], a)
- else: szUnknownSize
+ result = computeSizeAux(typ.base, a)
of tyForward: return szIllegalRecursion
else:
#internalError("computeSizeAux()")
@@ -1246,7 +1247,7 @@ proc computeSize(typ: PType): BiggestInt =
proc getReturnType*(s: PSym): PType =
# Obtains the return type of a iterator/proc/macro/template
- assert s.kind in {skProc, skTemplate, skMacro, skIterator}
+ assert s.kind in skProcKinds
result = s.typ.sons[0]
proc getSize(typ: PType): BiggestInt =
@@ -1258,7 +1259,7 @@ proc containsGenericTypeIter(t: PType, closure: PObject): bool =
return true
if t.kind == tyTypeDesc:
- if t.sons[0].kind == tyNone: return true
+ if t.base.kind == tyNone: return true
if containsGenericTypeIter(t.base, closure): return true
return false
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 10ac7aaaf..f9b143bce 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1,14 +1,14 @@
#
#
# The Nimrod Compiler
-# (c) Copyright 2013 Andreas Rumpf
+# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This file implements the new evaluation engine for Nimrod code.
-## An instruction is 1-2 int32s in memory, it is a register based VM.
+## An instruction is 1-3 int32s in memory, it is a register based VM.
import ast except getstr
@@ -23,10 +23,22 @@ when hasFFI:
import evalffi
type
+ TRegisterKind = enum
+ rkNone, rkNode, rkInt, rkFloat, rkRegisterAddr, rkNodeAddr
+ TFullReg = object # with a custom mark proc, we could use the same
+ # data representation as LuaJit (tagged NaNs).
+ case kind: TRegisterKind
+ of rkNone: nil
+ of rkInt: intVal: BiggestInt
+ of rkFloat: floatVal: BiggestFloat
+ of rkNode: node: PNode
+ of rkRegisterAddr: regAddr: ptr TFullReg
+ of rkNodeAddr: nodeAddr: ptr PNode
+
PStackFrame* = ref TStackFrame
TStackFrame* = object
prc: PSym # current prc; proc that is evaluated
- slots: TNodeSeq # parameters passed to the proc + locals;
+ slots: seq[TFullReg] # parameters passed to the proc + locals;
# parameters come first
next: PStackFrame # for stacking
comesFrom: int
@@ -63,24 +75,9 @@ proc bailOut(c: PCtx; tos: PStackFrame) =
when not defined(nimComputedGoto):
{.pragma: computedGoto.}
-proc myreset(n: PNode) =
+proc myreset(n: var TFullReg) =
when defined(system.reset):
- var oldInfo = n.info
- reset(n[])
- n.info = oldInfo
-
-proc skipMeta(n: PNode): PNode = (if n.kind != nkMetaNode: n else: n.sons[0])
-
-proc setMeta(n, child: PNode) =
- assert n.kind == nkMetaNode
- let child = child.skipMeta
- if n.sons.isNil: n.sons = @[child]
- else: n.sons[0] = child
-
-proc uast(n: PNode): PNode {.inline.} =
- # "underlying ast"
- assert n.kind == nkMetaNode
- n.sons[0]
+ reset(n)
template ensureKind(k: expr) {.immediate, dirty.} =
if regs[ra].kind != k:
@@ -112,23 +109,32 @@ template decodeBx(k: expr) {.immediate, dirty.} =
template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b)
# XXX fix minor 'shallowCopy' overloading bug in compiler
-proc moveConst(x, y: PNode) =
+proc createStrKeepNode(x: var TFullReg) =
+ if x.node.isNil:
+ x.node = newNode(nkStrLit)
+ elif x.node.kind == nkNilLit:
+ system.reset(x.node[])
+ x.node.kind = nkStrLit
+ elif x.node.kind notin {nkStrLit..nkTripleStrLit}:
+ # XXX this is hacky; tests/txmlgen triggers it:
+ x.node = newNode(nkStrLit)
+ # debug x.node
+ #assert x.node.kind in {nkStrLit..nkTripleStrLit}
+
+template createStr(x) =
+ x.node = newNode(nkStrLit)
+
+proc moveConst(x: var TFullReg, y: TFullReg) =
if x.kind != y.kind:
myreset(x)
x.kind = y.kind
- x.typ = y.typ
case x.kind
- of nkCharLit..nkInt64Lit: x.intVal = y.intVal
- of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal
- of nkStrLit..nkTripleStrLit: move(x.strVal, y.strVal)
- of nkIdent: x.ident = y.ident
- of nkSym: x.sym = y.sym
- of nkMetaNode:
- if x.sons.isNil: x.sons = @[y.sons[0]]
- else: x.sons[0] = y.sons[0]
- else:
- if x.kind notin {nkEmpty..nkNilLit}:
- move(x.sons, y.sons)
+ of rkNone: discard
+ of rkInt: x.intVal = y.intVal
+ of rkFloat: x.floatVal = y.floatVal
+ of rkNode: x.node = y.node
+ of rkRegisterAddr: x.regAddr = y.regAddr
+ of rkNodeAddr: x.nodeAddr = y.nodeAddr
# this seems to be the best way to model the reference semantics
# of PNimrodNode:
@@ -155,29 +161,56 @@ proc copyValue(src: PNode): PNode =
for i in countup(0, sonsLen(src) - 1):
result.sons[i] = copyValue(src.sons[i])
-proc asgnComplex(x, y: PNode) =
+proc asgnComplex(x: var TFullReg, y: TFullReg) =
if x.kind != y.kind:
myreset(x)
x.kind = y.kind
- x.typ = y.typ
case x.kind
- of nkCharLit..nkInt64Lit: x.intVal = y.intVal
- of nkFloatLit..nkFloat64Lit: x.floatVal = y.floatVal
- of nkStrLit..nkTripleStrLit: x.strVal = y.strVal
- of nkIdent: x.ident = y.ident
- of nkSym: x.sym = y.sym
- of nkMetaNode:
- if x.sons.isNil: x.sons = @[y.sons[0]]
- else: x.sons[0] = y.sons[0]
+ of rkNone: discard
+ of rkInt: x.intVal = y.intVal
+ of rkFloat: x.floatVal = y.floatVal
+ of rkNode: x.node = copyValue(y.node)
+ of rkRegisterAddr: x.regAddr = y.regAddr
+ of rkNodeAddr: x.nodeAddr = y.nodeAddr
+
+proc putIntoNode(n: var PNode; x: TFullReg) =
+ case x.kind
+ of rkNone: discard
+ of rkInt: n.intVal = x.intVal
+ of rkFloat: n.floatVal = x.floatVal
+ of rkNode:
+ if nfIsRef in x.node.flags: n = x.node
+ else: n[] = x.node[]
+ of rkRegisterAddr: putIntoNode(n, x.regAddr[])
+ of rkNodeAddr: n[] = x.nodeAddr[][]
+
+proc putIntoReg(dest: var TFullReg; n: PNode) =
+ case n.kind
+ of nkStrLit..nkTripleStrLit:
+ dest.kind = rkNode
+ createStr(dest)
+ dest.node.strVal = n.strVal
+ of nkCharLit..nkUInt64Lit:
+ dest.kind = rkInt
+ dest.intVal = n.intVal
+ of nkFloatLit..nkFloat128Lit:
+ dest.kind = rkFloat
+ dest.floatVal = n.floatVal
else:
- if x.kind notin {nkEmpty..nkNilLit}:
- let y = y.copyValue
- for i in countup(0, sonsLen(y) - 1):
- if i < x.len: x.sons[i] = y.sons[i]
- else: addSon(x, y.sons[i])
+ dest.kind = rkNode
+ dest.node = n
+
+proc regToNode(x: TFullReg): PNode =
+ case x.kind
+ of rkNone: result = newNode(nkEmpty)
+ of rkInt: result = newNode(nkIntLit); result.intVal = x.intVal
+ of rkFloat: result = newNode(nkFloatLit); result.floatVal = x.floatVal
+ of rkNode: result = x.node
+ of rkRegisterAddr: result = regToNode(x.regAddr[])
+ of rkNodeAddr: result = x.nodeAddr[]
template getstr(a: expr): expr =
- (if a.kind in {nkStrLit..nkTripleStrLit}: a.strVal else: $chr(int(a.intVal)))
+ (if a.kind == rkNode: a.node.strVal else: $chr(int(a.intVal)))
proc pushSafePoint(f: PStackFrame; pc: int) =
if f.safePoints.isNil: f.safePoints = @[]
@@ -185,7 +218,7 @@ proc pushSafePoint(f: PStackFrame; pc: int) =
proc popSafePoint(f: PStackFrame) = discard f.safePoints.pop()
-proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: TNodeSeq): int =
+proc cleanUpOnException(c: PCtx; tos: PStackFrame; regs: seq[TFullReg]): int =
let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs)
var f = tos
while true:
@@ -227,50 +260,65 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int =
return pc
return -1
-proc opConv*(dest, src: PNode, typ: PType): bool =
- if typ.kind == tyString:
- if dest.kind != nkStrLit:
+proc opConv*(dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
+ if desttyp.kind == tyString:
+ if dest.kind != rkNode:
myreset(dest)
- dest.kind = nkStrLit
- case src.typ.skipTypes(abstractRange).kind
- of tyEnum:
- dest.strVal = ordinalValToString(src)
- of tyInt..tyInt64, tyUInt..tyUInt64:
- dest.strVal = $src.intVal
+ dest.kind = rkNode
+ dest.node = newNode(nkStrLit)
+ let styp = srctyp.skipTypes(abstractRange)
+ case styp.kind
+ of tyEnum:
+ let n = styp.n
+ let x = src.intVal.int
+ if x <% n.len and (let f = n.sons[x].sym; f.position == x):
+ dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal
+ else:
+ for i in 0.. <n.len:
+ if n.sons[i].kind != nkSym: internalError("opConv for enum")
+ let f = n.sons[i].sym
+ if f.position == x:
+ dest.node.strVal = if f.ast.isNil: f.name.s else: f.ast.strVal
+ return
+ internalError("opConv for enum")
+ of tyInt..tyInt64:
+ dest.node.strVal = $src.intVal
+ of tyUInt..tyUInt64:
+ dest.node.strVal = $uint64(src.intVal)
of tyBool:
- dest.strVal = if src.intVal == 0: "false" else: "true"
+ dest.node.strVal = if src.intVal == 0: "false" else: "true"
of tyFloat..tyFloat128:
- dest.strVal = $src.floatVal
+ dest.node.strVal = $src.floatVal
of tyString, tyCString:
- dest.strVal = src.strVal
+ dest.node.strVal = src.node.strVal
of tyChar:
- dest.strVal = $chr(src.intVal)
+ dest.node.strVal = $chr(src.intVal)
else:
- internalError("cannot convert to string " & typ.typeToString)
+ internalError("cannot convert to string " & desttyp.typeToString)
else:
- case skipTypes(typ, abstractRange).kind
+ case skipTypes(desttyp, abstractRange).kind
of tyInt..tyInt64:
- if dest.kind != nkIntLit:
- myreset(dest); dest.kind = nkIntLit
- case skipTypes(src.typ, abstractRange).kind
+ if dest.kind != rkInt:
+ myreset(dest); dest.kind = rkInt
+ case skipTypes(srctyp, abstractRange).kind
of tyFloat..tyFloat64:
dest.intVal = system.toInt(src.floatVal)
else:
dest.intVal = src.intVal
- if dest.intVal < firstOrd(typ) or dest.intVal > lastOrd(typ):
+ if dest.intVal < firstOrd(desttyp) or dest.intVal > lastOrd(desttyp):
return true
of tyUInt..tyUInt64:
- if dest.kind != nkIntLit:
- myreset(dest); dest.kind = nkIntLit
- case skipTypes(src.typ, abstractRange).kind
+ if dest.kind != rkInt:
+ myreset(dest); dest.kind = rkInt
+ case skipTypes(srctyp, abstractRange).kind
of tyFloat..tyFloat64:
dest.intVal = system.toInt(src.floatVal)
else:
- dest.intVal = src.intVal and ((1 shl typ.size)-1)
+ dest.intVal = src.intVal and ((1 shl desttyp.size)-1)
of tyFloat..tyFloat64:
- if dest.kind != nkFloatLit:
- myreset(dest); dest.kind = nkFloatLit
- case skipTypes(src.typ, abstractRange).kind
+ if dest.kind != rkFloat:
+ myreset(dest); dest.kind = rkFloat
+ case skipTypes(srctyp, abstractRange).kind
of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar:
dest.floatVal = toFloat(src.intVal.int)
else:
@@ -282,23 +330,19 @@ proc compile(c: PCtx, s: PSym): int =
result = vmgen.genProc(c, s)
#c.echoCode
-proc regsContents(regs: TNodeSeq) =
- for i in 0.. <regs.len:
- echo "Register ", i
- #debug regs[i]
-
-proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
+proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
var pc = start
var tos = tos
- var regs: TNodeSeq # alias to tos.slots for performance
+ var regs: seq[TFullReg] # alias to tos.slots for performance
move(regs, tos.slots)
#echo "NEW RUN ------------------------"
while true:
#{.computedGoto.}
let instr = c.code[pc]
let ra = instr.regA
- #echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
- #message(c.debug[pc], warnUser, "gah")
+ #if c.traceActive:
+ # echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
+ # message(c.debug[pc], warnUser, "Trace")
case instr.opcode
of opcEof: return regs[ra]
of opcRet:
@@ -318,325 +362,338 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
of opcYldYoid: assert false
of opcYldVal: assert false
of opcAsgnInt:
- decodeB(nkIntLit)
+ decodeB(rkInt)
regs[ra].intVal = regs[rb].intVal
of opcAsgnStr:
- if regs[instr.regB].kind == nkNilLit:
- decodeB(nkNilLit)
- else:
- decodeB(nkStrLit)
- regs[ra].strVal = regs[rb].strVal
+ decodeB(rkNode)
+ createStrKeepNode regs[ra]
+ regs[ra].node.strVal = regs[rb].node.strVal
of opcAsgnFloat:
- decodeB(nkFloatLit)
+ decodeB(rkFloat)
regs[ra].floatVal = regs[rb].floatVal
of opcAsgnComplex:
asgnComplex(regs[ra], regs[instr.regB])
of opcAsgnRef:
asgnRef(regs[ra], regs[instr.regB])
- of opcWrGlobalRef:
- asgnRef(c.globals.sons[instr.regBx-wordExcess-1], regs[ra])
- of opcWrGlobal:
- asgnComplex(c.globals.sons[instr.regBx-wordExcess-1], regs[ra])
- of opcLdArr, opcLdArrRef:
- # a = b[c]
+ of opcRegToNode:
+ decodeB(rkNode)
+ putIntoNode(regs[ra].node, regs[rb])
+ of opcNodeToReg:
+ let ra = instr.regA
let rb = instr.regB
- let rc = instr.regC
+ assert regs[rb].kind == rkNode
+ let nb = regs[rb].node
+ case nb.kind
+ of nkCharLit..nkInt64Lit:
+ ensureKind(rkInt)
+ regs[ra].intVal = nb.intVal
+ of nkFloatLit..nkFloat64Lit:
+ ensureKind(rkFloat)
+ regs[ra].floatVal = nb.floatVal
+ else:
+ ensureKind(rkNode)
+ regs[ra].node = nb
+ of opcLdArr:
+ # a = b[c]
+ decodeBC(rkNode)
if regs[rc].intVal > high(int):
stackTrace(c, tos, pc, errIndexOutOfBounds)
let idx = regs[rc].intVal.int
# XXX what if the array is not 0-based? -> codegen should insert a sub
- assert regs[rb].kind != nkMetaNode
- let src = regs[rb]
+ let src = regs[rb].node
if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len:
- if instr.opcode == opcLdArrRef and false:
- # XXX activate when seqs are fixed
- asgnRef(regs[ra], src.sons[idx])
- else:
- asgnComplex(regs[ra], src.sons[idx])
+ regs[ra].node = src.sons[idx]
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcLdStrIdx:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
let idx = regs[rc].intVal.int
- if idx <=% regs[rb].strVal.len:
- regs[ra].intVal = regs[rb].strVal[idx].ord
+ if idx <=% regs[rb].node.strVal.len:
+ regs[ra].intVal = regs[rb].node.strVal[idx].ord
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcWrArr:
# a[b] = c
- let rb = instr.regB
- let rc = instr.regC
- let idx = regs[rb].intVal.int
- if idx <% regs[ra].len:
- asgnComplex(regs[ra].sons[idx], regs[rc])
- else:
- stackTrace(c, tos, pc, errIndexOutOfBounds)
- of opcWrArrRef:
- let rb = instr.regB
- let rc = instr.regC
+ decodeBC(rkNode)
let idx = regs[rb].intVal.int
- if idx <% regs[ra].len:
- asgnRef(regs[ra].sons[idx], regs[rc])
+ if idx <% regs[ra].node.len:
+ putIntoNode(regs[ra].node.sons[idx], regs[rc])
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcLdObj:
# a = b.c
- let rb = instr.regB
- let rc = instr.regC
- #Message(c.debug[pc], warnUser, $regs[rb].safeLen & " " & $rc)
- asgnComplex(regs[ra], regs[rb].sons[rc])
- of opcLdObjRef:
- # a = b.c
- let rb = instr.regB
- let rc = instr.regC
- # XXX activate when seqs are fixed
- asgnComplex(regs[ra], regs[rb].sons[rc])
- #asgnRef(regs[ra], regs[rb].sons[rc])
+ decodeBC(rkNode)
+ let src = regs[rb].node
+ if src.kind notin {nkEmpty..nkNilLit}:
+ regs[ra].node = src.sons[rc]
+ else:
+ stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcWrObj:
# a.b = c
- let rb = instr.regB
- let rc = instr.regC
- #if regs[ra].isNil or regs[ra].sons.isNil or rb >= len(regs[ra]):
- # debug regs[ra]
- # debug regs[rc]
- # echo "RB ", rb
- # internalError(c.debug[pc], "argl")
- asgnComplex(regs[ra].sons[rb], regs[rc])
- of opcWrObjRef:
- let rb = instr.regB
- let rc = instr.regC
- asgnRef(regs[ra].sons[rb], regs[rc])
+ decodeBC(rkNode)
+ putIntoNode(regs[ra].node.sons[rb], regs[rc])
of opcWrStrIdx:
- decodeBC(nkStrLit)
+ decodeBC(rkNode)
let idx = regs[rb].intVal.int
- if idx <% regs[ra].strVal.len:
- regs[ra].strVal[idx] = chr(regs[rc].intVal)
+ if idx <% regs[ra].node.strVal.len:
+ regs[ra].node.strVal[idx] = chr(regs[rc].intVal)
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
- of opcAddr:
- decodeB(nkRefTy)
- if regs[ra].len == 0: regs[ra].add regs[rb]
- else: regs[ra].sons[0] = regs[rb]
- of opcDeref:
+ of opcAddrReg:
+ decodeB(rkRegisterAddr)
+ regs[ra].regAddr = addr(regs[rb])
+ of opcAddrNode:
+ decodeB(rkNodeAddr)
+ regs[ra].nodeAddr = addr(regs[rb].node)
+ of opcLdDeref:
# a = b[]
+ let ra = instr.regA
let rb = instr.regB
- if regs[rb].kind == nkNilLit:
+ case regs[rb].kind
+ of rkNodeAddr:
+ ensureKind(rkNode)
+ regs[ra].node = regs[rb].nodeAddr[]
+ of rkRegisterAddr:
+ ensureKind(regs[rb].regAddr.kind)
+ regs[ra] = regs[rb].regAddr[]
+ of rkNode:
+ if regs[rb].node.kind == nkNilLit:
+ stackTrace(c, tos, pc, errNilAccess)
+ assert regs[rb].node.kind == nkRefTy
+ regs[ra].node = regs[rb].node.sons[0]
+ else:
stackTrace(c, tos, pc, errNilAccess)
- assert regs[rb].kind == nkRefTy
- # XXX this is not correct
- regs[ra] = regs[rb].sons[0]
+ of opcWrDeref:
+ # a[] = b
+ let ra = instr.regA
+ let rb = instr.regB
+ case regs[ra].kind
+ of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rb])
+ of rkRegisterAddr: regs[ra].regAddr[] = regs[rb]
+ of rkNode: putIntoNode(regs[ra].node, regs[rb])
+ else: stackTrace(c, tos, pc, errNilAccess)
of opcAddInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal + regs[rc].intVal
of opcAddImmInt:
- decodeBImm(nkIntLit)
+ decodeBImm(rkInt)
regs[ra].intVal = regs[rb].intVal + imm
of opcSubInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal - regs[rc].intVal
of opcSubImmInt:
- decodeBImm(nkIntLit)
+ decodeBImm(rkInt)
regs[ra].intVal = regs[rb].intVal - imm
of opcLenSeq:
- decodeBImm(nkIntLit)
+ decodeBImm(rkInt)
#assert regs[rb].kind == nkBracket
# also used by mNLen:
- regs[ra].intVal = regs[rb].skipMeta.len - imm
+ regs[ra].intVal = regs[rb].node.safeLen - imm
of opcLenStr:
- decodeBImm(nkIntLit)
- if regs[rb].kind == nkNilLit:
- stackTrace(c, tos, pc, errNilAccess)
- else:
- assert regs[rb].kind in {nkStrLit..nkTripleStrLit}
- regs[ra].intVal = regs[rb].strVal.len - imm
+ decodeBImm(rkInt)
+ assert regs[rb].kind == rkNode
+ regs[ra].intVal = regs[rb].node.strVal.len - imm
of opcIncl:
- decodeB(nkCurly)
- if not inSet(regs[ra], regs[rb]): addSon(regs[ra], copyTree(regs[rb]))
+ decodeB(rkNode)
+ let b = regs[rb].regToNode
+ if not inSet(regs[ra].node, b):
+ addSon(regs[ra].node, copyTree(b))
of opcInclRange:
- decodeBC(nkCurly)
+ decodeBC(rkNode)
var r = newNode(nkRange)
- r.add regs[rb]
- r.add regs[rc]
- addSon(regs[ra], r.copyTree)
+ r.add regs[rb].regToNode
+ r.add regs[rc].regToNode
+ addSon(regs[ra].node, r.copyTree)
of opcExcl:
- decodeB(nkCurly)
- var b = newNodeIT(nkCurly, regs[rb].info, regs[rb].typ)
- addSon(b, regs[rb])
- var r = diffSets(regs[ra], b)
- discardSons(regs[ra])
- for i in countup(0, sonsLen(r) - 1): addSon(regs[ra], r.sons[i])
+ decodeB(rkNode)
+ var b = newNodeIT(nkCurly, regs[rb].node.info, regs[rb].node.typ)
+ addSon(b, regs[rb].regToNode)
+ var r = diffSets(regs[ra].node, b)
+ discardSons(regs[ra].node)
+ for i in countup(0, sonsLen(r) - 1): addSon(regs[ra].node, r.sons[i])
of opcCard:
- decodeB(nkIntLit)
- regs[ra].intVal = nimsets.cardSet(regs[rb])
+ decodeB(rkInt)
+ regs[ra].intVal = nimsets.cardSet(regs[rb].node)
of opcMulInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal * regs[rc].intVal
of opcDivInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal div regs[rc].intVal
of opcModInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal mod regs[rc].intVal
of opcAddFloat:
- decodeBC(nkFloatLit)
+ decodeBC(rkFloat)
regs[ra].floatVal = regs[rb].floatVal + regs[rc].floatVal
of opcSubFloat:
- decodeBC(nkFloatLit)
+ decodeBC(rkFloat)
regs[ra].floatVal = regs[rb].floatVal - regs[rc].floatVal
of opcMulFloat:
- decodeBC(nkFloatLit)
+ decodeBC(rkFloat)
regs[ra].floatVal = regs[rb].floatVal * regs[rc].floatVal
of opcDivFloat:
- decodeBC(nkFloatLit)
+ decodeBC(rkFloat)
regs[ra].floatVal = regs[rb].floatVal / regs[rc].floatVal
of opcShrInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal shr regs[rc].intVal
of opcShlInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal shl regs[rc].intVal
of opcBitandInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal and regs[rc].intVal
of opcBitorInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal or regs[rc].intVal
of opcBitxorInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal xor regs[rc].intVal
of opcAddu:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal +% regs[rc].intVal
of opcSubu:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal -% regs[rc].intVal
of opcMulu:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal *% regs[rc].intVal
of opcDivu:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal /% regs[rc].intVal
of opcModu:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = regs[rb].intVal %% regs[rc].intVal
of opcEqInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].intVal == regs[rc].intVal)
of opcLeInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].intVal <= regs[rc].intVal)
of opcLtInt:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].intVal < regs[rc].intVal)
of opcEqFloat:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].floatVal == regs[rc].floatVal)
of opcLeFloat:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].floatVal <= regs[rc].floatVal)
of opcLtFloat:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].floatVal < regs[rc].floatVal)
of opcLeu:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].intVal <=% regs[rc].intVal)
of opcLtu:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].intVal <% regs[rc].intVal)
of opcEqRef:
- decodeBC(nkIntLit)
- regs[ra].intVal = ord((regs[rb].kind == nkNilLit and
- regs[rc].kind == nkNilLit) or
- regs[rb].sons == regs[rc].sons)
+ decodeBC(rkInt)
+ regs[ra].intVal = ord((regs[rb].node.kind == nkNilLit and
+ regs[rc].node.kind == nkNilLit) or
+ regs[rb].node == regs[rc].node)
of opcEqNimrodNode:
- decodeBC(nkIntLit)
- regs[ra].intVal = ord(regs[rb].skipMeta == regs[rc].skipMeta)
+ decodeBC(rkInt)
+ regs[ra].intVal = ord(regs[rb].node == regs[rc].node)
of opcXor:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal)
of opcNot:
- decodeB(nkIntLit)
- assert regs[rb].kind == nkIntLit
+ decodeB(rkInt)
+ assert regs[rb].kind == rkInt
regs[ra].intVal = 1 - regs[rb].intVal
of opcUnaryMinusInt:
- decodeB(nkIntLit)
- assert regs[rb].kind == nkIntLit
+ decodeB(rkInt)
+ assert regs[rb].kind == rkInt
regs[ra].intVal = -regs[rb].intVal
of opcUnaryMinusFloat:
- decodeB(nkFloatLit)
- assert regs[rb].kind == nkFloatLit
+ decodeB(rkFloat)
+ assert regs[rb].kind == rkFloat
regs[ra].floatVal = -regs[rb].floatVal
of opcBitnotInt:
- decodeB(nkIntLit)
- assert regs[rb].kind == nkIntLit
+ decodeB(rkInt)
+ assert regs[rb].kind == rkInt
regs[ra].intVal = not regs[rb].intVal
of opcEqStr:
- decodeBC(nkIntLit)
- regs[ra].intVal = ord(regs[rb].strVal == regs[rc].strVal)
+ decodeBC(rkInt)
+ regs[ra].intVal = ord(regs[rb].node.strVal == regs[rc].node.strVal)
of opcLeStr:
- decodeBC(nkIntLit)
- regs[ra].intVal = ord(regs[rb].strVal <= regs[rc].strVal)
+ decodeBC(rkInt)
+ regs[ra].intVal = ord(regs[rb].node.strVal <= regs[rc].node.strVal)
of opcLtStr:
- decodeBC(nkIntLit)
- regs[ra].intVal = ord(regs[rb].strVal < regs[rc].strVal)
+ decodeBC(rkInt)
+ regs[ra].intVal = ord(regs[rb].node.strVal < regs[rc].node.strVal)
of opcLeSet:
- decodeBC(nkIntLit)
- regs[ra].intVal = ord(containsSets(regs[rb], regs[rc]))
+ decodeBC(rkInt)
+ regs[ra].intVal = ord(containsSets(regs[rb].node, regs[rc].node))
of opcEqSet:
- decodeBC(nkIntLit)
- regs[ra].intVal = ord(equalSets(regs[rb], regs[rc]))
+ decodeBC(rkInt)
+ regs[ra].intVal = ord(equalSets(regs[rb].node, regs[rc].node))
of opcLtSet:
- decodeBC(nkIntLit)
- let a = regs[rb]
- let b = regs[rc]
+ decodeBC(rkInt)
+ let a = regs[rb].node
+ let b = regs[rc].node
regs[ra].intVal = ord(containsSets(a, b) and not equalSets(a, b))
of opcMulSet:
- decodeBC(nkCurly)
- move(regs[ra].sons, nimsets.intersectSets(regs[rb], regs[rc]).sons)
+ decodeBC(rkNode)
+ move(regs[ra].node.sons,
+ nimsets.intersectSets(regs[rb].node, regs[rc].node).sons)
of opcPlusSet:
- decodeBC(nkCurly)
- move(regs[ra].sons, nimsets.unionSets(regs[rb], regs[rc]).sons)
+ decodeBC(rkNode)
+ move(regs[ra].node.sons,
+ nimsets.unionSets(regs[rb].node, regs[rc].node).sons)
of opcMinusSet:
- decodeBC(nkCurly)
- move(regs[ra].sons, nimsets.diffSets(regs[rb], regs[rc]).sons)
+ decodeBC(rkNode)
+ move(regs[ra].node.sons,
+ nimsets.diffSets(regs[rb].node, regs[rc].node).sons)
of opcSymdiffSet:
- decodeBC(nkCurly)
- move(regs[ra].sons, nimsets.symdiffSets(regs[rb], regs[rc]).sons)
+ decodeBC(rkNode)
+ move(regs[ra].node.sons,
+ nimsets.symdiffSets(regs[rb].node, regs[rc].node).sons)
of opcConcatStr:
- decodeBC(nkStrLit)
- regs[ra].strVal = getstr(regs[rb])
+ decodeBC(rkNode)
+ createStr regs[ra]
+ regs[ra].node.strVal = getstr(regs[rb])
for i in rb+1..rb+rc-1:
- regs[ra].strVal.add getstr(regs[i])
+ regs[ra].node.strVal.add getstr(regs[i])
of opcAddStrCh:
- decodeB(nkStrLit)
- regs[ra].strVal.add(regs[rb].intVal.chr)
+ decodeB(rkNode)
+ createStrKeepNode regs[ra]
+ regs[ra].node.strVal.add(regs[rb].intVal.chr)
of opcAddStrStr:
- decodeB(nkStrLit)
- regs[ra].strVal.add(regs[rb].strVal)
+ decodeB(rkNode)
+ createStrKeepNode regs[ra]
+ regs[ra].node.strVal.add(regs[rb].node.strVal)
of opcAddSeqElem:
- decodeB(nkBracket)
- regs[ra].add(copyTree(regs[rb]))
+ decodeB(rkNode)
+ if regs[ra].node.kind == nkBracket:
+ regs[ra].node.add(copyTree(regs[rb].regToNode))
+ else:
+ stackTrace(c, tos, pc, errNilAccess)
of opcEcho:
let rb = instr.regB
for i in ra..ra+rb-1:
- #if regs[i].kind != nkStrLit: debug regs[i]
- write(stdout, regs[i].strVal)
+ #if regs[i].kind != rkNode: debug regs[i]
+ write(stdout, regs[i].node.strVal)
writeln(stdout, "")
of opcContainsSet:
- decodeBC(nkIntLit)
- regs[ra].intVal = ord(inSet(regs[rb], regs[rc]))
+ decodeBC(rkInt)
+ regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))
of opcSubStr:
- decodeBC(nkStrLit)
+ decodeBC(rkNode)
inc pc
assert c.code[pc].opcode == opcSubStr
let rd = c.code[pc].regA
- regs[ra].strVal = substr(regs[rb].strVal, regs[rc].intVal.int,
- regs[rd].intVal.int)
+ createStr regs[ra]
+ regs[ra].node.strVal = substr(regs[rb].node.strVal,
+ regs[rc].intVal.int, regs[rd].intVal.int)
of opcRangeChck:
let rb = instr.regB
let rc = instr.regC
- if not (leValueConv(regs[rb], regs[ra]) and
- leValueConv(regs[ra], regs[rc])):
+ if not (leValueConv(regs[rb].regToNode, regs[ra].regToNode) and
+ leValueConv(regs[ra].regToNode, regs[rc].regToNode)):
stackTrace(c, tos, pc, errGenerated,
msgKindToString(errIllegalConvFromXtoY) % [
"unknown type" , "unknown type"])
@@ -644,8 +701,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
# dest = call regStart, n; where regStart = fn, arg1, ...
let rb = instr.regB
let rc = instr.regC
- let isClosure = regs[rb].kind == nkPar
- let prc = if not isClosure: regs[rb].sym else: regs[rb].sons[0].sym
+ let bb = regs[rb].node
+ let isClosure = bb.kind == nkPar
+ let prc = if not isClosure: bb.sym else: bb.sons[0].sym
if sfImportc in prc.flags:
if allowFFI notin c.features:
globalError(c.debug[pc], errGenerated, "VM not allowed to do FFI")
@@ -659,7 +717,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
rb+1, rc-1, c.debug[pc])
if newValue.kind != nkEmpty:
assert instr.opcode == opcIndCallAsgn
- asgnRef(regs[ra], newValue)
+ putIntoReg(regs[ra], newValue)
else:
globalError(c.debug[pc], errGenerated, "VM not built with FFI support")
elif prc.kind != skTemplate:
@@ -668,15 +726,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
newSeq(newFrame.slots, prc.offset)
if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro:
- newFrame.slots[0] = getNullValue(prc.typ.sons[0], prc.info)
- # pass every parameter by var (the language definition allows this):
+ putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info))
for i in 1 .. rc-1:
newFrame.slots[i] = regs[rb+i]
if isClosure:
- newFrame.slots[rc] = regs[rb].sons[1]
+ newFrame.slots[rc].kind = rkNode
+ newFrame.slots[rc].node = regs[rb].node.sons[1]
# allocate the temporaries:
- for i in rc+ord(isClosure) .. <prc.offset:
- newFrame.slots[i] = newNode(nkEmpty)
+ #for i in rc+ord(isClosure) .. <prc.offset:
+ # newFrame.slots[i] = newNode(nkEmpty)
tos = newFrame
move(regs, newFrame.slots)
# -1 for the following 'inc pc'
@@ -689,10 +747,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
c.module
var macroCall = newNodeI(nkCall, c.debug[pc])
macroCall.add(newSymNode(prc))
- for i in 1 .. rc-1: macroCall.add(regs[rb+i].skipMeta)
+ for i in 1 .. rc-1: macroCall.add(regs[rb+i].regToNode)
let a = evalTemplate(macroCall, prc, genSymOwner)
- ensureKind(nkMetaNode)
- setMeta(regs[ra], a)
+ ensureKind(rkNode)
+ regs[ra].node = a
of opcTJmp:
# jump Bx if A != 0
let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc'
@@ -712,7 +770,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
let branch = c.constants[instr.regBx-wordExcess]
var cond = false
for j in countup(0, sonsLen(branch) - 2):
- if overlap(regs[ra], branch.sons[j]):
+ if overlap(regs[ra].regToNode, branch.sons[j]):
cond = true
break
assert c.code[pc+1].opcode == opcFJmp
@@ -741,7 +799,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
bailOut(c, tos)
return
of opcRaise:
- let raised = regs[ra]
+ let raised = regs[ra].node
c.currentExceptionA = raised
c.exceptionInstr = pc
# -1 because of the following 'inc'
@@ -750,319 +808,345 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): PNode =
bailOut(c, tos)
return
of opcNew:
+ ensureKind(rkNode)
let typ = c.types[instr.regBx - wordExcess]
- regs[ra] = getNullValue(typ, regs[ra].info)
- regs[ra].flags.incl nfIsRef
+ regs[ra].node = getNullValue(typ, c.debug[pc])
+ regs[ra].node.flags.incl nfIsRef
of opcNewSeq:
let typ = c.types[instr.regBx - wordExcess]
inc pc
- ensureKind(nkBracket)
+ ensureKind(rkNode)
let instr2 = c.code[pc]
let count = regs[instr2.regA].intVal.int
- regs[ra].typ = typ
- newSeq(regs[ra].sons, count)
+ regs[ra].node = newNodeI(nkBracket, c.debug[pc])
+ regs[ra].node.typ = typ
+ newSeq(regs[ra].node.sons, count)
for i in 0 .. <count:
- regs[ra].sons[i] = getNullValue(typ.sons[0], regs[ra].info)
+ regs[ra].node.sons[i] = getNullValue(typ.sons[0], c.debug[pc])
of opcNewStr:
- decodeB(nkStrLit)
- regs[ra].strVal = newString(regs[rb].intVal.int)
+ decodeB(rkNode)
+ regs[ra].node = newNodeI(nkStrLit, c.debug[pc])
+ regs[ra].node.strVal = newString(regs[rb].intVal.int)
of opcLdImmInt:
# dest = immediate value
- decodeBx(nkIntLit)
+ decodeBx(rkInt)
regs[ra].intVal = rbx
of opcLdNull:
+ ensureKind(rkNode)
let typ = c.types[instr.regBx - wordExcess]
- regs[ra] = getNullValue(typ, c.debug[pc])
+ regs[ra].node = getNullValue(typ, c.debug[pc])
+ # opcLdNull really is the gist of the VM's problems: should it load
+ # a fresh null to regs[ra].node or to regs[ra].node[]? This really
+ # depends on whether regs[ra] represents the variable itself or wether
+ # it holds the indirection! Due to the way registers are re-used we cannot
+ # say for sure here! --> The codegen has to deal with it
+ # via 'genAsgnPatch'.
+ of opcLdNullReg:
+ let typ = c.types[instr.regBx - wordExcess]
+ if typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}).kind in {
+ tyFloat..tyFloat128}:
+ ensureKind(rkFloat)
+ regs[ra].floatVal = 0.0
+ else:
+ ensureKind(rkInt)
+ regs[ra].intVal = 0
of opcLdConst:
let rb = instr.regBx - wordExcess
- if regs[ra].isNil:
- regs[ra] = copyTree(c.constants.sons[rb])
+ let cnst = c.constants.sons[rb]
+ if fitsRegister(cnst.typ):
+ putIntoReg(regs[ra], cnst)
else:
- moveConst(regs[ra], c.constants.sons[rb])
+ ensureKind(rkNode)
+ regs[ra].node = cnst
of opcAsgnConst:
let rb = instr.regBx - wordExcess
- if regs[ra].isNil:
- regs[ra] = copyTree(c.constants.sons[rb])
+ let cnst = c.constants.sons[rb]
+ if fitsRegister(cnst.typ):
+ putIntoReg(regs[ra], cnst)
else:
- asgnComplex(regs[ra], c.constants.sons[rb])
+ ensureKind(rkNode)
+ regs[ra].node = cnst.copyTree
of opcLdGlobal:
let rb = instr.regBx - wordExcess - 1
- if regs[ra].isNil:
- regs[ra] = copyTree(c.globals.sons[rb])
- else:
- asgnComplex(regs[ra], c.globals.sons[rb])
+ ensureKind(rkNode)
+ regs[ra].node = c.globals.sons[rb]
of opcRepr:
- decodeB(nkStrLit)
- regs[ra].strVal = renderTree(regs[rb].skipMeta, {renderNoComments})
+ decodeB(rkNode)
+ createStr regs[ra]
+ regs[ra].node.strVal = renderTree(regs[rb].node, {renderNoComments})
of opcQuit:
if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
message(c.debug[pc], hintQuitCalled)
- quit(int(getOrdValue(regs[ra])))
+ quit(int(getOrdValue(regs[ra].regToNode)))
else:
- return nil
+ return TFullReg(kind: rkNone)
of opcSetLenStr:
- decodeB(nkStrLit)
- regs[ra].strVal.setLen(regs[rb].getOrdValue.int)
+ decodeB(rkNode)
+ createStrKeepNode regs[ra]
+ regs[ra].node.strVal.setLen(regs[rb].intVal.int)
of opcOf:
- decodeBC(nkIntLit)
+ decodeBC(rkInt)
let typ = c.types[regs[rc].intVal.int]
- regs[ra].intVal = ord(inheritanceDiff(regs[rb].typ, typ) >= 0)
+ regs[ra].intVal = ord(inheritanceDiff(regs[rb].node.typ, typ) >= 0)
of opcIs:
- decodeBC(nkIntLit)
- let t1 = regs[rb].typ.skipTypes({tyTypeDesc})
+ decodeBC(rkInt)
+ let t1 = regs[rb].node.typ.skipTypes({tyTypeDesc})
let t2 = c.types[regs[rc].intVal.int]
# XXX: This should use the standard isOpImpl
let match = if t2.kind == tyUserTypeClass: true
else: sameType(t1, t2)
regs[ra].intVal = ord(match)
of opcSetLenSeq:
- decodeB(nkBracket)
- let newLen = regs[rb].getOrdValue.int
- setLen(regs[ra].sons, newLen)
+ decodeB(rkNode)
+ let newLen = regs[rb].intVal.int
+ if regs[ra].node.isNil: stackTrace(c, tos, pc, errNilAccess)
+ else: setLen(regs[ra].node.sons, newLen)
of opcSwap, opcReset:
internalError(c.debug[pc], "too implement")
of opcIsNil:
- decodeB(nkIntLit)
- regs[ra].intVal = ord(regs[rb].skipMeta.kind == nkNilLit)
+ decodeB(rkInt)
+ regs[ra].intVal = ord(regs[rb].node.kind == nkNilLit)
of opcNBindSym:
- decodeBx(nkMetaNode)
- setMeta(regs[ra], copyTree(c.constants.sons[rbx]))
+ decodeBx(rkNode)
+ regs[ra].node = copyTree(c.constants.sons[rbx])
of opcNChild:
- decodeBC(nkMetaNode)
- if regs[rb].kind != nkMetaNode:
- internalError(c.debug[pc], "no MetaNode")
+ decodeBC(rkNode)
let idx = regs[rc].intVal.int
- let src = regs[rb].uast
+ let src = regs[rb].node
if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len:
- setMeta(regs[ra], src.sons[idx])
+ regs[ra].node = src.sons[idx]
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcNSetChild:
- decodeBC(nkMetaNode)
+ decodeBC(rkNode)
let idx = regs[rb].intVal.int
- var dest = regs[ra].uast
+ var dest = regs[ra].node
if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len:
- dest.sons[idx] = regs[rc].uast
+ dest.sons[idx] = regs[rc].node
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcNAdd:
- decodeBC(nkMetaNode)
- var u = regs[rb].uast
- u.add(regs[rc].uast)
- setMeta(regs[ra], u)
+ decodeBC(rkNode)
+ var u = regs[rb].node
+ u.add(regs[rc].node)
+ regs[ra].node = u
of opcNAddMultiple:
- decodeBC(nkMetaNode)
- let x = regs[rc]
- var u = regs[rb].uast
+ decodeBC(rkNode)
+ let x = regs[rc].node
+ var u = regs[rb].node
# XXX can be optimized:
- for i in 0.. <x.len: u.add(x.sons[i].skipMeta)
- setMeta(regs[ra], u)
+ for i in 0.. <x.len: u.add(x.sons[i])
+ regs[ra].node = u
of opcNKind:
- decodeB(nkIntLit)
- regs[ra].intVal = ord(regs[rb].uast.kind)
+ decodeB(rkInt)
+ regs[ra].intVal = ord(regs[rb].node.kind)
of opcNIntVal:
- decodeB(nkIntLit)
- let a = regs[rb].uast
+ decodeB(rkInt)
+ let a = regs[rb].node
case a.kind
of nkCharLit..nkInt64Lit: regs[ra].intVal = a.intVal
else: stackTrace(c, tos, pc, errFieldXNotFound, "intVal")
of opcNFloatVal:
- decodeB(nkFloatLit)
- let a = regs[rb].uast
+ decodeB(rkFloat)
+ let a = regs[rb].node
case a.kind
of nkFloatLit..nkFloat64Lit: regs[ra].floatVal = a.floatVal
else: stackTrace(c, tos, pc, errFieldXNotFound, "floatVal")
of opcNSymbol:
- decodeB(nkSym)
- let a = regs[rb].uast
+ decodeB(rkNode)
+ let a = regs[rb].node
if a.kind == nkSym:
- regs[ra].sym = a.sym
+ regs[ra].node = copyNode(a)
else:
stackTrace(c, tos, pc, errFieldXNotFound, "symbol")
of opcNIdent:
- decodeB(nkIdent)
- let a = regs[rb].uast
+ decodeB(rkNode)
+ let a = regs[rb].node
if a.kind == nkIdent:
- regs[ra].ident = a.ident
+ regs[ra].node = copyNode(a)
else:
stackTrace(c, tos, pc, errFieldXNotFound, "ident")
of opcNGetType:
internalError(c.debug[pc], "unknown opcode " & $instr.opcode)
of opcNStrVal:
- decodeB(nkStrLit)
- let a = regs[rb].uast
- case a.kind
- of nkStrLit..nkTripleStrLit: regs[ra].strVal = a.strVal
+ decodeB(rkNode)
+ createStr regs[ra]
+ let a = regs[rb].node
+ if a.kind in {nkStrLit..nkTripleStrLit}: regs[ra].node.strVal = a.strVal
else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
of opcSlurp:
- decodeB(nkStrLit)
- regs[ra].strVal = opSlurp(regs[rb].strVal, c.debug[pc], c.module)
+ decodeB(rkNode)
+ createStr regs[ra]
+ regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc],
+ c.module)
of opcGorge:
- decodeBC(nkStrLit)
- regs[ra].strVal = opGorge(regs[rb].strVal, regs[rc].strVal)
+ decodeBC(rkNode)
+ regs[ra].node.strVal = opGorge(regs[rb].node.strVal,
+ regs[rc].node.strVal)
of opcNError:
- stackTrace(c, tos, pc, errUser, regs[ra].strVal)
+ stackTrace(c, tos, pc, errUser, regs[ra].node.strVal)
of opcNWarning:
- message(c.debug[pc], warnUser, regs[ra].strVal)
+ message(c.debug[pc], warnUser, regs[ra].node.strVal)
of opcNHint:
- message(c.debug[pc], hintUser, regs[ra].strVal)
+ message(c.debug[pc], hintUser, regs[ra].node.strVal)
of opcParseExprToAst:
- decodeB(nkMetaNode)
+ decodeB(rkNode)
# c.debug[pc].line.int - countLines(regs[rb].strVal) ?
- let ast = parseString(regs[rb].strVal, c.debug[pc].toFilename,
+ let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFilename,
c.debug[pc].line.int)
if sonsLen(ast) != 1:
globalError(c.debug[pc], errExprExpected, "multiple statements")
- setMeta(regs[ra], ast.sons[0])
+ regs[ra].node = ast.sons[0]
of opcParseStmtToAst:
- decodeB(nkMetaNode)
- let ast = parseString(regs[rb].strVal, c.debug[pc].toFilename,
+ decodeB(rkNode)
+ let ast = parseString(regs[rb].node.strVal, c.debug[pc].toFilename,
c.debug[pc].line.int)
- setMeta(regs[ra], ast)
+ regs[ra].node = ast
of opcCallSite:
- ensureKind(nkMetaNode)
- if c.callsite != nil: setMeta(regs[ra], c.callsite)
+ ensureKind(rkNode)
+ if c.callsite != nil: regs[ra].node = c.callsite
else: stackTrace(c, tos, pc, errFieldXNotFound, "callsite")
of opcNLineInfo:
- decodeB(nkStrLit)
- let n = regs[rb]
- regs[ra].strVal = n.info.toFileLineCol
- regs[ra].info = c.debug[pc]
+ decodeB(rkNode)
+ let n = regs[rb].node
+ createStr regs[ra]
+ regs[ra].node.strVal = n.info.toFileLineCol
+ regs[ra].node.info = c.debug[pc]
of opcEqIdent:
- decodeBC(nkIntLit)
- if regs[rb].kind == nkIdent and regs[rc].kind == nkIdent:
- regs[ra].intVal = ord(regs[rb].ident.id == regs[rc].ident.id)
+ decodeBC(rkInt)
+ if regs[rb].node.kind == nkIdent and regs[rc].node.kind == nkIdent:
+ regs[ra].intVal = ord(regs[rb].node.ident.id == regs[rc].node.ident.id)
else:
regs[ra].intVal = 0
of opcStrToIdent:
- decodeB(nkIdent)
- if regs[rb].kind notin {nkStrLit..nkTripleStrLit}:
+ decodeB(rkNode)
+ if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}:
stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
else:
- regs[ra].info = c.debug[pc]
- regs[ra].ident = getIdent(regs[rb].strVal)
+ regs[ra].node = newNodeI(nkIdent, c.debug[pc])
+ regs[ra].node.ident = getIdent(regs[rb].node.strVal)
of opcIdentToStr:
- decodeB(nkStrLit)
- let a = regs[rb]
- regs[ra].info = c.debug[pc]
+ decodeB(rkNode)
+ let a = regs[rb].node
+ createStr regs[ra]
+ regs[ra].node.info = c.debug[pc]
if a.kind == nkSym:
- regs[ra].strVal = a.sym.name.s
+ regs[ra].node.strVal = a.sym.name.s
elif a.kind == nkIdent:
- regs[ra].strVal = a.ident.s
+ regs[ra].node.strVal = a.ident.s
else:
stackTrace(c, tos, pc, errFieldXNotFound, "ident")
of opcSetType:
- regs[ra].typ = c.types[instr.regBx - wordExcess]
+ if regs[ra].kind != rkNode:
+ internalError(c.debug[pc], "cannot set type")
+ regs[ra].node.typ = c.types[instr.regBx - wordExcess]
of opcConv:
let rb = instr.regB
inc pc
- let typ = c.types[c.code[pc].regBx - wordExcess]
- if opConv(regs[ra], regs[rb], typ):
+ let desttyp = c.types[c.code[pc].regBx - wordExcess]
+ inc pc
+ let srctyp = c.types[c.code[pc].regBx - wordExcess]
+
+ if opConv(regs[ra], regs[rb], desttyp, srctyp):
stackTrace(c, tos, pc, errGenerated,
msgKindToString(errIllegalConvFromXtoY) % [
- "unknown type" , "unknown type"])
+ typeToString(srctyp), typeToString(desttyp)])
of opcCast:
let rb = instr.regB
inc pc
- let typ = c.types[c.code[pc].regBx - wordExcess]
+ let desttyp = c.types[c.code[pc].regBx - wordExcess]
+ inc pc
+ let srctyp = c.types[c.code[pc].regBx - wordExcess]
+
when hasFFI:
- let dest = fficast(regs[rb], typ)
+ let dest = fficast(regs[rb], desttyp)
asgnRef(regs[ra], dest)
else:
globalError(c.debug[pc], "cannot evaluate cast")
of opcNSetIntVal:
- decodeB(nkMetaNode)
- var dest = regs[ra].uast
+ decodeB(rkNode)
+ var dest = regs[ra].node
if dest.kind in {nkCharLit..nkInt64Lit} and
- regs[rb].kind in {nkCharLit..nkInt64Lit}:
+ regs[rb].kind in {rkInt}:
dest.intVal = regs[rb].intVal
else:
stackTrace(c, tos, pc, errFieldXNotFound, "intVal")
of opcNSetFloatVal:
- decodeB(nkMetaNode)
- var dest = regs[ra].uast
+ decodeB(rkNode)
+ var dest = regs[ra].node
if dest.kind in {nkFloatLit..nkFloat64Lit} and
- regs[rb].kind in {nkFloatLit..nkFloat64Lit}:
+ regs[rb].kind in {rkFloat}:
dest.floatVal = regs[rb].floatVal
else:
stackTrace(c, tos, pc, errFieldXNotFound, "floatVal")
of opcNSetSymbol:
- decodeB(nkMetaNode)
- var dest = regs[ra].uast
- if dest.kind == nkSym and regs[rb].kind == nkSym:
- dest.sym = regs[rb].sym
+ decodeB(rkNode)
+ var dest = regs[ra].node
+ if dest.kind == nkSym and regs[rb].node.kind == nkSym:
+ dest.sym = regs[rb].node.sym
else:
stackTrace(c, tos, pc, errFieldXNotFound, "symbol")
of opcNSetIdent:
- decodeB(nkMetaNode)
- var dest = regs[ra].uast
- if dest.kind == nkIdent and regs[rb].kind == nkIdent:
- dest.ident = regs[rb].ident
+ decodeB(rkNode)
+ var dest = regs[ra].node
+ if dest.kind == nkIdent and regs[rb].node.kind == nkIdent:
+ dest.ident = regs[rb].node.ident
else:
stackTrace(c, tos, pc, errFieldXNotFound, "ident")
of opcNSetType:
- decodeB(nkMetaNode)
- let b = regs[rb].skipMeta
+ decodeB(rkNode)
+ let b = regs[rb].node
internalAssert b.kind == nkSym and b.sym.kind == skType
- regs[ra].uast.typ = b.sym.typ
+ internalAssert regs[ra].node != nil
+ regs[ra].node.typ = b.sym.typ
of opcNSetStrVal:
- decodeB(nkMetaNode)
- var dest = regs[ra].uast
+ decodeB(rkNode)
+ var dest = regs[ra].node
if dest.kind in {nkStrLit..nkTripleStrLit} and
- regs[rb].kind in {nkStrLit..nkTripleStrLit}:
- dest.strVal = regs[rb].strVal
+ regs[rb].kind in {rkNode}:
+ dest.strVal = regs[rb].node.strVal
else:
stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
of opcNNewNimNode:
- decodeBC(nkMetaNode)
+ decodeBC(rkNode)
var k = regs[rb].intVal
- if k < 0 or k > ord(high(TNodeKind)) or k == ord(nkMetaNode):
+ if k < 0 or k > ord(high(TNodeKind)):
internalError(c.debug[pc],
"request to create a NimNode of invalid kind")
- let cc = regs[rc].skipMeta
- setMeta(regs[ra], newNodeI(TNodeKind(int(k)),
- if cc.kind == nkNilLit: c.debug[pc] else: cc.info))
- regs[ra].sons[0].flags.incl nfIsRef
+ let cc = regs[rc].node
+ regs[ra].node = newNodeI(TNodeKind(int(k)),
+ if cc.kind == nkNilLit: c.debug[pc] else: cc.info)
+ regs[ra].node.flags.incl nfIsRef
of opcNCopyNimNode:
- decodeB(nkMetaNode)
- setMeta(regs[ra], copyNode(regs[rb]))
+ decodeB(rkNode)
+ regs[ra].node = copyNode(regs[rb].node)
of opcNCopyNimTree:
- decodeB(nkMetaNode)
- setMeta(regs[ra], copyTree(regs[rb]))
+ decodeB(rkNode)
+ regs[ra].node = copyTree(regs[rb].node)
of opcNDel:
- decodeBC(nkMetaNode)
+ decodeBC(rkNode)
let bb = regs[rb].intVal.int
for i in countup(0, regs[rc].intVal.int-1):
- delSon(regs[ra].uast, bb)
+ delSon(regs[ra].node, bb)
of opcGenSym:
- decodeBC(nkMetaNode)
+ decodeBC(rkNode)
let k = regs[rb].intVal
- let name = if regs[rc].strVal.len == 0: ":tmp" else: regs[rc].strVal
+ let name = if regs[rc].node.strVal.len == 0: ":tmp"
+ else: regs[rc].node.strVal
if k < 0 or k > ord(high(TSymKind)):
internalError(c.debug[pc], "request to create symbol of invalid kind")
var sym = newSym(k.TSymKind, name.getIdent, c.module, c.debug[pc])
incl(sym.flags, sfGenSym)
- setMeta(regs[ra], newSymNode(sym))
+ regs[ra].node = newSymNode(sym)
of opcTypeTrait:
# XXX only supports 'name' for now; we can use regC to encode the
# type trait operation
- decodeB(nkStrLit)
- var typ = regs[rb].typ
+ decodeB(rkNode)
+ var typ = regs[rb].node.typ
internalAssert typ != nil
while typ.kind == tyTypeDesc and typ.len > 0: typ = typ.sons[0]
- regs[ra].strVal = typ.typeToString(preferExported)
- of opcGlobalOnce:
- let rb = instr.regBx
- if c.globals.sons[rb - wordExcess - 1].kind != nkEmpty:
- # skip initialization instructions:
- while true:
- inc pc
- if c.code[pc].opcode in {opcWrGlobal, opcWrGlobalRef} and
- c.code[pc].regBx == rb:
- break
- of opcGlobalAlias:
- let rb = instr.regBx - wordExcess - 1
- regs[ra] = c.globals.sons[rb]
+ createStr regs[ra]
+ regs[ra].node.strVal = typ.typeToString(preferExported)
inc pc
proc fixType(result, n: PNode) {.inline.} =
@@ -1074,8 +1158,7 @@ proc fixType(result, n: PNode) {.inline.} =
proc execute(c: PCtx, start: int): PNode =
var tos = PStackFrame(prc: nil, comesFrom: 0, next: nil)
newSeq(tos.slots, c.prc.maxSlots)
- for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
- result = rawExecute(c, start, tos)
+ result = rawExecute(c, start, tos).regToNode
proc evalStmt*(c: PCtx, n: PNode) =
let n = transformExpr(c.module, n)
@@ -1091,7 +1174,6 @@ proc evalExpr*(c: PCtx, n: PNode): PNode =
assert c.code[start].opcode != opcEof
result = execute(c, start)
if not result.isNil:
- result = result.skipMeta
fixType(result, n)
# for now we share the 'globals' environment. XXX Coming soon: An API for
@@ -1138,8 +1220,8 @@ proc evalConstExprAux(module, prc: PSym, n: PNode, mode: TEvalMode): PNode =
assert c.code[start].opcode != opcEof
var tos = PStackFrame(prc: prc, comesFrom: 0, next: nil)
newSeq(tos.slots, c.prc.maxSlots)
- for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
- result = rawExecute(c, start, tos)
+ #for i in 0 .. <c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty)
+ result = rawExecute(c, start, tos).regToNode
fixType(result, n)
proc evalConstExpr*(module: PSym, e: PNode): PNode =
@@ -1151,13 +1233,13 @@ proc evalStaticExpr*(module: PSym, e: PNode, prc: PSym): PNode =
proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) =
discard evalConstExprAux(module, prc, e, emStaticStmt)
+proc setupCompileTimeVar*(module: PSym, n: PNode) =
+ discard evalConstExprAux(module, nil, n, emStaticStmt)
+
proc setupMacroParam(x: PNode): PNode =
result = x
if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1]
- let y = result
- y.flags.incl nfIsRef
- result = newNode(nkMetaNode)
- result.add y
+ result.flags.incl nfIsRef
result.typ = x.typ
var evalMacroCounter: int
@@ -1183,15 +1265,16 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
# doesn't end up in the parameter:
#InternalAssert tos.slots.len >= L
# return value:
- tos.slots[0] = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
+ tos.slots[0].kind = rkNode
+ tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
# setup parameters:
for i in 1 .. < min(tos.slots.len, L):
- tos.slots[i] = setupMacroParam(n.sons[i])
+ tos.slots[i].kind = rkNode
+ tos.slots[i].node = setupMacroParam(n.sons[i])
# temporary storage:
- for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
- result = rawExecute(c, start, tos)
+ #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
+ result = rawExecute(c, start, tos).regToNode
if cyclicTree(result): globalError(n.info, errCyclicTree)
dec(evalMacroCounter)
- if result != nil:
- result = result.skipMeta
c.callsite = nil
+ #debug result
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 87159c813..102fc3024 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -8,7 +8,7 @@
#
## This module contains the type definitions for the new evaluation engine.
-## An instruction is 1-2 int32s in memory, it is a register based VM.
+## An instruction is 1-3 int32s in memory, it is a register based VM.
import ast, passes, msgs, intsets
@@ -32,17 +32,17 @@ type
opcAsgnFloat,
opcAsgnRef,
opcAsgnComplex,
+ opcRegToNode,
+ opcNodeToReg,
opcLdArr, # a = b[c]
- opcLdArrRef,
opcWrArr, # a[b] = c
- opcWrArrRef,
opcLdObj, # a = b.c
- opcLdObjRef,
opcWrObj, # a.b = c
- opcWrObjRef,
- opcAddr,
- opcDeref,
+ opcAddrReg,
+ opcAddrNode,
+ opcLdDeref,
+ opcWrDeref,
opcWrStrIdx,
opcLdStrIdx, # a = b[c]
@@ -117,15 +117,13 @@ type
opcNew,
opcNewSeq,
opcLdNull, # dest = nullvalue(types[Bx])
+ opcLdNullReg,
opcLdConst, # dest = constants[Bx]
opcAsgnConst, # dest = copy(constants[Bx])
opcLdGlobal, # dest = globals[Bx]
+
opcLdImmInt, # dest = immediate value
opcNBindSym,
- opcWrGlobal,
- opcWrGlobalRef,
- opcGlobalAlias, # load an alias to a global into a register
- opcGlobalOnce, # used to introduce an assignment to a global once
opcSetType, # dest.typ = types[Bx]
opcTypeTrait
@@ -159,14 +157,13 @@ type
slotTempInt, # some temporary int
slotTempFloat, # some temporary float
slotTempStr, # some temporary string
- slotTempComplex # some complex temporary (n.sons field is used)
+ slotTempComplex # some complex temporary (s.node field is used)
PProc* = ref object
blocks*: seq[TBlock] # blocks; temp data structure
+ sym*: PSym
slots*: array[TRegister, tuple[inUse: bool, kind: TSlotKind]]
maxSlots*: int
- globals*: array[TRegister, int] # hack: to support passing globals byref
- # we map a slot persistently to a global
PCtx* = ref TCtx
TCtx* = object of passes.TPassContext # code gen context
@@ -183,6 +180,7 @@ type
callsite*: PNode
mode*: TEvalMode
features*: TSandboxFlags
+ traceActive*: bool
TPosition* = distinct int
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index a6d044e24..b1a751723 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -139,25 +139,12 @@ proc getTemp(c: PCtx; typ: PType): TRegister =
if not c.slots[i].inUse:
c.slots[i] = (inUse: true, kind: k)
return TRegister(i)
+ if c.maxSlots >= high(TRegister):
+ internalError("cannot generate code; too many registers required")
result = TRegister(c.maxSlots)
c.slots[c.maxSlots] = (inUse: true, kind: k)
inc c.maxSlots
-proc getGlobalSlot(c: PCtx; n: PNode; s: PSym): TRegister =
- let p = c.prc
- for i in 0 .. p.maxSlots-1:
- if p.globals[i] == s.id: return TRegister(i)
-
- result = TRegister(p.maxSlots)
- p.slots[p.maxSlots] = (inUse: true, kind: slotFixedVar)
- p.globals[p.maxSlots] = s.id
- inc p.maxSlots
- # XXX this is still not correct! We need to load the global in a proc init
- # section, otherwise control flow could lead to a usage before it's been
- # loaded.
- c.gABx(n, opcGlobalAlias, result, s.position)
- # XXX add some internal asserts here
-
proc freeTemp(c: PCtx; r: TRegister) =
let c = c.prc
if c.slots[r].kind >= slotSomeTemp: c.slots[r].inUse = false
@@ -321,9 +308,12 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
c.gen(n.sons[2], dest)
c.patch(L1)
+proc nilLiteral(n: PNode): PNode =
+ result = n
+
proc rawGenLiteral(c: PCtx; n: PNode): int =
result = c.constants.len
- c.constants.add n
+ c.constants.add n.nilLiteral
internalAssert result < 0x7fff
proc sameConstant*(a, b: PNode): bool =
@@ -461,21 +451,37 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
c.gABC(n, opcIndCallAsgn, dest, x, n.len)
c.freeTempRange(x, n.len)
+template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
+
proc needsAsgnPatch(n: PNode): bool =
- n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr}
+ n.kind in {nkBracketExpr, nkDotExpr, nkCheckedFieldExpr,
+ nkDerefExpr, nkHiddenDeref} or (n.kind == nkSym and n.sym.isGlobal)
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
case le.kind
of nkBracketExpr:
- let dest = c.genx(le.sons[0])
+ let dest = c.genx(le.sons[0], {gfAddrOf})
let idx = c.genx(le.sons[1])
- c.gABC(le, opcWrArrRef, dest, idx, value)
+ c.gABC(le, opcWrArr, dest, idx, value)
+ c.freeTemp(dest)
+ c.freeTemp(idx)
of nkDotExpr, nkCheckedFieldExpr:
# XXX field checks here
let left = if le.kind == nkDotExpr: le else: le.sons[0]
- let dest = c.genx(left.sons[0])
+ let dest = c.genx(left.sons[0], {gfAddrOf})
let idx = c.genx(left.sons[1])
- c.gABC(left, opcWrObjRef, dest, idx, value)
+ c.gABC(left, opcWrObj, dest, idx, value)
+ c.freeTemp(dest)
+ c.freeTemp(idx)
+ of nkDerefExpr, nkHiddenDeref:
+ let dest = c.genx(le.sons[0], {gfAddrOf})
+ c.gABC(le, opcWrDeref, dest, value)
+ c.freeTemp(dest)
+ of nkSym:
+ if le.sym.isGlobal:
+ let dest = c.genx(le, {gfAddrOf})
+ c.gABC(le, opcWrDeref, dest, value)
+ c.freeTemp(dest)
else:
discard
@@ -585,10 +591,10 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
let tmp = c.genx(arg)
- c.gABx(n, opcSetType, tmp, genType(c, arg.typ))
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opc, dest, tmp)
c.gABx(n, opc, 0, genType(c, n.typ))
+ c.gABx(n, opc, 0, genType(c, arg.typ))
c.freeTemp(tmp)
proc genCard(c: PCtx; n: PNode; dest: var TDest) =
@@ -614,6 +620,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.genAddSubInt(n, dest, opcAddInt)
of mInc, mDec:
unused(n, dest)
+ # XXX generates inefficient code for globals
var d = c.genx(n.sons[1]).TDest
c.genAddSubInt(n, d, if m == mInc: opcAddInt else: opcSubInt)
c.genAsgnPatch(n.sons[1], d)
@@ -627,6 +634,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
c.genNewSeq(n)
of mNewString:
genUnaryABC(c, n, dest, opcNewStr)
+ # XXX buggy
of mNewStringOfCap:
# we ignore the 'cap' argument and translate it as 'newString(0)'.
# eval n.sons[1] for possible side effects:
@@ -635,6 +643,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcNewStr, dest, tmp)
c.freeTemp(tmp)
+ # XXX buggy
of mLengthOpenArray, mLengthArray, mLengthSeq:
genUnaryABI(c, n, dest, opcLenSeq)
of mLengthStr:
@@ -896,6 +905,10 @@ const
tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
+proc fitsRegister*(t: PType): bool =
+ t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
+ tyRange, tyEnum, tyBool, tyInt..tyUInt64}
+
proc requiresCopy(n: PNode): bool =
if n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind in atomicTypes:
result = false
@@ -907,22 +920,33 @@ proc requiresCopy(n: PNode): bool =
proc unneededIndirection(n: PNode): bool =
n.typ.skipTypes(abstractInst-{tyTypeDesc}).kind == tyRef
-proc skipDeref(n: PNode): PNode =
- if n.kind in {nkDerefExpr, nkHiddenDeref} and unneededIndirection(n.sons[0]):
- result = n.sons[0]
- else:
- result = n
-
proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
flags: TGenFlags) =
# a nop for certain types
- let flags = if opc == opcAddr: flags+{gfAddrOf} else: flags
- if unneededIndirection(n.sons[0]):
- gen(c, n.sons[0], dest, flags)
+ let isAddr = opc in {opcAddrNode, opcAddrReg}
+ let newflags = if isAddr: flags+{gfAddrOf} else: flags
+ # consider:
+ # proc foo(f: var ref int) =
+ # f = new(int)
+ # proc blah() =
+ # var x: ref int
+ # foo x
+ #
+ # The type of 'f' is 'var ref int' and of 'x' is 'ref int'. Hence for
+ # nkAddr we must not use 'unneededIndirection', but for deref we use it.
+ if not isAddr and unneededIndirection(n.sons[0]):
+ gen(c, n.sons[0], dest, newflags)
else:
- let tmp = c.genx(n.sons[0], flags)
+ let tmp = c.genx(n.sons[0], newflags)
if dest < 0: dest = c.getTemp(n.typ)
- gABC(c, n, opc, dest, tmp)
+ if not isAddr:
+ gABC(c, n, opc, dest, tmp)
+ if gfAddrOf notin flags and fitsRegister(n.typ):
+ c.gABC(n, opcNodeToReg, dest, dest)
+ elif c.prc.slots[tmp].kind >= slotTempUnknown:
+ gABC(c, n, opcAddrNode, dest, tmp)
+ else:
+ gABC(c, n, opcAddrReg, dest, tmp)
c.freeTemp(tmp)
proc whichAsgnOpc(n: PNode): TOpcode =
@@ -940,8 +964,7 @@ proc whichAsgnOpc(n: PNode): TOpcode =
proc isRef(t: PType): bool = t.skipTypes(abstractRange-{tyTypeDesc}).kind == tyRef
-proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode =
- if isRef(n.typ): succ(opc) else: opc
+proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
let tmp = c.genx(ri)
@@ -949,8 +972,6 @@ proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
gABC(c, ri, whichAsgnOpc(ri), dest, tmp)
c.freeTemp(tmp)
-template isGlobal(s: PSym): bool = sfGlobal in s.flags and s.kind != skForVar
-
proc setSlot(c: PCtx; v: PSym) =
# XXX generate type initialization here?
if v.position == 0:
@@ -959,40 +980,67 @@ proc setSlot(c: PCtx; v: PSym) =
kind: if v.kind == skLet: slotFixedLet else: slotFixedVar)
inc c.prc.maxSlots
+proc cannotEval(n: PNode) {.noinline.} =
+ globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
+ n.renderTree)
+
+proc isOwnedBy(a, b: PSym): bool =
+ var a = a.owner
+ while a != nil and a.kind != skModule:
+ if a == b: return true
+ a = a.owner
+
+proc checkCanEval(c: PCtx; n: PNode) =
+ # we need to ensure that we don't evaluate 'x' here:
+ # proc foo() = var x ...
+ let s = n.sym
+ if s.position == 0:
+ if s.kind in {skVar, skTemp, skLet, skParam, skResult} and
+ not s.isOwnedBy(c.prc.sym) and s.owner != c.module:
+ cannotEval(n)
+
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
case le.kind
of nkBracketExpr:
- let dest = c.genx(le.sons[0])
+ let dest = c.genx(le.sons[0], {gfAddrOf})
let idx = c.genx(le.sons[1])
let tmp = c.genx(ri)
if le.sons[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
tyString, tyCString}:
c.gABC(le, opcWrStrIdx, dest, idx, tmp)
else:
- c.gABC(le, whichAsgnOpc(le, opcWrArr), dest, idx, tmp)
+ c.gABC(le, opcWrArr, dest, idx, tmp)
c.freeTemp(tmp)
of nkDotExpr, nkCheckedFieldExpr:
# XXX field checks here
let left = if le.kind == nkDotExpr: le else: le.sons[0]
- let dest = c.genx(left.sons[0])
+ let dest = c.genx(left.sons[0], {gfAddrOf})
let idx = c.genx(left.sons[1])
let tmp = c.genx(ri)
- c.gABC(left, whichAsgnOpc(left, opcWrObj), dest, idx, tmp)
+ c.gABC(left, opcWrObj, dest, idx, tmp)
+ c.freeTemp(tmp)
+ of nkDerefExpr, nkHiddenDeref:
+ let dest = c.genx(le.sons[0], {gfAddrOf})
+ let tmp = c.genx(ri)
+ c.gABC(le, opcWrDeref, dest, tmp)
c.freeTemp(tmp)
of nkSym:
let s = le.sym
+ checkCanEval(c, le)
if s.isGlobal:
withTemp(tmp, le.typ):
- gen(c, ri, tmp)
- c.gABx(le, whichAsgnOpc(le, opcWrGlobal), tmp, s.position)
+ c.gen(le, tmp, {gfAddrOf})
+ let val = c.genx(ri)
+ c.gABC(le, opcWrDeref, tmp, val)
+ c.freeTemp(val)
else:
- if s.kind == skForVar and c.mode == emRepl: c.setSlot s
+ if s.kind == skForVar: c.setSlot s
internalAssert s.position > 0 or (s.position == 0 and
s.kind in {skParam,skResult})
var dest: TRegister = s.position + ord(s.kind == skParam)
gen(c, ri, dest)
else:
- let dest = c.genx(le)
+ let dest = c.genx(le, {gfAddrOf})
genAsgn(c, dest, ri, requiresCopy)
proc genLit(c: PCtx; n: PNode; dest: var TDest) =
@@ -1018,22 +1066,22 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
localError(info, errGenerated,
"cannot 'importc' variable at compile time")
-proc cannotEval(n: PNode) {.noinline.} =
- globalError(n.info, errGenerated, "cannot evaluate at compile time: " &
- n.renderTree)
+proc getNullValue*(typ: PType, info: TLineInfo): PNode
proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
- c.globals.add(emptyNode.copyNode)
+ c.globals.add(getNullValue(s.typ, n.info))
s.position = c.globals.len
# This is rather hard to support, due to the laziness of the VM code
# generator. See tests/compile/tmacro2 for why this is necesary:
# var decls{.compileTime.}: seq[PNimrodNode] = @[]
- c.gABx(n, opcGlobalOnce, 0, s.position)
+ let dest = c.getTemp(s.typ)
+ c.gABx(n, opcLdGlobal, dest, s.position)
let tmp = c.genx(s.ast)
- c.gABx(n, whichAsgnOpc(n, opcWrGlobal), tmp, s.position)
+ c.gABC(n, opcWrDeref, dest, tmp)
+ c.freeTemp(dest)
c.freeTemp(tmp)
-proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
+proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
let s = n.sym
if s.isGlobal:
if sfCompileTime in s.flags or c.mode == emRepl:
@@ -1043,9 +1091,12 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest) =
if s.position == 0:
if sfImportc in s.flags: c.importcSym(n.info, s)
else: genGlobalInit(c, n, s)
- if dest < 0:
- dest = c.getGlobalSlot(n, s)
- #c.gABx(n, opcAliasGlobal, dest, s.position)
+ if dest < 0: dest = c.getTemp(n.typ)
+ if gfAddrOf notin flags and fitsRegister(s.typ):
+ var cc = c.getTemp(n.typ)
+ c.gABx(n, opcLdGlobal, cc, s.position)
+ c.gABC(n, opcNodeToReg, dest, cc)
+ c.freeTemp(cc)
else:
c.gABx(n, opcLdGlobal, dest, s.position)
else:
@@ -1066,7 +1117,13 @@ proc genAccess(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
let a = c.genx(n.sons[0], flags)
let b = c.genx(n.sons[1], {})
if dest < 0: dest = c.getTemp(n.typ)
- c.gABC(n, (if gfAddrOf in flags: succ(opc) else: opc), dest, a, b)
+ if gfAddrOf notin flags and fitsRegister(n.typ):
+ var cc = c.getTemp(n.typ)
+ c.gABC(n, opc, cc, a, b)
+ c.gABC(n, opcNodeToReg, dest, cc)
+ c.freeTemp(cc)
+ else:
+ c.gABC(n, opc, dest, a, b)
c.freeTemp(a)
c.freeTemp(b)
@@ -1084,7 +1141,6 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
else:
genAccess(c, n, dest, opcLdArr, flags)
-proc getNullValue*(typ: PType, info: TLineInfo): PNode
proc getNullValueAux(obj: PNode, result: PNode) =
case obj.kind
of nkRecList:
@@ -1137,6 +1193,9 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkCurly, info, t)
else: internalError("getNullValue: " & $t.kind)
+proc ldNullOpcode(t: PType): TOpcode =
+ if fitsRegister(t): opcLdNullReg else: opcLdNull
+
proc genVarSection(c: PCtx; n: PNode) =
for a in n:
if a.kind == nkCommentStmt: continue
@@ -1147,12 +1206,14 @@ proc genVarSection(c: PCtx; n: PNode) =
setSlot(c, a[i].sym)
# v = t[i]
var v: TDest = -1
- genRdVar(c, a[i], v)
- c.gABC(n, opcLdObj, v, tmp, i)
+ checkCanEval(c, a[i])
+ genRdVar(c, a[i], v, {gfAddrOf})
+ c.gABC(n, opcWrObj, v, tmp, i)
# XXX globals?
c.freeTemp(tmp)
elif a.sons[0].kind == nkSym:
let s = a.sons[0].sym
+ checkCanEval(c, a.sons[0])
if s.isGlobal:
if s.position == 0:
if sfImportc in s.flags: c.importcSym(a.info, s)
@@ -1160,27 +1221,28 @@ proc genVarSection(c: PCtx; n: PNode) =
let sa = if s.ast.isNil: getNullValue(s.typ, a.info) else: s.ast
c.globals.add(sa)
s.position = c.globals.len
- # "Once support" is unnecessary here
if a.sons[2].kind == nkEmpty:
when false:
withTemp(tmp, s.typ):
c.gABx(a, opcLdNull, tmp, c.genType(s.typ))
c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
else:
- let tmp = genx(c, a.sons[2])
- c.gABx(a, whichAsgnOpc(a.sons[0], opcWrGlobal), tmp, s.position)
+ let tmp = c.genx(a.sons[0], {gfAddrOf})
+ let val = c.genx(a.sons[2])
+ c.gABC(a, opcWrDeref, tmp, val)
+ c.freeTemp(val)
c.freeTemp(tmp)
else:
setSlot(c, s)
if a.sons[2].kind == nkEmpty:
- c.gABx(a, opcLdNull, s.position, c.genType(s.typ))
+ c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
else:
gen(c, a.sons[2], s.position.TRegister)
else:
# assign to a.sons[0]; happens for closures
if a.sons[2].kind == nkEmpty:
let tmp = genx(c, a.sons[0])
- c.gABx(a, opcLdNull, tmp, c.genType(a.sons[0].typ))
+ c.gABx(a, ldNullOpcode(a[0].typ), tmp, c.genType(a.sons[0].typ))
c.freeTemp(tmp)
else:
genAsgn(c, a.sons[0], a.sons[2], true)
@@ -1188,10 +1250,19 @@ proc genVarSection(c: PCtx; n: PNode) =
proc genArrayConstr(c: PCtx, n: PNode, dest: var TDest) =
if dest < 0: dest = c.getTemp(n.typ)
c.gABx(n, opcLdNull, dest, c.genType(n.typ))
+
+ let intType = getSysType(tyInt)
+ let seqType = n.typ.skipTypes(abstractVar-{tyTypeDesc})
+ if seqType.kind == tySequence:
+ var tmp = c.getTemp(intType)
+ c.gABx(n, opcLdImmInt, tmp, n.len)
+ c.gABx(n, opcNewSeq, dest, c.genType(seqType))
+ c.gABx(n, opcNewSeq, tmp, 0)
+ c.freeTemp(tmp)
+
if n.len > 0:
- let intType = getSysType(tyInt)
var tmp = getTemp(c, intType)
- c.gABx(n, opcLdNull, tmp, c.genType(intType))
+ c.gABx(n, opcLdNullReg, tmp, c.genType(intType))
for x in n:
let a = c.genx(x)
c.gABC(n, whichAsgnOpc(x, opcWrArr), dest, tmp, a)
@@ -1255,10 +1326,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
case n.kind
of nkSym:
let s = n.sym
+ checkCanEval(c, n)
case s.kind
of skVar, skForVar, skTemp, skLet, skParam, skResult:
- genRdVar(c, n, dest)
- of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator:
+ genRdVar(c, n, dest, flags)
+ of skProc, skConverter, skMacro, skTemplate, skMethod, skIterators:
# 'skTemplate' is only allowed for 'getAst' support:
if sfImportc in s.flags: c.importcSym(n.info, s)
genLit(c, n, dest)
@@ -1303,8 +1375,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
of nkDotExpr: genObjAccess(c, n, dest, flags)
of nkCheckedFieldExpr: genCheckedObjAccess(c, n, dest, flags)
of nkBracketExpr: genArrAccess(c, n, dest, flags)
- of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcDeref, flags)
- of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddr, flags)
+ of nkDerefExpr, nkHiddenDeref: genAddrDeref(c, n, dest, opcLdDeref, flags)
+ of nkAddr, nkHiddenAddr: genAddrDeref(c, n, dest, opcAddrNode, flags)
of nkWhenStmt, nkIfStmt, nkIfExpr: genIf(c, n, dest)
of nkCaseStmt: genCase(c, n, dest)
of nkWhileStmt:
@@ -1472,7 +1544,7 @@ proc genProc(c: PCtx; s: PSym): int =
# procs easily:
let body = s.getBody
let procStart = c.xjmp(body, opcJmp, 0)
- var p = PProc(blocks: @[])
+ var p = PProc(blocks: @[], sym: s)
let oldPrc = c.prc
c.prc = p
# iterate over the parameters and allocate space for them:
@@ -1489,9 +1561,9 @@ proc genProc(c: PCtx; s: PSym): int =
c.gABC(body, opcEof, eofInstr.regA)
c.optimizeJumps(result)
s.offset = c.prc.maxSlots
- #if s.name.s == "concatStyleInterpolation":
+ #if s.name.s == "xmlConstructor":
+ # echo renderTree(body)
# c.echoCode(result)
- # echo renderTree(body)
c.prc = oldPrc
else:
c.prc.maxSlots = s.offset
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index 837bb4f50..f4a0c59f4 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -62,8 +62,8 @@ type
wWatchPoint, wSubsChar,
wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, wInjectStmt,
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
- wNoStackFrame,
- wImplicitStatic, wGlobal, wCodegenDecl,
+ wAsmNoStackFrame,
+ wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked,
wAuto, wBool, wCatch, wChar, wClass,
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
@@ -145,7 +145,7 @@ const
"subschar", "acyclic", "shallow", "unroll", "linearscanend",
"computedgoto", "injectstmt",
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
- "nostackframe", "implicitstatic", "global", "codegendecl",
+ "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
"auto", "bool", "catch", "char", "class",
"const_cast", "default", "delete", "double",
diff --git a/doc/idetools.txt b/doc/idetools.txt
index c1eba9e5f..d4f0f077d 100644
--- a/doc/idetools.txt
+++ b/doc/idetools.txt
@@ -279,8 +279,8 @@ skForVar
col 7: ""
-skIterator
-----------
+skIterator, skClosureIterator
+-----------------------------
The fourth column will be the empty string if the iterator is being
defined, since at that point in the file the parser hasn't processed
diff --git a/doc/manual.txt b/doc/manual.txt
index fb357f7d3..8c8db7658 100644
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -480,8 +480,8 @@ precedence and associativity; this is useful for meta programming.
Associativity
-------------
-All binary operators are left-associative, except binary operators whose
-relevant char is ``^``.
+Binary operators whose relevant character is ``^`` are right-associative, all
+other binary operators are left-associative.
Precedence
----------
@@ -508,7 +508,7 @@ Precedence level Operators Relevant char
7 ``+ -`` ``+ ~ |`` OP7
6 ``&`` ``&`` OP6
5 ``..`` ``.`` OP5
- 4 ``== <= < >= > != in not_in is isnot not of`` ``= < > !`` OP4
+ 4 ``== <= < >= > != in notin is isnot not of`` ``= < > !`` OP4
3 ``and`` OP3
2 ``or xor`` OP2
1 ``@ : ?`` OP1
@@ -516,6 +516,46 @@ Precedence level Operators Relevant char
================ =============================================== ================== ===============
+Strong spaces
+-------------
+
+The number of spaces preceeding a non-keyword operator affects precedence
+if the experimental parser directive ``#!strongSpaces`` is used. Indentation
+is not used to determine the number of spaces. If 2 or more operators have the
+same number of preceeding spaces the precedence table applies, so ``1 + 3 * 4``
+is still parsed as ``1 + (3 * 4)``, but ``1+3 * 4`` is parsed as ``(1+3) * 4``:
+
+.. code-block:: nimrod
+ #! strongSpaces
+ if foo+4 * 4 == 8 and b&c | 9 ++
+ bar:
+ echo ""
+ # is parsed as
+ if ((foo+4)*4 == 8) and (((b&c) | 9) ++ bar): echo ""
+
+
+Furthermore whether an operator is used a prefix operator is affected by the
+number of spaces:
+
+.. code-block:: nimrod
+ #! strongSpaces
+ echo $foo
+ # is parsed as
+ echo($foo)
+
+This also affects whether ``[]``, ``{}``, ``()`` are parsed as constructors
+or as accessors:
+
+.. code-block:: nimrod
+ #! strongSpaces
+ echo (1,2)
+ # is parsed as
+ echo((1,2))
+
+
+Grammar
+-------
+
The grammar's start symbol is ``module``.
.. include:: grammar.txt
@@ -3441,7 +3481,7 @@ Declarative type classes are written in the following form:
c.len is ordinal
items(c) is iterator
for value in c:
- type(value) is T
+ value.type is T
The type class will be matched if:
@@ -4106,6 +4146,59 @@ types that will match the typedesc param:
The constraint can be a concrete type or a type class.
+Special Operators
+=================
+
+dot operators
+-------------
+
+Nimrod offers a special family of dot operators that can be used to
+intercept and rewrite proc call and field access attempts, referring
+to previously undeclared symbol names. They can be used to provide a
+fluent interface to objects lying outside the static confines of the
+Nimrod's type system such as values from dynamic scripting languages
+or dynamic file formats such as JSON or XML.
+
+When Nimrod encounters an expression that cannot be resolved by the
+standard overload resolution rules, the current scope will be searched
+for a dot operator that can be matched against a re-written form of
+the expression, where the unknown field or proc name is converted to
+an additional static string parameter:
+
+.. code-block:: nimrod
+ a.b # becomes `.`(a, "b")
+ a.b(c, d) # becomes `.`(a, "b", c, d)
+
+The matched dot operators can be symbols of any callable kind (procs,
+templates and macros), depending on the desired effect:
+
+.. code-block:: nimrod
+ proc `.` (js: PJsonNode, field: string): JSON = js[field]
+
+ var js = parseJson("{ x: 1, y: 2}")
+ echo js.x # outputs 1
+ echo js.y # outputs 2
+
+The following dot operators are available:
+
+operator `.`
+------------
+This operator will be matched against both field accesses and method calls.
+
+operator `.()`
+---------------
+This operator will be matched exclusively against method calls. It has higher
+precedence than the `.` operator and this allows you to handle expressions like
+`x.y` and `x.y()` differently if you are interfacing with a scripting language
+for example.
+
+operator `.=`
+-------------
+This operator will be matched against assignments to missing fields.
+
+.. code-block:: nimrod
+ a.b = c # becomes `.=`(a, "b", c)
+
Term rewriting macros
=====================
@@ -4758,42 +4851,6 @@ This may change in future versions of language, but for now use
the ``finalizer`` parameter to ``new``.
-delegator pragma
-----------------
-
-**Note**: The design of the delegator feature is subject to change.
-
-The delegator pragma can be used to intercept and rewrite proc call and field
-access attempts referring to previously undeclared symbol names. It can be used
-to provide a fluent interface to objects lying outside the static confines of
-the Nimrod's type system such as values from dynamic scripting languages or
-dynamic file formats such as JSON or XML.
-
-A delegator is a special form of the `()` operator marked with the delagator
-pragma. When Nimrod encounters an expression that cannot be resolved by the
-standard overload resolution, any delegators in the current scope will be
-matched against a rewritten form of the expression following the standard
-signature matching rules. In the rewritten expression, the name of the unknown
-proc or field name is inserted as an additional static string parameter always
-appearing in the leading position:
-
-.. code-block:: nimrod
- a.b => delegator("b", a)
- a.b(c, d) => delegator("b", a, c)
- a b, c, d => delegator("a", b, c, d)
-
-
-The delegators can be any callable symbol type (procs, templates, macros)
-depending on the desired effect:
-
-.. code-block:: nimrod
- proc `()` (field: string, js: PJsonNode): JSON {.delegator.} = js[field]
-
- var js = parseJson("{ x: 1, y: 2}")
- echo js.x # outputs 1
- echo js.y # outputs 2
-
-
procvar pragma
--------------
The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
@@ -4876,16 +4933,16 @@ field which is used for runtime type identification is omitted. This is
necessary for binary compatibility with other compiled languages.
-NoStackFrame pragma
--------------------
-A proc can be marked with the `noStackFrame`:idx: pragma to tell the compiler
+AsmNoStackFrame pragma
+----------------------
+A proc can be marked with the `AsmNoStackFrame`:idx: pragma to tell the compiler
it should not generate a stack frame for the proc. There are also no exit
statements like ``return result;`` generated and the generated C function is
declared as ``__declspec(naked)`` or ``__attribute__((naked))`` (depending on
the used C compiler).
-**Note**: This pragma should only be used by procs which consist solely of assembler
-statements.
+**Note**: This pragma should only be used by procs which consist solely of
+assembler statements.
error pragma
------------
@@ -5303,6 +5360,53 @@ strings automatically:
printf("hallo %s", "world") # "world" will be passed as C string
+Union pragma
+------------
+The `union`:idx: pragma can be applied to any ``object`` type. It means all
+of the object's fields are overlaid in memory. This produces a ``union``
+instead of a ``struct`` in the generated C/C++ code. The object declaration
+then must not use inheritance or any GC'ed memory but this is currently not
+checked.
+
+**Future directions**: GC'ed memory should be allowed in unions and the GC
+should scan unions conservatively.
+
+
+Unchecked pragma
+----------------
+The `unchecked`:idx: pragma can be used to mark a named array as ``unchecked``
+meaning its bounds are not checked. This is often useful when one wishes to
+implement his own flexibly sized arrays. Additionally an unchecked array is
+translated into a C array of undetermined size:
+
+.. code-block:: nimrod
+ type
+ ArrayPart{.unchecked.} = array[0..0, int]
+ MySeq = object
+ len, cap: int
+ data: ArrayPart
+
+Produces roughly this C code:
+
+.. code-block:: C
+ typedef struct {
+ NI len;
+ NI cap;
+ NI data[];
+ } MySeq;
+
+The bounds checking done at compile time is not disabled for now, so to access
+``s.data[C]`` (where ``C`` is a constant) the array's index needs needs to
+include ``C``.
+
+The base type of the unchecked array may not contain any GC'ed memory but this
+is currently not checked.
+
+**Future directions**: GC'ed memory should be allowed in unchecked arrays and
+there should be an explicit annotation of how the GC is to determine the
+runtime size of the array.
+
+
Dynlib pragma for import
------------------------
With the `dynlib`:idx: pragma a procedure or a variable can be imported from
diff --git a/examples/htmlrefs.nim b/examples/htmlrefs.nim
index 824c1d8c7..8b668325f 100644
--- a/examples/htmlrefs.nim
+++ b/examples/htmlrefs.nim
@@ -36,7 +36,7 @@ block mainLoop:
case x.kind
of xmlEof: break mainLoop
of xmlElementClose: break
- else: nil
+ else: discard
x.next() # skip ``xmlElementClose``
# now we have the description for the ``a`` element
var desc = ""
diff --git a/examples/htmltitle.nim b/examples/htmltitle.nim
index f3c672382..a3280bb13 100644
--- a/examples/htmltitle.nim
+++ b/examples/htmltitle.nim
@@ -4,10 +4,10 @@
import os, streams, parsexml, strutils
-if paramCount() < 1:
+if paramCount() < 1:
quit("Usage: htmltitle filename[.html]")
-var filename = addFileExt(ParamStr(1), "html")
+var filename = addFileExt(paramStr(1), "html")
var s = newFileStream(filename, fmRead)
if s == nil: quit("cannot open the file " & filename)
var x: TXmlParser
@@ -23,13 +23,13 @@ while true:
title.add(x.charData)
x.next()
if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
- Echo("Title: " & title)
+ echo("Title: " & title)
quit(0) # Success!
else:
echo(x.errorMsgExpected("/title"))
of xmlEof: break # end of file reached
- else: nil # ignore other events
+ else: discard # ignore other events
x.close()
quit("Could not determine title!")
diff --git a/koch.nim b/koch.nim
index 4d2b3bfb7..ce01d36a5 100644
--- a/koch.nim
+++ b/koch.nim
@@ -58,6 +58,15 @@ Boot options:
proc exe(f: string): string = return addFileExt(f, ExeExt)
+proc findNim(): string =
+ var nimrod = "nimrod".exe
+ result = "bin" / nimrod
+ if existsFile(result): return
+ for dir in split(getEnv("PATH"), PathSep):
+ if existsFile(dir / nimrod): return dir / nimrod
+ # assume there is a symlink to the exe or something:
+ return nimrod
+
proc exec(cmd: string) =
echo(cmd)
if execShellCmd(cmd) != 0: quit("FAILURE")
@@ -70,15 +79,15 @@ const
compileNimInst = "-d:useLibzipSrc tools/niminst/niminst"
proc csource(args: string) =
- exec("nimrod cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" %
- [args, NimrodVersion, compileNimInst])
+ exec("$4 cc $1 -r $3 --var:version=$2 csource compiler/nimrod.ini $1" %
+ [args, NimrodVersion, compileNimInst, findNim()])
proc zip(args: string) =
- exec("nimrod cc -r $2 --var:version=$1 zip compiler/nimrod.ini" %
- [NimrodVersion, compileNimInst])
+ exec("$3 cc -r $2 --var:version=$1 zip compiler/nimrod.ini" %
+ [NimrodVersion, compileNimInst, findNim()])
proc buildTool(toolname, args: string) =
- exec("nimrod cc $# $#" % [args, toolname])
+ exec("$# cc $# $#" % [findNim(), args, toolname])
copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe)
proc inno(args: string) =
@@ -90,13 +99,13 @@ proc inno(args: string) =
NimrodVersion)
proc install(args: string) =
- exec("nimrod cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
- [compileNimInst, NimrodVersion])
+ exec("$# cc -r $# --var:version=$# scripts compiler/nimrod.ini" %
+ [findNim(), compileNimInst, NimrodVersion])
exec("sh ./install.sh $#" % args)
proc web(args: string) =
- exec(("nimrod cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" &
- " --path:$#") % [NimrodVersion, getCurrentDir()])
+ exec("$# cc -r tools/nimweb.nim web/nimrod --putenv:nimrodversion=$#" %
+ [findNim(), NimrodVersion])
# -------------- boot ---------------------------------------------------------
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 585ccf869..db2bbc5a4 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -88,7 +88,7 @@ type
nskUnknown, nskConditional, nskDynLib, nskParam,
nskGenericParam, nskTemp, nskModule, nskType, nskVar, nskLet,
nskConst, nskResult,
- nskProc, nskMethod, nskIterator,
+ nskProc, nskMethod, nskIterator, nskClosureIterator,
nskConverter, nskMacro, nskTemplate, nskField,
nskEnumField, nskForVar, nskLabel,
nskStub
@@ -516,7 +516,7 @@ proc last*(node: PNimrodNode): PNimrodNode {.compileTime.} = node[node.high]
const
- RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda}
+ RoutineNodes* = {nnkProcDef, nnkMethodDef, nnkDo, nnkLambda, nnkIteratorDef}
AtomicNodes* = {nnkNone..nnkNilLit}
CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
nnkCallStrLit, nnkHiddenCallConv}
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 19d161adf..1100e084b 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -285,8 +285,8 @@ static N_INLINE(NI32, float32ToInt32)(float x) {
typedef struct TStringDesc* string;
-/* declared size of a sequence: */
-#if defined(__GNUC__)
+/* declared size of a sequence/variable length array: */
+#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
# define SEQ_DECL_SIZE /* empty is correct! */
#else
# define SEQ_DECL_SIZE 1000000
@@ -373,5 +373,8 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); }
# define GC_GUARD
#endif
+/* Test to see if nimrod and the C compiler agrees on the size of a pointer.
+ On disagreement, your C compiler will say something like:
+ "error: 'assert_numbits' declared as an array with a negative size" */
typedef int assert_numbits[sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8 ? 1 : -1];
#endif
diff --git a/lib/packages/docutils/docutils.babel b/lib/packages/docutils/docutils.babel
new file mode 100644
index 000000000..1ed86ca05
--- /dev/null
+++ b/lib/packages/docutils/docutils.babel
@@ -0,0 +1,6 @@
+[Package]
+name = "docutils"
+version = "0.9.0"
+author = "Andreas Rumpf"
+description = "Nimrod's reStructuredText processor."
+license = "MIT"
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index 4ca0c79e0..c507f5e1c 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -61,9 +61,8 @@ proc getSourceLanguage*(name: string): TSourceLanguage =
if cmpIgnoreStyle(name, sourceLanguageToStr[i]) == 0:
return i
result = langNone
-
-proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
- g.buf = cstring(buf)
+proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: cstring) =
+ g.buf = buf
g.kind = low(TTokenClass)
g.start = 0
g.length = 0
@@ -71,6 +70,8 @@ proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
var pos = 0 # skip initial whitespace:
while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos)
g.pos = pos
+proc initGeneralTokenizer*(g: var TGeneralTokenizer, buf: string) =
+ initGeneralTokenizer(g, cstring(buf))
proc deinitGeneralTokenizer*(g: var TGeneralTokenizer) =
discard
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index bb018bc1e..30cc9026b 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -1543,7 +1543,7 @@ proc dirRaw(p: var TRstParser): PRstNode =
elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0:
dirRawAux(p, result, rnRawLatex, parseLiteralBlock)
else:
- rstMessage(p, meInvalidDirective, result.sons[0].text)
+ rstMessage(p, meInvalidDirective, result.sons[0].sons[0].text)
else:
dirRawAux(p, result, rnRaw, parseSectionWrapper)
diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim
index d50394f60..366521551 100644
--- a/lib/posix/epoll.nim
+++ b/lib/posix/epoll.nim
@@ -7,6 +7,8 @@
# distribution, for details about the copyright.
#
+from posix import TSocketHandle
+
const
EPOLLIN* = 0x00000001
EPOLLPRI* = 0x00000002
@@ -33,8 +35,8 @@ const
type
epoll_data* {.importc: "union epoll_data",
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
- thePtr* {.importc: "ptr".}: pointer # \
- #fd*: cint
+ #thePtr* {.importc: "ptr".}: pointer
+ fd*: cint # \
#u32*: uint32
#u64*: uint64
@@ -54,7 +56,7 @@ proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1",
## Same as epoll_create but with an FLAGS parameter. The unused SIZE
## parameter has been dropped.
-proc epoll_ctl*(epfd: cint; op: cint; fd: cint; event: ptr epoll_event): cint {.
+proc epoll_ctl*(epfd: cint; op: cint; fd: cint | TSocketHandle; event: ptr epoll_event): cint {.
importc: "epoll_ctl", header: "<sys/epoll.h>".}
## Manipulate an epoll instance "epfd". Returns 0 in case of success,
## -1 in case of error ( the "errno" variable will contain the
diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim
new file mode 100644
index 000000000..1ed1af3b6
--- /dev/null
+++ b/lib/posix/linux.nim
@@ -0,0 +1,25 @@
+import posix
+
+const
+ CSIGNAL* = 0x000000FF
+ CLONE_VM* = 0x00000100
+ CLONE_FS* = 0x00000200
+ CLONE_FILES* = 0x00000400
+ CLONE_SIGHAND* = 0x00000800
+ CLONE_PTRACE* = 0x00002000
+ CLONE_VFORK* = 0x00004000
+ CLONE_PARENT* = 0x00008000
+ CLONE_THREAD* = 0x00010000
+ CLONE_NEWNS* = 0x00020000
+ CLONE_SYSVSEM* = 0x00040000
+ CLONE_SETTLS* = 0x00080000
+ CLONE_PARENT_SETTID* = 0x00100000
+ CLONE_CHILD_CLEARTID* = 0x00200000
+ CLONE_DETACHED* = 0x00400000
+ CLONE_UNTRACED* = 0x00800000
+ CLONE_CHILD_SETTID* = 0x01000000
+ CLONE_STOPPED* = 0x02000000
+
+# fn should be of type proc (a2: pointer): void {.cdecl.}
+proc clone*(fn: pointer; child_stack: pointer; flags: cint;
+ arg: pointer; ptid: ptr TPid; tls: pointer; ctid: ptr TPid): cint {.importc, header: "<sched.h>".}
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index 41260b36f..131f23fdd 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -2066,6 +2066,7 @@ proc pthread_spin_unlock*(a1: ptr Tpthread_spinlock): cint {.
proc pthread_testcancel*() {.importc, header: "<pthread.h>".}
+proc exitnow*(code: int): void {.importc: "_exit", header: "<unistd.h>".}
proc access*(a1: cstring, a2: cint): cint {.importc, header: "<unistd.h>".}
proc alarm*(a1: cint): cint {.importc, header: "<unistd.h>".}
proc chdir*(a1: cstring): cint {.importc, header: "<unistd.h>".}
@@ -2265,6 +2266,7 @@ proc gmtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>
proc localtime*(a1: var TTime): ptr Ttm {.importc, header: "<time.h>".}
proc localtime_r*(a1: var TTime, a2: var Ttm): ptr Ttm {.importc, header: "<time.h>".}
proc mktime*(a1: var Ttm): TTime {.importc, header: "<time.h>".}
+proc timegm*(a1: var Ttm): TTime {.importc, header: "<time.h>".}
proc nanosleep*(a1, a2: var Ttimespec): cint {.importc, header: "<time.h>".}
proc strftime*(a1: cstring, a2: int, a3: cstring,
a4: var Ttm): int {.importc, header: "<time.h>".}
@@ -2356,7 +2358,7 @@ proc FD_ZERO*(a1: var TFdSet) {.importc, header: "<sys/select.h>".}
proc pselect*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimespec,
a6: var Tsigset): cint {.importc, header: "<sys/select.h>".}
-proc select*(a1: cint, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {.
+proc select*(a1: cint | TSocketHandle, a2, a3, a4: ptr TFdSet, a5: ptr Ttimeval): cint {.
importc, header: "<sys/select.h>".}
when hasSpawnH:
diff --git a/lib/pure/asyncio.nim b/lib/pure/asyncio.nim
index 96afc6f4f..ab09dc860 100644
--- a/lib/pure/asyncio.nim
+++ b/lib/pure/asyncio.nim
@@ -167,7 +167,7 @@ proc asyncSocket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
result = newAsyncSocket()
result.socket = socket(domain, typ, protocol, buffered)
result.proto = protocol
- if result.socket == InvalidSocket: OSError(OSLastError())
+ if result.socket == invalidSocket: osError(osLastError())
result.socket.setBlocking(false)
proc toAsyncSocket*(sock: TSocket, state: TInfo = SockConnected): PAsyncSocket =
@@ -357,7 +357,7 @@ proc acceptAddr*(server: PAsyncSocket, client: var PAsyncSocket,
client.sslNeedAccept = false
client.info = SockConnected
- if c == InvalidSocket: SocketError(server.socket)
+ if c == invalidSocket: socketError(server.socket)
c.setBlocking(false) # TODO: Needs to be tested.
# deleg.open is set in ``toDelegate``.
@@ -481,7 +481,7 @@ proc recvLine*(s: PAsyncSocket, line: var TaintedString): bool {.deprecated.} =
of RecvDisconnected:
result = true
of RecvFail:
- s.SocketError(async = true)
+ s.socketError(async = true)
result = false
{.pop.}
@@ -615,11 +615,11 @@ proc poll*(d: PDispatcher, timeout: int = 500): bool =
if d.hasDataBuffered(d.deleVal):
hasDataBufferedCount.inc()
d.handleRead(d.deleVal)
- if hasDataBufferedCount > 0: return True
+ if hasDataBufferedCount > 0: return true
if readDg.len() == 0 and writeDg.len() == 0:
## TODO: Perhaps this shouldn't return if errorDg has something?
- return False
+ return false
if select(readDg, writeDg, errorDg, timeout) != 0:
for i in 0..len(d.delegates)-1:
diff --git a/lib/pure/asyncio2.nim b/lib/pure/asyncio2.nim
index cdb4a6f49..12d4cb5a3 100644
--- a/lib/pure/asyncio2.nim
+++ b/lib/pure/asyncio2.nim
@@ -7,9 +7,7 @@
# distribution, for details about the copyright.
#
-import os, oids, tables, strutils
-
-import winlean
+import os, oids, tables, strutils, macros
import sockets2, net
@@ -23,14 +21,13 @@ import sockets2, net
# -- Futures
type
- PFutureVoid* = ref object of PObject
- cbVoid: proc () {.closure.}
+ PFutureBase* = ref object of PObject
+ cb: proc () {.closure.}
finished: bool
- PFuture*[T] = ref object of PFutureVoid
+ PFuture*[T] = ref object of PFutureBase
value: T
error: ref EBase
- cb: proc (future: PFuture[T]) {.closure.}
proc newFuture*[T](): PFuture[T] =
## Creates a new future.
@@ -39,42 +36,38 @@ proc newFuture*[T](): PFuture[T] =
proc complete*[T](future: PFuture[T], val: T) =
## Completes ``future`` with value ``val``.
- assert(not future.finished)
+ assert(not future.finished, "Future already finished, cannot finish twice.")
assert(future.error == nil)
future.value = val
future.finished = true
if future.cb != nil:
- future.cb(future)
- if future.cbVoid != nil:
- future.cbVoid()
+ future.cb()
proc fail*[T](future: PFuture[T], error: ref EBase) =
## Completes ``future`` with ``error``.
- assert(not future.finished)
+ assert(not future.finished, "Future already finished, cannot finish twice.")
future.finished = true
future.error = error
if future.cb != nil:
- future.cb(future)
+ future.cb()
-proc `callback=`*[T](future: PFuture[T],
- cb: proc (future: PFuture[T]) {.closure.}) =
+proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
+ ##
+ ## **Note**: You most likely want the other ``callback`` setter which
+ ## passes ``future`` as a param to the callback.
future.cb = cb
if future.finished:
- future.cb(future)
+ future.cb()
-proc `callbackVoid=`*(future: PFutureVoid, cb: proc () {.closure.}) =
- ## Sets the **void** callback proc to be called when the future completes.
+proc `callback=`*[T](future: PFuture[T],
+ cb: proc (future: PFuture[T]) {.closure.}) =
+ ## Sets the callback proc to be called when the future completes.
##
## If future has already completed then ``cb`` will be called immediately.
- ##
- ## **Note**: This is used for the ``await`` functionality, you most likely
- ## want to use ``callback``.
- future.cbVoid = cb
- if future.finished:
- future.cbVoid()
+ future.callback = proc () = cb(future)
proc read*[T](future: PFuture[T]): T =
## Retrieves the value of ``future``. Future must be finished otherwise
@@ -98,16 +91,21 @@ proc failed*[T](future: PFuture[T]): bool =
## Determines whether ``future`` completed with an error.
future.error != nil
-when defined(windows):
+# TODO: Get rid of register. Do it implicitly.
+
+when defined(windows) or defined(nimdoc):
+ import winlean
type
TCompletionKey = dword
TCompletionData* = object
sock: TSocketHandle
- cb: proc (sock: TSocketHandle, errcode: TOSErrorCode) {.closure.}
+ cb: proc (sock: TSocketHandle, bytesTransferred: DWORD,
+ errcode: TOSErrorCode) {.closure.}
PDispatcher* = ref object
ioPort: THandle
+ hasHandles: bool
TCustomOverlapped = object
Internal*: DWORD
@@ -129,9 +127,13 @@ when defined(windows):
if CreateIOCompletionPort(sock.THandle, p.ioPort,
cast[TCompletionKey](sock), 1) == 0:
OSError(OSLastError())
+ p.hasHandles = true
proc poll*(p: PDispatcher, timeout = 500) =
## Waits for completion events and processes them.
+ if not p.hasHandles:
+ raise newException(EInvalidValue, "No handles registered in dispatcher.")
+
let llTimeout =
if timeout == -1: winlean.INFINITE
else: timeout.int32
@@ -145,16 +147,19 @@ when defined(windows):
# TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html
var customOverlapped = cast[PCustomOverlapped](lpOverlapped)
if res:
+ # This is useful for ensuring the reliability of the overlapped struct.
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
- customOverlapped.data.cb(customOverlapped.data.sock, TOSErrorCode(-1))
+ customOverlapped.data.cb(customOverlapped.data.sock,
+ lpNumberOfBytesTransferred, TOSErrorCode(-1))
dealloc(customOverlapped)
else:
let errCode = OSLastError()
if lpOverlapped != nil:
assert customOverlapped.data.sock == lpCompletionKey.TSocketHandle
+ customOverlapped.data.cb(customOverlapped.data.sock,
+ lpNumberOfBytesTransferred, errCode)
dealloc(customOverlapped)
- customOverlapped.data.cb(customOverlapped.data.sock, errCode)
else:
if errCode.int32 == WAIT_TIMEOUT:
# Timed out
@@ -252,11 +257,12 @@ when defined(windows):
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
- proc (sock: TSocketHandle, errcode: TOSErrorCode) =
- if errcode == TOSErrorCode(-1):
- retFuture.complete(0)
- else:
- retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+ proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
+ if not retFuture.finished:
+ if errcode == TOSErrorCode(-1):
+ retFuture.complete(0)
+ else:
+ retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
var ret = connectEx(socket, it.ai_addr, sizeof(TSockAddrIn).cint,
@@ -265,7 +271,9 @@ when defined(windows):
# Request to connect completed immediately.
success = true
retFuture.complete(0)
- dealloc(ol)
+ # We don't deallocate ``ol`` here because even though this completed
+ # immediately poll will still be notified about its completion and it will
+ # free ``ol``.
break
else:
lastError = OSLastError()
@@ -283,9 +291,13 @@ when defined(windows):
retFuture.fail(newException(EOS, osErrorMsg(lastError)))
return retFuture
- proc recv*(p: PDispatcher, socket: TSocketHandle, size: int): PFuture[string] =
+ proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
+ flags: int = 0): PFuture[string] =
## Reads ``size`` bytes from ``socket``. Returned future will complete once
- ## all of the requested data is read.
+ ## all of the requested data is read. If socket is disconnected during the
+ ## recv operation then the future may complete with only a part of the
+ ## requested data read. If socket is disconnected and no data is available
+ ## to be read then the future will complete with a value of ``""``.
var retFuture = newFuture[string]()
@@ -293,31 +305,50 @@ when defined(windows):
dataBuf.buf = newString(size)
dataBuf.len = size
- var bytesReceived, flags: DWord
+ var bytesReceived: DWord
+ var flagsio = flags.dword
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
- proc (sock: TSocketHandle, errcode: TOSErrorCode) =
- if errcode == TOSErrorCode(-1):
- var data = newString(size)
- copyMem(addr data[0], addr dataBuf.buf[0], size)
- retFuture.complete($data)
- else:
- retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+ proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
+ if not retFuture.finished:
+ if errcode == TOSErrorCode(-1):
+ if bytesCount == 0 and dataBuf.buf[0] == '\0':
+ retFuture.complete("")
+ else:
+ var data = newString(size)
+ copyMem(addr data[0], addr dataBuf.buf[0], size)
+ retFuture.complete($data)
+ else:
+ retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
-
+
let ret = WSARecv(socket, addr dataBuf, 1, addr bytesReceived,
- addr flags, cast[POverlapped](ol), nil)
+ addr flagsio, cast[POverlapped](ol), nil)
if ret == -1:
let err = OSLastError()
if err.int32 != ERROR_IO_PENDING:
retFuture.fail(newException(EOS, osErrorMsg(err)))
dealloc(ol)
+ elif ret == 0 and bytesReceived == 0 and dataBuf.buf[0] == '\0':
+ # We have to ensure that the buffer is empty because WSARecv will tell
+ # us immediatelly when it was disconnected, even when there is still
+ # data in the buffer.
+ # We want to give the user as much data as we can. So we only return
+ # the empty string (which signals a disconnection) when there is
+ # nothing left to read.
+ retFuture.complete("")
+ # TODO: "For message-oriented sockets, where a zero byte message is often
+ # allowable, a failure with an error code of WSAEDISCON is used to
+ # indicate graceful closure."
+ # ~ http://msdn.microsoft.com/en-us/library/ms741688%28v=vs.85%29.aspx
else:
# Request to read completed immediately.
var data = newString(size)
copyMem(addr data[0], addr dataBuf.buf[0], size)
retFuture.complete($data)
- dealloc(ol)
+ # We don't deallocate ``ol`` here because even though this completed
+ # immediately poll will still be notified about its completion and it will
+ # free ``ol``.
return retFuture
proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
@@ -332,11 +363,12 @@ when defined(windows):
var bytesReceived, flags: DWord
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
- proc (sock: TSocketHandle, errcode: TOSErrorCode) =
- if errcode == TOSErrorCode(-1):
- retFuture.complete(0)
- else:
- retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+ proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
+ if not retFuture.finished:
+ if errcode == TOSErrorCode(-1):
+ retFuture.complete(0)
+ else:
+ retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
let ret = WSASend(socket, addr dataBuf, 1, addr bytesReceived,
@@ -348,7 +380,9 @@ when defined(windows):
dealloc(ol)
else:
retFuture.complete(0)
- dealloc(ol)
+ # We don't deallocate ``ol`` here because even though this completed
+ # immediately poll will still be notified about its completion and it will
+ # free ``ol``.
return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
@@ -390,11 +424,12 @@ when defined(windows):
var ol = cast[PCustomOverlapped](alloc0(sizeof(TCustomOverlapped)))
ol.data = TCompletionData(sock: socket, cb:
- proc (sock: TSocketHandle, errcode: TOSErrorCode) =
- if errcode == TOSErrorCode(-1):
- completeAccept()
- else:
- retFuture.fail(newException(EOS, osErrorMsg(errcode)))
+ proc (sock: TSocketHandle, bytesCount: DWord, errcode: TOSErrorCode) =
+ if not retFuture.finished:
+ if errcode == TOSErrorCode(-1):
+ completeAccept()
+ else:
+ retFuture.fail(newException(EOS, osErrorMsg(errcode)))
)
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx
@@ -411,70 +446,472 @@ when defined(windows):
dealloc(ol)
else:
completeAccept()
- dealloc(ol)
+ # We don't deallocate ``ol`` here because even though this completed
+ # immediately poll will still be notified about its completion and it will
+ # free ``ol``.
return retFuture
- proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
- ## Accepts a new connection. Returns a future containing the client socket
- ## corresponding to that connection.
- ## The future will complete when the connection is successfully accepted.
- var retFut = newFuture[TSocketHandle]()
- var fut = p.acceptAddr(socket)
- fut.callback =
- proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
- assert future.finished
- if future.failed:
- retFut.fail(future.error)
- else:
- retFut.complete(future.read.client)
- return retFut
-
initAll()
else:
- # TODO: Selectors.
+ import selectors
+ from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK
+ type
+ TCallback = proc (sock: TSocketHandle): bool {.closure.}
+
+ PData* = ref object of PObject
+ sock: TSocketHandle
+ readCBs: seq[TCallback]
+ writeCBs: seq[TCallback]
+ PDispatcher* = ref object
+ selector: PSelector
+
+ proc newDispatcher*(): PDispatcher =
+ new result
+ result.selector = newSelector()
+
+ proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) =
+ assert sock in p.selector
+ echo("Update: ", events)
+ if events == {}:
+ discard p.selector.unregister(sock)
+ else:
+ discard p.selector.update(sock, events)
+
+ proc addRead(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
+ if sock notin p.selector:
+ var data = PData(sock: sock, readCBs: @[cb], writeCBs: @[])
+ p.selector.register(sock, {EvRead}, data.PObject)
+ else:
+ p.selector[sock].data.PData.readCBs.add(cb)
+ p.update(sock, p.selector[sock].events + {EvRead})
+
+ proc addWrite(p: PDispatcher, sock: TSocketHandle, cb: TCallback) =
+ if sock notin p.selector:
+ var data = PData(sock: sock, readCBs: @[], writeCBs: @[cb])
+ p.selector.register(sock, {EvWrite}, data.PObject)
+ else:
+ p.selector[sock].data.PData.writeCBs.add(cb)
+ p.update(sock, p.selector[sock].events + {EvWrite})
+
+ proc poll*(p: PDispatcher, timeout = 500) =
+ for info in p.selector.select(timeout):
+ let data = PData(info.key.data)
+ assert data.sock == info.key.fd
+ echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events)
+
+ if EvRead in info.events:
+ var newReadCBs: seq[TCallback] = @[]
+ for cb in data.readCBs:
+ if not cb(data.sock):
+ # Callback wants to be called again.
+ newReadCBs.add(cb)
+ data.readCBs = newReadCBs
+
+ if EvWrite in info.events:
+ var newWriteCBs: seq[TCallback] = @[]
+ for cb in data.writeCBs:
+ if not cb(data.sock):
+ # Callback wants to be called again.
+ newWriteCBs.add(cb)
+ data.writeCBs = newWriteCBs
+
+ var newEvents: set[TEvent]
+ if data.readCBs.len != 0: newEvents = {EvRead}
+ if data.writeCBs.len != 0: newEvents = newEvents + {EvWrite}
+ p.update(data.sock, newEvents)
+
+ proc connect*(p: PDispatcher, socket: TSocketHandle, address: string, port: TPort,
+ af = AF_INET): PFuture[int] =
+ var retFuture = newFuture[int]()
+
+ proc cb(sock: TSocketHandle): bool =
+ # We have connected.
+ retFuture.complete(0)
+ return true
+
+ var aiList = getAddrInfo(address, port, af)
+ var success = false
+ var lastError: TOSErrorCode
+ var it = aiList
+ while it != nil:
+ var ret = connect(socket, it.ai_addr, it.ai_addrlen.TSocklen)
+ if ret == 0:
+ # Request to connect completed immediately.
+ success = true
+ retFuture.complete(0)
+ break
+ else:
+ lastError = osLastError()
+ if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
+ success = true
+ addWrite(p, socket, cb)
+ break
+ else:
+ success = false
+ it = it.ai_next
+
+ dealloc(aiList)
+ if not success:
+ retFuture.fail(newException(EOS, osErrorMsg(lastError)))
+ return retFuture
+
+ proc recv*(p: PDispatcher, socket: TSocketHandle, size: int,
+ flags: int = 0): PFuture[string] =
+ var retFuture = newFuture[string]()
+
+ var readBuffer = newString(size)
+ var sizeRead = 0
+
+ proc cb(sock: TSocketHandle): bool =
+ result = true
+ let netSize = size - sizeRead
+ let res = recv(sock, addr readBuffer[sizeRead], netSize, flags.cint)
+ if res < 0:
+ let lastError = osLastError()
+ if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
+ retFuture.fail(newException(EOS, osErrorMsg(lastError)))
+ else:
+ result = false # We still want this callback to be called.
+ elif res == 0:
+ # Disconnected
+ if sizeRead == 0:
+ retFuture.complete("")
+ else:
+ readBuffer.setLen(sizeRead)
+ retFuture.complete(readBuffer)
+ else:
+ sizeRead.inc(res)
+ if res != netSize:
+ result = false # We want to read all the data requested.
+ else:
+ retFuture.complete(readBuffer)
+
+ addRead(p, socket, cb)
+ return retFuture
+
+ proc send*(p: PDispatcher, socket: TSocketHandle, data: string): PFuture[int] =
+ var retFuture = newFuture[int]()
+
+ var written = 0
+
+ proc cb(sock: TSocketHandle): bool =
+ result = true
+ let netSize = data.len-written
+ var d = data.cstring
+ let res = send(sock, addr d[written], netSize, 0.cint)
+ if res < 0:
+ let lastError = osLastError()
+ if lastError.int32 notin {EINTR, EWOULDBLOCK, EAGAIN}:
+ retFuture.fail(newException(EOS, osErrorMsg(lastError)))
+ else:
+ result = false # We still want this callback to be called.
+ else:
+ written.inc(res)
+ if res != netSize:
+ result = false # We still have data to send.
+ else:
+ retFuture.complete(0)
+ addWrite(p, socket, cb)
+ return retFuture
+
+
+ proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
+ PFuture[tuple[address: string, client: TSocketHandle]] =
+ var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
+ proc cb(sock: TSocketHandle): bool =
+ result = true
+ var sockAddress: Tsockaddr_in
+ var addrLen = sizeof(sockAddress).TSocklen
+ var client = accept(sock, cast[ptr TSockAddr](addr(sockAddress)),
+ addr(addrLen))
+ if client == osInvalidSocket:
+ let lastError = osLastError()
+ assert lastError.int32 notin {EWOULDBLOCK, EAGAIN}
+ if lastError.int32 == EINTR:
+ return false
+ else:
+ retFuture.fail(newException(EOS, osErrorMsg(lastError)))
+ else:
+ retFuture.complete(($inet_ntoa(sockAddress.sin_addr), client))
+ addRead(p, socket, cb)
+ return retFuture
+
+proc accept*(p: PDispatcher, socket: TSocketHandle): PFuture[TSocketHandle] =
+ ## Accepts a new connection. Returns a future containing the client socket
+ ## corresponding to that connection.
+ ## The future will complete when the connection is successfully accepted.
+ var retFut = newFuture[TSocketHandle]()
+ var fut = p.acceptAddr(socket)
+ fut.callback =
+ proc (future: PFuture[tuple[address: string, client: TSocketHandle]]) =
+ assert future.finished
+ if future.failed:
+ retFut.fail(future.error)
+ else:
+ retFut.complete(future.read.client)
+ return retFut
+
+# -- Await Macro
+
+template createCb*(cbName, varNameIterSym, retFutureSym: expr): stmt {.immediate, dirty.} =
+ proc cbName {.closure.} =
+ if not varNameIterSym.finished:
+ var next = varNameIterSym()
+ if next == nil:
+ assert retFutureSym.finished, "Async procedure's return Future was not finished."
+ else:
+ next.callback = cbName
+
+template createVar(futSymName: string, asyncProc: PNimrodNode,
+ valueReceiver: expr) {.immediate, dirty.} =
+ # TODO: Used template here due to bug #926
+ result = newNimNode(nnkStmtList)
+ var futSym = newIdentNode(futSymName) #genSym(nskVar, "future")
+ result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y
+ result.add newNimNode(nnkYieldStmt).add(futSym) # -> yield future<x>
+ valueReceiver = newDotExpr(futSym, newIdentNode("read")) # -> future<x>.read
+
+proc processBody(node, retFutureSym: PNimrodNode): PNimrodNode {.compileTime.} =
+ result = node
+ case node.kind
+ of nnkReturnStmt:
+ result = newNimNode(nnkStmtList)
+ result.add newCall(newIdentNode("complete"), retFutureSym,
+ if node[0].kind == nnkEmpty: newIdentNode("result") else: node[0])
+ result.add newNimNode(nnkYieldStmt).add(newNilLit())
+ of nnkCommand:
+ if node[0].ident == !"await":
+ case node[1].kind
+ of nnkIdent:
+ # await x
+ result = newNimNode(nnkYieldStmt).add(node[1]) # -> yield x
+ of nnkCall:
+ # await foo(p, x)
+ var futureValue: PNimrodNode
+ createVar("future" & $node[1][0].toStrLit, node[1], futureValue)
+ result.add futureValue
+ else:
+ error("Invalid node kind in 'await', got: " & $node[1].kind)
+ elif node[1].kind == nnkCommand and node[1][0].kind == nnkIdent and
+ node[1][0].ident == !"await":
+ # foo await x
+ var newCommand = node
+ createVar("future" & $node[0].ident, node[1][0], newCommand[1])
+ result.add newCommand
+
+ of nnkVarSection, nnkLetSection:
+ case node[0][2].kind
+ of nnkCommand:
+ if node[0][2][0].ident == !"await":
+ # var x = await y
+ var newVarSection = node # TODO: Should this use copyNimNode?
+ createVar("future" & $node[0][0].ident, node[0][2][1],
+ newVarSection[0][2])
+ result.add newVarSection
+ else: discard
+ of nnkAsgn:
+ case node[1].kind
+ of nnkCommand:
+ if node[1][0].ident == !"await":
+ # x = await y
+ var newAsgn = node
+ createVar("future" & $node[0].ident, node[1][1], newAsgn[1])
+ result.add newAsgn
+ else: discard
+ of nnkDiscardStmt:
+ # discard await x
+ if node[0][0].ident == !"await":
+ var dummy = newNimNode(nnkStmtList)
+ createVar("futureDiscard_" & $toStrLit(node[0][1]), node[0][1], dummy)
+ else: discard
+
+ for i in 0 .. <result.len:
+ result[i] = processBody(result[i], retFutureSym)
+ #echo(treeRepr(result))
+
+proc getName(node: PNimrodNode): string {.compileTime.} =
+ case node.kind
+ of nnkPostfix:
+ return $node[1].ident
+ of nnkIdent:
+ return $node.ident
+ else:
+ assert false
+
+macro async*(prc: stmt): stmt {.immediate.} =
+ expectKind(prc, nnkProcDef)
+
+ hint("Processing " & prc[0].getName & " as an async proc.")
+
+ # Verify that the return type is a PFuture[T]
+ if prc[3][0].kind == nnkIdent:
+ error("Expected return type of 'PFuture' got '" & $prc[3][0] & "'")
+ elif prc[3][0].kind == nnkBracketExpr:
+ if $prc[3][0][0] != "PFuture":
+ error("Expected return type of 'PFuture' got '" & $prc[3][0][0] & "'")
+
+ # TODO: Why can't I use genSym? I get illegal capture errors for Syms.
+ # TODO: It seems genSym is broken. Change all usages back to genSym when fixed
+
+ var outerProcBody = newNimNode(nnkStmtList)
+
+ # -> var retFuture = newFuture[T]()
+ var retFutureSym = newIdentNode("retFuture") #genSym(nskVar, "retFuture")
+ outerProcBody.add(
+ newVarStmt(retFutureSym,
+ newCall(
+ newNimNode(nnkBracketExpr).add(
+ newIdentNode("newFuture"),
+ prc[3][0][1])))) # Get type from return type of this proc.
+
+ # -> iterator nameIter(): PFutureBase {.closure.} =
+ # -> var result: T
+ # -> <proc_body>
+ # -> complete(retFuture, result)
+ var iteratorNameSym = newIdentNode($prc[0].getName & "Iter") #genSym(nskIterator, $prc[0].ident & "Iter")
+ var procBody = prc[6].processBody(retFutureSym)
+ procBody.insert(0, newNimNode(nnkVarSection).add(
+ newIdentDefs(newIdentNode("result"), prc[3][0][1]))) # -> var result: T
+ procBody.add(
+ newCall(newIdentNode("complete"),
+ retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result)
+
+ var closureIterator = newProc(iteratorNameSym, [newIdentNode("PFutureBase")],
+ procBody, nnkIteratorDef)
+ closureIterator[4] = newNimNode(nnkPragma).add(newIdentNode("closure"))
+ outerProcBody.add(closureIterator)
+
+ # -> var nameIterVar = nameIter
+ # -> var first = nameIterVar()
+ var varNameIterSym = newIdentNode($prc[0].getName & "IterVar") #genSym(nskVar, $prc[0].ident & "IterVar")
+ var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym)
+ outerProcBody.add varNameIter
+ var varFirstSym = genSym(nskVar, "first")
+ var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym))
+ outerProcBody.add varFirst
+
+ # -> createCb(cb, nameIter, retFuture)
+ var cbName = newIdentNode("cb")
+ var procCb = newCall("createCb", cbName, varNameIterSym, retFutureSym)
+ outerProcBody.add procCb
+
+ # -> first.callback = cb
+ outerProcBody.add newAssignment(
+ newDotExpr(varFirstSym, newIdentNode("callback")),
+ cbName)
+
+ # -> return retFuture
+ outerProcBody.add newNimNode(nnkReturnStmt).add(retFutureSym)
+
+ result = prc
+
+ # Remove the 'async' pragma.
+ for i in 0 .. <result[4].len:
+ if result[4][i].ident == !"async":
+ result[4].del(i)
+
+ result[6] = outerProcBody
+
+ echo(toStrLit(result))
+
+proc recvLine*(p: PDispatcher, socket: TSocketHandle): PFuture[string] {.async.} =
+ ## Reads a line of data from ``socket``. Returned future will complete once
+ ## a full line is read or an error occurs.
+ ##
+ ## If a full line is read ``\r\L`` is not
+ ## added to ``line``, however if solely ``\r\L`` is read then ``line``
+ ## will be set to it.
+ ##
+ ## If the socket is disconnected, ``line`` will be set to ``""``.
+
+ template addNLIfEmpty(): stmt =
+ if result.len == 0:
+ result.add("\c\L")
+
+ result = ""
+ var c = ""
+ while true:
+ c = await p.recv(socket, 1)
+ if c.len == 0:
+ return
+ if c == "\r":
+ c = await p.recv(socket, 1, MSG_PEEK)
+ if c.len > 0 and c == "\L":
+ discard await p.recv(socket, 1)
+ addNLIfEmpty()
+ return
+ elif c == "\L":
+ addNLIfEmpty()
+ return
+ add(result.string, c)
when isMainModule:
var p = newDispatcher()
var sock = socket()
- #sock.setBlocking false
- p.register(sock)
-
- when true:
-
- var f = p.connect(sock, "irc.freenode.org", TPort(6667))
- f.callback =
- proc (future: PFuture[int]) =
- echo("Connected in future!")
- echo(future.read)
- for i in 0 .. 50:
- var recvF = p.recv(sock, 10)
- recvF.callback =
- proc (future: PFuture[string]) =
- echo("Read: ", future.read)
+ sock.setBlocking false
+
+
+ when false:
+ # Await tests
+ proc main(p: PDispatcher): PFuture[int] {.async.} =
+ discard await p.connect(sock, "irc.freenode.net", TPort(6667))
+ while true:
+ var line = await p.recvLine(sock)
+ echo("Line is: ", line.repr)
+ if line == "":
+ echo "Disconnected"
+ break
+
+ proc peekTest(p: PDispatcher): PFuture[int] {.async.} =
+ discard await p.connect(sock, "localhost", TPort(6667))
+ while true:
+ var line = await p.recv(sock, 1, MSG_PEEK)
+ var line2 = await p.recv(sock, 1)
+ echo(line.repr)
+ echo(line2.repr)
+ echo("---")
+ if line2 == "": break
+ sleep(500)
+
+ var f = main(p)
+
else:
+ when false:
- sock.bindAddr(TPort(6667))
- sock.listen()
- proc onAccept(future: PFuture[TSocketHandle]) =
- echo "Accepted"
- var t = p.send(future.read, "test\c\L")
- t.callback =
+ var f = p.connect(sock, "irc.freenode.org", TPort(6667))
+ f.callback =
proc (future: PFuture[int]) =
+ echo("Connected in future!")
echo(future.read)
-
+ for i in 0 .. 50:
+ var recvF = p.recv(sock, 10)
+ recvF.callback =
+ proc (future: PFuture[string]) =
+ echo("Read ", future.read.len, ": ", future.read.repr)
+
+ else:
+
+ sock.bindAddr(TPort(6667))
+ sock.listen()
+ proc onAccept(future: PFuture[TSocketHandle]) =
+ echo "Accepted"
+ var t = p.send(future.read, "test\c\L")
+ t.callback =
+ proc (future: PFuture[int]) =
+ echo(future.read)
+
+ var f = p.accept(sock)
+ f.callback = onAccept
+
var f = p.accept(sock)
f.callback = onAccept
-
- var f = p.accept(sock)
- f.callback = onAccept
while true:
p.poll()
- echo "polled"
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index e6ab617e5..ad3fe7218 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -241,3 +241,7 @@ proc `<=`*[A](s, t: TSet[A]): bool =
proc `==`*[A](s, t: TSet[A]): bool =
s.counter == t.counter and s <= t
+
+proc map*[A, B](data: TSet[A], op: proc (x: A): B {.closure.}): TSet[B] =
+ result = initSet[B]()
+ for item in data: result.incl(op(item))
diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim
index 3ed00fdb2..889912052 100644
--- a/lib/pure/dynlib.nim
+++ b/lib/pure/dynlib.nim
@@ -14,7 +14,7 @@
type
TLibHandle* = pointer ## a handle to a dynamically loaded library
-proc loadLib*(path: string): TLibHandle
+proc loadLib*(path: string, global_symbols=false): TLibHandle
## loads a library from `path`. Returns nil if the library could not
## be loaded.
@@ -53,6 +53,7 @@ when defined(posix):
#
var
RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: int
+ RTLD_GLOBAL {.importc: "RTLD_GLOBAL", header: "<dlfcn.h>".}: int
proc dlclose(lib: TLibHandle) {.importc, header: "<dlfcn.h>".}
proc dlopen(path: CString, mode: int): TLibHandle {.
@@ -60,7 +61,10 @@ when defined(posix):
proc dlsym(lib: TLibHandle, name: cstring): pointer {.
importc, header: "<dlfcn.h>".}
- proc loadLib(path: string): TLibHandle = return dlopen(path, RTLD_NOW)
+ proc loadLib(path: string, global_symbols=false): TLibHandle =
+ var flags = RTLD_NOW
+ if global_symbols: flags = flags or RTLD_GLOBAL
+ return dlopen(path, flags)
proc loadLib(): TLibHandle = return dlopen(nil, RTLD_NOW)
proc unloadLib(lib: TLibHandle) = dlclose(lib)
proc symAddr(lib: TLibHandle, name: cstring): pointer =
@@ -81,7 +85,7 @@ elif defined(windows) or defined(dos):
proc getProcAddress(lib: THINSTANCE, name: cstring): pointer {.
importc: "GetProcAddress", header: "<windows.h>", stdcall.}
- proc loadLib(path: string): TLibHandle =
+ proc loadLib(path: string, global_symbols=false): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(path))
proc loadLib(): TLibHandle =
result = cast[TLibHandle](winLoadLibrary(nil))
diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim
index 63737d583..b9d6aec7b 100644
--- a/lib/pure/htmlgen.nim
+++ b/lib/pure/htmlgen.nim
@@ -1,12 +1,17 @@
#
#
# Nimrod's Runtime Library
-# (c) Copyright 2012 Andreas Rumpf
+# (c) Copyright 2014 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
+## **Warning**: This module uses ``immediate`` macros which are known to
+## cause problems. Do yourself a favor and import the module
+## as ``from htmlgen import nil`` and then fully qualify the macros.
+##
+##
## This module implements a simple `XML`:idx: and `HTML`:idx: code
## generator. Each commonly used HTML tag has a corresponding macro
## that generates a string with its HTML representation.
@@ -15,11 +20,11 @@
##
## .. code-block:: nimrod
## var nim = "Nimrod"
-## echo h1(a(href="http://nimrod-code.org", nim))
+## echo h1(a(href="http://nimrod-lang.org", nim))
##
## Writes the string::
##
-## <h1><a href="http://nimrod-code.org">Nimrod</a></h1>
+## <h1><a href="http://nimrod-lang.org">Nimrod</a></h1>
##
import
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index bdcae677e..0ec007009 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -37,4 +37,19 @@ proc bindAddr*(socket: TSocket, port = TPort(0), address = "") {.
if bindAddr(socket, aiList.ai_addr, aiList.ai_addrlen.TSocklen) < 0'i32:
dealloc(aiList)
osError(osLastError())
- dealloc(aiList) \ No newline at end of file
+ dealloc(aiList)
+
+proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].} =
+ ## Sets blocking mode on socket
+ when defined(Windows):
+ var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
+ if ioctlsocket(s, FIONBIO, addr(mode)) == -1:
+ osError(osLastError())
+ else: # BSD sockets
+ var x: int = fcntl(s, F_GETFL, 0)
+ if x == -1:
+ osError(osLastError())
+ else:
+ var mode = if blocking: x and not O_NONBLOCK else: x or O_NONBLOCK
+ if fcntl(s, F_SETFL, mode) == -1:
+ osError(osLastError()) \ No newline at end of file
diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim
index 02f0366cd..3d0cc2154 100644
--- a/lib/pure/nimprof.nim
+++ b/lib/pure/nimprof.nim
@@ -67,7 +67,7 @@ when withThreads:
proc hookAux(st: TStackTrace, costs: int) =
# this is quite performance sensitive!
- when withThreads: Acquire profilingLock
+ when withThreads: acquire profilingLock
inc totalCalls
var last = high(st)
while last > 0 and isNil(st[last]): dec last
@@ -106,7 +106,7 @@ proc hookAux(st: TStackTrace, costs: int) =
h = ((5 * h) + 1) and high(profileData)
inc chain
maxChainLen = max(maxChainLen, chain)
- when withThreads: Release profilingLock
+ when withThreads: release profilingLock
when defined(memProfiler):
const
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index bb70f28b6..bfecc569a 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -1037,7 +1037,10 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
## the process has finished. To execute a program without having a
## shell involved, use the `execProcess` proc of the `osproc`
## module.
- result = c_system(command) shr 8
+ when defined(linux):
+ result = c_system(command) shr 8
+ else:
+ result = c_system(command)
# Environment handling cannot be put into RTL, because the ``envPairs``
# iterator depends on ``environment``.
@@ -1189,7 +1192,8 @@ iterator walkFiles*(pattern: string): string {.tags: [FReadDir].} =
res = findFirstFile(pattern, f)
if res != -1:
while true:
- if not skipFindData(f):
+ if not skipFindData(f) and
+ (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) == 0'i32:
yield splitFile(pattern).dir / extractFilename(getFilename(f))
if findNextFile(res, f) == 0'i32: break
findClose(res)
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 6df85bbc6..582b3c960 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -13,13 +13,16 @@
include "system/inclrtl"
import
- strutils, os, strtabs, streams, sequtils
+ strutils, os, strtabs, streams
when defined(windows):
import winlean
else:
import posix
+when defined(linux):
+ import linux
+
type
TProcess = object of TObject
when defined(windows):
@@ -44,7 +47,7 @@ type
poStdErrToStdOut, ## merge stdout and stderr to the stdout stream
poParentStreams ## use the parent's streams
-template poUseShell*: TProcessOption {.deprecated.} = poUsePath
+const poUseShell* {.deprecated.} = poUsePath
## Deprecated alias for poUsePath.
proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} =
@@ -165,6 +168,9 @@ proc processID*(p: PProcess): int {.rtl, extern: "nosp$1".} =
proc waitForExit*(p: PProcess, timeout: int = -1): int {.rtl,
extern: "nosp$1", tags: [].}
## waits for the process to finish and returns `p`'s error code.
+ ##
+ ## **Warning**: Be careful when using waitForExit for processes created without
+ ## poParentStreams because they may fill output buffers, causing deadlock.
proc peekExitCode*(p: PProcess): int {.tags: [].}
## return -1 if the process is still running. Otherwise the process' exit code
@@ -590,6 +596,23 @@ elif not defined(useNimRtl):
copyMem(result[i], addr(x[0]), x.len+1)
inc(i)
+ type TStartProcessData = object
+ sysCommand: cstring
+ sysArgs: cstringArray
+ sysEnv: cstringArray
+ workingDir: cstring
+ pStdin, pStdout, pStderr, pErrorPipe: array[0..1, cint]
+ optionPoUsePath: bool
+ optionPoParentStreams: bool
+ optionPoStdErrToStdOut: bool
+
+ proc startProcessAuxSpawn(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
+ proc startProcessAuxFork(data: TStartProcessData): TPid {.tags: [FExecIO, FReadEnv].}
+ {.push stacktrace: off, profiler: off.}
+ proc startProcessAfterFork(data: ptr TStartProcessData) {.
+ tags: [FExecIO, FReadEnv], cdecl.}
+ {.pop.}
+
proc startProcess(command: string,
workingDir: string = "",
args: openArray[string] = [],
@@ -604,100 +627,48 @@ elif not defined(useNimRtl):
pipe(pStderr) != 0'i32:
osError(osLastError())
- var sys_command: string
- var sys_args_raw: seq[string]
+ var sysCommand: string
+ var sysArgsRaw: seq[string]
if poEvalCommand in options:
- sys_command = "/bin/sh"
- sys_args_raw = @[sys_command, "-c", command]
+ sysCommand = "/bin/sh"
+ sysArgsRaw = @[sysCommand, "-c", command]
assert args.len == 0
else:
- sys_command = command
- sys_args_raw = @[command]
+ sysCommand = command
+ sysArgsRaw = @[command]
for arg in args.items:
- sys_args_raw.add arg
-
- var sys_args = allocCStringArray(sys_args_raw)
- finally: deallocCStringArray(sys_args)
+ sysArgsRaw.add arg
var pid: TPid
- when defined(posix_spawn) and not defined(useFork):
- var attr: Tposix_spawnattr
- var fops: Tposix_spawn_file_actions
-
- template chck(e: expr) =
- if e != 0'i32: osError(osLastError())
-
- chck posix_spawn_file_actions_init(fops)
- chck posix_spawnattr_init(attr)
-
- var mask: Tsigset
- chck sigemptyset(mask)
- chck posix_spawnattr_setsigmask(attr, mask)
- chck posix_spawnattr_setpgroup(attr, 0'i32)
-
- chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
- POSIX_SPAWN_SETSIGMASK or
- POSIX_SPAWN_SETPGROUP)
-
- if poParentStreams notin options:
- chck posix_spawn_file_actions_addclose(fops, pStdin[writeIdx])
- chck posix_spawn_file_actions_adddup2(fops, pStdin[readIdx], readIdx)
- chck posix_spawn_file_actions_addclose(fops, pStdout[readIdx])
- chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], writeIdx)
- chck posix_spawn_file_actions_addclose(fops, pStderr[readIdx])
- if poStdErrToStdOut in options:
- chck posix_spawn_file_actions_adddup2(fops, pStdout[writeIdx], 2)
- else:
- chck posix_spawn_file_actions_adddup2(fops, p_stderr[writeIdx], 2)
-
- var sys_env = if env == nil: envToCStringArray() else: envToCStringArray(env)
- var res: cint
- # This is incorrect!
- if workingDir.len > 0: os.setCurrentDir(workingDir)
- if poUsePath in options:
- res = posix_spawnp(pid, sys_command, fops, attr, sys_args, sys_env)
+
+ var sysArgs = allocCStringArray(sysArgsRaw)
+ finally: deallocCStringArray(sysArgs)
+
+ var sysEnv = if env == nil:
+ envToCStringArray()
else:
- res = posix_spawn(pid, sys_command, fops, attr, sys_args, sys_env)
- deallocCStringArray(sys_env)
- discard posix_spawn_file_actions_destroy(fops)
- discard posix_spawnattr_destroy(attr)
- chck res
+ envToCStringArray(env)
+
+ finally: deallocCStringArray(sysEnv)
+
+ var data: TStartProcessData
+ data.sysCommand = sysCommand
+ data.sysArgs = sysArgs
+ data.sysEnv = sysEnv
+ data.pStdin = pStdin
+ data.pStdout = pStdout
+ data.pStderr = pStderr
+ data.optionPoParentStreams = poParentStreams in options
+ data.optionPoUsePath = poUsePath in options
+ data.optionPoStdErrToStdOut = poStdErrToStdOut in options
+ data.workingDir = workingDir
+
+ when defined(posix_spawn) and not defined(useFork) and not defined(useClone) and not defined(linux):
+ pid = startProcessAuxSpawn(data)
else:
- pid = fork()
- if pid < 0: osError(osLastError())
- if pid == 0:
- ## child process:
-
- if poParentStreams notin options:
- discard close(p_stdin[writeIdx])
- if dup2(p_stdin[readIdx], readIdx) < 0: osError(osLastError())
- discard close(p_stdout[readIdx])
- if dup2(p_stdout[writeIdx], writeIdx) < 0: osError(osLastError())
- discard close(p_stderr[readIdx])
- if poStdErrToStdOut in options:
- if dup2(p_stdout[writeIdx], 2) < 0: osError(osLastError())
- else:
- if dup2(p_stderr[writeIdx], 2) < 0: osError(osLastError())
-
- # Create a new process group
- if setpgid(0, 0) == -1: quit("setpgid call failed: " & $strerror(errno))
-
- if workingDir.len > 0: os.setCurrentDir(workingDir)
-
- if env == nil:
- if poUsePath in options:
- discard execvp(sys_command, sys_args)
- else:
- discard execv(sys_command, sys_args)
- else:
- var c_env = envToCStringArray(env)
- if poUsePath in options:
- discard execvpe(sys_command, sys_args, c_env)
- else:
- discard execve(sys_command, sys_args, c_env)
- # too risky to raise an exception here:
- quit("execve call failed: " & $strerror(errno))
+ pid = startProcessAuxFork(data)
+
# Parent process. Copy process information.
if poEchoCmd in options:
echo(command, " ", join(args, " "))
@@ -723,6 +694,137 @@ elif not defined(useNimRtl):
discard close(pStdin[readIdx])
discard close(pStdout[writeIdx])
+ proc startProcessAuxSpawn(data: TStartProcessData): TPid =
+ var attr: Tposix_spawnattr
+ var fops: Tposix_spawn_file_actions
+
+ template chck(e: expr) =
+ if e != 0'i32: osError(osLastError())
+
+ chck posix_spawn_file_actions_init(fops)
+ chck posix_spawnattr_init(attr)
+
+ var mask: Tsigset
+ chck sigemptyset(mask)
+ chck posix_spawnattr_setsigmask(attr, mask)
+ chck posix_spawnattr_setpgroup(attr, 0'i32)
+
+ chck posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK or
+ POSIX_SPAWN_SETSIGMASK or
+ POSIX_SPAWN_SETPGROUP)
+
+ if not data.optionPoParentStreams:
+ chck posix_spawn_file_actions_addclose(fops, data.pStdin[writeIdx])
+ chck posix_spawn_file_actions_adddup2(fops, data.pStdin[readIdx], readIdx)
+ chck posix_spawn_file_actions_addclose(fops, data.pStdout[readIdx])
+ chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], writeIdx)
+ chck posix_spawn_file_actions_addclose(fops, data.pStderr[readIdx])
+ if data.optionPoStdErrToStdOut:
+ chck posix_spawn_file_actions_adddup2(fops, data.pStdout[writeIdx], 2)
+ else:
+ chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2)
+
+ var res: cint
+ # FIXME: chdir is global to process
+ if data.workingDir.len > 0:
+ setCurrentDir($data.workingDir)
+ var pid: TPid
+
+ if data.optionPoUsePath:
+ res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
+ else:
+ res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
+
+ discard posix_spawn_file_actions_destroy(fops)
+ discard posix_spawnattr_destroy(attr)
+ chck res
+ return pid
+
+ proc startProcessAuxFork(data: TStartProcessData): TPid =
+ if pipe(data.pErrorPipe) != 0:
+ osError(osLastError())
+
+ finally:
+ discard close(data.pErrorPipe[readIdx])
+
+ var pid: TPid
+ var dataCopy = data
+
+ when defined(useClone):
+ const stackSize = 65536
+ let stackEnd = cast[clong](alloc(stackSize))
+ let stack = cast[pointer](stackEnd + stackSize)
+ let fn: pointer = startProcessAfterFork
+ pid = clone(fn, stack,
+ cint(CLONE_VM or CLONE_VFORK or SIGCHLD),
+ pointer(addr dataCopy), nil, nil, nil)
+ discard close(data.pErrorPipe[writeIdx])
+ dealloc(stack)
+ else:
+ pid = fork()
+ if pid == 0:
+ startProcessAfterFork(addr(dataCopy))
+ exitnow(1)
+
+ discard close(data.pErrorPipe[writeIdx])
+ if pid < 0: osError(osLastError())
+
+ var error: cint
+ let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error))
+ if sizeRead == sizeof(error):
+ osError($strerror(error))
+
+ return pid
+
+ {.push stacktrace: off, profiler: off.}
+ proc startProcessFail(data: ptr TStartProcessData) =
+ var error: cint = errno
+ discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
+ exitnow(1)
+
+ when defined(macosx):
+ var environ {.importc.}: cstringArray
+
+ proc startProcessAfterFork(data: ptr TStartProcessData) =
+ # Warning: no GC here!
+ # Or anythink that touches global structures - all called nimrod procs
+ # must be marked with noStackFrame. Inspect C code after making changes.
+ if not data.optionPoParentStreams:
+ discard close(data.pStdin[writeIdx])
+ if dup2(data.pStdin[readIdx], readIdx) < 0:
+ startProcessFail(data)
+ discard close(data.pStdout[readIdx])
+ if dup2(data.pStdout[writeIdx], writeIdx) < 0:
+ startProcessFail(data)
+ discard close(data.pStderr[readIdx])
+ if data.optionPoStdErrToStdOut:
+ if dup2(data.pStdout[writeIdx], 2) < 0:
+ startProcessFail(data)
+ else:
+ if dup2(data.pStderr[writeIdx], 2) < 0:
+ startProcessFail(data)
+
+ if data.workingDir.len > 0:
+ if chdir(data.workingDir) < 0:
+ startProcessFail(data)
+
+ discard close(data.pErrorPipe[readIdx])
+ discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
+
+ if data.optionPoUsePath:
+ when defined(macosx):
+ # MacOSX doesn't have execvpe, so we need workaround.
+ # On MacOSX we can arrive here only from fork, so this is safe:
+ environ = data.sysEnv
+ discard execvp(data.sysCommand, data.sysArgs)
+ else:
+ discard execvpe(data.sysCommand, data.sysArgs, data.sysEnv)
+ else:
+ discard execve(data.sysCommand, data.sysArgs, data.sysEnv)
+
+ startProcessFail(data)
+ {.pop}
+
proc close(p: PProcess) =
if p.inStream != nil: close(p.inStream)
if p.outStream != nil: close(p.outStream)
@@ -791,7 +893,10 @@ elif not defined(useNimRtl):
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
proc execCmd(command: string): int =
- result = csystem(command) shr 8
+ when defined(linux):
+ result = csystem(command) shr 8
+ else:
+ result = csystem(command)
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
FD_ZERO(fd)
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 70b617393..68b1ab223 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -836,9 +836,11 @@ iterator findAll*(s: string, pattern: TPeg, start = 0): string =
while i < s.len:
c.ml = 0
var L = rawMatch(s, pattern, i, c)
- if L < 0: break
- yield substr(s, i, i+L-1)
- inc(i, L)
+ if L < 0:
+ inc(i, 1)
+ else:
+ yield substr(s, i, i+L-1)
+ inc(i, L)
proc findAll*(s: string, pattern: TPeg, start = 0): seq[string] {.
nosideEffect, rtl, extern: "npegs$1".} =
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 83c158da1..6482a01a6 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
-# (c) Copyright 2013 Dominik Picheta
+# (c) Copyright 2014 Dominik Picheta
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -9,212 +9,211 @@
# TODO: Docs.
-import tables, os, unsigned
-when defined(windows):
- import winlean
-else:
- import posix
+import tables, os, unsigned, hashes
+
+when defined(linux): import posix, epoll
+elif defined(windows): import winlean
+
+proc hash*(x: TSocketHandle): THash {.borrow.}
type
TEvent* = enum
EvRead, EvWrite
- TSelectorKey* = object
- fd: cint
- events: set[TEvent]
- data: PObject
-
- TReadyInfo* = tuple[key: TSelectorKey, events: set[TEvent]]
-
- PSelector* = ref object of PObject ## Selector interface.
- fds*: TTable[cint, TSelectorKey]
- registerImpl*: proc (s: PSelector, fd: cint, events: set[TEvent],
- data: PObject): TSelectorKey {.nimcall, tags: [FWriteIO].}
- unregisterImpl*: proc (s: PSelector, fd: cint): TSelectorKey {.nimcall, tags: [FWriteIO].}
- selectImpl*: proc (s: PSelector, timeout: int): seq[TReadyInfo] {.nimcall, tags: [FReadIO].}
- closeImpl*: proc (s: PSelector) {.nimcall.}
-
-template initSelector(r: expr) =
- new r
- r.fds = initTable[cint, TSelectorKey]()
-
-proc register*(s: PSelector, fd: cint, events: set[TEvent], data: PObject):
- TSelectorKey =
- if not s.registerImpl.isNil: result = s.registerImpl(s, fd, events, data)
-
-proc unregister*(s: PSelector, fd: cint): TSelectorKey =
- ##
- ## **Note:** For the ``epoll`` implementation the resulting ``TSelectorKey``
- ## will only have the ``fd`` field set. This is an optimisation and may
- ## change in the future if a viable use case is presented.
- if not s.unregisterImpl.isNil: result = s.unregisterImpl(s, fd)
-
-proc select*(s: PSelector, timeout = 500): seq[TReadyInfo] =
- ##
- ## The ``events`` field of the returned ``key`` contains the original events
- ## for which the ``fd`` was bound. This is contrary to the ``events`` field
- ## of the ``TReadyInfo`` tuple which determines which events are ready
- ## on the ``fd``.
-
- if not s.selectImpl.isNil: result = s.selectImpl(s, timeout)
+ PSelectorKey* = ref object
+ fd*: TSocketHandle
+ events*: set[TEvent] ## The events which ``fd`` listens for.
+ data*: PObject ## User object.
-proc close*(s: PSelector) =
- if not s.closeImpl.isNil: s.closeImpl(s)
+ TReadyInfo* = tuple[key: PSelectorKey, events: set[TEvent]]
-# ---- Select() ----------------------------------------------------------------
-
-type
- PSelectSelector* = ref object of PSelector ## Implementation of select()
-
-proc ssRegister(s: PSelector, fd: cint, events: set[TEvent],
- data: PObject): TSelectorKey =
- if s.fds.hasKey(fd):
- raise newException(EInvalidValue, "FD already exists in selector.")
- var sk = TSelectorKey(fd: fd, events: events, data: data)
- s.fds[fd] = sk
- result = sk
-
-proc ssUnregister(s: PSelector, fd: cint): TSelectorKey =
- result = s.fds[fd]
- s.fds.del(fd)
-
-proc ssClose(s: PSelector) = nil
-
-proc timeValFromMilliseconds(timeout: int): TTimeVal =
- if timeout != -1:
- var seconds = timeout div 1000
- result.tv_sec = seconds.int32
- result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
-
-proc createFdSet(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey],
- m: var int) =
- FD_ZERO(rd); FD_ZERO(wr)
- for k, v in pairs(fds):
- if EvRead in v.events:
- m = max(m, int(k))
- FD_SET(k, rd)
- if EvWrite in v.events:
- m = max(m, int(k))
- FD_SET(k, wr)
-
-proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[cint, TSelectorKey]):
- seq[TReadyInfo] =
- result = @[]
- for k, v in pairs(fds):
- var events: set[TEvent] = {}
- if FD_ISSET(k, rd) != 0'i32:
- events = events + {EvRead}
- if FD_ISSET(k, wr) != 0'i32:
- events = events + {EvWrite}
- result.add((v, events))
-
-proc select(fds: TTable[cint, TSelectorKey], timeout = 500):
- seq[TReadyInfo] =
- var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
-
- var rd, wr: TFdSet
- var m = 0
- createFdSet(rd, wr, fds, m)
-
- var retCode = 0
- if timeout != -1:
- retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
- else:
- retCode = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
-
- if retCode < 0:
- OSError(OSLastError())
- elif retCode == 0:
- return @[]
- else:
- return getReadyFDs(rd, wr, fds)
-
-proc ssSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
- result = select(s.fds, timeout)
-
-proc newSelectSelector*(): PSelectSelector =
- initSelector(result)
- result.registerImpl = ssRegister
- result.unregisterImpl = ssUnregister
- result.selectImpl = ssSelect
- result.closeImpl = ssClose
-
-# ---- Epoll -------------------------------------------------------------------
-
-when defined(linux):
- import epoll
+when defined(linux) or defined(nimdoc):
type
- PEpollSelector* = ref object of PSelector
+ PSelector* = ref object
epollFD: cint
events: array[64, ptr epoll_event]
+ fds: TTable[TSocketHandle, PSelectorKey]
- TDataWrapper = object
- fd: cint
- boundEvents: set[TEvent] ## The events which ``fd`` listens for.
- data: PObject ## User object.
-
- proc esRegister(s: PSelector, fd: cint, events: set[TEvent],
- data: PObject): TSelectorKey =
- var es = PEpollSelector(s)
- var event: epoll_event
+ proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
if EvRead in events:
- event.events = EPOLLIN
+ result.events = EPOLLIN
if EvWrite in events:
- event.events = event.events or EPOLLOUT
-
- var dw = cast[ptr TDataWrapper](alloc0(sizeof(TDataWrapper))) # TODO: This needs to be dealloc'd
- dw.fd = fd
- dw.boundEvents = events
- dw.data = data
- event.data.thePtr = dw
+ result.events = result.events or EPOLLOUT
+ result.data.fd = fd.cint
+
+ proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
+ data: PObject): PSelectorKey {.discardable.} =
+ ## Registers file descriptor ``fd`` to selector ``s`` with a set of TEvent
+ ## ``events``.
+ if s.fds.hasKey(fd):
+ raise newException(EInvalidValue, "File descriptor already exists.")
- if epoll_ctl(es.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
+ var event = createEventStruct(events, fd)
+
+ if epoll_ctl(s.epollFD, EPOLL_CTL_ADD, fd, addr(event)) != 0:
OSError(OSLastError())
-
- result = TSelectorKey(fd: fd, events: events, data: data)
- proc esUnregister(s: PSelector, fd: cint): TSelectorKey =
- # We cannot find out the information about this ``fd`` from the epoll
- # context. As such I will simply return an almost empty TSelectorKey.
- var es = PEpollSelector(s)
- if epoll_ctl(es.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
+ var key = PSelectorKey(fd: fd, events: events, data: data)
+
+ s.fds[fd] = key
+ result = key
+
+ proc update*(s: PSelector, fd: TSocketHandle,
+ events: set[TEvent]): PSelectorKey {.discardable.} =
+ ## Updates the events which ``fd`` wants notifications for.
+ if not s.fds.hasKey(fd):
+ raise newException(EInvalidValue, "File descriptor not found.")
+ var event = createEventStruct(events, fd)
+
+ s.fds[fd].events = events
+ echo("About to update")
+ if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
OSError(OSLastError())
- # We could fill in the ``fds`` TTable to get the info, but that wouldn't
- # be nice for our memory.
- result = TSelectorKey(fd: fd, events: {}, data: nil)
-
- proc esClose(s: PSelector) =
- var es = PEpollSelector(s)
- if es.epollFD.close() != 0: OSError(OSLastError())
- dealloc(addr es.events) # TODO: Test this
+ echo("finished updating")
+ result = s.fds[fd]
- proc esSelect(s: PSelector, timeout: int): seq[TReadyInfo] =
+ proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
+ if not s.fds.hasKey(fd):
+ raise newException(EInvalidValue, "File descriptor not found.")
+ if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
+ OSError(OSLastError())
+ result = s.fds[fd]
+ s.fds.del(fd)
+
+ proc close*(s: PSelector) =
+ if s.epollFD.close() != 0: OSError(OSLastError())
+ dealloc(addr s.events) # TODO: Test this
+
+ proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
+ ##
+ ## The ``events`` field of the returned ``key`` contains the original events
+ ## for which the ``fd`` was bound. This is contrary to the ``events`` field
+ ## of the ``TReadyInfo`` tuple which determines which events are ready
+ ## on the ``fd``.
result = @[]
- var es = PEpollSelector(s)
- let evNum = epoll_wait(es.epollFD, es.events[0], 64.cint, timeout.cint)
+ let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint)
if evNum < 0: OSError(OSLastError())
if evNum == 0: return @[]
for i in 0 .. <evNum:
var evSet: set[TEvent] = {}
- if (es.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
- if (es.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
- let dw = cast[ptr TDataWrapper](es.events[i].data.thePtr)
+ if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
+ if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
- let selectorKey = TSelectorKey(fd: dw.fd, events: dw.boundEvents,
- data: dw.data)
+ let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
result.add((selectorKey, evSet))
- proc newEpollSelector*(): PEpollSelector =
+ proc newSelector*(): PSelector =
new result
result.epollFD = epoll_create(64)
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64))
+ result.fds = initTable[TSocketHandle, PSelectorKey]()
if result.epollFD < 0:
OSError(OSLastError())
- result.registerImpl = esRegister
- result.unregisterImpl = esUnregister
- result.closeImpl = esClose
- result.selectImpl = esSelect
+
+ proc contains*(s: PSelector, fd: TSocketHandle): bool =
+ ## Determines whether selector contains a file descriptor.
+ return s.fds.hasKey(fd)
+
+ proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
+ ## Retrieves the selector key for ``fd``.
+ return s.fds[fd]
+
+elif defined(windows):
+ type
+ PSelector* = ref object
+ fds: TTable[TSocketHandle, PSelectorKey]
+
+ proc register*(s: PSelector, fd: TSocketHandle, events: set[TEvent],
+ data: PObject): PSelectorKey {.discardable.} =
+ if s.fds.hasKey(fd):
+ raise newException(EInvalidValue, "File descriptor already exists.")
+ var sk = PSelectorKey(fd: fd, events: events, data: data)
+ s.fds[fd] = sk
+ result = sk
+
+ proc update*(s: PSelector, fd: TSocketHandle,
+ events: set[TEvent]): PSelectorKey {.discardable.} =
+ ## Updates the events which ``fd`` wants notifications for.
+ if not s.fds.hasKey(fd):
+ raise newException(EInvalidValue, "File descriptor not found.")
+
+ s.fds[fd].events = events
+ result = s.fds[fd]
+
+ proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
+ result = s.fds[fd]
+ s.fds.del(fd)
+
+ proc close*(s: PSelector) = nil
+
+ proc timeValFromMilliseconds(timeout: int): TTimeVal =
+ if timeout != -1:
+ var seconds = timeout div 1000
+ result.tv_sec = seconds.int32
+ result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
+
+ proc createFdSet(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey],
+ m: var int) =
+ FD_ZERO(rd); FD_ZERO(wr)
+ for k, v in pairs(fds):
+ if EvRead in v.events:
+ m = max(m, int(k))
+ FD_SET(k, rd)
+ if EvWrite in v.events:
+ m = max(m, int(k))
+ FD_SET(k, wr)
+
+ proc getReadyFDs(rd, wr: var TFdSet, fds: TTable[TSocketHandle, PSelectorKey]):
+ seq[TReadyInfo] =
+ result = @[]
+ for k, v in pairs(fds):
+ var events: set[TEvent] = {}
+ if FD_ISSET(k, rd) != 0'i32:
+ events = events + {EvRead}
+ if FD_ISSET(k, wr) != 0'i32:
+ events = events + {EvWrite}
+ result.add((v, events))
+
+ proc select(fds: TTable[TSocketHandle, PSelectorKey], timeout = 500):
+ seq[TReadyInfo] =
+ var tv {.noInit.}: TTimeVal = timeValFromMilliseconds(timeout)
+
+ var rd, wr: TFdSet
+ var m = 0
+ createFdSet(rd, wr, fds, m)
+
+ var retCode = 0
+ if timeout != -1:
+ retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, addr(tv)))
+ else:
+ retCode = int(select(TSocketHandle(m+1), addr(rd), addr(wr), nil, nil))
+
+ if retCode < 0:
+ OSError(OSLastError())
+ elif retCode == 0:
+ return @[]
+ else:
+ return getReadyFDs(rd, wr, fds)
+
+ proc select*(s: PSelector, timeout: int): seq[TReadyInfo] =
+ result = select(s.fds, timeout)
+
+ proc newSelector*(): PSelector =
+ new result
+ result.fds = initTable[TSocketHandle, PSelectorKey]()
+
+ proc contains*(s: PSelector, fd: TSocketHandle): bool =
+ return s.fds.hasKey(fd)
+
+ proc `[]`*(s: PSelector, fd: TSocketHandle): PSelectorKey =
+ return s.fds[fd]
+
+elif defined(bsd) or defined(macosx):
+ # TODO: kqueue
+ {.error: "Sorry your platform is not supported yet.".}
+else:
+ {.error: "Sorry your platform is not supported.".}
when isMainModule:
# Select()
@@ -224,11 +223,12 @@ when isMainModule:
sock: TSocket
var sock = socket()
+ sock.setBlocking(false)
sock.connect("irc.freenode.net", TPort(6667))
- var selector = newEpollSelector()
+ var selector = newSelector()
var data = PSockWrapper(sock: sock)
- let key = selector.register(sock.getFD.cint, {EvRead}, data)
+ let key = selector.register(sock.getFD, {EvWrite}, data)
var i = 0
while true:
let ready = selector.select(1000)
@@ -236,6 +236,7 @@ when isMainModule:
if ready.len > 0: echo ready[0].events
i.inc
if i == 6:
+ assert selector.unregister(sock.getFD).fd == sock.getFD
selector.close()
break
diff --git a/lib/pure/sockets2.nim b/lib/pure/sockets2.nim
index 22624bbad..3542a0694 100644
--- a/lib/pure/sockets2.nim
+++ b/lib/pure/sockets2.nim
@@ -19,9 +19,10 @@ when defined(Windows):
import winlean
else:
import posix
+ export fcntl, F_GETFL, O_NONBLOCK, F_SETFL
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
- inet_ntoa
+ inet_ntoa, recv, `==`, connect, send, accept
type
@@ -63,10 +64,20 @@ type
when defined(windows):
let
- OSInvalidSocket* = winlean.INVALID_SOCKET
+ osInvalidSocket* = winlean.INVALID_SOCKET
+
+ const
+ IOCPARM_MASK* = 127
+ IOC_IN* = int(-2147483648)
+ FIONBIO* = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
+ (102 shl 8) or 126
+
+ proc ioctlsocket*(s: TSocketHandle, cmd: clong,
+ argptr: ptr clong): cint {.
+ stdcall, importc: "ioctlsocket", dynlib: "ws2_32.dll".}
else:
let
- OSInvalidSocket* = posix.INVALID_SOCKET
+ osInvalidSocket* = posix.INVALID_SOCKET
proc `==`*(a, b: TPort): bool {.borrow.}
## ``==`` for ports.
@@ -89,7 +100,7 @@ when defined(posix):
of AF_UNIX: result = posix.AF_UNIX
of AF_INET: result = posix.AF_INET
of AF_INET6: result = posix.AF_INET6
- else: nil
+ else: discard
proc toInt(typ: TType): cint =
case typ
@@ -97,7 +108,7 @@ when defined(posix):
of SOCK_DGRAM: result = posix.SOCK_DGRAM
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
of SOCK_RAW: result = posix.SOCK_RAW
- else: nil
+ else: discard
proc toInt(p: TProtocol): cint =
case p
@@ -107,7 +118,7 @@ when defined(posix):
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
of IPPROTO_RAW: result = posix.IPPROTO_RAW
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
- else: nil
+ else: discard
else:
proc toInt(domain: TDomain): cint =
@@ -199,4 +210,4 @@ proc htons*(x: int16): int16 =
when defined(Windows):
var wsa: TWSADATA
- if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) \ No newline at end of file
+ if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index de6c4e4fa..2fce235e2 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -557,6 +557,119 @@ proc `$`*(m: TMonth): string =
"November", "December"]
return lookup[m]
+proc format_token(info: TTimeInfo, token: string, buf: var string) =
+ ## Helper of the format proc to parse individual tokens.
+ ##
+ ## Pass the found token in the user input string, and the buffer where the
+ ## final string is being built. This has to be a var value because certain
+ ## formatting tokens require modifying the previous characters.
+ case token
+ of "d":
+ buf.add($info.monthday)
+ of "dd":
+ if info.monthday < 10:
+ buf.add("0")
+ buf.add($info.monthday)
+ of "ddd":
+ buf.add(($info.weekday)[0 .. 2])
+ of "dddd":
+ buf.add($info.weekday)
+ of "h":
+ buf.add($(if info.hour > 12: info.hour - 12 else: info.hour))
+ of "hh":
+ let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
+ if amerHour < 10:
+ buf.add('0')
+ buf.add($amerHour)
+ of "H":
+ buf.add($info.hour)
+ of "HH":
+ if info.hour < 10:
+ buf.add('0')
+ buf.add($info.hour)
+ of "m":
+ buf.add($info.minute)
+ of "mm":
+ if info.minute < 10:
+ buf.add('0')
+ buf.add($info.minute)
+ of "M":
+ buf.add($(int(info.month)+1))
+ of "MM":
+ if info.month < mOct:
+ buf.add('0')
+ buf.add($(int(info.month)+1))
+ of "MMM":
+ buf.add(($info.month)[0..2])
+ of "MMMM":
+ buf.add($info.month)
+ of "s":
+ buf.add($info.second)
+ of "ss":
+ if info.second < 10:
+ buf.add('0')
+ buf.add($info.second)
+ of "t":
+ if info.hour >= 12:
+ buf.add('P')
+ else: buf.add('A')
+ of "tt":
+ if info.hour >= 12:
+ buf.add("PM")
+ else: buf.add("AM")
+ of "y":
+ var fr = ($info.year).len()-1
+ if fr < 0: fr = 0
+ buf.add(($info.year)[fr .. ($info.year).len()-1])
+ of "yy":
+ var fr = ($info.year).len()-2
+ if fr < 0: fr = 0
+ var fyear = ($info.year)[fr .. ($info.year).len()-1]
+ if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
+ buf.add(fyear)
+ of "yyy":
+ var fr = ($info.year).len()-3
+ if fr < 0: fr = 0
+ var fyear = ($info.year)[fr .. ($info.year).len()-1]
+ if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
+ buf.add(fyear)
+ of "yyyy":
+ var fr = ($info.year).len()-4
+ if fr < 0: fr = 0
+ var fyear = ($info.year)[fr .. ($info.year).len()-1]
+ if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
+ buf.add(fyear)
+ of "yyyyy":
+ var fr = ($info.year).len()-5
+ if fr < 0: fr = 0
+ var fyear = ($info.year)[fr .. ($info.year).len()-1]
+ if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
+ buf.add(fyear)
+ of "z":
+ let hrs = (info.timezone div 60) div 60
+ buf.add($hrs)
+ of "zz":
+ let hrs = (info.timezone div 60) div 60
+
+ buf.add($hrs)
+ if hrs.abs < 10:
+ var atIndex = buf.len-(($hrs).len-(if hrs < 0: 1 else: 0))
+ buf.insert("0", atIndex)
+ of "zzz":
+ let hrs = (info.timezone div 60) div 60
+
+ buf.add($hrs & ":00")
+ if hrs.abs < 10:
+ var atIndex = buf.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
+ buf.insert("0", atIndex)
+ of "ZZZ":
+ buf.add(info.tzname)
+ of "":
+ discard
+ else:
+ raise newException(EInvalidValue, "Invalid format string: " & token)
+
+
proc format*(info: TTimeInfo, f: string): string =
## This function formats `info` as specified by `f`. The following format
## specifiers are available:
@@ -591,8 +704,11 @@ proc format*(info: TTimeInfo, f: string): string =
## ZZZ Displays the name of the timezone. ``GMT -> GMT``, ``EST -> EST``
## ========== ================================================================================= ================================================
##
- ## Other strings can be inserted by putting them in ``''``. For example ``hh'->'mm`` will give ``01->56``.
- ## The following characters can be inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]`` ``,``
+ ## Other strings can be inserted by putting them in ``''``. For example
+ ## ``hh'->'mm`` will give ``01->56``. The following characters can be
+ ## inserted without quoting them: ``:`` ``-`` ``(`` ``)`` ``/`` ``[`` ``]``
+ ## ``,``. However you don't need to necessarily separate format specifiers, a
+ ## unambiguous format string like ``yyyyMMddhhmmss`` is valid too.
result = ""
var i = 0
@@ -600,112 +716,8 @@ proc format*(info: TTimeInfo, f: string): string =
while true:
case f[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
- case currentF
- of "d":
- result.add($info.monthday)
- of "dd":
- if info.monthday < 10:
- result.add("0")
- result.add($info.monthday)
- of "ddd":
- result.add(($info.weekday)[0 .. 2])
- of "dddd":
- result.add($info.weekday)
- of "h":
- result.add($(if info.hour > 12: info.hour - 12 else: info.hour))
- of "hh":
- let amerHour = if info.hour > 12: info.hour - 12 else: info.hour
- if amerHour < 10:
- result.add('0')
- result.add($amerHour)
- of "H":
- result.add($info.hour)
- of "HH":
- if info.hour < 10:
- result.add('0')
- result.add($info.hour)
- of "m":
- result.add($info.minute)
- of "mm":
- if info.minute < 10:
- result.add('0')
- result.add($info.minute)
- of "M":
- result.add($(int(info.month)+1))
- of "MM":
- if info.month < mOct:
- result.add('0')
- result.add($(int(info.month)+1))
- of "MMM":
- result.add(($info.month)[0..2])
- of "MMMM":
- result.add($info.month)
- of "s":
- result.add($info.second)
- of "ss":
- if info.second < 10:
- result.add('0')
- result.add($info.second)
- of "t":
- if info.hour >= 12:
- result.add('P')
- else: result.add('A')
- of "tt":
- if info.hour >= 12:
- result.add("PM")
- else: result.add("AM")
- of "y":
- var fr = ($info.year).len()-1
- if fr < 0: fr = 0
- result.add(($info.year)[fr .. ($info.year).len()-1])
- of "yy":
- var fr = ($info.year).len()-2
- if fr < 0: fr = 0
- var fyear = ($info.year)[fr .. ($info.year).len()-1]
- if fyear.len != 2: fyear = repeatChar(2-fyear.len(), '0') & fyear
- result.add(fyear)
- of "yyy":
- var fr = ($info.year).len()-3
- if fr < 0: fr = 0
- var fyear = ($info.year)[fr .. ($info.year).len()-1]
- if fyear.len != 3: fyear = repeatChar(3-fyear.len(), '0') & fyear
- result.add(fyear)
- of "yyyy":
- var fr = ($info.year).len()-4
- if fr < 0: fr = 0
- var fyear = ($info.year)[fr .. ($info.year).len()-1]
- if fyear.len != 4: fyear = repeatChar(4-fyear.len(), '0') & fyear
- result.add(fyear)
- of "yyyyy":
- var fr = ($info.year).len()-5
- if fr < 0: fr = 0
- var fyear = ($info.year)[fr .. ($info.year).len()-1]
- if fyear.len != 5: fyear = repeatChar(5-fyear.len(), '0') & fyear
- result.add(fyear)
- of "z":
- let hrs = (info.timezone div 60) div 60
- result.add($hrs)
- of "zz":
- let hrs = (info.timezone div 60) div 60
-
- result.add($hrs)
- if hrs.abs < 10:
- var atIndex = result.len-(($hrs).len-(if hrs < 0: 1 else: 0))
- result.insert("0", atIndex)
- of "zzz":
- let hrs = (info.timezone div 60) div 60
-
- result.add($hrs & ":00")
- if hrs.abs < 10:
- var atIndex = result.len-(($hrs & ":00").len-(if hrs < 0: 1 else: 0))
- result.insert("0", atIndex)
- of "ZZZ":
- result.add(info.tzname)
- of "":
- discard
- else:
- raise newException(EInvalidValue, "Invalid format string: " & currentF)
-
+ format_token(info, currentF, result)
+
currentF = ""
if f[i] == '\0': break
@@ -716,7 +728,15 @@ proc format*(info: TTimeInfo, f: string): string =
inc(i)
else: result.add(f[i])
- else: currentF.add(f[i])
+ else:
+ # Check if the letter being added matches previous accumulated buffer.
+ if currentF.len < 1 or currentF[high(currentF)] == f[i]:
+ currentF.add(f[i])
+ else:
+ format_token(info, currentF, result)
+ dec(i) # Move position back to re-process the character separately.
+ currentF = ""
+
inc(i)
{.pop.}
@@ -727,11 +747,15 @@ when isMainModule:
var t = getGMTime(fromSeconds(2147483647))
echo t.format("ddd dd MMM hh:mm:ss ZZZ yyyy")
+ echo t.format("ddd ddMMMhhmmssZZZyyyy")
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
+ assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
+
+ assert t.format("yyyyMMddhhmmss") == "20380119031407"
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
diff --git a/lib/stdlib.babel b/lib/stdlib.babel
new file mode 100644
index 000000000..f22598aba
--- /dev/null
+++ b/lib/stdlib.babel
@@ -0,0 +1,6 @@
+[Package]
+name = "stdlib"
+version = "0.9.0"
+author = "Dominik Picheta"
+description = "Nimrod's standard library."
+license = "MIT"
diff --git a/lib/system.nim b/lib/system.nim
index 2acb989c5..24ad50f97 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -185,6 +185,8 @@ proc `..`*[T](b: T): TSlice[T] {.noSideEffect, inline.} =
when not defined(niminheritable):
{.pragma: inheritable.}
+when not defined(nimunion):
+ {.pragma: unchecked.}
const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \
## "fake variables" like 'var EBADF {.importc.}: cint'.
@@ -194,9 +196,10 @@ when not defined(JS):
TGenericSeq {.compilerproc, pure, inheritable.} = object
len, reserved: int
PGenericSeq {.exportc.} = ptr TGenericSeq
+ UncheckedCharArray {.unchecked.} = array[0..100_000_000, char]
# len and space without counting the terminating zero:
NimStringDesc {.compilerproc, final.} = object of TGenericSeq
- data: array[0..100_000_000, char]
+ data: UncheckedCharArray
NimString = ptr NimStringDesc
when not defined(JS) and not defined(NimrodVM):
@@ -1161,6 +1164,14 @@ when not defined(nimrodVM):
## from it before writing to it is undefined behaviour!
## The allocated memory belongs to its allocating thread!
## Use `allocShared` to allocate from a shared heap.
+ proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
+ ## allocates a new memory block with at least ``T.sizeof * size``
+ ## bytes. The block has to be freed with ``resize(block, 0)`` or
+ ## ``free(block)``. The block is not initialized, so reading
+ ## from it before writing to it is undefined behaviour!
+ ## The allocated memory belongs to its allocating thread!
+ ## Use `createSharedU` to allocate from a shared heap.
+ cast[ptr T](alloc(T.sizeof * size))
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
## allocates a new memory block with at least ``size`` bytes. The
## block has to be freed with ``realloc(block, 0)`` or
@@ -1168,14 +1179,31 @@ when not defined(nimrodVM):
## containing zero, so it is somewhat safer than ``alloc``.
## The allocated memory belongs to its allocating thread!
## Use `allocShared0` to allocate from a shared heap.
- proc realloc*(p: pointer, newsize: int): pointer {.noconv, rtl, tags: [].}
+ proc create*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
+ ## allocates a new memory block with at least ``T.sizeof * size``
+ ## bytes. The block has to be freed with ``resize(block, 0)`` or
+ ## ``free(block)``. The block is initialized with all bytes
+ ## containing zero, so it is somewhat safer than ``createU``.
+ ## The allocated memory belongs to its allocating thread!
+ ## Use `createShared` to allocate from a shared heap.
+ cast[ptr T](alloc0(T.sizeof * size))
+ proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].}
## grows or shrinks a given memory block. If p is **nil** then a new
## memory block is returned. In either way the block has at least
- ## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
+ ## ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
## ``realloc`` calls ``dealloc(p)``. In other cases the block has to
## be freed with ``dealloc``.
## The allocated memory belongs to its allocating thread!
## Use `reallocShared` to reallocate from a shared heap.
+ proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
+ ## grows or shrinks a given memory block. If p is **nil** then a new
+ ## memory block is returned. In either way the block has at least
+ ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not
+ ## **nil** ``resize`` calls ``free(p)``. In other cases the block
+ ## has to be freed with ``free``. The allocated memory belongs to
+ ## its allocating thread!
+ ## Use `resizeShared` to reallocate from a shared heap.
+ cast[ptr T](realloc(p, T.sizeof * newSize))
proc dealloc*(p: pointer) {.noconv, rtl, tags: [].}
## frees the memory allocated with ``alloc``, ``alloc0`` or
## ``realloc``. This procedure is dangerous! If one forgets to
@@ -1184,31 +1212,60 @@ when not defined(nimrodVM):
## or other memory may be corrupted.
## The freed memory must belong to its allocating thread!
## Use `deallocShared` to deallocate from a shared heap.
-
+ proc free*[T](p: ptr T) {.inline.} =
+ dealloc(p)
proc allocShared*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
## is not initialized, so reading from it before writing to it is
## undefined behaviour!
+ proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
+ ## allocates a new memory block on the shared heap with at
+ ## least ``T.sizeof * size`` bytes. The block has to be freed with
+ ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block
+ ## is not initialized, so reading from it before writing to it is
+ ## undefined behaviour!
+ cast[ptr T](allocShared(T.sizeof * size))
proc allocShared0*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``.
- proc reallocShared*(p: pointer, newsize: int): pointer {.noconv, rtl.}
+ proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
+ ## allocates a new memory block on the shared heap with at
+ ## least ``T.sizeof * size`` bytes. The block has to be freed with
+ ## ``resizeShared(block, 0)`` or ``freeShared(block)``.
+ ## The block is initialized with all bytes
+ ## containing zero, so it is somewhat safer than ``createSharedU``.
+ cast[ptr T](allocShared0(T.sizeof * size))
+ proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.}
## grows or shrinks a given memory block on the heap. If p is **nil**
- ## then a new memory block is returned. In either way the block has at least
- ## ``newsize`` bytes. If ``newsize == 0`` and p is not **nil**
+ ## then a new memory block is returned. In either way the block has at
+ ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
## block has to be freed with ``deallocShared``.
+ proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
+ ## grows or shrinks a given memory block on the heap. If p is **nil**
+ ## then a new memory block is returned. In either way the block has at
+ ## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is
+ ## not **nil** ``resizeShared`` calls ``freeShared(p)``. In other
+ ## cases the block has to be freed with ``freeShared``.
+ cast[ptr T](reallocShared(p, T.sizeof * newSize))
proc deallocShared*(p: pointer) {.noconv, rtl.}
## frees the memory allocated with ``allocShared``, ``allocShared0`` or
## ``reallocShared``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted.
+ proc freeShared*[T](p: ptr T) {.inline.} =
+ ## frees the memory allocated with ``createShared``, ``createSharedU`` or
+ ## ``resizeShared``. This procedure is dangerous! If one forgets to
+ ## free the memory a leak occurs; if one tries to access freed
+ ## memory (or just freeing it twice!) a core dump may happen
+ ## or other memory may be corrupted.
+ deallocShared(p)
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
## swaps the values `a` and `b`. This is often more efficient than
@@ -1514,7 +1571,7 @@ when not defined(NimrodVM):
proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} =
result = cast[pointer](x)
else:
- proc seqToPtr[T](x: seq[T]): pointer {.noStackFrame, nosideeffect.} =
+ proc seqToPtr[T](x: seq[T]): pointer {.asmNoStackFrame, nosideeffect.} =
asm """return `x`"""
proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
@@ -1797,7 +1854,7 @@ type
len*: int ## length of the inspectable slots
when defined(JS):
- proc add*(x: var string, y: cstring) {.noStackFrame.} =
+ proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
asm """
var len = `x`[0].length-1;
for (var i = 0; i < `y`.length; ++i) {
@@ -2014,8 +2071,10 @@ when not defined(JS): #and not defined(NimrodVM):
## Flushes `f`'s buffer.
proc readAll*(file: TFile): TaintedString {.tags: [FReadIO].}
- ## Reads all data from the stream `file`. Raises an IO exception
- ## in case of an error
+ ## Reads all data from the stream `file`.
+ ##
+ ## Raises an IO exception in case of an error. It is an error if the
+ ## current file position is not at the beginning of the file.
proc readFile*(filename: string): TaintedString {.tags: [FReadIO].}
## Opens a file named `filename` for reading. Then calls `readAll`
diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim
index d764a6672..d9b3aebac 100644
--- a/lib/system/arithm.nim
+++ b/lib/system/arithm.nim
@@ -111,7 +111,7 @@ const
when asmVersion and not defined(gcc) and not defined(llvm_gcc):
# assembler optimized versions for compilers that
# have an intel syntax assembler:
- proc addInt(a, b: int): int {.compilerProc, noStackFrame.} =
+ proc addInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
# a in eax, and b in edx
asm """
mov eax, `a`
@@ -121,7 +121,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
theEnd:
"""
- proc subInt(a, b: int): int {.compilerProc, noStackFrame.} =
+ proc subInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """
mov eax, `a`
sub eax, `b`
@@ -130,7 +130,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
theEnd:
"""
- proc negInt(a: int): int {.compilerProc, noStackFrame.} =
+ proc negInt(a: int): int {.compilerProc, asmNoStackFrame.} =
asm """
mov eax, `a`
neg eax
@@ -139,7 +139,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
theEnd:
"""
- proc divInt(a, b: int): int {.compilerProc, noStackFrame.} =
+ proc divInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """
mov eax, `a`
mov ecx, `b`
@@ -150,7 +150,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
theEnd:
"""
- proc modInt(a, b: int): int {.compilerProc, noStackFrame.} =
+ proc modInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """
mov eax, `a`
mov ecx, `b`
@@ -162,7 +162,7 @@ when asmVersion and not defined(gcc) and not defined(llvm_gcc):
mov eax, edx
"""
- proc mulInt(a, b: int): int {.compilerProc, noStackFrame.} =
+ proc mulInt(a, b: int): int {.compilerProc, asmNoStackFrame.} =
asm """
mov eax, `a`
mov ecx, `b`
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index 4fc5f479b..1720804c4 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -23,9 +23,9 @@ type
PCallFrame = ptr TCallFrame
TCallFrame {.importc, nodecl, final.} = object
prev: PCallFrame
- procname: CString
+ procname: cstring
line: int # current line number
- filename: CString
+ filename: cstring
var
framePtr {.importc, nodecl, volatile.}: PCallFrame
@@ -48,7 +48,7 @@ proc getCurrentExceptionMsg*(): string =
proc auxWriteStackTrace(f: PCallFrame): string =
type
- TTempFrame = tuple[procname: CString, line: int]
+ TTempFrame = tuple[procname: cstring, line: int]
var
it = f
i = 0
@@ -84,7 +84,7 @@ proc rawWriteStackTrace(): string =
framePtr = nil
proc raiseException(e: ref E_Base, ename: cstring) {.
- compilerproc, noStackFrame.} =
+ compilerproc, asmNoStackFrame.} =
e.name = ename
if excHandler != nil:
excHandler.exc = e
@@ -104,7 +104,7 @@ proc raiseException(e: ref E_Base, ename: cstring) {.
alert(buf)
asm """throw `e`;"""
-proc reraiseException() {.compilerproc, noStackFrame.} =
+proc reraiseException() {.compilerproc, asmNoStackFrame.} =
if excHandler == nil:
raise newException(ENoExceptionToReraise, "no exception to reraise")
else:
@@ -125,7 +125,7 @@ proc raiseIndexError() {.compilerproc, noreturn.} =
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
raise newException(EInvalidField, f & " is not accessible")
-proc SetConstr() {.varargs, noStackFrame, compilerproc.} =
+proc SetConstr() {.varargs, asmNoStackFrame, compilerproc.} =
asm """
var result = {};
for (var i = 0; i < arguments.length; ++i) {
@@ -141,7 +141,7 @@ proc SetConstr() {.varargs, noStackFrame, compilerproc.} =
return result;
"""
-proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} =
+proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} =
asm """
var result = [];
for (var i = 0; i < `c`.length; ++i) {
@@ -151,7 +151,7 @@ proc cstrToNimstr(c: cstring): string {.noStackFrame, compilerproc.} =
return result;
"""
-proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} =
+proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} =
asm """
var len = `s`.length-1;
var result = new Array(len);
@@ -162,7 +162,7 @@ proc toJSStr(s: string): cstring {.noStackFrame, compilerproc.} =
return result.join("");
"""
-proc mnewString(len: int): string {.noStackFrame, compilerproc.} =
+proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
asm """
var result = new Array(`len`+1);
result[0] = 0;
@@ -170,7 +170,7 @@ proc mnewString(len: int): string {.noStackFrame, compilerproc.} =
return result;
"""
-proc SetCard(a: int): int {.compilerproc, noStackFrame.} =
+proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
# argument type is a fake
asm """
var result = 0;
@@ -178,14 +178,14 @@ proc SetCard(a: int): int {.compilerproc, noStackFrame.} =
return result;
"""
-proc SetEq(a, b: int): bool {.compilerproc, noStackFrame.} =
+proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
asm """
for (var elem in `a`) { if (!`b`[elem]) return false; }
for (var elem in `b`) { if (!`a`[elem]) return false; }
return true;
"""
-proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} =
+proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
asm """
for (var elem in `a`) { if (!`b`[elem]) return false; }
return true;
@@ -194,7 +194,7 @@ proc SetLe(a, b: int): bool {.compilerproc, noStackFrame.} =
proc SetLt(a, b: int): bool {.compilerproc.} =
result = SetLe(a, b) and not SetEq(a, b)
-proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} =
+proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} =
asm """
var result = {};
for (var elem in `a`) {
@@ -203,7 +203,7 @@ proc SetMul(a, b: int): int {.compilerproc, noStackFrame.} =
return result;
"""
-proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} =
+proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
asm """
var result = {};
for (var elem in `a`) { result[elem] = true; }
@@ -211,7 +211,7 @@ proc SetPlus(a, b: int): int {.compilerproc, noStackFrame.} =
return result;
"""
-proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} =
+proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
asm """
var result = {};
for (var elem in `a`) {
@@ -220,7 +220,7 @@ proc SetMinus(a, b: int): int {.compilerproc, noStackFrame.} =
return result;
"""
-proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} =
+proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} =
asm """
if (`a` == `b`) return 0;
if (!`a`) return -1;
@@ -234,7 +234,7 @@ proc cmpStrings(a, b: string): int {.noStackFrame, compilerProc.} =
proc cmp(x, y: string): int = return cmpStrings(x, y)
-proc eqStrings(a, b: string): bool {.noStackFrame, compilerProc.} =
+proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} =
asm """
if (`a` == `b`) return true;
if ((!`a`) || (!`b`)) return false;
@@ -300,7 +300,7 @@ type
setAttributeNode*: proc (attr: ref TNode) {.nimcall.}
when defined(kwin):
- proc rawEcho {.compilerproc, nostackframe.} =
+ proc rawEcho {.compilerproc, asmNoStackFrame.} =
asm """
var buf = "";
for (var i = 0; i < arguments.length; ++i) {
@@ -312,7 +312,7 @@ when defined(kwin):
elif defined(nodejs):
proc ewriteln(x: cstring) = log(x)
- proc rawEcho {.compilerproc, nostackframe.} =
+ proc rawEcho {.compilerproc, asmNoStackFrame.} =
asm """
var buf = "";
for (var i = 0; i < arguments.length; ++i) {
@@ -345,42 +345,42 @@ else:
node.appendChild(document.createElement("br"))
# Arithmetic:
-proc addInt(a, b: int): int {.noStackFrame, compilerproc.} =
+proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
var result = `a` + `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
-proc subInt(a, b: int): int {.noStackFrame, compilerproc.} =
+proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
var result = `a` - `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
-proc mulInt(a, b: int): int {.noStackFrame, compilerproc.} =
+proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
var result = `a` * `b`;
if (result > 2147483647 || result < -2147483648) `raiseOverflow`();
return result;
"""
-proc divInt(a, b: int): int {.noStackFrame, compilerproc.} =
+proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
return Math.floor(`a` / `b`);
"""
-proc modInt(a, b: int): int {.noStackFrame, compilerproc.} =
+proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
return Math.floor(`a` % `b`);
"""
-proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} =
+proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
var result = `a` + `b`;
if (result > 9223372036854775807
@@ -388,7 +388,7 @@ proc addInt64(a, b: int): int {.noStackFrame, compilerproc.} =
return result;
"""
-proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} =
+proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
var result = `a` - `b`;
if (result > 9223372036854775807
@@ -396,7 +396,7 @@ proc subInt64(a, b: int): int {.noStackFrame, compilerproc.} =
return result;
"""
-proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} =
+proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
var result = `a` * `b`;
if (result > 9223372036854775807
@@ -404,90 +404,89 @@ proc mulInt64(a, b: int): int {.noStackFrame, compilerproc.} =
return result;
"""
-proc divInt64(a, b: int): int {.noStackFrame, compilerproc.} =
+proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
return Math.floor(`a` / `b`);
"""
-proc modInt64(a, b: int): int {.noStackFrame, compilerproc.} =
+proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
asm """
if (`b` == 0) `raiseDivByZero`();
if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
return Math.floor(`a` % `b`);
"""
-proc NegInt(a: int): int {.compilerproc.} =
+proc negInt(a: int): int {.compilerproc.} =
result = a*(-1)
-proc NegInt64(a: int64): int64 {.compilerproc.} =
+proc negInt64(a: int64): int64 {.compilerproc.} =
result = a*(-1)
-proc AbsInt(a: int): int {.compilerproc.} =
+proc absInt(a: int): int {.compilerproc.} =
result = if a < 0: a*(-1) else: a
-proc AbsInt64(a: int64): int64 {.compilerproc.} =
+proc absInt64(a: int64): int64 {.compilerproc.} =
result = if a < 0: a*(-1) else: a
-proc LeU(a, b: int): bool {.compilerproc.} =
+proc leU(a, b: int): bool {.compilerproc.} =
result = abs(a) <= abs(b)
-proc LtU(a, b: int): bool {.compilerproc.} =
+proc ltU(a, b: int): bool {.compilerproc.} =
result = abs(a) < abs(b)
-proc LeU64(a, b: int64): bool {.compilerproc.} =
+proc leU64(a, b: int64): bool {.compilerproc.} =
result = abs(a) <= abs(b)
-
-proc LtU64(a, b: int64): bool {.compilerproc.} =
+proc ltU64(a, b: int64): bool {.compilerproc.} =
result = abs(a) < abs(b)
-proc AddU(a, b: int): int {.compilerproc.} =
+proc addU(a, b: int): int {.compilerproc.} =
result = abs(a) + abs(b)
-proc AddU64(a, b: int64): int64 {.compilerproc.} =
+proc addU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) + abs(b)
-proc SubU(a, b: int): int {.compilerproc.} =
+proc subU(a, b: int): int {.compilerproc.} =
result = abs(a) - abs(b)
-proc SubU64(a, b: int64): int64 {.compilerproc.} =
+proc subU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) - abs(b)
-proc MulU(a, b: int): int {.compilerproc.} =
+proc mulU(a, b: int): int {.compilerproc.} =
result = abs(a) * abs(b)
-proc MulU64(a, b: int64): int64 {.compilerproc.} =
+proc mulU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) * abs(b)
-proc DivU(a, b: int): int {.compilerproc.} =
+proc divU(a, b: int): int {.compilerproc.} =
result = abs(a) div abs(b)
-proc DivU64(a, b: int64): int64 {.compilerproc.} =
+proc divU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) div abs(b)
-proc ModU(a, b: int): int {.compilerproc.} =
+proc modU(a, b: int): int {.compilerproc.} =
result = abs(a) mod abs(b)
-proc ModU64(a, b: int64): int64 {.compilerproc.} =
+proc modU64(a, b: int64): int64 {.compilerproc.} =
result = abs(a) mod abs(b)
-proc Ze(a: int): int {.compilerproc.} =
+proc ze*(a: int): int {.compilerproc.} =
result = a
-proc Ze64(a: int64): int64 {.compilerproc.} =
+
+proc ze64*(a: int64): int64 {.compilerproc.} =
result = a
-proc ToU8(a: int): int8 {.noStackFrame, compilerproc.} =
+proc ToU8(a: int): int8 {.asmNoStackFrame, compilerproc.} =
asm """
return `a`;
"""
-proc ToU16(a: int): int16 {.noStackFrame, compilerproc.} =
+proc ToU16(a: int): int16 {.asmNoStackFrame, compilerproc.} =
asm """
return `a`;
"""
-proc ToU32(a: int): int32 {.noStackFrame, compilerproc.} =
+proc ToU32(a: int): int32 {.asmNoStackFrame, compilerproc.} =
asm """
return `a`;
"""
-
proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
@@ -500,9 +499,9 @@ proc isFatPointer(ti: PNimType): bool =
tyArray, tyArrayConstr, tyTuple,
tyOpenArray, tySet, tyVar, tyRef, tyPtr}
-proc NimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
+proc nimCopy(x: pointer, ti: PNimType): pointer {.compilerproc.}
-proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
+proc nimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
case n.kind
of nkNone: sysAssert(false, "NimCopyAux")
of nkSlot:
@@ -518,7 +517,7 @@ proc NimCopyAux(dest, src: Pointer, n: ptr TNimNode) {.compilerproc.} =
}
"""
-proc NimCopy(x: pointer, ti: PNimType): pointer =
+proc nimCopy(x: pointer, ti: PNimType): pointer =
case ti.kind
of tyPtr, tyRef, tyVar, tyNil:
if not isFatPointer(ti):
@@ -586,7 +585,7 @@ proc genericReset(x: Pointer, ti: PNimType): pointer {.compilerproc.} =
result = nil
proc ArrayConstr(len: int, value: pointer, typ: PNimType): pointer {.
- noStackFrame, compilerproc.} =
+ asmNoStackFrame, compilerproc.} =
# types are fake
asm """
var result = new Array(`len`);
@@ -620,7 +619,7 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
x = x.base
return true
-proc addChar(x: string, c: char) {.compilerproc, noStackFrame.} =
+proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
asm """
`x`[`x`.length-1] = `c`; `x`.push(0);
"""
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 6c8fa4882..74ef9c9ec 100644
--- a/lib/windows/winlean.nim
+++ b/lib/windows/winlean.nim
@@ -199,14 +199,14 @@ else:
importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall.}
- proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {.
+ proc createDirectoryA*(pathName: cstring, security: Pointer=nil): int32 {.
importc: "CreateDirectoryA", dynlib: "kernel32", stdcall.}
proc removeDirectoryA*(lpPathName: cstring): int32 {.
importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall.}
proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA".}
- proc getModuleFileNameA*(handle: THandle, buf: cstring, size: int32): int32 {.
+ proc getModuleFileNameA*(handle: THandle, buf: CString, size: int32): int32 {.
importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
when useWinUnicode:
@@ -304,7 +304,7 @@ else:
dwFileAttributes: int32): WINBOOL {.
stdcall, dynlib: "kernel32", importc: "SetFileAttributesA".}
- proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
+ proc copyFileA*(lpExistingFileName, lpNewFileName: CString,
bFailIfExists: cint): cint {.
importc: "CopyFileA", stdcall, dynlib: "kernel32".}
diff --git a/lib/wrappers/zip/zlib.nim b/lib/wrappers/zip/zlib.nim
index c4c6ac071..cd3a765c1 100644
--- a/lib/wrappers/zip/zlib.nim
+++ b/lib/wrappers/zip/zlib.nim
@@ -134,6 +134,7 @@ proc gzerror*(thefile: gzFile, errnum: var int32): pbytef{.cdecl, dynlib: libz,
importc: "gzerror".}
proc adler32*(adler: uLong, buf: pbytef, length: uInt): uLong{.cdecl,
dynlib: libz, importc: "adler32".}
+ ## **Warning**: Adler-32 requires at least a few hundred bytes to get rolling.
proc crc32*(crc: uLong, buf: pbytef, length: uInt): uLong{.cdecl, dynlib: libz,
importc: "crc32".}
proc deflateInitu*(strm: var TZStream, level: int32, version: cstring,
diff --git a/lib/wrappers/zmq.nim b/lib/wrappers/zmq.nim
index 4e658028e..9826ab813 100644
--- a/lib/wrappers/zmq.nim
+++ b/lib/wrappers/zmq.nim
@@ -299,12 +299,12 @@ proc open*(address: string, server: bool, mode: TConnectionMode = conDEALER,
else:
if connect(result.s, address) != 0'i32: zmqError()
-proc close*(c: var TConnection) =
+proc close*(c: TConnection) =
## closes the connection.
if close(c.s) != 0'i32: zmqError()
if term(c.c) != 0'i32: zmqError()
-proc send*(c: var TConnection, msg: string) =
+proc send*(c: TConnection, msg: string) =
## sends a message over the connection.
var m: TMsg
if msg_init(m, msg.len) != 0'i32: zmqError()
@@ -312,7 +312,7 @@ proc send*(c: var TConnection, msg: string) =
if send(c.s, m, 0'i32) != 0'i32: zmqError()
discard msg_close(m)
-proc receive*(c: var TConnection): string =
+proc receive*(c: TConnection): string =
## receives a message from a connection.
var m: TMsg
if msg_init(m) != 0'i32: zmqError()
@@ -320,4 +320,3 @@ proc receive*(c: var TConnection): string =
result = newString(msg_size(m))
copyMem(addr(result[0]), msg_data(m), result.len)
discard msg_close(m)
-
diff --git a/readme.md b/readme.md
index 3eaef0b35..38e04233f 100644
--- a/readme.md
+++ b/readme.md
@@ -33,7 +33,7 @@ If you are on a fairly modern *nix system, the following steps should work:
$ git clone git://github.com/Araq/Nimrod.git
$ cd Nimrod
$ git clone --depth 1 git://github.com/nimrod-code/csources
-$ cd csources && ./build.sh
+$ cd csources && sh build.sh
$ cd ..
$ bin/nimrod c koch
$ ./koch boot -d:release
diff --git a/tests/actiontable/tactiontable2.nim b/tests/actiontable/tactiontable2.nim
index 00b427603..878356321 100644
--- a/tests/actiontable/tactiontable2.nim
+++ b/tests/actiontable/tactiontable2.nim
@@ -1,6 +1,6 @@
discard """
line: 21
- errormsg: "invalid type: 'TTable'"
+ errormsg: "invalid type: 'TTable[string, proc (string)]'"
"""
import tables
diff --git a/tests/ambsym/mambsym1.nim b/tests/ambsym/mambsym1.nim
index cf8ac5242..d9d57b5e5 100644
--- a/tests/ambsym/mambsym1.nim
+++ b/tests/ambsym/mambsym1.nim
@@ -7,4 +7,4 @@ type
proc ha() =
var
x: TExport # no error
- nil
+ discard
diff --git a/tests/ambsym/mambsys1.nim b/tests/ambsym/mambsys1.nim
index 5472b5ae4..04f9561d3 100644
--- a/tests/ambsym/mambsys1.nim
+++ b/tests/ambsym/mambsys1.nim
@@ -4,4 +4,4 @@ type
TExport* = enum x, y, z
proc foo*(x: int) =
- nil
+ discard
diff --git a/tests/ambsym/mambsys2.nim b/tests/ambsym/mambsys2.nim
index 395425b86..d59706865 100644
--- a/tests/ambsym/mambsys2.nim
+++ b/tests/ambsym/mambsys2.nim
@@ -1,4 +1,4 @@
type
TExport* = enum x, y, z # exactly the same type!
-proc foo*(x: int) = nil
+proc foo*(x: int) = discard
diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim
new file mode 100644
index 000000000..bde5bf8c8
--- /dev/null
+++ b/tests/async/tasyncawait.nim
@@ -0,0 +1,64 @@
+discard """
+ file: "tasyncawait.nim"
+ cmd: "nimrod cc --hints:on $# $#"
+ output: "5000"
+"""
+import asyncio2, sockets2, net, strutils
+
+var disp = newDispatcher()
+var msgCount = 0
+
+const
+ swarmSize = 50
+ messagesToSend = 100
+
+var clientCount = 0
+
+proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
+ for i in 0 .. <messagesToSend:
+ discard await disp.send(client, "Message " & $i & "\c\L")
+
+proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
+ for i in 0 .. <swarmSize:
+ var sock = socket()
+ #disp.register(sock)
+ discard await disp.connect(sock, "localhost", port)
+ when true:
+ discard await sendMessages(disp, sock)
+ sock.close()
+ else:
+ # Issue #932: https://github.com/Araq/Nimrod/issues/932
+ var msgFut = sendMessages(disp, sock)
+ msgFut.callback =
+ proc () =
+ sock.close()
+
+proc readMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
+ while true:
+ var line = await disp.recvLine(client)
+ if line == "":
+ client.close()
+ clientCount.inc
+ break
+ else:
+ if line.startswith("Message "):
+ msgCount.inc
+ else:
+ doAssert false
+
+proc createServer(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
+ var server = socket()
+ #disp.register(server)
+ server.bindAddr(port)
+ server.listen()
+ while true:
+ discard readMessages(disp, await disp.accept(server))
+
+discard disp.createServer(TPort(10335))
+discard disp.launchSwarm(TPort(10335))
+while true:
+ disp.poll()
+ if clientCount == swarmSize: break
+
+assert msgCount == swarmSize * messagesToSend
+echo msgCount
diff --git a/tests/casestmt/tcasestm.nim b/tests/casestmt/tcasestm.nim
index 003ec6e50..b033b98ec 100644
--- a/tests/casestmt/tcasestm.nim
+++ b/tests/casestmt/tcasestm.nim
@@ -19,8 +19,8 @@ of eB, eC: write(stdout, "b or c")
case x
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
-of "cc", "hash", "when": nil
-of "will", "it", "finally", "be", "generated": nil
+of "cc", "hash", "when": discard
+of "will", "it", "finally", "be", "generated": discard
var z = case i
of 1..5, 8, 9: "aa"
diff --git a/tests/ccgbugs/tcgbug.nim b/tests/ccgbugs/tcgbug.nim
index 417b909ae..535424a27 100644
--- a/tests/ccgbugs/tcgbug.nim
+++ b/tests/ccgbugs/tcgbug.nim
@@ -19,5 +19,18 @@ var
new(a)
q(a)
+# bug #914
+var x = newWideCString("Hello")
+
echo "success"
+
+# bug #833
+
+type
+ PFuture*[T] = ref object
+ value*: T
+ finished*: bool
+ cb: proc (future: PFuture[T]) {.closure.}
+
+var k = PFuture[void]()
diff --git a/tests/collections/tsets.nim b/tests/collections/tsets.nim
new file mode 100644
index 000000000..656c5b3f2
--- /dev/null
+++ b/tests/collections/tsets.nim
@@ -0,0 +1,17 @@
+discard """
+ output: '''true
+true'''
+"""
+
+import sets
+var
+ a = initSet[int]()
+ b = initSet[int]()
+ c = initSet[string]()
+
+for i in 0..5: a.incl(i)
+for i in 1..6: b.incl(i)
+for i in 0..5: c.incl($i)
+
+echo map(a, proc(x: int): int = x + 1) == b
+echo map(a, proc(x: int): string = $x) == c
diff --git a/tests/compiles/tcompiles.nim b/tests/compiles/tcompiles.nim
index d0fccdaff..b3d9c17ce 100644
--- a/tests/compiles/tcompiles.nim
+++ b/tests/compiles/tcompiles.nim
@@ -24,3 +24,5 @@ ok supports(`+`, 34)
no compiles(4+5.0 * "hallo")
+no compiles(undeclaredIdentifier)
+no compiles(undeclaredIdentifier)
diff --git a/tests/concurrency/tnodeadlocks.nim b/tests/concurrency/tnodeadlocks.nim
index 18fdca3e9..3f27e24f6 100644
--- a/tests/concurrency/tnodeadlocks.nim
+++ b/tests/concurrency/tnodeadlocks.nim
@@ -12,7 +12,7 @@ var
thr: array [0..5, TThread[tuple[a, b: int]]]
L, M, N: TLock
-proc doNothing() = nil
+proc doNothing() = discard
proc threadFunc(interval: tuple[a, b: int]) {.thread.} =
doNothing()
diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim
index bb1410d92..e5236aaab 100644
--- a/tests/destructor/tdestructor.nim
+++ b/tests/destructor/tdestructor.nim
@@ -12,6 +12,13 @@ myobj destroyed
----
mygeneric3 constructed
mygeneric1 destroyed
+----
+mygeneric1 destroyed
+----
+myobj destroyed
+----
+----
+myobj destroyed
'''
"""
@@ -31,6 +38,22 @@ type
x: A
y: B
z: C
+
+ TObjKind = enum A, B, C, D
+
+ TCaseObj = object
+ case kind: TObjKind
+ of A:
+ x: TMyGeneric1[int]
+ of B, C:
+ y: TMyObj
+ else:
+ case innerKind: TObjKind
+ of A, B, C:
+ p: TMyGeneric3[int, float, string]
+ of D:
+ q: TMyGeneric3[TMyObj, int, int]
+ r: string
proc destruct(o: var TMyObj) {.destructor.} =
if o.p != nil: dealloc o.p
@@ -57,13 +80,13 @@ proc mygeneric1() =
echo "mygeneric1 constructed"
proc mygeneric2[T](val: T) =
- var
- a = open()
- b = TMyGeneric2[int, T](x: 10, y: val)
- c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
-
+ var a = open()
+
+ var b = TMyGeneric2[int, T](x: 10, y: val)
echo "mygeneric2 constructed"
+ var c = TMyGeneric3[int, int, string](x: 10, y: 20, z: "test")
+
proc mygeneric3 =
var x = TMyGeneric3[int, string, TMyGeneric1[int]](
x: 10, y: "test", z: TMyGeneric1[int](x: 10))
@@ -82,3 +105,24 @@ mygeneric2[int](10)
echo "----"
mygeneric3()
+proc caseobj =
+ block:
+ echo "----"
+ var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
+
+ block:
+ echo "----"
+ var o2 = TCaseObj(kind: B, y: open())
+
+ block:
+ echo "----"
+ var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
+ p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
+
+ block:
+ echo "----"
+ var o4 = TCaseObj(kind: D, innerKind: D, r: "test",
+ q: TMyGeneric3[TMyObj, int, int](x: open(), y: 1, z: 0))
+
+caseobj()
+
diff --git a/tests/destructor/tdictdestruct.nim b/tests/destructor/tdictdestruct.nim
index ec1084105..b7043f7b7 100644
--- a/tests/destructor/tdictdestruct.nim
+++ b/tests/destructor/tdictdestruct.nim
@@ -6,7 +6,7 @@ type
PDict[TK, TV] = ref TDict[TK, TV]
proc fakeNew[T](x: var ref T, destroy: proc (a: ref T) {.nimcall.}) =
- nil
+ discard
proc destroyDict[TK, TV](a: PDict[TK, TV]) =
return
diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim
index c0551ba2f..a806ccdce 100644
--- a/tests/discard/tdiscardable.nim
+++ b/tests/discard/tdiscardable.nim
@@ -11,3 +11,19 @@ proc q[T](x, y: T): T {.discardable.} =
p(8, 2)
q[float](0.8, 0.2)
+# bug #942
+
+template maybeMod(x: Tinteger, module:Natural):expr =
+ if module > 0: x mod module
+ else: x
+
+proc foo(b: int):int =
+ var x = 1
+ result = x.maybeMod(b) # Works fine
+
+proc bar(b: int):int =
+ result = 1
+ result = result.maybeMod(b) # Error: value returned by statement has to be discarded
+
+echo foo(0)
+echo bar(0)
diff --git a/tests/discard/tneedsdiscard.nim b/tests/discard/tneedsdiscard.nim
index 24f5b2eee..2a7856b4a 100644
--- a/tests/discard/tneedsdiscard.nim
+++ b/tests/discard/tneedsdiscard.nim
@@ -1,6 +1,6 @@
discard """
line: 10
- errormsg: "value returned by statement has to be discarded"
+ errormsg: "value of type 'bool' has to be discarded"
"""
proc p =
diff --git a/tests/effects/teffects6.nim b/tests/effects/teffects6.nim
index 54200f2c3..47c85c160 100644
--- a/tests/effects/teffects6.nim
+++ b/tests/effects/teffects6.nim
@@ -4,7 +4,7 @@ type
PMenuItem = ref object
proc createMenuItem*(menu: PMenu, label: string,
- action: proc (i: PMenuItem, p: pointer) {.cdecl.}) = nil
+ action: proc (i: PMenuItem, p: pointer) {.cdecl.}) = discard
var s: PMenu
createMenuItem(s, "Go to definition...",
diff --git a/tests/exception/texceptionbreak.nim b/tests/exception/texceptionbreak.nim
new file mode 100644
index 000000000..76e986787
--- /dev/null
+++ b/tests/exception/texceptionbreak.nim
@@ -0,0 +1,45 @@
+discard """
+ file: "tnestedbreak.nim"
+ output: "1\n2\n3\n4"
+"""
+
+# First variety
+try:
+ raise newException(EOS, "Problem")
+except EOS:
+ for y in [1, 2, 3]:
+ discard
+ try:
+ discard
+ except EOS:
+ discard
+echo "1"
+
+# Second Variety
+try:
+ raise newException(EOS, "Problem")
+except EOS:
+ for y in [1, 2, 3]:
+ discard
+ for y in [1, 2, 3]:
+ discard
+
+echo "2"
+
+# Third Variety
+try:
+ raise newException(EOS, "Problem")
+except EOS:
+ block:
+ break
+
+echo "3"
+
+# Fourth Variety
+block:
+ try:
+ raise newException(EOS, "Problem")
+ except EOS:
+ break
+
+echo "4" \ No newline at end of file
diff --git a/tests/exception/tfinally4.nim b/tests/exception/tfinally4.nim
new file mode 100644
index 000000000..05c57c4f5
--- /dev/null
+++ b/tests/exception/tfinally4.nim
@@ -0,0 +1,40 @@
+discard """
+ file: "tfinally4.nim"
+ output: "B1\nA1\n1\nB1\nB2\ncatch\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\ncatch\nA1\nA2\n0\nB1\nA1\n1\nB1\nB2\nA1\n1\nB1\nA1\nA2\n2\nB1\nB2\nA1\nA2\n3"
+"""
+
+# More thorough test of return-in-finaly
+
+var raiseEx = true
+var returnA = true
+var returnB = false
+
+proc main: int =
+ try: #A
+ try: #B
+ if raiseEx:
+ raise newException(EOS, "")
+ return 3
+ finally: #B
+ echo "B1"
+ if returnB:
+ return 2
+ echo "B2"
+ except EOS: #A
+ echo "catch"
+ finally: #A
+ echo "A1"
+ if returnA:
+ return 1
+ echo "A2"
+
+for x in [true, false]:
+ for y in [true, false]:
+ for z in [true, false]:
+ # echo "raiseEx: " & $x
+ # echo "returnA: " & $y
+ # echo "returnB: " & $z
+ raiseEx = x
+ returnA = y
+ returnB = z
+ echo main()
diff --git a/tests/exception/tnestedreturn.nim b/tests/exception/tnestedreturn.nim
new file mode 100644
index 000000000..b9f7843f6
--- /dev/null
+++ b/tests/exception/tnestedreturn.nim
@@ -0,0 +1,40 @@
+discard """
+ file: "tnestedreturn.nim"
+ output: "A\nB\nC\n"
+"""
+
+# Various tests of return nested in double try/except statements
+
+proc test1() =
+
+ finally: echo "A"
+
+ try:
+ raise newException(EOS, "Problem")
+ except EOS:
+ return
+
+test1()
+
+
+proc test2() =
+
+ finally: echo "B"
+
+ try:
+ return
+ except EOS:
+ discard
+
+test2()
+
+proc test3() =
+ try:
+ try:
+ raise newException(EOS, "Problem")
+ except EOS:
+ return
+ finally:
+ echo "C"
+
+test3()
diff --git a/tests/exception/tnestedreturn2.nim b/tests/exception/tnestedreturn2.nim
new file mode 100644
index 000000000..14a2dab92
--- /dev/null
+++ b/tests/exception/tnestedreturn2.nim
@@ -0,0 +1,20 @@
+discard """
+ file: "tnestedreturn.nim"
+ outputsub: "Error: unhandled exception: Problem [EOS]"
+ exitcode: "1"
+"""
+
+proc test4() =
+ try:
+ try:
+ raise newException(EOS, "Problem")
+ except EOS:
+ return
+ finally:
+ discard
+
+# Should cause unhandled exception error,
+# but could cause segmentation fault if
+# exceptions are not handled properly.
+test4()
+raise newException(EOS, "Problem")
diff --git a/tests/exprs/texprstmt.nim b/tests/exprs/texprstmt.nim
index b32394d8d..355da2407 100644
--- a/tests/exprs/texprstmt.nim
+++ b/tests/exprs/texprstmt.nim
@@ -1,6 +1,6 @@
discard """
line: 10
- errormsg: "value returned by statement has to be discarded"
+ errormsg: "value of type 'string' has to be discarded"
"""
# bug #578
diff --git a/tests/exprs/tifexpr_typeinference.nim b/tests/exprs/tifexpr_typeinference.nim
new file mode 100644
index 000000000..3ae95c571
--- /dev/null
+++ b/tests/exprs/tifexpr_typeinference.nim
@@ -0,0 +1,20 @@
+#bug #712
+
+import tables
+
+proc test(): TTable[string, string] =
+ discard
+
+proc test2(): TTable[string, string] =
+ discard
+
+var x = 5
+let blah =
+ case x
+ of 5:
+ test2()
+ of 2:
+ test()
+ else: test()
+
+echo blah.len
diff --git a/tests/exprs/tstmtexp.nim b/tests/exprs/tstmtexp.nim
index 7cbf2eb3d..fe60dd3ba 100644
--- a/tests/exprs/tstmtexp.nim
+++ b/tests/exprs/tstmtexp.nim
@@ -1,9 +1,9 @@
discard """
file: "tstmtexp.nim"
line: 8
- errormsg: "value returned by statement has to be discarded"
+ errormsg: "value of type 'int literal(5)' has to be discarded"
"""
# Test 3
-1+4 #ERROR_MSG value returned by statement has to be discarded
+1+4
diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim
index 3c543ecfa..963d0ccfb 100644
--- a/tests/generics/tgeneric3.nim
+++ b/tests/generics/tgeneric3.nim
@@ -32,7 +32,7 @@ const
proc len[T,D] (n:PNode[T,D]): Int {.inline.} =
return n.Count
-proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = nil
+proc clean[T: TOrdinal|TNumber](o: var T) {.inline.} = discard
proc clean[T: string|seq](o: var T) {.inline.} =
o = nil
@@ -98,7 +98,7 @@ proc DeleteItem[T,D] (n: PNode[T,D], x: Int): PNode[T,D] {.inline.} =
of cLen3 : setLen(n.slots, cLen3)
of cLenCenter : setLen(n.slots, cLenCenter)
of cLen4 : setLen(n.slots, cLen4)
- else: nil
+ else: discard
Result = n
else :
@@ -232,7 +232,7 @@ proc InsertItem[T,D](APath: RPath[T,D], ANode:PNode[T,D], AKey: T, AValue: D) =
of cLen3: setLen(APath.Nd.slots, cLenCenter)
of cLenCenter: setLen(APath.Nd.slots, cLen4)
of cLen4: setLen(APath.Nd.slots, cLenMax)
- else: nil
+ else: discard
for i in countdown(APath.Nd.Count.int - 1, x + 1): shallowCopy(APath.Nd.slots[i], APath.Nd.slots[i - 1])
APath.Nd.slots[x] = setItem(AKey, AValue, ANode)
diff --git a/tests/generics/tgenericlambda.nim b/tests/generics/tgenericlambda.nim
new file mode 100644
index 000000000..f7aafe1d9
--- /dev/null
+++ b/tests/generics/tgenericlambda.nim
@@ -0,0 +1,18 @@
+discard """
+ output: "10\n10\n1\n2\n3"
+"""
+
+proc test(x: proc (a, b: int): int) =
+ echo x(5, 5)
+
+test(proc (a, b): auto = a + b)
+
+test do (a, b) -> auto: a + b
+
+proc foreach[T](s: seq[T], body: proc(x: T)) =
+ for e in s:
+ body(e)
+
+foreach(@[1,2,3]) do (x):
+ echo x
+
diff --git a/tests/generics/tinferredgenericprocs.nim b/tests/generics/tinferredgenericprocs.nim
new file mode 100644
index 000000000..ac445fd32
--- /dev/null
+++ b/tests/generics/tinferredgenericprocs.nim
@@ -0,0 +1,20 @@
+discard """
+ output: '''123
+1
+2
+3'''
+"""
+
+# https://github.com/Araq/Nimrod/issues/797
+proc foo[T](s:T):string = $s
+
+type IntStringProc = proc(x: int): string
+
+var f1 = IntStringProc(foo)
+var f2: proc(x: int): string = foo
+var f3: IntStringProc = foo
+
+echo f1(1), f2(2), f3(3)
+
+for x in map([1,2,3], foo): echo x
+
diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim
new file mode 100644
index 000000000..42353006d
--- /dev/null
+++ b/tests/generics/tmetafield.nim
@@ -0,0 +1,18 @@
+discard """
+ cmd: "nimrod check $# $#"
+ msg: "'proc' is not a concrete type"
+ msg: "'Foo' is not a concrete type."
+ msg: "invalid type: 'TBaseMed'"
+"""
+
+type
+ Foo[T] = object
+ x: T
+
+ TBaseMed = object
+ doSmth: proc
+ data: seq[Foo]
+
+var a: TBaseMed
+
+# issue 188
diff --git a/tests/global/globalaux.nim b/tests/global/globalaux.nim
new file mode 100644
index 000000000..5f6f72721
--- /dev/null
+++ b/tests/global/globalaux.nim
@@ -0,0 +1,15 @@
+type
+ TObj*[T] = object
+ val*: T
+
+var
+ totalGlobals* = 0
+
+proc makeObj[T](x: T): TObj[T] =
+ totalGlobals += 1
+ result.val = x
+
+proc globalInstance*[T]: var TObj[T] =
+ var g {.global.} = when T is int: makeObj(10) else: makeObj("hello")
+ result = g
+
diff --git a/tests/global/globalaux2.nim b/tests/global/globalaux2.nim
new file mode 100644
index 000000000..6c77f1f48
--- /dev/null
+++ b/tests/global/globalaux2.nim
@@ -0,0 +1,4 @@
+import globalaux
+
+echo "in globalaux2: ", globalInstance[int]().val
+
diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim
index 578749caf..9f0d0a74b 100644
--- a/tests/iter/tanoniter1.nim
+++ b/tests/iter/tanoniter1.nim
@@ -8,7 +8,7 @@ discard """
"""
proc factory(a, b: int): iterator (): int =
- iterator foo(): int =
+ iterator foo(): int {.closure.} =
var x = a
while x <= b:
yield x
diff --git a/tests/lookups/tredef.nim b/tests/lookups/tredef.nim
index 02d1f7776..a1647000c 100644
--- a/tests/lookups/tredef.nim
+++ b/tests/lookups/tredef.nim
@@ -1,28 +1,28 @@
-template foo(a: int, b: string) = nil
+template foo(a: int, b: string) = discard
foo(1, "test")
-proc bar(a: int, b: string) = nil
+proc bar(a: int, b: string) = discard
bar(1, "test")
template foo(a: int, b: string) = bar(a, b)
foo(1, "test")
block:
- proc bar(a: int, b: string) = nil
- template foo(a: int, b: string) = nil
+ proc bar(a: int, b: string) = discard
+ template foo(a: int, b: string) = discard
foo(1, "test")
bar(1, "test")
proc baz =
- proc foo(a: int, b: string) = nil
+ proc foo(a: int, b: string) = discard
proc foo(b: string) =
- template bar(a: int, b: string) = nil
+ template bar(a: int, b: string) = discard
bar(1, "test")
foo("test")
block:
- proc foo(b: string) = nil
+ proc foo(b: string) = discard
foo("test")
foo(1, "test")
diff --git a/tests/macros/tmacro5.nim b/tests/macros/tmacro5.nim
index 39324e497..9882ad90d 100644
--- a/tests/macros/tmacro5.nim
+++ b/tests/macros/tmacro5.nim
@@ -51,7 +51,7 @@ macro okayy:stmt =
for node in decls: result.add node
for node in impls: result.add node
-importimpl(Item, int):
+importImpl(Item, int):
echo 42
importImpl(Foo, int16):
echo 77
diff --git a/tests/macros/tvarnimnode.nim b/tests/macros/tvarnimnode.nim
new file mode 100644
index 000000000..73fcc16ea
--- /dev/null
+++ b/tests/macros/tvarnimnode.nim
@@ -0,0 +1,19 @@
+discard """
+ output: 10
+"""
+
+#bug #926
+
+import macros
+
+proc test(f: var PNimrodNode) {.compileTime.} =
+ f = newNimNode(nnkStmtList)
+ f.add newCall(newIdentNode("echo"), newLit(10))
+
+macro blah(prc: stmt): stmt =
+ result = prc
+
+ test(result)
+
+proc test() {.blah.} =
+ echo 5
diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim
index 95c71c08c..1edda4aa0 100644
--- a/tests/manyloc/argument_parser/argument_parser.nim
+++ b/tests/manyloc/argument_parser/argument_parser.nim
@@ -178,14 +178,14 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter =
## #parsed_param3 = new_parsed_parameter(PK_INT, "231")
var result {.gensym.}: Tparsed_parameter
result.kind = tkind
- when tkind == PK_EMPTY: nil
+ when tkind == PK_EMPTY: discard
elif tkind == PK_INT: result.int_val = expr
elif tkind == PK_BIGGEST_INT: result.big_int_val = expr
elif tkind == PK_FLOAT: result.float_val = expr
elif tkind == PK_BIGGEST_FLOAT: result.big_float_val = expr
elif tkind == PK_STRING: result.str_val = expr
elif tkind == PK_BOOL: result.bool_val = expr
- elif tkind == PK_HELP: nil
+ elif tkind == PK_HELP: discard
else: {.error: "unknown kind".}
result
diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
index 8226b0b04..d9c933939 100644
--- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
+++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
@@ -18,10 +18,9 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
-when defined(Linux):
- const Lib = "libchipmunk.so.6.1.1"
-else:
- {.error: "Platform unsupported".}
+
+const Lib = "libchipmunk.so.6.1.1"
+
when defined(MoreNimrod):
{.hint: "MoreNimrod defined; some Chipmunk functions replaced in Nimrod".}
{.deadCodeElim: on.}
diff --git a/tests/manyloc/keineschweine/dependencies/enet/enet.nim b/tests/manyloc/keineschweine/dependencies/enet/enet.nim
index ad43c69b7..df1b743ee 100644
--- a/tests/manyloc/keineschweine/dependencies/enet/enet.nim
+++ b/tests/manyloc/keineschweine/dependencies/enet/enet.nim
@@ -17,10 +17,9 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
-when defined(Linux):
- const Lib = "libenet.so.1(|.0.3)"
-else:
- {.error: "Your platform has not been accounted for."}
+
+const Lib = "libenet.so.1(|.0.3)"
+
{.deadCodeElim: ON.}
const
ENET_VERSION_MAJOR* = 1
@@ -267,7 +266,7 @@ const
ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000
ENET_PEER_FREE_RELIABLE_WINDOWS = 8
-when defined(Linux):
+when defined(Linux) or true:
import posix
const
ENET_SOCKET_NULL*: cint = -1
diff --git a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim
index 27163e271..0d09d40e3 100644
--- a/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim
+++ b/tests/manyloc/keineschweine/dependencies/sfml/sfml.nim
@@ -6,7 +6,12 @@ when defined(linux):
LibS = "libcsfml-system.so.2.0"
LibW = "libcsfml-window.so.2.0"
else:
- {.error: "Platform unsupported".}
+ # We only compile for testing here, so it doesn't matter it's not supported
+ const
+ LibG = "libcsfml-graphics.so.2.0"
+ LibS = "libcsfml-system.so.2.0"
+ LibW = "libcsfml-window.so.2.0"
+ #{.error: "Platform unsupported".}
{.deadCodeElim: on.}
{.pragma: pf, pure, final.}
type
@@ -153,8 +158,9 @@ type
KeyF15, #/< The F15 key
KeyPause, #/< The Pause key
KeyCount #/< Keep last -- the total number of keyboard keys
-when defined(linux): #or defined(bsd) ??
- type TWindowHandle* = clong
+
+type TWindowHandle* = clong
+
#elif defined(mac):
# type TWindowHandle* = pointer ##typedef void* sfWindowHandle; <- whatever the hell that is
#elif defined(windows):
diff --git a/tests/metatype/tbindtypedesc.nim b/tests/metatype/tbindtypedesc.nim
index 5ea8cf063..84527362f 100644
--- a/tests/metatype/tbindtypedesc.nim
+++ b/tests/metatype/tbindtypedesc.nim
@@ -1,10 +1,10 @@
discard """
- msg: '''
-int
-float
-TFoo
-TFoo
-'''
+ msg: '''int int
+float float
+int int
+TFoo TFoo
+int float
+TFoo TFoo'''
"""
import typetraits
@@ -24,9 +24,8 @@ template reject(e: expr) =
proc genericParamRepeated[T: typedesc](a: T, b: T) =
static:
- echo a.name
- echo b.name
-
+ echo a.name, " ", b.name
+
accept genericParamRepeated(int, int)
accept genericParamRepeated(float, float)
@@ -35,8 +34,7 @@ reject genericParamRepeated(int, float)
proc genericParamOnce[T: typedesc](a, b: T) =
static:
- echo a.name
- echo b.name
+ echo a.name, " ", b.name
accept genericParamOnce(int, int)
accept genericParamOnce(TFoo, TFoo)
@@ -68,8 +66,7 @@ reject typePairs2(string, int, TBAR, TBAR)
proc dontBind(a: typedesc, b: typedesc) =
static:
- echo a.name
- echo b.name
+ echo a.name, " ", b.name
accept dontBind(int, float)
accept dontBind(TFoo, TFoo)
diff --git a/tests/metatype/tusertypeclasses.nim b/tests/metatype/tusertypeclasses.nim
index 4c2f07b85..5b04c490f 100644
--- a/tests/metatype/tusertypeclasses.nim
+++ b/tests/metatype/tusertypeclasses.nim
@@ -26,3 +26,17 @@ foo 10
foo "test"
foo(@[TObj(x: 10), TObj(x: 20)])
+proc intval(x: int) = discard
+
+# check real and virtual fields
+type
+ TFoo = generic T
+ T.x
+ y(T)
+ intval T.y
+
+proc y(x: TObj): int = 10
+
+proc testFoo(x: TFoo) = discard
+testFoo(TObj(x: 10))
+
diff --git a/tests/metatype/udtcmanual.nim b/tests/metatype/udtcmanual.nim
new file mode 100644
index 000000000..f22bd6ac6
--- /dev/null
+++ b/tests/metatype/udtcmanual.nim
@@ -0,0 +1,43 @@
+discard """
+ output: '''1
+2
+3
+4
+5
+6
+a
+b
+t
+e
+s
+t
+'''
+"""
+
+template accept(e: expr) =
+ static: assert compiles(e)
+
+template reject(e: expr) =
+ static: assert(not compiles(e))
+
+type
+ Container[T] = generic C
+ C.len is Ordinal
+ items(c) is iterator
+ for value in C:
+ value.type is T
+
+proc takesIntContainer(c: Container[int]) =
+ for e in c: echo e
+
+takesIntContainer(@[1, 2, 3])
+reject takesIntContainer(@["x", "y"])
+
+proc takesContainer(c: Container) =
+ for e in c: echo e
+
+takesContainer(@[4, 5, 6])
+takesContainer(@["a", "b"])
+takesContainer "test"
+reject takesContainer(10)
+
diff --git a/tests/method/tmethods1.nim b/tests/method/tmethods1.nim
index f4add6af4..43a260bca 100644
--- a/tests/method/tmethods1.nim
+++ b/tests/method/tmethods1.nim
@@ -14,8 +14,8 @@ type
TSomethingElse = object
PSomethingElse = ref TSomethingElse
-method foo(a: PNode, b: PSomethingElse) = nil
-method foo(a: PNodeFoo, b: PSomethingElse) = nil
+method foo(a: PNode, b: PSomethingElse) = discard
+method foo(a: PNodeFoo, b: PSomethingElse) = discard
var o: TObject
o.somethin()
diff --git a/tests/module/trecinca.nim b/tests/module/trecinca.nim
index 73a0ec937..62d37783c 100644
--- a/tests/module/trecinca.nim
+++ b/tests/module/trecinca.nim
@@ -1,7 +1,7 @@
discard """
file: "tests/reject/trecincb.nim"
line: 9
- errormsg: "recursive dependency: 'tests/reject/trecincb.nim'"
+ errormsg: "recursive dependency: 'tests/module/trecincb.nim'"
"""
# Test recursive includes
diff --git a/tests/module/trecincb.nim b/tests/module/trecincb.nim
index 9dd7d51de..a2934052f 100644
--- a/tests/module/trecincb.nim
+++ b/tests/module/trecincb.nim
@@ -1,7 +1,7 @@
discard """
file: "trecincb.nim"
line: 9
- errormsg: "recursive dependency: 'tests/reject/trecincb.nim'"
+ errormsg: "recursive dependency: 'tests/module/trecincb.nim'"
"""
# Test recursive includes
diff --git a/tests/notnil/tnotnil3.nim b/tests/notnil/tnotnil3.nim
new file mode 100644
index 000000000..b7c7a811d
--- /dev/null
+++ b/tests/notnil/tnotnil3.nim
@@ -0,0 +1,35 @@
+discard """
+ errormsg: "cannot prove 'variable' is not nil"
+ line: 31
+"""
+
+# bug #584
+# Testprogram for 'not nil' check
+
+const testWithResult = true
+
+type
+ A = object
+ B = object
+ C = object
+ a: ref A
+ b: ref B
+
+
+proc testNotNil(c: ref C not nil) =
+ discard
+
+
+when testWithResult:
+ proc testNotNilOnResult(): ref C =
+ new(result)
+ #result.testNotNil() # Here 'not nil' can't be proved
+
+
+var variable: ref C
+new(variable)
+variable.testNotNil() # Here 'not nil' is proved
+
+when testWithResult:
+ discard testNotNilOnResult()
+
diff --git a/tests/overload/toverwr.nim b/tests/overload/toverwr.nim
index ef25e8913..32d50faaa 100644
--- a/tests/overload/toverwr.nim
+++ b/tests/overload/toverwr.nim
@@ -1,13 +1,13 @@
-discard """
- file: "toverwr.nim"
- output: "hello"
-"""
+discard """
+ file: "toverwr.nim"
+ output: "hello"
+"""
# Test the overloading resolution in connection with a qualifier
proc write(t: TFile, s: string) =
- nil # a nop
+ discard # a nop
system.write(stdout, "hello")
#OUT hello
-
-
+
+
diff --git a/tests/patterns/targlist.nim b/tests/patterns/targlist.nim
index a2fa1fa48..e416edf0a 100644
--- a/tests/patterns/targlist.nim
+++ b/tests/patterns/targlist.nim
@@ -2,7 +2,7 @@ discard """
output: "12false3ha"
"""
-proc f(x: varargs[string, `$`]) = nil
+proc f(x: varargs[string, `$`]) = discard
template optF{f(X)}(x: varargs[expr]) =
writeln(stdout, x)
diff --git a/tests/range/tsubrange2.nim b/tests/range/tsubrange2.nim
index 51598713b..d14111bb9 100644
--- a/tests/range/tsubrange2.nim
+++ b/tests/range/tsubrange2.nim
@@ -8,7 +8,7 @@ type
TRange = range[0..40]
proc p(r: TRange) =
- nil
+ discard
var
r: TRange
diff --git a/tests/range/tsubrange3.nim b/tests/range/tsubrange3.nim
index b3e02fd29..9afb5018b 100644
--- a/tests/range/tsubrange3.nim
+++ b/tests/range/tsubrange3.nim
@@ -8,7 +8,7 @@ type
TRange = range[0..40]
proc p(r: TRange) =
- nil
+ discard
var
r: TRange
diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim
index 7b806f15b..e370209ed 100644
--- a/tests/sets/tsets.nim
+++ b/tests/sets/tsets.nim
@@ -1,7 +1,7 @@
-discard """
- file: "tsets.nim"
- output: "Ha ein F ist in s!"
-"""
+discard """
+ file: "tsets.nim"
+ output: "Ha ein F ist in s!"
+"""
# Test the handling of sets
import
@@ -38,7 +38,7 @@ type
TTokTypes* = set[TTokTypeRange]
const
- toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),
+ toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit),
tkStrLit..tkTripleStrLit}
var
@@ -51,14 +51,14 @@ else: write(stdout, "BUG: F ist nicht in s!\n")
a = {} #{'a'..'z'}
for x in low(TAZ) .. high(TAZ):
incl(a, x)
- if x in a: nil
+ if x in a: discard
else: write(stdout, "BUG: something not in a!\n")
for x in low(TTokTypeRange) .. high(TTokTypeRange):
if x in tokTypes:
- nil
+ discard
#writeln(stdout, "the token '$1' is in the set" % repr(x))
#OUT Ha ein F ist in s!
-
-
+
+
diff --git a/tests/sets/tsets_lt.nim b/tests/sets/tsets_lt.nim
new file mode 100644
index 000000000..6d0b3a60c
--- /dev/null
+++ b/tests/sets/tsets_lt.nim
@@ -0,0 +1,12 @@
+discard """
+ output: '''true
+true
+true'''
+"""
+
+var s, s1: set[char]
+s = {'a'..'d'}
+s1 = {'a'..'c'}
+echo s1 < s
+echo s1 * s == {'a'..'c'}
+echo s1 <= s
diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim
new file mode 100644
index 000000000..ce5b3942d
--- /dev/null
+++ b/tests/specialops/tdotops.nim
@@ -0,0 +1,66 @@
+discard """
+ output: '''
+10
+assigning z = 20
+reading field y
+20
+call to y
+dot call
+no params call to a
+100
+no params call to b
+100
+one param call to c with 10
+100'''
+"""
+
+type
+ T1 = object
+ x*: int
+
+ TD = distinct T1
+
+ T2 = object
+ x: int
+
+proc `.`*(v: T1, f: string): int =
+ echo "reading field ", f
+ return v.x
+
+proc `.=`(x: var T1, f: string{lit}, v: int) =
+ echo "assigning ", f, " = ", v
+ x.x = v
+
+template `.()`(x: T1, f: string, args: varargs[expr]): string =
+ echo "call to ", f
+ "dot call"
+
+echo ""
+
+var t = T1(x: 10)
+
+echo t.x
+t.z = 20
+echo t.y
+echo t.y()
+
+var d = TD(t)
+assert(not compiles(d.y))
+
+proc `.`(v: T2, f: string): int =
+ echo "no params call to ", f
+ return v.x
+
+proc `.`*(v: T2, f: string, a: int): int =
+ echo "one param call to ", f, " with ", a
+ return v.x
+
+var tt = T2(x: 100)
+
+echo tt.a
+echo tt.b()
+echo tt.c(10)
+
+assert(not compiles(tt.d("x")))
+assert(not compiles(tt.d(1, 2)))
+
diff --git a/tests/stdlib/tircbot.nim b/tests/stdlib/tircbot.nim
index 6008838ff..f0417c7ac 100644
--- a/tests/stdlib/tircbot.nim
+++ b/tests/stdlib/tircbot.nim
@@ -200,7 +200,7 @@ proc setSeen(d: TDb, s: TSeen) =
var hashToSet = @[("type", $s.kind.int), ("channel", s.channel),
("timestamp", $s.timestamp.int)]
case s.kind
- of PSeenJoin: nil
+ of PSeenJoin: discard
of PSeenPart, PSeenMsg, PSeenQuit:
hashToSet.add(("msg", s.msg))
of PSeenNick:
@@ -338,7 +338,7 @@ proc hubConnect(state: PState) =
proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
case event.typ
- of EvConnected: nil
+ of EvConnected: discard
of EvDisconnected:
while not state.ircClient.isConnected:
try:
@@ -424,7 +424,7 @@ proc handleIrc(irc: PAsyncIRC, event: TIRCEvent, state: PState) =
seenNick.newNick = event.params[0]
state.database.setSeen(seenNick)
else:
- nil # TODO: ?
+ discard # TODO: ?
proc open(port: TPort = TPort(5123)): PState =
var res: PState
diff --git a/tests/stdlib/tmath2.nim b/tests/stdlib/tmath2.nim
index 6a1dae54d..935b08634 100644
--- a/tests/stdlib/tmath2.nim
+++ b/tests/stdlib/tmath2.nim
@@ -1,7 +1,7 @@
# tests for the interpreter
proc loops(a: var int) =
- nil
+ discard
#var
# b: int
#b = glob
diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim
index fa9993cc9..ebe577b00 100644
--- a/tests/stdlib/tos.nim
+++ b/tests/stdlib/tos.nim
@@ -7,6 +7,6 @@ proc walkDirTree(root: string) =
case k
of pcFile, pcLinkToFile: echo(f)
of pcDir: walkDirTree(f)
- of pcLinkToDir: nil
+ of pcLinkToDir: discard
walkDirTree(".")
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
index bdd8db0f8..7775091a1 100644
--- a/tests/stdlib/tpegs.nim
+++ b/tests/stdlib/tpegs.nim
@@ -72,7 +72,7 @@ type
rule: TNode ## the rule that the symbol refers to
TNode {.final, shallow.} = object
case kind: TPegKind
- of pkEmpty..pkWhitespace: nil
+ of pkEmpty..pkWhitespace: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: term: string
of pkChar, pkGreedyRepChar: ch: char
of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char]
@@ -123,7 +123,7 @@ proc add(d: var TPeg, s: TPeg) {.inline.} = add(d.sons, s)
proc copyPeg(a: TPeg): TPeg =
result.kind = a.kind
case a.kind
- of pkEmpty..pkWhitespace: nil
+ of pkEmpty..pkWhitespace: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle:
result.term = a.term
of pkChar, pkGreedyRepChar:
@@ -229,7 +229,7 @@ when false:
case a.kind
of pkEmpty, pkAny, pkAnyRune, pkGreedyAny, pkNewLine, pkTerminal,
pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar,
- pkCharChoice, pkGreedyRepSet: nil
+ pkCharChoice, pkGreedyRepSet: discard
of pkNonTerminal: return true
else:
for i in 0..a.sons.len-1:
@@ -318,7 +318,7 @@ proc backrefIgnoreStyle*(index: range[1..MaxSubPatterns]): TPeg {.
proc spaceCost(n: TPeg): int =
case n.kind
- of pkEmpty: nil
+ of pkEmpty: discard
of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar,
pkGreedyRepChar, pkCharChoice, pkGreedyRepSet,
pkAny..pkWhitespace, pkGreedyAny:
@@ -1111,7 +1111,7 @@ proc handleHexChar(c: var TPegLexer, xi: var int) =
of 'A'..'F':
xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10)
inc(c.bufpos)
- else: nil
+ else: discard
proc getEscapedChar(c: var TPegLexer, tok: var TToken) =
inc(c.bufpos)
@@ -1341,7 +1341,7 @@ proc getTok(c: var TPegLexer, tok: var TToken) =
of "i": tok.modifier = modIgnoreCase
of "y": tok.modifier = modIgnoreStyle
of "v": tok.modifier = modVerbatim
- else: nil
+ else: discard
setLen(tok.literal, 0)
if c.buf[c.bufpos] == '$':
getDollar(c, tok)
@@ -1488,7 +1488,7 @@ proc primary(p: var TPegParser): TPeg =
of tkCurlyAt:
getTok(p)
return !*\primary(p).token(p)
- else: nil
+ else: discard
case p.tok.kind
of tkIdentifier:
if p.identIsVerbatim:
diff --git a/tests/system/alloc.nim b/tests/system/alloc.nim
new file mode 100644
index 000000000..7abefec2a
--- /dev/null
+++ b/tests/system/alloc.nim
@@ -0,0 +1,52 @@
+var x: ptr int
+
+x = cast[ptr int](alloc(7))
+assert x != nil
+x = cast[ptr int](x.realloc(2))
+assert x != nil
+x.dealloc()
+
+x = createU(int, 3)
+assert x != nil
+x.free()
+
+x = create(int, 4)
+assert cast[ptr array[4, int]](x)[0] == 0
+assert cast[ptr array[4, int]](x)[1] == 0
+assert cast[ptr array[4, int]](x)[2] == 0
+assert cast[ptr array[4, int]](x)[3] == 0
+
+x = x.resize(4)
+assert x != nil
+x.free()
+
+x = cast[ptr int](allocShared(100))
+assert x != nil
+deallocShared(x)
+
+x = createSharedU(int, 3)
+assert x != nil
+x.freeShared()
+
+x = createShared(int, 3)
+assert x != nil
+assert cast[ptr array[3, int]](x)[0] == 0
+assert cast[ptr array[3, int]](x)[1] == 0
+assert cast[ptr array[3, int]](x)[2] == 0
+
+assert x != nil
+x = cast[ptr int](x.resizeShared(2))
+assert x != nil
+x.freeShared()
+
+x = create(int, 10)
+assert x != nil
+x = x.resize(12)
+assert x != nil
+x.dealloc()
+
+x = createShared(int, 1)
+assert x != nil
+x = x.resizeShared(1)
+assert x != nil
+x.freeShared()
diff --git a/tests/table/ttableconstr.nim b/tests/table/ttableconstr.nim
index c627e68e8..1a21a18d1 100644
--- a/tests/table/ttableconstr.nim
+++ b/tests/table/ttableconstr.nim
@@ -1,7 +1,7 @@
# Test if the new table constructor syntax works:
template ignoreExpr(e: expr): stmt {.immediate.} =
- nil
+ discard
# test first class '..' syntactical citizen:
ignoreExpr x <> 2..4
diff --git a/tests/sunset.tmpl b/tests/template/sunset.tmpl
index 6475bac4e..6475bac4e 100644
--- a/tests/sunset.tmpl
+++ b/tests/template/sunset.tmpl
diff --git a/tests/template/ttempl5.nim b/tests/template/ttempl5.nim
index 85692e97b..1f2378780 100644
--- a/tests/template/ttempl5.nim
+++ b/tests/template/ttempl5.nim
@@ -3,3 +3,16 @@ import mtempl5
echo templ()
+#bug #892
+
+proc parse_to_close(value: string, index: int, open='(', close=')'): int =
+ discard
+
+# Call parse_to_close
+template get_next_ident: stmt =
+ discard "{something}".parse_to_close(0, open = '{', close = '}')
+
+get_next_ident()
+
+
+#identifier expected, but found '(open|open|open)'
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index 442dd1212..9bb4838e0 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -222,9 +222,88 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) =
else:
testNoSpec r, makeTest(test, options, cat, actionCompile)
+# ----------------------------- babel ----------------------------------------
+type PackageFilter = enum
+ pfCoreOnly
+ pfExtraOnly
+ pfAll
+
+let
+ babelExe = findExe("babel")
+ babelDir = getHomeDir() / ".babel"
+ packageDir = babelDir / "pkgs"
+ packageIndex = babelDir / "packages.json"
+
+proc waitForExitEx(p: PProcess): int =
+ var outp: PStream = outputStream(p)
+ var line = newStringOfCap(120).TaintedString
+ while true:
+ if outp.readLine(line):
+ discard
+ else:
+ result = peekExitCode(p)
+ if result != -1: break
+ close(p)
+
+proc getPackageDir(package: string): string =
+ ## TODO - Replace this with dom's version comparison magic.
+ var commandOutput = execCmdEx("babel path $#" % package)
+ if commandOutput.exitCode != quitSuccess:
+ return ""
+ else:
+ result = commandOutput[0].string
+
+iterator listPackages(filter: PackageFilter): tuple[name, url: string] =
+ let packageList = parseFile(packageIndex)
+
+ for package in packageList.items():
+ let
+ name = package["name"].str
+ url = package["url"].str
+ isCorePackage = "nimrod-code" in normalize(url)
+ case filter:
+ of pfCoreOnly:
+ if isCorePackage:
+ yield (name, url)
+ of pfExtraOnly:
+ if not isCorePackage:
+ yield (name, url)
+ of pfAll:
+ yield (name, url)
+
+proc testBabelPackages(r: var TResults, cat: Category, filter: PackageFilter) =
+ if babelExe == "":
+ echo("[Warning] - Cannot run babel tests: Babel binary not found.")
+ return
+
+ if execCmd("$# update" % babelExe) == quitFailure:
+ echo("[Warning] - Cannot run babel tests: Babel update failed.")
+ return
+
+ for name, url in listPackages(filter):
+ var test = makeTest(name, "", cat)
+ echo(url)
+ let
+ installProcess = startProcess(babelExe, "", ["install", "-y", name])
+ installStatus = waitForExitEx(installProcess)
+ installProcess.close
+ if installStatus != quitSuccess:
+ r.addResult(test, "", "", reInstallFailed)
+ continue
+
+ let
+ buildPath = getPackageDir(name)[0.. -3]
+ let
+ buildProcess = startProcess(babelExe, buildPath, ["build"])
+ buildStatus = waitForExitEx(buildProcess)
+ buildProcess.close
+ if buildStatus != quitSuccess:
+ r.addResult(test, "", "", reBuildFailed)
+ r.addResult(test, "", "", reSuccess)
+
# ----------------------------------------------------------------------------
-const AdditionalCategories = ["debugger", "tools", "examples", "stdlib"]
+const AdditionalCategories = ["debugger", "tools", "examples", "stdlib", "babel-core"]
proc `&.?`(a, b: string): string =
# candidate for the stdlib?
@@ -264,6 +343,12 @@ proc processCategory(r: var TResults, cat: Category, options: string) =
compileExample(r, "examples/*.nim", options, cat)
compileExample(r, "examples/gtk/*.nim", options, cat)
compileExample(r, "examples/talk/*.nim", options, cat)
+ of "babel-core":
+ testBabelPackages(r, cat, pfCoreOnly)
+ of "babel-extra":
+ testBabelPackages(r, cat, pfExtraOnly)
+ of "babel-all":
+ testBabelPackages(r, cat, pfAll)
else:
for name in os.walkFiles("tests" & DirSep &.? cat.string / "t*.nim"):
testSpec r, makeTest(name, options, cat)
diff --git a/tests/testament/htmlgen.nim b/tests/testament/htmlgen.nim
index eb674a171..74d8811b8 100644
--- a/tests/testament/htmlgen.nim
+++ b/tests/testament/htmlgen.nim
@@ -9,7 +9,7 @@
## HTML generator for the tester.
-import db_sqlite, cgi, backend, strutils
+import db_sqlite, cgi, backend, strutils, json
const
TableHeader = """<table border="1">
@@ -114,8 +114,6 @@ proc getCommit(db: TDbConn, c: int): string =
for thisCommit in db.rows(sql"select id from [Commit] order by id desc"):
if commit == 0: result = thisCommit[0]
inc commit
- if result.isNil:
- quit "cannot determine commit " & $c
proc generateHtml*(filename: string, commit: int) =
const selRow = """select name, category, target, action,
@@ -161,20 +159,48 @@ proc generateHtml*(filename: string, commit: int) =
close(outfile)
proc generateJson*(filename: string, commit: int) =
- const selRow = """select count(*),
+ const
+ selRow = """select count(*),
sum(result = 'reSuccess'),
sum(result = 'reIgnored')
- from TestResult
- where [commit] = ? and machine = ?
- order by category"""
+ from TestResult
+ where [commit] = ? and machine = ?
+ order by category"""
+ selDiff = """select A.category || '/' || A.target || '/' || A.name,
+ A.result,
+ B.result
+ from TestResult A
+ inner join TestResult B
+ on A.name = B.name and A.category = B.category
+ where A.[commit] = ? and B.[commit] = ? and A.machine = ?
+ and A.result != B.result"""
var db = open(connection="testament.db", user="testament", password="",
database="testament")
let lastCommit = db.getCommit(commit)
+ if lastCommit.isNil:
+ quit "cannot determine commit " & $commit
+
+ let previousCommit = db.getCommit(commit-1)
var outfile = open(filename, fmWrite)
- let data = db.getRow(sql(selRow), lastCommit, $backend.getMachine(db))
+ let machine = $backend.getMachine(db)
+ let data = db.getRow(sql(selRow), lastCommit, machine)
+
+ outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#""" % data)
+
+ if not previousCommit.isNil:
+ let diff = newJArray()
+
+ for row in db.rows(sql(selDiff), previousCommit, lastCommit, machine):
+ var obj = newJObject()
+ obj["name"] = %row[0]
+ obj["old"] = %row[1]
+ obj["new"] = %row[2]
+ diff.add obj
+ outfile.writeln(""", "diff": """)
+ outfile.writeln(diff.pretty)
- outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#}""" % data)
+ outfile.writeln "}"
close(db)
close(outfile)
diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim
index e97015946..f7982127f 100644
--- a/tests/testament/specs.nim
+++ b/tests/testament/specs.nim
@@ -28,6 +28,8 @@ type
reCodegenFailure,
reCodeNotFound,
reExeNotFound,
+ reInstallFailed # package installation failed
+ reBuildFailed # package building failed
reIgnored, # test is ignored
reSuccess # test was successful
TTarget* = enum
diff --git a/tests/threads/nimrod.cfg b/tests/threads/nimrod.cfg
new file mode 100644
index 000000000..b81c89721
--- /dev/null
+++ b/tests/threads/nimrod.cfg
@@ -0,0 +1 @@
+threads:on
diff --git a/tests/typerel/tvoid.nim b/tests/typerel/tvoid.nim
index bb569e7f8..d31936217 100644
--- a/tests/typerel/tvoid.nim
+++ b/tests/typerel/tvoid.nim
@@ -1,5 +1,9 @@
discard """
- output: "he, no return type;abc a string"
+ output: '''12
+empty
+he, no return type;
+abc a string
+ha'''
"""
proc ReturnT[T](x: T): T =
diff --git a/tests/typerel/typalias.nim b/tests/typerel/typalias.nim
index ba9f38ed9..40ff06765 100644
--- a/tests/typerel/typalias.nim
+++ b/tests/typerel/typalias.nim
@@ -9,7 +9,7 @@ proc init: TYourObj =
result.y = -1
proc f(x: var TYourObj) =
- nil
+ discard
var m: TMyObj = init()
f(m)
diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim
index 6d3c51749..3c2b9ee5e 100644
--- a/tests/types/tisopr.nim
+++ b/tests/types/tisopr.nim
@@ -1,8 +1,8 @@
discard """
- output: "true true false yes"
+ output: '''true true false yes'''
"""
-proc IsVoid[T](): string =
+proc IsVoid[T](): string =
when T is void:
result = "yes"
else:
@@ -11,3 +11,26 @@ proc IsVoid[T](): string =
const x = int is int
echo x, " ", float is float, " ", float is string, " ", IsVoid[void]()
+template yes(e: expr): stmt =
+ static: assert e
+
+template no(e: expr): stmt =
+ static: assert(not e)
+
+var s = @[1, 2, 3]
+
+yes s.items is iterator
+no s.items is proc
+
+yes s.items is iterator: int
+no s.items is iterator: float
+
+yes s.items is iterator: TNumber
+no s.items is iterator: object
+
+type
+ Iter[T] = iterator: T
+
+yes s.items is Iter[TNumber]
+no s.items is Iter[float]
+
diff --git a/tests/vm/tstaticprintseq.nim b/tests/vm/tstaticprintseq.nim
new file mode 100644
index 000000000..99a56d161
--- /dev/null
+++ b/tests/vm/tstaticprintseq.nim
@@ -0,0 +1,21 @@
+discard """
+ msg: '''1
+2
+3
+1
+2
+3'''
+"""
+
+const s = @[1,2,3]
+
+macro foo: stmt =
+ for e in s:
+ echo e
+
+foo()
+
+static:
+ for e in s:
+ echo e
+
diff --git a/tests/vm/twrongwhen.nim b/tests/vm/twrongwhen.nim
new file mode 100644
index 000000000..085bb6fb6
--- /dev/null
+++ b/tests/vm/twrongwhen.nim
@@ -0,0 +1,13 @@
+discard """
+ output: "Error: cannot evaluate at compile time: x"
+ line: 7
+"""
+
+proc bla(x:int) =
+ when x == 0:
+ echo "oops"
+ else:
+ echo "good"
+
+bla(2) # echos "oops"
+
diff --git a/todo.txt b/todo.txt
index 44aa39791..51f883d1d 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,10 +1,13 @@
version 0.9.4
=============
+- make testament produce full JSON information
+- fix gensym capture bug
+- vm
+ - at least try to get the basic type zoo ops right
+ - optimize opcAsgnStr
- fix GC issues
-- fix macros\tstringinterp.nim
- test and fix showoff
-- fix closure iterators
Bugs
@@ -18,20 +21,17 @@ Bugs
- docgen: sometimes effects are listed twice
- 'result' is not properly cleaned for NRVO --> use uninit checking instead
- blocks can "export" an identifier but the CCG generates {} for them ...
-- osproc execProcesses can deadlock if all processes fail (as experienced
- in c++ mode)
version 0.9.x
=============
-- implement 'union' and 'bits' pragmas
-- fix closures
-- test and fix exception handling
+- memory manager: add a measure of fragmentation
+- implement 'bits' pragmas
+- fix closures/lambdalifting
- ensure (ref T)(a, b) works as a type conversion and type constructor
- optimize 'genericReset'; 'newException' leads to code bloat
- stack-less GC
-- implement strongSpaces:on
- make '--implicitStatic:on' the default
- implicit deref for parameter matching
@@ -41,7 +41,7 @@ version 0.9.x
- built-in 'getImpl'
- change comment handling in the AST; that's lots of work as c2nim and pas2nim
- make use of the fast every node can have a comment!
+ make use of the fact every node can have a comment!
version 0.9.X
@@ -143,11 +143,9 @@ Not essential for 1.0.0
semantics instead ...
- implement "closure tuple consists of a single 'ref'" optimization
- optimize method dispatchers
-- ``with proc `+`(x, y: T): T`` for generic code
- new feature: ``distinct T with operations``
- arglist as a type (iterator chaining); variable length type lists for generics
- implement marker procs for message passing
-- activate more thread tests
- implement closures that support nesting of *procs* > 1
- object constructors: static check for fields if discriminator is known at
compile time
diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim
index b20e86a68..28d92402e 100644
--- a/tools/nimgrep.nim
+++ b/tools/nimgrep.nim
@@ -249,7 +249,7 @@ proc walker(dir: string) =
of pcDir:
if optRecursive in options:
walker(path)
- else: nil
+ else: discard
if existsFile(dir): processFile(dir)
proc writeHelp() =
diff --git a/tools/nimweb.nim b/tools/nimweb.nim
index 56d6bcadb..ff343bd2a 100644
--- a/tools/nimweb.nim
+++ b/tools/nimweb.nim
@@ -130,7 +130,7 @@ proc walkDirRecursively(s: var seq[string], root, ext: string) =
if cmpIgnoreCase(ext, splitFile(f).ext) == 0:
add(s, f)
of pcDir: walkDirRecursively(s, f, ext)
- of pcLinkToDir: nil
+ of pcLinkToDir: discard
proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) =
for p in items(patterns):
@@ -153,7 +153,7 @@ proc parseIniFile(c: var TConfigData) =
of cfgSectionStart:
section = normalize(k.section)
case section
- of "project", "links", "tabs", "ticker", "documentation", "var": nil
+ of "project", "links", "tabs", "ticker", "documentation", "var": discard
else: echo("[Warning] Skipping unknown section: " & section)
of cfgKeyValuePair:
@@ -168,7 +168,7 @@ proc parseIniFile(c: var TConfigData) =
of "logo": c.logo = v
of "authors": c.authors = v
else: quit(errorStr(p, "unknown variable: " & k.key))
- of "var": nil
+ of "var": discard
of "links":
let valID = v.split(';')
add(c.links, (k.key.replace('_', ' '), valID[1], valID[0]))
@@ -186,7 +186,7 @@ proc parseIniFile(c: var TConfigData) =
let vSplit = v.split('-')
doAssert vSplit.len == 2
c.quotations[k.key.normalize] = (vSplit[0], vSplit[1])
- else: nil
+ else: discard
of cfgOption: quit(errorStr(p, "syntax error"))
of cfgError: quit(errorStr(p, k.msg))
@@ -211,9 +211,9 @@ proc buildDocSamples(c: var TConfigData, destPath: string) =
## it didn't make much sense to integrate into the existing generic
## documentation builders.
const src = "doc"/"docgen_sample.nim"
- Exec("nimrod doc $# -o:$# $#" %
+ exec("nimrod doc $# -o:$# $#" %
[c.nimrodArgs, destPath / "docgen_sample.html", src])
- Exec("nimrod doc2 $# -o:$# $#" %
+ exec("nimrod doc2 $# -o:$# $#" %
[c.nimrodArgs, destPath / "docgen_sample2.html", src])
proc buildDoc(c: var TConfigData, destPath: string) =
diff --git a/tools/website.tmpl b/tools/website.tmpl
index 091079c1c..08c0b450c 100644
--- a/tools/website.tmpl
+++ b/tools/website.tmpl
@@ -74,5 +74,15 @@
<div id="legal">Copyright &copy; 2013 - Andreas Rumpf &amp; Contributors - All rights reserved - <a href="http://reign-studios.com/philipwitte/">Design by Philip Witte</a></div>
</div>
</div>
+ <script>
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+ ga('create', 'UA-48159761-1', 'nimrod-lang.org');
+ ga('send', 'pageview');
+
+ </script>
</body>
</html>
diff --git a/web/assets/style.css b/web/assets/style.css
index 715214a1a..5cee279fc 100644
--- a/web/assets/style.css
+++ b/web/assets/style.css
@@ -65,7 +65,7 @@ html, body {
#page { position:relative; float:left; padding:20px 30px 50px 50px; width:620px; color:#343739; }
- #page h1 { margin-top:40px; }
+ #page h1 { margin-top:40px; line-height: 28px; }
#page h2 { margin-top:40px; }
#page p { text-align:justify; }
diff --git a/web/community.txt b/web/community.txt
index b9a0a4196..d45e7df61 100644
--- a/web/community.txt
+++ b/web/community.txt
@@ -1,15 +1,55 @@
-Discuss Nimrod in our `forum <http://forum.nimrod-code.org/>`_.
+Forum
+=====
-Visit our project page at GitHub: http://github.com/Araq/Nimrod.
+The `Nimrod forum <http://forum.nimrod-code.org/>`_ is the place where most
+discussions related to the language happen. It not only includes discussions
+relating to the design of Nimrod but also allows for beginners to ask questions
+relating to Nimrod.
-Wiki: http://github.com/Araq/Nimrod/wiki.
+IRC
+====
-Bug reports: http://github.com/Araq/Nimrod/issues.
+Many Nimrod developers are a part of the
+`#nimrod IRC channel <http://webchat.freenode.net/?channels=nimrod>`_ on
+Freenode. That is the place where the rest of the discussion relating to Nimrod
+occurs. Be sure to join us there if you wish to discuss Nimrod in real-time.
+IRC is the perfect place for people just starting to learn Nimrod and we
+welcome any questions that you may have!
-For quickest feedback, join our IRC channel: irc://irc.freenode.net/nimrod
-(logs at `<http://build.nimrod-code.org/irclogs/>`_).
+You may also be interested in reading the
+`IRC logs <http://build.nimrod-code.org/irclogs/>`_ which are an archive of all
+of the previous discussions that took place in the IRC channel.
-Check out our Twitter account for latest news and announcements: `@nimrodlang <http://twitter.com/nimrodlang>`_.
+Github
+======
+
+Nimrod's `source code <http://github.com/Araq/Nimrod>`_ is hosted on Github.
+Together with the `wiki <http://github.com/Araq/Nimrod/wiki>`_ and
+`issue tracker <http://github.com/Araq/Nimrod/issues>`_.
+
+Github also hosts other projects relating to Nimrod. These projects are a part
+of the `nimrod-code organisation <http://github.com/nimrod-code>`_.
+This includes the `Babel package manager <http://github.com/nimrod-code/babel>`_
+and its `package repository <http://github.com/nimrod-code/packages>`_.
+
+Twitter
+=======
+
+Follow us `@nimrodlang <http://twitter.com/nimrodlang>`_ for latest news about
+Nimrod.
+
+Reddit
+======
+
+Subscribe to `/r/nimrod <http://reddit.com/r/nimrod>`_ for latest news about
+Nimrod.
+
+StackOverflow
+=============
+
+When asking a question relating to Nimrod, be sure to use the
+`Nimrod <http://stackoverflow.com/questions/tagged/nimrod>`_ tag in your
+question.
How to help
===========
@@ -26,7 +66,9 @@ can't find anything you fancy doing, you can always ask for inspiration on IRC
Donations
---------
-If you love what we do and are feeling generous then you can always donate:
+If you love what we do and are feeling generous then you can always donate.
+Contributions of any quantity are greatly appreciated and will contribute to
+making Nimrod even better!
Gittip
``````
diff --git a/web/news.txt b/web/news.txt
index a045eb880..83863cdd9 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -3,86 +3,109 @@ News
====
-2014-XX-XX Version 0.9.4 released
-=================================
-
-
-Bugfixes
---------
-
-
-Library Additions
------------------
-
-- Added ``macros.genSym`` builtin for AST generation.
-- Added ``macros.newLit`` procs for easier AST generation.
-
-
-Changes affecting backwards compatibility
------------------------------------------
-
-- The scoping rules for the ``if`` statement changed for better interaction
- with the new syntactic construct ``(;)``.
-- ``OSError`` family of procedures has been deprecated. Procedures with the same
- name but which take different parameters have been introduced. These procs now
- require an error code to be passed to them. This error code can be retrieved
- using the new ``OSLastError`` proc.
-- ``os.parentDir`` now returns "" if there is no parent dir.
-- In CGI scripts stacktraces are shown to the user only
- if ``cgi.setStackTraceStdout`` is used.
-- The symbol binding rules for clean templates changed: ``bind`` for any
- symbol that's not a parameter is now the default. ``mixin`` can be used
- to require instantiation scope for a symbol.
-- ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely
- passed to shell, instead of just adding double quotes.
-- ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``,
- ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated.
-- The ``nil`` statement has been deprecated, use an empty ``discard`` instead.
-- ``sockets.select`` now prunes sockets that are **not** ready from the list
- of sockets given to it.
-
-
-Compiler Additions
-------------------
-
-- The compiler can now warn about "uninitialized" variables. (There are no
- real uninitialized variables in Nimrod as they are initialized to binary
- zero). Activate via ``{.warning[Uninit]:on.}``.
-- The compiler now enforces the ``not nil`` constraint.
-- The compiler now supports a ``codegenDecl`` pragma for even more control
- over the generated code.
-- The compiler now supports a ``computedGoto`` pragma to support very fast
- dispatching for interpreters and the like.
-- The old evaluation engine has been replaced by a proper register based
- virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro
- evaluation.
-- ``--gc:none`` produces warnings when code uses the GC.
-
-
-Language Additions
-------------------
-
-- Arrays can now be declared with a single integer literal ``N`` instead of a
- range; the range is then ``0..N-1``.
-- Added ``requiresInit`` pragma to enforce explicit initialization.
-- Exported templates are allowed to access hidden fields.
-- The ``using statement`` enables you to more easily author domain-specific
- languages and libraries providing OOP-like syntactic sugar.
-- Added a new ``delegator pragma`` for handling calls to missing procs and
- fields at compile-time.
-- The overload resolution now supports ``static[T]`` params that must be
- evaluable at compile-time.
-- Support for user-defined type classes has been added.
-- The *command syntax* is supported in a lot more contexts.
-- Anonymous iterators are now supported and iterators can capture variables
- of an outer proc.
-
-
-Tools improvements
-------------------
-
-- c2nim can deal with a subset of C++. Use the ``--cpp`` command line option
- to activate.
+..
+ 2014-XX-XX Version 0.9.4 released
+ =================================
+
+
+ Bugfixes
+ --------
+
+
+ Library Additions
+ -----------------
+
+ - Added ``macros.genSym`` builtin for AST generation.
+ - Added ``macros.newLit`` procs for easier AST generation.
+
+
+ Changes affecting backwards compatibility
+ -----------------------------------------
+
+ - The scoping rules for the ``if`` statement changed for better interaction
+ with the new syntactic construct ``(;)``.
+ - ``OSError`` family of procedures has been deprecated. Procedures with the same
+ name but which take different parameters have been introduced. These procs now
+ require an error code to be passed to them. This error code can be retrieved
+ using the new ``OSLastError`` proc.
+ - ``os.parentDir`` now returns "" if there is no parent dir.
+ - In CGI scripts stacktraces are shown to the user only
+ if ``cgi.setStackTraceStdout`` is used.
+ - The symbol binding rules for clean templates changed: ``bind`` for any
+ symbol that's not a parameter is now the default. ``mixin`` can be used
+ to require instantiation scope for a symbol.
+ - ``quoteIfContainsWhite`` now escapes argument in such way that it can be safely
+ passed to shell, instead of just adding double quotes.
+ - ``macros.dumpTree`` and ``macros.dumpLisp`` have been made ``immediate``,
+ ``dumpTreeImm`` and ``dumpLispImm`` are now deprecated.
+ - The ``nil`` statement has been deprecated, use an empty ``discard`` instead.
+ - ``sockets.select`` now prunes sockets that are **not** ready from the list
+ of sockets given to it.
+ - The ``noStackFrame`` pragma has been renamed to ``asmNoStackFrame`` to
+ ensure you only use it when you know what you're doing.
+
+
+ Compiler Additions
+ ------------------
+
+ - The compiler can now warn about "uninitialized" variables. (There are no
+ real uninitialized variables in Nimrod as they are initialized to binary
+ zero). Activate via ``{.warning[Uninit]:on.}``.
+ - The compiler now enforces the ``not nil`` constraint.
+ - The compiler now supports a ``codegenDecl`` pragma for even more control
+ over the generated code.
+ - The compiler now supports a ``computedGoto`` pragma to support very fast
+ dispatching for interpreters and the like.
+ - The old evaluation engine has been replaced by a proper register based
+ virtual machine. This fixes numerous bugs for ``nimrod i`` and for macro
+ evaluation.
+ - ``--gc:none`` produces warnings when code uses the GC.
+ - A ``union`` pragma for better C interoperability is now supported.
+ - Arrays can be annotated to be ``unchecked`` for easier low level
+ manipulations of memory.
+
+
+ Language Additions
+ ------------------
+
+ - Arrays can now be declared with a single integer literal ``N`` instead of a
+ range; the range is then ``0..N-1``.
+ - Added ``requiresInit`` pragma to enforce explicit initialization.
+ - Exported templates are allowed to access hidden fields.
+ - The ``using statement`` enables you to more easily author domain-specific
+ languages and libraries providing OOP-like syntactic sugar.
+ - Added the possibility to override various dot operators in order to handle
+ calls to missing procs and reads from undeclared fields at compile-time.
+ - The overload resolution now supports ``static[T]`` params that must be
+ evaluable at compile-time.
+ - Support for user-defined type classes has been added.
+ - The *command syntax* is supported in a lot more contexts.
+ - Anonymous iterators are now supported and iterators can capture variables
+ of an outer proc.
+ - The experimental ``strongSpaces`` parsing mode has been implemented.
+
+
+ Tools improvements
+ ------------------
+
+ - c2nim can deal with a subset of C++. Use the ``--cpp`` command line option
+ to activate.
+
+
+2014-02-11 Nimrod Featured in Dr. Dobb's Journal
+================================================
+
+Nimrod has been `featured<http://www.drdobbs.com/open-source/nimrod-a-new-systems-programming-languag/240165321>`_
+as the cover story in the February 2014 issue of Dr. Dobb's Journal.
+
+
+2014-01-15 Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online
+============================================================================
+
+Andreas Rumpf presented *Nimrod: A New Approach to Metaprogramming* at
+`Strange Loop 2013<https://thestrangeloop.com/sessions/nimrod-a-new-approach-to-meta-programming>`_.
+The `video and slides<http://www.infoq.com/presentations/nimrod>`_
+of the talk are now available.
2013-05-20 New website design!
diff --git a/web/ticker.txt b/web/ticker.txt
index 00bc6a125..844ed3033 100644
--- a/web/ticker.txt
+++ b/web/ticker.txt
@@ -1,4 +1,14 @@
-<a class="news" href="news.html#new-website-design">
+<a class="news" href="news.html#Z2014-02-11-nimrod-featured-in-dr-dobb-s-journal">
+ <h3>Feb 11, 2014</h3>
+ <p>Nimrod featured in Dr. Dobb's Journal</p>
+</a>
+
+<a class="news" href="news.html#Z2014-01-15-andreas-rumpf-s-talk-on-nimrod-at-strange-loop-2013-is-now-online">
+ <h3>Jan 15, 2014</h3>
+ <p>Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online.</p>
+</a>
+
+<a class="news" href="news.html#Z2013-05-20-new-website-design">
<h3>May 20, 2013</h3>
<p>New website design!</p>
</a>