diff options
author | Zahary Karadjov <zahary@gmail.com> | 2013-01-27 23:41:45 +0200 |
---|---|---|
committer | Zahary Karadjov <zahary@gmail.com> | 2013-01-27 23:41:45 +0200 |
commit | 81a3585872b1a327b62ba528addbee913d6bbe5a (patch) | |
tree | aff8358bc86704edbd89fd56ec4f7b0cd3583bca /compiler | |
parent | 67f37264b3f461fe46f5cfea7c35c0a4f709dcb0 (diff) | |
parent | 07585088955c1fe8fb815c40409ed9f5d66fd446 (diff) | |
download | Nim-81a3585872b1a327b62ba528addbee913d6bbe5a.tar.gz |
merged upstream master
Diffstat (limited to 'compiler')
45 files changed, 1322 insertions, 412 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 8763e750e..0e4700065 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -172,6 +172,9 @@ type nkDiscardStmt, # a discard statement nkStmtList, # a list of statements nkImportStmt, # an import statement + nkImportExceptStmt, # an import x except a statement + nkExportStmt, # an export statement + nkExportExceptStmt, # an 'export except' statement nkFromStmt, # a from * import statement nkIncludeStmt, # an include statement nkBindStmt, # a bind statement @@ -647,7 +650,8 @@ type loc*: TLoc annex*: PLib # additional fields (seldom used, so we use a # reference to another object to safe space) - + constraint*: PNode # additional constraints like 'lit|result' + TTypeSeq* = seq[PType] TType* = object of TIdObj # types are identical iff they have the # same id; there may be multiple copies of a type @@ -673,7 +677,6 @@ type align*: int # the type's alignment requirements containerID*: int # used for type checking of generics loc*: TLoc - constraint*: PNode # additional constraints like 'lit|result' TPair*{.final.} = object key*, val*: PObject diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index da0de3e94..564f262d7 100755 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -429,7 +429,7 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int): PRope = [istr, makeYamlString($n.kind)]) if maxRecDepth != 0: case n.kind - of nkCharLit..nkInt64Lit: + of nkCharLit..nkUInt64Lit: appf(result, ",$N$1\"intVal\": $2", [istr, toRope(n.intVal)]) of nkFloatLit, nkFloat32Lit, nkFloat64Lit: appf(result, ",$N$1\"floatVal\": $2", @@ -585,8 +585,9 @@ proc StrTableContains(t: TStrTable, n: PSym): bool = proc StrTableRawInsert(data: var TSymSeq, n: PSym) = var h: THash = n.name.h and high(data) while data[h] != nil: - if data[h] == n: - InternalError(n.info, "StrTableRawInsert: " & n.name.s) + if data[h] == n: + # allowed for 'export' feature: + #InternalError(n.info, "StrTableRawInsert: " & n.name.s) return h = nextTry(h, high(data)) assert(data[h] == nil) @@ -617,23 +618,23 @@ proc StrTableAdd(t: var TStrTable, n: PSym) = StrTableRawInsert(t.data, n) inc(t.counter) -proc StrTableIncl*(t: var TStrTable, n: PSym): bool = +proc StrTableIncl*(t: var TStrTable, n: PSym): bool {.discardable.} = # returns true if n is already in the string table: # It is essential that `n` is written nevertheless! # This way the newest redefinition is picked by the semantic analyses! assert n.name != nil var h: THash = n.name.h and high(t.data) - while true: + while true: var it = t.data[h] - if it == nil: break - if it.name.id == n.name.id: + if it == nil: break + if it.name.id == n.name.id: t.data[h] = n # overwrite it with newer definition! return true # found it h = nextTry(h, high(t.data)) - if mustRehash(len(t.data), t.counter): + if mustRehash(len(t.data), t.counter): StrTableEnlarge(t) StrTableRawInsert(t.data, n) - else: + else: assert(t.data[h] == nil) t.data[h] = n inc(t.counter) diff --git a/compiler/babelcmd.nim b/compiler/babelcmd.nim new file mode 100644 index 000000000..956c6a6ae --- /dev/null +++ b/compiler/babelcmd.nim @@ -0,0 +1,90 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Implements some helper procs for Babel (Nimrod's package manager) support. + +import parseutils, strutils, strtabs, os, options, msgs, lists + +proc addPath*(path: string, info: TLineInfo) = + if not contains(options.searchPaths, path): + lists.PrependStr(options.searchPaths, path) + +proc versionSplitPos(s: string): int = + result = s.len-2 + while result > 1 and s[result] in {'0'..'9', '.'}: dec result + if s[result] != '-': result = s.len + +const + latest = "head" + +proc `<.`(a, b: string): bool = + # wether a has a smaller version than b: + if a == latest: return false + var i = 0 + var j = 0 + var verA = 0 + var verB = 0 + while true: + let ii = parseInt(a, verA, i) + let jj = parseInt(b, verB, j) + # if A has no number left, but B has, B is prefered: 0.8 vs 0.8.3 + if ii <= 0 or jj <= 0: return jj > 0 + if verA < verB: return true + elif verA > verB: return false + # else: same version number; continue: + inc i, ii + inc j, jj + if a[i] == '.': inc i + if b[j] == '.': inc j + +proc addPackage(packages: PStringTable, p: string) = + let x = versionSplitPos(p) + let name = p.subStr(0, x-1) + if x < p.len: + let version = p.subStr(x+1) + if packages[name] <. version: + packages[name] = version + else: + packages[name] = latest + +iterator chosen(packages: PStringTable): string = + for key, val in pairs(packages): + let res = if val == latest: key else: key & '-' & val + yield res + +proc addBabelPath(p: string, info: TLineInfo) = + if not contains(options.searchPaths, p): + Message(info, hintPath, p) + lists.PrependStr(options.lazyPaths, p) + +proc addPathWithNimFiles(p: string, info: TLineInfo) = + proc hasNimFile(dir: string): bool = + for kind, path in walkDir(dir): + if kind == pcFile and path.endsWith(".nim"): + result = true + break + if hasNimFile(p): + addBabelPath(p, info) + else: + for kind, p2 in walkDir(p): + if hasNimFile(p2): addBabelPath(p2, info) + +proc addPathRec(dir: string, info: TLineInfo) = + var packages = newStringTable(modeStyleInsensitive) + var pos = dir.len-1 + if dir[pos] in {DirSep, AltSep}: inc(pos) + for k,p in os.walkDir(dir): + if k == pcDir and p[pos] != '.': + addPackage(packages, p) + for p in packages.chosen: + addPathWithNimFiles(p, info) + +proc babelPath*(path: string, info: TLineInfo) = + addPathRec(path, info) + addBabelPath(path, info) diff --git a/compiler/c2nim/cpp.nim b/compiler/c2nim/cpp.nim index 3b7f58fcc..c210eca3a 100755 --- a/compiler/c2nim/cpp.nim +++ b/compiler/c2nim/cpp.nim @@ -42,6 +42,7 @@ proc parseDefine(p: var TParser): PNode = result = newNodeP(nkTemplateDef, p) getTok(p) addSon(result, skipIdentExport(p)) + addSon(result, ast.emptyNode) eat(p, pxParLe) var params = newNodeP(nkFormalParams, p) # return type; not known yet: @@ -60,6 +61,7 @@ proc parseDefine(p: var TParser): PNode = addSon(result, ast.emptyNode) # no generic parameters addSon(result, params) addSon(result, ast.emptyNode) # no pragmas + addSon(result, ast.emptyNode) var kind = parseDefineBody(p, result) params.sons[0] = newIdentNodeP(kind, p) eatNewLine(p, result) diff --git a/compiler/c2nim/tests/systest.c b/compiler/c2nim/tests/systest.c index 241526e07..2a9dd6c28 100755 --- a/compiler/c2nim/tests/systest.c +++ b/compiler/c2nim/tests/systest.c @@ -17,6 +17,8 @@ int aw_instance_callback_set (AW_CALLBACK c, callback_t callback); unsigned long int wawa; +#define MAX(x, y) ((x) < (y)? (y) : (x)) + #define AW_BUILD 85 // AW 5.0 // Limits #define AW_MAX_AVCHANGE_PER_SECOND 10 diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 84c56cd28..71e4fe39b 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -146,7 +146,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = proc addComma(r: PRope): PRope = result = if r == nil: r else: con(r, ~", ") - const CallPattern = "$1.ClEnv? $1.ClPrc($3$1.ClEnv) : (($4)($1.ClPrc))($2);$n" + const CallPattern = "$1.ClEnv? $1.ClPrc($3$1.ClEnv) : (($4)($1.ClPrc))($2)" var op: TLoc initLocExpr(p, ri.sons[0], op) var pl: PRope @@ -164,7 +164,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = if i < length - 1: app(pl, ~", ") template genCallPattern = - lineF(p, cpsStmts, CallPattern, op.r, pl, pl.addComma, rawProc) + lineF(p, cpsStmts, CallPattern & ";$n", op.r, pl, pl.addComma, rawProc) let rawProc = getRawProcType(p, typ) if typ.sons[0] != nil: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1ac3dad7d..20636f122 100755 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -939,22 +939,31 @@ proc genNew(p: BProc, e: PNode) = var a, b: TLoc reftype, bt: PType + sizeExpr: PRope refType = skipTypes(e.sons[1].typ, abstractVarRange) InitLocExpr(p, e.sons[1], a) initLoc(b, locExpr, a.t, OnHeap) + # 'genNew' also handles 'unsafeNew': + if e.len == 3: + var se: TLoc + InitLocExpr(p, e.sons[2], se) + sizeExpr = se.rdLoc + else: + sizeExpr = ropef("sizeof($1)", + getTypeDesc(p.module, skipTypes(reftype.sons[0], abstractRange))) let args = [getTypeDesc(p.module, reftype), genTypeInfo(p.module, refType), - getTypeDesc(p.module, skipTypes(reftype.sons[0], abstractRange))] + sizeExpr] if a.s == OnHeap and optRefcGc in gGlobalOptions: # use newObjRC1 as an optimization; and we don't need 'keepAlive' either if canFormAcycle(a.t): linefmt(p, cpsStmts, "if ($1) #nimGCunref($1);$n", a.rdLoc) else: linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", a.rdLoc) - b.r = ropecg(p.module, "($1) #newObjRC1($2, sizeof($3))", args) + b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args) linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc) else: - b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", args) + b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) genAssignment(p, a, b, {needToKeepAlive}) # set the object type: bt = skipTypes(refType.sons[0], abstractRange) genObjectInit(p, cpsStmts, bt, a, false) @@ -1645,7 +1654,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = initLocExpr(p, n.sons[0], a) var r = rdLoc(a) if skipTypes(n.sons[0].typ, abstractInst).kind in {tyRef, tyPtr, tyVar} and - n.sons[0].kind notin {nkHiddenAddr, nkAddr}: + n.sons[0].kind notin {nkHiddenAddr, nkAddr, nkObjDownConv}: app(r, "->Sup") for i in countup(2, abs(inheritanceDiff(dest, src))): app(r, ".Sup") r = con("&", r) diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index cb654cbb5..baf4f5586 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -148,20 +148,47 @@ 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 = lexbase.HandleCR(L, pos) + buf = L.buf + result.data.add(tnl) + of LF: + pos = lexbase.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 - result = newMutableRope(30_000) + var r = newStringOfCap(30_000) while true: case buf[pos] of CR: pos = lexbase.HandleCR(L, pos) buf = L.buf - result.data.add(tnl) + r.add(tnl) of LF: pos = lexbase.HandleLF(L, pos) buf = L.buf - result.data.add(tnl) + r.add(tnl) of '\0': InternalError("ccgmerge: expected: " & NimMergeEndMark) break @@ -169,10 +196,10 @@ proc readVerbatimSection(L: var TBaseLexer): PRope = if atEndMark(buf, pos): inc pos, NimMergeEndMark.len break - result.data.add(buf[pos]) + r.add(buf[pos]) inc pos L.bufpos = pos - freezeMutableRope(result) + result = r.toRope proc readKey(L: var TBaseLexer, result: var string) = var pos = L.bufpos diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 5927c6afd..2f07d24cb 100755 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -122,6 +122,7 @@ proc genBreakState(p: BProc, n: PNode) = lineF(p, cpsStmts, "if ((((NI*) $1.ClEnv)[0]) < 0) break;$n", [rdLoc(a)]) # lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)]) +proc genVarPrototypeAux(m: BModule, sym: PSym) proc genSingleVar(p: BProc, a: PNode) = var v = a.sons[0].sym if sfCompileTime in v.flags: return @@ -140,6 +141,9 @@ proc genSingleVar(p: BProc, a: PNode) = genObjectInit(p.module.preInitProc, cpsInit, v.typ, v.loc, true) # Alternative construction using default constructor (which may zeromem): # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) + if sfExportc in v.flags and generatedHeader != nil: + genVarPrototypeAux(generatedHeader, v) + else: assignLocalVar(p, v) initLocalVar(p, v, immediateAsgn) @@ -878,7 +882,8 @@ proc genStmts(p: BProc, t: PNode) = # we have to emit the type information for object types here to support # separate compilation: genTypeSection(p.module, t) - of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, nkImportStmt, + of nkCommentStmt, nkNilLit, nkIteratorDef, nkIncludeStmt, + nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef: nil of nkPragma: genPragma(p, t) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 71d55c879..399785c82 100755 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -558,17 +558,6 @@ include "ccgexprs.nim", "ccgstmts.nim" # ----------------------------- dynamic library handling ----------------- # We don't finalize dynamic libs as this does the OS for us. -proc libCandidates(s: string, dest: var TStringSeq) = - var le = strutils.find(s, '(') - var ri = strutils.find(s, ')', le+1) - if le >= 0 and ri > le: - var prefix = substr(s, 0, le - 1) - var suffix = substr(s, ri + 1) - for middle in split(substr(s, le + 1, ri - 1), '|'): - libCandidates(prefix & middle & suffix, dest) - else: - add(dest, s) - proc isGetProcAddr(lib: PLib): bool = let n = lib.path result = n.kind in nkCallKinds and n.typ != nil and @@ -870,8 +859,6 @@ proc genVarPrototypeAux(m: BModule, sym: PSym) = proc genVarPrototype(m: BModule, sym: PSym) = genVarPrototypeAux(m, sym) - if sfExportc in sym.flags and generatedHeader != nil: - genVarPrototypeAux(generatedHeader, sym) proc addIntTypes(result: var PRope) {.inline.} = appf(result, "#define NIM_INTBITS $1", [ diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 687653aaf..e7bd54ef0 100755 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -65,10 +65,10 @@ proc sameMethodBucket(a, b: PSym): bool = break if sameType(aa, bb) or (aa.kind == tyObject) and (bb.kind == tyObject) and - (inheritanceDiff(bb, aa) < 0): + (inheritanceDiff(bb, aa) < 0): nil - else: - return + else: + return result = true proc attachDispatcher(s: PSym, dispatcher: PNode) = @@ -106,17 +106,16 @@ proc methodDef*(s: PSym, fromCache: bool) = # attach to itself to prevent bugs: attachDispatcher(disp, newSymNode(disp)) -proc relevantCol(methods: TSymSeq, col: int): bool = +proc relevantCol(methods: TSymSeq, col: int): bool = # returns true iff the position is relevant var t = methods[0].typ.sons[col] - result = false - if skipTypes(t, skipPtrs).kind == tyObject: - for i in countup(1, high(methods)): - if not SameType(methods[i].typ.sons[col], t): + if skipTypes(t, skipPtrs).kind == tyObject: + for i in countup(1, high(methods)): + let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs) + if not SameType(t2, t): return true proc cmpSignatures(a, b: PSym, relevantCols: TIntSet): int = - result = 0 for col in countup(1, sonsLen(a.typ) - 1): if Contains(relevantCols, col): var aa = skipTypes(a.typ.sons[col], skipPtrs) diff --git a/compiler/commands.nim b/compiler/commands.nim index a1a9f0791..a26626cc4 100755 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -11,7 +11,7 @@ import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, lists, - wordrecg, parseutils + wordrecg, parseutils, babelcmd proc writeCommandLineUsage*() @@ -28,7 +28,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) const HelpMessage = "Nimrod Compiler Version $1 (" & compileDate & ") [$2: $3]\n" & - "Copyright (c) 2004-2012 by Andreas Rumpf\n" + "Copyright (c) 2004-2013 by Andreas Rumpf\n" const Usage = slurp"doc/basicopt.txt".replace("//", "") @@ -194,20 +194,6 @@ proc processPath(path: string): string = "projectname", options.gProjectName, "projectpath", options.gProjectPath]) -proc addPath(path: string, info: TLineInfo) = - if not contains(options.searchPaths, path): - lists.PrependStr(options.searchPaths, path) - -proc addPathRec(dir: string, info: TLineInfo) = - var pos = dir.len-1 - if dir[pos] in {DirSep, AltSep}: inc(pos) - for k,p in os.walkDir(dir): - if k == pcDir and p[pos] != '.': - addPathRec(p, info) - if not contains(options.searchPaths, p): - Message(info, hintPath, p) - lists.PrependStr(options.searchPaths, p) - proc track(arg: string, info: TLineInfo) = var a = arg.split(',') if a.len != 3: LocalError(info, errTokenExpected, "FILE,LINE,COLUMN") @@ -227,11 +213,16 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = of "path", "p": expectArg(switch, arg, pass, info) addPath(processPath(arg), info) - of "recursivepath": + of "babelpath": + if pass in {passCmd2, passPP}: + expectArg(switch, arg, pass, info) + let path = processPath(arg) + babelpath(path, info) + of "excludepath": expectArg(switch, arg, pass, info) - var path = processPath(arg) - addPathRec(path, info) - addPath(path, info) + let path = processPath(arg) + lists.ExcludeStr(options.searchPaths, path) + lists.ExcludeStr(options.lazyPaths, path) of "nimcache": expectArg(switch, arg, pass, info) options.nimcacheDir = processPath(arg) @@ -474,6 +465,9 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = of "stdout": expectNoArg(switch, arg, pass, info) incl(gGlobalOptions, optStdout) + of "listfullpaths": + expectNoArg(switch, arg, pass, info) + gListFullPaths = true else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: InvalidCmdLineOption(pass, switch, info) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 234029ea9..17366f6e9 100755 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -60,6 +60,7 @@ proc InitDefines*() = DefineSymbol("niminheritable") DefineSymbol("nimmixin") DefineSymbol("nimeffects") + DefineSymbol("nimbabel") # add platform specific symbols: case targetCPU diff --git a/compiler/depends.nim b/compiler/depends.nim index 2e0f833a0..1468cbdb9 100755 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -33,7 +33,7 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): var imported = getModuleName(n.sons[i]) addDependencyAux(g.module.name.s, imported) - of nkFromStmt: + of nkFromStmt, nkImportExceptStmt: var imported = getModuleName(n.sons[0]) addDependencyAux(g.module.name.s, imported) of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 496136c23..2b7c567c6 100755 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -137,7 +137,27 @@ proc genRecComment(d: PDoc, n: PNode): PRope = if result != nil: return else: n.comment = nil - + +proc findDocComment(n: PNode): PNode = + if n == nil: return nil + if not isNil(n.comment) and startsWith(n.comment, "##"): return n + for i in countup(0, safeLen(n)-1): + result = findDocComment(n.sons[i]) + if result != nil: return + +proc extractDocComment*(s: PSym, d: PDoc = nil): string = + let n = findDocComment(s.ast) + result = "" + if not n.isNil: + if not d.isNil: + var dummyHasToc: bool + renderRstToOut(d[], parseRst(n.comment, toFilename(n.info), + toLineNumber(n.info), toColumn(n.info), + dummyHasToc, d.options + {roSkipPounds}), + result) + else: + result = n.comment.substr(2).replace("\n##", "\n").strip + proc isVisible(n: PNode): bool = result = false if n.kind == nkPostfix: @@ -145,6 +165,9 @@ proc isVisible(n: PNode): bool = var v = n.sons[0].ident result = v.id == ord(wStar) or v.id == ord(wMinus) elif n.kind == nkSym: + # we cannot generate code for forwarded symbols here as we have no + # exception tracking information here. Instead we copy over the comment + # from the proc header. result = {sfExported, sfFromGeneric, sfForward}*n.sym.flags == {sfExported} elif n.kind == nkPragmaExpr: result = isVisible(n.sons[0]) @@ -273,7 +296,7 @@ proc generateDoc*(d: PDoc, n: PNode) = generateDoc(d, lastSon(n.sons[0])) of nkImportStmt: for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i]) - of nkFromStmt: traceDeps(d, n.sons[0]) + of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0]) else: nil proc genSection(d: PDoc, kind: TSymKind) = @@ -350,9 +373,11 @@ proc CommandRstAux(filename, outExt: string) = var d = newDocumentor(filen, options.gConfigVars) var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc, {roSupportRawDirective}) - d.modDesc = newMutableRope(30_000) - renderRstToOut(d[], rst, d.modDesc.data) - freezeMutableRope(d.modDesc) + var modDesc = newStringOfCap(30_000) + #d.modDesc = newMutableRope(30_000) + renderRstToOut(d[], rst, modDesc) + #freezeMutableRope(d.modDesc) + d.modDesc = toRope(modDesc) writeOutput(d, filename, outExt) generateIndex(d) diff --git a/compiler/ecmasgen.nim b/compiler/ecmasgen.nim index 30b1ac8f0..34b3b4ff5 100755 --- a/compiler/ecmasgen.nim +++ b/compiler/ecmasgen.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -207,27 +207,42 @@ proc genObjectInfo(p: var TProc, typ: PType, name: PRope) = appf(p.g.typeInfo, "$1.base = $2;$n", [name, genTypeInfo(p, typ.sons[0])]) -proc genEnumInfo(p: var TProc, typ: PType, name: PRope) = - var - s, n: PRope - length: int - field: PSym - length = sonsLen(typ.n) - s = nil +proc genTupleFields(p: var TProc, typ: PType): PRope = + var s: PRope = nil + for i in 0 .. <typ.len: + if i > 0: app(s, ", " & tnl) + s.appf("{kind: 1, offset: \"Field$1\", len: 0, " & + "typ: $2, name: \"Field$1\", sons: null}", + [i.toRope, genTypeInfo(p, typ.sons[i])]) + result = ropef("{kind: 2, len: $1, offset: 0, " & + "typ: null, name: null, sons: [$2]}", [toRope(typ.len), s]) + +proc genTupleInfo(p: var TProc, typ: PType, name: PRope) = + var s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & + "finalizer: null};$n", [name, toRope(ord(typ.kind))]) + prepend(p.g.typeInfo, s) + appf(p.g.typeInfo, "var NNI$1 = $2;$n", + [toRope(typ.id), genTupleFields(p, typ)]) + appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) + +proc genEnumInfo(p: var TProc, typ: PType, name: PRope) = + let length = sonsLen(typ.n) + var s: PRope = nil for i in countup(0, length - 1): if (typ.n.sons[i].kind != nkSym): InternalError(typ.n.info, "genEnumInfo") - field = typ.n.sons[i].sym + let field = typ.n.sons[i].sym if i > 0: app(s, ", " & tnl) + let extName = if field.ast == nil: field.name.s else: field.ast.strVal appf(s, "{kind: 1, offset: $1, typ: $2, name: $3, len: 0, sons: null}", - [toRope(field.position), name, makeCString(field.name.s)]) - n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " & + [toRope(field.position), name, makeCString(extName)]) + var n = ropef("var NNI$1 = {kind: 2, offset: 0, typ: null, " & "name: null, len: $2, sons: [$3]};$n", [toRope(typ.id), toRope(length), s]) s = ropef("var $1 = {size: 0, kind: $2, base: null, node: null, " & "finalizer: null};$n", [name, toRope(ord(typ.kind))]) prepend(p.g.typeInfo, s) app(p.g.typeInfo, n) appf(p.g.typeInfo, "$1.node = NNI$2;$n", [name, toRope(typ.id)]) - if typ.sons[0] != nil: + if typ.sons[0] != nil: appf(p.g.typeInfo, "$1.base = $2;$n", [name, genTypeInfo(p, typ.sons[0])]) @@ -259,7 +274,8 @@ proc genTypeInfo(p: var TProc, typ: PType): PRope = appf(p.g.typeInfo, "$1.base = $2;$n", [result, genTypeInfo(p, typ.sons[1])]) of tyEnum: genEnumInfo(p, t, result) - of tyObject, tyTuple: genObjectInfo(p, t, result) + of tyObject: genObjectInfo(p, t, result) + of tyTuple: genTupleInfo(p, t, result) else: InternalError("genTypeInfo(" & $t.kind & ')') proc gen(p: var TProc, n: PNode, r: var TCompRes) @@ -938,7 +954,9 @@ proc genSym(p: var TProc, n: PNode, r: var TCompRes) = of skProc, skConverter, skMethod: discard mangleName(s) r.res = s.loc.r - if lfNoDecl in s.loc.flags or s.magic != mNone or isGenericRoutine(s): nil + if lfNoDecl in s.loc.flags or s.magic != mNone or isGenericRoutine(s) or + {sfImportc, sfInfixCall} * s.flags != {}: + nil elif s.kind == skMethod and s.getBody.kind == nkEmpty: # we cannot produce code for the dispatcher yet: nil @@ -962,24 +980,45 @@ proc genDeref(p: var TProc, n: PNode, r: var TCompRes) = if a.kind != etyBaseIndex: InternalError(n.info, "genDeref") r.res = ropef("$1[$2]", [a.com, a.res]) +proc genArg(p: var TProc, n: PNode, r: var TCompRes) = + var a: TCompRes + gen(p, n, a) + if a.kind == etyBaseIndex: + app(r.res, a.com) + app(r.res, ", ") + app(r.res, a.res) + else: + app(r.res, mergeExpr(a)) + proc genArgs(p: var TProc, n: PNode, r: var TCompRes) = app(r.res, "(") for i in countup(1, sonsLen(n) - 1): if i > 1: app(r.res, ", ") - var a: TCompRes - gen(p, n.sons[i], a) - if a.kind == etyBaseIndex: - app(r.res, a.com) - app(r.res, ", ") - app(r.res, a.res) - else: - app(r.res, mergeExpr(a)) + genArg(p, n.sons[i], r) app(r.res, ")") proc genCall(p: var TProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) genArgs(p, n, r) +proc genInfixCall(p: var TProc, n: PNode, r: var TCompRes) = + gen(p, n.sons[1], r) + if r.kind == etyBaseIndex: + if r.com == nil: + GlobalError(n.info, "cannot invoke with infix syntax") + r.res = ropef("$1[0]", [r.res, r.com]) + r.com = nil + app(r.res, ".") + var op: TCompRes + gen(p, n.sons[0], op) + app(r.res, mergeExpr(op)) + + app(r.res, "(") + for i in countup(2, sonsLen(n) - 1): + if i > 2: app(r.res, ", ") + genArg(p, n.sons[i], r) + app(r.res, ")") + proc genEcho(p: var TProc, n: PNode, r: var TCompRes) = useMagic(p, "rawEcho") app(r.res, "rawEcho") @@ -1176,10 +1215,13 @@ proc genConStrStr(p: var TProc, n: PNode, r: var TCompRes) = proc genRepr(p: var TProc, n: PNode, r: var TCompRes) = var t = skipTypes(n.sons[1].typ, abstractVarRange) case t.kind - of tyInt..tyInt64: - unaryExpr(p, n, r, "", "reprInt($1)") + of tyInt..tyUInt64: + unaryExpr(p, n, r, "", "(\"\"+ ($1))") of tyEnum, tyOrdinal: - binaryExpr(p, n, r, "", "reprEnum($1, $2)") + gen(p, n.sons[1], r) + useMagic(p, "cstrToNimstr") + r.res = ropef("cstrToNimstr($1.node.sons[$2].name)", + [genTypeInfo(p, t), r.res]) else: # XXX: internalError(n.info, "genRepr: Not implemented") @@ -1463,7 +1505,8 @@ proc genStmt(p: var TProc, n: PNode, r: var TCompRes) = of nkAsmStmt: genAsmStmt(p, n, r) of nkTryStmt: genTryStmt(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n, r) - of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, + of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, + nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef, nkPragma: nil of nkProcDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym @@ -1510,9 +1553,12 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) = else: r.res = toRope(f.ToStrMaxPrecision) of nkBlockExpr: genBlock(p, n, r) of nkIfExpr: genIfExpr(p, n, r) - of nkCallKinds: + of nkCallKinds: if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): genMagic(p, n, r) + elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and + n.len >= 2: + genInfixCall(p, n, r) else: genCall(p, n, r) of nkCurly: genSetConstr(p, n, r) @@ -1526,6 +1572,7 @@ proc gen(p: var TProc, n: PNode, r: var TCompRes) = of nkCheckedFieldExpr: genCheckedFieldAccess(p, n, r) of nkObjDownConv: gen(p, n.sons[0], r) of nkObjUpConv: upConv(p, n, r) + of nkCast: gen(p, n.sons[1], r) of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF") of nkChckRange64: genRangeChck(p, n, r, "chckRange64") of nkChckRange: genRangeChck(p, n, r, "chckRange") @@ -1555,7 +1602,7 @@ proc newModule(module: PSym): BModule = proc genHeader(): PRope = result = ropef("/* Generated by the Nimrod Compiler v$1 */$n" & - "/* (c) 2012 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" & + "/* (c) 2013 Andreas Rumpf */$n$n" & "$nvar Globals = this;$n" & "var framePtr = null;$n" & "var excHandler = null;$n", [toRope(versionAsString)]) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim new file mode 100644 index 000000000..ba6e7ee8f --- /dev/null +++ b/compiler/evalffi.nim @@ -0,0 +1,443 @@ +# +# +# The Nimrod Compiler +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This file implements the FFI part of the evaluator for Nimrod code. + +import ast, astalgo, ropes, types, options, tables, dynlib, libffi, msgs + +when defined(windows): + const libcDll = "msvcrt.dll" +else: + const libcDll = "libc.so(.6|.5|)" + +type + TDllCache = tables.TTable[string, TLibHandle] +var + gDllCache = initTable[string, TLibHandle]() + gExeHandle = LoadLib() + +proc getDll(cache: var TDllCache; dll: string; info: TLineInfo): pointer = + result = cache[dll] + if result.isNil: + var libs: seq[string] = @[] + libCandidates(dll, libs) + for c in libs: + result = LoadLib(c) + if not result.isNil: break + if result.isNil: + GlobalError(info, "cannot load: " & dll) + cache[dll] = result + +const + nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon + +proc importcSymbol*(sym: PSym): PNode = + let name = ropeToStr(sym.loc.r) + + # the AST does not support untyped pointers directly, so we use an nkIntLit + # that contains the address instead: + result = newNodeIT(nkPtrLit, sym.info, sym.typ) + case name + of "stdin": result.intVal = cast[TAddress](system.stdin) + of "stdout": result.intVal = cast[TAddress](system.stdout) + of "stderr": result.intVal = cast[TAddress](system.stderr) + else: + let lib = sym.annex + if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}: + GlobalError(sym.info, "dynlib needs to be a string lit for the REPL") + var theAddr: pointer + if lib.isNil and not gExehandle.isNil: + # first try this exe itself: + theAddr = gExehandle.symAddr(name) + # then try libc: + if theAddr.isNil: + let dllhandle = gDllCache.getDll(libcDll, sym.info) + theAddr = dllhandle.checkedSymAddr(name) + else: + let dllhandle = gDllCache.getDll(lib.path.strVal, sym.info) + theAddr = dllhandle.checkedSymAddr(name) + result.intVal = cast[TAddress](theAddr) + +proc mapType(t: ast.PType): ptr libffi.TType = + if t == nil: return addr libffi.type_void + + case t.kind + of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet: + case t.getSize + of 1: result = addr libffi.type_uint8 + of 2: result = addr libffi.type_sint16 + of 4: result = addr libffi.type_sint32 + of 8: result = addr libffi.type_sint64 + else: result = nil + of tyFloat, tyFloat64: result = addr libffi.type_double + of tyFloat32: result = addr libffi.type_float + of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr, + tyStmt, tyTypeDesc, tyProc, tyArray, tyArrayConstr, tyNil: + result = addr libffi.type_pointer + of tyDistinct: + result = mapType(t.sons[0]) + else: + result = nil + # too risky: + #of tyFloat128: result = addr libffi.type_longdouble + +proc mapCallConv(cc: TCallingConvention, info: TLineInfo): TABI = + case cc + of ccDefault: result = DEFAULT_ABI + of ccStdCall: result = when defined(windows): STDCALL else: DEFAULT_ABI + of ccCDecl: result = DEFAULT_ABI + else: + GlobalError(info, "cannot map calling convention to FFI") + +template rd(T, p: expr): expr {.immediate.} = (cast[ptr T](p))[] +template wr(T, p, v: expr) {.immediate.} = (cast[ptr T](p))[] = v +template `+!`(x, y: expr): expr {.immediate.} = + cast[pointer](cast[TAddress](x) + y) + +proc packSize(v: PNode, typ: PType): int = + ## computes the size of the blob + case typ.kind + of tyPtr, tyRef, tyVar: + if v.kind in {nkNilLit, nkPtrLit}: + result = sizeof(pointer) + else: + result = sizeof(pointer) + packSize(v.sons[0], typ.sons[0]) + of tyDistinct, tyGenericInst: + result = packSize(v, typ.sons[0]) + of tyArray, tyArrayConstr: + # consider: ptr array[0..1000_000, int] which is common for interfacing; + # we use the real length here instead + if v.kind in {nkNilLit, nkPtrLit}: + result = sizeof(pointer) + elif v.len != 0: + result = v.len * packSize(v.sons[0], typ.sons[1]) + else: + result = typ.getSize.int + +proc pack(v: PNode, typ: PType, res: pointer) + +proc getField(n: PNode; position: int): PSym = + case n.kind + of nkRecList: + for i in countup(0, sonsLen(n) - 1): + result = getField(n.sons[i], position) + if result != nil: return + of nkRecCase: + result = getField(n.sons[0], position) + if result != nil: return + for i in countup(1, sonsLen(n) - 1): + case n.sons[i].kind + of nkOfBranch, nkElse: + result = getField(lastSon(n.sons[i]), position) + if result != nil: return + else: internalError(n.info, "getField(record case branch)") + of nkSym: + if n.sym.position == position: result = n.sym + else: nil + +proc packObject(x: PNode, typ: PType, res: pointer) = + InternalAssert x.kind == nkPar + # compute the field's offsets: + discard typ.getSize + for i in countup(0, sonsLen(x) - 1): + var it = x.sons[i] + if it.kind == nkExprColonExpr: + internalAssert it.sons[0].kind == nkSym + let field = it.sons[0].sym + pack(it.sons[1], field.typ, res +! field.offset) + elif typ.n != nil: + let field = getField(typ.n, i) + pack(it, field.typ, res +! field.offset) + else: + GlobalError(x.info, "cannot pack unnamed tuple") + +const maxPackDepth = 20 +var packRecCheck = 0 + +proc pack(v: PNode, typ: PType, res: pointer) = + template awr(T, v: expr) {.immediate, dirty.} = + wr(T, res, v) + + case typ.kind + of tyBool: awr(bool, v.intVal != 0) + of tyChar: awr(char, v.intVal.chr) + of tyInt: awr(int, v.intVal.int) + of tyInt8: awr(int8, v.intVal.int8) + of tyInt16: awr(int16, v.intVal.int16) + of tyInt32: awr(int32, v.intVal.int32) + of tyInt64: awr(int64, v.intVal.int64) + of tyUInt: awr(uint, v.intVal.uint) + of tyUInt8: awr(uint8, v.intVal.uint8) + of tyUInt16: awr(uint16, v.intVal.uint16) + of tyUInt32: awr(uint32, v.intVal.uint32) + of tyUInt64: awr(uint64, v.intVal.uint64) + of tyEnum, tySet: + case v.typ.getSize + of 1: awr(uint8, v.intVal.uint8) + of 2: awr(uint16, v.intVal.uint16) + of 4: awr(int32, v.intVal.int32) + of 8: awr(int64, v.intVal.int64) + else: + GlobalError(v.info, "cannot map value to FFI (tyEnum, tySet)") + of tyFloat: awr(float, v.floatVal) + of tyFloat32: awr(float32, v.floatVal) + of tyFloat64: awr(float64, v.floatVal) + + of tyPointer, tyProc, tyCString, tyString: + if v.kind == nkNilLit: + # nothing to do since the memory is 0 initialized anyway + nil + elif v.kind == nkPtrLit: + awr(pointer, cast[pointer](v.intVal)) + elif v.kind in {nkStrLit..nkTripleStrLit}: + awr(cstring, cstring(v.strVal)) + else: + GlobalError(v.info, "cannot map pointer/proc value to FFI") + of tyPtr, tyRef, tyVar: + if v.kind == nkNilLit: + # nothing to do since the memory is 0 initialized anyway + nil + elif v.kind == nkPtrLit: + awr(pointer, cast[pointer](v.intVal)) + else: + if packRecCheck > maxPackDepth: + packRecCheck = 0 + GlobalError(v.info, "cannot map value to FFI " & typeToString(v.typ)) + inc packRecCheck + pack(v.sons[0], typ.sons[0], res +! sizeof(pointer)) + dec packRecCheck + awr(pointer, res +! sizeof(pointer)) + of tyArray, tyArrayConstr: + let baseSize = typ.sons[1].getSize + for i in 0 .. <v.len: + pack(v.sons[i], typ.sons[1], res +! i * baseSize) + of tyObject, tyTuple: + packObject(v, typ, res) + of tyNil: + nil + of tyDistinct, tyGenericInst: + pack(v, typ.sons[0], res) + else: + GlobalError(v.info, "cannot map value to FFI " & typeToString(v.typ)) + +proc unpack(x: pointer, typ: PType, n: PNode): PNode + +proc unpackObjectAdd(x: pointer, n, result: PNode) = + case n.kind + of nkRecList: + for i in countup(0, sonsLen(n) - 1): + unpackObjectAdd(x, n.sons[i], result) + of nkRecCase: + GlobalError(result.info, "case objects cannot be unpacked") + of nkSym: + var pair = newNodeI(nkExprColonExpr, result.info, 2) + pair.sons[0] = n + pair.sons[1] = unpack(x +! n.sym.offset, n.sym.typ, nil) + #echo "offset: ", n.sym.name.s, " ", n.sym.offset + result.add pair + else: nil + +proc unpackObject(x: pointer, typ: PType, n: PNode): PNode = + # compute the field's offsets: + discard typ.getSize + + # iterate over any actual field of 'n' ... if n is nil we need to create + # the nkPar node: + if n.isNil: + result = newNode(nkPar) + result.typ = typ + if typ.n.isNil: + InternalError("cannot unpack unnamed tuple") + unpackObjectAdd(x, typ.n, result) + else: + result = n + if result.kind != nkPar: + GlobalError(n.info, "cannot map value from FFI") + if typ.n.isNil: + GlobalError(n.info, "cannot unpack unnamed tuple") + for i in countup(0, sonsLen(n) - 1): + var it = n.sons[i] + if it.kind == nkExprColonExpr: + internalAssert it.sons[0].kind == nkSym + let field = it.sons[0].sym + it.sons[1] = unpack(x +! field.offset, field.typ, it.sons[1]) + else: + let field = getField(typ.n, i) + n.sons[i] = unpack(x +! field.offset, field.typ, it) + +proc unpackArray(x: pointer, typ: PType, n: PNode): PNode = + if n.isNil: + result = newNode(nkBracket) + result.typ = typ + newSeq(result.sons, lengthOrd(typ).int) + else: + result = n + if result.kind != nkBracket: + GlobalError(n.info, "cannot map value from FFI") + let baseSize = typ.sons[1].getSize + for i in 0 .. < result.len: + result.sons[i] = unpack(x +! i * baseSize, typ.sons[1], result.sons[i]) + +proc canonNodeKind(k: TNodeKind): TNodeKind = + case k + of nkCharLit..nkUInt64Lit: result = nkIntLit + of nkFloatLit..nkFloat128Lit: result = nkFloatLit + of nkStrLit..nkTripleStrLit: result = nkStrLit + else: result = k + +proc unpack(x: pointer, typ: PType, n: PNode): PNode = + template aw(k, v, field: expr) {.immediate, dirty.} = + if n.isNil: + result = newNode(k) + result.typ = typ + else: + # check we have the right field: + result = n + if result.kind.canonNodeKind != k.canonNodeKind: + #echo "expected ", k, " but got ", result.kind + #debug result + return newNodeI(nkExceptBranch, n.info) + #GlobalError(n.info, "cannot map value from FFI") + result.field = v + + template setNil() = + if n.isNil: + result = newNode(nkNilLit) + result.typ = typ + else: + reset n[] + result = n + result.kind = nkNilLit + result.typ = typ + + template awi(kind, v: expr) {.immediate, dirty.} = aw(kind, v, intVal) + template awf(kind, v: expr) {.immediate, dirty.} = aw(kind, v, floatVal) + template aws(kind, v: expr) {.immediate, dirty.} = aw(kind, v, strVal) + + case typ.kind + of tyBool: awi(nkIntLit, rd(bool, x).ord) + of tyChar: awi(nkCharLit, rd(char, x).ord) + of tyInt: awi(nkIntLit, rd(int, x)) + of tyInt8: awi(nkInt8Lit, rd(int8, x)) + of tyInt16: awi(nkInt16Lit, rd(int16, x)) + of tyInt32: awi(nkInt32Lit, rd(int32, x)) + of tyInt64: awi(nkInt64Lit, rd(int64, x)) + of tyUInt: awi(nkUIntLit, rd(uint, x).biggestInt) + of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).biggestInt) + of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).biggestInt) + of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).biggestInt) + of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).biggestInt) + of tyEnum: + case typ.getSize + of 1: awi(nkIntLit, rd(uint8, x).biggestInt) + of 2: awi(nkIntLit, rd(uint16, x).biggestInt) + of 4: awi(nkIntLit, rd(int32, x).biggestInt) + of 8: awi(nkIntLit, rd(int64, x).biggestInt) + else: + GlobalError(n.info, "cannot map value from FFI (tyEnum, tySet)") + of tyFloat: awf(nkFloatLit, rd(float, x)) + of tyFloat32: awf(nkFloat32Lit, rd(float32, x)) + of tyFloat64: awf(nkFloat64Lit, rd(float64, x)) + of tyPointer, tyProc: + let p = rd(pointer, x) + if p.isNil: + setNil() + elif n != nil and n.kind == nkStrLit: + # we passed a string literal as a pointer; however strings are already + # in their unboxed representation so nothing it to be unpacked: + result = n + else: + awi(nkPtrLit, cast[TAddress](p)) + of tyPtr, tyRef, tyVar: + let p = rd(pointer, x) + if p.isNil: + setNil() + elif n == nil or n.kind == nkPtrLit: + awi(nkPtrLit, cast[TAddress](p)) + elif n != nil and n.len == 1: + internalAssert n.kind == nkRefTy + n.sons[0] = unpack(p, typ.sons[0], n.sons[0]) + result = n + else: + GlobalError(n.info, "cannot map value from FFI " & typeToString(typ)) + of tyObject, tyTuple: + result = unpackObject(x, typ, n) + of tyArray, tyArrayConstr: + result = unpackArray(x, typ, n) + of tyCString, tyString: + let p = rd(cstring, x) + if p.isNil: + setNil() + else: + aws(nkStrLit, $p) + of tyNil: + setNil() + of tyDistinct, tyGenericInst: + result = unpack(x, typ.sons[0], n) + else: + # XXX what to do with 'array' here? + GlobalError(n.info, "cannot map value from FFI " & typeToString(typ)) + +proc fficast*(x: PNode, destTyp: PType): PNode = + if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyPointer, + tyProc, tyCString, tyString, + tySequence}: + result = newNodeIT(x.kind, x.info, destTyp) + result.intVal = x.intVal + elif x.kind == nkNilLit: + result = newNodeIT(x.kind, x.info, destTyp) + else: + # we play safe here and allocate the max possible size: + let size = max(packSize(x, x.typ), packSize(x, destTyp)) + var a = alloc0(size) + pack(x, x.typ, a) + # cast through a pointer needs a new inner object: + let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1) + else: x.copyTree + y.typ = x.typ + result = unpack(a, destTyp, y) + dealloc a + +proc callForeignFunction*(call: PNode): PNode = + InternalAssert call.sons[0].kind == nkPtrLit + + var cif: TCif + var sig: TParamList + # use the arguments' types for varargs support: + for i in 1..call.len-1: + sig[i-1] = mapType(call.sons[i].typ) + if sig[i-1].isNil: + GlobalError(call.info, "cannot map FFI type") + + let typ = call.sons[0].typ + if prep_cif(cif, mapCallConv(typ.callConv, call.info), cuint(call.len-1), + mapType(typ.sons[0]), sig) != OK: + GlobalError(call.info, "error in FFI call") + + var args: TArgList + let fn = cast[pointer](call.sons[0].intVal) + for i in 1 .. call.len-1: + var t = call.sons[i].typ + args[i-1] = alloc0(packSize(call.sons[i], t)) + pack(call.sons[i], t, args[i-1]) + let retVal = if isEmptyType(typ.sons[0]): pointer(nil) + else: alloc(typ.sons[0].getSize.int) + + libffi.call(cif, fn, retVal, args) + + if retVal.isNil: + result = emptyNode + else: + result = unpack(retVal, typ.sons[0], nil) + result.info = call.info + + if retVal != nil: dealloc retVal + for i in 1 .. call.len-1: + call.sons[i] = unpack(args[i-1], typ.sons[i], call[i]) + dealloc args[i-1] diff --git a/compiler/evals.nim b/compiler/evals.nim index 924540b26..4b83cb703 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -18,6 +18,9 @@ import msgs, os, condsyms, idents, renderer, types, passes, semfold, transf, parser, ropes, rodread, idgen, osproc, streams, evaltempl +when hasFFI: + import evalffi + type PStackFrame* = ref TStackFrame TStackFrame*{.final.} = object @@ -34,12 +37,20 @@ type ## emConst?) emStatic ## evaluate for enforced compile time eval ## ('static' context) + + TSandboxFlag* = enum ## what the evaluation engine should allow + allowCast, ## allow unsafe language feature: 'cast' + allowFFI, ## allow the FFI + allowInfiniteLoops ## allow endless loops + TSandboxFlags* = set[TSandboxFlag] + TEvalContext* = object of passes.TPassContext module*: PSym tos*: PStackFrame # top of stack lastException*: PNode callsite: PNode # for 'callsite' magic mode*: TEvalMode + features: TSandboxFlags globals*: TIdNodeTable # state of global vars getType*: proc(n: PNode): PNode {.closure.} @@ -65,6 +76,7 @@ proc newEvalContext*(module: PSym, mode: TEvalMode): PEvalContext = new(result) result.module = module result.mode = mode + result.features = {allowFFI} initIdNodeTable(result.globals) proc pushStackFrame*(c: PEvalContext, t: PStackFrame) {.inline.} = @@ -78,7 +90,7 @@ proc popStackFrame*(c: PEvalContext) {.inline.} = proc evalMacroCall*(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode -proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = +proc raiseCannotEval(c: PEvalContext, info: TLineInfo): PNode = result = newNodeI(nkExceptBranch, info) # creating a nkExceptBranch without sons # means that it could not be evaluated @@ -162,9 +174,12 @@ proc evalWhile(c: PEvalContext, n: PNode): PNode = of nkExceptBranch, nkReturnToken: break else: nil dec(gWhileCounter) - if gWhileCounter <= 0: - stackTrace(c, n, errTooManyIterations) - break + if gWhileCounter <= 0: + if allowInfiniteLoops in c.features: + gWhileCounter = 0 + else: + stackTrace(c, n, errTooManyIterations) + break proc evalBlock(c: PEvalContext, n: PNode): PNode = result = evalAux(c, n.sons[1], {}) @@ -303,45 +318,9 @@ proc evalVar(c: PEvalContext, n: PNode): PNode = else: if x.kind notin {nkEmpty..nkNilLit}: discardSons(x) - for i in countup(0, sonsLen(result) - 1): addSon(x, result.sons[i]) + for j in countup(0, sonsLen(result) - 1): addSon(x, result.sons[j]) result = emptyNode -proc evalCall(c: PEvalContext, n: PNode): PNode = - var d = newStackFrame() - d.call = n - var prc = n.sons[0] - let isClosure = prc.kind == nkClosure - setlen(d.params, sonsLen(n) + ord(isClosure)) - if isClosure: - #debug prc - result = evalAux(c, prc.sons[1], {efLValue}) - if isSpecial(result): return - d.params[sonsLen(n)] = result - result = evalAux(c, prc.sons[0], {}) - else: - result = evalAux(c, prc, {}) - - if isSpecial(result): return - prc = result - # bind the actual params to the local parameter of a new binding - if prc.kind != nkSym: - InternalError(n.info, "evalCall " & n.renderTree) - return - d.prc = prc.sym - if prc.sym.kind notin {skProc, skConverter, skMacro}: - InternalError(n.info, "evalCall") - return - for i in countup(1, sonsLen(n) - 1): - result = evalAux(c, n.sons[i], {}) - if isSpecial(result): return - d.params[i] = result - if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info) - pushStackFrame(c, d) - result = evalAux(c, prc.sym.getBody, {}) - if result.kind == nkExceptBranch: return - if n.typ != nil: result = d.params[0] - popStackFrame(c) - proc aliasNeeded(n: PNode, flags: TEvalFlags): bool = result = efLValue in flags or n.typ == nil or n.typ.kind in {tyExpr, tyStmt, tyTypeDesc} @@ -371,6 +350,12 @@ proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode = if not aliasNeeded(result, flags): result = copyTree(result) else: + when hasFFI: + if sfImportc in s.flags and allowFFI in c.features: + result = importcSymbol(s) + IdNodeTablePut(c.globals, s, result) + return result + result = s.ast if result == nil or result.kind == nkEmpty: result = getNullValue(s.typ, s.info) @@ -381,6 +366,51 @@ proc evalGlobalVar(c: PEvalContext, s: PSym, flags: TEvalFlags): PNode = else: result = raiseCannotEval(nil, s.info) +proc evalCall(c: PEvalContext, n: PNode): PNode = + var d = newStackFrame() + d.call = n + var prc = n.sons[0] + let isClosure = prc.kind == nkClosure + setlen(d.params, sonsLen(n) + ord(isClosure)) + if isClosure: + #debug prc + result = evalAux(c, prc.sons[1], {efLValue}) + if isSpecial(result): return + d.params[sonsLen(n)] = result + result = evalAux(c, prc.sons[0], {}) + else: + result = evalAux(c, prc, {}) + + if isSpecial(result): return + prc = result + # bind the actual params to the local parameter of a new binding + if prc.kind != nkSym: + InternalError(n.info, "evalCall " & n.renderTree) + return + d.prc = prc.sym + if prc.sym.kind notin {skProc, skConverter, skMacro}: + InternalError(n.info, "evalCall") + return + for i in countup(1, sonsLen(n) - 1): + result = evalAux(c, n.sons[i], {}) + if isSpecial(result): return + d.params[i] = result + if n.typ != nil: d.params[0] = getNullValue(n.typ, n.info) + + when hasFFI: + if sfImportc in prc.sym.flags and allowFFI in c.features: + var newCall = newNodeI(nkCall, n.info, n.len) + newCall.sons[0] = evalGlobalVar(c, prc.sym, {}) + for i in 1 .. <n.len: + newCall.sons[i] = d.params[i] + return callForeignFunction(newCall) + + pushStackFrame(c, d) + result = evalAux(c, prc.sym.getBody, {}) + if result.kind == nkExceptBranch: return + if n.typ != nil: result = d.params[0] + popStackFrame(c) + proc evalArrayAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = evalAux(c, n.sons[0], flags) if isSpecial(result): return @@ -519,7 +549,9 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = of skConst: result = s.ast of skEnumField: result = newIntNodeT(s.position, n) else: result = nil - if result == nil or {sfImportc, sfForward} * s.flags != {}: + let mask = if hasFFI and allowFFI in c.features: {sfForward} + else: {sfImportc, sfForward} + if result == nil or mask * s.flags != {}: result = raiseCannotEval(c, n.info) proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = @@ -617,6 +649,18 @@ proc evalConv(c: PEvalContext, n: PNode): PNode = # foldConv() cannot deal with everything that we want to do here: result = a +proc evalCast(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = + if allowCast in c.features: + when hasFFI: + result = evalAux(c, n.sons[1], {efLValue}) + if isSpecial(result): return + InternalAssert result.typ != nil + result = fficast(result, n.typ) + else: + result = evalConv(c, n) + else: + result = raiseCannotEval(c, n.info) + proc evalCheckedFieldAccess(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result = evalAux(c, n.sons[0], flags) @@ -1363,7 +1407,9 @@ proc evalAux(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = result.typ = n.typ of nkPragmaBlock: result = evalAux(c, n.sons[1], flags) - of nkIdentDefs, nkCast, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, + of nkCast: + result = evalCast(c, n, flags) + of nkIdentDefs, nkYieldStmt, nkAsmStmt, nkForStmt, nkPragmaExpr, nkLambdaKinds, nkContinueStmt, nkIdent, nkParForStmt, nkBindStmt: result = raiseCannotEval(c, n.info) of nkRefTy: @@ -1388,7 +1434,7 @@ proc eval*(c: PEvalContext, n: PNode): PNode = if sonsLen(result) >= 1: stackTrace(c, n, errUnhandledExceptionX, typeToString(result.typ)) else: - stackTrace(c, n, errCannotInterpretNodeX, renderTree(n)) + stackTrace(c, result, errCannotInterpretNodeX, renderTree(n)) proc evalConstExprAux(module: PSym, e: PNode, mode: TEvalMode): PNode = var p = newEvalContext(module, mode) @@ -1431,8 +1477,9 @@ proc evalMacroCall(c: PEvalContext, n, nOrig: PNode, sym: PSym): PNode = dec(evalTemplateCounter) c.callsite = nil -proc myOpen(module: PSym): PPassContext = +proc myOpen(module: PSym): PPassContext = var c = newEvalContext(module, emRepl) + c.features = {allowCast, allowFFI, allowInfiniteLoops} pushStackFrame(c, newStackFrame()) result = c diff --git a/compiler/importer.nim b/compiler/importer.nim index 1723e9e0e..d274b4693 100755 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -11,11 +11,10 @@ import intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups, - semdata, passes + semdata, passes, renderer proc evalImport*(c: PContext, n: PNode): PNode proc evalFrom*(c: PContext, n: PNode): PNode -proc importAllSymbols*(c: PContext, fromMod: PSym) proc getModuleName*(n: PNode): string = # This returns a short relative module name without the nim extension @@ -29,8 +28,11 @@ proc getModuleName*(n: PNode): string = of nkSym: result = n.sym.name.s else: - internalError(n.info, "getModuleName") - result = "" + # hacky way to implement 'x / y /../ z': + result = renderTree(n, {renderNoComments}).replace(" ") + #localError(n.info, errGenerated, + # "invalide module name: '$1'" % renderTree(n)) + #result = "" proc checkModuleName*(n: PNode): int32 = # This returns the full canonical path for a given module import @@ -42,42 +44,43 @@ proc checkModuleName*(n: PNode): int32 = else: result = fullPath.fileInfoIdx -proc rawImportSymbol(c: PContext, s: PSym) = +proc rawImportSymbol(c: PContext, s: PSym) = # This does not handle stubs, because otherwise loading on demand would be # pointless in practice. So importing stubs is fine here! - var copy = s # do not copy symbols when importing! # check if we have already a symbol of the same name: var check = StrTableGet(c.tab.stack[importTablePos], s.name) - if check != nil and check.id != copy.id: - if s.kind notin OverloadableSyms: + if check != nil and check.id != s.id: + if s.kind notin OverloadableSyms: # s and check need to be qualified: - Incl(c.AmbiguousSymbols, copy.id) + Incl(c.AmbiguousSymbols, s.id) Incl(c.AmbiguousSymbols, check.id) - StrTableAdd(c.tab.stack[importTablePos], copy) - if s.kind == skType: + # thanks to 'export' feature, it could be we import the same symbol from + # multiple sources, so we need to call 'StrTableAdd' here: + StrTableAdd(c.tab.stack[importTablePos], s) + if s.kind == skType: var etyp = s.typ - if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags: - for j in countup(0, sonsLen(etyp.n) - 1): + if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags: + for j in countup(0, sonsLen(etyp.n) - 1): var e = etyp.n.sons[j].sym - if (e.Kind != skEnumField): + if e.Kind != skEnumField: InternalError(s.info, "rawImportSymbol") # BUGFIX: because of aliases for enums the symbol may already # have been put into the symbol table # BUGFIX: but only iff they are the same symbols! var it: TIdentIter check = InitIdentIter(it, c.tab.stack[importTablePos], e.name) - while check != nil: - if check.id == e.id: + while check != nil: + if check.id == e.id: e = nil - break + break check = NextIdentIter(it, c.tab.stack[importTablePos]) - if e != nil: + if e != nil: rawImportSymbol(c, e) else: # rodgen assures that converters and patterns are no stubs if s.kind == skConverter: addConverter(c, s) if hasPattern(s): addPattern(c, s) - + proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = let ident = lookups.considerAcc(n) let s = StrTableGet(fromMod.tab, ident) @@ -98,20 +101,43 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) = rawImportSymbol(c, e) e = NextIdentIter(it, fromMod.tab) else: rawImportSymbol(c, s) - -proc importAllSymbols(c: PContext, fromMod: PSym) = + +proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: TIntSet) = var i: TTabIter var s = InitTabIter(i, fromMod.tab) - while s != nil: - if s.kind != skModule: - if s.kind != skEnumField: - if not (s.Kind in ExportableSymKinds): + while s != nil: + if s.kind != skModule: + if s.kind != skEnumField: + if s.Kind notin ExportableSymKinds: InternalError(s.info, "importAllSymbols: " & $s.kind) - rawImportSymbol(c, s) # this is correct! + if exceptSet.empty or s.name.id notin exceptSet: + rawImportSymbol(c, s) s = NextIter(i, fromMod.tab) +proc importAllSymbols*(c: PContext, fromMod: PSym) = + var exceptSet: TIntSet + importAllSymbolsExcept(c, fromMod, exceptSet) + +proc importForwarded(c: PContext, n: PNode, exceptSet: TIntSet) = + if n.isNil: return + case n.kind + of nkExportStmt: + for a in n: + assert a.kind == nkSym + let s = a.sym + if s.kind == skModule: + importAllSymbolsExcept(c, s, exceptSet) + elif exceptSet.empty or s.name.id notin exceptSet: + rawImportSymbol(c, s) + of nkExportExceptStmt: + localError(n.info, errGenerated, "'export except' not implemented") + else: + for i in 0 ..safeLen(n)-1: + importForwarded(c, n.sons[i], exceptSet) + proc evalImport(c: PContext, n: PNode): PNode = result = n + var emptySet: TIntSet for i in countup(0, sonsLen(n) - 1): var f = checkModuleName(n.sons[i]) if f != InvalidFileIDX: @@ -120,7 +146,8 @@ proc evalImport(c: PContext, n: PNode): PNode = Message(n.sons[i].info, warnDeprecated, m.name.s) # ``addDecl`` needs to be done before ``importAllSymbols``! addDecl(c, m) # add symbol to symbol table of module - importAllSymbols(c, m) + importAllSymbolsExcept(c, m, emptySet) + importForwarded(c, m.ast, emptySet) proc evalFrom(c: PContext, n: PNode): PNode = result = n @@ -131,3 +158,18 @@ proc evalFrom(c: PContext, n: PNode): PNode = n.sons[0] = newSymNode(m) addDecl(c, m) # add symbol to symbol table of module for i in countup(1, sonsLen(n) - 1): importSymbol(c, n.sons[i], m) + +proc evalImportExcept*(c: PContext, n: PNode): PNode = + result = n + checkMinSonsLen(n, 2) + var f = checkModuleName(n.sons[0]) + if f != InvalidFileIDX: + var m = gImportModule(c.module, f) + n.sons[0] = newSymNode(m) + addDecl(c, m) # add symbol to symbol table of module + var exceptSet = initIntSet() + for i in countup(1, sonsLen(n) - 1): + let ident = lookups.considerAcc(n.sons[i]) + exceptSet.incl(ident.id) + importAllSymbolsExcept(c, m, exceptSet) + importForwarded(c, m.ast, exceptSet) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 309e01c6f..0c3eea3be 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -257,6 +257,8 @@ proc captureVar(o: POuterContext, i: PInnerContext, local: PSym, # Currently captures are restricted to a single level of nesting: LocalError(info, errIllegalCaptureX, local.name.s) i.fn.typ.callConv = ccClosure + #echo "captureVar ", i.fn.name.s, i.fn.id, " ", local.name.s, local.id + incl(i.fn.typ.flags, tfCapturesEnv) # we need to remember which inner most closure belongs to this lambda: @@ -286,9 +288,10 @@ proc interestingVar(s: PSym): bool {.inline.} = sfGlobal notin s.flags proc semCaptureSym*(s, owner: PSym) = - if interestingVar(s) and owner.id != s.owner.id: + if interestingVar(s) and owner.id != s.owner.id and s.kind != skResult: if owner.typ != nil and not isGenericRoutine(owner): owner.typ.callConv = ccClosure + #echo "semCaptureSym ", owner.name.s, owner.id, " ", s.name.s, s.id # since the analysis is not entirely correct, we don't set 'tfCapturesEnv' # here diff --git a/compiler/lists.nim b/compiler/lists.nim index 1998581ce..67b32f919 100755 --- a/compiler/lists.nim +++ b/compiler/lists.nim @@ -89,9 +89,25 @@ proc Remove*(list: var TLinkedList, entry: PListEntry) = list.head = entry.next if entry.next != nil: entry.next.prev = entry.prev if entry.prev != nil: entry.prev.next = entry.next - + +proc bringToFront*(list: var TLinkedList, entry: PListEntry) = + if entry == list.head: return + if entry == list.tail: list.tail = entry.prev + if entry.next != nil: entry.next.prev = entry.prev + if entry.prev != nil: entry.prev.next = entry.next + entry.prev = nil + entry.next = list.head + list.head = entry + +proc ExcludeStr*(list: var TLinkedList, data: string) = + var it = list.head + while it != nil: + let nxt = it.next + if PStrEntry(it).data == data: remove(list, it) + it = nxt + proc Find*(list: TLinkedList, fn: TCompareProc, closure: Pointer): PListEntry = result = list.head - while result != nil: + while result != nil: if fn(result, closure): return result = result.next diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 1d1722176..8ccf24b99 100755 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -103,17 +103,24 @@ proc continueLine(line: string, inTripleString: bool): bool {.inline.} = line[0] == ' ' or line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) -proc LLreadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int = - var inTripleString = false +proc countTriples(s: string): int = + var i = 0 + while i < s.len: + if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"': + inc result + inc i, 2 + inc i + +proc LLreadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int = s.s = "" s.rd = 0 var line = newStringOfCap(120) + var triples = 0 while ReadLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line): add(s.s, line) add(s.s, "\n") - if line.contains("\"\"\""): - inTripleString = not inTripleString - if not continueLine(line, inTripleString): break + inc triples, countTriples(line) + if not continueLine(line, (triples and 1) == 1): break inc(s.lineOffset) result = min(bufLen, len(s.s) - s.rd) if result > 0: diff --git a/compiler/main.nim b/compiler/main.nim index 4767c1537..cba96a104 100755 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -302,7 +302,7 @@ when has_LLVM_Backend: compileProject() proc CommandCompileToEcmaScript = - incl(gGlobalOptions, optSafeCode) + #incl(gGlobalOptions, optSafeCode) setTarget(osEcmaScript, cpuEcmaScript) #initDefines() DefineSymbol("nimrod") # 'nimrod' is always defined @@ -316,6 +316,7 @@ proc InteractivePasses = #setTarget(osNimrodVM, cpuNimrodVM) initDefines() DefineSymbol("nimrodvm") + when hasFFI: DefineSymbol("nimffi") registerPass(verbosePass) registerPass(semPass) registerPass(evalPass) @@ -524,11 +525,11 @@ proc MainCommand = gCmd = cmdGenDepend wantMainModule() CommandGenDepend() - of "dump": + of "dump": gCmd = cmdDump condsyms.ListSymbols() - for it in iterSearchPath(): MsgWriteln(it) - of "check": + for it in iterSearchPath(searchPaths): MsgWriteln(it) + of "check": gCmd = cmdCheck wantMainModule() CommandCheck() diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 587ac4867..0f2affc36 100755 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -132,7 +132,7 @@ const errNumberOutOfRange: "number $1 out of valid range", errNnotAllowedInCharacter: "\\n not allowed in character literal", errClosingBracketExpected: "closing ']' expected, but end of file reached", - errMissingFinalQuote: "missing final \'", + errMissingFinalQuote: "missing final \' for character literal", errIdentifierExpected: "identifier expected, but found \'$1\'", errNewlineExpected: "newline expected, but found \'$1\'", errInvalidModuleName: "invalid module name: '$1'", @@ -575,7 +575,15 @@ template toFilename*(info: TLineInfo): string = template toFullPath*(info: TLineInfo): string = info.fileIndex.toFullPath - + +proc toMsgFilename*(info: TLineInfo): string = + if info.fileIndex < 0: result = "???" + else: + if gListFullPaths: + result = fileInfos[info.fileIndex].fullPath + else: + result = fileInfos[info.fileIndex].projPath + proc toLinenumber*(info: TLineInfo): int {.inline.} = result = info.line @@ -666,7 +674,7 @@ proc writeContext(lastinfo: TLineInfo) = var info = lastInfo for i in countup(0, len(msgContext) - 1): if msgContext[i] != lastInfo and msgContext[i] != info: - MsgWriteln(posContextFormat % [toFilename(msgContext[i]), + MsgWriteln(posContextFormat % [toMsgFilename(msgContext[i]), coordToStr(msgContext[i].line), coordToStr(msgContext[i].col), getMessageStr(errInstantiationFrom, "")]) @@ -720,7 +728,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, ignoreMsg = optHints notin gOptions or msg notin gNotes frmt = posHintFormat inc(gHintCounter) - let s = frmt % [toFilename(info), coordToStr(info.line), + let s = frmt % [toMsgFilename(info), coordToStr(info.line), coordToStr(info.col), getMessageStr(msg, arg)] if not ignoreMsg: MsgWriteln(s) @@ -732,6 +740,9 @@ proc Fatal*(info: TLineInfo, msg: TMsgKind, arg = "") = proc GlobalError*(info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(info, msg, arg, doRaise) +proc GlobalError*(info: TLineInfo, arg: string) = + liMessage(info, errGenerated, arg, doRaise) + proc LocalError*(info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(info, msg, arg, doNothing) diff --git a/compiler/nimrod.nim b/compiler/nimrod.nim index e192b64ab..6c999128c 100755 --- a/compiler/nimrod.nim +++ b/compiler/nimrod.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. diff --git a/compiler/options.nim b/compiler/options.nim index 2d45201dc..7350f81b7 100755 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -13,6 +13,7 @@ import const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true + hasFFI* = defined(useFFI) type # please make sure we have under 32 options # (improves code efficiency a lot!) @@ -91,7 +92,7 @@ var optPatterns} gGlobalOptions*: TGlobalOptions = {optRefcGC, optThreadAnalysis} gExitcode*: int8 - searchPaths*: TLinkedList + searchPaths*, lazyPaths*: TLinkedList outFile*: string = "" headerFile*: string = "" gCmd*: TCommands = cmdNone # the command @@ -100,7 +101,10 @@ var gWholeProject*: bool # for 'doc2': output any dependency gEvalExpr*: string # expression for idetools --eval gLastCmdTime*: float # when caas is enabled, we measure each command + gListFullPaths*: bool +proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools} + const genSubDir* = "nimcache" NimExt* = "nim" @@ -199,27 +203,53 @@ proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string = result = joinPath(subdir, tail) #echo "completeGeneratedFilePath(", f, ") = ", result -iterator iterSearchPath*(): string = +iterator iterSearchPath*(SearchPaths: TLinkedList): string = var it = PStrEntry(SearchPaths.head) - while it != nil: + while it != nil: yield it.data it = PStrEntry(it.Next) proc rawFindFile(f: string): string = - for it in iterSearchPath(): + for it in iterSearchPath(SearchPaths): result = JoinPath(it, f) - if ExistsFile(result): + if existsFile(result): return result.canonicalizePath result = "" +proc rawFindFile2(f: string): string = + var it = PStrEntry(lazyPaths.head) + while it != nil: + result = JoinPath(it.data, f) + if existsFile(result): + bringToFront(lazyPaths, it) + return result.canonicalizePath + it = PStrEntry(it.Next) + result = "" + proc FindFile*(f: string): string {.procvar.} = - result = rawFindFile(f) - if len(result) == 0: result = rawFindFile(toLower(f)) + result = f.rawFindFile + if result.len == 0: + result = f.toLower.rawFindFile + if result.len == 0: + result = f.rawFindFile2 + if result.len == 0: + result = f.toLower.rawFindFile2 proc findModule*(modulename: string): string {.inline.} = # returns path to module result = FindFile(AddFileExt(modulename, nimExt)) +proc libCandidates*(s: string, dest: var seq[string]) = + var le = strutils.find(s, '(') + var ri = strutils.find(s, ')', le+1) + if le >= 0 and ri > le: + var prefix = substr(s, 0, le - 1) + var suffix = substr(s, ri + 1) + for middle in split(substr(s, le + 1, ri - 1), '|'): + libCandidates(prefix & middle & suffix, dest) + else: + add(dest, s) + proc binaryStrSearch*(x: openarray[string], y: string): int = var a = 0 var b = len(x) - 1 diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index ee1f69818..21c7faf19 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -48,8 +48,8 @@ proc add(code: var TPatternCode, op: TOpcode) {.inline.} = add(code, chr(ord(op))) proc whichAlias*(p: PSym): TAliasRequest = - if p.typ.constraint != nil: - result = TAliasRequest(p.typ.constraint.strVal[0].ord) + if p.constraint != nil: + result = TAliasRequest(p.constraint.strVal[0].ord) proc compileConstraints(p: PNode, result: var TPatternCode) = case p.kind diff --git a/compiler/parser.nim b/compiler/parser.nim index 3634168bb..a2c7f71d2 100755 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -875,10 +875,10 @@ proc parseExprStmt(p: var TParser): PNode = getTok(p) skipComment(p, result) if p.tok.tokType == tkSad: getTok(p) - if not (p.tok.TokType in {tkOf, tkElif, tkElse, tkExcept}): + if p.tok.TokType notin {tkOf, tkElif, tkElse, tkExcept}: let body = parseStmt(p) addSon(result, newProcNode(nkDo, body.info, body)) - while true: + while true: if p.tok.tokType == tkSad: getTok(p) var b: PNode case p.tok.tokType @@ -903,30 +903,34 @@ proc parseExprStmt(p: var TParser): PNode = addSon(b, parseStmt(p)) addSon(result, b) if b.kind == nkElse: break - -proc parseImportOrIncludeStmt(p: var TParser, kind: TNodeKind): PNode = - var a: PNode + +proc parseImport(p: var TParser, kind: TNodeKind): PNode = result = newNodeP(kind, p) - getTok(p) # skip `import` or `include` + getTok(p) # skip `import` or `export` optInd(p, result) - while true: - case p.tok.tokType - of tkEof, tkSad, tkDed: - break - of tkSymbol, tkAccent: - a = parseSymbol(p) - of tkRStrLit: - a = newStrNodeP(nkRStrLit, p.tok.literal, p) - getTok(p) - of tkStrLit: - a = newStrNodeP(nkStrLit, p.tok.literal, p) - getTok(p) - of tkTripleStrLit: - a = newStrNodeP(nkTripleStrLit, p.tok.literal, p) + var a = parseExpr(p) + addSon(result, a) + if p.tok.tokType in {tkComma, tkExcept}: + if p.tok.tokType == tkExcept: + result.kind = succ(kind) + getTok(p) + optInd(p, result) + while p.tok.tokType notin {tkEof, tkSad, tkDed}: + a = parseExpr(p) + if a.kind == nkEmpty: break + addSon(result, a) + if p.tok.tokType != tkComma: break getTok(p) - else: - parMessage(p, errIdentifierExpected, p.tok) - break + optInd(p, a) + expectNl(p) + +proc parseIncludeStmt(p: var TParser): PNode = + result = newNodeP(nkIncludeStmt, p) + getTok(p) # skip `import` or `include` + optInd(p, result) + while p.tok.tokType notin {tkEof, tkSad, tkDed}: + var a = parseExpr(p) + if a.kind == nkEmpty: break addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) @@ -934,37 +938,16 @@ proc parseImportOrIncludeStmt(p: var TParser, kind: TNodeKind): PNode = expectNl(p) proc parseFromStmt(p: var TParser): PNode = - var a: PNode result = newNodeP(nkFromStmt, p) getTok(p) # skip `from` optInd(p, result) - case p.tok.tokType - of tkSymbol, tkAccent: - a = parseSymbol(p) - of tkRStrLit: - a = newStrNodeP(nkRStrLit, p.tok.literal, p) - getTok(p) - of tkStrLit: - a = newStrNodeP(nkStrLit, p.tok.literal, p) - getTok(p) - of tkTripleStrLit: - a = newStrNodeP(nkTripleStrLit, p.tok.literal, p) - getTok(p) - else: - parMessage(p, errIdentifierExpected, p.tok) - return + var a = parseExpr(p) addSon(result, a) #optInd(p, a); eat(p, tkImport) optInd(p, result) - while true: - case p.tok.tokType #optInd(p, a); - of tkEof, tkSad, tkDed: - break - of tkSymbol, tkAccent: - a = parseSymbol(p) - else: - parMessage(p, errIdentifierExpected, p.tok) - break + while p.tok.tokType notin {tkEof, tkSad, tkDed}: + a = parseExpr(p) + if a.kind == nkEmpty: break addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) @@ -1279,14 +1262,7 @@ proc parseEnum(p: var TParser): PNode = result = newNodeP(nkEnumTy, p) a = nil getTok(p) - if false and p.tok.tokType == tkOf: - a = newNodeP(nkOfInherit, p) - getTok(p) - optInd(p, a) - addSon(a, parseTypeDesc(p)) - addSon(result, a) - else: - addSon(result, ast.emptyNode) + addSon(result, ast.emptyNode) optInd(p, result) while true: case p.tok.tokType @@ -1425,18 +1401,6 @@ proc parseDistinct(p: var TParser): PNode = optInd(p, result) addSon(result, parseTypeDesc(p)) -proc parsePointerInTypeSection(p: var TParser, kind: TNodeKind): PNode = - result = newNodeP(kind, p) - getTok(p) - optInd(p, result) - if not isOperator(p.tok): - case p.tok.tokType - of tkObject: addSon(result, parseObject(p)) - of tkTuple: addSon(result, parseTuple(p, true)) - else: - if isExprStart(p): - addSon(result, parseTypeDesc(p)) - proc parseTypeDef(p: var TParser): PNode = result = newNodeP(nkTypeDef, p) addSon(result, identWithPragma(p)) @@ -1445,15 +1409,6 @@ proc parseTypeDef(p: var TParser): PNode = if p.tok.tokType == tkEquals: getTok(p) optInd(p, result) - #var a: PNode - #case p.tok.tokType - #of tkObject: a = parseObject(p) - #of tkEnum: a = parseEnum(p) - #of tkDistinct: a = parseDistinct(p) - #of tkTuple: a = parseTuple(p, true) - #of tkRef: a = parsePointerInTypeSection(p, nkRefTy) - #of tkPtr: a = parsePointerInTypeSection(p, nkPtrTy) - #else: a = parseTypeDesc(p) addSon(result, parseTypeDefAux(p)) else: addSon(result, ast.emptyNode) @@ -1511,11 +1466,12 @@ proc simpleStmt(p: var TParser): PNode = of tkBreak: result = parseBreakOrContinue(p, nkBreakStmt) of tkContinue: result = parseBreakOrContinue(p, nkContinueStmt) of tkCurlyDotLe: result = parseStmtPragma(p) - of tkImport: result = parseImportOrIncludeStmt(p, nkImportStmt) + of tkImport: result = parseImport(p, nkImportStmt) + of tkExport: result = parseImport(p, nkExportStmt) of tkFrom: result = parseFromStmt(p) - of tkInclude: result = parseImportOrIncludeStmt(p, nkIncludeStmt) + of tkInclude: result = parseIncludeStmt(p) of tkComment: result = newCommentStmt(p) - else: + else: if isExprStart(p): result = parseExprStmt(p) else: result = ast.emptyNode if result.kind != nkEmpty: skipComment(p, result) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index af259c916..ff7f18ac0 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -71,8 +71,8 @@ proc inSymChoice(sc, x: PNode): bool = proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool = # check param constraints first here as this is quite optimized: - if p.typ.constraint != nil: - result = matchNodeKinds(p.typ.constraint, n) + if p.constraint != nil: + result = matchNodeKinds(p.constraint, n) if not result: return if isNil(n.typ): result = p.typ.kind in {tyEmpty, tyStmt} @@ -237,6 +237,15 @@ proc addToArgList(result, n: PNode) = else: for i in 0 .. <n.len: result.add(n.sons[i]) +when false: + proc procPatternMatches*(c: PContext, s: PSym, n: PNode): bool = + ## for AST-based overloading: + var ctx: TPatternContext + ctx.owner = s + ctx.c = c + ctx.formals = sonsLen(s.typ)-1 + result = matches(ctx, s.ast.sons[patternPos], n) + proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = ## returns a tree to semcheck if the rule triggered; nil otherwise var ctx: TPatternContext diff --git a/compiler/procfind.nim b/compiler/procfind.nim index fde4d22ea..4a1fb5ac8 100755 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -29,7 +29,7 @@ proc equalGenericParams(procA, procB: PNode): bool = a = procA.sons[i].sym b = procB.sons[i].sym if (a.name.id != b.name.id) or - not sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): return + not sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): return if (a.ast != nil) and (b.ast != nil): if not ExprStructuralEquivalent(a.ast, b.ast): return result = true @@ -44,7 +44,7 @@ proc SearchForProc*(c: PContext, fn: PSym, tos: int): PSym = if equalGenericParams(result.ast.sons[genericParamsPos], fn.ast.sons[genericParamsPos]): case equalParams(result.typ.n, fn.typ.n) - of paramsEqual: + of paramsEqual: return of paramsIncompatible: LocalError(fn.info, errNotOverloadable, fn.name.s) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index b9d522694..48a190ec1 100755 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -753,8 +753,7 @@ proc doParamsAux(g: var TSrcGen, params: PNode) = proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if isNil(n): return - var - L: int + var a: TContext if n.comment != nil: pushCom(g, n) case n.kind # atoms: @@ -1096,7 +1095,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = incl(a.flags, rfInConstExpr) gsection(g, n, a, tkConst, "const") of nkVarSection, nkLetSection: - L = sonsLen(n) + var L = sonsLen(n) if L == 0: return if n.kind == nkVarSection: putWithSpace(g, tkVar, "var") else: putWithSpace(g, tkLet, "let") @@ -1134,13 +1133,27 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkCurlyDotLe, "{.") gcomma(g, n, emptyContext) put(g, tkCurlyDotRi, ".}") - of nkImportStmt: - putWithSpace(g, tkImport, "import") + of nkImportStmt, nkExportStmt: + if n.kind == nkImportStmt: + putWithSpace(g, tkImport, "import") + else: + putWithSpace(g, tkExport, "export") gcoms(g) indentNL(g) gcommaAux(g, n, g.indent) dedent(g) putNL(g) + of nkImportExceptStmt, nkExportExceptStmt: + if n.kind == nkImportExceptStmt: + putWithSpace(g, tkImport, "import") + else: + putWithSpace(g, tkExport, "export") + gsub(g, n.sons[0]) + put(g, tkSpaces, Space) + putWithSpace(g, tkExcept, "except") + gcommaAux(g, n, g.indent, 1) + gcoms(g) + putNL(g) of nkFromStmt: putWithSpace(g, tkFrom, "from") gsub(g, n.sons[0]) diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 2d6399438..5dccee9a7 100755 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -330,9 +330,6 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType = if r.s[r.pos] == '@': inc(r.pos) result.containerID = decodeVInt(r.s, r.pos) - if r.s[r.pos] == '`': - inc(r.pos) - result.constraint = decodeNode(r, UnknownLineInfo()) decodeLoc(r, result.loc, info) while r.s[r.pos] == '^': inc(r.pos) @@ -423,6 +420,9 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym = result.offset = - 1 decodeLoc(r, result.loc, result.info) result.annex = decodeLib(r, info) + if r.s[r.pos] == '#': + inc(r.pos) + result.constraint = decodeNode(r, UnknownLineInfo()) if r.s[r.pos] == '(': if result.kind in routineKinds: result.ast = decodeNodeLazyBody(r, result.info, result) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 691d20553..c0a0cc4eb 100755 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -233,9 +233,6 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = if t.containerID != 0: add(result, '@') encodeVInt(t.containerID, result) - if t.constraint != nil: - add(result, '`') - encodeNode(w, UnknownLineInfo(), t.constraint, result) encodeLoc(w, t.loc, result) for i in countup(0, sonsLen(t) - 1): if t.sons[i] == nil: @@ -295,6 +292,9 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = 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: diff --git a/compiler/sem.nim b/compiler/sem.nim index 9844d71b0..555f5e7b7 100755 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -38,13 +38,15 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType proc semStmt(c: PContext, n: PNode): PNode proc semParamList(c: PContext, n, genericParams: PNode, s: PSym) proc addParams(c: PContext, n: PNode, kind: TSymKind) -proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) -proc addResultNode(c: PContext, n: PNode) +proc maybeAddResult(c: PContext, s: PSym, n: PNode) proc instGenericContainer(c: PContext, n: PNode, header: PType): PType proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc fixImmediateParams(n: PNode): PNode proc activate(c: PContext, n: PNode) proc semQuoteAst(c: PContext, n: PNode): PNode +proc finishMethod(c: PContext, s: PSym) + +proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode proc typeMismatch(n: PNode, formal, actual: PType) = if formal.kind != tyError and actual.kind != tyError: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index a5107bf64..67d157261 100755 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -15,8 +15,22 @@ proc sameMethodDispatcher(a, b: PSym): bool = if a.kind == skMethod and b.kind == skMethod: var aa = lastSon(a.ast) var bb = lastSon(b.ast) - if aa.kind == nkSym and bb.kind == nkSym and aa.sym == bb.sym: - result = true + if aa.kind == nkSym and bb.kind == nkSym: + if aa.sym == bb.sym: + result = true + else: + nil + # generics have no dispatcher yet, so we need to compare the method + # names; however, the names are equal anyway because otherwise we + # wouldn't even consider them to be overloaded. But even this does + # not work reliably! See tmultim6 for an example: + # method collide[T](a: TThing, b: TUnit[T]) is instantiated and not + # method collide[T](a: TUnit[T], b: TThing)! This means we need to + # *instantiate* every candidate! However, we don't keep more than 2-3 + # candidated around so we cannot implement that for now. So in order + # to avoid subtle problems, the call remains ambiguous and needs to + # be disambiguated by the programmer; this way the right generic is + # instantiated. proc resolveOverloads(c: PContext, n, orig: PNode, filter: TSymKinds): TCandidate = @@ -84,6 +98,35 @@ proc resolveOverloads(c: PContext, n, orig: PNode, getProcHeader(best.calleeSym), getProcHeader(alt.calleeSym), args]) + +proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = + if a.kind == nkHiddenCallConv and a.sons[0].kind == nkSym and + isGenericRoutine(a.sons[0].sym): + let finalCallee = generateInstance(c, a.sons[0].sym, x.bindings, a.info) + a.sons[0].sym = finalCallee + a.sons[0].typ = finalCallee.typ + #a.typ = finalCallee.typ.sons[0] + +proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) = + assert n.kind in nkCallKinds + if x.genericConverter: + for i in 1 .. <n.len: + instGenericConvertersArg(c, n.sons[i], x) + +proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = + var m: TCandidate + initCandidate(m, f) + result = paramTypesMatch(c, m, f, a, arg, nil) + if m.genericConverter and result != nil: + instGenericConvertersArg(c, result, m) + +proc ConvertTo*(c: PContext, f: PType, n: PNode): PNode = + var m: TCandidate + initCandidate(m, f) + result = paramTypesMatch(c, m, f, n.typ, n, nil) + if m.genericConverter and result != nil: + instGenericConvertersArg(c, result, m) + proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = assert x.state == csMatch var finalCallee = x.calleeSym @@ -101,6 +144,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = if ContainsGenericType(result.typ): result.typ = errorType(c) return result = x.call + instGenericConvertersSons(c, result, x) result.sons[0] = newSymNode(finalCallee, result.sons[0].info) result.typ = finalCallee.typ.sons[0] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e6123b1bc..48fe5b4d7 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -318,7 +318,7 @@ proc semIs(c: PContext, n: PNode): PNode = if not containsGenericType(t1): result = evalIsOp(n) proc semOpAux(c: PContext, n: PNode) = - let flags = {efDetermineType} + const flags = {efDetermineType} for i in countup(1, n.sonsLen- 1): var a = n.sons[i] if a.kind == nkExprEqExpr and sonsLen(a) == 2: @@ -589,11 +589,12 @@ proc semStaticExpr(c: PContext, n: PNode): PNode = proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = - if efWantIterator in flags: - result = semOverloadedCall(c, n, nOrig, {skIterator}) - elif efInTypeOf in flags: + if flags*{efInTypeOf, efWantIterator} != {}: + # consider: 'for x in pReturningArray()' --> we don't want the restriction + # to 'skIterator' anymore; skIterator is preferred in sigmatch already for + # typeof support. # for ``type(countup(1,3))``, see ``tests/ttoseq``. - result = semOverloadedCall(c, n, nOrig, + result = semOverloadedCall(c, n, nOrig, {skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}) else: result = semOverloadedCall(c, n, nOrig, @@ -663,6 +664,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil else: result = m.call + instGenericConvertersSons(c, result, m) # we assume that a procedure that calls something indirectly # has side-effects: if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect) @@ -1658,6 +1660,26 @@ proc fixImmediateParams(n: PNode): PNode = result = n +proc semExport(c: PContext, n: PNode): PNode = + var x = newNodeI(n.kind, n.info) + #let L = if n.kind == nkExportExceptStmt: L = 1 else: n.len + for i in 0.. <n.len: + let a = n.sons[i] + var o: TOverloadIter + var s = initOverloadIter(o, c, a) + if s == nil: + localError(a.info, errGenerated, "invalid expr for 'export': " & + renderTree(a)) + while s != nil: + if s.kind in ExportableSymKinds+{skModule}: + x.add(newSymNode(s, a.info)) + s = nextOverloadIter(o, c, a) + if c.module.ast.isNil: + c.module.ast = newNodeI(nkStmtList, n.info) + assert c.module.ast.kind == nkStmtList + c.module.ast.add x + result = n + proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = n if gCmd == cmdIdeTools: suggestExpr(c, n) @@ -1851,12 +1873,18 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkImportStmt: if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "import") result = evalImport(c, n) + of nkImportExceptStmt: + if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "import") + result = evalImportExcept(c, n) of nkFromStmt: if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "from") result = evalFrom(c, n) of nkIncludeStmt: if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "include") result = evalInclude(c, n) + of nkExportStmt, nkExportExceptStmt: + if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "export") + result = semExport(c, n) of nkPragmaBlock: result = semPragmaBlock(c, n) of nkStaticStmt: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 5a818103d..95a394a09 100755 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -89,9 +89,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) = # add it here, so that recursive generic procs are possible: addDecl(c, result) pushProcCon(c, result) - if result.kind in {skProc, skMethod, skConverter, skMacro}: - addResult(c, result.typ.sons[0], n.info, result.kind) - addResultNode(c, n) + maybeAddResult(c, result, n) var b = n.sons[bodyPos] var symMap: TIdTable InitIdTable symMap @@ -163,6 +161,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, result.typ = newTypeS(tyProc, c) rawAddSon(result.typ, nil) result.typ.callConv = fn.typ.callConv + if result.kind == skIterator: result.typ.flags.incl(tfIterator) var oldPrc = GenericCacheGet(fn, entry[]) if oldPrc == nil: fn.procInstCache.safeAdd(entry) @@ -182,6 +181,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, popOwner() c.friendModule = oldFriend dec(c.InstCounter) + if result.kind == skMethod: finishMethod(c, result) proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = var cl: TReplTypeVars diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index bd8a3ba02..b2fd0fb04 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -146,8 +146,11 @@ proc catches(tracked: PEffects, e: PType) = dec L else: inc i - setLen(tracked.exc.sons, L) - + if not isNil(tracked.exc.sons): + setLen(tracked.exc.sons, L) + else: + assert L == 0 + proc catchesAll(tracked: PEffects) = if not isNil(tracked.exc.sons): setLen(tracked.exc.sons, tracked.bottom) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f8860212b..c38e2f3ad 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -172,11 +172,16 @@ proc fitRemoveHiddenConv(c: PContext, typ: Ptype, n: PNode): PNode = changeType(result, typ) proc findShadowedVar(c: PContext, v: PSym): PSym = - for i in countdown(c.tab.tos - 2, 0): + for i in countdown(c.tab.tos - 2, ModuleTablePos+1): let shadowed = StrTableGet(c.tab.stack[i], v.name) if shadowed != nil and shadowed.kind in skLocalVars: return shadowed +proc identWithin(n: PNode, s: PIdent): bool = + for i in 0 .. n.safeLen-1: + if identWithin(n.sons[i], s): return true + result = n.kind == nkSym and n.sym.name.id == s.id + proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = if isTopLevel(c): result = semIdentWithPragma(c, kind, n, {sfExported}) @@ -239,7 +244,10 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = let shadowed = findShadowedVar(c, v) if shadowed != nil: shadowed.flags.incl(sfShadowed) - Message(a.info, warnShadowIdent, v.name.s) + # a shadowed variable is an error unless it appears on the right + # side of the '=': + if warnShadowIdent in gNotes and not identWithin(def, v.name): + Message(a.info, warnShadowIdent, v.name.s) if def != nil and def.kind != nkEmpty: # this is only needed for the evaluation pass: v.ast = def @@ -247,7 +255,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if a.kind != nkVarTuple: v.typ = typ b = newNodeI(nkIdentDefs, a.info) - if gCmd == cmdDoc: + if importantComments(): # keep documentation information: b.comment = a.comment addSon(b, newSymNode(v)) @@ -287,7 +295,7 @@ proc semConst(c: PContext, n: PNode): PNode = v.ast = def # no need to copy if sfGenSym notin v.flags: addInterfaceDecl(c, v) var b = newNodeI(nkConstDef, a.info) - if gCmd == cmdDoc: b.comment = a.comment + if importantComments(): b.comment = a.comment addSon(b, newSymNode(v)) addSon(b, ast.emptyNode) # no type description addSon(b, copyTree(def)) @@ -368,6 +376,15 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = b.add(ast.emptyNode) stmts.add(b) +proc addForVarDecl(c: PContext, v: PSym) = + if warnShadowIdent in gNotes: + let shadowed = findShadowedVar(c, v) + if shadowed != nil: + # XXX should we do this here? + #shadowed.flags.incl(sfShadowed) + Message(v.info, warnShadowIdent, v.name.s) + addDecl(c, v) + proc semForVars(c: PContext, n: PNode): PNode = result = n var length = sonsLen(n) @@ -383,7 +400,7 @@ proc semForVars(c: PContext, n: PNode): PNode = # for an example: v.typ = n.sons[length-2].typ n.sons[0] = newSymNode(v) - if sfGenSym notin v.flags: addDecl(c, v) + if sfGenSym notin v.flags: addForVarDecl(c, v) else: LocalError(n.info, errWrongNumberOfVariables) elif length-2 != sonsLen(iter): @@ -394,7 +411,7 @@ proc semForVars(c: PContext, n: PNode): PNode = if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) v.typ = iter.sons[i] n.sons[i] = newSymNode(v) - if sfGenSym notin v.flags: addDecl(c, v) + if sfGenSym notin v.flags: addForVarDecl(c, v) Inc(c.p.nestedLoopCounter) n.sons[length-1] = SemStmt(c, n.sons[length-1]) Dec(c.p.nestedLoopCounter) @@ -669,17 +686,18 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = if n.sons[pragmasPos].kind != nkEmpty: pragma(c, s, n.sons[pragmasPos], lambdaPragmas) s.options = gOptions - if n.sons[bodyPos].kind != nkEmpty: - if sfImportc in s.flags: + if n.sons[bodyPos].kind != nkEmpty: + if sfImportc in s.flags: LocalError(n.sons[bodyPos].info, errImplOfXNotAllowed, s.name.s) - if efDetermineType notin flags: - 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) + #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) + sideEffectsCheck(c, s) else: LocalError(n.info, errImplOfXexpected, s.name.s) closeScope(c.tab) # close scope for parameters @@ -689,13 +707,16 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = proc activate(c: PContext, n: PNode) = # XXX: This proc is part of my plan for getting rid of # forward declarations. stay tuned. - case n.kind - of nkLambdaKinds: - discard semLambda(c, n, {}) - of nkCallKinds: - for i in 1 .. <n.len: activate(c, n[i]) - else: - nil + when false: + # well for now it breaks code ... I added the test case in main.nim of the + # compiler itself to break bootstrapping :P + case n.kind + of nkLambdaKinds: + discard semLambda(c, n, {}) + of nkCallKinds: + for i in 1 .. <n.len: activate(c, n[i]) + else: + nil proc instantiateDestructor*(c: PContext, typ: PType): bool @@ -713,6 +734,12 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = useSym(t.sons[i].destructor), n.sons[paramsPos][1][0]])) +proc maybeAddResult(c: PContext, s: PSym, n: PNode) = + if s.typ.sons[0] != nil and + (s.kind != skIterator or s.typ.callConv == ccClosure): + addResult(c, s.typ.sons[0], n.info, s.kind) + addResultNode(c, n) + proc semProcAux(c: PContext, n: PNode, kind: TSymKind, validPragmas: TSpecialWords): PNode = result = semProcAnnotation(c, n) @@ -780,6 +807,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, n.sons[pragmasPos] = proto.ast.sons[pragmasPos] if n.sons[namePos].kind != nkSym: InternalError(n.info, "semProcAux") n.sons[namePos].sym = proto + if importantComments() and not isNil(proto.ast.comment): + n.comment = proto.ast.comment proto.ast = n # needed for code generation popOwner() pushOwner(s) @@ -792,17 +821,13 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if n.sons[genericParamsPos].kind == nkEmpty: ParamsTypeCheck(c, s.typ) pushProcCon(c, s) - if s.typ.sons[0] != nil and - (kind != skIterator or s.typ.callConv == ccClosure): - addResult(c, s.typ.sons[0], n.info, kind) - addResultNode(c, n) + maybeAddResult(c, s, n) if sfImportc notin s.flags: # no semantic checking for importc: let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) # unfortunately we cannot skip this step when in 'system.compiles' # context as it may even be evaluated in 'system.compiles': n.sons[bodyPos] = transformBody(c.module, semBody, s) - #if s.typ.sons[0] != nil and kind != skIterator: addResultNode(c, n) popProcCon(c) else: if s.typ.sons[0] != nil and kind != skIterator: @@ -846,31 +871,30 @@ proc semIterator(c: PContext, n: PNode): PNode = proc semProc(c: PContext, n: PNode): PNode = result = semProcAux(c, n, skProc, procPragmas) +proc hasObjParam(s: PSym): bool = + var t = s.typ + for col in countup(1, sonsLen(t)-1): + if skipTypes(t.sons[col], skipPtrs).kind == tyObject: + return true + +proc finishMethod(c: PContext, s: PSym) = + if hasObjParam(s): + methodDef(s, false) + proc semMethod(c: PContext, n: PNode): PNode = if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "method") result = semProcAux(c, n, skMethod, methodPragmas) var s = result.sons[namePos].sym - var t = s.typ - var hasObjParam = false - - for col in countup(1, sonsLen(t)-1): - if skipTypes(t.sons[col], skipPtrs).kind == tyObject: - hasObjParam = true - break - - # XXX this not really correct way to do it: Perhaps it should be done after - # generic instantiation. Well it's good enough for now: - if hasObjParam: - methodDef(s, false) - else: - LocalError(n.info, errXNeedsParamObjectType, "method") + if not isGenericRoutine(s): + if hasObjParam(s): + methodDef(s, false) + else: + LocalError(n.info, errXNeedsParamObjectType, "method") proc semConverterDef(c: PContext, n: PNode): PNode = if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "converter") checkSonsLen(n, bodyPos + 1) - if n.sons[genericParamsPos].kind != nkEmpty: - LocalError(n.info, errNoGenericParamsAllowedForX, "converter") result = semProcAux(c, n, skConverter, converterPragmas) var s = result.sons[namePos].sym var t = s.typ @@ -1075,7 +1099,8 @@ proc insertDestructors(c: PContext, varSection: PNode): varTyp = varId.sym.typ info = varId.info - if varTyp != nil and instantiateDestructor(c, varTyp): + if varTyp != nil and instantiateDestructor(c, varTyp) and + sfGlobal notin varId.sym.flags: var tryStmt = newNodeI(nkTryStmt, info) if j < totalVars - 1: diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim index 75621be79..9fc6a54d9 100755 --- a/compiler/semthreads.nim +++ b/compiler/semthreads.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f644683f5..3ad275601 100755 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -635,6 +635,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, genericParams.addSon(newSymNode(s)) result = typeClass +proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = + if n.kind == nkCurlyExpr: + result = semTypeNode(c, n.sons[0], nil) + constraint = semNodeKindConstraints(n) + else: + result = semTypeNode(c, n, nil) + proc semProcTypeNode(c: PContext, n, genericParams: PNode, prev: PType, kind: TSymKind): PType = var @@ -660,13 +667,14 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, checkMinSonsLen(a, 3) var typ: PType = nil - def: PNode = nil + def: PNode = nil + constraint: PNode = nil length = sonsLen(a) hasType = a.sons[length-2].kind != nkEmpty hasDefault = a.sons[length-1].kind != nkEmpty if hasType: - typ = semTypeNode(c, a.sons[length-2], nil) + typ = semParamType(c, a.sons[length-2], constraint) if hasDefault: def = semExprWithType(c, a.sons[length-1]) @@ -689,6 +697,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, arg.name.s, arg.info).skipIntLit arg.typ = finalType arg.position = counter + arg.constraint = constraint inc(counter) if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) if ContainsOrIncl(check, arg.name.id): @@ -787,6 +796,12 @@ proc semTypeExpr(c: PContext, n: PNode): PType = else: LocalError(n.info, errTypeExpected, n.renderTree) +proc freshType(res, prev: PType): PType {.inline.} = + if prev.isNil: + result = copyType(res, res.owner, keepId=false) + else: + result = res + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil if gCmd == cmdIdeTools: suggestExpr(c, n) @@ -825,6 +840,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = checkSonsLen(n, 3) result = semTypeNode(c, n.sons[1], prev) if result.kind in NilableTypes and n.sons[2].kind == nkNilLit: + result = freshType(result, prev) result.flags.incl(tfNotNil) else: LocalError(n.info, errGenerated, "invalid type") @@ -832,11 +848,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = semTypeExpr(c, n) else: result = semTypeExpr(c, n) - of nkCurlyExpr: - result = semTypeNode(c, n.sons[0], nil) - if result != nil: - result = copyType(result, getCurrOwner(), true) - result.constraint = semNodeKindConstraints(n) of nkWhenStmt: var whenResult = semWhen(c, n, false) if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType @@ -919,6 +930,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkSharedTy: checkSonsLen(n, 1) result = semTypeNode(c, n.sons[0], prev) + result = freshType(result, prev) result.flags.incl(tfShared) else: LocalError(n.info, errTypeExpected) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 799622355..953dcfa74 100755 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -12,18 +12,19 @@ import intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, - magicsys, condsyms, idents, lexer, options + magicsys, condsyms, idents, lexer, options, parampatterns, strutils, + docgen type TCandidateState* = enum csEmpty, csMatch, csNoMatch TCandidate* {.final.} = object - exactMatches*: int + exactMatches*: int # also misused to prefer iters over procs + genericMatches: int # also misused to prefer constraints subtypeMatches: int intConvMatches: int # conversions to int are not as expensive convMatches: int - genericMatches: int state*: TCandidateState callee*: PType # may not be nil! calleeSym*: PSym # may be nil @@ -33,6 +34,8 @@ type baseTypeMatch: bool # needed for conversions from T to openarray[T] # for example proxyMatch*: bool # to prevent instantiations + genericConverter*: bool # true if a generic converter needs to + # be instantiated inheritancePenalty: int # to prefer closest father object type TTypeRelation* = enum # order is important! @@ -57,6 +60,7 @@ proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} = c.callee = callee c.call = nil c.baseTypeMatch = false + c.genericConverter = false c.inheritancePenalty = 0 proc initCandidate*(c: var TCandidate, callee: PType) = @@ -571,17 +575,27 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, for i in countup(0, len(c.converters) - 1): var src = c.converters[i].typ.sons[1] var dest = c.converters[i].typ.sons[0] - if (typeRel(m, f, dest) == isEqual) and - (typeRel(m, src, a) == isEqual): + # for generic type converters we need to check 'src <- a' before + # 'f <- dest' in order to not break the unification: + # see tests/tgenericconverter: + let srca = typeRel(m, src, a) + if srca notin {isEqual, isGeneric}: continue + + let destIsGeneric = containsGenericType(dest) + if destIsGeneric: + dest = generateTypeInstance(c, m.bindings, arg, dest) + let fdest = typeRel(m, f, dest) + if fdest in {isEqual, isGeneric}: markUsed(arg, c.converters[i]) var s = newSymNode(c.converters[i]) s.typ = c.converters[i].typ s.info = arg.info - result = newNodeIT(nkHiddenCallConv, arg.info, s.typ.sons[0]) + result = newNodeIT(nkHiddenCallConv, arg.info, dest) addSon(result, s) addSon(result, copyTree(arg)) inc(m.convMatches) - return + m.genericConverter = srca == isGeneric or destIsGeneric + return result proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, arg: PNode): PNode = @@ -692,8 +706,8 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, else: result = userConvMatch(c, m, base(f), a, arg) -proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType, - arg, argOrig: PNode): PNode = +proc ParamTypesMatch*(c: PContext, m: var TCandidate, f, a: PType, + arg, argOrig: PNode): PNode = if arg == nil or arg.kind notin nkSymChoices: result = ParamTypesMatchAux(c, m, f, a, arg, argOrig) else: @@ -739,27 +753,21 @@ proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType, result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best], argOrig) -proc IndexTypesMatch*(c: PContext, f, a: PType, arg: PNode): PNode = - var m: TCandidate - initCandidate(m, f) - result = paramTypesMatch(c, m, f, a, arg, nil) - -proc ConvertTo*(c: PContext, f: PType, n: PNode): PNode = - var m: TCandidate - initCandidate(m, f) - result = paramTypesMatch(c, m, f, n.typ, n, nil) - -proc argtypeMatches*(c: PContext, f, a: PType): bool = - var m: TCandidate - initCandidate(m, f) - result = paramTypesMatch(c, m, f, a, ast.emptyNode, nil) != nil - proc setSon(father: PNode, at: int, son: PNode) = if sonsLen(father) <= at: setlen(father.sons, at + 1) father.sons[at] = son -proc matchesAux*(c: PContext, n, nOrig: PNode, - m: var TCandidate, marker: var TIntSet) = +proc matchesAux(c: PContext, n, nOrig: PNode, + m: var TCandidate, marker: var TIntSet) = + template checkConstraint(n: expr) {.immediate, dirty.} = + if not formal.constraint.isNil: + if matchNodeKinds(formal.constraint, n): + # better match over other routines with no such restriction: + inc(m.genericMatches, 100) + else: + m.state = csNoMatch + return + var f = 1 # iterates over formal parameters var a = 1 # iterates over the actual given arguments m.state = csMatch # until proven otherwise @@ -792,7 +800,8 @@ proc matchesAux*(c: PContext, n, nOrig: PNode, n.sons[a].sons[1], nOrig.sons[a].sons[1]) if arg == nil: m.state = csNoMatch - return + return + checkConstraint(n.sons[a].sons[1]) if m.baseTypeMatch: assert(container == nil) container = newNodeI(nkBracket, n.sons[a].info) @@ -837,10 +846,10 @@ proc matchesAux*(c: PContext, n, nOrig: PNode, m.baseTypeMatch = false var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a], nOrig.sons[a]) - if arg == nil: + if arg == nil: m.state = csNoMatch - return - if m.baseTypeMatch: + return + if m.baseTypeMatch: assert(container == nil) container = newNodeI(nkBracket, n.sons[a].info) addSon(container, arg) @@ -849,6 +858,7 @@ proc matchesAux*(c: PContext, n, nOrig: PNode, if f != formalLen - 1: container = nil else: setSon(m.call, formal.position + 1, arg) + checkConstraint(n.sons[a]) inc(a) inc(f) @@ -880,4 +890,13 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = setSon(m.call, formal.position + 1, copyTree(formal.ast)) inc(f) +proc argtypeMatches*(c: PContext, f, a: PType): bool = + var m: TCandidate + initCandidate(m, f) + let res = paramTypesMatch(c, m, f, a, ast.emptyNode, nil) + #instantiateGenericConverters(c, res, m) + # XXX this is used by patterns.nim too; I think it's better to not + # instantiate generic converters for that + result = res != nil + include suggest diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 404f1c1bb..130666f4d 100755 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -39,6 +39,8 @@ proc SymToStr(s: PSym, isLocal: bool, section: string, li: TLineInfo): string = result.add($ToLinenumber(li)) result.add(sep) result.add($ToColumn(li)) + result.add(sep) + result.add(s.extractDocComment.escape) proc SymToStr(s: PSym, isLocal: bool, section: string): string = result = SymToStr(s, isLocal, section, s.info) diff --git a/compiler/transf.nim b/compiler/transf.nim index 5ba997524..679f7d12f 100755 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -150,7 +150,7 @@ proc transformVarSection(c: PTransf, v: PNode): PTransNode = newVar.owner = getCurrOwner(c) IdNodeTablePut(c.transCon.mapping, it.sons[0].sym, newSymNode(newVar)) var defs = newTransNode(nkIdentDefs, it.info, 3) - if gCmd == cmdDoc: + if importantComments(): # keep documentation information: pnode(defs).comment = it.comment defs[0] = newSymNode(newVar).PTransNode @@ -665,7 +665,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = of nkIdentDefs, nkConstDef: result = transformSons(c, n) # XXX comment handling really sucks: - if gCmd == cmdDoc: + if importantComments(): pnode(result).comment = n.comment else: result = transformSons(c, n) diff --git a/compiler/types.nim b/compiler/types.nim index 825b1027a..998ba43d2 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2013 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -632,7 +632,8 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool = result = SameTypeAux(a, b, c) proc equalParam(a, b: PSym): TParamsEquality = - if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): + if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}) and + ExprStructuralEquivalent(a.constraint, b.constraint): if a.ast == b.ast: result = paramsEqual elif a.ast != nil and b.ast != nil: @@ -875,20 +876,24 @@ proc inheritanceDiff*(a, b: PType): int = # | returns: -x iff `a` is the x'th direct superclass of `b` # | returns: +x iff `a` is the x'th direct subclass of `b` # | returns: `maxint` iff `a` and `b` are not compatible at all + assert a.kind == tyObject + assert b.kind == tyObject var x = a result = 0 while x != nil: + x = skipTypes(x, skipPtrs) if sameObjectTypes(x, b): return x = x.sons[0] dec(result) var y = b result = 0 while y != nil: + y = skipTypes(y, skipPtrs) if sameObjectTypes(y, a): return y = y.sons[0] inc(result) result = high(int) - + proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool = result = true |