diff --git a/.travis.yml b/.travis.yml index 0e55d3affc89..279da7920a20 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,11 @@ sudo: false language: c -dist: trusty +dist: xenial matrix: + fast_finish: true + include: - os: linux env: NIM_COMPILE_TO_CPP=false @@ -45,10 +47,12 @@ before_script: - set +e # prevents breaking after_failure script: + - set -e - echo "travis_fold:start:nim_c_koch" - nim c koch - echo "travis_fold:end:nim_c_koch" - ./koch runCI + - set +e before_deploy: # Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html diff --git a/appveyor.yml b/appveyor.yml index 8c4b2a07e702..31be04d1b4f8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,28 +1,29 @@ version: '{build}' +environment: + MINGW_DIR: mingw64 + MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z/download + MINGW_ARCHIVE: x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z + OPENBLAS_URL: https://sourceforge.net/projects/openblas/files/v0.3.5/OpenBLAS%200.3.5%20version.zip/download + OPENBLAS_ARCHIVE: OpenBLAS-0.3.5.zip + SQLITE_URL: http://www.sqlite.org/2017/sqlite-dll-win64-x64-3160200.zip + SQLITE_ARCHIVE: sqlite-dll-win64-x64-3160200.zip + platform: x64 + + matrix: + - NIM_TEST_PACKAGES: true + - NIM_TEST_PACKAGES: false + cache: -- x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z -- sqlite-dll-win64-x64-3160200.zip -# - i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z + - '%MINGW_ARCHIVE%' + - '%SQLITE_ARCHIVE%' + - '%OPENBLAS_ARCHIVE%' matrix: + #allow_failures: + # - NIM_TEST_PACKAGES: true fast_finish: true -environment: - matrix: - - MINGW_DIR: mingw64 - MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z/download - MINGW_ARCHIVE: x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z - SQLITE_URL: http://www.sqlite.org/2017/sqlite-dll-win64-x64-3160200.zip - SQLITE_ARCHIVE: sqlite-dll-win64-x64-3160200.zip - platform: x64 - # - MINGW_DIR: mingw32 - # MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/4.9.2/threads-win32/dwarf/i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z/download - # MINGW_ARCHIVE: i686-4.9.2-release-win32-dwarf-rt_v4-rev4.7z - # SQLITE_URL: http://www.sqlite.org/2017/sqlite-dll-win32-x86-3160200.zip - # SQLITE_ARCHIVE: sqlite-dll-win32-x86-3160200.zip - # platform: x86 - install: - ps: Install-Product node 8 # node 8 or later is required to test js async stuff - MKDIR %CD%\DIST @@ -32,6 +33,8 @@ install: - 7z x -y "%SQLITE_ARCHIVE%" -o"%CD%\DIST"> nul - IF not exist "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%" - 7z x -y "%MINGW_ARCHIVE%" -o"%CD%\DIST"> nul + - IF not exist "%OPENBLAS_ARCHIVE%" appveyor DownloadFile "%OPENBLAS_URL%" -FileName "%OPENBLAS_ARCHIVE%" + - 7z x -y "%OPENBLAS_ARCHIVE%" -o"%CD%\DIST"> nul - SET PATH=%CD%\DIST\%MINGW_DIR%\BIN;%CD%\BIN;%PATH% - IF "%PLATFORM%" == "x64" ( copy C:\OpenSSL-Win64\libeay32.dll %CD%\BIN\libeay64.dll & copy C:\OpenSSL-Win64\libeay32.dll %CD%\BIN\libeay32.dll & copy C:\OpenSSL-Win64\libssl32.dll %CD%\BIN\libssl64.dll & copy C:\OpenSSL-Win64\libssl32.dll %CD%\BIN\libssl32.dll ) ELSE ( copy C:\OpenSSL-Win32\libeay32.dll %CD%\BIN\libeay32.dll & copy C:\OpenSSL-Win32\libssl32.dll %CD%\BIN\libssl32.dll ) diff --git a/bin/nim-gdb b/bin/nim-gdb index e7b41094df71..9d6f462bb85c 100755 --- a/bin/nim-gdb +++ b/bin/nim-gdb @@ -3,6 +3,9 @@ # Exit if anything fails set -e +which nim > /dev/null || (echo "nim not in PATH"; exit 1) +which gdb > /dev/null || (echo "gdb not in PATH"; exit 1) + # Find out where the pretty printer Python module is NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim)))) GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py" diff --git a/build_all.sh b/build_all.sh index 0706dcaf2b2b..333814d62dc4 100644 --- a/build_all.sh +++ b/build_all.sh @@ -27,7 +27,6 @@ build_nim_csources(){ [ -f $nim_csources ] || echo_run build_nim_csources # Note: if fails, may need to `cd csources && git pull` -# see D20190115T162028 echo_run bin/nim c --skipUserCfg --skipParentCfg koch echo_run ./koch boot -d:release diff --git a/changelog.md b/changelog.md index 85e06112bb7b..211855bfc289 100644 --- a/changelog.md +++ b/changelog.md @@ -124,6 +124,11 @@ proc enumToString*(enums: openArray[enum]): string = - Added `xmltree.toXmlAttributes`. +- Added ``std/sums`` module for fast summation functions. + +- Added `Rusage`, `getrusage`, `wait4` to posix interface. + + ### Library changes diff --git a/compiler/ast.nim b/compiler/ast.nim index 24891d6d3ec1..fbe6132b3769 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -271,6 +271,10 @@ type # language; for interfacing with Objective C sfDiscardable, # returned value may be discarded implicitly sfOverriden, # proc is overriden + sfCallSideLineinfo# A flag for template symbols to tell the + # compiler it should use line information from + # the calling side of the macro, not from the + # implementation. sfGenSym # symbol is 'gensym'ed; do not add to symbol table TSymFlags* = set[TSymFlag] @@ -655,7 +659,7 @@ type mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, - mNimvm, mIntDefine, mStrDefine, mRunnableExamples, + mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, mSymIsInstantiationOf @@ -1265,7 +1269,10 @@ proc `$`*(x: TLockLevel): string = else: result = $int16(x) proc `$`*(s: PSym): string = - result = s.name.s & "@" & $s.id + if s != nil: + result = s.name.s & "@" & $s.id + else: + result = "" proc newType*(kind: TTypeKind, owner: PSym): PType = new(result) @@ -1352,7 +1359,6 @@ proc copySym*(s: PSym): PSym = result = newSym(s.kind, s.name, s.owner, s.info, s.options) #result.ast = nil # BUGFIX; was: s.ast which made problems result.typ = s.typ - result.id = getID() when debugIds: registerId(result) result.flags = s.flags result.magic = s.magic diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 60eecbdc5d27..48a651632b99 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -238,17 +238,6 @@ proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent, maxRecDepth: int): Rope -proc ropeConstr(indent: int, c: openArray[Rope]): Rope = - # array of (name, value) pairs - var istr = rspaces(indent + 2) - result = rope("{") - var i = 0 - while i <= high(c): - if i > 0: add(result, ",") - addf(result, "$N$1\"$2\": $3", [istr, c[i], c[i + 1]]) - inc(i, 2) - addf(result, "$N$1}", [rspaces(indent)]) - proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int, maxRecDepth: int): Rope = if n == nil: @@ -257,51 +246,60 @@ proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int, result = "\"$1\"" % [rope(n.name.s)] else: var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1) - result = ropeConstr(indent, [rope("kind"), - makeYamlString($n.kind), - rope("name"), makeYamlString(n.name.s), #rope("typ"), typeToYamlAux(conf, n.typ, marker, # indent + 2, maxRecDepth - 1), - rope("info"), lineInfoToStr(conf, n.info), - rope("flags"), flagsToStr(n.flags), - rope("magic"), makeYamlString($n.magic), - rope("ast"), ast, rope("options"), - flagsToStr(n.options), rope("position"), - rope(n.position), - rope("k"), makeYamlString($n.loc.k), - rope("storage"), makeYamlString($n.loc.storage), - rope("flags"), makeYamlString($n.loc.flags), - rope("r"), n.loc.r, - rope("lode"), treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1) - ]) + let istr = rspaces(indent + 2) + result = rope("{") + addf(result, "$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) + addf(result, "$N$1\"name\": $2", [istr, makeYamlString(n.name.s)]) + addf(result, "$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1)]) + if conf != nil: + # if we don't pass the config, we probably don't care about the line info + addf(result, "$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) + if card(n.flags) > 0: + addf(result, "$N$1\"flags\": $2", [istr, flagsToStr(n.flags)]) + addf(result, "$N$1\"magic\": $2", [istr, makeYamlString($n.magic)]) + addf(result, "$N$1\"ast\": $2", [istr, ast]) + addf(result, "$N$1\"options\": $2", [istr, flagsToStr(n.options)]) + addf(result, "$N$1\"position\": $2", [istr, rope(n.position)]) + addf(result, "$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)]) + addf(result, "$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)]) + if card(n.loc.flags) > 0: + addf(result, "$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)]) + addf(result, "$N$1\"r\": $2", [istr, n.loc.r]) + addf(result, "$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)]) + addf(result, "$N$1}", [rspaces(indent)]) proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int, maxRecDepth: int): Rope = + var sonsRope: Rope if n == nil: - result = rope("null") + sonsRope = rope("null") elif containsOrIncl(marker, n.id): - result = "\"$1 @$2\"" % [rope($n.kind), rope( + sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope( strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))] else: if sonsLen(n) > 0: - result = rope("[") + sonsRope = rope("[") for i in countup(0, sonsLen(n) - 1): - if i > 0: add(result, ",") - addf(result, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i], + if i > 0: add(sonsRope, ",") + addf(sonsRope, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i], marker, indent + 4, maxRecDepth - 1)]) - addf(result, "$N$1]", [rspaces(indent + 2)]) + addf(sonsRope, "$N$1]", [rspaces(indent + 2)]) else: - result = rope("null") - result = ropeConstr(indent, [rope("kind"), - makeYamlString($n.kind), - rope("sym"), symToYamlAux(conf, n.sym, marker, - indent + 2, maxRecDepth - 1), rope("n"), treeToYamlAux(conf, n.n, marker, - indent + 2, maxRecDepth - 1), rope("flags"), flagsToStr(n.flags), - rope("callconv"), - makeYamlString(CallingConvToStr[n.callConv]), - rope("size"), rope(n.size), - rope("align"), rope(n.align), - rope("sons"), result]) + sonsRope = rope("null") + + let istr = rspaces(indent + 2) + result = rope("{") + addf(result, "$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) + addf(result, "$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)]) + addf(result, "$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)]) + if card(n.flags) > 0: + addf(result, "$N$1\"flags\": $2", [istr, flagsToStr(n.flags)]) + addf(result, "$N$1\"callconv\": $2", [istr, makeYamlString(CallingConvToStr[n.callConv])]) + addf(result, "$N$1\"size\": $2", [istr, rope(n.size)]) + addf(result, "$N$1\"align\": $2", [istr, rope(n.align)]) + addf(result, "$N$1\"sons\": $2", [istr, sonsRope]) proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int, maxRecDepth: int): Rope = @@ -311,7 +309,8 @@ proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int, var istr = rspaces(indent + 2) result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)] if maxRecDepth != 0: - addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) + if conf != nil: + addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) case n.kind of nkCharLit..nkInt64Lit: addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) @@ -362,8 +361,8 @@ proc debugType(conf: ConfigRef; n: PType, maxRecDepth=100): Rope = add(result, " ") add(result, n.sym.name.s) if n.kind in IntegralTypes and n.n != nil: - add(result, ", node: ") - add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true)) + add(result, ", n: ") + add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=false)) if (n.kind != tyString) and (sonsLen(n) > 0) and maxRecDepth != 0: add(result, "(") for i in countup(0, sonsLen(n) - 1): @@ -373,7 +372,7 @@ proc debugType(conf: ConfigRef; n: PType, maxRecDepth=100): Rope = else: add(result, debugType(conf, n.sons[i], maxRecDepth-1)) if n.kind == tyObject and n.n != nil: - add(result, ", node: ") + add(result, ", n: ") add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true)) add(result, ")") @@ -387,9 +386,11 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; [istr, makeYamlString($n.kind)] when defined(useNodeIds): addf(result, ",$N$1\"id\": $2", [istr, rope(n.id)]) - addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) + if conf != nil: + addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) if maxRecDepth != 0: - addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)]) + if card(n.flags) > 0: + addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)]) case n.kind of nkCharLit..nkUInt64Lit: addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) @@ -400,15 +401,19 @@ proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: let s = n.sym - addf(result, ",$N$1\"sym\": $2_$3 k: $4 storage: $5 flags: $6 r: $7", - [istr, rope(s.name.s), rope(s.id), - rope($s.loc.k), - rope($s.loc.storage), - rope($s.loc.flags), - s.loc.r - ]) -# [istr, symToYaml(conf, n.sym, indent, maxRecDepth), -# rope(n.sym.id)]) + var symStr = "" + symStr.add "\"kind\": " + symStr.add $s.kind + symStr.add ", \"name\": " + symStr.add s.name.s + symStr.add ", \"id\": " + symStr.add s.id + if s.kind in {skField, skEnumField, skParam}: + symStr.add ", \"position\": " + symStr.add s.position + addf(result, ",$N$1\"sym\": {$2}", [ + istr, rope(symStr)]) + if renderType and n.sym.typ != nil: addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.sym.typ, 2)]) of nkIdent: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ed62550040b6..8ccca9813109 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -460,7 +460,7 @@ proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, frmt: string) = if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr") initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - lineCg(p, cpsStmts, frmt, addrLoc(p.config, a), rdLoc(b)) + lineCg(p, cpsStmts, frmt, byRefLoc(p, a), rdLoc(b)) proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc @@ -1028,7 +1028,7 @@ proc gcUsage(conf: ConfigRef; n: PNode) = proc strLoc(p: BProc; d: TLoc): Rope = if p.config.selectedGc == gcDestructors: - result = addrLoc(p.config, d) + result = byRefLoc(p, d) else: result = rdLoc(d) @@ -1110,7 +1110,7 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = strLoc(p, dest), rdLoc(a))) if p.config.selectedGC == gcDestructors: linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n", - addrLoc(p.config, dest), lens, rope(L)) + byRefLoc(p, dest), lens, rope(L)) else: initLoc(call, locCall, e, OnHeap) call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, rope(L)]) @@ -1123,7 +1123,7 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; let seqAppendPattern = if not p.module.compileToCpp: - "($2) #incrSeqV3(&($1)->Sup, $3)" + "($2) #incrSeqV3((TGenericSeq*)($1), $3)" else: "($2) #incrSeqV3($1, $3)" var a, b, dest, tmpL, call: TLoc @@ -1997,6 +1997,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mSizeOf: let t = e.sons[1].typ.skipTypes({tyTypeDesc}) putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)]) + of mAlignOf: + let t = e.sons[1].typ.skipTypes({tyTypeDesc}) + if not p.module.compileToCpp: + p.module.includeHeader("") + putIntoDest(p, d, e, "((NI)alignof($1))" % [getTypeDesc(p.module, t)]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 6c33b302d207..bc873539772e 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -124,13 +124,14 @@ proc endBlock(p: BProc, blockEnd: Rope) = proc endBlock(p: BProc) = let topBlock = p.blocks.len - 1 - var blockEnd = if p.blocks[topBlock].label != nil: - ropecg(p.module, "} $1: ;$n", p.blocks[topBlock].label) - else: - ~"}$n" let frameLen = p.blocks[topBlock].frameLen + var blockEnd: Rope if frameLen > 0: blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope]) + if p.blocks[topBlock].label != nil: + blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label]) + else: + blockEnd.addf("}$n", []) endBlock(p, blockEnd) proc genSimpleBlock(p: BProc, stmts: PNode) {.inline.} = @@ -167,7 +168,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = let tryStmt = p.nestedTryStmts.pop if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions: # Pop safe points generated by try - if not tryStmt.inExcept: + if not tryStmt.inExcept and not isDefined(p.config, "nimQuirky"): linefmt(p, cpsStmts, "#popSafePoint();$n") # Pop this try-stmt of the list of nested trys @@ -382,7 +383,7 @@ proc genReturnStmt(p: BProc, t: PNode) = blockLeaveActions(p, howManyTrys = p.nestedTryStmts.len, howManyExcepts = p.inExceptBlockLen) - if (p.finallySafePoints.len > 0): + if (p.finallySafePoints.len > 0) and not isDefined(p.config, "nimQuirky"): # If we're in a finally block, and we came here by exception # consume it before we return. var safePoint = p.finallySafePoints[p.finallySafePoints.len-1] @@ -919,29 +920,38 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = # if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) - p.module.includeHeader("") + let quirkyExceptions = isDefined(p.config, "nimQuirky") + if not quirkyExceptions: + p.module.includeHeader("") genLineDir(p, t) - var safePoint = getTempName(p.module) discard cgsym(p.module, "Exception") - linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint) - linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint) - if isDefined(p.config, "nimStdSetjmp"): - linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) - elif isDefined(p.config, "nimSigSetjmp"): - linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint) - elif isDefined(p.config, "nimRawSetjmp"): - linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint) - else: - linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) - startBlock(p, "if ($1.status == 0) {$n", [safePoint]) + var safePoint: Rope + if not quirkyExceptions: + safePoint = getTempName(p.module) + linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint) + linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint) + if isDefined(p.config, "nimStdSetjmp"): + linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) + elif isDefined(p.config, "nimSigSetjmp"): + linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint) + elif isDefined(p.config, "nimRawSetjmp"): + linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint) + else: + linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) + startBlock(p, "if ($1.status == 0) {$n", [safePoint]) var length = sonsLen(t) add(p.nestedTryStmts, (t, false)) expr(p, t.sons[0], d) - linefmt(p, cpsStmts, "#popSafePoint();$n") - endBlock(p) - startBlock(p, "else {$n") - linefmt(p, cpsStmts, "#popSafePoint();$n") - genRestoreFrameAfterException(p) + if not quirkyExceptions: + linefmt(p, cpsStmts, "#popSafePoint();$n") + endBlock(p) + startBlock(p, "else {$n") + linefmt(p, cpsStmts, "#popSafePoint();$n") + genRestoreFrameAfterException(p) + elif 1 < length and t.sons[1].kind == nkExceptBranch: + startBlock(p, "if (#getCurrentException()) {$n") + else: + startBlock(p) p.nestedTryStmts[^1].inExcept = true var i = 1 while (i < length) and (t.sons[i].kind == nkExceptBranch): @@ -952,7 +962,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = # general except section: if i > 1: lineF(p, cpsStmts, "else", []) startBlock(p) - linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) + if not quirkyExceptions: + linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) expr(p, t.sons[i].sons[0], d) linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) @@ -968,7 +979,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = [genTypeInfo(p.module, t[i][j].typ, t[i][j].info)]) if i > 1: line(p, cpsStmts, "else ") startBlock(p, "if ($1) {$n", [orExpr]) - linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) + if not quirkyExceptions: + linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) expr(p, t.sons[i].sons[blen-1], d) linefmt(p, cpsStmts, "#popCurrentException();$n") endBlock(p) @@ -979,7 +991,8 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = p.finallySafePoints.add(safePoint) genSimpleBlock(p, t.sons[i].sons[0]) discard pop(p.finallySafePoints) - linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) + if not quirkyExceptions: + linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = var res = "" diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 23e16cb93497..c557123ac41c 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -165,7 +165,6 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = of tySet: if mapSetType(conf, base) == ctArray: result = ctPtrToArray else: result = ctPtr - # XXX for some reason this breaks the pegs module else: result = ctPtr of tyPointer: result = ctPtr of tySequence: result = ctNimSeq diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 2d9814621d95..cb186de10100 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -16,7 +16,7 @@ import condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings, tables, sets, ndi, lineinfos, pathutils, transf -import system/helpers2 +import system/indexerrors when not defined(leanCompiler): import semparallel @@ -264,6 +264,12 @@ proc addrLoc(conf: ConfigRef; a: TLoc): Rope = if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray: result = "(&" & result & ")" +proc byRefLoc(p: BProc; a: TLoc): Rope = + result = a.r + if lfIndirect notin a.flags and mapType(p.config, a.t) != ctArray and not + p.module.compileToCpp: + result = "(&" & result & ")" + proc rdCharLoc(a: TLoc): Rope = # read a location that may need a char-cast: result = rdLoc(a) @@ -310,7 +316,9 @@ proc resetLoc(p: BProc, loc: var TLoc) = let containsGcRef = containsGarbageCollectedRef(loc.t) let typ = skipTypes(loc.t, abstractVarRange) if isImportedCppType(typ): return - if not isComplexValueType(typ): + if p.config.selectedGc == gcDestructors and typ.kind in {tyString, tySequence}: + linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", rdLoc(loc)) + elif not isComplexValueType(typ): if containsGcRef: var nilLoc: TLoc initLoc(nilLoc, locTemp, loc.lode, OnStack) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 9a4c1701c2d5..5e7ce3a08184 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -15,26 +15,21 @@ import from options import Feature from lineinfos import HintsToStr, WarningsToStr -const - catNone = "false" - proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") = symbols[symbol] = value proc undefSymbol*(symbols: StringTableRef; symbol: string) = - symbols[symbol] = catNone + symbols.del(symbol) #proc lookupSymbol*(symbols: StringTableRef; symbol: string): string = # result = if isDefined(symbol): gSymbols[symbol] else: nil iterator definedSymbolNames*(symbols: StringTableRef): string = for key, val in pairs(symbols): - if val != catNone: yield key + yield key proc countDefinedSymbols*(symbols: StringTableRef): int = - result = 0 - for key, val in pairs(symbols): - if val != catNone: inc(result) + symbols.len proc initDefines*(symbols: StringTableRef) = # for bootstrapping purposes and old code: diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index e21d532ea985..0570873ea986 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -139,7 +139,7 @@ proc isLastRead(s: PSym; c: var Con; pc, comesFrom: int): int = of def: if c.g[pc].sym == s: # the path lead to a redefinition of 's' --> abandon it. - return high(int) + return high(int) inc pc of use: if c.g[pc].sym == s: @@ -154,7 +154,7 @@ proc isLastRead(s: PSym; c: var Con; pc, comesFrom: int): int = if variantA < 0: return -1 let variantB = isLastRead(s, c, pc + c.g[pc].dest, pc) if variantB < 0: return -1 - elif variantA == high(int): + elif variantA == high(int): variantA = variantB pc = variantA of InstrKind.join: @@ -244,7 +244,10 @@ proc patchHead(n: PNode) = proc patchHead(s: PSym) = if sfFromGeneric in s.flags: - patchHead(s.ast[bodyPos]) + # do not patch the builtin type bound operators for seqs: + let dest = s.typ.sons[1].skipTypes(abstractVar) + if dest.kind != tySequence: + patchHead(s.ast[bodyPos]) proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) = var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">" @@ -267,7 +270,8 @@ template genOp(opr, opname, ri) = globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t)) elif op.ast[genericParamsPos].kind != nkEmpty: - globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic") + globalError(c.graph.config, dest.info, "internal error: '" & opname & + "' operator is generic") patchHead op if sfError in op.flags: checkForErrorPragma(c, t, ri, opname) let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) @@ -275,6 +279,12 @@ template genOp(opr, opname, ri) = result = newTree(nkCall, newSymNode(op), addrExp) proc genSink(c: Con; t: PType; dest, ri: PNode): PNode = + when false: + if t.kind != tyString: + echo "this one ", c.graph.config$dest.info, " for ", typeToString(t, preferDesc) + debug t.sink.typ.sons[2] + echo t.sink.id, " owner ", t.id + quit 1 let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) genOp(if t.sink != nil: t.sink else: t.assignment, "=sink", ri) @@ -591,6 +601,12 @@ proc p(n: PNode; c: var Con): PNode = of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef: result = n + of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv: + result = copyNode(n) + # Destination type + result.add n[0] + # Analyse the inner expression + result.add p(n[1], c) else: result = copyNode(n) recurse(n, result) @@ -616,7 +632,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = let params = owner.typ.n for i in 1 ..< params.len: let param = params[i].sym - if param.typ.kind == tySink and hasDestructor(param.typ): + if param.typ.kind == tySink and hasDestructor(param.typ.sons[0]): c.destroys.add genDestroy(c, param.typ.skipTypes({tyGenericInst, tyAlias, tySink}), params[i]) let body = p(n, c) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a7f7d77b5c7f..5af4c464e16e 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -149,14 +149,16 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, if filename.len == 0: inc(d.id) let nameOnly = splitFile(d.filename).name - let subdir = getNimcacheDir(conf) / RelativeDir(nameOnly) - createDir(subdir) - outp = subdir / RelativeFile(nameOnly & "_snippet_" & $d.id & ".nim") + outp = getNimcacheDir(conf) / RelativeDir(nameOnly) / + RelativeFile(nameOnly & "_snippet_" & $d.id & ".nim") elif isAbsolute(filename): - outp = AbsoluteFile filename + outp = AbsoluteFile(filename) else: # Nim's convention: every path is relative to the file it was written in: - outp = splitFile(d.filename).dir.AbsoluteDir / RelativeFile(filename) + let nameOnly = splitFile(d.filename).name + outp = AbsoluteDir(nameOnly) / RelativeFile(filename) + # Make sure the destination directory exists + createDir(outp.splitFile.dir) # Include the current file if we're parsing a nim file let importStmt = if d.isPureRst: "" else: "import \"$1\"\n" % [d.filename] writeFile(outp, importStmt & content) @@ -244,7 +246,7 @@ proc genComment(d: PDoc, n: PNode): string = result = "" var dummyHasToc: bool if n.comment.len > 0: - renderRstToOut(d[], parseRst(n.comment, toFilename(d.conf, n.info), + renderRstToOut(d[], parseRst(n.comment, toFullPath(d.conf, n.info), toLinenumber(n.info), toColumn(n.info), dummyHasToc, d.options, d.conf), result) @@ -654,7 +656,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) = path = path[cwd.len+1 .. ^1].replace('\\', '/') let gitUrl = getConfigVar(d.conf, "git.url") if gitUrl.len > 0: - let commit = getConfigVar(d.conf, "git.commit", "master") + let defaultBranch = if NimPatch mod 2 == 1: "devel" else: "master" + let commit = getConfigVar(d.conf, "git.commit", defaultBranch) let develBranch = getConfigVar(d.conf, "git.devel", "devel") dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc, ["path", "line", "url", "commit", "devel"], [rope path.string, diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 0f9220102b6c..43c038270e95 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -186,9 +186,9 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; renderTree(result, {renderNoComments})) else: result = copyNode(body) - #ctx.instLines = body.kind notin {nkStmtList, nkStmtListExpr, - # nkBlockStmt, nkBlockExpr} - #if ctx.instLines: result.info = n.info + ctx.instLines = sfCallSideLineinfo in tmpl.flags + if ctx.instLines: + result.info = n.info for i in countup(0, safeLen(body) - 1): evalTemplateAux(body.sons[i], args, ctx, result) result.flags.incl nfFromTemplate @@ -196,4 +196,3 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; #if ctx.debugActive: # echo "instantion of ", renderTree(result, {renderIds}) dec(conf.evalTemplateCounter) - diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 8625f2fe1de4..5263230fd5ab 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -519,6 +519,7 @@ proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp] else: r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer] + r.kind = resExpr proc ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = var x, y, z: TCompRes @@ -1149,7 +1150,7 @@ template isIndirect(x: PSym): bool = let v = x ({sfAddrTaken, sfGlobal} * v.flags != {} and #(mapType(v.typ) != etyObject) and - {sfImportc, sfVolatile, sfExportc} * v.flags == {} and + {sfImportc, sfExportc} * v.flags == {} and v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, skConst, skTemp, skLet}) @@ -1235,7 +1236,7 @@ proc genProcForSymIfNeeded(p: PProc, s: PSym) = proc genCopyForParamIfNeeded(p: PProc, n: PNode) = let s = n.sym - if p.prc == s.owner or needsNoCopy(p, n): + if p.prc == s.owner or needsNoCopy(p, n): return var owner = p.up while true: @@ -1597,8 +1598,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = internalError(p.config, "createVar: " & $t.kind) result = nil -template returnType: untyped = - ~"" +template returnType: untyped = ~"" proc genVarInit(p: PProc, v: PSym, n: PNode) = var @@ -1913,13 +1913,13 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mHigh: unaryExpr(p, n, r, "", "($1 != null ? ($2.length-1) : -1)") of mInc: - if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: + if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: binaryUintExpr(p, n, r, "+", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)") of ast.mDec: - if n[1].typ.skipTypes(abstractRange).kind in tyUInt .. tyUInt64: + if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: binaryUintExpr(p, n, r, "-", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ddde1be31143..3054b1f2ecff 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -320,17 +320,30 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym; rawAddSon(result, obj) c.ownerToType[owner.id] = result +proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym; + info: TLineInfo): PType = + var r = c.getEnvTypeForOwner(owner, info) + result = newType(tyPtr, owner) + rawAddSon(result, r.base) + proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = let refObj = c.getEnvTypeForOwner(dest, info) # getHiddenParam(dest).typ let obj = refObj.lastSon - let fieldType = c.getEnvTypeForOwner(dep, info) #getHiddenParam(dep).typ + # The assumption here is that gcDestructors means we cannot deal + # with cycles properly, so it's better to produce a weak ref (=ptr) here. + # This seems to be generally correct but since it's a bit risky it's only + # enabled for gcDestructors. + let fieldType = if c.graph.config.selectedGc == gcDestructors: + c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ + else: + c.getEnvTypeForOwner(dep, info) if refObj == fieldType: localError(c.graph.config, dep.info, "internal error: invalid up reference computed") let upIdent = getIdent(c.graph.cache, upName) let upField = lookupInRecord(obj.n, upIdent) if upField != nil: - if upField.typ != fieldType: + if upField.typ.base != fieldType.base: localError(c.graph.config, dep.info, "internal error: up references do not agree") else: let result = newSym(skField, upIdent, obj.owner, obj.owner.info) @@ -452,7 +465,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = w = up of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, - nkConverterDef, nkMacroDef, nkFuncDef: + nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt: discard of nkLambdaKinds, nkIteratorDef: if n.typ != nil: @@ -555,7 +568,7 @@ proc rawClosureCreation(owner: PSym; let upField = lookupInRecord(env.typ.lastSon.n, getIdent(d.graph.cache, upName)) if upField != nil: let up = getUpViaParam(d.graph, owner) - if up != nil and upField.typ == up.typ: + if up != nil and upField.typ.base == up.typ.base: result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info), up, env.info)) #elif oldenv != nil and oldenv.typ == upField.typ: @@ -586,7 +599,7 @@ proc closureCreationForIter(iter: PNode; let upField = lookupInRecord(v.typ.lastSon.n, getIdent(d.graph.cache, upName)) if upField != nil: let u = setupEnvVar(owner, d, c) - if u.typ == upField.typ: + if u.typ.base == upField.typ.base: result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info), u, iter.info)) else: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index a4414d18644f..5eaa4c09f1f9 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -618,7 +618,12 @@ proc getNumber(L: var TLexer, result: var TToken) = tokenEnd(result, postPos-1) L.bufpos = postPos -proc handleHexChar(L: var TLexer, xi: var int) = +proc handleHexChar(L: var TLexer, xi: var int; position: range[0..4]) = + template invalid() = + lexMessage(L, errGenerated, + "expected a hex digit, but found: " & L.buf[L.bufpos] & + "; maybe prepend with 0") + case L.buf[L.bufpos] of '0'..'9': xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('0')) @@ -629,10 +634,12 @@ proc handleHexChar(L: var TLexer, xi: var int) = of 'A'..'F': xi = (xi shl 4) or (ord(L.buf[L.bufpos]) - ord('A') + 10) inc(L.bufpos) + of '"', '\'': + if position <= 1: invalid() + # do not progress the bufpos here. + if position == 0: inc(L.bufpos) else: - lexMessage(L, errGenerated, - "expected a hex digit, but found: " & L.buf[L.bufpos] & - " ; maybe prepend with 0") + invalid() # Need to progress for `nim check` inc(L.bufpos) @@ -727,8 +734,8 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = of 'x', 'X': inc(L.bufpos) var xi = 0 - handleHexChar(L, xi) - handleHexChar(L, xi) + handleHexChar(L, xi, 1) + handleHexChar(L, xi, 2) add(tok.literal, chr(xi)) of 'u', 'U': if tok.tokType == tkCharLit: @@ -739,7 +746,7 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = inc(L.bufpos) var start = L.bufpos while L.buf[L.bufpos] != '}': - handleHexChar(L, xi) + handleHexChar(L, xi, 0) if start == L.bufpos: lexMessage(L, errGenerated, "Unicode codepoint cannot be empty") @@ -749,10 +756,10 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = lexMessage(L, errGenerated, "Unicode codepoint must be lower than 0x10FFFF, but was: " & hex) else: - handleHexChar(L, xi) - handleHexChar(L, xi) - handleHexChar(L, xi) - handleHexChar(L, xi) + handleHexChar(L, xi, 1) + handleHexChar(L, xi, 2) + handleHexChar(L, xi, 3) + handleHexChar(L, xi, 4) addUnicodeCodePoint(tok.literal, xi) of '0'..'9': if matchTwoChars(L, '0', {'0'..'9'}): diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 165d75821863..f3aba49b40e0 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -224,7 +224,7 @@ type proc `==`*(a, b: FileIndex): bool {.borrow.} -proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} = +proc raiseRecoverableError*(msg: string) {.noinline.} = raise newException(ERecoverableError, msg) const diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index 9e27a2d7d53f..129f719e2528 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -114,7 +114,6 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = try: result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir) - .replace(" ") except ValueError: localError(conf, n.info, "invalid path: " & n.strVal) result = n.strVal diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 6bb2c3fa3cd3..78f253bdc540 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -171,6 +171,7 @@ proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string = proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile) = assert fileIdx.int32 >= 0 conf.m.fileInfos[fileIdx.int32].dirtyFile = filename + setLen conf.m.fileInfos[fileIdx.int32].lines, 0 proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) = assert fileIdx.int32 >= 0 @@ -194,6 +195,9 @@ template toFilename*(conf: ConfigRef; info: TLineInfo): string = template toFullPath*(conf: ConfigRef; info: TLineInfo): string = toFullPath(conf, info.fileIndex) +template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string = + string toFullPathConsiderDirty(conf, info.fileIndex) + proc toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = if info.fileIndex.int32 < 0: result = "???" @@ -434,7 +438,7 @@ proc sourceLine*(conf: ConfigRef; i: TLineInfo): string = if conf.m.fileInfos[i.fileIndex.int32].lines.len == 0: try: - for line in lines(toFullPath(conf, i)): + for line in lines(toFullPathConsiderDirty(conf, i)): addSourceLine conf, i.fileIndex, line.string except IOError: discard diff --git a/compiler/options.nim b/compiler/options.nim index 54276f99d938..0a25b1b96975 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -360,7 +360,7 @@ proc cppDefine*(c: ConfigRef; define: string) = proc isDefined*(conf: ConfigRef; symbol: string): bool = if conf.symbols.hasKey(symbol): - result = conf.symbols[symbol] != "false" + result = true elif cmpIgnoreStyle(symbol, CPU[conf.target.targetCPU].name) == 0: result = true elif cmpIgnoreStyle(symbol, platform.OS[conf.target.targetOS].name) == 0: diff --git a/compiler/parser.nim b/compiler/parser.nim index c9626c527c2c..01a3ce4d001a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -724,6 +724,14 @@ const tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType, tkEnum, tkTuple, tkObject, tkProc} +proc commandExpr(p: var TParser; r: PNode; mode: TPrimaryMode): PNode = + result = newNodeP(nkCommand, p) + addSon(result, r) + var isFirstParam = true + # progress NOT guaranteed + p.hasProgress = false + addSon result, commandParam(p, isFirstParam, mode) + proc primarySuffix(p: var TParser, r: PNode, baseIndent: int, mode: TPrimaryMode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? @@ -734,8 +742,6 @@ proc primarySuffix(p: var TParser, r: PNode, #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax result = r - template somePar() = - if p.tok.strongSpaceA > 0: break # progress guaranteed while p.tok.indent < 0 or (p.tok.tokType == tkDot and p.tok.indent >= baseIndent): @@ -749,6 +755,8 @@ proc primarySuffix(p: var TParser, r: PNode, result = newNodeP(nkCommand, p) result.addSon r result.addSon primary(p, pmNormal) + else: + result = commandExpr(p, result, mode) break result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: @@ -759,39 +767,27 @@ proc primarySuffix(p: var TParser, r: PNode, result = parseGStrLit(p, result) of tkBracketLe: # progress guaranteed - somePar() + if p.tok.strongSpaceA > 0: + result = commandExpr(p, result, mode) + break result = namedParams(p, result, nkBracketExpr, tkBracketRi) of tkCurlyLe: # progress guaranteed - somePar() + if p.tok.strongSpaceA > 0: + result = commandExpr(p, result, mode) + break result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}: - # XXX: In type sections we allow the free application of the - # command syntax, with the exception of expressions such as - # `foo ref` or `foo ptr`. Unfortunately, these two are also - # used as infix operators for the memory regions feature and - # the current parsing rules don't play well here. + # XXX: In type sections we allow the free application of the + # command syntax, with the exception of expressions such as + # `foo ref` or `foo ptr`. Unfortunately, these two are also + # used as infix operators for the memory regions feature and + # the current parsing rules don't play well here. if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that - let a = result - result = newNodeP(nkCommand, p) - addSon(result, a) - var isFirstParam = true - when true: - # progress NOT guaranteed - p.hasProgress = false - addSon result, commandParam(p, isFirstParam, mode) - if not p.hasProgress: break - else: - while p.tok.tokType != tkEof: - let x = parseExpr(p) - addSon(result, x) - if p.tok.tokType != tkComma: break - getTok(p) - optInd(p, x) - result = postExprBlocks(p, result) + result = commandExpr(p, result, mode) break else: break diff --git a/compiler/passes.nim b/compiler/passes.nim index 50fc13e1e2cd..f45ee03f78a1 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -167,7 +167,9 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool { if graph.stopCompile(): break var n = parseTopLevelStmt(p) if n.kind == nkEmpty: break - if {sfNoForward, sfReorder} * module.flags != {}: + if sfSystemModule notin module.flags and + ({sfNoForward, sfReorder} * module.flags != {} or + codeReordering in graph.config.features): # read everything, no streaming possible var sl = newNodeI(nkStmtList, n.info) sl.add n @@ -175,7 +177,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool { var n = parseTopLevelStmt(p) if n.kind == nkEmpty: break sl.add n - if sfReorder in module.flags: + if sfReorder in module.flags or codeReordering in graph.config.features: sl = reorder(graph, sl, module) discard processTopLevelStmt(graph, sl, a) break diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 3967fa22dcbd..9b3c66104c78 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -67,7 +67,7 @@ const wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims, - wIntDefine, wStrDefine, wUsed, wCompilerProc, wCore} + wIntDefine, wStrDefine, wBoolDefine, wUsed, wCompilerProc, wCore} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} @@ -241,7 +241,7 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = # deprecated as of 0.18.1 message(c.config, n.info, warnDeprecated, - "use {.experimental: \"codeReordering.\".} instead; " & + "use {.experimental: \"codeReordering\".} instead; " & (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated") proc processCallConv(c: PContext, n: PNode) = @@ -719,26 +719,34 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = result = qualifiedLookUp(c, n, {checkUndeclared}) proc semCustomPragma(c: PContext, n: PNode): PNode = + var callNode: PNode + if n.kind == nkIdent: - result = newTree(nkCall, n) + # pragma -> pragma() + callNode = newTree(nkCall, n) elif n.kind == nkExprColonExpr: # pragma: arg -> pragma(arg) - result = newTree(nkCall, n[0], n[1]) + callNode = newTree(nkCall, n[0], n[1]) elif n.kind in nkPragmaCallKinds: - result = n + callNode = n else: invalidPragma(c, n) return n - let r = c.semOverloadedCall(c, result, n, {skTemplate}, {efNoUndeclared}) + let r = c.semOverloadedCall(c, callNode, n, {skTemplate}, {efNoUndeclared}) + if r.isNil or sfCustomPragma notin r[0].sym.flags: invalidPragma(c, n) - else: - result = r - if n.kind == nkIdent: - result = result[0] - elif n.kind == nkExprColonExpr: - result.kind = n.kind # pragma(arg) -> pragma: arg + return n + + result = r + # Transform the nkCall node back to its original form if possible + if n.kind == nkIdent and r.len == 1: + # pragma() -> pragma + result = result[0] + elif n.kind == nkExprColonExpr and r.len == 2: + # pragma(arg) -> pragma: arg + result.kind = n.kind proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, validPragmas: TSpecialWords, comesFromPush: bool) : bool = @@ -803,12 +811,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wSize: if sym.typ == nil: invalidPragma(c, it) var size = expectIntLit(c, it) - if not isPowerOfTwo(size) or size <= 0 or size > 8: - localError(c.config, it.info, "size may only be 1, 2, 4 or 8") - else: + case size + of 1, 2, 4, 8: sym.typ.size = size - # TODO, this is not correct - sym.typ.align = int16(size) + if size == 8 and c.config.target.targetCPU == cpuI386: + sym.typ.align = 4 + else: + sym.typ.align = int16(size) + else: + localError(c.config, it.info, "size may only be 1, 2, 4 or 8") of wNodecl: noVal(c, it) incl(sym.loc.flags, lfNoDecl) @@ -864,7 +875,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, incl(sym.flags, sfSideEffect) of wNoreturn: noVal(c, it) - incl(sym.flags, sfNoReturn) + # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode! + if not isDefined(c.config, "nimQuirky"): + incl(sym.flags, sfNoReturn) if sym.typ[0] != nil: localError(c.config, sym.ast[paramsPos][0].info, ".noreturn with return type not allowed") @@ -1106,6 +1119,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, sym.magic = mIntDefine of wStrDefine: sym.magic = mStrDefine + of wBoolDefine: + sym.magic = mBoolDefine of wUsed: noVal(c, it) if sym == nil: invalidPragma(c, it) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 8c4d0d307ed5..18ec659baf4f 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -212,7 +212,7 @@ proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) = # Problematic circular dependency, we arrange the nodes into # their original relative order and make sure to re-merge # consecutive type and const sections - var wmsg = "Circular dependency detected. reorder pragma may not be able to" & + var wmsg = "Circular dependency detected. `codeReordering` pragma may not be able to" & " reorder some nodes properely" when defined(debugReorder): wmsg &= ":\n" diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 9f1ef313b71e..41b0879e663e 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -316,6 +316,11 @@ proc liftBody(g: ModuleGraph; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = if typ.kind == tyDistinct: return liftBodyDistinctType(g, typ, kind, info) + when false: + var typ = typ + if c.config.selectedGC == gcDestructors and typ.kind == tySequence: + # use the canonical type to access the =sink and =destroy etc. + typ = c.graph.sysTypes[tySequence] var a: TLiftCtx a.info = info diff --git a/compiler/semcall.nim b/compiler/semcall.nim index e8723e3df2bc..05bfae0df8da 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -221,7 +221,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add(diag & "\n") if skipped > 0: candidates.add($skipped & " other mismatching symbols have been " & - " suppressed; compile with --showAllMismatches:on to see them\n") + "suppressed; compile with --showAllMismatches:on to see them\n") result = (prefer, candidates) const @@ -239,6 +239,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = if c.config.m.errorOutputs == {}: # fail fast: globalError(c.config, n.info, "type mismatch") + return if errors.len == 0: localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree) return diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 68f1c6c3a7e6..239dbad5441a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -15,7 +15,7 @@ const errXExpectsTypeOrValue = "'$1' expects a type or value" errVarForOutParamNeededX = "for a 'var' type a variable needs to be passed; but '$1' is immutable" errXStackEscape = "address of '$1' may not escape its stack frame" - errExprHasNoAddress = "expression has no address; maybe use 'unsafeAddr'" + errExprHasNoAddress = "expression has no address" errCannotInterpretNodeX = "cannot evaluate '$1'" errNamedExprExpected = "named expression expected" errNamedExprNotAllowed = "named expression not allowed here" @@ -39,13 +39,14 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode +template rejectEmptyNode(n: PNode) = + # No matter what a nkEmpty node is not what we want here + if n.kind == nkEmpty: illFormedAst(n, c.config) + proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = + rejectEmptyNode(n) # same as 'semExprWithType' but doesn't check for proc vars result = semExpr(c, n, flags + {efOperand}) - #if result.kind == nkEmpty and result.typ.isNil: - # do not produce another redundant error message: - #raiseRecoverableError("") - # result = errorNode(c, n) if result.typ != nil: # XXX tyGenericInst here? if result.typ.kind == tyProc and tfUnresolved in result.typ.flags: @@ -59,10 +60,10 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result.typ = errorType(c) proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = + rejectEmptyNode(n) result = semExpr(c, n, flags+{efWantValue}) - if result.isNil or result.kind == nkEmpty: + if result.kind == nkEmpty: # do not produce another redundant error message: - #raiseRecoverableError("") result = errorNode(c, n) if result.typ == nil or result.typ == c.enforceVoidContext: localError(c.config, n.info, errExprXHasNoType % @@ -72,7 +73,8 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = - result = semExpr(c, n, flags) + rejectEmptyNode(n) + result = semExpr(c, n, flags+{efWantValue}) if result.kind == nkEmpty: # do not produce another redundant error message: result = errorNode(c, n) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 0bdd0b64c451..24687192862a 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -15,7 +15,7 @@ import nversion, platform, math, msgs, os, condsyms, idents, renderer, types, commands, magicsys, modulegraphs, strtabs, lineinfos -import system/helpers2 +import system/indexerrors proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode = case skipTypes(n.typ, abstractVarRange).kind @@ -465,7 +465,7 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode = else: result = a result.typ = n.typ - of tyOpenArray, tyVarargs, tyProc: + of tyOpenArray, tyVarargs, tyProc, tyPointer: discard else: result = a @@ -493,11 +493,11 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = result = x.sons[int(idx)] if result.kind == nkExprColonExpr: result = result.sons[1] else: - localError(g.config, n.info, formatErrorIndexBound(idx, sonsLen(x)+1) & $n) + localError(g.config, n.info, formatErrorIndexBound(idx, sonsLen(x)-1) & $n) of nkBracket: idx = idx - firstOrd(g.config, x.typ) if idx >= 0 and idx < x.len: result = x.sons[int(idx)] - else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len+1) & $n) + else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n) of nkStrLit..nkTripleStrLit: result = newNodeIT(nkCharLit, x.info, n.typ) if idx >= 0 and idx < len(x.strVal): @@ -569,10 +569,20 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = try: result = newIntNodeT(g.config.symbols[s.name.s].parseInt, n, g) except ValueError: - localError(g.config, n.info, "expression is not an integer literal") + localError(g.config, s.info, + "{.intdefine.} const was set to an invalid integer: '" & + g.config.symbols[s.name.s] & "'") of mStrDefine: if isDefined(g.config, s.name.s): result = newStrNodeT(g.config.symbols[s.name.s], n, g) + of mBoolDefine: + if isDefined(g.config, s.name.s): + try: + result = newIntNodeT(g.config.symbols[s.name.s].parseBool.int, n, g) + except ValueError: + localError(g.config, s.info, + "{.booldefine.} const was set to an invalid bool: '" & + g.config.symbols[s.name.s] & "'") else: result = copyTree(s.ast) of skProc, skFunc, skMethod: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 2311136b4920..77286393ec88 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -16,7 +16,12 @@ proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode = if x.kind == nkSym: x.sym.flags.incl(sfAddrTaken) if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: - localError(c.config, n.info, errExprHasNoAddress) + # Do not suggest the use of unsafeAddr if this expression already is a + # unsafeAddr + if isUnsafeAddr: + localError(c.config, n.info, errExprHasNoAddress) + else: + localError(c.config, n.info, errExprHasNoAddress & "; maybe use 'unsafeAddr'") result.add x result.typ = makePtrType(c, x.typ) @@ -348,9 +353,17 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely, type: " & n[1].typ.typeToString) result = n of mAlignOf: - result = newIntNode(nkIntLit, getAlign(c.config, n[1].typ)) - result.info = n.info - result.typ = n.typ + # this is 100% analog to mSizeOf, could be made more dry. + let align = getAlign(c.config, n[1].typ) + if align == szUnknownSize: + result = n + elif align >= 0: + result = newIntNode(nkIntLit, align) + result.info = n.info + result.typ = n.typ + else: + localError(c.config, n.info, "cannot evaluate 'alignof' because its type is not defined completely, type: " & n[1].typ.typeToString) + result = n of mOffsetOf: var dotExpr: PNode diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 622e72074b09..923558a8d48b 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -232,7 +232,10 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) = proc useVar(a: PEffects, n: PNode) = let s = n.sym if isLocalVar(a, s): - if s.id notin a.init: + if sfNoInit in s.flags: + # If the variable is explicitly marked as .noinit. do not emit any error + a.init.add s.id + elif s.id notin a.init: if {tfNeedsInit, tfNotNil} * s.typ.flags != {}: message(a.config, n.info, warnProveInit, s.name.s) else: @@ -569,10 +572,14 @@ proc isNoEffectList(n: PNode): bool {.inline.} = assert n.kind == nkEffectList n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil) -proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = +proc isTrival(caller: PNode): bool {.inline.} = + result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil} + +proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = let a = skipConvAndClosure(n) let op = a.typ - if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit: + # assume indirect calls are taken here: + if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and not isTrival(caller): internalAssert tracked.config, op.n.sons[0].kind == nkEffectList var effectList = op.n.sons[0] var s = n.skipConv @@ -718,11 +725,17 @@ proc track(tracked: PEffects, n: PNode) = of nkSym: useVar(tracked, n) of nkRaiseStmt: - n.sons[0].info = n.info - #throws(tracked.exc, n.sons[0]) - addEffect(tracked, n.sons[0], useLineInfo=false) - for i in 0 ..< safeLen(n): - track(tracked, n.sons[i]) + if n[0].kind != nkEmpty: + n.sons[0].info = n.info + #throws(tracked.exc, n.sons[0]) + addEffect(tracked, n.sons[0], useLineInfo=false) + for i in 0 ..< safeLen(n): + track(tracked, n.sons[i]) + else: + # A `raise` with no arguments means we're going to re-raise the exception + # being handled or, if outside of an `except` block, a `ReraiseError`. + # Here we add a `Exception` tag in order to cover both the cases. + addEffect(tracked, createRaise(tracked.graph, n)) of nkCallKinds: if getConstExpr(tracked.owner_module, n, tracked.graph) != nil: return @@ -764,7 +777,7 @@ proc track(tracked: PEffects, n: PNode) = if not (a.kind == nkSym and a.sym == tracked.owner): markSideEffect(tracked, a) if a.kind != nkSym or a.sym.magic != mNBindSym: - for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) + for i in 1 ..< len(n): trackOperand(tracked, n.sons[i], paramType(op, i), a) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: # may not look like an assignment, but it is: let arg = n.sons[1] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5e9d5d9c5389..3827da220cfd 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -168,7 +168,7 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode = else: illFormedAst(it, c.config) if isEmptyType(typ) or typ.kind in {tyNil, tyExpr} or (not hasElse and efInTypeof notin flags): - for it in n: + for it in n: it.sons[^1] = discardCheck(c, it.sons[^1], flags) result.kind = nkIfStmt # propagate any enforced VoidContext: @@ -448,10 +448,14 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var def: PNode = c.graph.emptyNode if a.sons[length-1].kind != nkEmpty: def = semExprWithType(c, a.sons[length-1], {efAllowDestructor}) - if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: + if def.typ.kind == tyProc and def.kind == nkSym and def.sym.kind == skMacro: + localError(c.config, def.info, "cannot assign macro symbol to variable here. Forgot to invoke the macro with '()'?") + def.typ = errorType(c) + elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: # prevent the all too common 'var x = int' bug: localError(c.config, def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?") def.typ = errorType(c) + if typ != nil: if typ.isMetaType: def = inferWithMetatype(c, typ, def) @@ -583,7 +587,10 @@ proc semConst(c: PContext, n: PNode): PNode = localError(c.config, a.sons[length-1].info, errConstExprExpected) continue - if def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: + if def.typ.kind == tyProc and def.kind == nkSym and def.sym.kind == skMacro: + localError(c.config, def.info, "cannot assign macro symbol to constant here. Forgot to invoke the macro with '()'?") + def.typ = errorType(c) + elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: # prevent the all too common 'const x = int' bug: localError(c.config, def.info, "'typedesc' metatype is not valid here; typed '=' instead of ':'?") def.typ = errorType(c) @@ -873,6 +880,9 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode = if chckCovered: if covered == toCover(c, n.sons[0].typ): hasElse = true + elif n.sons[0].typ.kind == tyEnum: + localError(c.config, n.info, "not all cases are covered; missing: {$1}" % + formatMissingEnums(n)) else: localError(c.config, n.info, "not all cases are covered") closeScope(c) @@ -1129,6 +1139,11 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = #debug s.typ s.ast = a popOwner(c) + # If the right hand side expression was a macro call we replace it with + # its evaluated result here so that we don't execute it once again in the + # final pass + if a[2].kind in nkCallKinds: + incl a[2].flags, nfSem # bug #10548 if sfExportc in s.flags and s.typ.kind == tyAlias: localError(c.config, name.info, "{.exportc.} not allowed for type aliases") let aa = a.sons[2] @@ -1183,24 +1198,27 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = # compute the type's size and check for illegal recursions: if a.sons[1].kind == nkEmpty: var x = a[2] - while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0: - x = x.lastSon - if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and - s.typ.kind notin {tyObject, tyEnum}: - # type aliases are hard: - var t = semTypeNode(c, x, nil) - assert t != nil - if s.typ != nil and s.typ.kind notin {tyAlias, tySink}: - if t.kind in {tyProc, tyGenericInst} and not t.isMetaType: - assignType(s.typ, t) - s.typ.id = t.id - elif t.kind in {tyObject, tyEnum, tyDistinct}: - assert s.typ != nil - assignType(s.typ, t) - s.typ.id = t.id # same id - checkConstructedType(c.config, s.info, s.typ) - if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: - checkForMetaFields(c, s.typ.n) + if x.kind in nkCallKinds and nfSem in x.flags: + discard "already semchecked, see line marked with bug #10548" + else: + while x.kind in {nkStmtList, nkStmtListExpr} and x.len > 0: + x = x.lastSon + if x.kind notin {nkObjectTy, nkDistinctTy, nkEnumTy, nkEmpty} and + s.typ.kind notin {tyObject, tyEnum}: + # type aliases are hard: + var t = semTypeNode(c, x, nil) + assert t != nil + if s.typ != nil and s.typ.kind notin {tyAlias, tySink}: + if t.kind in {tyProc, tyGenericInst} and not t.isMetaType: + assignType(s.typ, t) + s.typ.id = t.id + elif t.kind in {tyObject, tyEnum, tyDistinct}: + assert s.typ != nil + assignType(s.typ, t) + s.typ.id = t.id # same id + checkConstructedType(c.config, s.info, s.typ) + if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: + checkForMetaFields(c, s.typ.n) instAllTypeBoundOp(c, n.info) @@ -1563,6 +1581,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): # attach these ops to the canonical tySequence obj = canonType(c, obj) + #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink) if opr[].isNil: opr[] = s diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index d920a54b8a02..f180b5373afc 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -558,6 +558,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = incl(s.flags, sfGlobal) else: s = semIdentVis(c, skTemplate, n.sons[0], {}) + + if s.owner != nil and s.owner.name.s == "system" and s.name.s in ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]: + incl(s.flags, sfCallSideLineinfo) + styleCheckDef(c.config, s) onDef(n[0].info, s) # check parameter list: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index fbf36383472f..a497e1eae4ee 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -184,39 +184,6 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errXExpectsOneTypeParam % "varargs") addSonSkipIntLit(result, errorType(c)) -proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = - if n.len < 1: - result = newConstraint(c, kind) - else: - let isCall = int ord(n.kind in nkCallKinds+{nkBracketExpr}) - let n = if n[0].kind == nkBracket: n[0] else: n - checkMinSonsLen(n, 1, c.config) - var t = semTypeNode(c, n.lastSon, nil) - if t.kind == tyTypeDesc and tfUnresolved notin t.flags: - t = t.base - if t.kind == tyVoid: - const kindToStr: array[tyPtr..tyRef, string] = ["ptr", "ref"] - localError(c.config, n.info, "type '$1 void' is not allowed" % kindToStr[kind]) - result = newOrPrevType(kind, prev, c) - var isNilable = false - # check every except the last is an object: - for i in isCall .. n.len-2: - let ni = n[i] - if ni.kind == nkNilLit: - isNilable = true - else: - let region = semTypeNode(c, ni, nil) - if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin { - tyError, tyObject}: - message c.config, n[i].info, errGenerated, "region needs to be an object type" - else: - message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated") - addSonSkipIntLit(result, region) - addSonSkipIntLit(result, t) - if tfPartial in result.flags: - if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial) - #if not isNilable: result.flags.incl tfNotNil - proc semVarType(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 1: result = newOrPrevType(tyVar, prev, c) @@ -608,6 +575,24 @@ proc toCover(c: PContext, t: PType): BiggestInt = proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType, hasCaseFields = false) + +proc formatMissingEnums(n: PNode): string = + var coveredCases = initIntSet() + for i in 1 ..< n.len: + let ofBranch = n[i] + for j in 0 ..< ofBranch.len - 1: + let child = ofBranch[j] + if child.kind == nkIntLit: + coveredCases.incl(child.intVal.int) + elif child.kind == nkRange: + for k in child[0].intVal.int .. child[1].intVal.int: + coveredCases.incl k + for child in n[0].typ.n.sons: + if child.sym.position notin coveredCases: + if result.len > 0: + result.add ", " + result.add child.sym.name.s + proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, father: PNode, rectype: PType) = var a = copyNode(n) @@ -644,7 +629,11 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, delSon(b, sonsLen(b) - 1) semRecordNodeAux(c, lastSon(n.sons[i]), check, pos, b, rectype, hasCaseFields = true) if chckCovered and covered != toCover(c, a.sons[0].typ): - localError(c.config, a.info, "not all cases are covered") + if a.sons[0].typ.kind == tyEnum: + localError(c.config, a.info, "not all cases are covered; missing: {$1}" % + formatMissingEnums(a)) + else: + localError(c.config, a.info, "not all cases are covered") addSon(father, a) proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, @@ -771,7 +760,7 @@ proc addInheritedFields(c: PContext, check: var IntSet, pos: var int, addInheritedFields(c, check, pos, obj.sons[0].skipGenericInvocation) addInheritedFieldsAux(c, check, pos, obj.n) -proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = +proc semObjectNode(c: PContext, n: PNode, prev: PType; isInheritable: bool): PType = if n.sonsLen == 0: return newConstraint(c, tyObject) var check = initIntSet() @@ -803,6 +792,8 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if n.kind != nkObjectTy: internalError(c.config, n.info, "semObjectNode") result = newOrPrevType(tyObject, prev, c) rawAddSon(result, realBase) + if realBase == nil and isInheritable: + result.flags.incl tfInheritable if result.n.isNil: result.n = newNodeI(nkRecList, n.info) else: @@ -817,6 +808,43 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if base == nil and tfInheritable notin result.flags: incl(result.flags, tfFinal) +proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = + if n.len < 1: + result = newConstraint(c, kind) + else: + let isCall = int ord(n.kind in nkCallKinds+{nkBracketExpr}) + let n = if n[0].kind == nkBracket: n[0] else: n + checkMinSonsLen(n, 1, c.config) + let body = n.lastSon + var t = if prev != nil and body.kind == nkObjectTy and tfInheritable in prev.flags: + semObjectNode(c, body, nil, isInheritable=true) + else: + semTypeNode(c, body, nil) + if t.kind == tyTypeDesc and tfUnresolved notin t.flags: + t = t.base + if t.kind == tyVoid: + const kindToStr: array[tyPtr..tyRef, string] = ["ptr", "ref"] + localError(c.config, n.info, "type '$1 void' is not allowed" % kindToStr[kind]) + result = newOrPrevType(kind, prev, c) + var isNilable = false + # check every except the last is an object: + for i in isCall .. n.len-2: + let ni = n[i] + if ni.kind == nkNilLit: + isNilable = true + else: + let region = semTypeNode(c, ni, nil) + if region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin { + tyError, tyObject}: + message c.config, n[i].info, errGenerated, "region needs to be an object type" + else: + message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated") + addSonSkipIntLit(result, region) + addSonSkipIntLit(result, t) + if tfPartial in result.flags: + if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial) + #if not isNilable: result.flags.incl tfNotNil + proc findEnforcedStaticType(t: PType): PType = # This handles types such as `static[T] and Foo`, # which are subset of `static[T]`, hence they could @@ -1159,7 +1187,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # compiler only checks for 'nil': if skipTypes(r, {tyGenericInst, tyAlias, tySink}).kind != tyVoid: if kind notin {skMacro, skTemplate} and r.kind in {tyStmt, tyExpr}: - localError(c.config, n.sons[0].info, "return type '" & typeToString(r) & + localError(c.config, n.sons[0].info, "return type '" & typeToString(r) & "' is only valid for macros and templates") # 'auto' as a return type does not imply a generic: elif r.kind == tyAnything: @@ -1577,11 +1605,16 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = assert s != nil assert prev == nil result = copyType(s, s.owner, keepId=false) - # XXX figure out why this has children already... + # Remove the 'T' parameter from tySequence: result.sons.setLen 0 result.n = nil result.flags = {tfHasAsgn} semContainerArg(c, n, "seq", result) + if result.len > 0: + var base = result[0] + if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base) + if base.kind != tyGenericParam: + c.typesWithOps.add((result, result)) else: result = semContainer(c, n, tySequence, "seq", prev) if c.config.selectedGc == gcDestructors: @@ -1678,7 +1711,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = else: if s.kind != skError: localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) - of nkObjectTy: result = semObjectNode(c, n, prev) + of nkObjectTy: result = semObjectNode(c, n, prev, isInheritable=false) of nkTupleTy: result = semTuple(c, n, prev) of nkTupleClassTy: result = newConstraint(c, tyTuple) of nkTypeClassTy: result = semTypeClass(c, n, prev) @@ -1714,11 +1747,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyError, prev, c) n.typ = result dec c.inTypeContext - if c.inTypeContext == 0: instAllTypeBoundOp(c, n.info) - -when false: - proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = - result = semTypeNodeInner(c, n, prev) + if c.inTypeContext == 0: + #if $n == "var seq[StackTraceEntry]": + # echo "begin ", n instAllTypeBoundOp(c, n.info) proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) = diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 027ffd4aa857..ceabd8e60dee 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -297,16 +297,9 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType = #result.destructor = nil result.sink = nil -template typeBound(c, newty, oldty, field, info) = - let opr = newty.field - if opr != nil and sfFromGeneric notin opr.flags: - # '=' needs to be instantiated for generics when the type is constructed: - newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1) - proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # tyGenericInvocation[A, tyGenericInvocation[A, B]] # is difficult to handle: - const eqFlags = eqTypeFlags + {tfGcSafe} var body = t.sons[0] if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body") @@ -317,7 +310,10 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = else: result = searchInstTypes(t) - if result != nil and eqFlags*result.flags == eqFlags*t.flags: return + if result != nil and sameFlags(result, t): + when defined(reportCacheHits): + echo "Generic instantiation cached ", typeToString(result), " for ", typeToString(t) + return for i in countup(1, sonsLen(t) - 1): var x = t.sons[i] if x.kind in {tyGenericParam}: @@ -332,7 +328,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = if header != t: # search again after first pass: result = searchInstTypes(header) - if result != nil and eqFlags*result.flags == eqFlags*t.flags: return + if result != nil and sameFlags(result, t): + when defined(reportCacheHits): + echo "Generic instantiation cached ", typeToString(result), " for ", + typeToString(t), " header ", typeToString(header) + return else: header = instCopyType(cl, t) @@ -384,7 +384,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = rawAddSon(result, newbody) checkPartialConstructedType(cl.c.config, cl.info, newbody) let dc = newbody.deepCopy - if cl.allowMetaTypes == false: + if not cl.allowMetaTypes: if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: # 'deepCopy' needs to be instantiated for # generics *when the type is constructed*: @@ -402,6 +402,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = discard else: newbody.lastSon.typeInst = result + # DESTROY: adding object|opt for opt[topttree.Tree] + # sigmatch: Formal opt[=destroy.T] real opt[topttree.Tree] + # adding myseq for myseq[system.int] + # sigmatch: Formal myseq[=destroy.T] real myseq[system.int] + #echo "DESTROY: adding ", typeToString(newbody), " for ", typeToString(result, preferDesc) cl.c.typesWithOps.add((newbody, result)) let mm = skipTypes(bbody, abstractPtrs) if tfFromGeneric notin mm.flags: @@ -432,7 +437,7 @@ proc eraseVoidParams*(t: PType) = inc pos setLen t.sons, pos setLen t.n.sons, pos - return + break proc skipIntLiteralParams*(t: PType) = for i in 0 ..< t.sonsLen: @@ -561,9 +566,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = for i in countup(0, sonsLen(result) - 1): if result.sons[i] != nil: if result.sons[i].kind == tyGenericBody: - localError( - cl.c.config, - t.sym.info, + localError(cl.c.config, t.sym.info, "cannot instantiate '" & typeToString(result.sons[i], preferDesc) & "' inside of type definition: '" & @@ -603,6 +606,13 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result.size = -1 result.n = replaceObjBranches(cl, result.n) +template typeBound(c, newty, oldty, field, info) = + let opr = newty.field + if opr != nil and sfFromGeneric notin opr.flags: + # '=' needs to be instantiated for generics when the type is constructed: + #echo "DESTROY: instantiating ", astToStr(field), " for ", typeToString(oldty) + newty.field = c.instTypeBoundOp(c, opr, oldty, info, attachedAsgn, 1) + proc instAllTypeBoundOp*(c: PContext, info: TLineInfo) = var i = 0 while i < c.typesWithOps.len: diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 218011b1d60a..3096d94a0c17 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -196,18 +196,23 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = else: c.hashSym(t.sym) if {sfAnon, sfGenSym} * t.sym.flags != {}: - # generated object names can be identical, so we need to - # disambiguate furthermore by hashing the field types and names: - # mild hack to prevent endless recursions (makes nimforum compile again): - let oldFlags = t.sym.flags - t.sym.flags = t.sym.flags - {sfAnon, sfGenSym} - let n = t.n - for i in 0 ..< n.len: - assert n[i].kind == nkSym - let s = n[i].sym - c.hashSym s - c.hashType s.typ, flags - t.sym.flags = oldFlags + # Generated object names can be identical, so we need to + # disambiguate furthermore by hashing the field types and names. + if t.n.len > 0: + let oldFlags = t.sym.flags + # Mild hack to prevent endless recursion. + t.sym.flags = t.sym.flags - {sfAnon, sfGenSym} + for n in t.n: + assert(n.kind == nkSym) + let s = n.sym + c.hashSym s + c.hashType s.typ, flags + t.sym.flags = oldFlags + else: + # The object has no fields: we _must_ add something here in order to + # make the hash different from the one we produce by hashing only the + # type name. + c &= ".empty" else: c &= t.id if t.len > 0 and t.sons[0] != nil: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index fa4ab3703f12..3eaac06e5105 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2505,6 +2505,11 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; if f.kind in {tyRef, tyPtr}: f = f.lastSon else: if f.kind == tyVar: f = f.lastSon + #if c.config.selectedGC == gcDestructors and f.kind == tySequence: + # use the canonical type to access the =sink and =destroy etc. + # f = c.graph.sysTypes[tySequence] + #echo "YUP_---------Formal ", typeToString(f, preferDesc), " real ", typeToString(t, preferDesc), " ", f.id, " ", t.id + if typeRel(m, f, t) == isNone: localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") else: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 09eacbbede3f..d501acd4667e 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -32,7 +32,7 @@ # included from sigmatch.nim -import algorithm, prefixmatches, lineinfos, pathutils +import algorithm, prefixmatches, lineinfos, pathutils, parseutils from wordrecg import wDeprecated, wError, wAddr, wYield, specialWords when defined(nimsuggest): @@ -81,6 +81,38 @@ proc cmpSuggestions(a, b: Suggest): int = # independent of hashing order: result = cmp(a.name[], b.name[]) +proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int = + let + line = sourceLine(conf, info) + column = toColumn(info) + + proc isOpeningBacktick(col: int): bool = + if col >= 0 and col < line.len: + if line[col] == '`': + not isOpeningBacktick(col - 1) + else: + isOpeningBacktick(col - 1) + else: + false + + if column > line.len: + result = 0 + elif column > 0 and line[column - 1] == '`' and isOpeningBacktick(column - 1): + result = skipUntil(line, '`', column) + if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0: + result = 0 + elif ident[0] in linter.Letters and ident[^1] != '=': + result = identLen(line, column) + if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0: + result = 0 + else: + result = skipWhile(line, OpChars + {'[', '(', '{', ']', ')', '}'}, column) + if ident[^1] == '=' and ident[0] in linter.Letters: + if line[column..column + result - 1] != "=": + result = 0 + elif line[column..column + result - 1] != ident: + result = 0 + proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; quality: range[0..100]; prefix: PrefixMatch; inTypeContext: bool; scope: int): Suggest = @@ -88,7 +120,6 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info result.section = section result.quality = quality result.isGlobal = sfGlobal in s.flags - result.tokenLen = s.name.s.len result.prefix = prefix result.contextFits = inTypeContext == (s.kind in {skType, skGenericParam}) result.scope = scope @@ -126,6 +157,10 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info result.line = toLinenumber(infox) result.column = toColumn(infox) result.version = conf.suggestVersion + result.tokenLen = if section != ideHighlight: + s.name.s.len + else: + getTokenLenFromSource(conf, s.name.s, infox) proc `$`*(suggest: Suggest): string = result = $suggest.section @@ -460,7 +495,7 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; proc extractPragma(s: PSym): PNode = if s.kind in routineKinds: result = s.ast[pragmasPos] - elif s.kind in {skType, skVar, skLet}: + elif s.kind in {skType, skVar, skLet} and s.ast[0].kind == nkPragmaExpr: # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma] result = s.ast[0][1] doAssert result == nil or result.kind == nkPragma diff --git a/compiler/transf.nim b/compiler/transf.nim index 82be4158ff86..071cb00ee788 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -321,6 +321,38 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PTransNode = for i in countup(0, sonsLen(n)-1): result[i] = introduceNewLocalVars(c, n.sons[i]) +proc transformAsgn(c: PTransf, n: PNode): PTransNode = + let rhs = n[1] + + if rhs.kind != nkTupleConstr: + return transformSons(c, n) + + # Unpack the tuple assignment into N temporary variables and then pack them + # into a tuple: this allows us to get the correct results even when the rhs + # depends on the value of the lhs + let letSection = newTransNode(nkLetSection, n.info, rhs.len) + let newTupleConstr = newTransNode(nkTupleConstr, n.info, rhs.len) + for i, field in rhs: + let val = if field.kind == nkExprColonExpr: field[1] else: field + let def = newTransNode(nkIdentDefs, field.info, 3) + def[0] = PTransNode(newTemp(c, val.typ, field.info)) + def[1] = PTransNode(newNodeI(nkEmpty, field.info)) + def[2] = transform(c, val) + letSection[i] = def + # NOTE: We assume the constructor fields are in the correct order for the + # given tuple type + newTupleConstr[i] = def[0] + + PNode(newTupleConstr).typ = rhs.typ + + let asgnNode = newTransNode(nkAsgn, n.info, 2) + asgnNode[0] = transform(c, n[0]) + asgnNode[1] = newTupleConstr + + result = newTransNode(nkStmtList, n.info, 2) + result[0] = letSection + result[1] = asgnNode + proc transformYield(c: PTransf, n: PNode): PTransNode = proc asgnTo(lhs: PNode, rhs: PTransNode): PTransNode = # Choose the right assignment instruction according to the given ``lhs`` @@ -914,6 +946,7 @@ proc transform(c: PTransf, n: PNode): PTransNode = let hoisted = hoistParamsUsedInDefault(c, call, hoistedParams, call[i]) if hoisted != nil: call[i] = hoisted result = newTree(nkStmtListExpr, hoistedParams, call).PTransNode + PNode(result).typ = call.typ of nkAddr, nkHiddenAddr: result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref) of nkDerefExpr, nkHiddenDeref: @@ -948,6 +981,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformYield(c, n) else: result = transformSons(c, n) + #of nkAsgn: + # result = transformAsgn(c, n) of nkIdentDefs, nkConstDef: result = PTransNode(n) result[0] = transform(c, n[0]) diff --git a/compiler/types.nim b/compiler/types.nim index 1d6e71f14368..7034917d410c 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -124,7 +124,7 @@ proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferNa add(result, ')') if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ, prefer)) - result.add "[declared in " + result.add " [declared in " result.add(conf$sym.info) result.add "]" @@ -588,7 +588,15 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if prefer != preferExported: result.add("(" & typeToString(t.sons[0]) & ")") of tyProc: - result = if tfIterator in t.flags: "iterator " else: "proc " + result = if tfIterator in t.flags: "iterator " + elif t.owner != nil: + case t.owner.kind + of skTemplate: "template " + of skMacro: "macro " + of skConverter: "converter " + else: "proc " + else: + "proc " if tfUnresolved in t.flags: result.add "[*missing parameters*]" result.add "(" for i in countup(1, sonsLen(t) - 1): diff --git a/compiler/vm.nim b/compiler/vm.nim index e95a491fd12e..74f2a367dc85 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -11,6 +11,7 @@ ## An instruction is 1-3 int32s in memory, it is a register based VM. import ast except getstr +import system/indexerrors import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, @@ -23,7 +24,7 @@ from evaltempl import evalTemplate from modulegraphs import ModuleGraph, PPassContext const - traceCode = debugEchoCode + traceCode = defined(nimVMDebug) when hasFFI: import evalffi @@ -259,64 +260,101 @@ proc pushSafePoint(f: PStackFrame; pc: int) = f.safePoints.add(pc) proc popSafePoint(f: PStackFrame) = - # XXX this needs a proper fix! - if f.safePoints.len > 0: - discard f.safePoints.pop() - -proc cleanUpOnException(c: PCtx; tos: PStackFrame): - tuple[pc: int, f: PStackFrame] = - let raisedType = c.currentExceptionA.typ.skipTypes(abstractPtrs) - var f = tos - while true: - while f.safePoints.len == 0: - f = f.next - if f.isNil: return (-1, nil) - var pc2 = f.safePoints[f.safePoints.high] - - var nextExceptOrFinally = -1 - if c.code[pc2].opcode == opcExcept: - nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess - inc pc2 - while c.code[pc2].opcode == opcExcept: - let excIndex = c.code[pc2].regBx-wordExcess - let exceptType = if excIndex > 0: c.types[excIndex].skipTypes( - abstractPtrs) - else: nil - #echo typeToString(exceptType), " ", typeToString(raisedType) - if exceptType.isNil or inheritanceDiff(raisedType, exceptType) <= 0: - # mark exception as handled but keep it in B for - # the getCurrentException() builtin: - c.currentExceptionB = c.currentExceptionA - c.currentExceptionA = nil - # execute the corresponding handler: - while c.code[pc2].opcode == opcExcept: inc pc2 - discard f.safePoints.pop - return (pc2, f) - inc pc2 - if c.code[pc2].opcode != opcExcept and nextExceptOrFinally >= 0: - # we're at the end of the *except list*, but maybe there is another - # *except branch*? - pc2 = nextExceptOrFinally+1 - if c.code[pc2].opcode == opcExcept: - nextExceptOrFinally = pc2 + c.code[pc2].regBx - wordExcess - - if nextExceptOrFinally >= 0: - pc2 = nextExceptOrFinally - if c.code[pc2].opcode == opcFinally: - # execute the corresponding handler, but don't quit walking the stack: - discard f.safePoints.pop - return (pc2+1, f) - # not the right one: - discard f.safePoints.pop + discard f.safePoints.pop() + +type + ExceptionGoto = enum + ExceptionGotoHandler, + ExceptionGotoFinally, + ExceptionGotoUnhandled + +proc findExceptionHandler(c: PCtx, f: PStackFrame, exc: PNode): + tuple[why: ExceptionGoto, where: int] = + let raisedType = exc.typ.skipTypes(abstractPtrs) + + while f.safePoints.len > 0: + var pc = f.safePoints.pop() + + var matched = false + var pcEndExcept = pc + + # Scan the chain of exceptions starting at pc. + # The structure is the following: + # pc - opcExcept, + # - opcExcept, + # - opcExcept, + # ... + # - opcExcept, + # - Exception handler body + # - ... more opcExcept blocks may follow + # - ... an optional opcFinally block may follow + # + # Note that the exception handler body already contains a jump to the + # finally block or, if that's not present, to the point where the execution + # should continue. + # Also note that opcFinally blocks are the last in the chain. + while c.code[pc].opcode == opcExcept: + # Where this Except block ends + pcEndExcept = pc + c.code[pc].regBx - wordExcess + inc pc + + # A series of opcExcept follows for each exception type matched + while c.code[pc].opcode == opcExcept: + let excIndex = c.code[pc].regBx - wordExcess + let exceptType = + if excIndex > 0: c.types[excIndex].skipTypes(abstractPtrs) + else: nil + + # echo typeToString(exceptType), " ", typeToString(raisedType) + + # Determine if the exception type matches the pattern + if exceptType.isNil or inheritanceDiff(raisedType, exceptType) <= 0: + matched = true + break + + inc pc + + # Skip any further ``except`` pattern and find the first instruction of + # the handler body + while c.code[pc].opcode == opcExcept: + inc pc + + if matched: + break + + # If no handler in this chain is able to catch this exception we check if + # the "parent" chains are able to. If this chain ends with a `finally` + # block we must execute it before continuing. + pc = pcEndExcept + + # Where the handler body starts + let pcBody = pc + + if matched: + return (ExceptionGotoHandler, pcBody) + elif c.code[pc].opcode == opcFinally: + # The +1 here is here because we don't want to execute it since we've + # already pop'd this statepoint from the stack. + return (ExceptionGotoFinally, pc + 1) + + return (ExceptionGotoUnhandled, 0) proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = - for s in f.safePoints: - var pc = s + # Walk up the chain of safepoints and return the PC of the first `finally` + # block we find or -1 if no such block is found. + # Note that the safepoint is removed once the function returns! + result = -1 + + # Traverse the stack starting from the end in order to execute the blocks in + # the inteded order + for i in 1 .. f.safePoints.len: + var pc = f.safePoints[^i] + # Skip the `except` blocks while c.code[pc].opcode == opcExcept: - pc = pc + c.code[pc].regBx - wordExcess + pc += c.code[pc].regBx - wordExcess if c.code[pc].opcode == opcFinally: - return pc - return -1 + discard f.safePoints.pop + return pc + 1 proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = if desttyp.kind == tyString: @@ -436,7 +474,6 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = node.sons[i] = getNullValue(typ.sons[0], info, c.config) const - errIndexOutOfBounds = "index out of bounds" errNilAccess = "attempt to access a nil address" errOverOrUnderflow = "over- or underflow" errConstantDivisionByZero = "division by zero" @@ -449,6 +486,9 @@ const proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos + # Used to keep track of where the execution is resumed. + var savedPC = -1 + var savedFrame: PStackFrame var regs: seq[TFullReg] # alias to tos.slots for performance move(regs, tos.slots) #echo "NEW RUN ------------------------" @@ -456,27 +496,31 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = #{.computedGoto.} let instr = c.code[pc] let ra = instr.regA - #if c.traceActive: + when traceCode: echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra, " rb ", instr.regB, " rc ", instr.regC - # message(c.config, c.debug[pc], warnUser, "Trace") case instr.opcode of opcEof: return regs[ra] of opcRet: - # XXX perform any cleanup actions - pc = tos.comesFrom - tos = tos.next - let retVal = regs[0] - if tos.isNil: - #echo "RET ", retVal.rendertree - return retVal - - move(regs, tos.slots) - assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn} - if c.code[pc].opcode == opcIndCallAsgn: - regs[c.code[pc].regA] = retVal - #echo "RET2 ", retVal.rendertree, " ", c.code[pc].regA + let newPc = c.cleanUpOnReturn(tos) + # Perform any cleanup action before returning + if newPc < 0: + pc = tos.comesFrom + tos = tos.next + let retVal = regs[0] + if tos.isNil: + return retVal + + move(regs, tos.slots) + assert c.code[pc].opcode in {opcIndCall, opcIndCallAsgn} + if c.code[pc].opcode == opcIndCallAsgn: + regs[c.code[pc].regA] = retVal + else: + savedPC = pc + savedFrame = tos + # The -1 is needed because at the end of the loop we increment `pc` + pc = newPc - 1 of opcYldYoid: assert false of opcYldVal: assert false of opcAsgnInt: @@ -533,7 +577,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # a = b[c] decodeBC(rkNode) if regs[rc].intVal > high(int): - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int))) let idx = regs[rc].intVal.int let src = regs[rb].node if src.kind in {nkStrLit..nkTripleStrLit}: @@ -541,11 +585,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node = newNodeI(nkCharLit, c.debug[pc]) regs[ra].node.intVal = src.strVal[idx].ord else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.strVal.len-1)) elif src.kind notin {nkEmpty..nkFloat128Lit} and idx <% src.len: regs[ra].node = src.sons[idx] else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1)) of opcLdStrIdx: decodeBC(rkInt) let idx = regs[rc].intVal.int @@ -555,7 +599,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = elif idx == s.len and optLaxStrings in c.config.options: regs[ra].intVal = 0 else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, s.len-1)) of opcWrArr: # a[b] = c decodeBC(rkNode) @@ -565,11 +609,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if idx <% arr.strVal.len: arr.strVal[idx] = chr(regs[rc].intVal) else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, arr.strVal.len-1)) elif idx <% arr.len: writeField(arr.sons[idx], regs[rc]) else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, arr.len-1)) of opcLdObj: # a = b.c decodeBC(rkNode) @@ -600,7 +644,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if idx <% regs[ra].node.strVal.len: regs[ra].node.strVal[idx] = chr(regs[rc].intVal) else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, regs[ra].node.strVal.len-1)) of opcAddrReg: decodeB(rkRegisterAddr) regs[ra].regAddr = addr(regs[rb]) @@ -1025,7 +1069,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # it's a callback: c.callbacks[-prc.offset-2].value( VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs), - currentException: c.currentExceptionB, + currentException: c.currentExceptionA, currentLineInfo: c.debug[pc])) elif sfImportc in prc.flags: if allowFFI notin c.features: @@ -1118,44 +1162,55 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = tos.pushSafePoint(pc + rbx) assert c.code[pc+rbx].opcode in {opcExcept, opcFinally} of opcExcept: - # just skip it; it's followed by a jump; - # we'll execute in the 'raise' handler - let rbx = instr.regBx - wordExcess - 1 # -1 for the following 'inc pc' - inc pc, rbx - while c.code[pc+1].opcode == opcExcept: - let rbx = c.code[pc+1].regBx - wordExcess - 1 - inc pc, rbx - #assert c.code[pc+1].opcode in {opcExcept, opcFinally} - if c.code[pc+1].opcode != opcFinally: - # in an except handler there is no active safe point for the 'try': - tos.popSafePoint() + # This opcode is never executed, it only holds informations for the + # exception handling routines. + doAssert(false) of opcFinally: - # just skip it; it's followed by the code we need to execute anyway + # Pop the last safepoint introduced by a opcTry. This opcode is only + # executed _iff_ no exception was raised in the body of the `try` + # statement hence the need to pop the safepoint here. + doAssert(savedPC < 0) tos.popSafePoint() of opcFinallyEnd: - if c.currentExceptionA != nil: - # we are in a cleanup run: - let (newPc, newTos) = cleanUpOnException(c, tos) - if newPc-1 < 0: - bailOut(c, tos) - return - pc = newPc-1 - if tos != newTos: - tos = newTos + # The control flow may not resume at the next instruction since we may be + # raising an exception or performing a cleanup. + if not savedPC < 0: + pc = savedPC - 1 + savedPC = -1 + if tos != savedFrame: + tos = savedFrame move(regs, tos.slots) of opcRaise: let raised = regs[ra].node c.currentExceptionA = raised c.exceptionInstr = pc - let (newPc, newTos) = cleanUpOnException(c, tos) - # -1 because of the following 'inc' - if newPc-1 < 0: + + var frame = tos + var jumpTo = findExceptionHandler(c, frame, raised) + while jumpTo.why == ExceptionGotoUnhandled and not frame.next.isNil: + frame = frame.next + jumpTo = findExceptionHandler(c, frame, raised) + + case jumpTo.why: + of ExceptionGotoHandler: + # Jump to the handler, do nothing when the `finally` block ends. + savedPC = -1 + pc = jumpTo.where - 1 + if tos != frame: + tos = frame + move(regs, tos.slots) + of ExceptionGotoFinally: + # Jump to the `finally` block first then re-jump here to continue the + # traversal of the exception chain + savedPC = pc + savedFrame = tos + pc = jumpTo.where - 1 + if tos != frame: + tos = frame + move(regs, tos.slots) + of ExceptionGotoUnhandled: + # Nobody handled this exception, error out. bailOut(c, tos) - return - pc = newPc-1 - if tos != newTos: - tos = newTos - move(regs, tos.slots) of opcNew: ensureKind(rkNode) let typ = c.types[instr.regBx - wordExcess] @@ -1264,6 +1319,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNarrowU: decodeB(rkInt) regs[ra].intVal = regs[ra].intVal and ((1'i64 shl rb)-1) + of opcSignExtend: + # like opcNarrowS, but no out of range possible + decodeB(rkInt) + let imm = 64 - rb + regs[ra].intVal = ashr(regs[ra].intVal shl imm, imm) of opcIsNil: decodeB(rkInt) let node = regs[rb].node @@ -1290,7 +1350,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = idx = int(regs[rb+rc-1].intVal) callback = c.callbacks[idx].value args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs), - currentException: c.currentExceptionB, + currentException: c.currentExceptionA, currentLineInfo: c.debug[pc]) callback(args) regs[ra].node.flags.incl nfIsRef @@ -1301,7 +1361,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len: regs[ra].node = src.sons[idx] else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1)) of opcNSetChild: decodeBC(rkNode) let idx = regs[rb].intVal.int @@ -1309,7 +1369,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len: dest.sons[idx] = regs[rc].node else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, dest.len-1)) of opcNAdd: decodeBC(rkNode) var u = regs[rb].node @@ -1369,7 +1429,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNGetType: let rb = instr.regB let rc = instr.regC - case rc: + case rc of 0: # getType opcode: ensureKind(rkNode) @@ -1411,7 +1471,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = createStr regs[ra] let a = regs[rb].node case a.kind - of {nkStrLit..nkTripleStrLit}: + of nkStrLit..nkTripleStrLit: regs[ra].node.strVal = a.strVal of nkCommentStmt: regs[ra].node.strVal = a.comment @@ -1513,7 +1573,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var bStrVal: cstring = nil # extract strVal from argument ``a`` case aNode.kind - of {nkStrLit..nkTripleStrLit}: + of nkStrLit..nkTripleStrLit: aStrVal = aNode.strVal.cstring of nkIdent: aStrVal = aNode.ident.s.cstring @@ -1525,7 +1585,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = discard # extract strVal from argument ``b`` case bNode.kind - of {nkStrLit..nkTripleStrLit}: + of nkStrLit..nkTripleStrLit: bStrVal = bNode.strVal.cstring of nkIdent: bStrVal = bNode.ident.s.cstring @@ -1538,7 +1598,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # set result regs[ra].intVal = if aStrVal != nil and bStrVal != nil: - ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0) + ord(idents.cmpIgnoreStyle(aStrVal, bStrVal, high(int)) == 0) else: 0 @@ -1714,7 +1774,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len: regs[ra].node = g.cacheSeqs[destKey][idx.int] else: - stackTrace(c, tos, pc, errIndexOutOfBounds) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, g.cacheSeqs[destKey].len-1)) of opcNctPut: let g = c.graph let destKey = regs[ra].node.strVal @@ -1888,6 +1948,7 @@ const evalPass* = makePass(myOpen, myProcess, myClose) proc evalConstExprAux(module: PSym; g: ModuleGraph; prc: PSym, n: PNode, mode: TEvalMode): PNode = + if g.config.errorCounter > 0: return n let n = transformExpr(g, module, n, noDestructors = true) setupGlobalCtx(module, g) var c = PCtx g.vm diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index a43f8dbba52a..58158a7cc9b1 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -75,6 +75,7 @@ type opcSubStr, opcParseFloat, opcConv, opcCast, opcQuit, opcNarrowS, opcNarrowU, + opcSignExtend, opcAddStrCh, opcAddStrStr, diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index f160a30967c3..edbed5ca1609 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -297,14 +297,14 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyOptAsRef: assert(false, "mapTypeToAstX") proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(cache, t, info, false, true) + result = mapTypeToAstX(cache, t, info, inst=false, allowRecursionX=true) # the "Inst" version includes generic parameters in the resulting type tree # and also tries to look like the corresponding Nim type declaration proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(cache, t, info, true, false) + result = mapTypeToAstX(cache, t, info, inst=true, allowRecursionX=false) # the "Impl" version includes generic parameters in the resulting type tree # and also tries to look like the corresponding Nim type implementation proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(cache, t, info, true, true) + result = mapTypeToAstX(cache, t, info, inst=true, allowRecursionX=true) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index f87821da4fde..980901593767 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -83,9 +83,12 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = elif opc < firstABxInstr: result.addf("\t$#\tr$#, r$#, r$#", opc.toStr, x.regA, x.regB, x.regC) - elif opc in relativeJumps: + elif opc in relativeJumps + {opcTry}: result.addf("\t$#\tr$#, L$#", opc.toStr, x.regA, i+x.regBx-wordExcess) + elif opc in {opcExcept}: + let idx = x.regBx-wordExcess + result.addf("\t$#\t$#, $#", opc.toStr, x.regA, $idx) elif opc in {opcLdConst, opcAsgnConst}: let idx = x.regBx-wordExcess result.addf("\t$#\tr$#, $# ($#)", opc.toStr, x.regA, @@ -480,10 +483,13 @@ proc genType(c: PCtx; typ: PType): int = proc genTry(c: PCtx; n: PNode; dest: var TDest) = if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) var endings: seq[TPosition] = @[] - let elsePos = c.xjmp(n, opcTry, 0) + let ehPos = c.xjmp(n, opcTry, 0) c.gen(n.sons[0], dest) c.clearDest(n, dest) - c.patch(elsePos) + # Add a jump past the exception handling code + endings.add(c.xjmp(n, opcJmp, 0)) + # This signals where the body ends and where the exception handling begins + c.patch(ehPos) for i in 1 ..< n.len: let it = n.sons[i] if it.kind != nkFinally: @@ -499,14 +505,14 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = c.gABx(it, opcExcept, 0, 0) c.gen(it.lastSon, dest) c.clearDest(n, dest) - if i < sonsLen(n)-1: + if i < sonsLen(n): endings.add(c.xjmp(it, opcJmp, 0)) c.patch(endExcept) - for endPos in endings: c.patch(endPos) let fin = lastSon(n) # we always generate an 'opcFinally' as that pops the safepoint - # from the stack + # from the stack if no exception is raised in the body. c.gABx(fin, opcFinally, 0, 0) + for endPos in endings: c.patch(endPos) if fin.kind == nkFinally: c.gen(fin.sons[0]) c.clearDest(n, dest) @@ -986,11 +992,18 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(tmp) c.freeTemp(tmp2) - of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt) - of mAshrI: genBinaryABCnarrow(c, n, dest, opcAshrInt) - of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt) - of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt) - of mBitxorI: genBinaryABCnarrowU(c, n, dest, opcBitxorInt) + of mShlI: + genBinaryABC(c, n, dest, opcShlInt) + # genNarrowU modified + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): + c.gABC(n, opcSignExtend, dest, TRegister(t.size*8)) + of mAshrI: genBinaryABC(c, n, dest, opcAshrInt) + of mBitandI: genBinaryABC(c, n, dest, opcBitandInt) + of mBitorI: genBinaryABC(c, n, dest, opcBitorInt) + of mBitxorI: genBinaryABC(c, n, dest, opcBitxorInt) of mAddU: genBinaryABCnarrowU(c, n, dest, opcAddu) of mSubU: genBinaryABCnarrowU(c, n, dest, opcSubu) of mMulU: genBinaryABCnarrowU(c, n, dest, opcMulu) @@ -1009,7 +1022,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mLtPtr, mLtU, mLtU64: genBinaryABC(c, n, dest, opcLtu) of mEqProc, mEqRef, mEqUntracedRef: genBinaryABC(c, n, dest, opcEqRef) - of mXor: genBinaryABCnarrowU(c, n, dest, opcXor) + of mXor: genBinaryABC(c, n, dest, opcXor) of mNot: genUnaryABC(c, n, dest, opcNot) of mUnaryMinusI, mUnaryMinusI64: genUnaryABC(c, n, dest, opcUnaryMinusInt) @@ -1018,7 +1031,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mUnaryPlusI, mUnaryPlusF64: gen(c, n.sons[1], dest) of mBitnotI: genUnaryABC(c, n, dest, opcBitnotInt) - genNarrowU(c, n, dest) + #genNarrowU modified, do not narrow signed types + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) of mToFloat, mToBiggestFloat, mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: @@ -2204,9 +2220,9 @@ proc genProc(c: PCtx; s: PSym): int = c.gABC(body, opcEof, eofInstr.regA) c.optimizeJumps(result) s.offset = c.prc.maxSlots - #if s.name.s == "calc": - # echo renderTree(body) - # c.echoCode(result) + # if s.name.s == "fun1": + # echo renderTree(body) + # c.echoCode(result) c.prc = oldPrc else: c.prc.maxSlots = s.offset diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 56c97dec6035..4f9c7cf89452 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -62,6 +62,9 @@ template wrap2svoid(op, modop) {.dirty.} = op(getString(a, 0), getString(a, 1)) modop op +template ioop(op) {.dirty.} = + registerCallback(c, "stdlib.io." & astToStr(op), `op Wrapper`) + proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = setResult(a, if a.currentException.isNil: "" else: a.currentException.sons[3].skipColon.strVal) @@ -113,8 +116,8 @@ proc registerAdditionalOps*(c: PCtx) = wrap2svoid(putEnv, osop) wrap1s(dirExists, osop) wrap1s(fileExists, osop) - wrap2svoid(writeFile, systemop) - wrap1s(readFile, systemop) + wrap2svoid(writeFile, ioop) + wrap1s(readFile, ioop) systemop getCurrentExceptionMsg registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 41bdc9fcbc2a..6f78c9d6fbf7 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -35,7 +35,7 @@ type wColon, wColonColon, wEquals, wDot, wDotDot, wStar, wMinus, wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks, - wIntDefine, wStrDefine, + wIntDefine, wStrDefine, wBoolDefine wDestroy, @@ -122,7 +122,8 @@ const ":", "::", "=", ".", "..", "*", "-", - "magic", "thread", "final", "profiler", "memtracker", "objchecks", "intdefine", "strdefine", + "magic", "thread", "final", "profiler", "memtracker", "objchecks", + "intdefine", "strdefine", "booldefine", "destroy", diff --git a/config/nim.cfg b/config/nim.cfg index 2a118c5cf3bb..c3e0823b6561 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -290,13 +290,16 @@ tcc.options.always = "-w" # Configuration for the Genode toolchain @if genode: + noCppExceptions # avoid std C++ + tlsEmulation:on # no TLS segment register magic gcc.path = "/usr/local/genode-gcc/bin" - gcc.cpp.options.always = "-D__GENODE__ -fno-stack-protector" @if i386 or amd64: gcc.exe = "genode-x86-gcc" gcc.cpp.exe = "genode-x86-g++" + gcc.cpp.linkerexe = "genode-x86-ld" @elif arm: gcc.exe = "genode-arm-gcc" gcc.cpp.exe = "genode-arm-g++" + gcc.cpp.linkerexe = "genode-arm-ld" @end @end diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index f534264a0a6a..c5effd04e7ee 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -77,7 +77,20 @@ $content doc.body_toc = """
-
+ +
Search:
@@ -100,7 +113,7 @@ doc.body_toc_group = """