diff options
39 files changed, 777 insertions, 333 deletions
diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 04f62d66f..7fef34a27 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1366,7 +1366,7 @@ proc updateCachedModule(m: BModule) = cf.flags = {CfileFlag.Cached} addFileToCompile(cf) -proc myClose(b: PPassContext, n: PNode): PNode = +proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = result = n if b == nil or passes.skipCodegen(n): return var m = BModule(b) @@ -1378,7 +1378,7 @@ proc myClose(b: PPassContext, n: PNode): PNode = if sfMainModule in m.module.flags: incl m.flags, objHasKidsValid - var disp = generateMethodDispatchers() + var disp = generateMethodDispatchers(graph) for i in 0..sonsLen(disp)-1: genProcAux(m, disp.sons[i].sym) genMainProc(m) diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index eda80be30..3a945ae0a 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -11,7 +11,7 @@ import intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys, - sempass2, strutils + sempass2, strutils, modulegraphs proc genConv(n: PNode, d: PType, downcast: bool): PNode = var dest = skipTypes(d, abstractPtrs) @@ -35,19 +35,24 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode = else: result = n +proc getDispatcher*(s: PSym): PSym = + ## can return nil if is has no dispatcher. + let dispn = lastSon(s.ast) + if dispn.kind == nkSym: + let disp = dispn.sym + if sfDispatcher in disp.flags: result = disp + proc methodCall*(n: PNode): PNode = result = n # replace ordinary method by dispatcher method: - var disp = lastSon(result.sons[0].sym.ast).sym - assert sfDispatcher in disp.flags - result.sons[0].sym = disp - # change the arguments to up/downcasts to fit the dispatcher's parameters: - for i in countup(1, sonsLen(result)-1): - result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true) - -# save for incremental compilation: -var - gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[] + let disp = getDispatcher(result.sons[0].sym) + if disp != nil: + result.sons[0].sym = disp + # change the arguments to up/downcasts to fit the dispatcher's parameters: + for i in countup(1, sonsLen(result)-1): + result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true) + else: + localError(n.info, "'" & $result.sons[0] & "' lacks a dispatcher") type MethodResult = enum No, Invalid, Yes @@ -144,27 +149,27 @@ proc fixupDispatcher(meth, disp: PSym) = if disp.typ.lockLevel < meth.typ.lockLevel: disp.typ.lockLevel = meth.typ.lockLevel -proc methodDef*(s: PSym, fromCache: bool) = - let L = len(gMethods) +proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) = + let L = len(g.methods) var witness: PSym for i in countup(0, L - 1): - let disp = gMethods[i].dispatcher + let disp = g.methods[i].dispatcher case sameMethodBucket(disp, s) of Yes: - add(gMethods[i].methods, s) + add(g.methods[i].methods, s) attachDispatcher(s, lastSon(disp.ast)) fixupDispatcher(s, disp) #echo "fixup ", disp.name.s, " ", disp.id when useEffectSystem: checkMethodEffects(disp, s) - if sfBase in s.flags and gMethods[i].methods[0] != s: + if sfBase in s.flags and g.methods[i].methods[0] != s: # already exists due to forwarding definition? localError(s.info, "method is not a base") return of No: discard of Invalid: - if witness.isNil: witness = gMethods[i].methods[0] + if witness.isNil: witness = g.methods[i].methods[0] # create a new dispatcher: - add(gMethods, (methods: @[s], dispatcher: createDispatcher(s))) + add(g.methods, (methods: @[s], dispatcher: createDispatcher(s))) #echo "adding ", s.info #if fromCache: # internalError(s.info, "no method dispatcher found") @@ -258,12 +263,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym = disp = ret result.ast.sons[bodyPos] = disp -proc generateMethodDispatchers*(): PNode = +proc generateMethodDispatchers*(g: ModuleGraph): PNode = result = newNode(nkStmtList) - for bucket in countup(0, len(gMethods) - 1): + for bucket in countup(0, len(g.methods) - 1): var relevantCols = initIntSet() - for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1): - if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col) - sortBucket(gMethods[bucket].methods, relevantCols) + for col in countup(1, sonsLen(g.methods[bucket].methods[0].typ) - 1): + if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col) + sortBucket(g.methods[bucket].methods, relevantCols) addSon(result, - newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols))) + newSymNode(genDispatcher(g.methods[bucket].methods, relevantCols))) diff --git a/compiler/compilerlog.nim b/compiler/compilerlog.nim new file mode 100644 index 000000000..fbf8cd9aa --- /dev/null +++ b/compiler/compilerlog.nim @@ -0,0 +1,9 @@ + +from os import getHomeDir, `/` + +proc logStr*(line: string) = + var f: File + if open(f, getHomeDir() / "nimsuggest.log", fmAppend): + f.writeLine(line) + f.close() + diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 9504ab52f..118f1c7c5 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -33,11 +33,11 @@ template closeImpl(body: untyped) {.dirty.} = except IOError: discard -proc close(p: PPassContext, n: PNode): PNode = +proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: writeOutput(g.doc, g.module.filename, HtmlExt, useWarning) -proc closeJson(p: PPassContext, n: PNode): PNode = +proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: writeOutputJson(g.doc, g.module.filename, ".json", useWarning) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 7d40fe44a..8f819686d 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2237,13 +2237,13 @@ proc myProcess(b: PPassContext, n: PNode): PNode = add(p.g.code, p.body) globals.unique = p.unique -proc wholeCode*(m: BModule): Rope = +proc wholeCode(graph: ModuleGraph; m: BModule): Rope = for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) attachProc(p, prc) - var disp = generateMethodDispatchers() + var disp = generateMethodDispatchers(graph) for i in 0..sonsLen(disp)-1: let prc = disp.sons[i].sym if not globals.generatedSyms.containsOrIncl(prc.id): @@ -2277,7 +2277,7 @@ proc genClass(obj: PType; content: Rope; ext: string) = let outfile = changeFileExt(completeCFilePath($cls), ext) discard writeRopeIfNotEqual(result, outfile) -proc myClose(b: PPassContext, n: PNode): PNode = +proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = if passes.skipCodegen(n): return n result = myProcess(b, n) var m = BModule(b) @@ -2285,7 +2285,7 @@ proc myClose(b: PPassContext, n: PNode): PNode = let ext = if m.target == targetJS: "js" else: "php" let f = if globals.classes.len == 0: m.module.filename else: "nimsystem" - let code = wholeCode(m) + let code = wholeCode(graph, m) let outfile = if options.outFile.len > 0: if options.outFile.isAbsolute: options.outFile diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 19a4da07b..089e69ff9 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -115,7 +115,7 @@ proc errorSym*(c: PContext, n: PNode): PSym = considerQuotedIdent(m) else: getIdent("err:" & renderTree(m)) - result = newSym(skError, ident, getCurrOwner(), n.info) + result = newSym(skError, ident, getCurrOwner(c), n.info) result.typ = errorType(c) incl(result.flags, sfDiscardable) # pretend it's imported from some unknown module to prevent cascading errors: diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 87a35b290..c081a099a 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -40,9 +40,16 @@ type # module dependencies. backend*: RootRef # minor hack so that a backend can extend this easily config*: ConfigRef + doStopCompile*: proc(): bool {.closure.} + usageSym*: PSym # for nimsuggest + owners*: seq[PSym] + methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] {.this: g.} +proc stopCompile*(g: ModuleGraph): bool {.inline.} = + result = doStopCompile != nil and doStopCompile() + proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph = result = ModuleGraph() initStrTable(result.packageSyms) @@ -54,6 +61,8 @@ proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph = result.config = newConfigRef() else: result.config = config + result.owners = @[] + result.methods = @[] proc resetAllModules*(g: ModuleGraph) = initStrTable(packageSyms) @@ -61,6 +70,9 @@ proc resetAllModules*(g: ModuleGraph) = modules = @[] importStack = @[] inclToMod = initTable[int32, int32]() + usageSym = nil + owners = @[] + methods = @[] proc getModule*(g: ModuleGraph; fileIdx: int32): PSym = if fileIdx >= 0 and fileIdx < modules.len: @@ -73,7 +85,7 @@ proc addDep*(g: ModuleGraph; m: PSym, dep: int32) = deps.incl m.position.dependsOn(dep) # we compute the transitive closure later when quering the graph lazily. # this improve efficiency quite a lot: - invalidTransitiveClosure = true + #invalidTransitiveClosure = true proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) = discard hasKeyOrPut(inclToMod, includeFile, module) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 49e4fa184..a1ba82263 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -567,6 +567,15 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo = if optEmbedOrigSrc in gGlobalOptions or true: result.lines = @[] +proc fileInfoKnown*(filename: string): bool = + var + canon: string + try: + canon = canonicalizePath(filename) + except: + canon = filename + result = filenameToIndexTbl.hasKey(canon) + proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 = var canon: string diff --git a/compiler/options.nim b/compiler/options.nim index 7fd7129e5..6281980ff 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -100,7 +100,7 @@ type IdeCmd* = enum ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod, - ideHighlight, ideOutline + ideHighlight, ideOutline, ideKnown ConfigRef* = ref object ## eventually all global configuration should be moved here cppDefines*: HashSet[string] @@ -436,6 +436,7 @@ proc parseIdeCmd*(s: string): IdeCmd = of "mod": ideMod of "highlight": ideHighlight of "outline": ideOutline + of "known": ideKnown else: ideNone proc `$`*(c: IdeCmd): string = @@ -450,3 +451,4 @@ proc `$`*(c: IdeCmd): string = of ideNone: "none" of ideHighlight: "highlight" of ideOutline: "outline" + of ideKnown: "known" diff --git a/compiler/passaux.nim b/compiler/passaux.nim index eeaf12953..2065d5893 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -29,21 +29,3 @@ proc verboseProcess(context: PPassContext, n: PNode): PNode = message(n.info, hintProcessing, $idgen.gFrontendId) const verbosePass* = makePass(open = verboseOpen, process = verboseProcess) - -proc cleanUp(c: PPassContext, n: PNode): PNode = - result = n - # we cannot clean up if dead code elimination is activated - if optDeadCodeElim in gGlobalOptions or n == nil: return - case n.kind - of nkStmtList: - for i in countup(0, sonsLen(n) - 1): discard cleanUp(c, n.sons[i]) - of nkProcDef, nkMethodDef: - if n.sons[namePos].kind == nkSym: - var s = n.sons[namePos].sym - if sfDeadCodeElim notin getModule(s).flags and not astNeeded(s): - s.ast.sons[bodyPos] = ast.emptyNode # free the memory - else: - discard - -const cleanupPass* = makePass(process = cleanUp, close = cleanUp) - diff --git a/compiler/passes.nim b/compiler/passes.nim index 328826f82..7966ee88d 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -24,7 +24,7 @@ type TPassOpen* = proc (graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext {.nimcall.} TPassOpenCached* = proc (graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext {.nimcall.} - TPassClose* = proc (p: PPassContext, n: PNode): PNode {.nimcall.} + TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.} TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.} TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached, @@ -94,7 +94,7 @@ proc carryPass*(g: ModuleGraph; p: TPass, module: PSym; cache: IdentCache; m: TPassData): TPassData = var c = p.open(g, module, cache) result.input = p.process(c, m.input) - result.closeOutput = if p.close != nil: p.close(c, m.closeOutput) + result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput) else: m.closeOutput proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym; @@ -121,10 +121,10 @@ proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym, else: a[i] = nil -proc closePasses(a: var TPassContextArray) = +proc closePasses(graph: ModuleGraph; a: var TPassContextArray) = var m: PNode = nil for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].close): m = gPasses[i].close(a[i], m) + if not isNil(gPasses[i].close): m = gPasses[i].close(graph, a[i], m) a[i] = nil # free the memory here proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool = @@ -142,11 +142,11 @@ proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) = for i in countup(0, gPassesLen - 1): if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m) -proc closePassesCached(a: var TPassContextArray) = +proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) = var m: PNode = nil for i in countup(0, gPassesLen - 1): if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close): - m = gPasses[i].close(a[i], m) + m = gPasses[i].close(graph, a[i], m) a[i] = nil # free the memory here proc resolveMod(module, relativeTo: string): int32 = @@ -171,6 +171,7 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, rd: PRodReader; cache: IdentCache): bool {.discardable.} = + if graph.stopCompile(): return true var p: TParsers a: TPassContextArray @@ -198,6 +199,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, processImplicits implicitIncludes, nkIncludeStmt, a, module while true: + if graph.stopCompile(): break var n = parseTopLevelStmt(p) if n.kind == nkEmpty: break if sfNoForward in module.flags: @@ -213,12 +215,14 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, elif not processTopLevelStmt(n, a): break closeParsers(p) if s.kind != llsStdIn: break - closePasses(a) + closePasses(graph, a) # id synchronization point for more consistent code generation: idSynchronizationPoint(1000) else: openPassesCached(graph, a, module, rd) var n = loadInitSection(rd) - for i in countup(0, sonsLen(n) - 1): processTopLevelStmtCached(n.sons[i], a) - closePassesCached(a) + for i in countup(0, sonsLen(n) - 1): + if graph.stopCompile(): break + processTopLevelStmtCached(n.sons[i], a) + closePassesCached(graph, a) result = true diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 09b8d3305..859fe2a81 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -289,7 +289,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode = # constraint not fulfilled: if not ok: return nil - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) if ctx.subMatch: assert m.len == 3 m.sons[1] = result diff --git a/compiler/plugins/locals/locals.nim b/compiler/plugins/locals/locals.nim index 8a3f67dd4..338e7bcac 100644 --- a/compiler/plugins/locals/locals.nim +++ b/compiler/plugins/locals/locals.nim @@ -28,7 +28,7 @@ proc semLocals(c: PContext, n: PNode): PNode = it.typ.skipTypes({tyGenericInst, tyVar}).kind notin {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyExpr, tyStmt, tyEmpty}: - var field = newSym(skField, it.name, getCurrOwner(), n.info) + var field = newSym(skField, it.name, getCurrOwner(c), n.info) field.typ = it.typ.skipTypes({tyGenericInst, tyVar}) field.position = counter inc(counter) diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 4ce9e616a..d61d817dd 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -641,7 +641,7 @@ proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = rawAddInterfaceSym(w, module) result = w -proc myClose(c: PPassContext, n: PNode): PNode = +proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = result = process(c, n) var w = PRodWriter(c) writeRod(w) diff --git a/compiler/sem.nim b/compiler/sem.nim index 59d726b43..682e9d0ca 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -166,7 +166,7 @@ proc commonType*(x, y: PType): PType = result.addSonSkipIntLit(r) proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = - result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) + result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info) proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii @@ -186,9 +186,9 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = # when there is a nested proc inside a template, semtmpl # will assign a wrong owner during the first pass over the # template; we must fix it here: see #909 - result.owner = getCurrOwner() + result.owner = getCurrOwner(c) else: - result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info) + result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info) #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule: # incl(result.flags, sfGlobal) @@ -376,7 +376,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = pushInfoContext(nOrig.info) - markUsed(n.info, sym) + markUsed(n.info, sym, c.graph.usageSym) styleCheckUse(n.info, sym) if sym == c.p.owner: globalError(n.info, errRecursiveDependencyX, sym.name.s) @@ -434,7 +434,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = c.instTypeBoundOp = sigmatch.instTypeBoundOp pushProcCon(c, module) - pushOwner(c.module) + pushOwner(c, c.module) c.importTable = openScope(c) c.importTable.addSym(module) # a module knows itself if sfSystemModule in module.flags: @@ -450,7 +450,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext = result = myOpen(graph, module, rd.cache) - for m in items(rd.methods): methodDef(m, true) + for m in items(rd.methods): methodDef(graph, m, true) proc isImportSystemStmt(n: PNode): bool = if magicsys.systemModule == nil: return false @@ -502,7 +502,7 @@ proc recoverContext(c: PContext) = # faster than wrapping every stack operation in a 'try finally' block and # requires far less code. c.currentScope = c.topLevelScope - while getCurrOwner().kind != skModule: popOwner() + while getCurrOwner(c).kind != skModule: popOwner(c) while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next proc myProcess(context: PPassContext, n: PNode): PNode = @@ -523,7 +523,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode = else: result = ast.emptyNode #if gCmd == cmdIdeTools: findSuggest(c, n) -proc myClose(context: PPassContext, n: PNode): PNode = +proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = var c = PContext(context) closeScope(c) # close module's scope rawCloseScope(c) # imported symbols; don't check for unused ones! @@ -533,7 +533,7 @@ proc myClose(context: PPassContext, n: PNode): PNode = addCodeForGenerics(c, result) if c.module.ast != nil: result.add(c.module.ast) - popOwner() + popOwner(c) popProcCon(c) const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index c4dc13624..70765d087 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -105,7 +105,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = of attachedDestructor: let op = t.destructor if op != nil: - markUsed(c.info, op) + markUsed(c.info, op, c.c.graph.usageSym) styleCheckUse(c.info, op) body.add newDestructorCall(op, x) result = true @@ -123,14 +123,14 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = op = t.assignment if op == nil: op = liftBody(c.c, t, c.info) - markUsed(c.info, op) + markUsed(c.info, op, c.c.graph.usageSym) styleCheckUse(c.info, op) body.add newAsgnCall(c.c, op, x, y) result = true of attachedDeepCopy: let op = t.deepCopy if op != nil: - markUsed(c.info, op) + markUsed(c.info, op, c.c.graph.usageSym) styleCheckUse(c.info, op) body.add newDeepCopyCall(op, x, y) result = true @@ -248,6 +248,7 @@ proc addParam(procType: PType; param: PSym) = proc liftBody(c: PContext; typ: PType; info: TLineInfo): PSym = var a: TLiftCtx a.info = info + a.c = c let body = newNodeI(nkStmtList, info) result = newSym(skProc, getIdent":lifted=", typ.owner, info) a.fn = result diff --git a/compiler/semcall.nim b/compiler/semcall.nim index ae7bab89d..fe73d60e6 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -335,7 +335,7 @@ proc inferWithMetatype(c: PContext, formal: PType, proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = assert x.state == csMatch var finalCallee = x.calleeSym - markUsed(n.sons[0].info, finalCallee) + markUsed(n.sons[0].info, finalCallee, c.graph.usageSym) styleCheckUse(n.sons[0].info, finalCallee) assert finalCallee.ast != nil if x.hasFauxMatch: @@ -411,7 +411,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = let tm = typeRel(m, formal, arg, true) if tm in {isNone, isConvertible}: return nil var newInst = generateInstance(c, s, m.bindings, n.info) - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(newInst, n.info) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index b501ec440..77d461007 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -124,22 +124,19 @@ proc scopeDepth*(c: PContext): int {.inline.} = result = if c.currentScope != nil: c.currentScope.depthLevel else: 0 -var gOwners*: seq[PSym] = @[] - -proc getCurrOwner*(): PSym = +proc getCurrOwner*(c: PContext): PSym = # owner stack (used for initializing the # owner field of syms) # the documentation comment always gets # assigned to the current owner - # BUGFIX: global array is needed! - result = gOwners[^1] + result = c.graph.owners[^1] -proc pushOwner*(owner: PSym) = - add(gOwners, owner) +proc pushOwner*(c: PContext; owner: PSym) = + add(c.graph.owners, owner) -proc popOwner*() = - var length = len(gOwners) - if length > 0: setLen(gOwners, length - 1) +proc popOwner*(c: PContext) = + var length = len(c.graph.owners) + if length > 0: setLen(c.graph.owners, length - 1) else: internalError("popOwner") proc lastOptionEntry*(c: PContext): POptionEntry = @@ -225,7 +222,7 @@ proc addToLib*(lib: PLib, sym: PSym) = sym.annex = lib proc newTypeS*(kind: TTypeKind, c: PContext): PType = - result = newType(kind, getCurrOwner()) + result = newType(kind, getCurrOwner(c)) proc makePtrType*(c: PContext, baseType: PType): PType = result = newTypeS(tyPtr, c) @@ -244,7 +241,7 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = makeTypeDesc(c, typ) - let sym = newSym(skType, c.cache.idAnon, getCurrOwner(), info).linkTo(typedesc) + let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info).linkTo(typedesc) return newSymNode(sym, info) proc makeTypeFromExpr*(c: PContext, n: PNode): PType = @@ -254,7 +251,7 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = proc newTypeWithSons*(c: PContext, kind: TTypeKind, sons: seq[PType]): PType = - result = newType(kind, getCurrOwner()) + result = newType(kind, getCurrOwner(c)) result.sons = sons proc makeStaticExpr*(c: PContext, n: PNode): PNode = @@ -325,7 +322,7 @@ proc errorNode*(c: PContext, n: PNode): PNode = proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) = dest.kind = kind - dest.owner = getCurrOwner() + dest.owner = getCurrOwner(c) dest.size = - 1 proc makeRangeType*(c: PContext; first, last: BiggestInt; diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index a8873bbe2..b09404b39 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -51,7 +51,7 @@ proc doDestructorStuff(c: PContext, s: PSym, n: PNode) = let destructableT = instantiateDestructor(c, t.sons[i]) if destructableT != nil: n.sons[bodyPos].addSon(newNode(nkCall, t.sym.info, @[ - useSym(destructableT.destructor), + useSym(destructableT.destructor, c.graph.usageSym), n.sons[paramsPos][1][0]])) proc destroyFieldOrFields(c: PContext, field: PNode, holder: PNode): PNode @@ -60,8 +60,8 @@ proc destroySym(c: PContext, field: PSym, holder: PNode): PNode = let destructableT = instantiateDestructor(c, field.typ) if destructableT != nil: result = newNode(nkCall, field.info, @[ - useSym(destructableT.destructor), - newNode(nkDotExpr, field.info, @[holder, useSym(field)])]) + useSym(destructableT.destructor, c.graph.usageSym), + newNode(nkDotExpr, field.info, @[holder, useSym(field, c.graph.usageSym)])]) proc destroyCase(c: PContext, n: PNode, holder: PNode): PNode = var nonTrivialFields = 0 @@ -181,7 +181,8 @@ proc createDestructorCall(c: PContext, s: PSym): PNode = let destructableT = instantiateDestructor(c, varTyp) if destructableT != nil: let call = semStmt(c, newNode(nkCall, s.info, @[ - useSym(destructableT.destructor), useSym(s)])) + useSym(destructableT.destructor, c.graph.usageSym), + useSym(s, c.graph.usageSym)])) result = newNode(nkDefer, s.info, @[call]) proc insertDestructors(c: PContext, @@ -233,8 +234,8 @@ proc insertDestructors(c: PContext, tryStmt.addSon( newNode(nkFinally, info, @[ semStmt(c, newNode(nkCall, info, @[ - useSym(destructableT.destructor), - useSym(varId.sym)]))])) + useSym(destructableT.destructor, c.graph.usageSym), + useSym(varId.sym, c.graph.usageSym)]))])) result.outer = newNodeI(nkStmtList, info) varSection.sons.setLen(j+1) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index d9a5f6c50..6db7724f9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -12,10 +12,10 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}): PNode = - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) pushInfoContext(n.info) - result = evalTemplate(n, s, getCurrOwner(), efFromHlo in flags) + result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags) popInfoContext() @@ -209,7 +209,7 @@ proc semConv(c: PContext, n: PNode): PNode = let it = op.sons[i] let status = checkConvertible(c, result.typ, it.typ) if status in {convOK, convNotNeedeed}: - markUsed(n.info, it.sym) + markUsed(n.info, it.sym, c.graph.usageSym) styleCheckUse(n.info, it.sym) markIndirect(c, it.sym) return it @@ -926,7 +926,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = let s = getGenSym(c, sym) case s.kind of skConst: - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) case skipTypes(s.typ, abstractInst-{tyTypeDesc}).kind of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, @@ -950,20 +950,20 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = result = newSymNode(s, n.info) of skMacro: if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0: - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(s, n.info) else: result = semMacroExpr(c, n, n, s, flags) of skTemplate: if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0: - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(s, n.info) else: result = semTemplateExpr(c, n, s, flags) of skParam: - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) if s.typ.kind == tyStatic and s.typ.n != nil: # XXX see the hack in sigmatch.nim ... @@ -985,7 +985,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if s.magic == mNimvm: localError(n.info, "illegal context for 'nimvm' magic") - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(s, n.info) # We cannot check for access to outer vars for example because it's still @@ -1003,7 +1003,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = n.typ = s.typ return n of skType: - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) if s.typ.kind == tyStatic and s.typ.n != nil: return s.typ.n @@ -1025,7 +1025,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if f != nil and fieldVisible(c, f): # is the access to a public field or in the same module or in a friend? doAssert f == s - markUsed(n.info, f) + markUsed(n.info, f, c.graph.usageSym) styleCheckUse(n.info, f) result = newNodeIT(nkDotExpr, n.info, f.typ) result.add makeDeref(newSymNode(p.selfSym)) @@ -1038,11 +1038,11 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if ty.sons[0] == nil: break ty = skipTypes(ty.sons[0], skipPtrs) # old code, not sure if it's live code: - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(s, n.info) else: - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) result = newSymNode(s, n.info) @@ -1062,7 +1062,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = symChoice(c, n, s, scClosed) if result.kind == nkSym: result = semSym(c, n, s, flags) else: - markUsed(n.sons[1].info, s) + markUsed(n.sons[1].info, s, c.graph.usageSym) result = semSym(c, n, s, flags) styleCheckUse(n.sons[1].info, s) return @@ -1087,7 +1087,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = newSymNode(f) result.info = n.info result.typ = ty - markUsed(n.info, f) + markUsed(n.info, f, c.graph.usageSym) styleCheckUse(n.info, f) return of tyTypeParamsHolders: @@ -1120,7 +1120,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if f != nil: if fieldVisible(c, f): # is the access to a public field or in the same module or in a friend? - markUsed(n.sons[1].info, f) + markUsed(n.sons[1].info, f, c.graph.usageSym) styleCheckUse(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) # we now have the correct field @@ -1134,7 +1134,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = elif ty.kind == tyTuple and ty.n != nil: f = getSymFromList(ty.n, i) if f != nil: - markUsed(n.sons[1].info, f) + markUsed(n.sons[1].info, f, c.graph.usageSym) styleCheckUse(n.sons[1].info, f) n.sons[0] = makeDeref(n.sons[0]) n.sons[1] = newSymNode(f) @@ -1581,9 +1581,8 @@ proc getMagicSym(magic: TMagic): PSym = result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo) result.magic = magic -proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo, - owner = getCurrOwner()): PSym = - result = newSym(kind, c.cache.idAnon, owner, info) +proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym = + result = newSym(kind, c.cache.idAnon, getCurrOwner(c), info) result.flags = {sfGenSym} proc semExpandToAst(c: PContext, n: PNode): PNode = @@ -1592,7 +1591,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode = if expandedSym.kind == skError: return n macroCall.sons[0] = newSymNode(expandedSym, macroCall.info) - markUsed(n.info, expandedSym) + markUsed(n.info, expandedSym, c.graph.usageSym) styleCheckUse(n.info, expandedSym) for i in countup(1, macroCall.len-1): @@ -1683,7 +1682,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # open a scope for temporary symbol inclusions: let oldScope = c.currentScope openScope(c) - let oldOwnerLen = len(gOwners) + let oldOwnerLen = len(c.graph.owners) let oldGenerics = c.generics let oldErrorOutputs = errorOutputs errorOutputs = {} @@ -1709,7 +1708,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = c.inGenericInst = oldInGenericInst c.p = oldProcCon msgs.setInfoContextLen(oldContextLen) - setLen(gOwners, oldOwnerLen) + setLen(c.graph.owners, oldOwnerLen) c.currentScope = oldScope errorOutputs = oldErrorOutputs msgs.gErrorCounter = oldErrorCount @@ -2108,7 +2107,7 @@ proc semBlock(c: PContext, n: PNode): PNode = if sfGenSym notin labl.flags: addDecl(c, labl) n.sons[0] = newSymNode(labl, n.sons[0].info) - suggestSym(n.sons[0].info, labl) + suggestSym(n.sons[0].info, labl, c.graph.usageSym) styleCheckDef(labl) n.sons[1] = semExpr(c, n.sons[1]) n.typ = n.sons[1].typ diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 9d8cea862..5ac1ad11a 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -108,7 +108,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true") if trueSymbol == nil: localError(n.info, errSystemNeeds, "true") - trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(), n.info) + trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(c), n.info) trueSymbol.typ = getSysType(tyBool) result.sons[0] = newSymNode(trueSymbol, n.info) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index acaade494..82d27cf10 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -61,7 +61,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses: continue let symKind = if q.typ.kind == tyStatic: skConst else: skType - var s = newSym(symKind, q.name, getCurrOwner(), q.info) + var s = newSym(symKind, q.name, getCurrOwner(c), q.info) s.flags = s.flags + {sfUsed, sfFromGeneric} var t = PType(idTableGet(pt, q.typ)) if t == nil: @@ -255,7 +255,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, incl(result.flags, sfFromGeneric) result.owner = fn result.ast = n - pushOwner(result) + pushOwner(c, result) openScope(c) let gp = n.sons[genericParamsPos] @@ -304,7 +304,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, popProcCon(c) popInfoContext() closeScope(c) # close scope for parameters - popOwner() + popOwner(c) c.currentScope = oldScope discard c.friendModules.pop() dec(c.instCounter) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index e72172c81..5eed1e702 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -107,7 +107,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode = if t.sonsLen > 0: # This is either a type known to sem or a typedesc # param to a regular proc (again, known at instantiation) - result = evalTypeTrait(n[0], t, getCurrOwner()) + result = evalTypeTrait(n[0], t, getCurrOwner(c)) else: # a typedesc variable, pass unmodified to evals result = n diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 282d4da88..c28dbf82f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -36,7 +36,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = x.info = n.info incl(s.flags, sfUsed) n.sons[0] = x - suggestSym(x.info, s) + suggestSym(x.info, s, c.graph.usageSym) styleCheckUse(x.info, s) else: localError(n.info, errInvalidControlFlowX, s.name.s) @@ -367,11 +367,13 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = if isTopLevel(c): result = semIdentWithPragma(c, kind, n, {sfExported}) incl(result.flags, sfGlobal) + #if kind in {skVar, skLet}: + # echo "global variable here ", n.info, " ", result.name.s else: result = semIdentWithPragma(c, kind, n, {}) if result.owner.kind == skModule: incl(result.flags, sfGlobal) - suggestSym(n.info, result) + suggestSym(n.info, result, c.graph.usageSym) styleCheckDef(result) proc checkNilable(v: PSym) = @@ -622,7 +624,7 @@ proc semForVars(c: PContext, n: PNode): PNode = if iter.kind != tyTuple or length == 3: if length == 3: var v = symForVar(c, n.sons[0]) - if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) + if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal) # BUGFIX: don't use `iter` here as that would strip away # the ``tyGenericInst``! See ``tests/compile/tgeneric.nim`` # for an example: @@ -636,7 +638,7 @@ proc semForVars(c: PContext, n: PNode): PNode = else: for i in countup(0, length - 3): var v = symForVar(c, n.sons[i]) - if getCurrOwner().kind == skModule: incl(v.flags, sfGlobal) + if getCurrOwner(c).kind == skModule: incl(v.flags, sfGlobal) v.typ = iter.sons[i] n.sons[i] = newSymNode(v) if sfGenSym notin v.flags and not isDiscardUnderscore(v): @@ -746,7 +748,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = # We have a generic type declaration here. In generic types, # symbol lookup needs to be done here. openScope(c) - pushOwner(s) + pushOwner(c, s) if s.magic == mNone: s.typ.kind = tyGenericBody # XXX for generic type aliases this is not correct! We need the # underlying Id really: @@ -770,11 +772,11 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = body.sym = s body.size = -1 # could not be computed properly s.typ.sons[sonsLen(s.typ) - 1] = body - popOwner() + popOwner(c) closeScope(c) elif a.sons[2].kind != nkEmpty: # process the type's body: - pushOwner(s) + pushOwner(c, s) var t = semTypeNode(c, a.sons[2], s.typ) if s.typ == nil: s.typ = t @@ -783,7 +785,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = assignType(s.typ, t) #debug s.typ s.ast = a - popOwner() + popOwner(c) let aa = a.sons[2] if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and aa.sons[0].kind == nkObjectTy: @@ -794,7 +796,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = internalAssert st.lastSon.sym == nil incl st.flags, tfRefsAnonObj let obj = newSym(skType, getIdent(s.name.s & ":ObjectType"), - getCurrOwner(), s.info) + getCurrOwner(c), s.info) obj.typ = st.lastSon st.lastSon.sym = obj @@ -928,7 +930,7 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) = proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = if t != nil: - var s = newSym(skResult, getIdent"result", getCurrOwner(), info) + var s = newSym(skResult, getIdent"result", getCurrOwner(c), info) s.typ = t incl(s.flags, sfUsed) addParamOrResult(c, s, owner) @@ -1003,12 +1005,12 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = checkSonsLen(n, bodyPos + 1) var s: PSym if n[namePos].kind != nkSym: - s = newSym(skProc, c.cache.idAnon, getCurrOwner(), n.info) + s = newSym(skProc, c.cache.idAnon, getCurrOwner(c), n.info) s.ast = n n.sons[namePos] = newSymNode(s) else: s = n[namePos].sym - pushOwner(s) + pushOwner(c, s) openScope(c) var gp: PNode if n.sons[genericParamsPos].kind != nkEmpty: @@ -1048,7 +1050,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = else: localError(n.info, errImplOfXexpected, s.name.s) closeScope(c) # close scope for parameters - popOwner() + popOwner(c) result.typ = s.typ proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode = @@ -1082,7 +1084,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = params[i].sym.name.s) #params[i].sym.owner = s openScope(c) - pushOwner(s) + pushOwner(c, s) addParams(c, params, skProc) pushProcCon(c, s) addResult(c, n.typ.sons[0], n.info, skProc) @@ -1090,7 +1092,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = let semBody = hloBody(c, semProcBody(c, n.sons[bodyPos])) n.sons[bodyPos] = transformBody(c.module, semBody, s) popProcCon(c) - popOwner() + popOwner(c) closeScope(c) # alternative variant (not quite working): @@ -1178,11 +1180,58 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = localError(n.info, errGenerated, "'destroy' or 'deepCopy' expected for 'override'") +proc cursorInProcAux(n: PNode): bool = + if inCheckpoint(n.info) != cpNone: return true + for i in 0..<n.safeLen: + if cursorInProcAux(n[i]): return true + +proc cursorInProc(n: PNode): bool = + if n.info.fileIndex == gTrackPos.fileIndex: + result = cursorInProcAux(n) + type TProcCompilationSteps = enum stepRegisterSymbol, stepDetermineType, +import compilerlog + +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(c.graph, s, false) + +proc semMethodPrototype(c: PContext; s: PSym; n: PNode) = + if isGenericRoutine(s): + let tt = s.typ + var foundObj = false + # we start at 1 for now so that tparsecombnum continues to compile. + # XXX Revisit this problem later. + for col in countup(1, sonsLen(tt)-1): + let t = tt.sons[col] + if t != nil and t.kind == tyGenericInvocation: + var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst, + tyGenericInvocation, tyGenericBody, + tyAlias}) + if x.kind == tyObject and t.len-1 == n.sons[genericParamsPos].len: + foundObj = true + x.methods.safeAdd((col,s)) + if not foundObj: + message(n.info, warnDeprecated, "generic method not attachable to object type") + else: + # why check for the body? bug #2400 has none. Checking for sfForward makes + # no sense either. + # and result.sons[bodyPos].kind != nkEmpty: + if hasObjParam(s): + methodDef(c.graph, s, fromCache=false) + else: + localError(n.info, errXNeedsParamObjectType, "method") + proc semProcAux(c: PContext, n: PNode, kind: TSymKind, validPragmas: TSpecialWords, phase = stepRegisterSymbol): PNode = @@ -1197,7 +1246,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, assert phase == stepRegisterSymbol if n[namePos].kind == nkEmpty: - s = newSym(kind, c.cache.idAnon, getCurrOwner(), n.info) + s = newSym(kind, c.cache.idAnon, getCurrOwner(c), n.info) incl(s.flags, sfUsed) isAnon = true else: @@ -1214,7 +1263,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, return else: s = n[namePos].sym - s.owner = getCurrOwner() + s.owner = getCurrOwner(c) typeIsDetermined = s.typ == nil s.ast = n #s.scope = c.currentScope @@ -1223,7 +1272,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # where the proc was declared let oldScope = c.currentScope #c.currentScope = s.scope - pushOwner(s) + pushOwner(c, s) openScope(c) var gp: PNode if n.sons[genericParamsPos].kind != nkEmpty: @@ -1289,8 +1338,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if importantComments() and not isNil(proto.ast.comment): n.comment = proto.ast.comment proto.ast = n # needed for code generation - popOwner() - pushOwner(s) + popOwner(c) + pushOwner(c, s) s.options = gOptions if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if s.name.s[0] in {'.', '('}: @@ -1304,30 +1353,40 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # Macros and Templates can have generic parameters, but they are # only used for overload resolution (there is no instantiation of # the symbol, so we must process the body now) - pushProcCon(c, s) - if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics: - if not usePseudoGenerics: paramsTypeCheck(c, s.typ) - - c.p.wasForwarded = proto != nil - maybeAddResult(c, s, n) - if lfDynamicLib notin s.loc.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 not usePseudoGenerics and gIdeCmd in {ideSug, ideCon} and not + cursorInProc(n.sons[bodyPos]): + discard "speed up nimsuggest" + if s.kind == skMethod: semMethodPrototype(c, s, n) else: - if s.typ.sons[0] != nil and kind != skIterator: - addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) - openScope(c) - n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos]) - closeScope(c) - fixupInstantiatedSymbols(c, s) - if sfImportc in s.flags: - # so we just ignore the body after semantic checking for importc: - n.sons[bodyPos] = ast.emptyNode - popProcCon(c) + pushProcCon(c, s) + if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics: + if not usePseudoGenerics: paramsTypeCheck(c, s.typ) + + c.p.wasForwarded = proto != nil + maybeAddResult(c, s, n) + if s.kind == skMethod: semMethodPrototype(c, s, n) + + if lfDynamicLib notin s.loc.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) + else: + if s.typ.sons[0] != nil and kind != skIterator: + addDecl(c, newSym(skUnknown, getIdent"result", nil, n.info)) + + openScope(c) + n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos]) + closeScope(c) + fixupInstantiatedSymbols(c, s) + if s.kind == skMethod: semMethodPrototype(c, s, n) + if sfImportc in s.flags: + # so we just ignore the body after semantic checking for importc: + n.sons[bodyPos] = ast.emptyNode + popProcCon(c) else: + if s.kind == skMethod: semMethodPrototype(c, s, n) if proto != nil: localError(n.info, errImplOfXexpected, proto.name.s) if {sfImportc, sfBorrow} * s.flags == {} and s.magic == mNone: incl(s.flags, sfForward) @@ -1335,7 +1394,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, sideEffectsCheck(c, s) closeScope(c) # close scope for parameters # c.currentScope = oldScope - popOwner() + popOwner(c) if n.sons[patternPos].kind != nkEmpty: c.patterns.add(s) if isAnon: result.typ = s.typ @@ -1354,7 +1413,7 @@ proc semIterator(c: PContext, n: PNode): PNode = let isAnon = n[namePos].kind == nkEmpty if n[namePos].kind == nkSym: # gensym'ed iterators might need to become closure iterators: - n[namePos].sym.owner = getCurrOwner() + n[namePos].sym.owner = getCurrOwner(c) n[namePos].sym.kind = skIterator result = semProcAux(c, n, skIterator, iteratorPragmas) var s = result.sons[namePos].sym @@ -1381,46 +1440,22 @@ 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) - # macros can transform methods to nothing: + # macros can transform converters to nothing: if namePos >= result.safeLen: return result var s = result.sons[namePos].sym - if isGenericRoutine(s): - let tt = s.typ - var foundObj = false - # we start at 1 for now so that tparsecombnum continues to compile. - # XXX Revisit this problem later. - for col in countup(1, sonsLen(tt)-1): - let t = tt.sons[col] - if t != nil and t.kind == tyGenericInvocation: - var x = skipTypes(t.sons[0], {tyVar, tyPtr, tyRef, tyGenericInst, - tyGenericInvocation, tyGenericBody, - tyAlias}) - if x.kind == tyObject and t.len-1 == result.sons[genericParamsPos].len: - foundObj = true - x.methods.safeAdd((col,s)) - if not foundObj: - message(n.info, warnDeprecated, "generic method not attachable to object type") - else: - # why check for the body? bug #2400 has none. Checking for sfForward makes - # no sense either. - # and result.sons[bodyPos].kind != nkEmpty: - if hasObjParam(s): - methodDef(s, fromCache=false) - else: - localError(n.info, errXNeedsParamObjectType, "method") + # we need to fix the 'auto' return type for the dispatcher here (see tautonotgeneric + # test case): + let disp = getDispatcher(s) + # auto return type? + if disp != nil and disp.typ.sons[0] != nil and disp.typ.sons[0].kind == tyExpr: + let ret = s.typ.sons[0] + disp.typ.sons[0] = ret + if disp.ast[resultPos].kind == nkSym: + if isEmptyType(ret): disp.ast.sons[resultPos] = emptyNode + else: disp.ast[resultPos].sym.typ = ret proc semConverterDef(c: PContext, n: PNode): PNode = if not isTopLevel(c): localError(n.info, errXOnlyAtModuleScope, "converter") diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 420e36116..d7134402f 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -60,7 +60,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = # (s.kind notin routineKinds or s.magic != mNone): # for instance 'nextTry' is both in tables.nim and astalgo.nim ... result = newSymNode(s, n.info) - markUsed(n.info, s) + markUsed(n.info, s, c.graph.usageSym) else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately @@ -561,7 +561,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = styleCheckDef(s) # check parameter list: #s.scope = c.currentScope - pushOwner(s) + pushOwner(c, s) openScope(c) n.sons[namePos] = newSymNode(s, n.sons[namePos].info) if n.sons[pragmasPos].kind != nkEmpty: @@ -615,7 +615,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = # only parameters are resolved, no type checking is performed semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody) closeScope(c) - popOwner() + popOwner(c) s.ast = n result = n if n.sons[bodyPos].kind == nkEmpty: @@ -760,7 +760,7 @@ proc semPattern(c: PContext, n: PNode): PNode = ctx.toMixin = initIntSet() ctx.toInject = initIntSet() ctx.c = c - ctx.owner = getCurrOwner() + ctx.owner = getCurrOwner(c) result = flattenStmts(semPatternBody(ctx, n)) if result.kind in {nkStmtList, nkStmtListExpr}: if result.len == 1: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 440edd226..97af2c5cc 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -306,7 +306,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = else: result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared}) if result != nil: - markUsed(n.info, result) + markUsed(n.info, result, c.graph.usageSym) styleCheckUse(n.info, result) if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? @@ -608,7 +608,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, let rec = rectype.sym for i in countup(0, sonsLen(n)-3): var f = semIdentWithPragma(c, skField, n.sons[i], {sfExported}) - suggestSym(n.sons[i].info, f) + suggestSym(n.sons[i].info, f, c.graph.usageSym) f.typ = typ f.position = pos if (rec != nil) and ({sfImportc, sfExportc} * rec.flags != {}) and @@ -750,7 +750,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, return genericParams.sons[i].typ let owner = if typeClass.sym != nil: typeClass.sym - else: getCurrOwner() + else: getCurrOwner(c) var s = newSym(skType, finalTypId, owner, info) if typId == nil: s.flags.incl(sfAnon) s.linkTo(typeClass) @@ -850,7 +850,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyGenericInst: if paramType.lastSon.kind == tyUserTypeClass: - var cp = copyType(paramType, getCurrOwner(), false) + var cp = copyType(paramType, getCurrOwner(c), false) cp.kind = tyUserTypeClassInst return addImplicitGeneric(cp) @@ -876,10 +876,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = liftingWalk(expanded, true) of tyUserTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: - result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true)) + result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true)) of tyGenericParam: - markUsed(info, paramType.sym) + markUsed(info, paramType.sym, c.graph.usageSym) styleCheckUse(info, paramType.sym) if tfWildcard in paramType.flags: paramType.flags.excl tfWildcard @@ -1267,7 +1267,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: - result = copyType(result, getCurrOwner(), false) + result = copyType(result, getCurrOwner(c), false) for i in countup(1, n.len - 1): result.rawAddSon(semTypeNode(c, n.sons[i], nil)) of mDistinct: @@ -1330,7 +1330,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: assignType(prev, t) result = prev - markUsed(n.info, n.sym) + markUsed(n.info, n.sym, c.graph.usageSym) styleCheckUse(n.info, n.sym) else: if s.kind != skError: localError(n.info, errTypeExpected) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d3c142954..f2caab41f 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -73,7 +73,7 @@ type const isNilConversion = isConvertible # maybe 'isIntConv' fits better? -proc markUsed*(info: TLineInfo, s: PSym) +proc markUsed*(info: TLineInfo, s: PSym; usageSym: var PSym) template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone @@ -543,7 +543,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = else: return isNone when useEffectSystem: - if not compatibleEffects(f, a): return isNone + if compatibleEffects(f, a) != efCompat: return isNone of tyNil: result = f.allowsNil @@ -1291,7 +1291,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, dest = generateTypeInstance(c, m.bindings, arg, dest) let fdest = typeRel(m, f, dest) if fdest in {isEqual, isGeneric}: - markUsed(arg.info, c.converters[i]) + markUsed(arg.info, c.converters[i], c.graph.usageSym) var s = newSymNode(c.converters[i]) s.typ = c.converters[i].typ s.info = arg.info @@ -1551,7 +1551,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, else: result = nil else: # only one valid interpretation found: - markUsed(arg.info, arg.sons[best].sym) + markUsed(arg.info, arg.sons[best].sym, m.c.graph.usageSym) styleCheckUse(arg.info, arg.sons[best].sym) result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best], argOrig) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 66876b9b5..9ae427583 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -28,7 +28,7 @@ type column*: int # Starts at 0 doc*: string # Not escaped (yet) symkind*: TSymKind - forth*: string # XXX TODO object on symkind + forth*: string # type quality*: range[0..100] # matching quality isGlobal*: bool # is a global variable tokenLen*: int @@ -88,7 +88,7 @@ proc symToSuggest(s: PSym, isLocal: bool, section: string, li: TLineInfo; when not defined(noDocgen): result.doc = s.extractDocComment -proc `$`(suggest: Suggest): string = +proc `$`*(suggest: Suggest): string = result = $suggest.section result.add(sep) if suggest.section == ideHighlight: @@ -131,7 +131,7 @@ proc suggestResult(s: Suggest) = if not isNil(suggestionResultHook): suggestionResultHook(s) else: - suggestWriteln($(s)) + suggestWriteln($s) proc filterSym(s: PSym): bool {.inline.} = result = s.kind != skModule @@ -297,10 +297,10 @@ proc suggestFieldAccess(c: PContext, n: PNode, outputs: var int) = suggestOperations(c, n, typ, outputs) type - TCheckPointResult = enum + TCheckPointResult* = enum cpNone, cpFuzzy, cpExact -proc inCheckpoint(current: TLineInfo): TCheckPointResult = +proc inCheckpoint*(current: TLineInfo): TCheckPointResult = if current.fileIndex == gTrackPos.fileIndex: if current.line == gTrackPos.line and abs(current.col-gTrackPos.col) < 4: @@ -353,10 +353,10 @@ when defined(nimsuggest): s.allUsages.add(info) var - usageSym*: PSym + #usageSym*: PSym lastLineInfo*: TLineInfo -proc findUsages(info: TLineInfo; s: PSym) = +proc findUsages(info: TLineInfo; s: PSym; usageSym: var PSym) = if suggestVersion < 2: if usageSym == nil and isTracked(info, s.name.s.len): usageSym = s @@ -385,7 +385,7 @@ proc ensureIdx[T](x: var T, y: int) = proc ensureSeq[T](x: var seq[T]) = if x == nil: newSeq(x, 0) -proc suggestSym*(info: TLineInfo; s: PSym; isDecl=true) {.inline.} = +proc suggestSym*(info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = ## misnamed: should be 'symDeclared' when defined(nimsuggest): if suggestVersion == 2: @@ -395,20 +395,20 @@ proc suggestSym*(info: TLineInfo; s: PSym; isDecl=true) {.inline.} = s.addNoDup(info) if gIdeCmd == ideUse: - findUsages(info, s) + findUsages(info, s, usageSym) elif gIdeCmd == ideDef: findDefinition(info, s) elif gIdeCmd == ideDus and s != nil: if isTracked(info, s.name.s.len): suggestResult(symToSuggest(s, isLocal=false, $ideDef, 100)) - findUsages(info, s) + findUsages(info, s, usageSym) elif gIdeCmd == ideHighlight and info.fileIndex == gTrackPos.fileIndex: suggestResult(symToSuggest(s, isLocal=false, $ideHighlight, info, 100)) elif gIdeCmd == ideOutline and info.fileIndex == gTrackPos.fileIndex and isDecl: suggestResult(symToSuggest(s, isLocal=false, $ideOutline, info, 100)) -proc markUsed(info: TLineInfo; s: PSym) = +proc markUsed(info: TLineInfo; s: PSym; usageSym: var PSym) = incl(s.flags, sfUsed) if s.kind == skEnumField and s.owner != nil: incl(s.owner.flags, sfUsed) @@ -416,11 +416,11 @@ proc markUsed(info: TLineInfo; s: PSym) = if sfDeprecated in s.flags: message(info, warnDeprecated, s.name.s) if sfError in s.flags: localError(info, errWrongSymbolX, s.name.s) when defined(nimsuggest): - suggestSym(info, s, false) + suggestSym(info, s, usageSym, false) -proc useSym*(sym: PSym): PNode = +proc useSym*(sym: PSym; usageSym: var PSym): PNode = result = newSymNode(sym) - markUsed(result.info, sym) + markUsed(result.info, sym, usageSym) proc safeSemExpr*(c: PContext, n: PNode): PNode = # use only for idetools support! diff --git a/compiler/transf.nim b/compiler/transf.nim index e14985145..0c53c0cbf 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -694,9 +694,10 @@ proc transformCall(c: PTransf, n: PNode): PTransNode = # bugfix: check after 'transformSons' if it's still a method call: # use the dispatcher for the call: if s.sons[0].kind == nkSym and s.sons[0].sym.kind == skMethod: - let t = lastSon(s.sons[0].sym.ast) - if t.kind != nkSym or sfDispatcher notin t.sym.flags: - methodDef(s.sons[0].sym, false) + when false: + let t = lastSon(s.sons[0].sym.ast) + if t.kind != nkSym or sfDispatcher notin t.sym.flags: + methodDef(s.sons[0].sym, false) result = methodCall(s).PTransNode else: result = s.PTransNode diff --git a/compiler/types.nim b/compiler/types.nim index d5ec2972a..b237753ae 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1377,7 +1377,16 @@ proc compatibleEffectsAux(se, re: PNode): bool = return false result = true -proc compatibleEffects*(formal, actual: PType): bool = +type + EffectsCompat* = enum + efCompat + efRaisesDiffer + efRaisesUnknown + efTagsDiffer + efTagsUnknown + efLockLevelsDiffer + +proc compatibleEffects*(formal, actual: PType): EffectsCompat = # for proc type compatibility checking: assert formal.kind == tyProc and actual.kind == tyProc internalAssert formal.n.sons[0].kind == nkEffectList @@ -1393,18 +1402,21 @@ proc compatibleEffects*(formal, actual: PType): bool = # 'r.msgHandler = if isNil(msgHandler): defaultMsgHandler else: msgHandler' if not isNil(se) and se.kind != nkArgList: # spec requires some exception or tag, but we don't know anything: - if real.len == 0: return false - result = compatibleEffectsAux(se, real.sons[exceptionEffects]) - if not result: return + if real.len == 0: return efRaisesUnknown + let res = compatibleEffectsAux(se, real.sons[exceptionEffects]) + if not res: return efRaisesDiffer let st = spec.sons[tagEffects] if not isNil(st) and st.kind != nkArgList: # spec requires some exception or tag, but we don't know anything: - if real.len == 0: return false - result = compatibleEffectsAux(st, real.sons[tagEffects]) - if not result: return - result = formal.lockLevel.ord < 0 or - actual.lockLevel.ord <= formal.lockLevel.ord + if real.len == 0: return efTagsUnknown + let res = compatibleEffectsAux(st, real.sons[tagEffects]) + if not res: return efTagsDiffer + if formal.lockLevel.ord < 0 or + actual.lockLevel.ord <= formal.lockLevel.ord: + result = efCompat + else: + result = efLockLevelsDiffer proc isCompileTimeOnly*(t: PType): bool {.inline.} = result = t.kind in {tyTypeDesc, tyStatic} @@ -1508,6 +1520,21 @@ proc typeMismatch*(n: PNode, formal, actual: PType) = let named = typeToString(formal) let desc = typeToString(formal, preferDesc) let x = if named == desc: named else: named & " = " & desc - localError(n.info, errGenerated, msgKindToString(errTypeMismatch) & - typeToString(actual) & ") " & - `%`(msgKindToString(errButExpectedX), [x])) + var msg = msgKindToString(errTypeMismatch) & + typeToString(actual) & ") " & + msgKindToString(errButExpectedX) % [x] + + if formal.kind == tyProc and actual.kind == tyProc: + case compatibleEffects(formal, actual) + of efCompat: discard + of efRaisesDiffer: + msg.add "\n.raise effects differ" + of efRaisesUnknown: + msg.add "\n.raise effect is 'can raise any'" + of efTagsDiffer: + msg.add "\n.tag effects differ" + of efTagsUnknown: + msg.add "\n.tag effect is 'any tag allowed'" + of efLockLevelsDiffer: + msg.add "\nlock levels differ" + localError(n.info, errGenerated, msg) diff --git a/compiler/vm.nim b/compiler/vm.nim index 5d4784281..6a9545193 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1556,7 +1556,10 @@ proc myProcess(c: PPassContext, n: PNode): PNode = result = n oldErrorCount = msgs.gErrorCounter -const evalPass* = makePass(myOpen, nil, myProcess, myProcess) +proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = + myProcess(c, n) + +const evalPass* = makePass(myOpen, nil, myProcess, myClose) proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode, mode: TEvalMode): PNode = diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index a124d7537..f28a124d2 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -292,10 +292,15 @@ proc writeFreeList(a: MemRegion) = it, it.next, it.prev, it.size) it = it.next +const nimMaxHeap {.intdefine.} = 0 + proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = when not defined(emscripten): if not a.blockChunkSizeIncrease: let usedMem = a.currMem # - a.freeMem + when nimMaxHeap != 0: + if usedMem > nimMaxHeap * 1024 * 1024: + raiseOutOfMem() if usedMem < 64 * 1024: a.nextChunkSize = PageSize*4 else: diff --git a/tests/metatype/tautonotgeneric.nim b/tests/metatype/tautonotgeneric.nim index a55ae488e..f0d6932f9 100644 --- a/tests/metatype/tautonotgeneric.nim +++ b/tests/metatype/tautonotgeneric.nim @@ -1,15 +1,24 @@ discard """ - output: "wof!" + output: '''wof! +wof!''' """ # bug #1659 type Animal = ref object {.inheritable.} type Dog = ref object of Animal -method say(a: Animal): auto = "wat!" +method say(a: Animal): auto {.base.} = "wat!" method say(a: Dog): auto = "wof!" proc saySomething(a: Animal): auto = a.say() + +method ec(a: Animal): auto {.base.} = echo "wat!" +method ec(a: Dog): auto = echo "wof!" + +proc ech(a: Animal): auto = a.ec() + + var a = Dog() echo saySomething(a) +ech a diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim index 05fe97ad1..4ff2f65d2 100644 --- a/tests/misc/parsecomb.nim +++ b/tests/misc/parsecomb.nim @@ -29,10 +29,10 @@ method runInput[T, O](self: Parser[T, O], inp: Input[T]): Result[T, O] = # XXX: above needed for now, as without the `tmp` bit below, it compiles to invalid C. tmp(self)(inp) -method run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] = +proc run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] = self.runInput(Input[T](toks: toks, index: 0)) -method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] = +proc chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] = result = proc (inp: Input[T]): Result[T, O2] = let r = self.runInput(inp) case r.kind: @@ -41,7 +41,7 @@ method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2] of rkFailure: Result[T, O2](kind: rkFailure) -method skip[T](self: Input[T], n: int): Input[T] = +method skip[T](self: Input[T], n: int): Input[T] {.base.} = Input[T](toks: self.toks, index: self.index + n) proc pskip*[T](n: int): Parser[T, tuple[]] = @@ -69,11 +69,11 @@ proc `+`*[T, O](first: Parser[T, O], second: Parser[T, O]): Parser[T, O] = # end of primitives (definitions involving Parser(..)) -method map*[T, O1, O2](self: Parser[T, O1], p: proc (v: O1): O2): Parser[T, O2] = +proc map*[T, O1, O2](self: Parser[T, O1], p: proc (v: O1): O2): Parser[T, O2] = self.chain(proc (v: O1): Parser[T, O2] = unit[T, O2](p(v))) -method then*[T, O1, O2](self: Parser[T, O1], next: Parser[T, O2]): Parser[T, O2] = +proc then*[T, O1, O2](self: Parser[T, O1], next: Parser[T, O2]): Parser[T, O2] = self.chain(proc (v: O1): Parser[T, O2] = next) diff --git a/tools/nimsuggest/nimsuggest.nim b/tools/nimsuggest/nimsuggest.nim index 7891f73e5..57d343ae4 100644 --- a/tools/nimsuggest/nimsuggest.nim +++ b/tools/nimsuggest/nimsuggest.nim @@ -13,11 +13,11 @@ import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp # Do NOT import suggest. It will lead to wierd bugs with # suggestionResultHook, because suggest.nim is included by sigmatch. # So we import that one instead. -import compiler/options, compiler/commands, compiler/modules, compiler/sem, - compiler/passes, compiler/passaux, compiler/msgs, compiler/nimconf, - compiler/extccomp, compiler/condsyms, compiler/lists, - compiler/sigmatch, compiler/ast, compiler/scriptconfig, - compiler/idents, compiler/modulegraphs +import compiler / [options, commands, modules, sem, + passes, passaux, msgs, nimconf, + extccomp, condsyms, + sigmatch, ast, scriptconfig, + idents, modulegraphs, compilerlog, vm] when defined(windows): import winlean @@ -40,6 +40,7 @@ Options: --log enable verbose logging to nimsuggest.log file --v2 use version 2 of the protocol; more features and much faster + --refresh perform automatic refreshes to keep the analysis precise --tester implies --v2 and --stdin and outputs a line '""" & DummyEof & """' for the tester @@ -57,10 +58,11 @@ var gMode: Mode gEmitEof: bool # whether we write '!EOF!' dummy lines gLogging = false + gRefresh: bool const seps = {':', ';', ' ', '\t'} - Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline file.nim[;dirtyfile.nim]:line:col\n" & + Help = "usage: sug|con|def|use|dus|chk|mod|highlight|outline|known file.nim[;dirtyfile.nim]:line:col\n" & "type 'quit' to quit\n" & "type 'debug' to toggle debug mode on/off\n" & "type 'terse' to toggle terse mode on/off" @@ -68,12 +70,6 @@ const type EUnexpectedCommand = object of Exception -proc logStr(line: string) = - var f: File - if open(f, getHomeDir() / "nimsuggest.log", fmAppend): - f.writeLine(line) - f.close() - proc parseQuoted(cmd: string; outp: var string; start: int): int = var i = start i += skipWhitespace(cmd, i) @@ -150,7 +146,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; gTrackPos = newLineInfo(dirtyIdx, line, col) gErrorCounter = 0 if suggestVersion < 2: - usageSym = nil + graph.usageSym = nil if not isKnownFile: graph.compileProject(cache) if suggestVersion == 2 and gIdeCmd in {ideUse, ideDus} and @@ -163,7 +159,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: string, line, col: int; if gIdeCmd != ideMod: graph.compileProject(cache, modIdx) if gIdeCmd in {ideUse, ideDus}: - let u = if suggestVersion >= 2: graph.symFromInfo(gTrackPos) else: usageSym + let u = if suggestVersion >= 2: graph.symFromInfo(gTrackPos) else: graph.usageSym if u != nil: listUsages(u) else: @@ -180,7 +176,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode; dirtyfile = args[3].getStr(nil) execute(cmd, file, dirtyfile, int(line), int(column), graph, cache) -proc returnEpc(socket: var Socket, uid: BiggestInt, s: SexpNode|string, +proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string, return_symbol = "return") = let response = $convertSexp([newSSymbol(return_symbol), uid, s]) socket.send(toHex(len(response), 6)) @@ -198,7 +194,7 @@ template sendEpc(results: typed, tdef, hook: untyped) = let res = sexp(results) if gLogging: logStr($res) - returnEPC(client, uid, res) + returnEpc(client, uid, res) template checkSanity(client, sizeHex, size, messageBuffer: typed) = if client.recv(sizeHex, 6) != 6: @@ -208,6 +204,48 @@ template checkSanity(client, sizeHex, size, messageBuffer: typed) = if client.recv(messageBuffer, size) != size: raise newException(ValueError, "didn't get all the bytes") +var + requests: Channel[string] + results: Channel[Suggest] + +proc toStdout() {.gcsafe.} = + while true: + let res = results.recv() + case res.section + of ideNone: break + of ideChk: echo res.doc + of ideKnown: echo res.quality == 1 + else: echo res + +proc toSocket(stdoutSocket: Socket) {.gcsafe.} = + while true: + let res = results.recv() + case res.section + of ideNone: break + of ideChk: stdoutSocket.send(res.doc & "\c\L") + of ideKnown: stdoutSocket.send($(res.quality == 1) & "\c\L") + else: stdoutSocket.send($res & "\c\L") + +proc toEpc(client: Socket; uid: BiggestInt) {.gcsafe.} = + var list = newSList() + while true: + let res = results.recv() + case res.section + of ideNone: break + of ideChk: + list.add sexp(res.doc) + of ideKnown: + list.add sexp(res.quality == 1) + else: + list.add sexp(res) + returnEpc(client, uid, list) + +proc writelnToChannel(line: string) = + results.send(Suggest(section: ideChk, doc: line)) + +proc sugResultHook(s: Suggest) = + results.send(s) + template setVerbosity(level: typed) = gVerbosity = level gNotes = NotesVerbosity[gVerbosity] @@ -217,16 +255,128 @@ proc connectToNextFreePort(server: Socket, host: string): Port = let (_, port) = server.getLocalAddr result = port -proc parseCmdLine(cmd: string; graph: ModuleGraph; cache: IdentCache) = +type + ThreadParams = tuple[port: Port; address: string] + +proc replStdin(x: ThreadParams) {.thread.} = + if gEmitEof: + echo DummyEof + while true: + let line = readLine(stdin) + requests.send line + toStdout() + echo DummyEof + flushFile(stdout) + else: + echo Help + var line = "" + while readLineFromStdin("> ", line): + requests.send line + toStdout() + echo "" + flushFile(stdout) + +proc replTcp(x: ThreadParams) {.thread.} = + var server = newSocket() + server.bindAddr(x.port, x.address) + var inp = "".TaintedString + server.listen() + while true: + var stdoutSocket = newSocket() + accept(server, stdoutSocket) + + stdoutSocket.readLine(inp) + requests.send inp + toSocket(stdoutSocket) + stdoutSocket.send("\c\L") + stdoutSocket.close() + +proc argsToStr(x: SexpNode): string = + if x.kind != SList: return x.getStr + doAssert x.kind == SList + doAssert x.len >= 4 + let file = x[0].getStr + let line = x[1].getNum + let col = x[2].getNum + let dirty = x[3].getStr + result = x[0].getStr.escape + if dirty.len > 0: + result.add ';' + result.add dirty.escape + result.add ':' + result.add line + result.add ':' + result.add col + +proc replEpc(x: ThreadParams) {.thread.} = + var server = newSocket() + let port = connectToNextFreePort(server, "localhost") + server.listen() + echo port + + var client = newSocket() + # Wait for connection + accept(server, client) + while true: + var + sizeHex = "" + size = 0 + messageBuffer = "" + checkSanity(client, sizeHex, size, messageBuffer) + let + message = parseSexp($messageBuffer) + epcApi = message[0].getSymbol + case epcApi + of "call": + let + uid = message[1].getNum + args = message[3] + + gIdeCmd = parseIdeCmd(message[2].getSymbol) + case gIdeCmd + of ideChk: + setVerbosity(1) + # Use full path because other emacs plugins depends it + gListFullPaths = true + incl(gGlobalOptions, optIdeDebug) + of ideSug, ideCon, ideDef, ideUse, ideDus, ideOutline, ideHighlight: + setVerbosity(0) + else: discard + let cmd = $gIdeCmd & " " & args.argsToStr + if gLogging: + logStr "MSG CMD: " & cmd + requests.send(cmd) + toEpc(client, uid) + of "methods": + returnEpc(client, message[1].getNum, listEpc()) + of "epc-error": + # an unhandled exception forces down the whole process anyway, so we + # use 'quit' here instead of 'raise' + quit("recieved epc error: " & $messageBuffer) + else: + let errMessage = case epcApi + of "return", "return-error": + "no return expected" + else: + "unexpected call: " & epcAPI + quit errMessage + +proc execCmd(cmd: string; graph: ModuleGraph; cache: IdentCache) = + template sentinel() = + # send sentinel for the input reading thread: + results.send(Suggest(section: ideNone)) + template toggle(sw) = if sw in gGlobalOptions: excl(gGlobalOptions, sw) else: incl(gGlobalOptions, sw) + sentinel() return template err() = echo Help + sentinel() return var opc = "" @@ -246,6 +396,7 @@ proc parseCmdLine(cmd: string; graph: ModuleGraph; cache: IdentCache) = of "quit": quit() of "debug": toggle optIdeDebug of "terse": toggle optIdeTerse + of "known": gIdeCmd = ideKnown else: err() var dirtyfile = "" var orig = "" @@ -259,25 +410,74 @@ proc parseCmdLine(cmd: string; graph: ModuleGraph; cache: IdentCache) = i += skipWhile(cmd, seps, i) i += parseInt(cmd, col, i) - execute(gIdeCmd, orig, dirtyfile, line, col-1, graph, cache) + if gIdeCmd == ideKnown: + results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(orig)))) + else: + execute(gIdeCmd, orig, dirtyfile, line, col-1, graph, cache) + sentinel() + +proc recompileFullProject(graph: ModuleGraph; cache: IdentCache) = + echo "recompiling full project" + resetSystemArtifacts() + vm.globalCtx = nil + graph.resetAllModules() + GC_fullcollect() + compileProject(graph, cache) + echo "recompiled!" + +proc mainThread(graph: ModuleGraph; cache: IdentCache) = + if gLogging: + for it in searchPaths: + logStr(it) + + proc wrHook(line: string) {.closure.} = + if gMode == mepc: + if gLogging: logStr(line) + else: + writelnToChannel(line) + + msgs.writelnHook = wrHook + suggestionResultHook = sugResultHook + graph.doStopCompile = proc (): bool = requests.peek() > 0 + var idle = 0 + while true: + let (hasData, req) = requests.tryRecv() + if hasData: + msgs.writelnHook = wrHook + suggestionResultHook = sugResultHook + + execCmd(req, graph, cache) + idle = 0 + else: + os.sleep 250 + idle += 1 + if idle == 20 and gRefresh: + # we use some nimsuggest activity to enable a lazy recompile: + gIdeCmd = ideChk + msgs.writelnHook = proc (s: string) = discard + suggestionResultHook = proc (s: Suggest) = discard + recompileFullProject(graph, cache) + +var + inputThread: Thread[ThreadParams] -proc serveStdin(graph: ModuleGraph; cache: IdentCache) = +proc serveStdin(graph: ModuleGraph; cache: IdentCache) {.deprecated.} = if gEmitEof: echo DummyEof while true: let line = readLine(stdin) - parseCmdLine line, graph, cache + execCmd line, graph, cache echo DummyEof flushFile(stdout) else: echo Help var line = "" while readLineFromStdin("> ", line): - parseCmdLine line, graph, cache + execCmd line, graph, cache echo "" flushFile(stdout) -proc serveTcp(graph: ModuleGraph; cache: IdentCache) = +proc serveTcp(graph: ModuleGraph; cache: IdentCache) {.deprecated.} = var server = newSocket() server.bindAddr(gPort, gAddress) var inp = "".TaintedString @@ -291,12 +491,12 @@ proc serveTcp(graph: ModuleGraph; cache: IdentCache) = accept(server, stdoutSocket) stdoutSocket.readLine(inp) - parseCmdLine inp.string, graph, cache + execCmd inp.string, graph, cache stdoutSocket.send("\c\L") stdoutSocket.close() - -proc serveEpc(server: Socket; graph: ModuleGraph; cache: IdentCache) = + +proc serveEpc(server: Socket; graph: ModuleGraph; cache: IdentCache) {.deprecated.} = var client = newSocket() # Wait for connection accept(server, client) @@ -355,33 +555,49 @@ proc mainCommand(graph: ModuleGraph; cache: IdentCache) = incl gGlobalOptions, optCaasEnabled isServing = true wantMainModule() - appendStr(searchPaths, options.libpath) + add(searchPaths, options.libpath) #if gProjectFull.len != 0: # current path is always looked first for modules # prependStr(searchPaths, gProjectPath) # do not stop after the first error: msgs.gErrorMax = high(int) + # compile the project before showing any input so that we already + # can answer questions right away: + compileProject(graph, cache) + + open(requests) + open(results) case gMode - of mstdin: - compileProject(graph, cache) - #modules.gFuzzyGraphChecking = false - serveStdin(graph, cache) - of mtcp: - # until somebody accepted the connection, produce no output (logging is too - # slow for big projects): - msgs.writelnHook = proc (msg: string) = discard - compileProject(graph, cache) - #modules.gFuzzyGraphChecking = false - serveTcp(graph, cache) - of mepc: - var server = newSocket() - let port = connectToNextFreePort(server, "localhost") - server.listen() - echo port - compileProject(graph, cache) - serveEpc(server, graph, cache) + of mstdin: createThread(inputThread, replStdin, (gPort, gAddress)) + of mtcp: createThread(inputThread, replTcp, (gPort, gAddress)) + of mepc: createThread(inputThread, replEpc, (gPort, gAddress)) + mainThread(graph, cache) + joinThread(inputThread) + close(requests) + close(results) + + when false: + case gMode + of mstdin: + compileProject(graph, cache) + #modules.gFuzzyGraphChecking = false + serveStdin(graph, cache) + of mtcp: + # until somebody accepted the connection, produce no output (logging is too + # slow for big projects): + msgs.writelnHook = proc (msg: string) = discard + compileProject(graph, cache) + #modules.gFuzzyGraphChecking = false + serveTcp(graph, cache) + of mepc: + var server = newSocket() + let port = connectToNextFreePort(server, "localhost") + server.listen() + echo port + compileProject(graph, cache) + serveEpc(server, graph, cache) proc processCmdLine*(pass: TCmdLinePass, cmd: string) = var p = parseopt.initOptParser(cmd) @@ -401,16 +617,14 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = of "epc": gMode = mepc gVerbosity = 0 # Port number gotta be first. - of "debug": - incl(gGlobalOptions, optIdeDebug) - of "v2": - suggestVersion = 2 + of "debug": incl(gGlobalOptions, optIdeDebug) + of "v2": suggestVersion = 2 of "tester": suggestVersion = 2 gMode = mstdin gEmitEof = true - of "log": - gLogging = true + of "log": gLogging = true + of "refresh": gRefresh = true else: processSwitch(pass, p) of cmdArgument: options.gProjectName = unixToNativePath(p.key) diff --git a/tools/nimsuggest/nimsuggest.nim.cfg b/tools/nimsuggest/nimsuggest.nim.cfg index 949bd18e8..2e14a4dd3 100644 --- a/tools/nimsuggest/nimsuggest.nim.cfg +++ b/tools/nimsuggest/nimsuggest.nim.cfg @@ -8,9 +8,18 @@ path:"$lib/packages/docutils" define:useStdoutAsStdmsg define:nimsuggest +# die when nimsuggest uses more than 4GB: +@if cpu32: + define:"nimMaxHeap=2000" +@else: + define:"nimMaxHeap=4000" +@end #cs:partial #define:useNodeIds #define:booting #define:noDocgen --path:"$nim" +--threads:on +--noNimblePath +--path:"../../compiler" diff --git a/tools/nimsuggest/sexp.nim b/tools/nimsuggest/sexp.nim index cf08111d7..61bba10bc 100644 --- a/tools/nimsuggest/sexp.nim +++ b/tools/nimsuggest/sexp.nim @@ -561,16 +561,16 @@ proc toPretty(result: var string, node: SexpNode, indent = 2, ml = true, result.add(escapeJson(node.str)) of SInt: if lstArr: result.indent(currIndent) - result.add($node.num) + result.add(node.num) of SFloat: if lstArr: result.indent(currIndent) - result.add($node.fnum) + result.add(node.fnum) of SNil: if lstArr: result.indent(currIndent) result.add("nil") of SSymbol: if lstArr: result.indent(currIndent) - result.add($node.symbol) + result.add(node.symbol) of SList: if lstArr: result.indent(currIndent) if len(node.elems) != 0: diff --git a/tools/nimsuggest/tester.nim b/tools/nimsuggest/tester.nim index 156d3ddb9..c4542e089 100644 --- a/tools/nimsuggest/tester.nim +++ b/tools/nimsuggest/tester.nim @@ -3,7 +3,7 @@ # before 'nimsuggest' is invoked to ensure this token doesn't make a # crucial difference for Nim's parser. -import os, osproc, strutils, streams, re +import os, osproc, strutils, streams, re, sexp, net type Test = object @@ -17,7 +17,7 @@ const template tpath(): untyped = getAppDir() / "tests" -proc parseTest(filename: string): Test = +proc parseTest(filename: string; epcMode=false): Test = const cursorMarker = "#[!]#" let nimsug = curDir & addFileExt("nimsuggest", ExeExt) result.dest = getTempDir() / extractFilename(filename) @@ -31,7 +31,10 @@ proc parseTest(filename: string): Test = for x in lines(filename): let marker = x.find(cursorMarker)+1 if marker > 0: - markers.add "\"" & filename & "\";\"" & result.dest & "\":" & $i & ":" & $marker + if epcMode: + markers.add "(\"" & filename & "\" " & $i & " " & $marker & " \"" & result.dest & "\")" + else: + markers.add "\"" & filename & "\";\"" & result.dest & "\":" & $i & ":" & $marker tmp.writeLine x.replace(cursorMarker, "") else: tmp.writeLine x @@ -133,6 +136,126 @@ proc smartCompare(pattern, x: string): bool = if pattern.contains('*'): result = match(x, re(escapeRe(pattern).replace("\\x2A","(.*)"), {})) +proc sendEpcStr(socket: Socket; cmd: string) = + let s = cmd.find(' ') + doAssert s > 0 + var args = cmd.substr(s+1) + if not args.startsWith("("): args = escapeJson(args) + let c = "(call 567 " & cmd.substr(0, s) & args & ")" + socket.send toHex(c.len, 6) + socket.send c + +proc recvEpc(socket: Socket): string = + var L = newStringOfCap(6) + if socket.recv(L, 6) != 6: + raise newException(ValueError, "recv A failed") + let x = parseHexInt(L) + result = newString(x) + if socket.recv(result, x) != x: + raise newException(ValueError, "recv B failed") + +proc sexpToAnswer(s: SexpNode): string = + result = "" + doAssert s.kind == SList + doAssert s.len >= 3 + let m = s[2] + if m.kind != SList: + echo s + doAssert m.kind == SList + for a in m: + doAssert a.kind == SList + var first = true + #s.section, + #s.symkind, + #s.qualifiedPath.map(newSString), + #s.filePath, + #s.forth, + #s.line, + #s.column, + #s.doc + if a.len >= 8: + let section = a[0].getStr + let symk = a[1].getStr + let qp = a[2] + let file = a[3].getStr + let typ = a[4].getStr + let line = a[5].getNum + let col = a[6].getNum + let doc = a[7].getStr.escape + result.add section + result.add '\t' + result.add symk + result.add '\t' + var i = 0 + for aa in qp: + if i > 0: result.add '.' + result.add aa.getStr + inc i + result.add '\t' + result.add typ + result.add '\t' + result.add file + result.add '\t' + result.add line + result.add '\t' + result.add col + result.add '\t' + result.add doc + result.add '\t' + # for now Nim EPC does not return the quality + result.add "100" + result.add '\L' + +proc doReport(filename, answer, resp: string; report: var string) = + if resp != answer and not smartCompare(resp, answer): + report.add "\nTest failed: " & filename + var hasDiff = false + for i in 0..min(resp.len-1, answer.len-1): + if resp[i] != answer[i]: + report.add "\n Expected: " & resp.substr(i) + report.add "\n But got: " & answer.substr(i) + hasDiff = true + break + if not hasDiff: + report.add "\n Expected: " & resp + report.add "\n But got: " & answer + +proc runEpcTest(filename: string): int = + let s = parseTest(filename, true) + for cmd in s.startup: + if not runCmd(cmd, s.dest): + quit "invalid command: " & cmd + let epccmd = s.cmd.replace("--tester", "--epc --v2") + let cl = parseCmdLine(epccmd) + var p = startProcess(command=cl[0], args=cl[1 .. ^1], + options={poStdErrToStdOut, poUsePath, + poInteractive, poDemon}) + let outp = p.outputStream + let inp = p.inputStream + var report = "" + var a = newStringOfCap(120) + try: + # read the port number: + if outp.readLine(a): + let port = parseInt(a) + var socket = newSocket() + socket.connect("localhost", Port(port)) + for req, resp in items(s.script): + if not runCmd(req, s.dest): + socket.sendEpcStr(req) + let sx = parseSexp(socket.recvEpc()) + if not req.startsWith("mod "): + let answer = sexpToAnswer(sx) + doReport(filename, answer, resp, report) + else: + raise newException(ValueError, "cannot read port number") + finally: + close(p) + if report.len > 0: + echo "==== EPC ========================================" + echo report + result = report.len + proc runTest(filename: string): int = let s = parseTest filename for cmd in s.startup: @@ -159,31 +282,28 @@ proc runTest(filename: string): int = if a == DummyEof: break answer.add a answer.add '\L' - if resp != answer and not smartCompare(resp, answer): - report.add "\nTest failed: " & filename - var hasDiff = false - for i in 0..min(resp.len-1, answer.len-1): - if resp[i] != answer[i]: - report.add "\n Expected: " & resp.substr(i) - report.add "\n But got: " & answer.substr(i) - hasDiff = true - break - if not hasDiff: - report.add "\n Expected: " & resp - report.add "\n But got: " & answer + doReport(filename, answer, resp, report) finally: inp.writeLine("quit") inp.flush() close(p) if report.len > 0: + echo "==== STDIN ======================================" echo report result = report.len proc main() = var failures = 0 - for x in walkFiles(getAppDir() / "tests/t*.nim"): - echo "Test ", x - failures += runTest(expandFilename(x)) + when false: + let x = getAppDir() / "tests/twithin_macro.nim" + let xx = expandFilename x + failures += runEpcTest(xx) + else: + for x in walkFiles(getAppDir() / "tests/t*.nim"): + echo "Test ", x + let xx = expandFilename x + failures += runTest(xx) + failures += runEpcTest(xx) if failures > 0: quit 1 diff --git a/tools/nimsuggest/tests/twithin_macro.nim b/tools/nimsuggest/tests/twithin_macro.nim index d67984707..7392dd605 100644 --- a/tools/nimsuggest/tests/twithin_macro.nim +++ b/tools/nimsuggest/tests/twithin_macro.nim @@ -206,8 +206,8 @@ $nimsuggest --tester $file >sug $1 sug;;skField;;name;;string;;$file;;166;;6;;"";;100 sug;;skField;;age;;int;;$file;;167;;6;;"";;100 -sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int{.noSideEffect, gcsafe, locks: 0.};;$file;;169;;9;;"";;100 +sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int;;$file;;169;;9;;"";;100 sug;;skMacro;;twithin_macro.class;;proc (head: untyped, body: untyped): untyped{.gcsafe, locks: <unknown>.};;$file;;4;;6;;"Iterates over the children of the NimNode ``n``.";;100 -sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string{.noSideEffect, gcsafe, locks: 0.};;$file;;168;;9;;"";;100 -sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string{.noSideEffect, gcsafe, locks: 0.};;$file;;184;;9;;"";;100* +sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string;;$file;;168;;9;;"";;100 +sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string;;$file;;184;;9;;"";;100* """ |