diff options
author | Michael Voronin <survivor.mail@gmail.com> | 2018-04-23 11:35:56 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-23 11:35:56 +0300 |
commit | 17d3c6f3f32649c7cc295e964e2fcc3af99eac20 (patch) | |
tree | d660abc250a7ded975908cac6e40a4c9a345efd9 | |
parent | 04df7f147c6e08b28113173328d7e03fce381af9 (diff) | |
parent | a8b70c550083e432f0028e6582c97d8310a48c8a (diff) | |
download | Nim-17d3c6f3f32649c7cc295e964e2fcc3af99eac20.tar.gz |
Merge pull request #1 from nim-lang/devel
pull-23-04-18
81 files changed, 1074 insertions, 1398 deletions
diff --git a/changelog.md b/changelog.md index f39dc2678..a3332789a 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,10 @@ to deal with! - Indexing into a ``cstring`` for the JS target is now mapped to ``charCodeAt``. +- Assignments that would "slice" an object into its supertype are not prevented + at runtime. Use ``ref object`` with inheritance rather than ``object`` with + inheritance to prevent this issue. + #### Breaking changes in the standard library @@ -27,6 +31,7 @@ - ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type to ``times.Duration`` in order to support higher time resolutions. The proc is no longer deprecated. +- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``. #### Breaking changes in the compiler diff --git a/compiler/ast.nim b/compiler/ast.nim index da7e828f2..4a0a9a20b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1042,9 +1042,9 @@ proc newNode*(kind: TNodeKind): PNode = new(result) result.kind = kind #result.info = UnknownLineInfo() inlined: - result.info.fileIndex = int32(-1) + result.info.fileIndex = InvalidFileIdx result.info.col = int16(-1) - result.info.line = int16(-1) + result.info.line = uint16(0) when defined(useNodeIds): result.id = gNodeId if result.id == nodeIdToDebug: @@ -1116,13 +1116,13 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} = s.typ = t result = s -template fileIdx*(c: PSym): int32 = +template fileIdx*(c: PSym): FileIndex = # XXX: this should be used only on module symbols - c.position.int32 + c.position.FileIndex template filename*(c: PSym): string = # XXX: this should be used only on module symbols - c.position.int32.toFilename + c.position.FileIndex.toFilename proc appendToModule*(m: PSym, n: PNode) = ## The compiler will use this internally to add nodes that will be diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index 6afd1bd78..e38732877 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -72,24 +72,25 @@ proc bitSetContains(x, y: TBitSet): bool = result = true # Number of set bits for all values of int8 -const populationCount: array[low(int8)..high(int8), int8] = [ - 1.int8, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, - 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7 -] +const populationCount: array[low(int8)..high(int8), int8] = block: + var arr: array[low(int8)..high(int8), int8] + + proc countSetBits(x: int8): int8 = + return + ( x and 0b00000001'i8) + + ((x and 0b00000010'i8) shr 1) + + ((x and 0b00000100'i8) shr 2) + + ((x and 0b00001000'i8) shr 3) + + ((x and 0b00010000'i8) shr 4) + + ((x and 0b00100000'i8) shr 5) + + ((x and 0b01000000'i8) shr 6) + + ((x and 0b10000000'i8) shr 7) + + + for it in low(int8)..high(int8): + arr[it] = countSetBits(it) + + arr proc bitSetCard(x: TBitSet): BiggestInt = for it in x: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7e3c2632a..461a86298 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2023,7 +2023,7 @@ template genStmtListExprImpl(exprOrStmt) {.dirty.} = let theMacro = it[0].sym add p.s(cpsStmts), initFrameNoDebug(p, frameName, makeCString theMacro.name.s, - theMacro.info.quotedFilename, it.info.line) + theMacro.info.quotedFilename, it.info.line.int) else: genStmts(p, it) if n.len > 0: exprOrStmt diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 4d3cabd3a..9e1f9349f 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -223,7 +223,7 @@ proc genLineDir(p: BProc, t: PNode) = line.rope, makeCString(toFilename(tt.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and - (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0: + (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX: if freshLineInfo(p, tt.info): linefmt(p, cpsStmts, "nimln_($1, $2);$n", line.rope, tt.info.quotedFilename) @@ -678,7 +678,7 @@ proc generateHeaders(m: BModule) = proc openNamespaceNim(): Rope = result.add("namespace Nim {" & tnl) - + proc closeNamespaceNim(): Rope = result.add("}" & tnl) @@ -1090,7 +1090,7 @@ proc genMainProc(m: BModule) = appcg(m, m.s[cfsProcs], nimMain, [m.g.mainModInit, initStackBottomCall, rope(m.labels)]) if optNoMain notin gGlobalOptions: - if useNimNamespace: + if useNimNamespace: m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl appcg(m, m.s[cfsProcs], otherMain, []) @@ -1202,7 +1202,7 @@ proc genModule(m: BModule, cfile: Cfile): Rope = add(result, genSectionStart(i)) add(result, m.s[i]) add(result, genSectionEnd(i)) - if useNimNamespace and i == cfsHeaders: result.add openNamespaceNim() + if useNimNamespace and i == cfsHeaders: result.add openNamespaceNim() add(result, m.s[cfsInitProc]) if useNimNamespace: result.add closeNamespaceNim() @@ -1301,7 +1301,7 @@ proc resetCgenModules*(g: BModuleList) = for m in cgenModules(g): resetModule(m) proc rawNewModule(g: BModuleList; module: PSym): BModule = - result = rawNewModule(g, module, module.position.int32.toFullPath) + result = rawNewModule(g, module, module.position.FileIndex.toFullPath) proc newModule(g: BModuleList; module: PSym): BModule = # we should create only one cgen module for each module sym @@ -1311,7 +1311,7 @@ proc newModule(g: BModuleList; module: PSym): BModule = if (optDeadCodeElim in gGlobalOptions): if (sfDeadCodeElim in module.flags): - internalError("added pending module twice: " & module.filename) + internalError("added pending module twice: " & toFilename(FileIndex module.position)) template injectG(config) {.dirty.} = if graph.backend == nil: diff --git a/compiler/commands.nim b/compiler/commands.nim index 820cb7e1a..e1dc1aacf 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -77,6 +77,14 @@ proc writeAdvancedUsage(pass: TCmdLinePass) = {msgStdout}) msgQuit(0) +proc writeFullhelp(pass: TCmdLinePass) = + if pass == passCmd1: + msgWriteln(`%`(HelpMessage, [VersionAsString, + platform.OS[platform.hostOS].name, + CPU[platform.hostCPU].name]) & Usage & AdvancedUsage, + {msgStdout}) + msgQuit(0) + proc writeVersionInfo(pass: TCmdLinePass) = if pass == passCmd1: msgWriteln(`%`(HelpMessage, [VersionAsString, @@ -311,7 +319,7 @@ proc trackDirty(arg: string, info: TLineInfo) = localError(info, errInvalidNumber, a[2]) let dirtyOriginalIdx = a[1].fileInfoIdx - if dirtyOriginalIdx >= 0: + if dirtyOriginalIdx.int32 >= 0: msgs.setDirtyFile(dirtyOriginalIdx, a[0]) gTrackPos = newLineInfo(dirtyOriginalIdx, line, column) @@ -611,6 +619,9 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "advanced": expectNoArg(switch, arg, pass, info) writeAdvancedUsage(pass) + of "fullhelp": + expectNoArg(switch, arg, pass, info) + writeFullhelp(pass) of "help", "h": expectNoArg(switch, arg, pass, info) helpOnError(pass) @@ -702,7 +713,6 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectNoArg(switch, arg, pass, info) useNimNamespace = true defineSymbol("cppCompileToNamespace") - else: if strutils.find(switch, '.') >= 0: options.setConfigVar(switch, arg) else: invalidCmdLineOption(pass, switch, info) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 74fb305ac..6f3dcde8b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -544,7 +544,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) - result = %{ "name": %name, "type": %($k), "line": %n.info.line, + result = %{ "name": %name, "type": %($k), "line": %n.info.line.int, "col": %n.info.col} if comm != nil and comm != "": result["description"] = %comm @@ -618,7 +618,7 @@ proc generateJson*(d: PDoc, n: PNode) = of nkCommentStmt: if n.comment != nil and startsWith(n.comment, "##"): let stripped = n.comment.substr(2).strip - d.add %{ "comment": %stripped, "line": %n.info.line, + d.add %{ "comment": %stripped, "line": %n.info.line.int, "col": %n.info.col } of nkProcDef: when useEffectSystem: documentRaises(n) @@ -790,7 +790,7 @@ proc writeOutputJson*(d: PDoc, filename, outExt: string, discard "fixme: error report" proc commandDoc*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true @@ -840,7 +840,7 @@ proc commandRst2TeX*() = commandRstAux(gProjectFull, TexExt) proc commandJson*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true @@ -855,7 +855,7 @@ proc commandJson*() = writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false) proc commandTags*() = - var ast = parseFile(gProjectMainIdx, newIdentCache()) + var ast = parseFile(gProjectMainIdx.FileIndex, newIdentCache()) if ast == nil: return var d = newDocumentor(gProjectFull, options.gConfigVars) d.hasToc = true diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 704ff819c..fbb7eb2e6 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -106,8 +106,9 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = for i in 1 .. genericParams: result.addSon n.sons[givenRegularParams + i] +# to prevent endless recursion in template instantiation +const evalTemplateLimit* = 1000 var evalTemplateCounter* = 0 - # to prevent endless recursion in templates instantiation proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = when true: @@ -133,7 +134,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode = inc(evalTemplateCounter) - if evalTemplateCounter > 100: + if evalTemplateCounter > evalTemplateLimit: globalError(n.info, errTemplateInstantiationTooNested) result = n diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index ca9a3a801..a1ba9113c 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -35,7 +35,7 @@ const proc newLine(p: var TTmplParser) = llStreamWrite(p.outp, repeat(')', p.emitPar)) p.emitPar = 0 - if p.info.line > int16(1): llStreamWrite(p.outp, "\n") + if p.info.line > uint16(1): llStreamWrite(p.outp, "\n") if p.pendingExprLine: llStreamWrite(p.outp, spaces(2)) p.pendingExprLine = false @@ -212,9 +212,9 @@ proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream = p.x = newStringOfCap(120) # do not process the first line which contains the directive: if llStreamReadLine(p.inp, p.x): - p.info.line = p.info.line + int16(1) + p.info.line = p.info.line + 1'u16 while llStreamReadLine(p.inp, p.x): - p.info.line = p.info.line + int16(1) + p.info.line = p.info.line + 1'u16 parseLine(p) newLine(p) result = p.outp diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 2bffaa173..c4288c362 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -44,7 +44,7 @@ proc applyPatterns(c: PContext, n: PNode): PNode = assert x.kind in {nkStmtList, nkCall} # better be safe than sorry, so check evalTemplateCounter too: inc(evalTemplateCounter) - if evalTemplateCounter > 100: + if evalTemplateCounter > evalTemplateLimit: globalError(n.info, errTemplateInstantiationTooNested) # deactivate this pattern: c.patterns[i] = nil diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index da5267b93..2ae3426ab 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -8,7 +8,6 @@ # # This is the JavaScript code generator. -# Also a PHP code generator. ;-) discard """ The JS code generator contains only 2 tricks: @@ -38,11 +37,8 @@ import from modulegraphs import ModuleGraph type - TTarget = enum - targetJS, targetPHP TJSGen = object of TPassContext module: PSym - target: TTarget sigConflicts: CountTable[SigHash] BModule = ref TJSGen @@ -92,16 +88,12 @@ type module: BModule g: PGlobals beforeRetNeeded: bool - target: TTarget # duplicated here for faster dispatching unique: int # for temp identifier generation blocks: seq[TBlock] extraIndent: int up: PProc # up the call chain; required for closure support declaredGlobals: IntSet -template `|`(a, b: untyped): untyped {.dirty.} = - (if p.target == targetJS: a else: b) - var indent = "\t".rope proc indentLine(p: PProc, r: Rope): Rope = @@ -157,11 +149,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode, module: module, procDef: procDef, g: globals, - target: module.target, extraIndent: int(procDef != nil)) if procDef != nil: result.prc = procDef.sons[namePos].sym - if result.target == targetPHP: - result.declaredGlobals = initIntSet() proc declareGlobal(p: PProc; id: int; r: Rope) = if p.prc != nil and not p.declaredGlobals.containsOrIncl(id): @@ -208,8 +197,7 @@ proc mapType(typ: PType): TJSTypeKind = of tyUnused, tyOptAsRef: internalError("mapType") proc mapType(p: PProc; typ: PType): TJSTypeKind = - if p.target == targetPHP: result = etyObject - else: result = mapType(typ) + result = mapType(typ) proc mangleName(m: BModule, s: PSym): Rope = proc validJsName(name: string): bool = @@ -236,7 +224,7 @@ proc mangleName(m: BModule, s: PSym): Rope = if result == nil: if s.kind == skField and s.name.s.validJsName: result = rope(s.name.s) - elif m.target == targetJS or s.kind == skTemp: + elif s.kind == skTemp: result = rope(mangle(s.name.s)) else: var x = newStringOfCap(s.name.s.len) @@ -314,7 +302,7 @@ proc useMagic(p: PProc, name: string) = proc isSimpleExpr(p: PProc; n: PNode): bool = # calls all the way down --> can stay expression based if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or - (p.target == targetJS and n.kind in {nkObjConstr, nkBracket, nkCurly}): + (n.kind in {nkObjConstr, nkBracket, nkCurly}): for c in n: if not p.isSimpleExpr(c): return false result = true @@ -323,12 +311,9 @@ proc isSimpleExpr(p: PProc; n: PNode): bool = proc getTemp(p: PProc, defineInLocals: bool = true): Rope = inc(p.unique) - if p.target == targetJS: - result = "Tmp$1" % [rope(p.unique)] - if defineInLocals: - add(p.locals, p.indentLine("var $1;$n" % [result])) - else: - result = "$$Tmp$1" % [rope(p.unique)] + result = "Tmp$1" % [rope(p.unique)] + if defineInLocals: + add(p.locals, p.indentLine("var $1;$n" % [result])) proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) = assert r.kind == resNone @@ -477,15 +462,9 @@ proc unsignedTrimmerJS(size: BiggestInt): Rope = of 4: rope">>> 0" else: rope"" -proc unsignedTrimmerPHP(size: BiggestInt): Rope = - case size - of 1: rope"& 0xff" - of 2: rope"& 0xffff" - of 4: rope"& 0xffffffff" - else: rope"" template unsignedTrimmer(size: BiggestInt): Rope = - size.unsignedTrimmerJS | size.unsignedTrimmerPHP + size.unsignedTrimmerJS proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign = false) = @@ -533,46 +512,18 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mMulU: binaryUintExpr(p, n, r, "*") of mDivU: binaryUintExpr(p, n, r, "/") of mDivI: - if p.target == targetPHP: - var x, y: TCompRes - gen(p, n.sons[1], x) - gen(p, n.sons[2], y) - r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc] - else: - arithAux(p, n, r, op) + arithAux(p, n, r, op) of mModI: - if p.target == targetPHP: - var x, y: TCompRes - gen(p, n.sons[1], x) - gen(p, n.sons[2], y) - r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc] - else: - arithAux(p, n, r, op) + arithAux(p, n, r, op) of mShrI: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) - if p.target == targetPHP: - # XXX prevent multi evaluations - r.res = "(($1 $2) >= 0) ? (($1 $2) >> $3) : ((($1 $2) & 0x7fffffff) >> $3) | (0x40000000 >> ($3 - 1))" % [x.rdLoc, trimmer, y.rdLoc] - else: - r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc] + r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc] of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: - if p.target == targetPHP: - if op == mEnumToStr: - var x: TCompRes - gen(p, n.sons[1], x) - r.res = "$#[$#]" % [genEnumInfoPHP(p, n.sons[1].typ), x.rdLoc] - elif op == mCharToStr: - var x: TCompRes - gen(p, n.sons[1], x) - r.res = "chr($#)" % [x.rdLoc] - else: - gen(p, n.sons[1], r) - else: - arithAux(p, n, r, op) + arithAux(p, n, r, op) else: arithAux(p, n, r, op) r.kind = resExpr @@ -591,7 +542,7 @@ proc genLineDir(p: PProc, n: PNode) = useMagic(p, "endb") lineF(p, "endb($1);$n", [rope(line)]) elif hasFrameInfo(p): - lineF(p, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)]) + lineF(p, "F.line = $1;$n", [rope(line)]) proc genWhileStmt(p: PProc, n: PNode) = var @@ -604,12 +555,12 @@ proc genWhileStmt(p: PProc, n: PNode) = p.blocks[length].id = -p.unique p.blocks[length].isLoop = true let labl = p.unique.rope - lineF(p, "L$1: while (true) {$n" | "while (true) {$n", [labl]) + lineF(p, "L$1: while (true) {$n", [labl]) p.nested: gen(p, n.sons[0], cond) - lineF(p, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n", + lineF(p, "if (!$1) break L$2;$n", [cond.res, labl]) p.nested: genStmt(p, n.sons[1]) - lineF(p, "}$n" | "}L$#:;$n", [labl]) + lineF(p, "}$n", [labl]) setLen(p.blocks, length) proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = @@ -653,26 +604,22 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = var i = 1 var length = sonsLen(n) var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch - if catchBranchesExist and p.target == targetJS: + if catchBranchesExist: add(p.body, "++excHandler;" & tnl) var tmpFramePtr = rope"F" if optStackTrace notin p.options: tmpFramePtr = p.getTemp(true) line(p, tmpFramePtr & " = framePtr;" & tnl) lineF(p, "try {$n", []) - if p.target == targetPHP and p.globals == nil: - p.globals = "global $lastJSError; global $prevJSError;".rope var a: TCompRes gen(p, n.sons[0], a) moveInto(p, a, r) var generalCatchBranchExists = false - let dollar = rope(if p.target == targetJS: "" else: "$") - if p.target == targetJS and catchBranchesExist: + let dollar = rope("") + if catchBranchesExist: addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & " lastJSError = EXC;$n --excHandler;$n", []) line(p, "framePtr = $1;$n" % [tmpFramePtr]) - elif p.target == targetPHP: - lineF(p, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", []) while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) if blen == 1: @@ -704,19 +651,11 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = line(p, indent & "reraiseException();" & tnl) line(p, "}" & tnl) addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) - if p.target == targetJS: - line(p, "} finally {" & tnl) - line(p, "framePtr = $1;$n" % [tmpFramePtr]) - if p.target == targetPHP: - # XXX ugly hack for PHP codegen - line(p, "}" & tnl) + line(p, "} finally {" & tnl) + line(p, "framePtr = $1;$n" % [tmpFramePtr]) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) - if p.target == targetPHP: - # XXX ugly hack for PHP codegen - line(p, "if($lastJSError) throw($lastJSError);" & tnl) - if p.target == targetJS: - line(p, "}" & tnl) + line(p, "}" & tnl) proc genRaiseStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -737,7 +676,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = genLineDir(p, n) gen(p, n.sons[0], cond) let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString - if stringSwitch and p.target == targetJS: + if stringSwitch: useMagic(p, "toJSStr") lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc]) else: @@ -789,12 +728,12 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) = sym.loc.k = locOther sym.position = idx+1 let labl = p.unique - lineF(p, "L$1: do {$n" | "", [labl.rope]) + lineF(p, "L$1: do {$n", [labl.rope]) setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet gen(p, n.sons[1], r) setLen(p.blocks, idx) - lineF(p, "} while(false);$n" | "$nL$#:;$n", [labl.rope]) + lineF(p, "} while(false);$n", [labl.rope]) proc genBreakStmt(p: PProc, n: PNode) = var idx: int @@ -812,7 +751,7 @@ proc genBreakStmt(p: PProc, n: PNode) = if idx < 0 or not p.blocks[idx].isLoop: internalError(n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used - lineF(p, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)]) + lineF(p, "break L$1;$n", [rope(p.blocks[idx].id)]) proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -826,7 +765,6 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = let v = it.sym # for backwards compatibility we don't deref syms here :-( if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}: - if p.target == targetPHP: p.body.add "$" p.body.add mangleName(p.module, v) else: var r: TCompRes @@ -869,24 +807,11 @@ proc generateHeader(p: PProc, typ: PType): Rope = if isCompileTimeOnly(param.typ): continue if result != nil: add(result, ", ") var name = mangleName(p.module, param) - if p.target == targetJS: - add(result, name) - if mapType(param.typ) == etyBaseIndex: - add(result, ", ") - add(result, name) - add(result, "_Idx") - elif not (i == 1 and param.name.s == "this"): - let k = param.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind - if k in {tyVar, tyRef, tyPtr, tyLent, tyPointer}: - add(result, "&") - add(result, "$") + add(result, name) + if mapType(param.typ) == etyBaseIndex: + add(result, ", ") add(result, name) - # XXX I think something like this is needed for PHP to really support - # ptr "inside" strings and seq - #if mapType(param.typ) == etyBaseIndex: - # add(result, ", $") - # add(result, name) - # add(result, "_Idx") + add(result, "_Idx") proc countJsParams(typ: PType): int = for i in countup(1, sonsLen(typ.n) - 1): @@ -906,21 +831,10 @@ const proc needsNoCopy(p: PProc; y: PNode): bool = result = (y.kind in nodeKindsNeedNoCopy) or - (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar}) or - p.target == targetPHP + (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar}) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes - - if p.target == targetPHP and x.kind == nkBracketExpr and - x[0].typ.skipTypes(abstractVar).kind in {tyString, tyCString}: - var c: TCompRes - gen(p, x[0], a) - gen(p, x[1], b) - gen(p, y, c) - lineF(p, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc]) - return - var xtyp = mapType(p, x.typ) if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: @@ -968,7 +882,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = proc genAsgn(p: PProc, n: PNode) = genLineDir(p, n) - genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=p.target == targetPHP) + genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false) proc genFastAsgn(p: PProc, n: PNode) = genLineDir(p, n) @@ -991,12 +905,10 @@ proc genSwap(p: PProc, n: PNode) = let tmp2 = p.getTemp(false) if a.typ != etyBaseIndex or b.typ != etyBaseIndex: internalError(n.info, "genSwap") - lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n" | - "$1 = $2; $2 = $3; $3 = $1;$n", + lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n", [tmp, a.address, b.address]) tmp = tmp2 - lineF(p, "var $1 = $2; $2 = $3; $3 = $1;" | - "$1 = $2; $2 = $3; $3 = $1;", + lineF(p, "var $1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) proc getFieldPosition(f: PNode): int = @@ -1011,10 +923,7 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = let b = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, b.sons[0], a) if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple: - if p.target == targetJS: - r.res = makeJSString( "Field" & $getFieldPosition(b.sons[1]) ) - else: - r.res = getFieldPosition(b.sons[1]).rope + r.res = makeJSString("Field" & $getFieldPosition(b.sons[1])) else: if b.sons[1].kind != nkSym: internalError(b.sons[1].info, "genFieldAddr") var f = b.sons[1].sym @@ -1029,19 +938,13 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) let otyp = skipTypes(n.sons[0].typ, abstractVarRange) if otyp.kind == tyTuple: - r.res = ("$1.Field$2" | "$1[$2]") % + r.res = ("$1.Field$2") % [r.res, getFieldPosition(n.sons[1]).rope] else: if n.sons[1].kind != nkSym: internalError(n.sons[1].info, "genFieldAccess") var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) - if p.target == targetJS: - r.res = "$1.$2" % [r.res, f.loc.r] - else: - if {sfImportc, sfExportc} * f.flags != {}: - r.res = "$1->$2" % [r.res, f.loc.r] - else: - r.res = "$1['$2']" % [r.res, f.loc.r] + r.res = "$1.$2" % [r.res, f.loc.r] r.kind = resExpr proc genAddr(p: PProc, n: PNode, r: var TCompRes) @@ -1069,13 +972,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = else: first = 0 if optBoundsCheck in p.options: useMagic(p, "chckIndx") - if p.target == targetPHP: - if typ.kind != tyString: - r.res = "chckIndx($1, $2, count($3)-1)-$2" % [b.res, rope(first), a.res] - else: - r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res] - else: - r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res] + r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: @@ -1089,27 +986,11 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n, r) of tyTuple: - if p.target == targetPHP: - genFieldAccess(p, n, r) - return genFieldAddr(p, n, r) else: internalError(n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = etyNone if r.res == nil: internalError(n.info, "genArrayAccess") - if p.target == targetPHP: - if n.sons[0].kind in nkCallKinds+{nkStrLit..nkTripleStrLit}: - useMagic(p, "nimAt") - if ty.kind in {tyString, tyCString}: - # XXX this needs to be more like substr($1,$2) - r.res = "ord(nimAt($1, $2))" % [r.address, r.res] - else: - r.res = "nimAt($1, $2)" % [r.address, r.res] - elif ty.kind in {tyString, tyCString}: - # XXX this needs to be more like substr($1,$2) - r.res = "ord(@$1[$2])" % [r.address, r.res] - else: - r.res = "$1[$2]" % [r.address, r.res] - elif ty.kind == tyCString: + if ty.kind == tyCString: r.res = "$1.charCodeAt($2)" % [r.address, r.res] else: r.res = "$1[$2]" % [r.address, r.res] @@ -1122,7 +1003,7 @@ template isIndirect(x: PSym): bool = #(mapType(v.typ) != etyObject) and {sfImportc, sfVolatile, sfExportc} * v.flags == {} and v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, - skConst, skTemp, skLet} and p.target == targetJS) + skConst, skTemp, skLet}) proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case n.sons[0].kind @@ -1138,8 +1019,6 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone if isIndirect(s): r.res = s.loc.r & "[0]" - elif p.target == targetPHP: - r.res = "&" & s.loc.r else: r.res = s.loc.r r.address = nil @@ -1180,15 +1059,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = else: internalError(n.sons[0].info, "genAddr: " & $n.sons[0].kind) proc thisParam(p: PProc; typ: PType): PType = - if p.target == targetPHP: - # XXX Might be very useful for the JS backend too? - let typ = skipTypes(typ, abstractInst) - assert(typ.kind == tyProc) - if 1 < sonsLen(typ.n): - assert(typ.n.sons[1].kind == nkSym) - let param = typ.n.sons[1].sym - if param.name.s == "this": - result = param.typ.skipTypes(abstractVar) + discard proc attachProc(p: PProc; content: Rope; s: PSym) = let otyp = thisParam(p, s.typ) @@ -1220,39 +1091,27 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = of skVar, skLet, skParam, skTemp, skResult, skForVar: if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) - if p.target == targetJS: - let k = mapType(p, s.typ) - if k == etyBaseIndex: - r.typ = etyBaseIndex - if {sfAddrTaken, sfGlobal} * s.flags != {}: - r.address = "$1[0]" % [s.loc.r] - r.res = "$1[1]" % [s.loc.r] - else: - r.address = s.loc.r - r.res = s.loc.r & "_Idx" - elif isIndirect(s): - r.res = "$1[0]" % [s.loc.r] + let k = mapType(p, s.typ) + if k == etyBaseIndex: + r.typ = etyBaseIndex + if {sfAddrTaken, sfGlobal} * s.flags != {}: + r.address = "$1[0]" % [s.loc.r] + r.res = "$1[1]" % [s.loc.r] else: - r.res = s.loc.r + r.address = s.loc.r + r.res = s.loc.r & "_Idx" + elif isIndirect(s): + r.res = "$1[0]" % [s.loc.r] else: - r.res = "$" & s.loc.r - if sfGlobal in s.flags: - p.declareGlobal(s.id, r.res) + r.res = s.loc.r of skConst: genConstant(p, s) if s.loc.r == nil: internalError(n.info, "symbol has no generated name: " & s.name.s) - if p.target == targetJS: - r.res = s.loc.r - else: - r.res = "$" & s.loc.r - p.declareGlobal(s.id, r.res) + r.res = s.loc.r of skProc, skFunc, skConverter, skMethod: discard mangleName(p.module, s) - if p.target == targetPHP and r.kind != resCallee: - r.res = makeJsString($s.loc.r) - else: - r.res = s.loc.r + r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone or {sfImportc, sfInfixCall} * s.flags != {}: discard @@ -1421,10 +1280,8 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.typ = etyNone - add(r.res, "." | "->") + add(r.res, ".") var op: TCompRes - if p.target == targetPHP: - op.kind = resCallee gen(p, n.sons[0], op) add(r.res, op.res) genArgs(p, n, r, 2) @@ -1433,28 +1290,21 @@ proc genCall(p: PProc, n: PNode, r: var TCompRes) = if n.sons[0].kind == nkSym and thisParam(p, n.sons[0].typ) != nil: genInfixCall(p, n, r) return - if p.target == targetPHP: - r.kind = resCallee gen(p, n.sons[0], r) genArgs(p, n, r) proc genEcho(p: PProc, n: PNode, r: var TCompRes) = let n = n[1].skipConv internalAssert n.kind == nkBracket - if p.target == targetJS: - useMagic(p, "toJSStr") # Used in rawEcho - useMagic(p, "rawEcho") - elif n.len == 0: - r.kind = resExpr - add(r.res, """print("\n")""") - return - add(r.res, "rawEcho(" | "print(") + useMagic(p, "toJSStr") # Used in rawEcho + useMagic(p, "rawEcho") + add(r.res, "rawEcho(") for i in countup(0, sonsLen(n) - 1): let it = n.sons[i] if it.typ.isCompileTimeOnly: continue - if i > 0: add(r.res, ", " | ".") + if i > 0: add(r.res, ", ") genArgNoParam(p, it, r) - add(r.res, ")" | """."\n")""") + add(r.res, ")") r.kind = resExpr proc putToSeq(s: string, indirect: bool): Rope = @@ -1474,10 +1324,7 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: of nkSym: if rec.sym.id notin excludedFieldIDs: if output.len > 0: output.add(", ") - if p.target == targetJS: - output.addf("$#: ", [mangleName(p.module, rec.sym)]) - else: - output.addf("'$#' => ", [mangleName(p.module, rec.sym)]) + output.addf("$#: ", [mangleName(p.module, rec.sym)]) output.add(createVar(p, rec.sym.typ, false)) else: internalError(rec.info, "createRecordVarAux") @@ -1485,7 +1332,7 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v var t = typ if objHasTypeField(t): if output.len > 0: output.add(", ") - addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)]) + addf(output, "m_type: $1", [genTypeInfo(p, t)]) while t != nil: t = t.skipTypes(skipPtrs) createRecordVarAux(p, t.n, excludedFieldIDs, output) @@ -1514,49 +1361,42 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = of tyRange, tyGenericInst, tyAlias, tySink: result = createVar(p, lastSon(typ), indirect) of tySet: - result = putToSeq("{}" | "array()", indirect) + result = putToSeq("{}", indirect) of tyBool: result = putToSeq("false", indirect) of tyArray: let length = int(lengthOrd(t)) let e = elemType(t) let jsTyp = arrayTypeForElemType(e) - if not jsTyp.isNil and p.target == targetJS: + if not jsTyp.isNil: result = "new $1($2)" % [rope(jsTyp), rope(length)] elif length > 32: useMagic(p, "arrayConstr") # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary. - if p.target == targetJS: useMagic(p, "nimCopy") + useMagic(p, "nimCopy") result = "arrayConstr($1, $2, $3)" % [rope(length), createVar(p, e, false), genTypeInfo(p, e)] else: - result = rope("[" | "array(") + result = rope("[") var i = 0 while i < length: if i > 0: add(result, ", ") add(result, createVar(p, e, false)) inc(i) - add(result, "]" | ")") + add(result, "]") if indirect: result = "[$1]" % [result] of tyTuple: - if p.target == targetJS: - result = rope("{") - for i in 0..<t.sonsLen: - if i > 0: add(result, ", ") - addf(result, "Field$1: $2", [i.rope, - createVar(p, t.sons[i], false)]) - add(result, "}") - if indirect: result = "[$1]" % [result] - else: - result = rope("array(") - for i in 0..<t.sonsLen: - if i > 0: add(result, ", ") - add(result, createVar(p, t.sons[i], false)) - add(result, ")") + result = rope("{") + for i in 0..<t.sonsLen: + if i > 0: add(result, ", ") + addf(result, "Field$1: $2", [i.rope, + createVar(p, t.sons[i], false)]) + add(result, "}") + if indirect: result = "[$1]" % [result] of tyObject: var initList: Rope createObjInitList(p, t, initIntSet(), initList) - result = ("{$1}" | "array($#)") % [initList] + result = ("{$1}") % [initList] if indirect: result = "[$1]" % [result] of tyVar, tyPtr, tyLent, tyRef: if mapType(p, t) == etyBaseIndex: @@ -1597,7 +1437,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = varCode = v.constraint.strVal if n.kind == nkEmpty: - lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", + lineF(p, varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))]) if v.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, v.typ) == etyBaseIndex: lineF(p, "var $1_Idx = 0;$n", [varName]) @@ -1632,7 +1472,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = if isIndirect(v): lineF(p, varCode & " = [$3];$n", [returnType, v.loc.r, s]) else: - lineF(p, varCode & " = $3;$n" | "$$$2 = $3;$n", [returnType, v.loc.r, s]) + lineF(p, varCode & " = $3;$n", [returnType, v.loc.r, s]) if useReloadingGuard: lineF(p, "}$n") @@ -1665,18 +1505,14 @@ proc genNew(p: PProc, n: PNode) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - if p.target == targetJS: - lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)]) - else: - lineF(p, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)]) + lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)]) proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] - lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" | - "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [ + lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = @@ -1708,21 +1544,6 @@ proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = else: r.res.add("$1)" % [a.res]) -proc genConStrStrPHP(p: PProc, n: PNode, r: var TCompRes) = - var a: TCompRes - gen(p, n.sons[1], a) - r.kind = resExpr - if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: - r.res.add("chr($1)" % [a.res]) - else: - r.res.add(a.res) - for i in countup(2, sonsLen(n) - 1): - gen(p, n.sons[i], a) - if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: - r.res.add(".chr($1)" % [a.res]) - else: - r.res.add(".$1" % [a.res]) - proc genToArray(p: PProc; n: PNode; r: var TCompRes) = # we map mArray to PHP's array constructor, a mild hack: var a, b: TCompRes @@ -1766,9 +1587,6 @@ proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = add(r.res, ")") proc genRepr(p: PProc, n: PNode, r: var TCompRes) = - if p.target == targetPHP: - localError(n.info, "'repr' not available for PHP backend") - return let t = skipTypes(n.sons[1].typ, abstractVarRange) case t.kind: of tyInt..tyInt64, tyUInt..tyUInt64: @@ -1828,57 +1646,36 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1") else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") of mAppendStrCh: - if p.target == targetJS: - binaryExpr(p, n, r, "addChar", - "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") - else: - binaryExpr(p, n, r, "", "$1 .= chr($2)") + binaryExpr(p, n, r, "addChar", + "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") of mAppendStrStr: - if p.target == targetJS: - if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: - binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }") - else: - binaryExpr(p, n, r, "", - "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") - # XXX: make a copy of $2, because of Javascript's sucking semantics + if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: + binaryExpr(p, n, r, "", "if ($1 != null) { $1 += $2; } else { $1 = $2; }") else: - binaryExpr(p, n, r, "", "$1 .= $2;") + binaryExpr(p, n, r, "", + "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") + # XXX: make a copy of $2, because of Javascript's sucking semantics of mAppendSeqElem: - if p.target == targetJS: - var x, y: TCompRes - gen(p, n.sons[1], x) - gen(p, n.sons[2], y) - if needsNoCopy(p, n[2]): - r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc] - else: - useMagic(p, "nimCopy") - let c = getTemp(p, defineInLocals=false) - lineF(p, "var $1 = nimCopy(null, $2, $3);$n", - [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) - r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c] - r.kind = resExpr + var x, y: TCompRes + gen(p, n.sons[1], x) + gen(p, n.sons[2], y) + if needsNoCopy(p, n[2]): + r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc] else: - binaryExpr(p, n, r, "", "$1[] = $2") + useMagic(p, "nimCopy") + let c = getTemp(p, defineInLocals=false) + lineF(p, "var $1 = nimCopy(null, $2, $3);$n", + [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) + r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c] + r.kind = resExpr of mConStrStr: - if p.target == targetJS: - genConStrStr(p, n, r) - else: - genConStrStrPHP(p, n, r) + genConStrStr(p, n, r) of mEqStr: - if p.target == targetJS: - binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") - else: - binaryExpr(p, n, r, "", "($1 == $2)") + binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") of mLeStr: - if p.target == targetJS: - binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") - else: - binaryExpr(p, n, r, "", "($1 <= $2)") + binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") of mLtStr: - if p.target == targetJS: - binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") - else: - binaryExpr(p, n, r, "", "($1 < $2)") + binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") of mIsNil: unaryExpr(p, n, r, "", "($1 === null)") of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) @@ -1886,24 +1683,20 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mChr, mArrToSeq: gen(p, n.sons[1], r) # nothing to do of mOrd: genOrd(p, n, r) of mLengthStr: - if p.target == targetJS and n.sons[1].typ.skipTypes(abstractInst).kind == tyCString: + if n.sons[1].typ.skipTypes(abstractInst).kind == tyCString: unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") else: - unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)" | - "strlen($1)") - of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1" | "strlen($1)") + unaryExpr(p, n, r, "", "($1 != null ? $1.length-1 : 0)") + of mXLenStr: unaryExpr(p, n, r, "", "$1.length-1") of mLengthSeq, mLengthOpenArray, mLengthArray: - unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)" | - "count($1)") + unaryExpr(p, n, r, "", "($1 != null ? $1.length : 0)") of mXLenSeq: - unaryExpr(p, n, r, "", "$1.length" | "count($1)") + unaryExpr(p, n, r, "", "$1.length") of mHigh: if skipTypes(n.sons[1].typ, abstractVar).kind == tyString: - unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)" | - "(strlen($1)-1)") + unaryExpr(p, n, r, "", "($1 != null ? ($1.length-2) : -1)") else: - unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)" | - "(count($1)-1)") + unaryExpr(p, n, r, "", "($1 != null ? ($1.length-1) : -1)") of mInc: if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: binaryUintExpr(p, n, r, "+", true) @@ -1917,7 +1710,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") of mSetLengthStr: - binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)") + binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0") of mSetLengthSeq: var x, y: TCompRes gen(p, n.sons[1], x) @@ -1934,50 +1727,23 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)") of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)") of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true") - of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]" | "unset $1[$2]") + of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]") of mInSet: - if p.target == targetJS: - binaryExpr(p, n, r, "", "($1[$2] != undefined)") - else: - let s = n.sons[1] - if s.kind == nkCurly: - var a, b, x: TCompRes - gen(p, n.sons[2], x) - r.res = rope("(") - r.kind = resExpr - for i in countup(0, sonsLen(s) - 1): - if i > 0: add(r.res, " || ") - var it = s.sons[i] - if it.kind == nkRange: - gen(p, it.sons[0], a) - gen(p, it.sons[1], b) - addf(r.res, "($1 >= $2 && $1 <= $3)", [x.res, a.res, b.res,]) - else: - gen(p, it, a) - addf(r.res, "($1 == $2)", [x.res, a.res]) - add(r.res, ")") - else: - binaryExpr(p, n, r, "", "isset($1[$2])") + binaryExpr(p, n, r, "", "($1[$2] != undefined)") of mNewSeq: genNewSeq(p, n) - of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]" | "array()") + of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]") of mOf: genOf(p, n, r) of mReset: genReset(p, n) of mEcho: genEcho(p, n, r) of mNLen..mNError, mSlurp, mStaticExec: localError(n.info, errXMustBeCompileTime, n.sons[0].sym.name.s) of mCopyStr: - binaryExpr(p, n, r, "", "($1.slice($2))" | "substr($1, $2)") + binaryExpr(p, n, r, "", "($1.slice($2))") of mCopyStrLast: - if p.target == targetJS: - ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") - else: - ternaryExpr(p, n, r, "nimSubstr", "nimSubstr($#, $#, $#)") + ternaryExpr(p, n, r, "", "($1.slice($2, ($3)+1).concat(0))") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") of mNewStringOfCap: - if p.target == targetJS: - unaryExpr(p, n, r, "mnewString", "mnewString(0)") - else: - unaryExpr(p, n, r, "", "''") + unaryExpr(p, n, r, "mnewString", "mnewString(0)") of mDotDot: genProcForSymIfNeeded(p, n.sons[0].sym) genCall(p, n, r) @@ -1985,8 +1751,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = useMagic(p, "nimParseBiggestFloat") genCall(p, n, r) of mArray: - if p.target == targetPHP: genToArray(p, n, r) - else: genCall(p, n, r) + genCall(p, n, r) else: genCall(p, n, r) #else internalError(e.info, 'genMagic: ' + magicToStr[op]); @@ -2003,13 +1768,13 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = if it.kind == nkRange: gen(p, it.sons[0], a) gen(p, it.sons[1], b) - addf(r.res, "[$1, $2]" | "array($#,$#)", [a.res, b.res]) + addf(r.res, "[$1, $2]", [a.res, b.res]) else: gen(p, it, a) add(r.res, a.res) add(r.res, ")") # emit better code for constant sets: - if p.target == targetJS and isDeepConstExpr(n): + if isDeepConstExpr(n): inc(p.g.unique) let tmp = rope("ConstSet") & rope(p.g.unique) addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res]) @@ -2017,25 +1782,25 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) = proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes - r.res = rope("[" | "array(") + r.res = rope("[") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") gen(p, n.sons[i], a) add(r.res, a.res) - add(r.res, "]" | ")") + add(r.res, "]") proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes - r.res = rope("{" | "array(") + r.res = rope("{") r.kind = resExpr for i in countup(0, sonsLen(n) - 1): if i > 0: add(r.res, ", ") var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) - addf(r.res, "Field$#: $#" | "$2", [i.rope, a.res]) - r.res.add("}" | ")") + addf(r.res, "Field$#: $#", [i.rope, a.res]) + r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes @@ -2059,10 +1824,10 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = else: useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] - addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res]) + addf(initList, "$#: $#", [f.loc.r, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) - r.res = ("{$1}" | "array($#)") % [initList] + r.res = ("{$1}") % [initList] proc genConv(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) @@ -2125,18 +1890,17 @@ proc genReturnStmt(p: PProc, n: PNode) = genStmt(p, n.sons[0]) else: genLineDir(p, n) - lineF(p, "break BeforeRet;$n" | "goto BeforeRet;$n", []) + lineF(p, "break BeforeRet;$n", []) proc frameCreate(p: PProc; procname, filename: Rope): Rope = let frameFmt = - "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" | - "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n" + "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" result = p.indentLine(frameFmt % [procname, filename]) - result.add p.indentLine(ropes.`%`("framePtr = F;$n" | "$$framePtr = &$$F;$n", [])) + result.add p.indentLine(ropes.`%`("framePtr = F;$n", [])) proc frameDestroy(p: PProc): Rope = - result = p.indentLine rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl) + result = p.indentLine rope(("framePtr = F.prev;") & tnl) proc genProcBody(p: PProc, prc: PSym): Rope = if hasFrameInfo(p): @@ -2146,15 +1910,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope = else: result = nil if p.beforeRetNeeded: - if p.target == targetJS: - result.add p.indentLine(~"BeforeRet: do {$n") - result.add p.body - result.add p.indentLine(~"} while (false);$n") - else: - addF(result, "$# BeforeRet:;$n", [p.body]) + result.add p.indentLine(~"BeforeRet: do {$n") + result.add p.body + result.add p.indentLine(~"} while (false);$n") else: add(result, p.body) - if prc.typ.callConv == ccSysCall and p.target == targetJS: + if prc.typ.callConv == ccSysCall: result = ("try {$n$1} catch (e) {$n" & " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result] if hasFrameInfo(p): @@ -2182,7 +1943,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = resultSym = prc.ast.sons[resultPos].sym let mname = mangleName(p.module, resultSym) let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) - resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar]) + resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) if resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef} and mapType(p, resultSym.typ) == etyBaseIndex: resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) @@ -2267,8 +2028,7 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = elif fromUint: if src.size == 4 and dest.size == 4: # XXX prevent multi evaluations - r.res = "($1|0)" % [r.res] | - "($1>(float)2147483647?(int)$1-4294967296:$1)" % [r.res] + r.res = "($1|0)" % [r.res] else: let trimmer = unsignedTrimmer(dest.size) let minuend = case dest.size @@ -2304,8 +2064,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.res = rope"null" r.kind = resExpr of nkStrLit..nkTripleStrLit: - if skipTypes(n.typ, abstractVarRange).kind == tyString and - p.target == targetJS: + if skipTypes(n.typ, abstractVarRange).kind == tyString: useMagic(p, "makeNimstrLit") r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)] else: @@ -2342,10 +2101,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkObjConstr: genObjConstr(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkAddr, nkHiddenAddr: - if p.target == targetJS: - genAddr(p, n, r) - else: - gen(p, n.sons[0], r) + genAddr(p, n, r) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r) of nkBracketExpr: genArrayAccess(p, n, r) of nkDotExpr: genFieldAccess(p, n, r) @@ -2422,31 +2178,22 @@ proc newModule(module: PSym): BModule = if globals == nil: globals = newGlobals() -proc genHeader(target: TTarget): Rope = - if target == targetJS: - result = ( - "/* Generated by the Nim Compiler v$1 */$n" & - "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" & - "var framePtr = null;$n" & - "var excHandler = 0;$n" & - "var lastJSError = null;$n" & - "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" & - "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" & - "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" & - "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" & - "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" & - "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" & - "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" & - "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") % - [rope(VersionAsString)] - else: - result = ("<?php$n" & - "/* Generated by the Nim Compiler v$1 */$n" & - "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" & - "$$framePtr = null;$n" & - "$$excHandler = 0;$n" & - "$$lastJSError = null;$n") % - [rope(VersionAsString)] +proc genHeader(): Rope = + result = ( + "/* Generated by the Nim Compiler v$1 */$n" & + "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" & + "var framePtr = null;$n" & + "var excHandler = 0;$n" & + "var lastJSError = null;$n" & + "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" & + "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" & + "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" & + "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" & + "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" & + "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" & + "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" & + "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") % + [rope(VersionAsString)] proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: @@ -2513,8 +2260,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = result = myProcess(b, n) var m = BModule(b) if sfMainModule in m.module.flags: - let ext = if m.target == targetJS: "js" else: "php" - let f = if globals.classes.len == 0: m.module.filename + let ext = "js" + let f = if globals.classes.len == 0: toFilename(FileIndex m.module.position) else: "nimsystem" let code = wholeCode(graph, m) let outfile = @@ -2523,7 +2270,7 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = else: getCurrentDir() / options.outFile else: changeFileExt(completeCFilePath(f), ext) - discard writeRopeIfNotEqual(genHeader(m.target) & code, outfile) + discard writeRopeIfNotEqual(genHeader() & code, outfile) for obj, content in items(globals.classes): genClass(obj, content, ext) @@ -2533,7 +2280,6 @@ proc myOpenCached(graph: ModuleGraph; s: PSym, rd: PRodReader): PPassContext = proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext = var r = newModule(s) - r.target = if gCmd == cmdCompileToPHP: targetPHP else: targetJS result = r const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 8bd963a65..f440ee7da 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -121,26 +121,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) = addf(p.g.typeInfo, "$1.base = $2;$n", [name, genTypeInfo(p, typ.sons[0])]) -proc genEnumInfoPHP(p: PProc; t: PType): Rope = - let t = t.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink}) - result = "$$NTI$1" % [rope(t.id)] - p.declareGlobal(t.id, result) - if containsOrIncl(p.g.typeInfoGenerated, t.id): return - - let length = sonsLen(t.n) - var s: Rope = nil - for i in countup(0, length - 1): - if (t.n.sons[i].kind != nkSym): internalError(t.n.info, "genEnumInfo") - let field = t.n.sons[i].sym - if i > 0: add(s, ", " & tnl) - let extName = if field.ast == nil: field.name.s else: field.ast.strVal - addf(s, "$# => $#$n", - [rope(field.position), makeJSString(extName)]) - prepend(p.g.typeInfo, "$$$# = $#;$n" % [result, s]) - proc genTypeInfo(p: PProc, typ: PType): Rope = - if p.target == targetPHP: - return makeJSString(typeToString(typ, preferModuleInfo)) let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink}) result = "NTI$1" % [rope(t.id)] if containsOrIncl(p.g.typeInfoGenerated, t.id): return diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index d881df5e9..775748425 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -230,7 +230,7 @@ template isIterator*(owner: PSym): bool = proc liftingHarmful(owner: PSym): bool {.inline.} = ## lambda lifting can be harmful for JS-like code generators. let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro - result = gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime + result = gCmd == cmdCompileToJS and not isCompileTime proc liftIterSym*(n: PNode; owner: PSym): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) @@ -813,7 +813,7 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode = let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro if body.kind == nkEmpty or ( - gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime) or + gCmd == cmdCompileToJS and not isCompileTime) or fn.skipGenericOwner.kind != skModule: # ignore forward declaration: result = body diff --git a/compiler/lexer.nim b/compiler/lexer.nim index e55da2f35..0b1090bb1 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -133,7 +133,7 @@ type TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string) TLexer* = object of TBaseLexer - fileIdx*: int32 + fileIdx*: FileIndex indentAhead*: int # if > 0 an indendation has already been read # this is needed because scanning comments # needs so much look-ahead @@ -222,7 +222,7 @@ proc fillToken(L: var TToken) = L.commentOffsetA = 0 L.commentOffsetB = 0 -proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream; +proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache) = openBaseLexer(lex, inputstream) lex.fileIdx = fileidx @@ -274,7 +274,7 @@ template tokenEnd(tok, pos) {.dirty.} = when defined(nimsuggest): let colB = getColNumber(L, pos)+1 if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}: L.cursor = CursorPosition.InToken gTrackPos.col = colA.int16 colA = 0 @@ -285,9 +285,9 @@ template tokenEndIgnore(tok, pos) = when defined(nimsuggest): let colB = getColNumber(L, pos) if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}: gTrackPos.fileIndex = trackPosInvalidFileIdx - gTrackPos.line = -1 + gTrackPos.line = 0'u16 colA = 0 when defined(nimpretty): tok.offsetB = L.offsetBase + pos @@ -299,7 +299,7 @@ template tokenEndPrevious(tok, pos) = # the cursor in a string literal or comment: let colB = getColNumber(L, pos) if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and - L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}: + L.lineNumber == gTrackPos.line.int and gIdeCmd in {ideSug, ideCon}: L.cursor = CursorPosition.BeforeToken gTrackPos = L.previousToken gTrackPosAttached = true @@ -1003,6 +1003,10 @@ proc skip(L: var TLexer, tok: var TToken) = var buf = L.buf tokenBegin(tok, pos) tok.strongSpaceA = 0 + when defined(nimpretty): + var hasComment = false + tok.commentOffsetA = L.offsetBase + pos + tok.commentOffsetB = tok.commentOffsetA while true: case buf[pos] of ' ': @@ -1021,6 +1025,7 @@ proc skip(L: var TLexer, tok: var TToken) = inc(pos) inc(indent) elif buf[pos] == '#' and buf[pos+1] == '[': + when defined(nimpretty): hasComment = true skipMultiLineComment(L, tok, pos+2, false) pos = L.bufpos buf = L.buf @@ -1034,14 +1039,11 @@ proc skip(L: var TLexer, tok: var TToken) = of '#': # do not skip documentation comment: if buf[pos+1] == '#': break - when defined(nimpretty): - tok.commentOffsetA = L.offsetBase + pos + when defined(nimpretty): hasComment = true if buf[pos+1] == '[': skipMultiLineComment(L, tok, pos+2, false) pos = L.bufpos buf = L.buf - when defined(nimpretty): - tok.commentOffsetB = L.offsetBase + pos else: tokenBegin(tok, pos) while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos) @@ -1053,6 +1055,9 @@ proc skip(L: var TLexer, tok: var TToken) = tokenEndPrevious(tok, pos-1) L.bufpos = pos when defined(nimpretty): + if hasComment: + tok.commentOffsetB = L.offsetBase + pos + tok.tokType = tkComment if gIndentationWidth <= 0: gIndentationWidth = tok.indent @@ -1061,7 +1066,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = when defined(nimsuggest): # we attach the cursor to the last *strong* token if tok.tokType notin weakTokens: - L.previousToken.line = tok.line.int16 + L.previousToken.line = tok.line.uint16 L.previousToken.col = tok.col.int16 when defined(nimsuggest): @@ -1074,6 +1079,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = else: tok.indent = -1 skip(L, tok) + when defined(nimpretty): + if tok.tokType == tkComment: + L.indentAhead = L.currLineIndent + return var c = L.buf[L.bufpos] tok.line = L.lineNumber tok.col = getColNumber(L, L.bufpos) @@ -1109,7 +1118,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = tok.tokType = tkParLe when defined(nimsuggest): if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and - tok.line == gTrackPos.line and gIdeCmd == ideCon: + tok.line == gTrackPos.line.int and gIdeCmd == ideCon: gTrackPos.col = tok.col.int16 of ')': tok.tokType = tkParRi @@ -1130,7 +1139,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = of '.': when defined(nimsuggest): if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and - tok.line == gTrackPos.line and gIdeCmd == ideSug: + tok.line == gTrackPos.line.int and gIdeCmd == ideSug: tok.tokType = tkDot L.cursor = CursorPosition.InToken gTrackPos.col = tok.col.int16 diff --git a/compiler/main.nim b/compiler/main.nim index 20a715886..401099fc3 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -35,7 +35,7 @@ proc writeDepsFile(g: ModuleGraph; project: string) = let f = open(changeFileExt(project, "deps"), fmWrite) for m in g.modules: if m != nil: - f.writeLine(toFullPath(m.position.int32)) + f.writeLine(toFullPath(m.position.FileIndex)) for k in g.inclToMod.keys: if g.getModule(k).isNil: # don't repeat includes which are also modules f.writeLine(k.toFullPath) @@ -94,7 +94,6 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) = defineSymbol("nimrod") # 'nimrod' is always defined defineSymbol("ecmascript") # For backward compatibility defineSymbol("js") - if gCmd == cmdCompileToPHP: defineSymbol("nimphp") semanticPasses() registerPass(JSgenPass) compileProject(graph, cache) @@ -189,9 +188,6 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = of "js", "compiletojs": gCmd = cmdCompileToJS commandCompileToJS(graph, cache) - of "php": - gCmd = cmdCompileToPHP - commandCompileToJS(graph, cache) of "doc0": wantMainModule() gCmd = cmdDoc @@ -269,7 +265,7 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) = of "parse": gCmd = cmdParse wantMainModule() - discard parseFile(gProjectMainIdx, cache) + discard parseFile(FileIndex gProjectMainIdx, cache) of "scan": gCmd = cmdScan wantMainModule() diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 2c59a9097..6c14a46e8 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -25,7 +25,7 @@ ## - Its dependent module stays the same. ## -import ast, intsets, tables, options, rod +import ast, intsets, tables, options, rod, msgs, hashes type ModuleGraph* = ref object @@ -34,10 +34,10 @@ type deps*: IntSet # the dependency graph or potentially its transitive closure. suggestMode*: bool # whether we are in nimsuggest mode or not. invalidTransitiveClosure: bool - inclToMod*: Table[int32, int32] # mapping of include file to the - # first module that included it - importStack*: seq[int32] # The current import stack. Used for detecting recursive - # module dependencies. + inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the + # first module that included it + importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive + # module dependencies. backend*: RootRef # minor hack so that a backend can extend this easily config*: ConfigRef doStopCompile*: proc(): bool {.closure.} @@ -45,6 +45,8 @@ type owners*: seq[PSym] methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]] +proc hash*(x: FileIndex): Hash {.borrow.} + {.this: g.} proc stopCompile*(g: ModuleGraph): bool {.inline.} = @@ -56,7 +58,7 @@ proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph = result.deps = initIntSet() result.modules = @[] result.importStack = @[] - result.inclToMod = initTable[int32, int32]() + result.inclToMod = initTable[FileIndex, FileIndex]() if config.isNil: result.config = newConfigRef() else: @@ -69,35 +71,35 @@ proc resetAllModules*(g: ModuleGraph) = deps = initIntSet() modules = @[] importStack = @[] - inclToMod = initTable[int32, int32]() + inclToMod = initTable[FileIndex, FileIndex]() usageSym = nil owners = @[] methods = @[] -proc getModule*(g: ModuleGraph; fileIdx: int32): PSym = - if fileIdx >= 0 and fileIdx < modules.len: - result = modules[fileIdx] +proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = + if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len: + result = modules[fileIdx.int32] proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b -proc addDep*(g: ModuleGraph; m: PSym, dep: int32) = - assert m.position == m.info.fileIndex +proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = + assert m.position == m.info.fileIndex.int32 addModuleDep(m.info.fileIndex, dep, isIncludeFile = false) if suggestMode: - deps.incl m.position.dependsOn(dep) + deps.incl m.position.dependsOn(dep.int) # we compute the transitive closure later when quering the graph lazily. # this improve efficiency quite a lot: #invalidTransitiveClosure = true -proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) = +proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) = addModuleDep(module, includeFile, isIncludeFile = true) discard hasKeyOrPut(inclToMod, includeFile, module) -proc parentModule*(g: ModuleGraph; fileIdx: int32): int32 = +proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex = ## returns 'fileIdx' if the file belonging to this index is ## directly used as a module or else the module that first ## references this include file. - if fileIdx >= 0 and fileIdx < modules.len and modules[fileIdx] != nil: + if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil: result = fileIdx else: result = inclToMod.getOrDefault(fileIdx) @@ -111,11 +113,11 @@ proc transitiveClosure(g: var IntSet; n: int) = if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)): g.incl i.dependsOn(j) -proc markDirty*(g: ModuleGraph; fileIdx: int32) = +proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = let m = getModule fileIdx if m != nil: incl m.flags, sfDirty -proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) = +proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) = # we need to mark its dependent modules D as dirty right away because after # nimsuggest is done with this module, the module's dirty flag will be # cleared but D still needs to be remembered as 'dirty'. @@ -126,7 +128,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) = # every module that *depends* on this file is also dirty: for i in 0i32..<modules.len.int32: let m = modules[i] - if m != nil and deps.contains(i.dependsOn(fileIdx)): + if m != nil and deps.contains(i.dependsOn(fileIdx.int)): incl m.flags, sfDirty proc isDirty*(g: ModuleGraph; m: PSym): bool = diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index 878c22cf8..1318b67a7 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -174,7 +174,7 @@ proc getModuleName*(n: PNode): string = localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree) result = "" -proc checkModuleName*(n: PNode; doLocalError=true): int32 = +proc checkModuleName*(n: PNode; doLocalError=true): FileIndex = # This returns the full canonical path for a given module import let modulename = n.getModuleName let fullPath = findModule(modulename, n.info.toFullPath) diff --git a/compiler/modules.nim b/compiler/modules.nim index a3be5a518..56bfdf662 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -13,115 +13,10 @@ import ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options, idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod -when false: - type - TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled - THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged - - TModuleInMemory* = object - hash*: SecureHash - deps*: seq[int32] ## XXX: slurped files are currently not tracked - - needsRecompile*: TNeedRecompile - hashStatus*: THashStatus - - var - gCompiledModules: seq[PSym] = @[] - gMemCacheData*: seq[TModuleInMemory] = @[] - ## XXX: we should implement recycling of file IDs - ## if the user keeps renaming modules, the file IDs will keep growing - gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why. - - proc hashChanged(fileIdx: int32): bool = - internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len - - template updateStatus = - gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged - else: hashNotChanged - # echo "TESTING Hash: ", fileIdx.toFilename, " ", result - - case gMemCacheData[fileIdx].hashStatus - of hashHasChanged: - result = true - of hashNotChanged: - result = false - of hashCached: - let newHash = secureHashFile(fileIdx.toFullPath) - result = newHash != gMemCacheData[fileIdx].hash - gMemCacheData[fileIdx].hash = newHash - updateStatus() - of hashNotTaken: - gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath) - result = true - updateStatus() - - proc doHash(fileIdx: int32) = - if gMemCacheData[fileIdx].hashStatus == hashNotTaken: - # echo "FIRST Hash: ", fileIdx.ToFilename - gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath) - - proc resetModule*(fileIdx: int32) = - # echo "HARD RESETTING ", fileIdx.toFilename - if fileIdx <% gMemCacheData.len: - gMemCacheData[fileIdx].needsRecompile = Yes - if fileIdx <% gCompiledModules.len: - gCompiledModules[fileIdx] = nil - if fileIdx <% cgendata.gModules.len: - cgendata.gModules[fileIdx] = nil - - proc resetModule*(module: PSym) = - let conflict = getModule(module.position.int32) - if conflict == nil: return - doAssert conflict == module - resetModule(module.position.int32) - initStrTable(module.tab) - - proc resetAllModules* = - for i in 0..gCompiledModules.high: - if gCompiledModules[i] != nil: - resetModule(i.int32) - resetPackageCache() - # for m in cgenModules(): echo "CGEN MODULE FOUND" - - proc resetAllModulesHard* = - resetPackageCache() - gCompiledModules.setLen 0 - gMemCacheData.setLen 0 - magicsys.resetSysTypes() - # XXX - #gOwners = @[] - - proc checkDepMem(fileIdx: int32): TNeedRecompile = - template markDirty = - resetModule(fileIdx) - return Yes - - if gFuzzyGraphChecking: - if gMemCacheData[fileIdx].needsRecompile != Maybe: - return gMemCacheData[fileIdx].needsRecompile - else: - # cycle detection: We claim that a cycle does no harm. - if gMemCacheData[fileIdx].needsRecompile == Probing: - return No - - if optForceFullMake in gGlobalOptions or hashChanged(fileIdx): - markDirty() - - if gMemCacheData[fileIdx].deps != nil: - gMemCacheData[fileIdx].needsRecompile = Probing - for dep in gMemCacheData[fileIdx].deps: - let d = checkDepMem(dep) - if d in {Yes, Recompiled}: - # echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d - markDirty() - - gMemCacheData[fileIdx].needsRecompile = No - return No - proc resetSystemArtifacts*() = magicsys.resetSysTypes() -proc newModule(graph: ModuleGraph; fileIdx: int32): PSym = +proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = # We cannot call ``newSym`` here, because we have to circumvent the ID # mechanism, which we do in order to assign each module a persistent ID. new(result) @@ -144,9 +39,9 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym = graph.packageSyms.strTableAdd(packSym) result.owner = packSym - result.position = fileIdx + result.position = int fileIdx - growCache graph.modules, fileIdx + growCache graph.modules, int fileIdx graph.modules[result.position] = result incl(result.flags, sfUsed) @@ -158,7 +53,7 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym = # strTableIncl() for error corrections: discard strTableIncl(packSym.tab, result) -proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags: TSymFlags): PSym = +proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym = result = graph.getModule(fileIdx) if result == nil: #growCache gMemCacheData, fileIdx @@ -199,7 +94,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags else: result = gCompiledModules[fileIdx] -proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; +proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.procvar.} = # this is called by the semantic checking phase result = compileModule(graph, fileIdx, cache, {}) @@ -210,11 +105,11 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes else: ForeignPackageNotes -proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; +proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = result = syntaxes.parseFile(fileIdx, cache) graph.addDep(s, fileIdx) - graph.addIncludeDep(s.position.int32, fileIdx) + graph.addIncludeDep(s.position.FileIndex, fileIdx) proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) = if magicsys.systemModule == nil: @@ -224,16 +119,16 @@ proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) = proc wantMainModule* = if gProjectFull.len == 0: fatal(gCmdLineInfo, errCommandExpectsFilename) - gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx + gProjectMainIdx = int32 addFileExt(gProjectFull, NimExt).fileInfoIdx passes.gIncludeFile = includeModule passes.gImportModule = importModule proc compileProject*(graph: ModuleGraph; cache: IdentCache; - projectFileIdx = -1'i32) = + projectFileIdx = InvalidFileIDX) = wantMainModule() let systemFileIdx = fileInfoIdx(options.libpath / "system.nim") - let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx + let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(gProjectMainIdx) else: projectFileIdx graph.importStack.add projectFile if projectFile == systemFileIdx: discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule}) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 70504cfc9..5ae2c4970 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -10,21 +10,24 @@ import options, strutils, os, tables, ropes, platform, terminal, macros +const + explanationsBaseUrl* = "https://nim-lang.org/docs/manual" + type TMsgKind* = enum errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errGenerated, - errXCompilerDoesNotSupportCpp, errStringLiteralExpected, + errStringLiteralExpected, errIntLiteralExpected, errInvalidCharacterConstant, errClosingTripleQuoteExpected, errClosingQuoteExpected, - errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, + errTabulatorsAreNotAllowed, errInvalidToken, errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange, errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, errNewlineExpected, errInvalidModuleName, - errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, + errOperatorExpected, errTokenExpected, errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, - errExceptionExpected, errExceptionAlreadyHandled, + errExceptionAlreadyHandled, errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, errInvalidNumberOfYieldExpr, errCannotReturnExpr, errNoReturnWithReturnTypeNotAllowed, errAttemptToRedefine, @@ -68,7 +71,7 @@ type errWrongNumberOfArgumentsInCall, errMissingGenericParamsForTemplate, errXCannotBePassedToProcVar, - errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, + errPragmaOnlyInHeaderOfProcX, errImplOfXNotAllowed, errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, errInvalidOrderInArrayConstructor, @@ -87,7 +90,8 @@ type errNoReturnTypeDeclared, errNoCommand, errInvalidCommandX, errXOnlyAtModuleScope, errXNeedsParamObjectType, - errTemplateInstantiationTooNested, errInstantiationFrom, + errTemplateInstantiationTooNested, errMacroInstantiationTooNested, + errInstantiationFrom, errInvalidIndexValueForTuple, errCommandExpectsFilename, errMainModuleMustBeSpecified, errXExpected, @@ -144,7 +148,6 @@ const errIllFormedAstX: "illformed AST: $1", errCannotOpenFile: "cannot open \'$1\'", errGenerated: "$1", - errXCompilerDoesNotSupportCpp: "\'$1\' compiler does not support C++", errStringLiteralExpected: "string literal expected", errIntLiteralExpected: "integer literal expected", errInvalidCharacterConstant: "invalid character constant", @@ -152,7 +155,6 @@ const errClosingQuoteExpected: "closing \" expected", errTabulatorsAreNotAllowed: "tabulators are not allowed", errInvalidToken: "invalid token: $1", - errLineTooLong: "line too long", errInvalidNumber: "$1 is not a valid number", errInvalidNumberOctalCode: "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", errNumberOutOfRange: "number $1 out of valid range", @@ -164,7 +166,6 @@ const errInvalidModuleName: "invalid module name: '$1'", errOperatorExpected: "operator expected, but found \'$1\'", errTokenExpected: "\'$1\' expected", - errStringAfterIncludeExpected: "string after \'include\' expected", errRecursiveDependencyX: "recursive dependency: \'$1\'", errOnOrOffExpected: "\'on\' or \'off\' expected", errNoneSpeedOrSizeExpected: "\'none\', \'speed\' or \'size\' expected", @@ -174,7 +175,6 @@ const errAtPopWithoutPush: "\'pop\' without a \'push\' pragma", errEmptyAsm: "empty asm statement", errInvalidIndentation: "invalid indentation", - errExceptionExpected: "exception expected", errExceptionAlreadyHandled: "exception already handled", errYieldNotAllowedHere: "'yield' only allowed in an iterator", errYieldNotAllowedInTryStmt: "'yield' cannot be used within 'try' in a non-inlined iterator", @@ -280,7 +280,6 @@ const errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'", errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters", errXCannotBePassedToProcVar: "\'$1\' cannot be passed to a procvar", - errXCannotBeInParamDecl: "$1 cannot be declared in parameter declaration", errPragmaOnlyInHeaderOfProcX: "pragmas are only allowed in the header of a proc; redefinition of $1", errImplOfXNotAllowed: "implementation of \'$1\' is not allowed", errImplOfXexpected: "implementation of \'$1\' expected", @@ -329,7 +328,8 @@ const errInvalidCommandX: "invalid command: \'$1\'", errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", - errTemplateInstantiationTooNested: "template/macro instantiation too nested", + errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N", + errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N", errInstantiationFrom: "template/generic instantiation from here", errInvalidIndexValueForTuple: "invalid index value for tuple subscript", errCommandExpectsFilename: "command expects a filename argument", @@ -495,15 +495,18 @@ type # and parsed; usually 'nil' but is used # for 'nimsuggest' hash*: string # the checksum of the file - + when defined(nimpretty): + fullContent*: string + FileIndex* = distinct int32 TLineInfo* = object # This is designed to be as small as possible, # because it is used # in syntax nodes. We save space here by using # two int16 and an int32. # On 64 bit and on 32 bit systems this is # only 8 bytes. - line*, col*: int16 - fileIndex*: int32 + line*: uint16 + col*: int16 + fileIndex*: FileIndex when defined(nimpretty): offsetA*, offsetB*: int commentOffsetA*, commentOffsetB*: int @@ -517,6 +520,8 @@ type ERecoverableError* = object of ValueError ESuggestDone* = object of Exception +proc `==`*(a, b: FileIndex): bool {.borrow.} + const NotesVerbosity*: array[0..3, TNoteKinds] = [ {low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit, @@ -541,14 +546,14 @@ const {low(TNoteKind)..high(TNoteKind)}] const - InvalidFileIDX* = int32(-1) + InvalidFileIDX* = FileIndex(-1) var ForeignPackageNotes*: TNoteKinds = {hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting} - filenameToIndexTbl = initTable[string, int32]() + filenameToIndexTbl = initTable[string, FileIndex]() fileInfos*: seq[TFileInfo] = @[] - systemFileIdx*: int32 + systemFileIdx*: FileIndex proc toCChar*(c: char): string = case c @@ -583,6 +588,18 @@ proc newFileInfo(fullPath, projPath: string): TFileInfo = result.quotedFullName = fullPath.makeCString if optEmbedOrigSrc in gGlobalOptions or true: result.lines = @[] + when defined(nimpretty): + if result.fullPath.len > 0: + try: + result.fullContent = readFile(result.fullPath) + except IOError: + #rawMessage(errCannotOpenFile, result.fullPath) + # XXX fixme + result.fullContent = "" + +when defined(nimpretty): + proc fileSection*(fid: FileIndex; a, b: int): string = + substr(fileInfos[fid.int].fullContent, a, b) proc fileInfoKnown*(filename: string): bool = var @@ -593,7 +610,7 @@ proc fileInfoKnown*(filename: string): bool = canon = filename result = filenameToIndexTbl.hasKey(canon) -proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 = +proc fileInfoIdx*(filename: string; isKnownFile: var bool): FileIndex = var canon: string pseudoPath = false @@ -611,28 +628,28 @@ proc fileInfoIdx*(filename: string; isKnownFile: var bool): int32 = result = filenameToIndexTbl[canon] else: isKnownFile = false - result = fileInfos.len.int32 + result = fileInfos.len.FileIndex fileInfos.add(newFileInfo(canon, if pseudoPath: filename else: canon.shortenDir)) filenameToIndexTbl[canon] = result -proc fileInfoIdx*(filename: string): int32 = +proc fileInfoIdx*(filename: string): FileIndex = var dummy: bool result = fileInfoIdx(filename, dummy) -proc newLineInfo*(fileInfoIdx: int32, line, col: int): TLineInfo = +proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = result.fileIndex = fileInfoIdx - result.line = int16(line) + result.line = uint16(line) result.col = int16(col) proc newLineInfo*(filename: string, line, col: int): TLineInfo {.inline.} = result = newLineInfo(filename.fileInfoIdx, line, col) fileInfos.add(newFileInfo("", "command line")) -var gCmdLineInfo* = newLineInfo(int32(0), 1, 1) +var gCmdLineInfo* = newLineInfo(FileIndex(0), 1, 1) fileInfos.add(newFileInfo("", "compilation artifact")) -var gCodegenLineInfo* = newLineInfo(int32(1), 1, 1) +var gCodegenLineInfo* = newLineInfo(FileIndex(1), 1, 1) proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = raise newException(ERecoverableError, msg) @@ -648,9 +665,9 @@ var gMainPackageNotes*: TNoteKinds = NotesVerbosity[1] proc unknownLineInfo*(): TLineInfo = - result.line = int16(-1) + result.line = uint16(0) result.col = int16(-1) - result.fileIndex = -1 + result.fileIndex = InvalidFileIDX type Severity* {.pure.} = enum ## VS Code only supports these three @@ -712,32 +729,32 @@ proc getInfoContext*(index: int): TLineInfo = if i >=% L: result = unknownLineInfo() else: result = msgContext[i] -template toFilename*(fileIdx: int32): string = - (if fileIdx < 0: "???" else: fileInfos[fileIdx].projPath) +template toFilename*(fileIdx: FileIndex): string = + (if fileIdx.int32 < 0: "???" else: fileInfos[fileIdx.int32].projPath) -proc toFullPath*(fileIdx: int32): string = - if fileIdx < 0: result = "???" - else: result = fileInfos[fileIdx].fullPath +proc toFullPath*(fileIdx: FileIndex): string = + if fileIdx.int32 < 0: result = "???" + else: result = fileInfos[fileIdx.int32].fullPath -proc setDirtyFile*(fileIdx: int32; filename: string) = - assert fileIdx >= 0 - fileInfos[fileIdx].dirtyFile = filename +proc setDirtyFile*(fileIdx: FileIndex; filename: string) = + assert fileIdx.int32 >= 0 + fileInfos[fileIdx.int32].dirtyFile = filename -proc setHash*(fileIdx: int32; hash: string) = - assert fileIdx >= 0 - shallowCopy(fileInfos[fileIdx].hash, hash) +proc setHash*(fileIdx: FileIndex; hash: string) = + assert fileIdx.int32 >= 0 + shallowCopy(fileInfos[fileIdx.int32].hash, hash) -proc getHash*(fileIdx: int32): string = - assert fileIdx >= 0 - shallowCopy(result, fileInfos[fileIdx].hash) +proc getHash*(fileIdx: FileIndex): string = + assert fileIdx.int32 >= 0 + shallowCopy(result, fileInfos[fileIdx.int32].hash) -proc toFullPathConsiderDirty*(fileIdx: int32): string = - if fileIdx < 0: +proc toFullPathConsiderDirty*(fileIdx: FileIndex): string = + if fileIdx.int32 < 0: result = "???" - elif not fileInfos[fileIdx].dirtyFile.isNil: - result = fileInfos[fileIdx].dirtyFile + elif not fileInfos[fileIdx.int32].dirtyFile.isNil: + result = fileInfos[fileIdx.int32].dirtyFile else: - result = fileInfos[fileIdx].fullPath + result = fileInfos[fileIdx.int32].fullPath template toFilename*(info: TLineInfo): string = info.fileIndex.toFilename @@ -746,15 +763,15 @@ template toFullPath*(info: TLineInfo): string = info.fileIndex.toFullPath proc toMsgFilename*(info: TLineInfo): string = - if info.fileIndex < 0: + if info.fileIndex.int32 < 0: result = "???" elif gListFullPaths: - result = fileInfos[info.fileIndex].fullPath + result = fileInfos[info.fileIndex.int32].fullPath else: - result = fileInfos[info.fileIndex].projPath + result = fileInfos[info.fileIndex.int32].projPath proc toLinenumber*(info: TLineInfo): int {.inline.} = - result = info.line + result = int info.line proc toColumn*(info: TLineInfo): int {.inline.} = result = info.col @@ -771,7 +788,7 @@ proc `??`* (info: TLineInfo, filename: string): bool = # only for debugging purposes result = filename in info.toFilename -const trackPosInvalidFileIdx* = -2 # special marker so that no suggestions +const trackPosInvalidFileIdx* = FileIndex(-2) # special marker so that no suggestions # are produced within comments and string literals var gTrackPos*: TLineInfo var gTrackPosAttached*: bool ## whether the tracking position was attached to some @@ -910,7 +927,7 @@ proc writeContext(lastinfo: TLineInfo) = else: styledMsgWriteln(styleBright, PosFormat % [toMsgFilename(msgContext[i]), - coordToStr(msgContext[i].line), + coordToStr(msgContext[i].line.int), coordToStr(msgContext[i].col+1)], resetStyle, getMessageStr(errInstantiationFrom, "")) @@ -978,7 +995,7 @@ proc formatMsg*(info: TLineInfo, msg: TMsgKind, arg: string): string = of warnMin..warnMax: WarningTitle of hintMin..hintMax: HintTitle else: ErrorTitle - result = PosFormat % [toMsgFilename(info), coordToStr(info.line), + result = PosFormat % [toMsgFilename(info), coordToStr(info.line.int), coordToStr(info.col+1)] & title & getMessageStr(msg, arg) @@ -1019,7 +1036,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, # NOTE: currently line info line numbers start with 1, # but column numbers start with 0, however most editors expect # first column to be 1, so we need to +1 here - let x = PosFormat % [toMsgFilename(info), coordToStr(info.line), + let x = PosFormat % [toMsgFilename(info), coordToStr(info.line.int), coordToStr(info.col+1)] let s = getMessageStr(msg, arg) @@ -1032,7 +1049,7 @@ proc liMessage(info: TLineInfo, msg: TMsgKind, arg: string, KindColor, `%`(KindFormat, kind)) else: styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) - if msg in errMin..errMax and hintSource in gNotes: + if hintSource in gNotes: info.writeSurroundingSrc handleError(msg, eh, s) @@ -1077,30 +1094,30 @@ template assertNotNil*(e): untyped = template internalAssert*(e: bool) = if not e: internalError($instantiationInfo()) -proc addSourceLine*(fileIdx: int32, line: string) = - fileInfos[fileIdx].lines.add line.rope +proc addSourceLine*(fileIdx: FileIndex, line: string) = + fileInfos[fileIdx.int32].lines.add line.rope proc sourceLine*(i: TLineInfo): Rope = - if i.fileIndex < 0: return nil + if i.fileIndex.int32 < 0: return nil - if not optPreserveOrigSource and fileInfos[i.fileIndex].lines.len == 0: + if not optPreserveOrigSource and fileInfos[i.fileIndex.int32].lines.len == 0: try: for line in lines(i.toFullPath): addSourceLine i.fileIndex, line.string except IOError: discard - internalAssert i.fileIndex < fileInfos.len + internalAssert i.fileIndex.int32 < fileInfos.len # can happen if the error points to EOF: - if i.line > fileInfos[i.fileIndex].lines.len: return nil + if i.line.int > fileInfos[i.fileIndex.int32].lines.len: return nil - result = fileInfos[i.fileIndex].lines[i.line-1] + result = fileInfos[i.fileIndex.int32].lines[i.line.int-1] proc quotedFilename*(i: TLineInfo): Rope = - internalAssert i.fileIndex >= 0 + internalAssert i.fileIndex.int32 >= 0 if optExcessiveStackTrace in gGlobalOptions: - result = fileInfos[i.fileIndex].quotedFullName + result = fileInfos[i.fileIndex.int32].quotedFullName else: - result = fileInfos[i.fileIndex].quotedName + result = fileInfos[i.fileIndex.int32].quotedName ropes.errorHandler = proc (err: RopesError, msg: string, useWarning: bool) = case err diff --git a/compiler/nim.nim b/compiler/nim.nim index 89225a5e0..8f3463be9 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -90,14 +90,6 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) = ex = quoteShell( completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir)) execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments) - elif gCmd == cmdCompileToPHP: - var ex: string - if options.outFile.len > 0: - ex = options.outFile.prependCurDir.quoteShell - else: - ex = quoteShell( - completeCFilePath(changeFileExt(gProjectFull, "php").prependCurDir)) - execExternalProgram("php " & ex & ' ' & commands.arguments) else: var binPath: string if options.outFile.len > 0: diff --git a/compiler/nimfix/pretty.nim b/compiler/nimfix/pretty.nim index 55603f4cd..4627264dc 100644 --- a/compiler/nimfix/pretty.nim +++ b/compiler/nimfix/pretty.nim @@ -28,7 +28,7 @@ proc overwriteFiles*() = let doStrip = options.getConfigVar("pretty.strip").normalize == "on" for i in 0 .. high(gSourceFiles): if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and - (not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx): + (not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx.FileIndex): let newFile = if gOverWrite: gSourceFiles[i].fullpath else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim") try: @@ -95,7 +95,7 @@ proc beautifyName(s: string, k: TSymKind): string = proc replaceInFile(info: TLineInfo; newName: string) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1] var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) @@ -107,8 +107,8 @@ proc replaceInFile(info: TLineInfo; newName: string) = if differ(line, first, last, newName): # last-first+1 != newName.len or var x = line.substr(0, first-1) & newName & line.substr(last+1) - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int].dirty = true proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) = let beau = beautifyName(s, k) @@ -136,7 +136,7 @@ template styleCheckDef*(s: PSym) = styleCheckDef(s.info, s, s.kind) proc styleCheckUseImpl(info: TLineInfo; s: PSym) = - if info.fileIndex < 0: return + if info.fileIndex.int < 0: return # we simply convert it to what it looks like in the definition # for consistency diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index f1d24183b..ecb4b0093 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -16,13 +16,13 @@ type lines*: seq[string] dirty*, isNimfixFile*: bool fullpath*, newline*: string - fileIdx*: int32 + fileIdx*: FileIndex var gSourceFiles*: seq[TSourceFile] = @[] proc loadFile*(info: TLineInfo) = - let i = info.fileIndex + let i = info.fileIndex.int if i >= gSourceFiles.len: gSourceFiles.setLen(i+1) if gSourceFiles[i].lines.isNil: @@ -64,7 +64,7 @@ proc differ*(line: string, a, b: int, x: string): bool = proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] var first = min(info.col.int, line.len) if first < 0: return #inc first, skipIgnoreCase(line, "proc ", first) @@ -75,8 +75,8 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) = let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int32].dirty = true #if newSym.s == "File": writeStackTrace() proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = @@ -85,10 +85,10 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) = proc replaceComment*(info: TLineInfo) = loadFile(info) - let line = gSourceFiles[info.fileIndex].lines[info.line-1] + let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1] var first = info.col.int if line[first] != '#': inc first var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape - system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x) - gSourceFiles[info.fileIndex].dirty = true + system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x) + gSourceFiles[info.fileIndex.int32].dirty = true diff --git a/compiler/options.nim b/compiler/options.nim index 69a555b3f..93a3f1796 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -81,7 +81,6 @@ type # **keep binary compatible** cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, - cmdCompileToPHP, cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, cmdGenDepend, cmdDump, cmdCheck, # semantic checking for whole project diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 8540f1b32..02c48c16d 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -235,7 +235,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult result = arLValue else: result = isAssignable(owner, n.sons[0], isUnsafeAddr) - if result != arNone and sfDiscriminant in n.sons[1].sym.flags: + if result != arNone and n[1].kind == nkSym and + sfDiscriminant in n[1].sym.flags: result = arDiscriminant of nkBracketExpr: if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in diff --git a/compiler/parser.nim b/compiler/parser.nim index 0019b7acb..ac0a57770 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -83,7 +83,7 @@ proc getTok(p: var TParser) = rawGetTok(p.lex, p.tok) p.hasProgress = true -proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream, +proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, cache: IdentCache; strongSpaces=false) = ## Open a parser, using the given arguments to set up its internal state. @@ -125,7 +125,13 @@ proc rawSkipComment(p: var TParser, node: PNode) = if p.tok.tokType == tkComment: if node != nil: if node.comment == nil: node.comment = "" - add(node.comment, p.tok.literal) + when defined(nimpretty): + if p.tok.commentOffsetB > p.tok.commentOffsetA: + add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) + else: + add node.comment, p.tok.literal + else: + add(node.comment, p.tok.literal) else: parMessage(p, errInternal, "skipComment") getTok(p) diff --git a/compiler/passes.nim b/compiler/passes.nim index f079100ea..d7c181676 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -52,8 +52,8 @@ proc makePass*(open: TPassOpen = nil, # the semantic checker needs these: var - gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.} - gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.} + gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.} + gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.} # implementation @@ -63,20 +63,6 @@ proc skipCodegen*(n: PNode): bool {.inline.} = # error count instead. result = msgs.gErrorCounter > 0 -proc astNeeded*(s: PSym): bool = - # The ``rodwrite`` module uses this to determine if the body of a proc - # needs to be stored. The passes manager frees s.sons[codePos] when - # appropriate to free the procedure body's memory. This is important - # to keep memory usage down. - if (s.kind in {skMethod, skProc, skFunc}) and - ({sfCompilerProc, sfCompileTime} * s.flags == {}) and - (s.typ.callConv != ccInline) and - (s.ast.sons[genericParamsPos].kind == nkEmpty): - result = false - # XXX this doesn't really make sense with excessive CTFE - else: - result = true - const maxPasses = 10 @@ -153,7 +139,7 @@ proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) = m = gPasses[i].close(graph, a[i], m) a[i] = nil # free the memory here -proc resolveMod(module, relativeTo: string): int32 = +proc resolveMod(module, relativeTo: string): FileIndex = let fullPath = findModule(module, relativeTo) if fullPath.len == 0: result = InvalidFileIDX @@ -166,7 +152,7 @@ proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, let relativeTo = m.info.toFullPath for module in items(implicits): # implicit imports should not lead to a module importing itself - if m.position != resolveMod(module, relativeTo): + if m.position != resolveMod(module, relativeTo).int32: var importStmt = newNodeI(nodeKind, gCmdLineInfo) var str = newStrNode(nkStrLit, module) str.info = gCmdLineInfo @@ -180,7 +166,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, p: TParsers a: TPassContextArray s: PLLStream - fileIdx = module.fileIdx + fileIdx = FileIndex module.fileIdx if module.id < 0: # new module caching mechanism: for i in 0..<gPassesLen: diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim index eba6f0b62..fe438d58b 100644 --- a/compiler/pbraces.nim +++ b/compiler/pbraces.nim @@ -19,7 +19,7 @@ proc getTok(p: var TParser) = ## `tok` member. rawGetTok(p.lex, p.tok) -proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream; +proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream; cache: IdentCache) = ## Open a parser, using the given arguments to set up its internal state. ## diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index bb7801f6f..cb11564a4 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -543,7 +543,7 @@ proc pragmaLine(c: PContext, n: PNode) = else: # XXX this produces weird paths which are not properly resolved: n.info.fileIndex = msgs.fileInfoIdx(x.strVal) - n.info.line = int16(y.intVal) + n.info.line = uint16(y.intVal) else: localError(n.info, errXExpected, "tuple") else: diff --git a/compiler/procfind.nim b/compiler/procfind.nim index 137765ddb..f7d86aaef 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -66,12 +66,10 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym = proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = const flags = {ExactGenericParams, ExactTypeDescValues, ExactConstraints, IgnoreCC} - var it: TIdentIter - result = initIdentIter(it, scope.symbols, fn.name) while result != nil: - if result.kind == fn.kind and sameType(result.typ, fn.typ, flags): + if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags): case equalParams(result.typ.n, fn.typ.n) of paramsEqual: if (sfExported notin result.flags) and (sfExported in fn.flags): @@ -85,11 +83,8 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = return of paramsNotEqual: discard - result = nextIdentIter(it, scope.symbols) - return nil - proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = result = searchForProcNew(c, scope, fn) when false: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 7d513afb1..0e631a898 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -39,8 +39,7 @@ type inPragma: int when defined(nimpretty): pendingNewlineCount: int - origContent: string - + fid*: FileIndex # We render the source code in a two phases: The first # determines how long the subtree will likely be, the second @@ -354,13 +353,13 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = proc atom(g: TSrcGen; n: PNode): string = when defined(nimpretty): let comment = if n.info.commentOffsetA < n.info.commentOffsetB: - " " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB) + " " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB) else: "" if n.info.offsetA <= n.info.offsetB: # for some constructed tokens this can not be the case and we're better # off to not mess with the offset then. - return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment + return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment var f: float32 case n.kind of nkEmpty: result = "" @@ -1460,17 +1459,13 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = proc `$`*(n: PNode): string = n.renderTree proc renderModule*(n: PNode, infile, outfile: string, - renderFlags: TRenderFlags = {}) = + renderFlags: TRenderFlags = {}; + fid = FileIndex(-1)) = var f: File g: TSrcGen initSrcGen(g, renderFlags) - when defined(nimpretty): - try: - g.origContent = readFile(infile) - except IOError: - rawMessage(errCannotOpenFile, infile) - + g.fid = fid for i in countup(0, sonsLen(n) - 1): gsub(g, n.sons[i]) optNL(g) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index cde5b3a9f..878e3b11e 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -1,6 +1,6 @@ -import - intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils, +import + intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils, sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables type @@ -135,13 +135,13 @@ proc hasIncludes(n:PNode): bool = if a.kind == nkIncludeStmt: return true -proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32; +proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = result = syntaxes.parseFile(fileIdx, cache) graph.addDep(s, fileIdx) - graph.addIncludeDep(s.position.int32, fileIdx) + graph.addIncludeDep(FileIndex s.position, fileIdx) -proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, +proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, modulePath: string, includedFiles: var IntSet, cache: IdentCache): PNode = # Parses includes and injects them in the current tree @@ -153,13 +153,13 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode, for i in 0..<a.len: var f = checkModuleName(a.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(includedFiles, f): + if containsOrIncl(includedFiles, f.int): localError(a.info, errRecursiveDependencyX, f.toFilename) else: let nn = includeModule(graph, module, f, cache) - let nnn = expandIncludes(graph, module, nn, modulePath, + let nnn = expandIncludes(graph, module, nn, modulePath, includedFiles, cache) - excl(includedFiles, f) + excl(includedFiles, f.int) for b in nnn: result.add b else: @@ -429,8 +429,8 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN if n.hasForbiddenPragma: return n var includedFiles = initIntSet() - let mpath = module.fileIdx.toFullPath - let n = expandIncludes(graph, module, n, mpath, + let mpath = module.fileIdx.FileIndex.toFullPath + let n = expandIncludes(graph, module, n, mpath, includedFiles, cache).splitSections result = newNodeI(nkStmtList, n.info) var deps = newSeq[(IntSet, IntSet)](n.len) diff --git a/compiler/rod.nim b/compiler/rod.nim index bc2f3931e..c144f15ef 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -9,124 +9,18 @@ ## This module implements the canonalization for the various caching mechanisms. -import ast, idgen +import ast, idgen, msgs when not defined(nimSymbolfiles): template setupModuleCache* = discard template storeNode*(module: PSym; n: PNode) = discard template loadNode*(module: PSym; index: var int): PNode = PNode(nil) - template getModuleId*(fileIdx: int32; fullpath: string): int = getID() + template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID() - template addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) = discard + template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard template storeRemaining*(module: PSym) = discard else: include rodimpl - -when false: - type - BlobWriter* = object - buf: string - pos: int - - SerializationAction = enum acRead, acWrite - - # Varint implementation inspired by SQLite. - proc rdVaruint64(z: ptr UncheckedArray[byte]; n: int; pResult: var uint64): int = - if z[0] <= 240: - pResult = z[0] - return 1 - if z[0] <= 248: - if n < 2: return 0 - pResult = (z[0] - 241) * 256 + z[1] + 240 - return 2 - if n < z[0]-246: return 0 - if z[0] == 249: - pResult = 2288 + 256*z[1] + z[2] - return 3 - if z[0] == 250: - pResult = (z[1] shl 16u64) + (z[2] shl 8u64) + z[3] - return 4 - let x = (z[1] shl 24) + (z[2] shl 16) + (z[3] shl 8) + z[4] - if z[0] == 251: - pResult = x - return 5 - if z[0] == 252: - pResult = (((uint64)x) shl 8) + z[5] - return 6 - if z[0] == 253: - pResult = (((uint64)x) shl 16) + (z[5] shl 8) + z[6] - return 7 - if z[0] == 254: - pResult = (((uint64)x) shl 24) + (z[5] shl 16) + (z[6] shl 8) + z[7] - return 8 - pResult = (((uint64)x) shl 32) + - (0xffffffff & ((z[5] shl 24) + (z[6] shl 16) + (z[7] shl 8) + z[8])) - return 9 - - proc varintWrite32(z: ptr UncheckedArray[byte]; y: uint32) = - z[0] = uint8(y shr 24) - z[1] = uint8(y shr 16) - z[2] = uint8(y shr 8) - z[3] = uint8(y) - - proc sqlite4PutVarint64(z: ptr UncheckedArray[byte], x: uint64): int = - ## Write a varint into z. The buffer z must be at least 9 characters - ## long to accommodate the largest possible varint. Returns the number of - ## bytes used. - if x <= 240: - z[0] = uint8 x - return 1 - if x <= 2287: - y = uint32(x - 240) - z[0] = uint8(y shr 8 + 241) - z[1] = uint8(y and 255) - return 2 - if x <= 67823: - y = uint32(x - 2288) - z[0] = 249 - z[1] = uint8(y shr 8) - z[2] = uint8(y and 255) - return 3 - let y = uint32 x - let w = uint32(x shr 32) - if w == 0: - if y <= 16777215: - z[0] = 250 - z[1] = uint8(y shr 16) - z[2] = uint8(y shr 8) - z[3] = uint8(y) - return 4 - z[0] = 251 - varintWrite32(z+1, y) - return 5 - if w <= 255: - z[0] = 252 - z[1] = uint8 w - varintWrite32(z+2, y) - return 6 - if w <= 65535: - z[0] = 253 - z[1] = uint8(w shr 8) - z[2] = uint8 w - varintWrite32(z+3, y) - return 7 - if w <= 16777215: - z[0] = 254 - z[1] = uint8(w shr 16) - z[2] = uint8(w shr 8) - z[3] = uint8 w - varintWrite32(z+4, y) - return 8 - z[0] = 255 - varintWrite32(z+1, w) - varintWrite32(z+5, y) - return 9 - - template field(x: BiggestInt; action: SerializationAction) = - when action == acRead: - readBiggestInt(x) - else: - writeBiggestInt() diff --git a/compiler/rodread.nim b/compiler/rodread.nim index 84f175cb5..aa699e337 100644 --- a/compiler/rodread.nim +++ b/compiler/rodread.nim @@ -126,8 +126,8 @@ type s: cstring # mmap'ed file contents options: TOptions reason: TReasonForRecompile - modDeps: seq[int32] - files: seq[int32] + modDeps: seq[FileIndex] + files: seq[FileIndex] dataIdx: int # offset of start of data section convertersIdx: int # offset of start of converters section initIdx, interfIdx, compilerProcsIdx, methodsIdx: int @@ -163,11 +163,11 @@ proc decodeLineInfo(r: PRodReader, info: var TLineInfo) = else: info.col = int16(decodeVInt(r.s, r.pos)) if r.s[r.pos] == ',': inc(r.pos) - if r.s[r.pos] == ',': info.line = -1'i16 - else: info.line = int16(decodeVInt(r.s, r.pos)) + if r.s[r.pos] == ',': info.line = 0'u16 + else: info.line = uint16(decodeVInt(r.s, r.pos)) if r.s[r.pos] == ',': inc(r.pos) - info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col) + info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col) proc skipNode(r: PRodReader) = assert r.s[r.pos] == '(' @@ -586,7 +586,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool = # new command forces us to consider it here :-) case old of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToJS, cmdCompileToPHP, cmdCompileToLLVM: + cmdCompileToJS, cmdCompileToLLVM: if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef, cmdInteractive}: return false @@ -861,29 +861,29 @@ proc loadMethods(r: PRodReader) = r.methods.add(rrGetSym(r, d, unknownLineInfo())) if r.s[r.pos] == ' ': inc(r.pos) -proc getHash*(fileIdx: int32): SecureHash = - if fileIdx <% gMods.len and gMods[fileIdx].hashDone: - return gMods[fileIdx].hash +proc getHash*(fileIdx: FileIndex): SecureHash = + if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone: + return gMods[fileIdx.int32].hash result = secureHashFile(fileIdx.toFullPath) - if fileIdx >= gMods.len: setLen(gMods, fileIdx+1) - gMods[fileIdx].hash = result + if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1) + gMods[fileIdx.int32].hash = result template growCache*(cache, pos) = if cache.len <= pos: cache.setLen(pos+1) -proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile = +proc checkDep(fileIdx: FileIndex; cache: IdentCache): TReasonForRecompile = assert fileIdx != InvalidFileIDX - growCache gMods, fileIdx - if gMods[fileIdx].reason != rrEmpty: + growCache gMods, fileIdx.int32 + if gMods[fileIdx.int32].reason != rrEmpty: # reason has already been computed for this module: - return gMods[fileIdx].reason + return gMods[fileIdx.int32].reason let filename = fileIdx.toFilename var hash = getHash(fileIdx) - gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles + gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles result = rrNone var rodfile = toGeneratedFile(filename.withPackageName, RodExt) - var r = newRodReader(rodfile, hash, fileIdx, cache) + var r = newRodReader(rodfile, hash, fileIdx.int32, cache) if r == nil: result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist) else: @@ -907,19 +907,18 @@ proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile = # recompilation is necessary: if r != nil: memfiles.close(r.memfile) r = nil - gMods[fileIdx].rd = r - gMods[fileIdx].reason = result # now we know better + gMods[fileIdx.int32].rd = r + gMods[fileIdx.int32].reason = result # now we know better proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader = - let fileIdx = module.fileIdx if gSymbolFiles in {disabledSf, writeOnlySf, v2Sf}: module.id = getID() return nil idgen.loadMaxIds(options.gProjectPath / options.gProjectName) - + let fileIdx = FileIndex module.fileIdx discard checkDep(fileIdx, cache) - if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile") - result = gMods[fileIdx].rd + if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile") + result = gMods[fileIdx.int32].rd if result != nil: module.id = result.moduleID result.syms[module.id] = module @@ -1153,7 +1152,7 @@ proc viewFile(rodfile: string) = if r.s[r.pos] == '\x0A': inc(r.pos) inc(r.line) - outf.write(w, " ", inclHash, "\n") + outf.write(w.int32, " ", inclHash, "\n") if r.s[r.pos] == ')': inc(r.pos) outf.write(")\n") of "DEPS": @@ -1163,7 +1162,7 @@ proc viewFile(rodfile: string) = let v = int32(decodeVInt(r.s, r.pos)) r.modDeps.add(r.files[v]) if r.s[r.pos] == ' ': inc(r.pos) - outf.write(" ", r.files[v]) + outf.write(" ", r.files[v].int32) outf.write("\n") of "INTERF", "COMPILERPROCS": inc r.pos, 2 diff --git a/compiler/rodwrite.nim b/compiler/rodwrite.nim index 96deb1d5a..0ae687268 100644 --- a/compiler/rodwrite.nim +++ b/compiler/rodwrite.nim @@ -55,7 +55,7 @@ proc fileIdx(w: PRodWriter, filename: string): int = w.files[result] = filename template filename*(w: PRodWriter): string = - w.module.filename + toFilename(FileIndex w.module.position) proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter = new(result) @@ -125,14 +125,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode, result.add('?') encodeVInt(n.info.col, result) result.add(',') - encodeVInt(n.info.line, result) + encodeVInt(int n.info.line, result) result.add(',') encodeVInt(fileIdx(w, toFullPath(n.info)), result) elif fInfo.line != n.info.line: result.add('?') encodeVInt(n.info.col, result) result.add(',') - encodeVInt(n.info.line, result) + encodeVInt(int n.info.line, result) elif fInfo.col != n.info.col: result.add('?') encodeVInt(n.info.col, result) @@ -303,7 +303,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) = result.add('?') if s.info.col != -1'i16: encodeVInt(s.info.col, result) result.add(',') - if s.info.line != -1'i16: encodeVInt(s.info.line, result) + if s.info.line != 0'u16: encodeVInt(int s.info.line, result) result.add(',') encodeVInt(fileIdx(w, toFullPath(s.info)), result) if s.owner != nil: @@ -642,7 +642,7 @@ proc process(c: PPassContext, n: PNode): PNode = proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = if module.id < 0: internalError("rodwrite: module ID not set") - var w = newRodWriter(rodread.getHash module.fileIdx, module, cache) + var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache) rawAddInterfaceSym(w, module) result = w diff --git a/compiler/sem.nim b/compiler/sem.nim index 4fef1bc60..041f2e127 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -377,7 +377,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, ## reassigned, and binding the unbound identifiers that the macro output ## contains. inc(evalTemplateCounter) - if evalTemplateCounter > 100: + if evalTemplateCounter > evalTemplateLimit: globalError(s.info, errTemplateInstantiationTooNested) c.friendModules.add(s.owner.getModule) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 3996188dc..bcc1bba15 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -144,7 +144,7 @@ proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair = proc filename*(c: PContext): string = # the module's filename - return c.module.filename + return toFilename(FileIndex c.module.position) proc scopeDepth*(c: PContext): int {.inline.} = result = if c.currentScope != nil: c.currentScope.depthLevel diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4256e0aa6..279b6cc57 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -526,6 +526,14 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = if isAssignable(c, it) notin {arLValue, arLocalLValue}: if it.kind != nkHiddenAddr: localError(it.info, errVarForOutParamNeededX, $it) + # bug #5113: disallow newSeq(result) where result is a 'var T': + if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}: + var arg = n[1] #.skipAddr + if arg.kind == nkHiddenDeref: arg = arg[0] + if arg.kind == nkSym and arg.sym.kind == skResult and + arg.typ.skipTypes(abstractInst).kind in {tyVar, tyLent}: + localError(n.info, errXStackEscape, renderTree(n[1], {renderNoComments})) + return for i in countup(1, sonsLen(n) - 1): if n.sons[i].kind == nkHiddenCallConv: @@ -1334,11 +1342,11 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = let root = exprRoot(n) if root != nil and root.owner == c.p.owner: if root.kind in {skLet, skVar, skTemp} and sfGlobal notin root.flags: - localError(n.info, "'$1' escapes its stack frame; context: '$2'" % [ - root.name.s, renderTree(n, {renderNoComments})]) + localError(n.info, "'$1' escapes its stack frame; context: '$2'; see $3/var_t_return.html" % [ + root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl]) elif root.kind == skParam and root.position != 0: - localError(n.info, "'$1' is not the first parameter; context: '$2'" % [ - root.name.s, renderTree(n, {renderNoComments})]) + localError(n.info, "'$1' is not the first parameter; context: '$2'; see $3/var_t_return.html" % [ + root.name.s, renderTree(n, {renderNoComments}), explanationsBaseUrl]) case n.kind of nkHiddenAddr, nkAddr: return n of nkHiddenDeref, nkDerefExpr: return n.sons[0] diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 096fc19e0..6fcc9a0a4 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -483,6 +483,19 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode = proc getConstExpr(m: PSym, n: PNode): PNode = result = nil + + proc getSrcTimestamp(): DateTime = + try: + result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH", + "not a number")))) + except ValueError: + # Environment variable malformed. + # https://reproducible-builds.org/specs/source-date-epoch/: "If the + # value is malformed, the build process SHOULD exit with a non-zero + # error code", which this doesn't do. This uses local time, because + # that maintains compatibility with existing usage. + result = local(getTime()) + case n.kind of nkSym: var s = n.sym @@ -492,8 +505,10 @@ proc getConstExpr(m: PSym, n: PNode): PNode = of skConst: case s.magic of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n) - of mCompileDate: result = newStrNodeT(times.getDateStr(), n) - of mCompileTime: result = newStrNodeT(times.getClockStr(), n) + of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(), + "yyyy-MM-dd"), n) + of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(), + "HH:mm:ss"), n) of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n) of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n) of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f90e5c5f9..8d7747fb4 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -779,20 +779,15 @@ proc semRaise(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) if n[0].kind != nkEmpty: - n[0] = semExprWithType(c, n[0]) let typ = n[0].typ - if not isImportedException(typ): - if typ.kind != tyRef or typ.lastSon.kind != tyObject: localError(n.info, errExprCannotBeRaised) - if not isException(typ.lastSon): localError(n.info, "raised object of type $1 does not inherit from Exception", [typeToString(typ)]) - proc addGenericParamListToScope(c: PContext, n: PNode) = if n.kind != nkGenericParams: illFormedAst(n) for i in countup(0, sonsLen(n)-1): @@ -1100,12 +1095,12 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode = for i in 0..<n.len: var f = checkModuleName(n.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(c.includedFiles, f): + if containsOrIncl(c.includedFiles, f.int): localError(n.info, errRecursiveDependencyX, f.toFilename) else: let code = gIncludeFile(c.graph, c.module, f, c.cache) gatherStmts c, code, result - excl(c.includedFiles, f) + excl(c.includedFiles, f.int) of nkStmtList: for i in 0 ..< n.len: gatherStmts(c, n.sons[i], result) @@ -1789,11 +1784,11 @@ proc evalInclude(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(n) - 1): var f = checkModuleName(n.sons[i]) if f != InvalidFileIDX: - if containsOrIncl(c.includedFiles, f): + if containsOrIncl(c.includedFiles, f.int): localError(n.info, errRecursiveDependencyX, f.toFilename) else: addSon(result, semStmt(c, gIncludeFile(c.graph, c.module, f, c.cache))) - excl(c.includedFiles, f) + excl(c.includedFiles, f.int) proc setLine(n: PNode, info: TLineInfo) = for i in 0 ..< safeLen(n): setLine(n.sons[i], info) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index af31495aa..555ec9867 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -415,7 +415,7 @@ when defined(nimsuggest): # Since TLineInfo defined a == operator that doesn't include the column, # we map TLineInfo to a unique int here for this lookup table: proc infoToInt(info: TLineInfo): int64 = - info.fileIndex + info.line.int64 shl 32 + info.col.int64 shl 48 + info.fileIndex.int64 + info.line.int64 shl 32 + info.col.int64 shl 48 proc addNoDup(s: PSym; info: TLineInfo) = # ensure nothing gets too slow: @@ -574,7 +574,7 @@ proc suggestEnum*(c: PContext; n: PNode; t: PType) = if outputs.len > 0: suggestQuit() proc suggestSentinel*(c: PContext) = - if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex: return + if gIdeCmd != ideSug or c.module.position != gTrackPos.fileIndex.int32: return if c.compilesContextId > 0: return inc(c.compilesContextId) var outputs: Suggestions = @[] diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 4745b1ac7..4014d4c58 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -138,7 +138,7 @@ proc evalPipe(p: var TParsers, n: PNode, filename: string, else: result = applyFilter(p, n, filename, result) -proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream; +proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache) = var s: PLLStream p.skin = skinStandard @@ -155,7 +155,7 @@ proc openParsers*(p: var TParsers, fileIdx: int32, inputstream: PLLStream; proc closeParsers*(p: var TParsers) = parser.closeParser(p.parser) -proc parseFile*(fileIdx: int32; cache: IdentCache): PNode {.procvar.} = +proc parseFile*(fileIdx: FileIndex; cache: IdentCache): PNode {.procvar.} = var p: TParsers f: File diff --git a/compiler/types.nim b/compiler/types.nim index a930779bf..5d60fa9b4 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -777,8 +777,8 @@ proc equalParams(a, b: PNode): TParamsEquality = return paramsNotEqual # paramsIncompatible; # continue traversal! If not equal, we can return immediately; else # it stays incompatible - if not sameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}): - if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): + if not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}): + if (a.typ == nil) or (b.typ == nil): result = paramsNotEqual # one proc has a result, the other not is OK else: result = paramsIncompatible # overloading by different @@ -970,7 +970,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameFlags(a, b) of tyGenericParam: result = sameChildrenAux(a, b, c) and sameFlags(a, b) - if result and ExactGenericParams in c.flags: + if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}: result = a.sym.position == b.sym.position of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink, diff --git a/compiler/vm.nim b/compiler/vm.nim index 09226988a..32db8f378 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1381,7 +1381,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNGetLine: decodeB(rkNode) let n = regs[rb].node - regs[ra].node = newIntNode(nkIntLit, n.info.line) + regs[ra].node = newIntNode(nkIntLit, n.info.line.int) regs[ra].node.info = n.info regs[ra].node.typ = n.typ of opcNGetColumn: @@ -1397,8 +1397,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let bNode = regs[rc].node # these are cstring to prevent string copy, and cmpIgnoreStyle from # takes cstring arguments - var aStrVal: cstring - var bStrVal: cstring + var aStrVal: cstring = nil + var bStrVal: cstring = nil # extract strVal from argument ``a`` case aNode.kind of {nkStrLit..nkTripleStrLit}: @@ -1407,8 +1407,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = aStrVal = aNode.ident.s.cstring of nkSym: aStrVal = aNode.sym.name.s.cstring + of nkOpenSymChoice, nkClosedSymChoice: + aStrVal = aNode[0].sym.name.s.cstring else: - stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + discard # extract strVal from argument ``b`` case bNode.kind of {nkStrLit..nkTripleStrLit}: @@ -1417,11 +1419,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = bStrVal = bNode.ident.s.cstring of nkSym: bStrVal = bNode.sym.name.s.cstring + of nkOpenSymChoice, nkClosedSymChoice: + bStrVal = bNode[0].sym.name.s.cstring else: - stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + discard # set result regs[ra].intVal = - ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0) + if aStrVal != nil and bStrVal != nil: + ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0) + else: + 0 + of opcStrToIdent: decodeB(rkNode) if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}: @@ -1513,7 +1521,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let x = newNodeI(TNodeKind(int(k)), if cc.kind != nkNilLit: cc.info - elif c.comesFromHeuristic.line > -1: + elif c.comesFromHeuristic.line != 0'u16: c.comesFromHeuristic elif c.callsite != nil and c.callsite.safeLen > 1: c.callsite[1].info @@ -1685,7 +1693,7 @@ proc evalConstExprAux(module: PSym; cache: IdentCache; prc: PSym, n: PNode, newSeq(tos.slots, c.prc.maxSlots) #for i in 0 ..< c.prc.maxSlots: tos.slots[i] = newNode(nkEmpty) result = rawExecute(c, start, tos).regToNode - if result.info.line < 0: result.info = n.info + if result.info.col < 0: result.info = n.info proc evalConstExpr*(module: PSym; cache: IdentCache, e: PNode): PNode = result = evalConstExprAux(module, cache, nil, e, emConst) @@ -1721,14 +1729,16 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = let posInCall = macroSym.typ.len + i yield (genericParam, call[posInCall]) +# to prevent endless recursion in macro instantiation +const evalMacroLimit = 1000 var evalMacroCounter: int proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, sym: PSym): PNode = # XXX globalError() is ugly here, but I don't know a better solution for now inc(evalMacroCounter) - if evalMacroCounter > 100: - globalError(n.info, errTemplateInstantiationTooNested) + if evalMacroCounter > evalMacroLimit: + globalError(n.info, errMacroInstantiationTooNested) # immediate macros can bypass any type and arity checking so we check the # arity here too: @@ -1738,7 +1748,7 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, setupGlobalCtx(module, cache) var c = globalCtx - c.comesFromHeuristic.line = -1 + c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig let start = genProc(c, sym) diff --git a/doc/advopt.txt b/doc/advopt.txt index 214ac8dd2..bf7dd7fb4 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -76,7 +76,7 @@ Advanced options: --NimblePath:PATH add a path for Nimble support --noNimblePath deactivate the Nimble path --noCppExceptions use default exception handling with C++ backend - --cppCompileToNamespace use namespace "Nim" for the generated C++ code + --cppCompileToNamespace use namespace "Nim" for the generated C++ code --excludePath:PATH exclude a path from the list of search paths --dynlibOverride:SYMBOL marks SYMBOL so that dynlib:SYMBOL has no effect and can be statically linked instead; diff --git a/doc/basicopt.txt b/doc/basicopt.txt index 4db2d5af7..4c11cc767 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -36,7 +36,7 @@ Options: --app:console|gui|lib|staticlib generate a console app|GUI app|DLL|static library -r, --run run the compiled program with given arguments - --advanced show advanced command line switches + --fullhelp show all command line switches -h, --help show this help Note, single letter options that take an argument require a colon. E.g. -p:PATH. diff --git a/doc/manual.rst b/doc/manual.rst index b114f0a8a..fbd043020 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3487,17 +3487,17 @@ returned value is an l-value and can be modified by the caller: .. code-block:: nim var g = 0 - proc WriteAccessToG(): var int = + proc writeAccessToG(): var int = result = g - WriteAccessToG() = 6 + writeAccessToG() = 6 assert g == 6 It is a compile time error if the implicitly introduced pointer could be used to access a location beyond its lifetime: .. code-block:: nim - proc WriteAccessToG(): var int = + proc writeAccessToG(): var int = var g = 0 result = g # Error! @@ -3512,6 +3512,24 @@ In the standard library every name of a routine that returns a ``var`` type starts with the prefix ``m`` per convention. +.. include:: manual/var_t_return.rst + +Future directions +~~~~~~~~~~~~~~~~~ + +Later versions of Nim can be more precise about the borrowing rule with +a syntax like: + +.. code-block:: nim + proc foo(other: Y; container: var X): var T from container + +Here ``var T from container`` explicitly exposes that the +location is deviated from the second parameter (called +'container' in this case). The syntax ``var T from p`` specifies a type +``varTy[T, 2]`` which is incompatible with ``varTy[T, 1]``. + + + Overloading of the subscript operator ------------------------------------- @@ -5241,15 +5259,21 @@ chance to convert it into a sequence. Macros ====== -A macro is a special kind of low level template. Macros can be used -to implement `domain specific languages`:idx:. +A macro is a special function that is executed at compile-time. +Normally the input for a macro is an abstract syntax +tree (AST) of the code that is passed to it. The macro can then do +transformations on it and return the transformed AST. The +transformed AST is then passed to the compiler as if the macro +invocation would have been replaced by its result in the source +code. This can be used to implement `domain specific +languages`:idx:. While macros enable advanced compile-time code transformations, they cannot change Nim's syntax. However, this is no real restriction because Nim's syntax is flexible enough anyway. To write macros, one needs to know how the Nim concrete syntax is converted -to an abstract syntax tree. +to an AST. There are two ways to invoke a macro: (1) invoking a macro like a procedure call (`expression macros`) @@ -5269,19 +5293,21 @@ variable number of arguments: # ``macros`` module: import macros - macro debug(n: varargs[untyped]): untyped = - # `n` is a Nim AST that contains the whole macro invocation - # this macro returns a list of statements: - result = newNimNode(nnkStmtList, n) + macro debug(args: varargs[untyped]): untyped = + # `args` is a collection of `NimNode` values that each contain the + # AST for an argument of the macro. A macro always has to + # return a `NimNode`. A node of kind `nnkStmtList` is suitable for + # this use case. + result = nnkStmtList.newTree() # iterate over any argument that is passed to this macro: - for i in 0..n.len-1: + for n in args: # add a call to the statement list that writes the expression; # `toStrLit` converts an AST to its string representation: - add(result, newCall("write", newIdentNode("stdout"), toStrLit(n[i]))) + result.add newCall("write", newIdentNode("stdout"), newLit(n.repr)) # add a call to the statement list that writes ": " - add(result, newCall("write", newIdentNode("stdout"), newStrLitNode(": "))) + result.add newCall("write", newIdentNode("stdout"), newLit(": ")) # add a call to the statement list that writes the expressions value: - add(result, newCall("writeLine", newIdentNode("stdout"), n[i])) + result.add newCall("writeLine", newIdentNode("stdout"), n) var a: array[0..10, int] @@ -8187,5 +8213,3 @@ validation errors: If the taint mode is turned off, ``TaintedString`` is simply an alias for ``string``. - - diff --git a/doc/manual/var_t_return.rst b/doc/manual/var_t_return.rst new file mode 100644 index 000000000..b9ff1d892 --- /dev/null +++ b/doc/manual/var_t_return.rst @@ -0,0 +1,20 @@ +Memory safety for returning by ``var T`` is ensured by a simple borrowing +rule: If ``result`` does not refer to a location pointing to the heap +(that is in ``result = X`` the ``X`` involves a ``ptr`` or ``ref`` access) +then it has to be deviated by the routine's first parameter: + +.. code-block:: nim + proc forward[T](x: var T): var T = + result = x # ok, deviated from the first parameter. + + proc p(param: var int): var int = + var x: int + # we know 'forward' provides a view into the location deviated by + # its first argument 'x'. + result = forward(x) # Error: location is derived from ``x`` + # which is not p's first parameter and lives + # on the stack. + +In other words, the lifetime of what ``result`` points to is attached to the +lifetime of the first parameter and that is enough knowledge to verify +memory safety at the callsite. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 993756b28..1dc067e1a 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -706,8 +706,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = res.add(($n.kind).substr(3)) case n.kind - of nnkEmpty: discard # same as nil node in this representation - of nnkNilLit: res.add(" nil") + of nnkEmpty, nnkNilLit: discard # same as nil node in this representation of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym: @@ -730,8 +729,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = add(result, "(") case n.kind - of nnkEmpty: discard # same as nil node in this representation - of nnkNilLit: add(result, "nil") + of nnkEmpty, nnkNilLit: discard # same as nil node in this representation of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym: @@ -766,7 +764,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = ## See also `repr`, `treeRepr`, and `lispRepr`. const - NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} + NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} proc traverse(res: var string, level: int, n: NimNode) {.benign.} = @@ -775,12 +773,13 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = res.add("new" & ($n.kind).substr(3) & "Node(") elif n.kind in LitKinds: res.add("newLit(") + elif n.kind == nnkNilLit: + res.add("newNilLit()") else: res.add($n.kind) case n.kind - of nnkEmpty: discard - of nnkNilLit: res.add("nil") + of nnkEmpty, nnkNilLit: discard of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) @@ -1289,7 +1288,11 @@ proc customPragmaNode(n: NimNode): NimNode = typ = n.getTypeInst() if typ.typeKind == ntyTypeDesc: - return typ[1].getImpl()[0][1] + let impl = typ[1].getImpl() + if impl[0].kind == nnkPragmaExpr: + return impl[0][1] + else: + return impl[0] # handle types which don't have macro at all if n.kind == nnkSym: # either an variable or a proc let impl = n.getImpl() diff --git a/lib/deprecated/pure/asyncio.nim b/lib/deprecated/pure/asyncio.nim index 5fd45b215..34cabefb0 100644 --- a/lib/deprecated/pure/asyncio.nim +++ b/lib/deprecated/pure/asyncio.nim @@ -101,8 +101,8 @@ when defined(windows): from winlean import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, FD_ISSET, select else: - from posix import TimeVal, SocketHandle, FD_SET, FD_ZERO, TFdSet, - FD_ISSET, select + from posix import TimeVal, Time, Suseconds, SocketHandle, FD_SET, FD_ZERO, + TFdSet, FD_ISSET, select type DelegateObj* = object @@ -556,8 +556,12 @@ proc send*(sock: AsyncSocket, data: string) = proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 - result.tv_sec = seconds.int32 - result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + when defined(posix): + result.tv_sec = seconds.Time + result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds + else: + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 proc createFdSet(fd: var TFdSet, s: seq[Delegate], m: var int) = FD_ZERO(fd) diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index f068c7d56..f0568366a 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -953,8 +953,12 @@ when defined(ssl): proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 - result.tv_sec = seconds.int32 - result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + when defined(posix): + result.tv_sec = seconds.Time + result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds + else: + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 proc createFdSet(fd: var TFdSet, s: seq[Socket], m: var int) = FD_ZERO(fd) diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 9e6211b63..4f114d394 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -351,8 +351,8 @@ type Timeval* {.importc: "struct timeval", header: "<sys/select.h>", final, pure.} = object ## struct timeval - tv_sec*: clong ## Seconds. - tv_usec*: clong ## Microseconds. + tv_sec*: Time ## Seconds. + tv_usec*: Suseconds ## Microseconds. TFdSet* {.importc: "fd_set", header: "<sys/select.h>", final, pure.} = object abi: array[1024 div (8 * sizeof(clong)), clong] diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 01bc1c1e5..ae41263e8 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -335,8 +335,8 @@ type Timeval* {.importc: "struct timeval", header: "<sys/select.h>", final, pure.} = object ## struct timeval - tv_sec*: int ## Seconds. - tv_usec*: int ## Microseconds. + tv_sec*: Time ## Seconds. + tv_usec*: Suseconds ## Microseconds. TFdSet* {.importc: "fd_set", header: "<sys/select.h>", final, pure.} = object Mcontext* {.importc: "mcontext_t", header: "<ucontext.h>", diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index a5eaec86e..ca4f80f2a 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -168,6 +168,15 @@ proc wakeupWorkerToProcessQueue(w: ptr Worker) = signal(w.q.empty) signal(w.taskArrived) +proc attach(fv: FlowVarBase; i: int): bool = + acquire(fv.cv.L) + if fv.cv.counter <= 0: + fv.idx = i + result = true + else: + result = false + release(fv.cv.L) + proc finished(fv: FlowVarBase) = doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'" # we have to protect against the rare cases where the owner of the flowVar @@ -245,26 +254,27 @@ proc `^`*[T](fv: FlowVar[T]): T = proc awaitAny*(flowVars: openArray[FlowVarBase]): int = ## awaits any of the given flowVars. Returns the index of one flowVar for ## which a value arrived. A flowVar only supports one call to 'awaitAny' at - ## the same time. That means if you await([a,b]) and await([b,c]) the second + ## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second ## call will only await 'c'. If there is no flowVar left to be able to wait ## on, -1 is returned. - ## **Note**: This results in non-deterministic behaviour and so should be - ## avoided. + ## **Note**: This results in non-deterministic behaviour and should be avoided. var ai: AwaitInfo ai.cv.initSemaphore() var conflicts = 0 + result = -1 for i in 0 .. flowVars.high: if cas(addr flowVars[i].ai, nil, addr ai): - flowVars[i].idx = i + if not attach(flowVars[i], i): + result = i + break else: inc conflicts if conflicts < flowVars.len: - await(ai.cv) - result = ai.idx + if result < 0: + await(ai.cv) + result = ai.idx for i in 0 .. flowVars.high: discard cas(addr flowVars[i].ai, addr ai, nil) - else: - result = -1 destroySemaphore(ai.cv) proc isReady*(fv: FlowVarBase): bool = diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 74b2c9741..09fa253f1 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -616,8 +616,12 @@ proc setBlocking*(s: SocketHandle, blocking: bool) = proc timeValFromMilliseconds(timeout = 500): Timeval = if timeout != -1: var seconds = timeout div 1000 - result.tv_sec = seconds.int32 - result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + when useWinVersion: + result.tv_sec = seconds.int32 + result.tv_usec = ((timeout - seconds * 1000) * 1000).int32 + else: + result.tv_sec = seconds.Time + result.tv_usec = ((timeout - seconds * 1000) * 1000).Suseconds proc createFdSet(fd: var TFdSet, s: seq[SocketHandle], m: var int) = FD_ZERO(fd) diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 8a771be71..aaffb57ca 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -104,6 +104,10 @@ proc none*(T: typedesc): Option[T] = # the default is the none type discard +proc none*[T]: Option[T] = + ## Alias for ``none(T)``. + none(T) + proc isSome*[T](self: Option[T]): bool {.inline.} = when T is SomePointer: self.val != nil @@ -290,3 +294,7 @@ when isMainModule: let tmp = option(intref) check(sizeof(tmp) == sizeof(ptr int)) + + test "none[T]": + check(none[int]().isNone) + check(none(int) == none[int]()) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index a77bee99d..ddeee2c6b 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -188,7 +188,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1".} var f: WIN32_FIND_DATA var h = findFirstFile(file, f) if h == -1'i32: raiseOSError(osLastError()) - result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastWriteTime)).int64) + result = fromWinTime(rdFileTime(f.ftLastWriteTime)) findClose(h) proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} = @@ -201,7 +201,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1".} = var f: WIN32_FIND_DATA var h = findFirstFile(file, f) if h == -1'i32: raiseOSError(osLastError()) - result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftLastAccessTime)).int64) + result = fromWinTime(rdFileTime(f.ftLastAccessTime)) findClose(h) proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} = @@ -218,7 +218,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1".} = var f: WIN32_FIND_DATA var h = findFirstFile(file, f) if h == -1'i32: raiseOSError(osLastError()) - result = fromUnix(winTimeToUnixTime(rdFileTime(f.ftCreationTime)).int64) + result = fromWinTime(rdFileTime(f.ftCreationTime)) findClose(h) proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} = @@ -329,20 +329,21 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", c_free(cast[pointer](r)) when defined(Windows): - proc openHandle(path: string, followSymlink=true): Handle = + proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle = var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL if not followSymlink: flags = flags or FILE_FLAG_OPEN_REPARSE_POINT + let access = if writeAccess: GENERIC_WRITE else: 0'i32 when useWinUnicode: result = createFileW( - newWideCString(path), 0'i32, + newWideCString(path), access, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, flags, 0 ) else: result = createFileA( - path, 0'i32, + path, access, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, flags, 0 ) @@ -1511,16 +1512,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = ## 'rawInfo' is either a 'TBY_HANDLE_FILE_INFORMATION' structure on Windows, ## or a 'Stat' structure on posix when defined(Windows): - template toTime(e: FILETIME): untyped {.gensym.} = - fromUnix(winTimeToUnixTime(rdFileTime(e)).int64) # local templates default to bind semantics template merge(a, b): untyped = a or (b shl 32) formalInfo.id.device = rawInfo.dwVolumeSerialNumber formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh) formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh) formalInfo.linkCount = rawInfo.nNumberOfLinks - formalInfo.lastAccessTime = toTime(rawInfo.ftLastAccessTime) - formalInfo.lastWriteTime = toTime(rawInfo.ftLastWriteTime) - formalInfo.creationTime = toTime(rawInfo.ftCreationTime) + formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime)) + formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime)) + formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime)) # Retrieve basic permissions if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: @@ -1654,3 +1653,18 @@ proc isHidden*(path: string): bool = result = (fileName[0] == '.') and (fileName[3] != '.') {.pop.} + +proc setLastModificationTime*(file: string, t: times.Time) = + ## Sets the `file`'s last modification time. `OSError` is raised in case of + ## an error. + when defined(posix): + let unixt = posix.Time(t.toUnix) + var timevals = [Timeval(tv_sec: unixt), Timeval(tv_sec: unixt)] # [last access, last modification] + if utimes(file, timevals.addr) != 0: raiseOSError(osLastError()) + else: + let h = openHandle(path = file, writeAccess = true) + if h == INVALID_HANDLE_VALUE: raiseOSError(osLastError()) + var ft = t.toWinTime.toFILETIME + let res = setFileTime(h, nil, nil, ft.addr) + discard h.closeHandle + if res == 0'i32: raiseOSError(osLastError()) \ No newline at end of file diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 555626514..bcab5ad3a 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1001,13 +1001,21 @@ elif not defined(useNimRtl): {.pop} proc close(p: Process) = - if p.inStream != nil: close(p.inStream) - if p.outStream != nil: close(p.outStream) - if p.errStream != nil: close(p.errStream) if poParentStreams notin p.options: - discard close(p.inHandle) - discard close(p.outHandle) - discard close(p.errHandle) + if p.inStream != nil: + close(p.inStream) + else: + discard close(p.inHandle) + + if p.outStream != nil: + close(p.outStream) + else: + discard close(p.outHandle) + + if p.errStream != nil: + close(p.errStream) + else: + discard close(p.errHandle) proc suspend(p: Process) = if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError()) @@ -1281,7 +1289,7 @@ elif not defined(useNimRtl): proc select(readfds: var seq[Process], timeout = 500): int = var tv: Timeval - tv.tv_sec = 0 + tv.tv_sec = posix.Time(0) tv.tv_usec = timeout * 1000 var rd: TFdSet diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index d772b066c..54fdcb4d0 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1647,11 +1647,15 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect, let last = s.high var i = 0 while true: - var j = find(a, s, sub, i, last) + let j = find(a, s, sub, i, last) if j < 0: break add result, substr(s, i, j - 1) add result, by - i = j + len(sub) + if sub.len == 0: + if i < s.len: add result, s[i] + i = j + 1 + else: + i = j + sub.len # copy the rest: add result, substr(s, i) @@ -1680,6 +1684,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, initSkipTable(a, sub) var i = 0 let last = s.high + let sublen = max(sub.len, 1) while true: var j = find(a, s, sub, i, last) if j < 0: break @@ -1688,7 +1693,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, (j+sub.len >= s.len or s[j+sub.len] notin wordChars): add result, substr(s, i, j - 1) add result, by - i = j + len(sub) + i = j + sublen else: add result, substr(s, i, j) i = j + 1 @@ -2546,6 +2551,9 @@ when isMainModule: doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" + doAssert "-lda-ldz -ld abc".replaceWord("") == "lda-ldz ld abc" + doAssert "oo".replace("", "abc") == "abcoabcoabc" + type MyEnum = enum enA, enB, enC, enuD, enE doAssert parseEnum[MyEnum]("enu_D") == enuD diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 4f2f73ba7..002181a6f 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -557,8 +557,8 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright=false) = when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not FOREGROUND_RGB - if bright: - old = old or FOREGROUND_INTENSITY + old = if bright: old or FOREGROUND_INTENSITY + else: old and not(FOREGROUND_INTENSITY) const lookup: array[ForegroundColor, int] = [ 0, (FOREGROUND_RED), @@ -579,8 +579,8 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright=false) = when defined(windows): let h = conHandle(f) var old = getAttributes(h) and not BACKGROUND_RGB - if bright: - old = old or BACKGROUND_INTENSITY + old = if bright: old or BACKGROUND_INTENSITY + else: old and not(BACKGROUND_INTENSITY) const lookup: array[BackgroundColor, int] = [ 0, (BACKGROUND_RED), diff --git a/lib/pure/times.nim b/lib/pure/times.nim index dc216477b..bf63ed344 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -191,6 +191,7 @@ const secondsInHour = 60*60 secondsInDay = 60*60*24 minutesInHour = 60 + rateDiff = 10000000'i64 # 100 nsecs # The number of hectonanoseconds between 1601/01/01 (windows epoch) # and 1970/01/01 (unix epoch). epochDiff = 116444736000000000'i64 @@ -352,7 +353,9 @@ proc `$`*(dur: Duration): string = parts.add $quantity & " " & unitStrings[unit] & "s" result = "" - if parts.len == 1: + if parts.len == 0: + result.add "0 nanoseconds" + elif parts.len == 1: result = parts[0] elif parts.len == 2: result = parts[0] & " and " & parts[1] @@ -371,6 +374,21 @@ proc toUnix*(t: Time): int64 {.benign, tags: [], raises: [], noSideEffect.} = ## Convert ``t`` to a unix timestamp (seconds since ``1970-01-01T00:00:00Z``). t.seconds +proc fromWinTime*(win: int64): Time = + ## Convert a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``) + ## to a ``Time``. + let hnsecsSinceEpoch = (win - epochDiff) + var seconds = hnsecsSinceEpoch div rateDiff + var nanos = ((hnsecsSinceEpoch mod rateDiff) * 100).int + if nanos < 0: + nanos += convert(Seconds, Nanoseconds, 1) + seconds -= 1 + result = initTime(seconds, nanos) + +proc toWinTime*(t: Time): int64 = + ## Convert ``t`` to a Windows file time (100-nanosecond intervals since ``1601-01-01T00:00:00Z``). + result = t.seconds * rateDiff + epochDiff + t.nanoseconds div 100 + proc isLeapYear*(year: int): bool = ## Returns true if ``year`` is a leap year. year mod 4 == 0 and (year mod 100 != 0 or year mod 400 == 0) @@ -853,10 +871,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} = elif defined(windows): var f: FILETIME getSystemTimeAsFileTime(f) - let nanosSinceEpoch = (rdFileTime(f) - epochDiff) * 100 - let seconds = convert(Nanoseconds, Seconds, nanosSinceEpoch) - let nanos = (nanosSinceEpoch mod convert(Seconds, Nanoseconds, 1)).int - result = initTime(seconds, nanos) + result = fromWinTime(rdFileTime(f)) proc now*(): DateTime {.tags: [TimeEffect], benign.} = ## Get the current time as a ``DateTime`` in the local timezone. @@ -1713,17 +1728,6 @@ when not defined(JS): var clocksPerSec {.importc: "CLOCKS_PER_SEC", nodecl.}: int - const - rateDiff = 10000000'i64 # 100 nsecs - - proc unixTimeToWinTime*(time: CTime): int64 = - ## converts a UNIX `Time` (``time_t``) to a Windows file time - result = int64(time) * rateDiff + epochDiff - - proc winTimeToUnixTime*(time: int64): CTime = - ## converts a Windows time to a UNIX `Time` (``time_t``) - result = CTime((time - epochDiff) div rateDiff) - when not defined(useNimRtl): proc cpuTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].} = ## gets time spent that the CPU spent to run the current process in @@ -1748,7 +1752,7 @@ when not defined(JS): when defined(posix): var a: Timeval gettimeofday(a) - result = toFloat(a.tv_sec) + toFloat(a.tv_usec)*0.00_0001 + result = toBiggestFloat(a.tv_sec.int64) + toFloat(a.tv_usec)*0.00_0001 elif defined(windows): var f: winlean.FILETIME getSystemTimeAsFileTime(f) @@ -1765,6 +1769,19 @@ when defined(JS): # Deprecated procs +when not defined(JS): + proc unixTimeToWinTime*(time: CTime): int64 {.deprecated: "Use toWinTime instead".} = + ## Converts a UNIX `Time` (``time_t``) to a Windows file time + ## + ## **Deprecated:** use ``toWinTime`` instead. + result = int64(time) * rateDiff + epochDiff + + proc winTimeToUnixTime*(time: int64): CTime {.deprecated: "Use fromWinTime instead".} = + ## Converts a Windows time to a UNIX `Time` (``time_t``) + ## + ## **Deprecated:** use ``fromWinTime`` instead. + result = CTime((time - epochDiff) div rateDiff) + proc initInterval*(seconds, minutes, hours, days, months, years: int = 0): TimeInterval {.deprecated.} = ## **Deprecated since v0.18.0:** use ``initTimeInterval`` instead. diff --git a/lib/system.nim b/lib/system.nim index 3761a35f8..5e08dadc0 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2404,7 +2404,7 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} = if x.isNil and y.isNil: return true else: - when not defined(JS) or defined(nimphp): + when not defined(JS): proc seqToPtr[T](x: seq[T]): pointer {.inline, nosideeffect.} = result = cast[pointer](x) else: @@ -2766,17 +2766,14 @@ type when defined(JS): proc add*(x: var string, y: cstring) {.asmNoStackFrame.} = - when defined(nimphp): - asm """`x` .= `y`;""" - else: - asm """ - var len = `x`[0].length-1; - for (var i = 0; i < `y`.length; ++i) { - `x`[0][len] = `y`.charCodeAt(i); - ++len; - } - `x`[0][len] = 0 - """ + asm """ + var len = `x`[0].length-1; + for (var i = 0; i < `y`.length; ++i) { + `x`[0][len] = `y`.charCodeAt(i); + ++len; + } + `x`[0][len] = 0 + """ proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".} elif hasAlloc: @@ -4139,17 +4136,18 @@ template doAssertRaises*(exception, code: untyped): typed = runnableExamples: doAssertRaises(ValueError): raise newException(ValueError, "Hello World") - + var wrong = false try: - block: - code - raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code)) + code + wrong = true except exception: discard except Exception as exc: raiseAssert(astToStr(exception) & " wasn't raised, another error was raised instead by:\n"& astToStr(code)) + if wrong: + raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code)) when defined(cpp) and appType != "lib" and not defined(js) and not defined(nimscript) and hostOS != "standalone": diff --git a/lib/system/assign.nim b/lib/system/assign.nim index f061c89cf..ff1ef31d2 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -79,8 +79,12 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = GenericSeqSize), mt.base, shallow) of tyObject: - if mt.base != nil: - genericAssignAux(dest, src, mt.base, shallow) + var it = mt.base + # don't use recursion here on the PNimType because the subtype + # check should only be done at the very end: + while it != nil: + genericAssignAux(dest, src, it.node, shallow) + it = it.base genericAssignAux(dest, src, mt.node, shallow) # we need to copy m_type field for tyObject, as it could be empty for # sequence reallocations: @@ -89,6 +93,8 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = # if p of TB: # var tbObj = TB(p) # tbObj of TC # needs to be false! + #c_fprintf(stdout, "%s %s\n", pint[].name, mt.name) + chckObjAsgn(cast[ptr PNimType](src)[], mt) pint[] = mt # cast[ptr PNimType](src)[] of tyTuple: genericAssignAux(dest, src, mt.node, shallow) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 8c3175eac..56a9f6575 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -48,10 +48,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} = result[0] = x proc isNimException(): bool {.asmNoStackFrame.} = - when defined(nimphp): - asm "return isset(`lastJSError`['m_type']);" - else: - asm "return `lastJSError`.m_type;" + asm "return `lastJSError`.m_type;" proc getCurrentException*(): ref Exception {.compilerRtl, benign.} = if isNimException(): result = cast[ref Exception](lastJSError) @@ -61,15 +58,14 @@ proc getCurrentExceptionMsg*(): string = if isNimException(): return cast[Exception](lastJSError).msg else: - when not defined(nimphp): - var msg: cstring - {.emit: """ - if (`lastJSError`.message !== undefined) { - `msg` = `lastJSError`.message; - } - """.} - if not msg.isNil: - return $msg + var msg: cstring + {.emit: """ + if (`lastJSError`.message !== undefined) { + `msg` = `lastJSError`.message; + } + """.} + if not msg.isNil: + return $msg return "" proc auxWriteStackTrace(f: PCallFrame): string = @@ -140,12 +136,9 @@ proc raiseException(e: ref Exception, ename: cstring) {. e.name = ename if excHandler == 0: unhandledException(e) - when defined(nimphp): - asm """throw new Exception($`e`["message"]);""" - else: - when NimStackTrace: - e.trace = rawWriteStackTrace() - asm "throw `e`;" + when NimStackTrace: + e.trace = rawWriteStackTrace() + asm "throw `e`;" proc reraiseException() {.compilerproc, asmNoStackFrame.} = if lastJSError == nil: @@ -173,57 +166,35 @@ proc raiseFieldError(f: string) {.compilerproc, noreturn.} = raise newException(FieldError, f & " is not accessible") proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - $args = func_get_args(); - $result = array(); - foreach ($args as $x) { - if (is_array($x)) { - for ($j = $x[0]; $j <= $x[1]; $j++) { - $result[$j] = true; - } - } else { - $result[$x] = true; - } - } - return $result; - """ - else: - asm """ - var result = {}; - for (var i = 0; i < arguments.length; ++i) { - var x = arguments[i]; - if (typeof(x) == "object") { - for (var j = x[0]; j <= x[1]; ++j) { - result[j] = true; - } - } else { - result[x] = true; + asm """ + var result = {}; + for (var i = 0; i < arguments.length; ++i) { + var x = arguments[i]; + if (typeof(x) == "object") { + for (var j = x[0]; j <= x[1]; ++j) { + result[j] = true; } + } else { + result[x] = true; } - return result; - """ - -proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - {.emit: """return `c`;""".} - else: - {.emit: """ - var ln = `c`.length; - var result = new Array(ln + 1); - var i = 0; - for (; i < ln; ++i) { - result[i] = `c`.charCodeAt(i); } - result[i] = 0; // terminating zero return result; - """.} + """ + +proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} = + {.emit: """ + var ln = `c`.length; + var result = new Array(ln + 1); + var i = 0; + for (; i < ln; ++i) { + result[i] = `c`.charCodeAt(i); + } + result[i] = 0; // terminating zero + return result; + """.} proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - {.emit: """return `c`;""".} - else: - {.emit: """ + {.emit: """ var ln = `c`.length; var result = new Array(ln); var r = 0; @@ -261,156 +232,93 @@ proc cstrToNimstr(c: cstring): string {.asmNoStackFrame, compilerproc.} = """.} proc toJSStr(s: string): cstring {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - {.emit: """return `s`;""".} - else: - asm """ - var len = `s`.length-1; - var asciiPart = new Array(len); - var fcc = String.fromCharCode; - var nonAsciiPart = null; - var nonAsciiOffset = 0; - for (var i = 0; i < len; ++i) { - if (nonAsciiPart !== null) { - var offset = (i - nonAsciiOffset) * 2; - var code = `s`[i].toString(16); - if (code.length == 1) { - code = "0"+code; - } - nonAsciiPart[offset] = "%"; - nonAsciiPart[offset + 1] = code; - } - else if (`s`[i] < 128) - asciiPart[i] = fcc(`s`[i]); - else { - asciiPart.length = i; - nonAsciiOffset = i; - nonAsciiPart = new Array((len - i) * 2); - --i; + asm """ + var len = `s`.length-1; + var asciiPart = new Array(len); + var fcc = String.fromCharCode; + var nonAsciiPart = null; + var nonAsciiOffset = 0; + for (var i = 0; i < len; ++i) { + if (nonAsciiPart !== null) { + var offset = (i - nonAsciiOffset) * 2; + var code = `s`[i].toString(16); + if (code.length == 1) { + code = "0"+code; } + nonAsciiPart[offset] = "%"; + nonAsciiPart[offset + 1] = code; } - asciiPart = asciiPart.join(""); - return (nonAsciiPart === null) ? - asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join("")); + else if (`s`[i] < 128) + asciiPart[i] = fcc(`s`[i]); + else { + asciiPart.length = i; + nonAsciiOffset = i; + nonAsciiPart = new Array((len - i) * 2); + --i; + } + } + asciiPart = asciiPart.join(""); + return (nonAsciiPart === null) ? + asciiPart : asciiPart + decodeURIComponent(nonAsciiPart.join("")); """ proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return str_repeat(chr(0),`len`); - """ - else: - asm """ - var result = new Array(`len`+1); - result[0] = 0; - result[`len`] = 0; - return result; - """ - -when defined(nimphp): - proc nimAt(x: string; i: int): string {.asmNoStackFrame, compilerproc.} = - asm """ - return `x`[`i`]; - """ - -when defined(nimphp): - proc nimSubstr(s: string; a, b: int): string {. - asmNoStackFrame, compilerproc.} = - asm """return substr(`s`,`a`,`b`-`a`+1);""" + asm """ + var result = new Array(`len`+1); + result[0] = 0; + result[`len`] = 0; + return result; + """ proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} = # argument type is a fake - when defined(nimphp): - asm """ - return count(`a`); - """ - else: - asm """ - var result = 0; - for (var elem in `a`) { ++result; } - return result; - """ + asm """ + var result = 0; + for (var elem in `a`) { ++result; } + return result; + """ proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; } - foreach (`b` as $elem=>$_) { if (!isset(`a`[$elem])) return false; } - return true; - """ - else: - asm """ - for (var elem in `a`) { if (!`b`[elem]) return false; } - for (var elem in `b`) { if (!`a`[elem]) return false; } - return true; - """ + asm """ + for (var elem in `a`) { if (!`b`[elem]) return false; } + for (var elem in `b`) { if (!`a`[elem]) return false; } + return true; + """ proc SetLe(a, b: int): bool {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - foreach (`a` as $elem=>$_) { if (!isset(`b`[$elem])) return false; } - return true; - """ - else: - asm """ - for (var elem in `a`) { if (!`b`[elem]) return false; } - return true; - """ + asm """ + for (var elem in `a`) { if (!`b`[elem]) return false; } + return true; + """ proc SetLt(a, b: int): bool {.compilerproc.} = result = SetLe(a, b) and not SetEq(a, b) proc SetMul(a, b: int): int {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - var $result = array(); - foreach (`a` as $elem=>$_) { - if (isset(`b`[$elem])) { $result[$elem] = true; } - } - return $result; - """ - else: - asm """ - var result = {}; - for (var elem in `a`) { - if (`b`[elem]) { result[elem] = true; } - } - return result; - """ + asm """ + var result = {}; + for (var elem in `a`) { + if (`b`[elem]) { result[elem] = true; } + } + return result; + """ proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - var $result = array(); - foreach (`a` as $elem=>$_) { $result[$elem] = true; } - foreach (`b` as $elem=>$_) { $result[$elem] = true; } - return $result; - """ - else: - asm """ - var result = {}; - for (var elem in `a`) { result[elem] = true; } - for (var elem in `b`) { result[elem] = true; } - return result; - """ + asm """ + var result = {}; + for (var elem in `a`) { result[elem] = true; } + for (var elem in `b`) { result[elem] = true; } + return result; + """ proc SetMinus(a, b: int): int {.compilerproc, asmNoStackFrame.} = - when defined(nimphp): - asm """ - $result = array(); - foreach (`a` as $elem=>$_) { - if (!isset(`b`[$elem])) { $result[$elem] = true; } - } - return $result; - """ - else: - asm """ - var result = {}; - for (var elem in `a`) { - if (!`b`[elem]) { result[elem] = true; } - } - return result; - """ + asm """ + var result = {}; + for (var elem in `a`) { + if (!`b`[elem]) { result[elem] = true; } + } + return result; + """ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} = asm """ @@ -424,15 +332,8 @@ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerProc.} = return `a`.length - `b`.length; """ -proc cmp(x, y: string): int = - when defined(nimphp): - asm """ - if(`x` < `y`) `result` = -1; - elseif (`x` > `y`) `result` = 1; - else `result` = 0; - """ - else: - return cmpStrings(x, y) +proc cmp(x, y: string): int = + return cmpStrings(x, y) proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerProc.} = asm """ @@ -467,7 +368,7 @@ elif not defined(nimOldEcho): console.log(buf); """ -elif not defined(nimphp): +else: proc ewriteln(x: cstring) = var node : JSRef {.emit: "`node` = document.getElementsByTagName('body')[0];".} @@ -493,127 +394,77 @@ elif not defined(nimphp): # Arithmetic: proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` + `b`; - """ - else: - asm """ - var result = `a` + `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` + `b`; + if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + return result; + """ proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` - `b`; - """ - else: - asm """ - var result = `a` - `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` - `b`; + if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + return result; + """ proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` * `b`; - """ - else: - asm """ - var result = `a` * `b`; - if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` * `b`; + if (result > 2147483647 || result < -2147483648) `raiseOverflow`(); + return result; + """ proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return trunc(`a` / `b`); - """ - else: - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); - return Math.trunc(`a` / `b`); - """ + asm """ + if (`b` == 0) `raiseDivByZero`(); + if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); + return Math.trunc(`a` / `b`); + """ proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` % `b`; - """ - else: - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); - return Math.trunc(`a` % `b`); - """ + asm """ + if (`b` == 0) `raiseDivByZero`(); + if (`b` == -1 && `a` == 2147483647) `raiseOverflow`(); + return Math.trunc(`a` % `b`); + """ proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` + `b`; - """ - else: - asm """ - var result = `a` + `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` + `b`; + if (result > 9223372036854775807 + || result < -9223372036854775808) `raiseOverflow`(); + return result; + """ proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` - `b`; - """ - else: - asm """ - var result = `a` - `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` - `b`; + if (result > 9223372036854775807 + || result < -9223372036854775808) `raiseOverflow`(); + return result; + """ proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` * `b`; - """ - else: - asm """ - var result = `a` * `b`; - if (result > 9223372036854775807 - || result < -9223372036854775808) `raiseOverflow`(); - return result; - """ + asm """ + var result = `a` * `b`; + if (result > 9223372036854775807 + || result < -9223372036854775808) `raiseOverflow`(); + return result; + """ proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return trunc(`a` / `b`); - """ - else: - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); - return Math.trunc(`a` / `b`); - """ + asm """ + if (`b` == 0) `raiseDivByZero`(); + if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); + return Math.trunc(`a` / `b`); + """ proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} = - when defined(nimphp): - asm """ - return `a` % `b`; - """ - else: - asm """ - if (`b` == 0) `raiseDivByZero`(); - if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); - return Math.trunc(`a` % `b`); - """ + asm """ + if (`b` == 0) `raiseDivByZero`(); + if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`(); + return Math.trunc(`a` % `b`); + """ proc negInt(a: int): int {.compilerproc.} = result = a*(-1) @@ -767,24 +618,14 @@ proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} = else: discard -when defined(nimphp): - proc arrayConstr(len: int, value: string, typ: string): JSRef {. - asmNoStackFrame, compilerproc.} = - # types are fake - asm """ - $result = array(); - for ($i = 0; $i < `len`; $i++) $result[] = `value`; - return $result; - """ -else: - proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {. - asmNoStackFrame, compilerproc.} = +proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {. + asmNoStackFrame, compilerproc.} = # types are fake - asm """ - var result = new Array(`len`); - for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`); - return result; - """ + asm """ + var result = new Array(`len`); + for (var i = 0; i < `len`; ++i) result[i] = nimCopy(null, `value`, `typ`); + return result; + """ proc chckIndx(i, a, b: int): int {.compilerproc.} = if i >= a and i <= b: return i diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index f40344396..62bc38da9 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -1086,3 +1086,14 @@ proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, d proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "Windows.h".} + +proc toFILETIME*(t: int64): FILETIME = + ## Convert the Windows file time timestamp ``t`` to ``FILETIME``. + result = FILETIME(dwLowDateTime: cast[DWORD](t), dwHighDateTime: DWORD(t shr 32)) + +type + LPFILETIME* = ptr FILETIME + +proc setFileTime*(hFile: HANDLE, lpCreationTime: LPFILETIME, + lpLastAccessTime: LPFILETIME, lpLastWriteTime: LPFILETIME): WINBOOL + {.stdcall, dynlib: "kernel32", importc: "SetFileTime".} diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim index f81d67a50..acde1b288 100644 --- a/tests/cpp/tcovariancerules.nim +++ b/tests/cpp/tcovariancerules.nim @@ -13,14 +13,8 @@ cat cat dog dog -dog value -cat value -dog value -cat value dog dog -dog value -cat value dog 1 dog 2 ''' @@ -243,11 +237,12 @@ reject modifiesCovariantArray(dogRefsArray.addr) var dogValues = @[vdog, vdog] var dogValuesArray = [vdog, vdog] -var animalValues = @[Animal(vdog), Animal(vcat)] -var animalValuesArray = [Animal(vdog), Animal(vcat)] +when false: + var animalValues = @[Animal(vdog), Animal(vcat)] + var animalValuesArray = [Animal(vdog), Animal(vcat)] -wantsNonCovariantSeq animalValues -wantsNonCovariantArray animalValuesArray + wantsNonCovariantSeq animalValues + wantsNonCovariantArray animalValuesArray reject wantsNonCovariantSeq(dogRefs) reject modifiesCovariantOperArray(dogRefs) @@ -260,7 +255,6 @@ modifiesDerivedOperArray dogRefs reject modifiesDerivedOperArray(dogValues) reject modifiesDerivedOperArray(animalRefs) -wantsNonCovariantOperArray animalValues reject wantsNonCovariantOperArray(animalRefs) reject wantsNonCovariantOperArray(dogRefs) reject wantsNonCovariantOperArray(dogValues) diff --git a/tests/errmsgs/t1154.nim b/tests/errmsgs/t1154.nim new file mode 100644 index 000000000..7fcbf8a27 --- /dev/null +++ b/tests/errmsgs/t1154.nim @@ -0,0 +1,11 @@ +discard """ +errormsg: "invalid type: 'expr' in this context: 'proc (a: varargs[expr])' for proc" +line: 8 +""" + +import typetraits + +proc foo(a:varargs[expr]) = + echo a[0].type.name + +foo(1) diff --git a/tests/errmsgs/tcant_overload_by_return_type.nim b/tests/errmsgs/tcant_overload_by_return_type.nim new file mode 100644 index 000000000..613a896b4 --- /dev/null +++ b/tests/errmsgs/tcant_overload_by_return_type.nim @@ -0,0 +1,9 @@ +discard """ +errormsg: "overloaded 'x' leads to ambiguous calls" +line: 9 +""" + +# bug #6393 + +proc x(): int = 7 +proc x(): string = "strange" diff --git a/tests/macros/tmacro1.nim b/tests/macros/tmacro1.nim index 0b83345a1..ac2bf9094 100644 --- a/tests/macros/tmacro1.nim +++ b/tests/macros/tmacro1.nim @@ -27,11 +27,19 @@ import strutils template assertNot(arg: untyped): untyped = assert(not(arg)) + +proc foo(arg: int): void = + discard + +proc foo(arg: float): void = + discard + static: ## test eqIdent let a = "abc_def" let b = "abcDef" let c = "AbcDef" + let d = nnkBracketExpr.newTree() # not an identifier at all assert eqIdent( a , b ) assert eqIdent(newIdentNode(a), b ) @@ -62,3 +70,12 @@ static: assertNot eqIdent(genSym(nskLet, c), newIdentNode( b)) assertNot eqIdent(newIdentNode( c), genSym(nskLet, b)) assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b)) + + # eqIdent on non identifier at all + assertNot eqIdent(a,d) + + # eqIdent on sym choice + let fooSym = bindSym"foo" + assert fooSym.kind in {nnkOpenSymChoice, nnkClosedSymChoice} + assert fooSym.eqIdent("fOO") + assertNot fooSym.eqIdent("bar") diff --git a/tests/objects/tinherentedvalues.nim b/tests/objects/tinherentedvalues.nim index c96a0fd6d..ad7b5f326 100644 --- a/tests/objects/tinherentedvalues.nim +++ b/tests/objects/tinherentedvalues.nim @@ -1,9 +1,7 @@ discard """ - output: '''tbObj of TC false -false + output: '''tbObj of TC true true -5 -false''' +5''' """ # bug #1053 @@ -20,10 +18,10 @@ type proc test(p: TA) = #echo "p of TB ", p of TB if p of TB: - var tbObj = TB(p) + #var tbObj = TB(p) # tbObj is actually no longer compatible with TC: - echo "tbObj of TC ", tbObj of TC + echo "tbObj of TC ", p of TC var v = TC() v.a = 1 @@ -48,8 +46,8 @@ proc isMyObject(obj: TObject) = asd.x = 5 -var asdCopy = TObject(asd) -echo asdCopy of MyObject +#var asdCopy = TObject(asd) +#echo asdCopy of MyObject isMyObject(asd) -isMyObject(asdCopy) +#isMyObject(asdCopy) diff --git a/tests/objects/tobj_asgn_dont_slice.nim b/tests/objects/tobj_asgn_dont_slice.nim new file mode 100644 index 000000000..866b5aecc --- /dev/null +++ b/tests/objects/tobj_asgn_dont_slice.nim @@ -0,0 +1,24 @@ +discard """ + outputsub: '''ObjectAssignmentError''' + exitcode: "1" +""" + +# bug #7637 +type + Fruit = object of RootObj + name*: string + Apple = object of Fruit + Pear = object of Fruit + +method eat(f: Fruit) {.base.} = + raise newException(Exception, "PURE VIRTUAL CALL") + +method eat(f: Apple) = + echo "fruity" + +method eat(f: Pear) = + echo "juicy" + +let basket = [Apple(name:"a"), Pear(name:"b")] + +eat(basket[0]) diff --git a/tests/objects/tobjconstr.nim b/tests/objects/tobjconstr.nim index d1f3c8bdb..7238d10c7 100644 --- a/tests/objects/tobjconstr.nim +++ b/tests/objects/tobjconstr.nim @@ -9,8 +9,8 @@ discard """ (k: kindA, a: (x: "abc", z: [1, 8, 3]), method: ()) (k: kindA, a: (x: "abc", z: [1, 9, 3]), method: ()) (k: kindA, a: (x: "abc", z: [1, 10, 3]), method: ()) -(x: 123) -(x: 123) +(y: 0, x: 123) +(y: 678, x: 123) (z: 89, y: 0, x: 128) (y: 678, x: 123) (y: 678, x: 123) @@ -33,7 +33,6 @@ type `method`: TEmpty # bug #1791 proc `$`[T](s: seq[T]): string = - # XXX why is that not in the stdlib? result = "[" for i, x in s: if i > 0: result.add(", ") @@ -59,7 +58,7 @@ type # inherited fields are ignored, so no fields are set when true: var - o: A + o: B o = B(x: 123) echo o o = B(y: 678, x: 123) diff --git a/tests/osproc/tclose.nim b/tests/osproc/tclose.nim new file mode 100644 index 000000000..d466b466a --- /dev/null +++ b/tests/osproc/tclose.nim @@ -0,0 +1,24 @@ +discard """ + exitcode: 0 +""" + +when defined(linux): + import osproc, os + + proc countFds(): int = + result = 0 + for i in walkDir("/proc/self/fd"): + result += 1 + + let initCount = countFds() + + let p = osproc.startProcess("echo", options={poUsePath}) + assert countFds() == initCount + 3 + p.close + assert countFds() == initCount + + let p1 = osproc.startProcess("echo", options={poUsePath}) + discard p1.inputStream + assert countFds() == initCount + 3 + p.close + assert countFds() == initCount diff --git a/tests/overload/toverprc.nim b/tests/overload/toverprc.nim index 112eae096..9be2203f6 100644 --- a/tests/overload/toverprc.nim +++ b/tests/overload/toverprc.nim @@ -11,7 +11,7 @@ proc parseInt(x: float): int {.noSideEffect.} = discard proc parseInt(x: bool): int {.noSideEffect.} = discard proc parseInt(x: float32): int {.noSideEffect.} = discard proc parseInt(x: int8): int {.noSideEffect.} = discard -proc parseInt(x: TFile): int {.noSideEffect.} = discard +proc parseInt(x: File): int {.noSideEffect.} = discard proc parseInt(x: char): int {.noSideEffect.} = discard proc parseInt(x: int16): int {.noSideEffect.} = discard diff --git a/tests/parallel/twaitany.nim b/tests/parallel/twaitany.nim new file mode 100644 index 000000000..69136a3b6 --- /dev/null +++ b/tests/parallel/twaitany.nim @@ -0,0 +1,35 @@ +discard """ + output: '''true''' +""" + +# bug #7638 +import threadpool, os, strformat + +proc timer(d: int): int = + #echo fmt"sleeping {d}" + sleep(d) + #echo fmt"done {d}" + return d + +var durations = [1000, 2000, 3000, 4000, 5000] +var tasks: seq[FlowVarBase] = @[] +var results: seq[int] = @[] + +for i in 0 .. durations.high: + tasks.add spawn timer(durations[i]) + +var index = awaitAny(tasks) +while index != -1: + results.add ^cast[FlowVar[int]](tasks[index]) + tasks.del(index) + #echo repr results + index = awaitAny(tasks) + +doAssert results.len == 5 +doAssert 1000 in results +doAssert 2000 in results +doAssert 3000 in results +doAssert 4000 in results +doAssert 5000 in results +sync() +echo "true" diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 28a8713ce..33a4a7e65 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -51,6 +51,9 @@ block: # A bit more advanced case static: assert(hasCustomPragma(myproc, alternativeKey)) + const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue) + static: assert(hasFieldCustomPragma == false) + # pragma on an object static: assert Subfield.hasCustomPragma(defaultValue) @@ -71,6 +74,8 @@ block: # ref types MyFile {.defaultValue: "closed".} = ref object path {.defaultValue: "invalid".}: string + TypeWithoutPragma = object + var s = NodeRef() const @@ -91,7 +96,7 @@ block: # ref types var ptrS = NodePtr(nil) const - ptrRightSerKey = getCustomPragmaVal(s.right, serializationKey) + ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey) static: assert ptrRightSerKey == "r" @@ -103,6 +108,9 @@ block: # ref types assert fileDefVal == "closed" assert filePathDefVal == "invalid" + static: + assert TypeWithoutPragma.hasCustomPragma(defaultValue) == false + block: type VariantKind = enum diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index f35965286..945d7ba3d 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -409,4 +409,11 @@ suite "ttimes": # Bug with adding a day to a Time let day = 24.hours let tomorrow = now + day - check tomorrow - now == initDuration(days = 1) \ No newline at end of file + check tomorrow - now == initDuration(days = 1) + + test "fromWinTime/toWinTime": + check 0.fromUnix.toWinTime.fromWinTime.toUnix == 0 + check (-1).fromWinTime.nanoseconds == convert(Seconds, Nanoseconds, 1) - 100 + check -1.fromWinTime.toWinTime == -1 + # One nanosecond is discarded due to differences in time resolution + check initTime(0, 101).toWinTime.fromWinTime.nanoseconds == 100 \ No newline at end of file diff --git a/tests/template/i2416.nim b/tests/template/i2416.nim new file mode 100644 index 000000000..4b53cd0ca --- /dev/null +++ b/tests/template/i2416.nim @@ -0,0 +1 @@ +template i2416*() = echo "i2416" diff --git a/tests/template/t2416.nim b/tests/template/t2416.nim new file mode 100644 index 000000000..f73880718 --- /dev/null +++ b/tests/template/t2416.nim @@ -0,0 +1,2 @@ +import i2416 +i2416() diff --git a/tests/varres/tnewseq_on_result_vart.nim b/tests/varres/tnewseq_on_result_vart.nim new file mode 100644 index 000000000..18935a1d1 --- /dev/null +++ b/tests/varres/tnewseq_on_result_vart.nim @@ -0,0 +1,9 @@ + +discard """ + line: 9 + errormsg: "address of 'result' may not escape its stack frame" +""" +# bug #5113 + +proc makeSeqVar(size: Natural): var seq[int] = + newSeq(result, size) diff --git a/tools/nimpretty.nim b/tools/nimpretty.nim index 36d1382cf..396f17b0b 100644 --- a/tools/nimpretty.nim +++ b/tools/nimpretty.nim @@ -24,7 +24,7 @@ const Usage: nimpretty [options] file.nim Options: - --backup:ON|OFF create a backup file before overwritting (default: ON) + --backup:on|off create a backup file before overwritting (default: ON) --version show the version --help show this help """ @@ -43,7 +43,7 @@ proc prettyPrint(infile: string) = let fileIdx = fileInfoIdx(infile) let tree = parseFile(fileIdx, newIdentCache()) let outfile = changeFileExt(infile, ".pretty.nim") - renderModule(tree, infile, outfile, {}) + renderModule(tree, infile, outfile, {}, fileIdx) proc main = var infile: string diff --git a/web/website.ini b/web/website.ini index 56b7c436f..18e7bf2cb 100644 --- a/web/website.ini +++ b/web/website.ini @@ -31,7 +31,7 @@ file: ticker.html [Documentation] doc: "endb.rst;intern.txt;apis.txt;lib.rst;manual.rst;tut1.rst;tut2.rst;nimc.rst;overview.rst;filters.rst" doc: "tools.txt;niminst.rst;nimgrep.rst;gc.rst;estp.rst;idetools.rst;docgen.rst;koch.rst;backends.txt" -doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst" +doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst;manual/*.rst" pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst" srcdoc2: "system.nim;system/nimscript;pure/ospaths" srcdoc2: "core/macros;pure/marshal;core/typeinfo" |