diff options
author | EXetoC <exetoc@gmail.com> | 2014-03-06 02:46:31 +0100 |
---|---|---|
committer | EXetoC <exetoc@gmail.com> | 2014-03-06 02:46:31 +0100 |
commit | d1bc6cf0981eddf283515c2d77bccca76abff99f (patch) | |
tree | ac28e32bd6143c7a06c44dbea632f28f65ea3d20 /compiler | |
parent | 853bdbf4948db7774bc3caf74f67ba1228f65016 (diff) | |
parent | 7904446c47f952a026d408f04431dbaf6d887b37 (diff) | |
download | Nim-d1bc6cf0981eddf283515c2d77bccca76abff99f.tar.gz |
Merge branch 'devel' into alloc-overloads
Diffstat (limited to 'compiler')
48 files changed, 1882 insertions, 1009 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index cd002eef1..93630979b 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 @@ -479,12 +481,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} tfIncompleteStruct* = tfVarargs + tfUncheckedArray* = tfVarargs + tfUnion* = tfNoSideEffect skError* = skUnknown # type flags that are essential for type equality: @@ -843,7 +848,8 @@ const ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator, 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 @@ -1044,6 +1050,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 +1115,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) 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..07e932b28 --- /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 &= "noTreeKind" + 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 &= "noTypeKind" + 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 be47ac0c4..40ebcbfa8 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: @@ -1049,7 +1049,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = app(tmp2.r, field.loc.r) tmp2.k = locTemp tmp2.t = field.loc.t - tmp2.s = OnHeap + tmp2.s = if isRef: OnHeap else: OnStack tmp2.heapRoot = tmp.r expr(p, it.sons[1], tmp2) if d.k == locNone: @@ -1376,7 +1376,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = getTemp(p, getSysType(tyInt), i) # our counter initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - if d.k == locNone: getTemp(p, a.t, d) + if d.k == locNone: getTemp(p, getSysType(tyBool), d) lineF(p, cpsStmts, lookupOpr[op], [rdLoc(i), toRope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: @@ -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..4576a54b5 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 = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c92c15fa9..156ebee5e 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -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..c3a28527e 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -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 diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 9cd2c0d87..0df7bb6dc 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,6 +144,7 @@ proc newProc*(prc: PSym, module: BModule): BProc = else: result.options = gOptions newSeq(result.blocks, 1) result.nestedTryStmts = @[] + result.finallySafePoints = @[] iterator cgenModules*: var BModule = for i in 0..high(gModules): 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..c05b55184 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]) = 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/jsgen.nim b/compiler/jsgen.nim index c0fc4131a..6e0bd5e44 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("[") @@ -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 00fa04556..3738f89b2 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -116,7 +116,8 @@ type TDep = tuple[e: PEnv, field: PSym] TEnv {.final.} = object of TObject attachedNode: PNode - createdVar: PSym # if != nil it is a used environment + createdVar: PSym # if != nil it is a used environment + createdVarComesFromIter: bool capturedVars: seq[PSym] # captured variables in this environment deps: seq[TDep] # dependencies up: PEnv @@ -571,7 +572,14 @@ proc rawClosureCreation(o: POuterContext, scope: PEnv; env: PSym): PNode = # maybe later: (sfByCopy in local.flags) # add ``env.param = param`` result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) - idNodeTablePut(o.localsToAccess, local, fieldAccess) + # it can happen that we already captured 'local' in some other environment + # then we capture by copy for now. This is not entirely correct but better + # than nothing: + let existing = idNodeTableGet(o.localsToAccess, local) + if existing.isNil: + idNodeTablePut(o.localsToAccess, local, fieldAccess) + else: + result.add(newAsgnStmt(fieldAccess, existing, env.info)) # add support for 'up' references: for e, field in items(scope.deps): # add ``env.up = env2`` @@ -584,14 +592,19 @@ proc generateClosureCreation(o: POuterContext, scope: PEnv): PNode = proc generateIterClosureCreation(o: POuterContext; env: PEnv; scope: PNode): PSym = - result = newClosureCreationVar(o, env) - let cc = rawClosureCreation(o, env, result) - var insertPoint = scope.sons[0] - if insertPoint.kind == nkEmpty: scope.sons[0] = cc + if env.createdVarComesFromIter or env.createdVar.isNil: + # we have to create a new closure: + result = newClosureCreationVar(o, env) + let cc = rawClosureCreation(o, env, result) + var insertPoint = scope.sons[0] + if insertPoint.kind == nkEmpty: scope.sons[0] = cc + else: + assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList + for x in cc: insertPoint.add(x) + if env.createdVar == nil: env.createdVar = result else: - assert cc.kind == nkStmtList and insertPoint.kind == nkStmtList - for x in cc: insertPoint.add(x) - if env.createdVar == nil: env.createdVar = result + result = env.createdVar + env.createdVarComesFromIter = true proc interestingIterVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags @@ -637,6 +650,22 @@ proc outerProcSons(o: POuterContext, n: PNode) = let x = transformOuterProc(o, n.sons[i]) if x != nil: n.sons[i] = x +proc liftIterSym*(n: PNode): PNode = + # transforms (iter) to (let env = newClosure[iter](); (iter, env)) + let iter = n.sym + assert iter.kind == skIterator + + result = newNodeIT(nkStmtListExpr, n.info, n.typ) + + var env = copySym(getHiddenParam(iter)) + env.kind = skLet + var v = newNodeI(nkVarSection, n.info) + addVar(v, newSymNode(env)) + result.add(v) + # add 'new' statement: + result.add(newCall(getSysSym"internalNew", env)) + result.add makeClosure(iter, env, n.info) + proc transformOuterProc(o: POuterContext, n: PNode): PNode = if n == nil: return nil case n.kind @@ -649,17 +678,22 @@ proc transformOuterProc(o: POuterContext, n: PNode): PNode = return indirectAccess(newSymNode(o.closureParam), local, n.info) var closure = PEnv(idTableGet(o.lambdasToEnv, local)) - if closure != nil: - # we need to replace the lambda with '(lambda, env)': - if local.kind == skIterator and local.typ.callConv == ccClosure: - # 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: - #if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s) - # XXX why doesn't this work? + + if local.kind == skIterator and local.typ.callConv == ccClosure: + # 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: + if local == o.fn: message(n.info, errRecursiveDependencyX, local.name.s) + # XXX why doesn't this work? + if closure.isNil: + return liftIterSym(n) + else: let createdVar = generateIterClosureCreation(o, closure, closure.attachedNode) return makeClosure(local, createdVar, n.info) + + if closure != nil: + # we need to replace the lambda with '(lambda, env)': let a = closure.createdVar if a != nil: @@ -773,22 +807,6 @@ proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode = # ------------------- iterator transformation -------------------------------- -proc liftIterSym*(n: PNode): PNode = - # transforms (iter) to (let env = newClosure[iter](); (iter, env)) - let iter = n.sym - assert iter.kind == skIterator - - result = newNodeIT(nkStmtListExpr, n.info, n.typ) - - var env = copySym(getHiddenParam(iter)) - env.kind = skLet - var v = newNodeI(nkVarSection, n.info) - addVar(v, newSymNode(env)) - result.add(v) - # add 'new' statement: - result.add(newCall(getSysSym"internalNew", env)) - result.add makeClosure(iter, env, n.info) - proc liftForLoop*(body: PNode): PNode = # problem ahead: the iterator could be invoked indirectly, but then # we don't know what environment to create here: diff --git a/compiler/lookups.nim b/compiler/lookups.nim index c31eb3121..fcb5b6731 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 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..5a5bfb574 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -281,7 +281,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: 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..bf3564016 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,25 @@ proc makeExternImport(s: PSym, extname: string) = incl(s.flags, sfImportc) excl(s.flags, sfForward) -proc makeExternExport(s: PSym, extname: string) = +const invalidIdentChars = AllChars - IdentChars + +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 (not (target[0] in IdentStartChars)) or + (not target.allCharsInSet(IdentChars)): + localError(info, errGenerated, "invalid exported symbol") + +proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = setExternName(s, extname) + case gCmd + of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC: + validateExternCName(s, info) + else: discard incl(s.flags, sfExportc) proc processImportCompilerProc(s: PSym, extname: string) = @@ -515,7 +532,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 +565,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 +620,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 +718,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) 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..0cd27a443 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -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 diff --git a/compiler/semdata.nim b/compiler/semdata.nim index c9d95e1bf..df58c896f 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) 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 6c7679578..538489490 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: @@ -339,22 +346,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 +641,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,6 +655,8 @@ 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 = @@ -676,20 +686,21 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, 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 +921,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 +1003,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 +1086,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, {}) @@ -1770,22 +1782,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 @@ -1819,6 +1815,10 @@ proc semExport(c: PContext, n: PNode): PNode = c.module.ast.add x result = n +proc setGenericParams(c: PContext, n: PNode) = + for i in 1 .. <n.len: + n[i].typ = semTypeNode(c, n[i], nil) + proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n if gCmd == cmdIdeTools: suggestExpr(c, n) @@ -1833,8 +1833,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = 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! @@ -1890,7 +1890,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = 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: @@ -1924,10 +1924,12 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = else: #liMessage(n.info, warnUser, renderTree(n)); result = semIndirectOp(c, n, flags) - elif isSymChoice(n.sons[0]) or n[0].kind == nkBracketExpr and - isSymChoice(n[0][0]): + elif n[0].kind == nkBracketExpr and isSymChoice(n[0][0]): + # indirectOp can deal with explicit instantiations; the fixes + # the 'newSeq[T](x)' bug + setGenericParams(c, n.sons[0]) result = semDirectOp(c, n, flags) - elif 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) 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 89a167b96..b21d851c9 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -215,8 +215,7 @@ proc semGenericStmt(c: PContext, n: PNode, if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) - a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, - ctx) + a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) @@ -226,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode, if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) - a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, - ctx) + a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) # do not perform symbol lookup for default expressions for j in countup(0, L-3): addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) @@ -281,8 +279,7 @@ proc semGenericStmt(c: PContext, n: PNode, if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) - a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, - ctx) + a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): addPrelimDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c)) 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..b2b3ceb6d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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): @@ -764,6 +773,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 +812,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 +917,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 +941,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 +955,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. @@ -1250,7 +1320,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 +1333,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/semthreads.nim b/compiler/semthreads.nim index f7322db80..d3426ca3e 100644 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -358,7 +358,7 @@ proc analyse(c: PProcCtx, n: PNode): TThreadOwner = of nkConstSection: result = analyseConstSection(c, n) of nkTypeSection, nkCommentStmt: result = toVoid of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, - nkElifBranch, nkElse, nkExceptBranch, nkOfBranch: + nkElifBranch, nkElse, nkExceptBranch, nkOfBranch, nkFinally: for i in 0 .. <n.len: discard analyse(c, n[i]) result = toVoid of nkBreakStmt, nkContinueStmt: result = toVoid diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 44e414e9c..809b80428 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: @@ -705,7 +713,6 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true)) result = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) - result.lastSon.shouldHaveMeta result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = addImplicitGeneric(result) @@ -1011,7 +1018,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 +1038,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: 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 f9200ea0c..f8e3459df 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 @@ -105,6 +107,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, var bound = binding[i].typ if bound != nil and formalTypeParam.kind != tyTypeDesc: bound = bound.skipTypes({tyTypeDesc}) + assert bound != nil put(c.bindings, formalTypeParam, bound) proc newCandidate*(ctx: PContext, callee: PSym, @@ -337,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} == {}: @@ -375,7 +404,8 @@ 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: @@ -401,18 +431,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 @@ -461,7 +481,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: @@ -851,7 +870,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: @@ -883,20 +902,25 @@ 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 = isNone + result = typeRel(c, f.base, a.base) + + if result != isNone: + put(c.bindings, f, a) else: - internalAssert prev.sonsLen == 1 - let toMatch = if tfUnresolved in f.flags: a - else: a.sons[0] - result = typeRel(c, prev.sons[0], toMatch) + 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 tyStmt: result = isGeneric @@ -987,7 +1011,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 @@ -1000,11 +1024,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) @@ -1021,24 +1067,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: @@ -1081,6 +1127,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) @@ -1102,10 +1149,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/transf.nim b/compiler/transf.nim index deb821eff..0911950d0 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -754,6 +754,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..edf5ab47b 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()") @@ -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 d0e8dacf3..123394436 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 @@ -216,10 +203,12 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = internalAssert tmp >= 0 result = TRegister(tmp) -proc clearDest(n: PNode; dest: var TDest) {.inline.} = +proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. # So we only set dest to -1 if 'void': - if n.typ.isNil or n.typ.kind == tyEmpty: dest = -1 + if dest >= 0 and (n.typ.isNil or n.typ.kind == tyEmpty): + c.freeTemp(dest) + dest = -1 proc isNotOpr(n: PNode): bool = n.kind in nkCallKinds and n.sons[0].kind == nkSym and @@ -259,7 +248,7 @@ proc genWhile(c: PCtx; n: PNode) = proc genBlock(c: PCtx; n: PNode; dest: var TDest) = withBlock(n.sons[0].sym): c.gen(n.sons[1], dest) - clearDest(n, dest) + c.clearDest(n, dest) proc genBreak(c: PCtx; n: PNode) = let L1 = c.xjmp(n, opcJmp) @@ -297,14 +286,16 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) = else: c.gen(it.sons[0], tmp) elsePos = c.xjmp(it.sons[0], opcFJmp, tmp) # if false + c.clearDest(n, dest) c.gen(it.sons[1], dest) # then part if i < sonsLen(n)-1: endings.add(c.xjmp(it.sons[1], opcJmp, 0)) c.patch(elsePos) else: + c.clearDest(n, dest) c.gen(it.sons[0], dest) for endPos in endings: c.patch(endPos) - clearDest(n, dest) + c.clearDest(n, dest) proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = # asgn dest, a @@ -317,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 = @@ -385,8 +379,8 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = if i < sonsLen(n)-1: endings.add(c.xjmp(it.lastSon, opcJmp, 0)) c.patch(elsePos) + c.clearDest(n, dest) for endPos in endings: c.patch(endPos) - clearDest(n, dest) proc genType(c: PCtx; typ: PType): int = for i, t in c.types: @@ -400,6 +394,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = var endings: seq[TPosition] = @[] let elsePos = c.xjmp(n, opcTry, 0) c.gen(n.sons[0], dest) + c.clearDest(n, dest) c.patch(elsePos) for i in 1 .. <n.len: let it = n.sons[i] @@ -415,6 +410,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = # general except section: c.gABx(it, opcExcept, 0, 0) c.gen(it.lastSon, dest) + c.clearDest(n, dest) if i < sonsLen(n)-1: endings.add(c.xjmp(it, opcJmp, 0)) c.patch(endExcept) @@ -425,8 +421,8 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = c.gABx(fin, opcFinally, 0, 0) if fin.kind == nkFinally: c.gen(fin.sons[0], dest) + c.clearDest(n, dest) c.gABx(fin, opcFinallyEnd, 0, 0) - clearDest(n, dest) proc genRaise(c: PCtx; n: PNode) = let dest = genx(c, n.sons[0]) @@ -455,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 @@ -579,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) = @@ -608,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) @@ -621,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: @@ -629,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: @@ -860,7 +875,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest) = of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mMinI, mMaxI, mMinI64, mMaxI64, mAbsF64, mMinF64, mMaxF64, mAbsI, mAbsI64: c.genCall(n, dest) - clearDest(n, dest) of mExpandToAst: if n.len != 2: globalError(n.info, errGenerated, "expandToAst requires 1 argument") @@ -891,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 @@ -902,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 = @@ -935,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) @@ -944,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: @@ -954,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) = @@ -1013,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: @@ -1038,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: @@ -1061,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) @@ -1079,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: @@ -1132,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 @@ -1142,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) @@ -1155,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) @@ -1183,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) @@ -1250,9 +1326,10 @@ 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) + genRdVar(c, n, dest, flags) of skProc, skConverter, skMacro, skTemplate, skMethod, skIterator: # 'skTemplate' is only allowed for 'getAst' support: if sfImportc in s.flags: c.importcSym(n.info, s) @@ -1281,7 +1358,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genMagic(c, n, dest) else: genCall(c, n, dest) - clearDest(n, dest) + clearDest(c, n, dest) of nkCharLit..nkInt64Lit: if isInt16Lit(n): if dest < 0: dest = c.getTemp(n.typ) @@ -1298,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: @@ -1467,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: @@ -1484,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", |