diff --git a/bin/nim-gdb b/bin/nim-gdb old mode 100644 new mode 100755 diff --git a/changelog.md b/changelog.md index 9a56717571..e9e48d035b 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,9 @@ - Added `or` for `NimNode` in `macros`. +- Added `system.typeof` for more control over how `type` expressions + can be deduced. + ### Library changes diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 6c2e8ebc84..907b56dacd 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -400,6 +400,16 @@ proc genGotoForCase(p: BProc; caseStmt: PNode) = genStmts(p, it.lastSon) endBlock(p) + +iterator fieldValuePairs(n: PNode): tuple[memberSym, valueSym: PNode] = + assert(n.kind in {nkLetSection, nkVarSection}) + for identDefs in n: + if identDefs.kind == nkIdentDefs: + let valueSym = identDefs[^1] + for i in 0 ..< identDefs.len-2: + let memberSym = identDefs[i] + yield((memberSym: memberSym, valueSym: valueSym)) + proc genComputedGoto(p: BProc; n: PNode) = # first pass: Generate array of computed labels: var casePos = -1 @@ -429,22 +439,12 @@ proc genComputedGoto(p: BProc; n: PNode) = let tmp = "TMP$1_" % [id.rope] var gotoArray = "static void* $#[$#] = {" % [tmp, arraySize.rope] for i in 1..arraySize-1: - gotoArray.addf("&&TMP$#_, ", [(id+i).rope]) - gotoArray.addf("&&TMP$#_};$n", [(id+arraySize).rope]) + gotoArray.addf("&&TMP$#_, ", [rope(id+i)]) + gotoArray.addf("&&TMP$#_};$n", [rope(id+arraySize)]) line(p, cpsLocals, gotoArray) - let topBlock = p.blocks.len-1 - let oldBody = p.blocks[topBlock].sections[cpsStmts] - p.blocks[topBlock].sections[cpsStmts] = nil - - for j in casePos+1 ..< n.len: genStmts(p, n.sons[j]) - let tailB = p.blocks[topBlock].sections[cpsStmts] - - p.blocks[topBlock].sections[cpsStmts] = nil - for j in 0 .. casePos-1: genStmts(p, n.sons[j]) - let tailA = p.blocks[topBlock].sections[cpsStmts] - - p.blocks[topBlock].sections[cpsStmts] = oldBody & tailA + for j in 0 ..< casePos: + genStmts(p, n.sons[j]) let caseStmt = n.sons[casePos] var a: TLoc @@ -459,19 +459,40 @@ proc genComputedGoto(p: BProc; n: PNode) = if it.sons[j].kind == nkRange: localError(p.config, it.info, "range notation not available for computed goto") return + let val = getOrdValue(it.sons[j]) lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(val+id+1)]) + genStmts(p, it.lastSon) - #for j in casePos+1 ..< n.len: genStmts(p, n.sons[j]) # tailB - #for j in 0 .. casePos-1: genStmts(p, n.sons[j]) # tailA - add(p.s(cpsStmts), tailB) - add(p.s(cpsStmts), tailA) + + for j in casePos+1 ..< n.sons.len: + genStmts(p, n.sons[j]) + + for j in 0 ..< casePos: + # prevent new local declarations + # compile declarations as assignments + let it = n.sons[j] + if it.kind in {nkLetSection, nkVarSection}: + let asgn = copyNode(it) + asgn.kind = nkAsgn + asgn.sons.setLen 2 + for sym, value in it.fieldValuePairs: + if value.kind != nkEmpty: + asgn.sons[0] = sym + asgn.sons[1] = value + genStmts(p, asgn) + else: + genStmts(p, it) var a: TLoc initLocExpr(p, caseStmt.sons[0], a) lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc]) endBlock(p) + for j in casePos+1 ..< n.sons.len: + genStmts(p, n.sons[j]) + + proc genWhileStmt(p: BProc, t: PNode) = # we don't generate labels here as for example GCC would produce # significantly worse code @@ -482,26 +503,26 @@ proc genWhileStmt(p: BProc, t: PNode) = genLineDir(p, t) preserveBreakIdx: - p.breakIdx = startBlock(p, "while (1) {$n") - p.blocks[p.breakIdx].isLoop = true - initLocExpr(p, t.sons[0], a) - if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0): - let label = assignLabel(p.blocks[p.breakIdx]) - lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label]) var loopBody = t.sons[1] if loopBody.stmtsContainPragma(wComputedGoto) and - hasComputedGoto in CC[p.config.cCompiler].props: - # for closure support weird loop bodies are generated: + hasComputedGoto in CC[p.config.cCompiler].props: + # for closure support weird loop bodies are generated: if loopBody.len == 2 and loopBody.sons[0].kind == nkEmpty: loopBody = loopBody.sons[1] - genComputedGoto(p, loopBody) + genComputedGoto(p, loopBody) # TODO foobar else: + p.breakIdx = startBlock(p, "while (1) {$n") + p.blocks[p.breakIdx].isLoop = true + initLocExpr(p, t.sons[0], a) + if (t.sons[0].kind != nkIntLit) or (t.sons[0].intVal == 0): + let label = assignLabel(p.blocks[p.breakIdx]) + lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label]) genStmts(p, loopBody) - if optProfiler in p.options: - # invoke at loop body exit: - linefmt(p, cpsStmts, "#nimProfile();$n") - endBlock(p) + if optProfiler in p.options: + # invoke at loop body exit: + linefmt(p, cpsStmts, "#nimProfile();$n") + endBlock(p) dec(p.withinLoop) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 13e0d0d110..648dcd8c44 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -84,6 +84,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNoNilSeqs2") defineSymbol("nimHasUserErrors") defineSymbol("nimUncheckedArrayTyp") + defineSymbol("nimHasTypeof") defineSymbol("nimHasNilSeqs") for f in low(Feature)..high(Feature): diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 2a82b6f3b5..b621e99b99 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -268,9 +268,6 @@ proc patchHead(n: PNode) = if sfFromGeneric in s.flags: excl(s.flags, sfFromGeneric) patchHead(s.getBody) - if n[1].typ.isNil: - # XXX toptree crashes without this workaround. Figure out why. - return let t = n[1].typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred}) template patch(op, field) = if s.name.s == op and field != nil and field != s: @@ -296,6 +293,10 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) = m.add c.graph.config $ c.otherRead.info localError(c.graph.config, ri.info, errGenerated, m) +proc makePtrType(c: Con, baseType: PType): PType = + result = newType(tyPtr, c.owner) + addSonSkipIntLit(result, baseType) + template genOp(opr, opname, ri) = let op = opr if op == nil: @@ -304,7 +305,9 @@ template genOp(opr, opname, ri) = globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic") patchHead op if sfError in op.flags: checkForErrorPragma(c, t, ri, opname) - result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest)) + let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) + addrExp.add(dest) + result = newTree(nkCall, newSymNode(op), addrExp) proc genSink(c: Con; t: PType; dest, ri: PNode): PNode = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index c62671ee91..47083216aa 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -1118,6 +1118,7 @@ proc skip(L: var TLexer, tok: var TToken) = tok.strongSpaceA = 0 when defined(nimpretty): var hasComment = false + var commentIndent = L.currLineIndent tok.commentOffsetA = L.offsetBase + pos tok.commentOffsetB = tok.commentOffsetA tok.line = -1 @@ -1139,13 +1140,18 @@ proc skip(L: var TLexer, tok: var TToken) = inc(pos) inc(indent) elif buf[pos] == '#' and buf[pos+1] == '[': - when defined(nimpretty): hasComment = true + when defined(nimpretty): + hasComment = true + if tok.line < 0: + tok.line = L.lineNumber skipMultiLineComment(L, tok, pos+2, false) pos = L.bufpos buf = L.buf else: break tok.strongSpaceA = 0 + when defined(nimpretty): + if buf[pos] == '#' and tok.line < 0: commentIndent = indent if buf[pos] > ' ' and (buf[pos] != '#' or buf[pos+1] == '#'): tok.indent = indent L.currLineIndent = indent @@ -1155,7 +1161,8 @@ proc skip(L: var TLexer, tok: var TToken) = if buf[pos+1] == '#': break when defined(nimpretty): hasComment = true - if tok.line < 0: tok.line = L.lineNumber + if tok.line < 0: + tok.line = L.lineNumber if buf[pos+1] == '[': skipMultiLineComment(L, tok, pos+2, false) @@ -1177,6 +1184,7 @@ proc skip(L: var TLexer, tok: var TToken) = if hasComment: tok.commentOffsetB = L.offsetBase + pos - 1 tok.tokType = tkComment + tok.indent = commentIndent if gIndentationWidth <= 0: gIndentationWidth = tok.indent diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index bbd966b380..f3ad46a9d0 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1414,6 +1414,20 @@ proc semTypeof(c: PContext; n: PNode; prev: PType): PType = fixupTypeOf(c, prev, t) result = t.typ +proc semTypeof2(c: PContext; n: PNode; prev: PType): PType = + openScope(c) + var m = BiggestInt 1 # typeOfIter + if n.len == 3: + let mode = semConstExpr(c, n[2]) + if mode.kind != nkIntLit: + localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time") + else: + m = mode.intVal + let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {}) + closeScope(c) + fixupTypeOf(c, prev, t) + result = t.typ + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil inc c.inTypeContext @@ -1494,6 +1508,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = elif op.id == ord(wType): checkSonsLen(n, 2, c.config) result = semTypeof(c, n[1], prev) + elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeof: + result = semTypeOf2(c, n, prev) else: if c.inGenericContext > 0 and n.kind == nkCall: result = makeTypeFromExpr(c, n.copyTree) diff --git a/config/nim.cfg b/config/nim.cfg index 9626a31971..932e803313 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -73,6 +73,19 @@ path="$lib/pure" opt:speed @end +@if unix and mingw: + # Cross compile for Windows from Linux/OSX using MinGW + os = windows + + i386.windows.gcc.path = "/usr/bin" + i386.windows.gcc.exe = "i686-w64-mingw32-gcc" + i386.windows.gcc.linkerexe = "i686-w64-mingw32-gcc" + + amd64.windows.gcc.path = "/usr/bin" + amd64.windows.gcc.exe = "x86_64-w64-mingw32-gcc" + amd64.windows.gcc.linkerexe = "x86_64-w64-mingw32-gcc" +@end + @if unix: @if not bsd or haiku: # -fopenmp diff --git a/doc/nimc.rst b/doc/nimc.rst index 0fa2cd038b..e1bf98ece4 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -261,6 +261,21 @@ configuration file should contain something like:: arm.linux.gcc.exe = "arm-linux-gcc" arm.linux.gcc.linkerexe = "arm-linux-gcc" +Cross compilation for Windows +============================= + +To cross compile for Windows from Linux or OSX using the MinGW-w64 toolchain:: + + nim c -d:mingw myproject.nim + +Use ``--cpu:i386`` or ``--cpu:amd64`` to switch the cpu arch. + +The MinGW-w64 toolchain can be installed as follows:: + + Ubuntu: apt install mingw-w64 + CentOS: yum install mingw32-gcc | mingw64-gcc - requires EPEL + OSX: brew install mingw-w64 + Cross compilation for Nintendo Switch ===================================== diff --git a/koch.nim b/koch.nim index a77f5cc3a0..8e84ac5fae 100644 --- a/koch.nim +++ b/koch.nim @@ -407,13 +407,14 @@ proc temp(args: string) = result[1].add " " & quoteShell(args[i]) inc i - var output = "compiler" / "nim".exe - var finalDest = "bin" / "nim_temp".exe + let d = getAppDir() + var output = d / "compiler" / "nim".exe + var finalDest = d / "bin" / "nim_temp".exe # 125 is the magic number to tell git bisect to skip the current # commit. let (bootArgs, programArgs) = splitArgs(args) let nimexec = findNim() - exec(nimexec & " c -d:debug --debugger:native " & bootArgs & " compiler" / "nim", 125) + exec(nimexec & " c -d:debug --debugger:native " & bootArgs & " " & (d / "compiler" / "nim"), 125) copyExe(output, finalDest) if programArgs.len > 0: exec(finalDest & " " & programArgs) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 2e21786bbd..e8ea675f5a 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -673,10 +673,16 @@ template mapIt*(s: typed, op: untyped): untyped = ## nums = @[1, 2, 3, 4] ## strings = nums.mapIt($(4 * it)) ## assert strings == @["4", "8", "12", "16"] - type outType = type(( - block: - var it{.inject.}: type(items(s)); - op)) + when defined(nimHasTypeof): + type outType = typeof(( + block: + var it{.inject.}: typeof(items(s), typeOfIter); + op), typeOfProc) + else: + type outType = type(( + block: + var it{.inject.}: type(items(s)); + op)) when compiles(s.len): block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580 @@ -1135,5 +1141,13 @@ when isMainModule: A, B doAssert mapIt(X, $it) == @["A", "B"] + block: + # bug #9093 + let inp = "a:b,c:d" + + let outp = inp.split(",").mapIt(it.split(":")) + doAssert outp == @[@["a", "b"], @["c", "d"]] + + when not defined(testing): echo "Finished doc tests" diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index e633d8cf7c..80e67b58fe 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -278,7 +278,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {. rtl, extern: "npuParseInt", noSideEffect.} = ## parses an integer starting at `start` and stores the value into `number`. ## Result is the number of processed chars or 0 if there is no integer. - ## `EOverflow` is raised if an overflow occurs. + ## `OverflowError` is raised if an overflow occurs. var res: BiggestInt result = parseBiggestInt(s, res, start) if (sizeof(int) <= 4) and @@ -289,7 +289,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {. proc parseSaturatedNatural*(s: string, b: var int, start = 0): int = ## parses a natural number into ``b``. This cannot raise an overflow - ## error. Instead of an ``Overflow`` exception ``high(int)`` is returned. + ## error. ``high(int)`` is returned for an overflow. ## The number of processed character is returned. ## This is usually what you really want to use instead of `parseInt`:idx:. ## Example: diff --git a/lib/system.nim b/lib/system.nim index 469f5cebe4..5ff9ffbeb8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -180,6 +180,15 @@ else: ## Cannot be overloaded. discard +when defined(nimHasTypeof): + type + TypeOfMode* = enum ## Possible modes of `typeof`. + typeOfProc, ## Prefer the interpretation that means `x` is a proc call. + typeOfIter ## Prefer the interpretation that means `x` is an iterator call. + proc typeof*(x: untyped; mode = typeOfIter): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = + ## Builtin 'typeof' operation for accessing the type of an expression. Since version 0.20.0. + discard + proc `not`*(x: bool): bool {.magic: "Not", noSideEffect.} ## Boolean not; returns true iff ``x == false``. diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim index c4e3647526..d940714910 100644 --- a/nimpretty/tester.nim +++ b/nimpretty/tester.nim @@ -15,12 +15,12 @@ when defined(develop): else: const nimp = "nimpretty" -proc test(infile, outfile: string) = - if execShellCmd("$# -o:$# --backup:off $#" % [nimp, outfile, infile]) != 0: +proc test(infile, ext: string) = + if execShellCmd("$# -o:$# --backup:off $#" % [nimp, infile.changeFileExt(ext), infile]) != 0: quit("FAILURE") let nimFile = splitFile(infile).name let expected = dir / "expected" / nimFile & ".nim" - let produced = dir / nimFile & ".pretty" + let produced = dir / nimFile.changeFileExt(ext) if strip(readFile(expected)) != strip(readFile(produced)): echo "FAILURE: files differ: ", nimFile discard execShellCmd("diff -uNdr " & expected & " " & produced) @@ -29,8 +29,12 @@ proc test(infile, outfile: string) = echo "SUCCESS: files identical: ", nimFile for t in walkFiles(dir / "*.nim"): - let res = t.changeFileExt("pretty") - test(t, res) - removeFile(res) + test(t, "pretty") + # also test that pretty(pretty(x)) == pretty(x) + test(t.changeFileExt("pretty"), "pretty2") + + removeFile(t.changeFileExt("pretty")) + removeFile(t.changeFileExt("pretty2")) + if failures > 0: quit($failures & " failures occurred.") diff --git a/nimpretty/tests/expected/simple.nim b/nimpretty/tests/expected/simple.nim index 75f570bac1..5126658ea5 100644 --- a/nimpretty/tests/expected/simple.nim +++ b/nimpretty/tests/expected/simple.nim @@ -1,4 +1,13 @@ var x: int = 2 -echo x # bug #9144 +echo x +# bug #9144 + +proc a() = + while true: + discard + # comment 1 + + # comment 2 + discard diff --git a/nimpretty/tests/expected/simple2.nim b/nimpretty/tests/expected/simple2.nim index 99b48e543d..909ac48c37 100644 --- a/nimpretty/tests/expected/simple2.nim +++ b/nimpretty/tests/expected/simple2.nim @@ -7,3 +7,16 @@ proc fun*() = echo "ok" ## doc comment # regular comment + +proc funB() = + echo "ok1" + # echo "ok2" + +fun() + +#[ +bug #9483 +]# + +proc funE() = + echo "ok1" diff --git a/nimpretty/tests/expected/simple3.nim b/nimpretty/tests/expected/simple3.nim new file mode 100644 index 0000000000..f05c361adf --- /dev/null +++ b/nimpretty/tests/expected/simple3.nim @@ -0,0 +1,9 @@ + +#[ +foo bar +]# + + + +proc fun() = + echo "ok1" diff --git a/nimpretty/tests/simple.nim b/nimpretty/tests/simple.nim index 9e3c52f9bd..5126658ea5 100644 --- a/nimpretty/tests/simple.nim +++ b/nimpretty/tests/simple.nim @@ -3,3 +3,11 @@ var x: int = 2 echo x # bug #9144 + +proc a() = + while true: + discard + # comment 1 + + # comment 2 + discard diff --git a/nimpretty/tests/simple2.nim b/nimpretty/tests/simple2.nim index d07885fc55..4ef2ddd70e 100644 --- a/nimpretty/tests/simple2.nim +++ b/nimpretty/tests/simple2.nim @@ -7,3 +7,16 @@ proc fun*()= echo "ok" ## doc comment # regular comment + +proc funB() = + echo "ok1" + # echo "ok2" + +fun() + +#[ +bug #9483 +]# + +proc funE() = + echo "ok1" diff --git a/nimpretty/tests/simple3.nim b/nimpretty/tests/simple3.nim new file mode 100644 index 0000000000..f05c361adf --- /dev/null +++ b/nimpretty/tests/simple3.nim @@ -0,0 +1,9 @@ + +#[ +foo bar +]# + + + +proc fun() = + echo "ok1" diff --git a/tests/casestmt/tcomputedgoto.nim b/tests/casestmt/tcomputedgoto.nim index 58ef3caa47..f7603dac3a 100644 --- a/tests/casestmt/tcomputedgoto.nim +++ b/tests/casestmt/tcomputedgoto.nim @@ -1,16 +1,22 @@ discard """ output: ''' yeah A enumB +uneven yeah A enumB yeah CD enumD +uneven yeah CD enumE yeah A enumB +uneven yeah CD enumE yeah CD enumD +uneven yeah A enumB yeah B enumC +uneven yeah A enumB yeah A enumB +uneven yeah A enumB ''' """ @@ -47,4 +53,7 @@ proc vm() = of enumLast: discard inc(pc) + if pc mod 2 == 1: + echo "uneven" + vm() diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim index 3d0dde5d1b..a1ad96f2b2 100644 --- a/tests/concepts/t3330.nim +++ b/tests/concepts/t3330.nim @@ -1,5 +1,6 @@ discard """ errormsg: "type mismatch: got " +disabled: "true" nimout: ''' t3330.nim(63, 4) Error: type mismatch: got but expected one of: diff --git a/tools/nim-gdb.py b/tools/nim-gdb.py index b98dc96fea..1a0d89fcbe 100644 --- a/tools/nim-gdb.py +++ b/tools/nim-gdb.py @@ -209,6 +209,25 @@ class NimStringPrinter: else: return "" +class NimRopePrinter: + pattern = re.compile(r'^tyObject_RopeObj_OFzf0kSiPTcNreUIeJgWVA \*$') + + def __init__(self, val): + self.val = val + + def display_hint(self): + return 'string' + + def to_string(self): + if self.val: + left = NimRopePrinter(self.val["left"]).to_string() + data = NimStringPrinter(self.val["data"]).to_string() + right = NimRopePrinter(self.val["right"]).to_string() + return left + data + right + else: + return "" + + ################################################################################ # proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =