mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 10:53:40 +00:00
Compare commits
224 Commits
pr_distinc
...
v0.20.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88a0edba4b | ||
|
|
5a12553394 | ||
|
|
7469f5fa78 | ||
|
|
6bd301ca9c | ||
|
|
0047f7cf4f | ||
|
|
8be734bf46 | ||
|
|
9b0cb52d74 | ||
|
|
1aefa74e46 | ||
|
|
c0085bc2b7 | ||
|
|
43cba52ed8 | ||
|
|
d6d5dd30e9 | ||
|
|
073651237a | ||
|
|
f23ca7f539 | ||
|
|
2bf1985f1a | ||
|
|
abebc395d3 | ||
|
|
ac41aec348 | ||
|
|
575f688545 | ||
|
|
95fd0d0a6f | ||
|
|
c4cbc5f449 | ||
|
|
d5d661b70e | ||
|
|
46cb304770 | ||
|
|
b4742cc496 | ||
|
|
abd4330dd0 | ||
|
|
876d0aaade | ||
|
|
8b6ea5d451 | ||
|
|
4771b08ab5 | ||
|
|
a59efc53f2 | ||
|
|
ae04734031 | ||
|
|
75f0a7a7af | ||
|
|
d91b4b1bb7 | ||
|
|
5a11ae85b1 | ||
|
|
8eb66f6c6b | ||
|
|
6f214131a2 | ||
|
|
d2f9a40b3c | ||
|
|
622eadff52 | ||
|
|
93b58ebe1b | ||
|
|
f7b061f597 | ||
|
|
e34a318b9f | ||
|
|
641d1e1175 | ||
|
|
07505d87e2 | ||
|
|
6ffe82a5aa | ||
|
|
2b4a7ff6f6 | ||
|
|
3d33e96388 | ||
|
|
19225b71ab | ||
|
|
e5e2b067f4 | ||
|
|
3866e3302a | ||
|
|
607ce16acf | ||
|
|
694d57c899 | ||
|
|
ed627cff3a | ||
|
|
8a5860d196 | ||
|
|
b5ab4dabe2 | ||
|
|
b3b10ec849 | ||
|
|
6ec566cdd8 | ||
|
|
c4e26f7400 | ||
|
|
ab98141c75 | ||
|
|
14c6e3a6ab | ||
|
|
808746e864 | ||
|
|
386f126dd7 | ||
|
|
ce9eaee303 | ||
|
|
7c7bab0626 | ||
|
|
da2a149522 | ||
|
|
8afe92a1ab | ||
|
|
2a0c9dc063 | ||
|
|
e0b4d034bc | ||
|
|
12baecb42d | ||
|
|
a144bc96b9 | ||
|
|
e0991c4bcf | ||
|
|
40c2f504fb | ||
|
|
80e51bb407 | ||
|
|
9b217c10e6 | ||
|
|
78b665cc7f | ||
|
|
092154de02 | ||
|
|
0646d6a720 | ||
|
|
585a08548a | ||
|
|
f77bc2522f | ||
|
|
5cc49aad44 | ||
|
|
e3c6f4d21b | ||
|
|
615be176e6 | ||
|
|
5dfec61597 | ||
|
|
df32d891a9 | ||
|
|
c96e6682a0 | ||
|
|
753f8a05d2 | ||
|
|
00ccdec238 | ||
|
|
49de021b1a | ||
|
|
5ecfbb5a4a | ||
|
|
1b10f926c8 | ||
|
|
a31b3ef3cf | ||
|
|
8501971a60 | ||
|
|
5863461d47 | ||
|
|
cb3a920097 | ||
|
|
88896194bd | ||
|
|
5689003410 | ||
|
|
789c8e0065 | ||
|
|
e74e85a41e | ||
|
|
2ee6d209c2 | ||
|
|
3e8c4519d4 | ||
|
|
cbdba3ceb3 | ||
|
|
a9322ed873 | ||
|
|
66660eca32 | ||
|
|
d13c75eb2f | ||
|
|
f70a0c90f1 | ||
|
|
6a23772e23 | ||
|
|
da3a7b852d | ||
|
|
7ab44997aa | ||
|
|
b94e7b65a0 | ||
|
|
20f6acc0da | ||
|
|
b0e86d0f05 | ||
|
|
6a6a9423e4 | ||
|
|
191b7e0bde | ||
|
|
1e48169bbc | ||
|
|
bd35d44808 | ||
|
|
d1de6b9fe4 | ||
|
|
466d5ba060 | ||
|
|
7938947907 | ||
|
|
476b3b1c13 | ||
|
|
59fbd261a6 | ||
|
|
eb295bbb7d | ||
|
|
e6c963259c | ||
|
|
c454cdac49 | ||
|
|
09d43c1d8f | ||
|
|
281baadb33 | ||
|
|
d7ea8588c5 | ||
|
|
10fa252426 | ||
|
|
b228b741a3 | ||
|
|
c2d0e0f7d5 | ||
|
|
95df6af527 | ||
|
|
199aa1f3a1 | ||
|
|
57df2c9c3a | ||
|
|
17cac707d0 | ||
|
|
970c7616bc | ||
|
|
1503970c3b | ||
|
|
922a4ed195 | ||
|
|
c39a799aec | ||
|
|
62691c5d73 | ||
|
|
ab5bd9e6ff | ||
|
|
3c3f07cfde | ||
|
|
843ae0073b | ||
|
|
d206e881e9 | ||
|
|
712d9ce1b9 | ||
|
|
ba6eb0151c | ||
|
|
28708e0b8e | ||
|
|
e08cf5a947 | ||
|
|
38aa7f6d20 | ||
|
|
763357089e | ||
|
|
6576c1ab5c | ||
|
|
8a16fb972b | ||
|
|
f1b2182035 | ||
|
|
50358e6d4a | ||
|
|
48770b04bd | ||
|
|
7e702d95c1 | ||
|
|
8396ddb3a4 | ||
|
|
8ab0be4530 | ||
|
|
30d7cf02ad | ||
|
|
98b9ffa3a4 | ||
|
|
a5a62b562f | ||
|
|
06904a212a | ||
|
|
a74104eda5 | ||
|
|
f8087a2815 | ||
|
|
6a7b490046 | ||
|
|
ceafb7f771 | ||
|
|
433dfaf288 | ||
|
|
4d18ee498a | ||
|
|
ef018d7a63 | ||
|
|
2a36cb7fc9 | ||
|
|
810166ca95 | ||
|
|
10407fefec | ||
|
|
d5ea497cff | ||
|
|
c6ab69b84e | ||
|
|
edc151fa64 | ||
|
|
6d977e66cb | ||
|
|
f3ae2dcfa3 | ||
|
|
cd49bba7b3 | ||
|
|
194eebd4b8 | ||
|
|
b41d31e887 | ||
|
|
cb4bb4d6c7 | ||
|
|
cd35f9664c | ||
|
|
0ba1624325 | ||
|
|
6ad45a1c97 | ||
|
|
5cf9cbe9a5 | ||
|
|
9789146899 | ||
|
|
905fc43abe | ||
|
|
aacc97e854 | ||
|
|
e0b41736b3 | ||
|
|
8d46524b1b | ||
|
|
3aa034f40d | ||
|
|
eb9ac127b9 | ||
|
|
22c515b067 | ||
|
|
4c7dec34d5 | ||
|
|
81f15184e5 | ||
|
|
368e12ed36 | ||
|
|
c57485e67b | ||
|
|
2d606e38ea | ||
|
|
c1097ea949 | ||
|
|
4144893ba2 | ||
|
|
dacd1d071a | ||
|
|
1348cb525b | ||
|
|
77166176b2 | ||
|
|
e15ce6bdeb | ||
|
|
464142683f | ||
|
|
7095a3dfa4 | ||
|
|
7e235779f0 | ||
|
|
b68ee27dd5 | ||
|
|
63116d94ae | ||
|
|
01fcde2146 | ||
|
|
ea4d72a379 | ||
|
|
78c6f01b90 | ||
|
|
41e0fc593c | ||
|
|
21910d6eb1 | ||
|
|
7bfde5fa40 | ||
|
|
9e28b3ccc9 | ||
|
|
b4464f1dfa | ||
|
|
4f69f7b62d | ||
|
|
6530a4100a | ||
|
|
9797da3615 | ||
|
|
0af9085e78 | ||
|
|
e1c29d7a19 | ||
|
|
32cecf3a42 | ||
|
|
05d647398a | ||
|
|
4c8a02165e | ||
|
|
7e6c3f3684 | ||
|
|
65a2b3bb6f | ||
|
|
8dfebb3282 | ||
|
|
de2c271293 | ||
|
|
022cd2c144 |
@@ -56,6 +56,7 @@ addons:
|
||||
- boehmgc
|
||||
- make
|
||||
- sfml
|
||||
update: true
|
||||
|
||||
install:
|
||||
- if [[ $CPU = i386 ]]; then echo -e "#!/bin/bash\n$(which gcc) -m32 \"\$@\"" > bin/gcc; fi
|
||||
|
||||
25
appveyor.yml
25
appveyor.yml
@@ -1,14 +1,11 @@
|
||||
version: '{build}'
|
||||
|
||||
environment:
|
||||
DLLS_URL: https://nim-lang.org/download/dlls.zip
|
||||
DLLS_ARCHIVE: dlls.zip
|
||||
MINGW_DIR: mingw64
|
||||
MINGW_URL: https://nim-lang.org/download/mingw64-6.3.0.7z
|
||||
MINGW_ARCHIVE: mingw64-6.3.0.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: false
|
||||
@@ -16,8 +13,7 @@ environment:
|
||||
|
||||
cache:
|
||||
- '%MINGW_ARCHIVE%'
|
||||
- '%SQLITE_ARCHIVE%'
|
||||
- '%OPENBLAS_ARCHIVE%'
|
||||
- '%DLLS_ARCHIVE%'
|
||||
|
||||
matrix:
|
||||
#allow_failures:
|
||||
@@ -26,23 +22,14 @@ matrix:
|
||||
|
||||
install:
|
||||
- ps: Install-Product node 8 # node 8 or later is required to test js async stuff
|
||||
- MKDIR %CD%\DIST
|
||||
- MKDIR %CD%\DIST\PCRE
|
||||
- nuget install pcre -Verbosity quiet -Version 8.33.0.1 -OutputDirectory %CD%\DIST\PCRE
|
||||
- IF not exist "%SQLITE_ARCHIVE%" appveyor DownloadFile "%SQLITE_URL%" -FileName "%SQLITE_ARCHIVE%"
|
||||
- 7z x -y "%SQLITE_ARCHIVE%" -o"%CD%\DIST"> nul
|
||||
- IF not exist "%DLLS_ARCHIVE%" appveyor DownloadFile "%DLLS_URL%" -FileName "%DLLS_ARCHIVE%"
|
||||
- 7z x -y "%DLLS_ARCHIVE%" -o"%CD%\BIN"> 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 )
|
||||
- IF "%PLATFORM%" == "x64" ( copy %CD%\DIST\sqlite3.dll %CD%\BIN\sqlite3_64.dll ) ELSE ( copy %CD%\DIST\sqlite3.dll %CD%\BIN\sqlite3_32.dll )
|
||||
- IF "%PLATFORM%" == "x64" ( copy %CD%\DIST\PCRE\pcre.redist.8.33.0.1\build\native\bin\v100\x64\Release\dynamic\utf8\pcre8.dll %CD%\bin\pcre64.dll ) ELSE ( copy %CD%\DIST\PCRE\pcre.redist.8.33.0.1\build\native\bin\v100\Win32\Release\dynamic\utf8\pcre8.dll %CD%\bin\pcre32.dll )
|
||||
- git clone --depth 1 https://github.com/nim-lang/csources
|
||||
- cd csources
|
||||
- IF "%PLATFORM%" == "x64" ( build64.bat ) else ( build.bat )
|
||||
- build64.bat
|
||||
- cd ..
|
||||
|
||||
build_script:
|
||||
|
||||
28
changelog.md
28
changelog.md
@@ -1,33 +1,37 @@
|
||||
## v0.20.2 - XXXX-XX-XX
|
||||
# v1.1 - XXXX-XX-XX
|
||||
|
||||
|
||||
### Changes affecting backwards compatibility
|
||||
## Changes affecting backwards compatibility
|
||||
|
||||
|
||||
#### Breaking changes in the standard library
|
||||
### Breaking changes in the standard library
|
||||
|
||||
|
||||
#### Breaking changes in the compiler
|
||||
### Breaking changes in the compiler
|
||||
|
||||
|
||||
### Library additions
|
||||
## Library additions
|
||||
|
||||
|
||||
### Library changes
|
||||
## Library changes
|
||||
|
||||
- Added `os.delEnv` and `nimscript.delEnv`. (#11466)
|
||||
|
||||
- Enable Oid usage in hashtables. (#11472)
|
||||
|
||||
#
|
||||
# Language additions
|
||||
|
||||
|
||||
|
||||
### Language additions
|
||||
|
||||
### Language changes
|
||||
## Language changes
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
|
||||
|
||||
### Compiler changes
|
||||
|
||||
- VM can now cast integer type arbitrarily. (#11459)
|
||||
|
||||
|
||||
### Bugfixes
|
||||
## Bugfixes
|
||||
|
||||
55
changelog_v020.md
Normal file
55
changelog_v020.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# v0.20.2 - XXXX-XX-XX
|
||||
|
||||
|
||||
## Changes affecting backwards compatibility
|
||||
|
||||
- All `strutils.rfind` procs now take `start` and `last` like `strutils.find`
|
||||
with the same data slice/index meaning. This is backwards compatible for
|
||||
calls *not* changing the `rfind` `start` parameter from its default. (#11487)
|
||||
|
||||
In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`,
|
||||
then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This
|
||||
should minimize gotchas porting code from other languages like Python or C++.)
|
||||
|
||||
- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch
|
||||
`-d:nimBinaryStdFiles` for a transition period.
|
||||
|
||||
### Breaking changes in the standard library
|
||||
|
||||
- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type
|
||||
conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)``
|
||||
need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``.
|
||||
|
||||
|
||||
### Breaking changes in the compiler
|
||||
|
||||
|
||||
## Library additions
|
||||
|
||||
- `toOpenArray` is now available for the JS target.
|
||||
|
||||
## Library changes
|
||||
|
||||
- Fix async IO operations stalling even after socket is closed. (#11232)
|
||||
|
||||
- More informative error message for `streams.openFileStream`. (#11438)
|
||||
|
||||
|
||||
## Language additions
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
|
||||
### Tool changes
|
||||
|
||||
|
||||
### Compiler changes
|
||||
|
||||
- Better error message for IndexError for empty containers. (#11476)
|
||||
|
||||
- Fix regression in semfold for old right shift. (#11477)
|
||||
|
||||
- Fix for passing tuples as static params to macros. (#11423)
|
||||
|
||||
## Bugfixes
|
||||
@@ -186,7 +186,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
if res != arNo:
|
||||
result = res
|
||||
if res == arYes: break
|
||||
of nkCall:
|
||||
of nkCallKinds:
|
||||
result = arNo
|
||||
for i in 1 ..< b.len:
|
||||
let res = isPartOf(a, b[i])
|
||||
|
||||
@@ -279,6 +279,8 @@ type
|
||||
sfNonReloadable # symbol will be left as-is when hot code reloading is on -
|
||||
# meaning that it won't be renamed and/or changed in any way
|
||||
sfGeneratedOp # proc is a generated '='; do not inject destructors in it
|
||||
# variable is generated closure environment; requires early
|
||||
# destruction for --newruntime.
|
||||
|
||||
|
||||
TSymFlags* = set[TSymFlag]
|
||||
@@ -619,10 +621,6 @@ type
|
||||
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot,
|
||||
mUnaryPlusI, mBitnotI,
|
||||
mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
|
||||
mZe8ToI, mZe8ToI64,
|
||||
mZe16ToI, mZe16ToI64,
|
||||
mZe32ToI64, mZeIToI64,
|
||||
mToU8, mToU16, mToU32,
|
||||
mToFloat, mToBiggestFloat,
|
||||
mToInt, mToBiggestInt,
|
||||
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
|
||||
@@ -693,10 +691,6 @@ const
|
||||
mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor,
|
||||
mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
|
||||
mUnaryPlusF64, mUnaryMinusF64, mAbsF64,
|
||||
mZe8ToI, mZe8ToI64,
|
||||
mZe16ToI, mZe16ToI64,
|
||||
mZe32ToI64, mZeIToI64,
|
||||
mToU8, mToU16, mToU32,
|
||||
mToFloat, mToBiggestFloat,
|
||||
mToInt, mToBiggestInt,
|
||||
mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr,
|
||||
|
||||
@@ -11,12 +11,17 @@
|
||||
# the code here should be reused in the Nim standard library
|
||||
|
||||
type
|
||||
TBitSet* = seq[int8] # we use byte here to avoid issues with
|
||||
ElemType = byte
|
||||
TBitSet* = seq[ElemType] # we use byte here to avoid issues with
|
||||
# cross-compiling; uint would be more efficient
|
||||
# however
|
||||
|
||||
const
|
||||
ElemSize* = sizeof(int8) * 8
|
||||
ElemSize* = 8
|
||||
One = ElemType(1)
|
||||
Zero = ElemType(0)
|
||||
|
||||
template modElemSize(arg: untyped): untyped = arg and 7
|
||||
template divElemSize(arg: untyped): untyped = arg shr 3
|
||||
|
||||
proc bitSetInit*(b: var TBitSet, length: int)
|
||||
proc bitSetUnion*(x: var TBitSet, y: TBitSet)
|
||||
@@ -32,17 +37,16 @@ proc bitSetCard*(x: TBitSet): BiggestInt
|
||||
# implementation
|
||||
|
||||
proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
|
||||
result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
|
||||
toU8(0)
|
||||
result = (x[int(e.divElemSize)] and (One shl e.modElemSize)) != Zero
|
||||
|
||||
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
|
||||
assert(elem >= 0)
|
||||
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
|
||||
toU8(int(1 shl (elem mod ElemSize)))
|
||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or
|
||||
(One shl elem.modElemSize)
|
||||
|
||||
proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
|
||||
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
|
||||
not toU8(int(1 shl (elem mod ElemSize)))
|
||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and
|
||||
not(One shl elem.modElemSize)
|
||||
|
||||
proc bitSetInit(b: var TBitSet, length: int) =
|
||||
newSeq(b, length)
|
||||
@@ -67,13 +71,13 @@ proc bitSetEquals(x, y: TBitSet): bool =
|
||||
|
||||
proc bitSetContains(x, y: TBitSet): bool =
|
||||
for i in 0 .. high(x):
|
||||
if (x[i] and not y[i]) != int8(0):
|
||||
if (x[i] and not y[i]) != Zero:
|
||||
return false
|
||||
result = true
|
||||
|
||||
# Number of set bits for all values of int8
|
||||
const populationCount: array[low(int8)..high(int8), int8] = block:
|
||||
var arr: array[low(int8)..high(int8), int8]
|
||||
const populationCount: array[uint8, uint8] = block:
|
||||
var arr: array[uint8, uint8]
|
||||
|
||||
proc countSetBits(x: uint8): uint8 =
|
||||
return
|
||||
@@ -87,11 +91,11 @@ const populationCount: array[low(int8)..high(int8), int8] = block:
|
||||
((x and 0b10000000'u8) shr 7)
|
||||
|
||||
|
||||
for it in low(int8)..high(int8):
|
||||
arr[it] = cast[int8](countSetBits(cast[uint8](it)))
|
||||
for it in low(uint8)..high(uint8):
|
||||
arr[it] = countSetBits(cast[uint8](it))
|
||||
|
||||
arr
|
||||
|
||||
proc bitSetCard(x: TBitSet): BiggestInt =
|
||||
for it in x:
|
||||
result.inc populationCount[it]
|
||||
result.inc int(populationCount[it])
|
||||
|
||||
@@ -156,7 +156,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
|
||||
# means '*T'. See posix.nim for lots of examples that do that in the wild.
|
||||
let callee = call.sons[0]
|
||||
if callee.kind == nkSym and
|
||||
{sfImportC, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportC} and
|
||||
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
|
||||
result = addrLoc(p.config, a)
|
||||
else:
|
||||
|
||||
@@ -76,7 +76,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
|
||||
# with the new semantics for 'nil' strings, we can map "" to nil and
|
||||
# save tons of allocations:
|
||||
if n.strVal.len == 0 and optNilSeqs notin p.options and
|
||||
p.config.selectedGc != gcDestructors:
|
||||
p.config.selectedGC != gcDestructors:
|
||||
result = genNilStringLiteral(p.module, n.info)
|
||||
else:
|
||||
result = genStringLiteral(p.module, n)
|
||||
@@ -93,26 +93,30 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
|
||||
proc genLiteral(p: BProc, n: PNode): Rope =
|
||||
result = genLiteral(p, n, n.typ)
|
||||
|
||||
proc bitSetToWord(s: TBitSet, size: int): BiggestInt =
|
||||
proc bitSetToWord(s: TBitSet, size: int): BiggestUInt =
|
||||
result = 0
|
||||
for j in 0 ..< size:
|
||||
if j < len(s): result = result or (ze64(s[j]) shl (j * 8))
|
||||
if j < len(s): result = result or (BiggestUInt(s[j]) shl (j * 8))
|
||||
|
||||
proc genRawSetData(cs: TBitSet, size: int): Rope =
|
||||
if size > 8:
|
||||
result = "{$n" % []
|
||||
var res = "{\n"
|
||||
for i in 0 ..< size:
|
||||
res.add "0x"
|
||||
res.add "0123456789abcdef"[cs[i] div 16]
|
||||
res.add "0123456789abcdef"[cs[i] mod 16]
|
||||
if i < size - 1:
|
||||
# not last iteration?
|
||||
if (i + 1) mod 8 == 0:
|
||||
addf(result, "0x$1,$n", [rope(toHex(ze64(cs[i]), 2))])
|
||||
# not last iteration
|
||||
if i mod 8 == 7:
|
||||
res.add ",\n"
|
||||
else:
|
||||
addf(result, "0x$1, ", [rope(toHex(ze64(cs[i]), 2))])
|
||||
res.add ", "
|
||||
else:
|
||||
addf(result, "0x$1}$n", [rope(toHex(ze64(cs[i]), 2))])
|
||||
res.add "}\n"
|
||||
|
||||
result = rope(res)
|
||||
else:
|
||||
result = intLiteral(bitSetToWord(cs, size))
|
||||
# result := rope('0x' + ToHex(bitSetToWord(cs, size), size * 2))
|
||||
result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size)))
|
||||
|
||||
proc genSetNode(p: BProc, n: PNode): Rope =
|
||||
var cs: TBitSet
|
||||
@@ -356,7 +360,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
||||
of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString,
|
||||
tyInt..tyUInt64, tyRange, tyVar, tyLent:
|
||||
tyInt..tyUInt64, tyRange, tyVar, tyLent, tyNil:
|
||||
linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
|
||||
else: internalError(p.config, "genAssignment: " & $ty.kind)
|
||||
|
||||
@@ -635,28 +639,6 @@ proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
|
||||
unaryExpr(p, e, d, "($1 == 0)")
|
||||
|
||||
proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
const
|
||||
unArithTab: array[mNot..mToBiggestInt, string] = [
|
||||
"!($1)", # Not
|
||||
"$1", # UnaryPlusI
|
||||
"($3)((NU$2) ~($1))", # BitnotI
|
||||
"$1", # UnaryPlusF64
|
||||
"-($1)", # UnaryMinusF64
|
||||
"($1 < 0? -($1) : ($1))", # AbsF64; BUGFIX: fabs() makes problems
|
||||
# for Tiny C, so we don't use it
|
||||
"(($3)(NU)(NU8)($1))", # mZe8ToI
|
||||
"(($3)(NU64)(NU8)($1))", # mZe8ToI64
|
||||
"(($3)(NU)(NU16)($1))", # mZe16ToI
|
||||
"(($3)(NU64)(NU16)($1))", # mZe16ToI64
|
||||
"(($3)(NU64)(NU32)($1))", # mZe32ToI64
|
||||
"(($3)(NU64)(NU)($1))", # mZeIToI64
|
||||
"(($3)(NU8)(NU)($1))", # ToU8
|
||||
"(($3)(NU16)(NU)($1))", # ToU16
|
||||
"(($3)(NU32)(NU64)($1))", # ToU32
|
||||
"((double) ($1))", # ToFloat
|
||||
"((double) ($1))", # ToBiggestFloat
|
||||
"float64ToInt32($1)", # ToInt
|
||||
"float64ToInt64($1)"] # ToBiggestInt
|
||||
var
|
||||
a: TLoc
|
||||
t: PType
|
||||
@@ -667,7 +649,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
template applyFormat(frmt: untyped) =
|
||||
putIntoDest(p, d, e, frmt % [rdLoc(a), rope(getSize(p.config, t) * 8),
|
||||
getSimpleTypeDesc(p.module, e.typ)])
|
||||
|
||||
case op
|
||||
of mNot:
|
||||
applyFormat("!($1)")
|
||||
@@ -682,24 +663,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mAbsF64:
|
||||
applyFormat("($1 < 0? -($1) : ($1))")
|
||||
# BUGFIX: fabs() makes problems for Tiny C
|
||||
of mZe8ToI:
|
||||
applyFormat("(($3)(NU)(NU8)($1))")
|
||||
of mZe8ToI64:
|
||||
applyFormat("(($3)(NU64)(NU8)($1))")
|
||||
of mZe16ToI:
|
||||
applyFormat("(($3)(NU)(NU16)($1))")
|
||||
of mZe16ToI64:
|
||||
applyFormat("(($3)(NU64)(NU16)($1))")
|
||||
of mZe32ToI64:
|
||||
applyFormat("(($3)(NU64)(NU32)($1))")
|
||||
of mZeIToI64:
|
||||
applyFormat("(($3)(NU64)(NU)($1))")
|
||||
of mToU8:
|
||||
applyFormat("(($3)(NU8)(NU)($1))")
|
||||
of mToU16:
|
||||
applyFormat("(($3)(NU16)(NU)($1))")
|
||||
of mToU32:
|
||||
applyFormat("(($3)(NU32)(NU64)($1))")
|
||||
of mToFloat:
|
||||
applyFormat("((double) ($1))")
|
||||
of mToBiggestFloat:
|
||||
@@ -1102,7 +1065,7 @@ proc gcUsage(conf: ConfigRef; n: PNode) =
|
||||
if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree)
|
||||
|
||||
proc strLoc(p: BProc; d: TLoc): Rope =
|
||||
if p.config.selectedGc == gcDestructors:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
result = byRefLoc(p, d)
|
||||
else:
|
||||
result = rdLoc(d)
|
||||
@@ -1343,11 +1306,17 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
|
||||
let seqtype = skipTypes(e.typ, abstractVarRange)
|
||||
var a: TLoc
|
||||
initLocExpr(p, e.sons[1], a)
|
||||
putIntoDest(p, d, e, ropecg(p.module,
|
||||
"($1)#nimNewSeqOfCap($2, $3)", [
|
||||
getTypeDesc(p.module, seqtype),
|
||||
genTypeInfo(p.module, seqtype, e.info), a.rdLoc]))
|
||||
gcUsage(p.config, e)
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
if d.k == locNone: getTemp(p, e.typ, d, needsInit=false)
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n",
|
||||
[d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
|
||||
getSeqPayloadType(p.module, seqtype)])
|
||||
else:
|
||||
putIntoDest(p, d, e, ropecg(p.module,
|
||||
"($1)#nimNewSeqOfCap($2, $3)", [
|
||||
getTypeDesc(p.module, seqtype),
|
||||
genTypeInfo(p.module, seqtype, e.info), a.rdLoc]))
|
||||
gcUsage(p.config, e)
|
||||
|
||||
proc genConstExpr(p: BProc, n: PNode): Rope
|
||||
proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
|
||||
@@ -1681,11 +1650,14 @@ proc makePtrType(baseType: PType): PType =
|
||||
addSonSkipIntLit(result, baseType)
|
||||
|
||||
proc makeAddr(n: PNode): PNode =
|
||||
result = newTree(nkHiddenAddr, n)
|
||||
result.typ = makePtrType(n.typ)
|
||||
if n.kind == nkHiddenAddr:
|
||||
result = n
|
||||
else:
|
||||
result = newTree(nkHiddenAddr, n)
|
||||
result.typ = makePtrType(n.typ)
|
||||
|
||||
proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
if p.config.selectedGc == gcDestructors:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
e.sons[1] = makeAddr(e[1])
|
||||
genCall(p, e, d)
|
||||
return
|
||||
@@ -1714,7 +1686,7 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
|
||||
gcUsage(p.config, e)
|
||||
|
||||
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
|
||||
if p.config.selectedGc == gcDestructors:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
binaryStmtAddr(p, e, d, "setLengthStrV2")
|
||||
else:
|
||||
var a, b, call: TLoc
|
||||
@@ -1928,7 +1900,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
|
||||
if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes:
|
||||
putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" %
|
||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||
elif p.config.selectedGc == gcDestructors and etyp.kind in {tySequence, tyString}:
|
||||
elif p.config.selectedGC == gcDestructors and etyp.kind in {tySequence, tyString}:
|
||||
putIntoDest(p, d, e, "(*($1*) (&$2))" %
|
||||
[getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage)
|
||||
else:
|
||||
@@ -2057,24 +2029,25 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||
|
||||
proc genDestroy(p: BProc; n: PNode) =
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
let t = n[1].typ.skipTypes(abstractInst)
|
||||
let arg = n[1].skipAddr
|
||||
let t = arg.typ.skipTypes(abstractInst)
|
||||
case t.kind
|
||||
of tyString:
|
||||
var a: TLoc
|
||||
initLocExpr(p, n[1].skipAddr, a)
|
||||
linefmt(p, cpsStmts, "if ($1.len && $1.p->allocator) {$n" &
|
||||
initLocExpr(p, arg, a)
|
||||
linefmt(p, cpsStmts, "if ($1.p && $1.p->allocator) {$n" &
|
||||
" $1.p->allocator->dealloc($1.p->allocator, $1.p, $1.p->cap + 1 + sizeof(NI) + sizeof(void*)); }$n",
|
||||
[rdLoc(a)])
|
||||
of tySequence:
|
||||
var a: TLoc
|
||||
initLocExpr(p, n[1].skipAddr, a)
|
||||
linefmt(p, cpsStmts, "if ($1.len && $1.p->allocator) {$n" &
|
||||
initLocExpr(p, arg, a)
|
||||
linefmt(p, cpsStmts, "if ($1.p && $1.p->allocator) {$n" &
|
||||
" $1.p->allocator->dealloc($1.p->allocator, $1.p, ($1.p->cap * sizeof($2)) + sizeof(NI) + sizeof(void*)); }$n",
|
||||
[rdLoc(a), getTypeDesc(p.module, t.lastSon)])
|
||||
else: discard "nothing to do"
|
||||
else:
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
if t.destructor != nil and t.destructor.magic != mDestroy:
|
||||
if t.destructor != nil and t.destructor.ast[bodyPos].len != 0:
|
||||
internalError(p.config, n.info, "destructor turned out to be not trivial")
|
||||
discard "ignore calls to the default destructor"
|
||||
|
||||
@@ -2161,7 +2134,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
genAssignment(p, dest, call, {})
|
||||
of mAppendStrStr: genStrAppend(p, e, d)
|
||||
of mAppendSeqElem:
|
||||
if p.config.selectedGc == gcDestructors:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
e.sons[1] = makeAddr(e[1])
|
||||
genCall(p, e, d)
|
||||
else:
|
||||
@@ -2195,6 +2168,22 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
if not p.module.compileToCpp:
|
||||
p.module.includeHeader("<stdalign.h>")
|
||||
putIntoDest(p, d, e, "((NI)alignof($1))" % [getTypeDesc(p.module, t)])
|
||||
of mOffsetOf:
|
||||
var dotExpr: PNode
|
||||
block findDotExpr:
|
||||
if e[1].kind == nkDotExpr:
|
||||
dotExpr = e[1]
|
||||
elif e[1].kind == nkCheckedFieldExpr:
|
||||
dotExpr = e[1][0]
|
||||
else:
|
||||
internalError(p.config, e.info, "unknown ast")
|
||||
let t = dotExpr[0].typ.skipTypes({tyTypeDesc})
|
||||
let member =
|
||||
if t.kind == tyTuple:
|
||||
"Field" & rope(dotExpr[1].sym.position)
|
||||
else:
|
||||
rope(dotExpr[1].sym.name.s)
|
||||
putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [getTypeDesc(p.module, t), member])
|
||||
of mChr: genSomeCast(p, e, d)
|
||||
of mOrd: genOrd(p, e, d)
|
||||
of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
|
||||
@@ -2255,8 +2244,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||
of mNLen..mNError, mSlurp..mQuoteAst:
|
||||
localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e.sons[0].sym.name.s))
|
||||
of mSpawn:
|
||||
let n = lowerings.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil)
|
||||
expr(p, n, d)
|
||||
when defined(leanCompiler):
|
||||
quit "compiler built without support for the 'spawn' statement"
|
||||
else:
|
||||
let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil)
|
||||
expr(p, n, d)
|
||||
of mParallel:
|
||||
when defined(leanCompiler):
|
||||
quit "compiler built without support for the 'parallel' statement"
|
||||
@@ -2696,9 +2688,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||
if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
|
||||
(sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
|
||||
(prc.kind == skMethod):
|
||||
# we have not only the header:
|
||||
if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags:
|
||||
genProc(p.module, prc)
|
||||
# Generate proc even if empty body, bugfix #11651.
|
||||
genProc(p.module, prc)
|
||||
of nkParForStmt: genParForStmt(p, n)
|
||||
of nkState: genState(p, n)
|
||||
of nkGotoState:
|
||||
@@ -2744,7 +2735,8 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
|
||||
else:
|
||||
globalError(p.config, info, "cannot create null element for: " & $t.kind)
|
||||
|
||||
proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) =
|
||||
proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode,
|
||||
result: var Rope; count: var int) =
|
||||
case obj.kind
|
||||
of nkRecList:
|
||||
for it in obj.sons:
|
||||
@@ -2770,7 +2762,8 @@ proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; cou
|
||||
else:
|
||||
localError(p.config, cons.info, "cannot create null element for: " & $obj)
|
||||
|
||||
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Rope; count: var int) =
|
||||
proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode,
|
||||
result: var Rope; count: var int) =
|
||||
var base = t.sons[0]
|
||||
let oldRes = result
|
||||
if not p.module.compileToCpp: result.add "{"
|
||||
@@ -2783,7 +2776,7 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, cons: PNode, result: var Ro
|
||||
inc count
|
||||
getNullValueAux(p, t, obj, cons, result, count)
|
||||
# do not emit '{}' as that is not valid C:
|
||||
if oldcount == count: result = oldres
|
||||
if oldcount == count: result = oldRes
|
||||
elif not p.module.compileToCpp: result.add "}"
|
||||
|
||||
proc genConstObjConstr(p: BProc; n: PNode): Rope =
|
||||
@@ -2857,7 +2850,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
|
||||
of nkBracket, nkPar, nkTupleConstr, nkClosure:
|
||||
var t = skipTypes(n.typ, abstractInstOwned)
|
||||
if t.kind == tySequence:
|
||||
if p.config.selectedGc == gcDestructors:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
result = genConstSeqV2(p, n, n.typ)
|
||||
else:
|
||||
result = genConstSeq(p, n, n.typ)
|
||||
@@ -2881,7 +2874,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope =
|
||||
of nkObjConstr:
|
||||
result = genConstObjConstr(p, n)
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
if p.config.selectedGc == gcDestructors:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
result = genStringLiteralV2Const(p.module, n)
|
||||
else:
|
||||
var d: TLoc
|
||||
|
||||
@@ -15,7 +15,7 @@ const
|
||||
stringCaseThreshold = 8
|
||||
# above X strings a hash-switch for strings is generated
|
||||
|
||||
proc getTraverseProc(p: BProc, v: Psym): Rope =
|
||||
proc getTraverseProc(p: BProc, v: PSym): Rope =
|
||||
if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, gcRefc} and
|
||||
optNimV2 notin p.config.globalOptions and
|
||||
containsGarbageCollectedRef(v.loc.t):
|
||||
@@ -690,7 +690,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
lineCg(p, cpsStmts, "#raiseExceptionEx((#Exception*)$1, $2, $3, $4, $5);$n",
|
||||
[e, makeCString(typ.sym.name.s),
|
||||
makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s),
|
||||
makeCString(toFileName(p.config, t.info)), toLinenumber(t.info)])
|
||||
quotedFilename(p.config, t.info), toLinenumber(t.info)])
|
||||
if optNimV2 in p.config.globalOptions:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [e])
|
||||
else:
|
||||
|
||||
@@ -329,6 +329,9 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
|
||||
addAbiCheck(m, typ, result)
|
||||
|
||||
proc pushType(m: BModule, typ: PType) =
|
||||
for i in 0 .. high(m.typeStack):
|
||||
# pointer equality is good enough here:
|
||||
if m.typeStack[i] == typ: return
|
||||
add(m.typeStack, typ)
|
||||
|
||||
proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
|
||||
@@ -384,14 +387,53 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
|
||||
result = getTypeForward(m, t, hashType(t))
|
||||
pushType(m, t)
|
||||
of tySequence:
|
||||
let sig = hashType(t)
|
||||
if m.config.selectedGC == gcDestructors:
|
||||
result = getTypeDescAux(m, t, check)
|
||||
if skipTypes(etB.sons[0], typedescInst).kind == tyEmpty:
|
||||
internalError(m.config, "cannot map the empty seq type to a C type")
|
||||
|
||||
result = cacheGetType(m.forwTypeCache, sig)
|
||||
if result == nil:
|
||||
result = getTypeName(m, t, sig)
|
||||
if not isImportedType(t):
|
||||
m.forwTypeCache[sig] = result
|
||||
addForwardStructFormat(m, rope"struct", result)
|
||||
let payload = result & "_Content"
|
||||
addForwardStructFormat(m, rope"struct", payload)
|
||||
|
||||
if cacheGetType(m.typeCache, sig) == nil:
|
||||
m.typeCache[sig] = result
|
||||
#echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
|
||||
appcg(m, m.s[cfsTypes],
|
||||
"struct $1 {$N" &
|
||||
" NI len; $1_Content* p;$N" &
|
||||
"};$N", [result])
|
||||
else:
|
||||
result = getTypeForward(m, t, hashType(t)) & seqStar(m)
|
||||
pushType(m, t)
|
||||
result = getTypeForward(m, t, sig) & seqStar(m)
|
||||
pushType(m, t)
|
||||
else:
|
||||
result = getTypeDescAux(m, t, check)
|
||||
|
||||
proc getSeqPayloadType(m: BModule; t: PType): Rope =
|
||||
var check = initIntSet()
|
||||
result = getTypeDescWeak(m, t, check) & "_Content"
|
||||
#result = getTypeForward(m, t, hashType(t)) & "_Content"
|
||||
|
||||
proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
|
||||
let sig = hashType(t)
|
||||
let result = cacheGetType(m.typeCache, sig)
|
||||
if result == nil:
|
||||
discard getTypeDescAux(m, t, check)
|
||||
else:
|
||||
# little hack for now to prevent multiple definitions of the same
|
||||
# Seq_Content:
|
||||
appcg(m, m.s[cfsTypes], """$N
|
||||
$3ifndef $2_Content_PP
|
||||
$3define $2_Content_PP
|
||||
struct $2_Content { NI cap;#AllocatorObj* allocator;$1 data[SEQ_DECL_SIZE];};
|
||||
$3endif$N
|
||||
""", [getTypeDescAux(m, t.skipTypes(abstractInst).sons[0], check), result, rope"#"])
|
||||
|
||||
proc paramStorageLoc(param: PSym): TStorageLoc =
|
||||
if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
|
||||
tyArray, tyOpenArray, tyVarargs}:
|
||||
@@ -513,7 +555,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||
if fieldType.kind == tyUncheckedArray:
|
||||
addf(result, "$1 $2[SEQ_DECL_SIZE];$n",
|
||||
[getTypeDescAux(m, fieldType.elemType, check), sname])
|
||||
elif fieldType.kind == tySequence and m.config.selectedGC != gcDestructors:
|
||||
elif fieldType.kind == tySequence:
|
||||
# we need to use a weak dependency here for trecursive_table.
|
||||
addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname])
|
||||
elif field.bitsize != 0:
|
||||
@@ -627,15 +669,6 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
|
||||
result = if result.kind == tyGenericInst: result.sons[1]
|
||||
else: result.elemType
|
||||
|
||||
proc getSeqPayloadType(m: BModule; t: PType): Rope =
|
||||
result = getTypeForward(m, t, hashType(t)) & "_Content"
|
||||
when false:
|
||||
var check = initIntSet()
|
||||
# XXX remove this duplication:
|
||||
appcg(m, m.s[cfsSeqTypes],
|
||||
"struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE]; };$n",
|
||||
[getTypeDescAux(m, t.sons[0], check), result])
|
||||
|
||||
proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
# returns only the type's name
|
||||
var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
|
||||
@@ -675,11 +708,15 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
result = name & star
|
||||
m.typeCache[sig] = result
|
||||
of tySequence:
|
||||
# no restriction! We have a forward declaration for structs
|
||||
let name = getTypeForward(m, et, hashType et)
|
||||
result = name & seqStar(m) & star
|
||||
m.typeCache[sig] = result
|
||||
pushType(m, et)
|
||||
if m.config.selectedGC == gcDestructors:
|
||||
result = getTypeDescWeak(m, et, check) & star
|
||||
m.typeCache[sig] = result
|
||||
else:
|
||||
# no restriction! We have a forward declaration for structs
|
||||
let name = getTypeForward(m, et, hashType et)
|
||||
result = name & seqStar(m) & star
|
||||
m.typeCache[sig] = result
|
||||
pushType(m, et)
|
||||
else:
|
||||
# else we have a strong dependency :-(
|
||||
result = getTypeDescAux(m, et, check) & star
|
||||
@@ -731,29 +768,25 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
"void* ClE_0;$n} $1;$n",
|
||||
[result, rettype, desc])
|
||||
of tySequence:
|
||||
# we cannot use getTypeForward here because then t would be associated
|
||||
# with the name of the struct, not with the pointer to the struct:
|
||||
result = cacheGetType(m.forwTypeCache, sig)
|
||||
if result == nil:
|
||||
result = getTypeName(m, origTyp, sig)
|
||||
if m.config.selectedGC == gcDestructors:
|
||||
result = getTypeDescWeak(m, t, check)
|
||||
else:
|
||||
# we cannot use getTypeForward here because then t would be associated
|
||||
# with the name of the struct, not with the pointer to the struct:
|
||||
result = cacheGetType(m.forwTypeCache, sig)
|
||||
if result == nil:
|
||||
result = getTypeName(m, origTyp, sig)
|
||||
if not isImportedType(t):
|
||||
addForwardStructFormat(m, structOrUnion(t), result)
|
||||
m.forwTypeCache[sig] = result
|
||||
assert(cacheGetType(m.typeCache, sig) == nil)
|
||||
m.typeCache[sig] = result & seqStar(m)
|
||||
if not isImportedType(t):
|
||||
addForwardStructFormat(m, structOrUnion(t), result)
|
||||
m.forwTypeCache[sig] = result
|
||||
assert(cacheGetType(m.typeCache, sig) == nil)
|
||||
m.typeCache[sig] = result & seqStar(m)
|
||||
if not isImportedType(t):
|
||||
if skipTypes(t.sons[0], typedescInst).kind != tyEmpty:
|
||||
const
|
||||
cppSeq = "struct $2 : #TGenericSeq {$n"
|
||||
cSeq = "struct $2 {$n" &
|
||||
" #TGenericSeq Sup;$n"
|
||||
if m.config.selectedGC == gcDestructors:
|
||||
appcg(m, m.s[cfsTypes],
|
||||
"typedef struct{ NI cap;#AllocatorObj* allocator;$1 data[SEQ_DECL_SIZE];}$2_Content;$n" &
|
||||
"struct $2 {$n" &
|
||||
" NI len; $2_Content* p;$n" &
|
||||
"};$n", [getTypeDescAux(m, t.sons[0], check), result])
|
||||
else:
|
||||
if skipTypes(t.sons[0], typedescInst).kind != tyEmpty:
|
||||
const
|
||||
cppSeq = "struct $2 : #TGenericSeq {$n"
|
||||
cSeq = "struct $2 {$n" &
|
||||
" #TGenericSeq Sup;$n"
|
||||
if m.compileToCpp:
|
||||
appcg(m, m.s[cfsSeqTypes],
|
||||
cppSeq & " $1 data[SEQ_DECL_SIZE];$n" &
|
||||
@@ -762,11 +795,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
appcg(m, m.s[cfsSeqTypes],
|
||||
cSeq & " $1 data[SEQ_DECL_SIZE];$n" &
|
||||
"};$n", [getTypeDescAux(m, t.sons[0], check), result])
|
||||
elif m.config.selectedGC == gcDestructors:
|
||||
internalError(m.config, "cannot map the empty seq type to a C type")
|
||||
else:
|
||||
result = rope("TGenericSeq")
|
||||
add(result, seqStar(m))
|
||||
else:
|
||||
result = rope("TGenericSeq")
|
||||
add(result, seqStar(m))
|
||||
of tyUncheckedArray:
|
||||
result = getTypeName(m, origTyp, sig)
|
||||
m.typeCache[sig] = result
|
||||
@@ -830,21 +861,8 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
|
||||
m.typeCache[sig] = typedefName
|
||||
result = typedefName
|
||||
else:
|
||||
when false:
|
||||
if t.sym != nil and t.sym.name.s == "KeyValuePair":
|
||||
if t == origTyp:
|
||||
echo "wtf: came here"
|
||||
writeStackTrace()
|
||||
quit 1
|
||||
result = cacheGetType(m.forwTypeCache, sig)
|
||||
if result == nil:
|
||||
when false:
|
||||
if t.sym != nil and t.sym.name.s == "KeyValuePair":
|
||||
# or {sfImportc, sfExportc} * t.sym.flags == {}:
|
||||
if t.loc.r != nil:
|
||||
echo t.kind, " ", hashType t
|
||||
echo origTyp.kind, " ", sig
|
||||
assert t.loc.r == nil
|
||||
result = getTypeName(m, origTyp, sig)
|
||||
m.forwTypeCache[sig] = result
|
||||
if not isImportedType(t):
|
||||
@@ -903,8 +921,13 @@ proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope =
|
||||
|
||||
proc finishTypeDescriptions(m: BModule) =
|
||||
var i = 0
|
||||
var check = initIntSet()
|
||||
while i < len(m.typeStack):
|
||||
discard getTypeDesc(m, m.typeStack[i])
|
||||
let t = m.typeStack[i]
|
||||
if m.config.selectedGC == gcDestructors and t.skipTypes(abstractInst).kind == tySequence:
|
||||
seqV2ContentType(m, t, check)
|
||||
else:
|
||||
discard getTypeDescAux(m, t, check)
|
||||
inc(i)
|
||||
|
||||
template cgDeclFrmt*(s: PSym): string =
|
||||
@@ -955,7 +978,7 @@ proc getNimNode(m: BModule): Rope =
|
||||
result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)]
|
||||
inc(m.typeNodes)
|
||||
|
||||
proc TINameForHcr(m: BModule, name: Rope): Rope =
|
||||
proc tiNameForHcr(m: BModule, name: Rope): Rope =
|
||||
return if m.hcrOn: "(*".rope & name & ")" else: name
|
||||
|
||||
proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
|
||||
@@ -967,7 +990,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
|
||||
else:
|
||||
nimtypeKind = ord(typ.kind)
|
||||
|
||||
let nameHcr = TINameForHcr(m, name)
|
||||
let nameHcr = tiNameForHcr(m, name)
|
||||
|
||||
var size: Rope
|
||||
if tfIncompleteStruct in typ.flags: size = rope"void*"
|
||||
@@ -989,7 +1012,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
|
||||
if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
|
||||
typename = "anon ref object from " & m.config$origType.skipTypes(skipPtrs).sym.info
|
||||
addf(m.s[cfsTypeInit3], "$1.name = $2;$n",
|
||||
[nameHcr, makeCstring typename])
|
||||
[nameHcr, makeCString typename])
|
||||
discard cgsym(m, "nimTypeRoot")
|
||||
addf(m.s[cfsTypeInit3], "$1.nextType = nimTypeRoot; nimTypeRoot=&$1;$n",
|
||||
[nameHcr])
|
||||
@@ -1119,7 +1142,7 @@ proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo
|
||||
var tmp = getNimNode(m)
|
||||
if not isImportedType(typ):
|
||||
genObjectFields(m, typ, origType, typ.n, tmp, info)
|
||||
addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [TINameForHcr(m, name), tmp])
|
||||
addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [tiNameForHcr(m, name), tmp])
|
||||
var t = typ.sons[0]
|
||||
while t != nil:
|
||||
t = t.skipTypes(skipPtrs)
|
||||
@@ -1147,7 +1170,7 @@ proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo)
|
||||
else:
|
||||
addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n",
|
||||
[expr, rope(length)])
|
||||
addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [TINameForHcr(m, name), expr])
|
||||
addf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [tiNameForHcr(m, name), expr])
|
||||
|
||||
proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
|
||||
# Type information for enumerations is quite heavy, so we do some
|
||||
@@ -1186,17 +1209,17 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
|
||||
add(m.s[cfsTypeInit3], specialCases)
|
||||
addf(m.s[cfsTypeInit3],
|
||||
"$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n$4.node = &$1;$n",
|
||||
[getNimNode(m), rope(length), nodePtrs, TINameForHcr(m, name)])
|
||||
[getNimNode(m), rope(length), nodePtrs, tiNameForHcr(m, name)])
|
||||
if hasHoles:
|
||||
# 1 << 2 is {ntfEnumHole}
|
||||
addf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [TINameForHcr(m, name)])
|
||||
addf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [tiNameForHcr(m, name)])
|
||||
|
||||
proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
|
||||
assert(typ.sons[0] != nil)
|
||||
genTypeInfoAux(m, typ, typ, name, info)
|
||||
var tmp = getNimNode(m)
|
||||
addf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
|
||||
[tmp, rope(firstOrd(m.config, typ)), TINameForHcr(m, name)])
|
||||
[tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)])
|
||||
|
||||
proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
|
||||
genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info)
|
||||
@@ -1327,12 +1350,12 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
|
||||
if m.config.selectedGC != gcDestructors:
|
||||
if m.config.selectedGC >= gcMarkAndSweep:
|
||||
let markerProc = genTraverseProc(m, origType, sig)
|
||||
addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [TINameForHcr(m, result), markerProc])
|
||||
addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
||||
of tyRef:
|
||||
genTypeInfoAux(m, t, t, result, info)
|
||||
if m.config.selectedGC >= gcMarkAndSweep:
|
||||
let markerProc = genTraverseProc(m, origType, sig)
|
||||
addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [TINameForHcr(m, result), markerProc])
|
||||
addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc])
|
||||
of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info)
|
||||
of tyArray: genArrayInfo(m, t, result, info)
|
||||
of tySet: genSetInfo(m, t, result, info)
|
||||
|
||||
@@ -17,7 +17,7 @@ import
|
||||
lowerings, tables, sets, ndi, lineinfos, pathutils, transf, enumtostr
|
||||
|
||||
when not defined(leanCompiler):
|
||||
import semparallel
|
||||
import spawn, semparallel
|
||||
|
||||
import strutils except `%` # collides with ropes.`%`
|
||||
|
||||
@@ -268,7 +268,7 @@ proc genLineDir(p: BProc, t: PNode) =
|
||||
[line, makeCString(toFilename(p.config, t.info))])
|
||||
elif ({optLineTrace, optStackTrace} * p.options ==
|
||||
{optLineTrace, optStackTrace}) and
|
||||
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIDX:
|
||||
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
|
||||
if freshLineInfo(p, t.info):
|
||||
linefmt(p, cpsStmts, "nimln_($1, $2);$n",
|
||||
[line, quotedFilename(p.config, t.info)])
|
||||
@@ -296,13 +296,13 @@ proc lenField(p: BProc): Rope =
|
||||
result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
|
||||
|
||||
proc lenExpr(p: BProc; a: TLoc): Rope =
|
||||
if p.config.selectedGc == gcDestructors:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
result = rdLoc(a) & ".len"
|
||||
else:
|
||||
result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
|
||||
|
||||
proc dataField(p: BProc): Rope =
|
||||
if p.config.selectedGc == gcDestructors:
|
||||
if p.config.selectedGC == gcDestructors:
|
||||
result = rope".p->data"
|
||||
else:
|
||||
result = rope"->data"
|
||||
@@ -380,10 +380,10 @@ proc isComplexValueType(t: PType): bool {.inline.} =
|
||||
(t.kind == tyProc and t.callConv == ccClosure)
|
||||
|
||||
proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
let containsGcRef = p.config.selectedGc != gcDestructors and containsGarbageCollectedRef(loc.t)
|
||||
let containsGcRef = p.config.selectedGC != gcDestructors and containsGarbageCollectedRef(loc.t)
|
||||
let typ = skipTypes(loc.t, abstractVarRange)
|
||||
if isImportedCppType(typ): return
|
||||
if p.config.selectedGc == gcDestructors and typ.kind in {tyString, tySequence}:
|
||||
if p.config.selectedGC == gcDestructors and typ.kind in {tyString, tySequence}:
|
||||
assert rdLoc(loc) != nil
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
|
||||
elif not isComplexValueType(typ):
|
||||
@@ -414,7 +414,7 @@ proc resetLoc(p: BProc, loc: var TLoc) =
|
||||
|
||||
proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
|
||||
let typ = loc.t
|
||||
if p.config.selectedGc == gcDestructors and skipTypes(typ, abstractInst).kind in {tyString, tySequence}:
|
||||
if p.config.selectedGC == gcDestructors and skipTypes(typ, abstractInst).kind in {tyString, tySequence}:
|
||||
linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
|
||||
elif not isComplexValueType(typ):
|
||||
linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
|
||||
@@ -780,7 +780,7 @@ proc cgsym(m: BModule, name: string): Rope =
|
||||
# we're picky here for the system module too:
|
||||
rawMessage(m.config, errGenerated, "system module needs: " & name)
|
||||
result = sym.loc.r
|
||||
if m.hcrOn and sym != nil and sym.kind in skProc..skIterator:
|
||||
if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
|
||||
result.addActualPrefixForHCR(m.module, sym)
|
||||
|
||||
proc generateHeaders(m: BModule) =
|
||||
@@ -1474,20 +1474,20 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
|
||||
datInit = m.getDatInitName
|
||||
|
||||
if m.hcrOn:
|
||||
var hcr_module_meta = "$nN_LIB_PRIVATE const char* hcr_module_list[] = {$n" % []
|
||||
var hcrModuleMeta = "$nN_LIB_PRIVATE const char* hcr_module_list[] = {$n" % []
|
||||
let systemModulePath = getModuleDllPath(m, g.modules[g.graph.config.m.systemFileIdx.int].module)
|
||||
let mainModulePath = getModuleDllPath(m, m.module)
|
||||
if sfMainModule in m.module.flags:
|
||||
addf(hcr_module_meta, "\t$1,$n", [systemModulePath])
|
||||
addf(hcrModuleMeta, "\t$1,$n", [systemModulePath])
|
||||
g.graph.importDeps.withValue(FileIndex(m.module.position), deps):
|
||||
for curr in deps[]:
|
||||
addf(hcr_module_meta, "\t$1,$n", [getModuleDllPath(m, g.modules[curr.int].module)])
|
||||
addf(hcr_module_meta, "\t\"\"};$n", [])
|
||||
addf(hcr_module_meta, "$nN_LIB_EXPORT N_NIMCALL(void**, HcrGetImportedModules)() { return (void**)hcr_module_list; }$n", [])
|
||||
addf(hcr_module_meta, "$nN_LIB_EXPORT N_NIMCALL(char*, HcrGetSigHash)() { return \"$1\"; }$n$n",
|
||||
addf(hcrModuleMeta, "\t$1,$n", [getModuleDllPath(m, g.modules[curr.int].module)])
|
||||
addf(hcrModuleMeta, "\t\"\"};$n", [])
|
||||
addf(hcrModuleMeta, "$nN_LIB_EXPORT N_NIMCALL(void**, HcrGetImportedModules)() { return (void**)hcr_module_list; }$n", [])
|
||||
addf(hcrModuleMeta, "$nN_LIB_EXPORT N_NIMCALL(char*, HcrGetSigHash)() { return \"$1\"; }$n$n",
|
||||
[($sigHash(m.module)).rope])
|
||||
if sfMainModule in m.module.flags:
|
||||
add(g.mainModProcs, hcr_module_meta)
|
||||
add(g.mainModProcs, hcrModuleMeta)
|
||||
addf(g.mainModProcs, "static void* hcr_handle;$N", [])
|
||||
addf(g.mainModProcs, "N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [init])
|
||||
addf(g.mainModProcs, "N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [datInit])
|
||||
@@ -1511,7 +1511,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
|
||||
add(g.mainDatInit, "\t*cmd_count = cmdCount;\n")
|
||||
add(g.mainDatInit, "\t*cmd_line = cmdLine;\n")
|
||||
else:
|
||||
add(m.s[cfsInitProc], hcr_module_meta)
|
||||
add(m.s[cfsInitProc], hcrModuleMeta)
|
||||
return
|
||||
|
||||
if m.s[cfsDatInitProc].len > 0:
|
||||
@@ -1774,7 +1774,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
|
||||
if sfSystemModule in module.flags:
|
||||
incl result.flags, preventStackTrace
|
||||
excl(result.preInitProc.options, optStackTrace)
|
||||
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
|
||||
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCfilePath(g.config, filename), "ndi")
|
||||
else: AbsoluteFile""
|
||||
open(result.ndi, ndiName, g.config)
|
||||
|
||||
@@ -1805,7 +1805,7 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
|
||||
let f = if graph.config.headerFile.len > 0: AbsoluteFile graph.config.headerFile
|
||||
else: graph.config.projectFull
|
||||
g.generatedHeader = rawNewModule(g, module,
|
||||
changeFileExt(completeCFilePath(graph.config, f), hExt))
|
||||
changeFileExt(completeCfilePath(graph.config, f), hExt))
|
||||
incl g.generatedHeader.flags, isHeaderFile
|
||||
|
||||
proc writeHeader(m: BModule) =
|
||||
@@ -1838,9 +1838,9 @@ proc writeHeader(m: BModule) =
|
||||
proc getCFile(m: BModule): AbsoluteFile =
|
||||
let ext =
|
||||
if m.compileToCpp: ".nim.cpp"
|
||||
elif m.config.cmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".nim.m"
|
||||
elif m.config.cmd == cmdCompileToOC or sfCompileToObjc in m.module.flags: ".nim.m"
|
||||
else: ".nim.c"
|
||||
result = changeFileExt(completeCFilePath(m.config, withPackageName(m.config, m.cfilename)), ext)
|
||||
result = changeFileExt(completeCfilePath(m.config, withPackageName(m.config, m.cfilename)), ext)
|
||||
|
||||
when false:
|
||||
proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
|
||||
@@ -1876,11 +1876,11 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
|
||||
m.initProc.options = initProcOptions(m)
|
||||
#softRnl = if optLineDir in m.config.options: noRnl else: rnl
|
||||
# XXX replicate this logic!
|
||||
let transformed_n = transformStmt(m.g.graph, m.module, n)
|
||||
let transformedN = transformStmt(m.g.graph, m.module, n)
|
||||
if m.hcrOn:
|
||||
addHcrInitGuards(m.initProc, transformed_n, m.inHcrInitGuard)
|
||||
addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard)
|
||||
else:
|
||||
genStmts(m.initProc, transformed_n)
|
||||
genStmts(m.initProc, transformedN)
|
||||
|
||||
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
|
||||
result = true
|
||||
@@ -1920,7 +1920,7 @@ proc writeModule(m: BModule, pending: bool) =
|
||||
generateThreadVarsSize(m)
|
||||
|
||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
var code = genModule(m, cf)
|
||||
if code != nil:
|
||||
when hasTinyCBackend:
|
||||
@@ -1932,7 +1932,7 @@ proc writeModule(m: BModule, pending: bool) =
|
||||
addFileToCompile(m.config, cf)
|
||||
elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
|
||||
let cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
mergeFiles(cfile, m)
|
||||
genInitCode(m)
|
||||
finishTypeDescriptions(m)
|
||||
@@ -1946,7 +1946,7 @@ proc writeModule(m: BModule, pending: bool) =
|
||||
# ``system.c`` but then compilation fails due to an error. This means
|
||||
# that ``system.o`` is missing, so we need to call the C compiler for it:
|
||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
if not fileExists(cf.obj): cf.flags = {CfileFlag.Cached}
|
||||
addFileToCompile(m.config, cf)
|
||||
close(m.ndi)
|
||||
@@ -1954,7 +1954,7 @@ proc writeModule(m: BModule, pending: bool) =
|
||||
proc updateCachedModule(m: BModule) =
|
||||
let cfile = getCFile(m)
|
||||
var cf = Cfile(nimname: m.module.name.s, cname: cfile,
|
||||
obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
|
||||
|
||||
if mergeRequired(m) and sfMainModule notin m.module.flags:
|
||||
mergeFiles(cfile, m)
|
||||
@@ -2020,7 +2020,7 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
|
||||
let disp = generateMethodDispatchers(graph)
|
||||
for x in disp: genProcAux(m, x.sym)
|
||||
|
||||
m.g.modules_closed.add m
|
||||
m.g.modulesClosed.add m
|
||||
|
||||
proc genForwardedProcs(g: BModuleList) =
|
||||
# Forward declared proc:s lack bodies when first encountered, so they're given
|
||||
|
||||
@@ -100,7 +100,7 @@ type
|
||||
TypeCache* = Table[SigHash, Rope]
|
||||
TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: PSym]]
|
||||
|
||||
Codegenflag* = enum
|
||||
CodegenFlag* = enum
|
||||
preventStackTrace, # true if stack traces need to be prevented
|
||||
usesThreadVars, # true if the module uses a thread var
|
||||
frameDeclared, # hack for ROD support so that we don't declare
|
||||
@@ -114,7 +114,7 @@ type
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||
mapping*: Rope # the generated mapping file (if requested)
|
||||
modules*: seq[BModule] # list of all compiled modules
|
||||
modules_closed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
||||
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
||||
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
|
||||
generatedHeader*: BModule
|
||||
breakPointId*: int
|
||||
@@ -137,7 +137,7 @@ type
|
||||
|
||||
TCGen = object of PPassContext # represents a C source file
|
||||
s*: TCFileSections # sections of the C file
|
||||
flags*: set[Codegenflag]
|
||||
flags*: set[CodegenFlag]
|
||||
module*: PSym
|
||||
filename*: AbsoluteFile
|
||||
cfilename*: AbsoluteFile # filename of the module (including path,
|
||||
@@ -196,6 +196,6 @@ proc newModuleList*(g: ModuleGraph): BModuleList =
|
||||
config: g.config, graph: g, nimtvDeclared: initIntSet())
|
||||
|
||||
iterator cgenModules*(g: BModuleList): BModule =
|
||||
for m in g.modules_closed:
|
||||
for m in g.modulesClosed:
|
||||
# iterate modules in the order they were closed
|
||||
yield m
|
||||
|
||||
@@ -854,156 +854,155 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||
|
||||
proc transformClosureIteratorBody(ctx: var Ctx, n: PNode, gotoOut: PNode): PNode =
|
||||
result = n
|
||||
case n.kind:
|
||||
of nkSkip:
|
||||
discard
|
||||
case n.kind
|
||||
of nkSkip: discard
|
||||
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
result = addGotoOut(result, gotoOut)
|
||||
for i in 0 ..< n.len:
|
||||
if n[i].hasYields:
|
||||
# Create a new split
|
||||
let go = newNodeI(nkGotoState, n[i].info)
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], go)
|
||||
of nkStmtList, nkStmtListExpr:
|
||||
result = addGotoOut(result, gotoOut)
|
||||
for i in 0 ..< n.len:
|
||||
if n[i].hasYields:
|
||||
# Create a new split
|
||||
let go = newNodeI(nkGotoState, n[i].info)
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], go)
|
||||
|
||||
let s = newNodeI(nkStmtList, n[i + 1].info)
|
||||
for j in i + 1 ..< n.len:
|
||||
s.add(n[j])
|
||||
let s = newNodeI(nkStmtList, n[i + 1].info)
|
||||
for j in i + 1 ..< n.len:
|
||||
s.add(n[j])
|
||||
|
||||
n.sons.setLen(i + 1)
|
||||
discard ctx.newState(s, go)
|
||||
if ctx.transformClosureIteratorBody(s, gotoOut) != s:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != s")
|
||||
break
|
||||
n.sons.setLen(i + 1)
|
||||
discard ctx.newState(s, go)
|
||||
if ctx.transformClosureIteratorBody(s, gotoOut) != s:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != s")
|
||||
break
|
||||
|
||||
of nkYieldStmt:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
result.add(n)
|
||||
result.add(gotoOut)
|
||||
of nkYieldStmt:
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
result.add(n)
|
||||
result.add(gotoOut)
|
||||
|
||||
of nkElse, nkElseExpr:
|
||||
result[0] = addGotoOut(result[0], gotoOut)
|
||||
result[0] = ctx.transformClosureIteratorBody(result[0], gotoOut)
|
||||
of nkElse, nkElseExpr:
|
||||
result[0] = addGotoOut(result[0], gotoOut)
|
||||
result[0] = ctx.transformClosureIteratorBody(result[0], gotoOut)
|
||||
|
||||
of nkElifBranch, nkElifExpr, nkOfBranch:
|
||||
result[^1] = addGotoOut(result[^1], gotoOut)
|
||||
result[^1] = ctx.transformClosureIteratorBody(result[^1], gotoOut)
|
||||
|
||||
of nkIfStmt, nkCaseStmt:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
|
||||
if n[^1].kind != nkElse:
|
||||
# We don't have an else branch, but every possible branch has to end with
|
||||
# gotoOut, so add else here.
|
||||
let elseBranch = newTree(nkElse, gotoOut)
|
||||
n.add(elseBranch)
|
||||
|
||||
of nkWhileStmt:
|
||||
# while e:
|
||||
# s
|
||||
# ->
|
||||
# BEGIN_STATE:
|
||||
# if e:
|
||||
# s
|
||||
# goto BEGIN_STATE
|
||||
# else:
|
||||
# goto OUT
|
||||
|
||||
result = newNodeI(nkGotoState, n.info)
|
||||
|
||||
let s = newNodeI(nkStmtList, n.info)
|
||||
discard ctx.newState(s, result)
|
||||
let ifNode = newNodeI(nkIfStmt, n.info)
|
||||
let elifBranch = newNodeI(nkElifBranch, n.info)
|
||||
elifBranch.add(n[0])
|
||||
|
||||
var body = addGotoOut(n[1], result)
|
||||
|
||||
body = ctx.transformBreaksAndContinuesInWhile(body, result, gotoOut)
|
||||
body = ctx.transformClosureIteratorBody(body, result)
|
||||
|
||||
elifBranch.add(body)
|
||||
ifNode.add(elifBranch)
|
||||
of nkElifBranch, nkElifExpr, nkOfBranch:
|
||||
result[^1] = addGotoOut(result[^1], gotoOut)
|
||||
result[^1] = ctx.transformClosureIteratorBody(result[^1], gotoOut)
|
||||
|
||||
of nkIfStmt, nkCaseStmt:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
|
||||
if n[^1].kind != nkElse:
|
||||
# We don't have an else branch, but every possible branch has to end with
|
||||
# gotoOut, so add else here.
|
||||
let elseBranch = newTree(nkElse, gotoOut)
|
||||
ifNode.add(elseBranch)
|
||||
s.add(ifNode)
|
||||
n.add(elseBranch)
|
||||
|
||||
of nkBlockStmt:
|
||||
result[1] = addGotoOut(result[1], gotoOut)
|
||||
result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut)
|
||||
result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
|
||||
of nkWhileStmt:
|
||||
# while e:
|
||||
# s
|
||||
# ->
|
||||
# BEGIN_STATE:
|
||||
# if e:
|
||||
# s
|
||||
# goto BEGIN_STATE
|
||||
# else:
|
||||
# goto OUT
|
||||
|
||||
of nkTryStmt, nkHiddenTryStmt:
|
||||
# See explanation above about how this works
|
||||
ctx.hasExceptions = true
|
||||
result = newNodeI(nkGotoState, n.info)
|
||||
|
||||
result = newNodeI(nkGotoState, n.info)
|
||||
var tryBody = toStmtList(n[0])
|
||||
var exceptBody = ctx.collectExceptState(n)
|
||||
var finallyBody = newTree(nkStmtList, getFinallyNode(ctx, n))
|
||||
finallyBody = ctx.transformReturnsInTry(finallyBody)
|
||||
finallyBody.add(ctx.newEndFinallyNode(finallyBody.info))
|
||||
let s = newNodeI(nkStmtList, n.info)
|
||||
discard ctx.newState(s, result)
|
||||
let ifNode = newNodeI(nkIfStmt, n.info)
|
||||
let elifBranch = newNodeI(nkElifBranch, n.info)
|
||||
elifBranch.add(n[0])
|
||||
|
||||
# The following index calculation is based on the knowledge how state
|
||||
# indexes are assigned
|
||||
let tryIdx = ctx.states.len
|
||||
var exceptIdx, finallyIdx: int
|
||||
if exceptBody.kind != nkEmpty:
|
||||
exceptIdx = -(tryIdx + 1)
|
||||
finallyIdx = tryIdx + 2
|
||||
else:
|
||||
exceptIdx = tryIdx + 1
|
||||
finallyIdx = tryIdx + 1
|
||||
var body = addGotoOut(n[1], result)
|
||||
|
||||
let outToFinally = newNodeI(nkGotoState, finallyBody.info)
|
||||
body = ctx.transformBreaksAndContinuesInWhile(body, result, gotoOut)
|
||||
body = ctx.transformClosureIteratorBody(body, result)
|
||||
|
||||
block: # Create initial states.
|
||||
let oldExcHandlingState = ctx.curExcHandlingState
|
||||
ctx.curExcHandlingState = exceptIdx
|
||||
let realTryIdx = ctx.newState(tryBody, result)
|
||||
assert(realTryIdx == tryIdx)
|
||||
elifBranch.add(body)
|
||||
ifNode.add(elifBranch)
|
||||
|
||||
if exceptBody.kind != nkEmpty:
|
||||
ctx.curExcHandlingState = finallyIdx
|
||||
let realExceptIdx = ctx.newState(exceptBody, nil)
|
||||
assert(realExceptIdx == -exceptIdx)
|
||||
let elseBranch = newTree(nkElse, gotoOut)
|
||||
ifNode.add(elseBranch)
|
||||
s.add(ifNode)
|
||||
|
||||
ctx.curExcHandlingState = oldExcHandlingState
|
||||
let realFinallyIdx = ctx.newState(finallyBody, outToFinally)
|
||||
assert(realFinallyIdx == finallyIdx)
|
||||
of nkBlockStmt:
|
||||
result[1] = addGotoOut(result[1], gotoOut)
|
||||
result[1] = ctx.transformBreaksInBlock(result[1], result[0], gotoOut)
|
||||
result[1] = ctx.transformClosureIteratorBody(result[1], gotoOut)
|
||||
|
||||
block: # Subdivide the states
|
||||
let oldNearestFinally = ctx.nearestFinally
|
||||
ctx.nearestFinally = finallyIdx
|
||||
of nkTryStmt, nkHiddenTryStmt:
|
||||
# See explanation above about how this works
|
||||
ctx.hasExceptions = true
|
||||
|
||||
let oldExcHandlingState = ctx.curExcHandlingState
|
||||
|
||||
ctx.curExcHandlingState = exceptIdx
|
||||
|
||||
if ctx.transformReturnsInTry(tryBody) != tryBody:
|
||||
internalError(ctx.g.config, "transformReturnsInTry != tryBody")
|
||||
if ctx.transformClosureIteratorBody(tryBody, outToFinally) != tryBody:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != tryBody")
|
||||
|
||||
ctx.curExcHandlingState = finallyIdx
|
||||
ctx.addElseToExcept(exceptBody)
|
||||
if ctx.transformReturnsInTry(exceptBody) != exceptBody:
|
||||
internalError(ctx.g.config, "transformReturnsInTry != exceptBody")
|
||||
if ctx.transformClosureIteratorBody(exceptBody, outToFinally) != exceptBody:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != exceptBody")
|
||||
|
||||
ctx.curExcHandlingState = oldExcHandlingState
|
||||
ctx.nearestFinally = oldNearestFinally
|
||||
if ctx.transformClosureIteratorBody(finallyBody, gotoOut) != finallyBody:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != finallyBody")
|
||||
|
||||
of nkGotoState, nkForStmt:
|
||||
internalError(ctx.g.config, "closure iter " & $n.kind)
|
||||
result = newNodeI(nkGotoState, n.info)
|
||||
var tryBody = toStmtList(n[0])
|
||||
var exceptBody = ctx.collectExceptState(n)
|
||||
var finallyBody = newTree(nkStmtList, getFinallyNode(ctx, n))
|
||||
finallyBody = ctx.transformReturnsInTry(finallyBody)
|
||||
finallyBody.add(ctx.newEndFinallyNode(finallyBody.info))
|
||||
|
||||
# The following index calculation is based on the knowledge how state
|
||||
# indexes are assigned
|
||||
let tryIdx = ctx.states.len
|
||||
var exceptIdx, finallyIdx: int
|
||||
if exceptBody.kind != nkEmpty:
|
||||
exceptIdx = -(tryIdx + 1)
|
||||
finallyIdx = tryIdx + 2
|
||||
else:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
|
||||
exceptIdx = tryIdx + 1
|
||||
finallyIdx = tryIdx + 1
|
||||
|
||||
let outToFinally = newNodeI(nkGotoState, finallyBody.info)
|
||||
|
||||
block: # Create initial states.
|
||||
let oldExcHandlingState = ctx.curExcHandlingState
|
||||
ctx.curExcHandlingState = exceptIdx
|
||||
let realTryIdx = ctx.newState(tryBody, result)
|
||||
assert(realTryIdx == tryIdx)
|
||||
|
||||
if exceptBody.kind != nkEmpty:
|
||||
ctx.curExcHandlingState = finallyIdx
|
||||
let realExceptIdx = ctx.newState(exceptBody, nil)
|
||||
assert(realExceptIdx == -exceptIdx)
|
||||
|
||||
ctx.curExcHandlingState = oldExcHandlingState
|
||||
let realFinallyIdx = ctx.newState(finallyBody, outToFinally)
|
||||
assert(realFinallyIdx == finallyIdx)
|
||||
|
||||
block: # Subdivide the states
|
||||
let oldNearestFinally = ctx.nearestFinally
|
||||
ctx.nearestFinally = finallyIdx
|
||||
|
||||
let oldExcHandlingState = ctx.curExcHandlingState
|
||||
|
||||
ctx.curExcHandlingState = exceptIdx
|
||||
|
||||
if ctx.transformReturnsInTry(tryBody) != tryBody:
|
||||
internalError(ctx.g.config, "transformReturnsInTry != tryBody")
|
||||
if ctx.transformClosureIteratorBody(tryBody, outToFinally) != tryBody:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != tryBody")
|
||||
|
||||
ctx.curExcHandlingState = finallyIdx
|
||||
ctx.addElseToExcept(exceptBody)
|
||||
if ctx.transformReturnsInTry(exceptBody) != exceptBody:
|
||||
internalError(ctx.g.config, "transformReturnsInTry != exceptBody")
|
||||
if ctx.transformClosureIteratorBody(exceptBody, outToFinally) != exceptBody:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != exceptBody")
|
||||
|
||||
ctx.curExcHandlingState = oldExcHandlingState
|
||||
ctx.nearestFinally = oldNearestFinally
|
||||
if ctx.transformClosureIteratorBody(finallyBody, gotoOut) != finallyBody:
|
||||
internalError(ctx.g.config, "transformClosureIteratorBody != finallyBody")
|
||||
|
||||
of nkGotoState, nkForStmt:
|
||||
internalError(ctx.g.config, "closure iter " & $n.kind)
|
||||
|
||||
else:
|
||||
for i in 0 ..< n.len:
|
||||
n[i] = ctx.transformClosureIteratorBody(n[i], gotoOut)
|
||||
|
||||
proc stateFromGotoState(n: PNode): int =
|
||||
assert(n.kind == nkGotoState)
|
||||
@@ -1149,7 +1148,6 @@ proc newCatchBody(ctx: var Ctx, info: TLineInfo): PNode {.inline.} =
|
||||
|
||||
# :state = exceptionTable[:state]
|
||||
block:
|
||||
|
||||
# exceptionTable[:state]
|
||||
let getNextState = newTree(nkBracketExpr,
|
||||
ctx.createExceptionTable(),
|
||||
|
||||
@@ -17,6 +17,7 @@ template bootSwitch(name, expr, userString) =
|
||||
const name = if expr: " " & userString else: ""
|
||||
|
||||
bootSwitch(usedRelease, defined(release), "-d:release")
|
||||
bootSwitch(usedDanger, defined(danger), "-d:danger")
|
||||
bootSwitch(usedGnuReadline, defined(useLinenoise), "-d:useLinenoise")
|
||||
bootSwitch(usedBoehm, defined(boehmgc), "--gc:boehm")
|
||||
bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
|
||||
@@ -96,7 +97,7 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) =
|
||||
when gitHash.len == 40:
|
||||
msgWriteln(conf, "git hash: " & gitHash, {msgStdout})
|
||||
|
||||
msgWriteln(conf, "active boot switches:" & usedRelease &
|
||||
msgWriteln(conf, "active boot switches:" & usedRelease & usedDanger &
|
||||
usedTinyC & usedGnuReadline & usedNativeStacktrace &
|
||||
usedFFI & usedBoehm & usedMarkAndSweep & usedGenerational & usedGoGC & usedNoGC,
|
||||
{msgStdout})
|
||||
@@ -275,8 +276,9 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
of "fieldchecks": result = contains(conf.options, optFieldCheck)
|
||||
of "rangechecks": result = contains(conf.options, optRangeCheck)
|
||||
of "boundchecks": result = contains(conf.options, optBoundsCheck)
|
||||
of "refchecks": result = contains(conf.options, optRefCheck)
|
||||
of "overflowchecks": result = contains(conf.options, optOverflowCheck)
|
||||
of "movechecks": result = contains(conf.options, optMoveCheck)
|
||||
of "stylechecks": result = contains(conf.options, optStyleCheck)
|
||||
of "linedir": result = contains(conf.options, optLineDir)
|
||||
of "assertions", "a": result = contains(conf.options, optAssert)
|
||||
of "run", "r": result = contains(conf.globalOptions, optRun)
|
||||
@@ -529,8 +531,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "fieldchecks": processOnOffSwitch(conf, {optFieldCheck}, arg, pass, info)
|
||||
of "rangechecks": processOnOffSwitch(conf, {optRangeCheck}, arg, pass, info)
|
||||
of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info)
|
||||
of "refchecks": processOnOffSwitch(conf, {optRefCheck}, arg, pass, info)
|
||||
of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info)
|
||||
of "movechecks": processOnOffSwitch(conf, {optMoveCheck}, arg, pass, info)
|
||||
of "stylechecks": processOnOffSwitch(conf, {optStyleCheck}, arg, pass, info)
|
||||
of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
|
||||
of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
|
||||
of "deadcodeelim": discard # deprecated, dead code elim always on
|
||||
@@ -806,7 +809,7 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
|
||||
argsCount: var int; config: ConfigRef): bool =
|
||||
if argsCount == 0:
|
||||
# nim filename.nims is the same as "nim e filename.nims":
|
||||
if p.key.endswith(".nims"):
|
||||
if p.key.endsWith(".nims"):
|
||||
config.command = "e"
|
||||
incl(config.globalOptions, optWasNimscript)
|
||||
config.projectName = unixToNativePath(p.key)
|
||||
|
||||
@@ -86,6 +86,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasSignatureHashInMacro")
|
||||
defineSymbol("nimHasDefault")
|
||||
defineSymbol("nimMacrosSizealignof")
|
||||
defineSymbol("nimNoZeroExtendMagic")
|
||||
for f in low(Feature)..high(Feature):
|
||||
defineSymbol("nimHas" & $f)
|
||||
|
||||
@@ -93,3 +94,6 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasWarning" & s)
|
||||
for s in HintsToStr:
|
||||
defineSymbol("nimHasHint" & s)
|
||||
|
||||
defineSymbol("nimFixedOwned")
|
||||
defineSymbol("nimHasStyleChecks")
|
||||
|
||||
@@ -119,10 +119,10 @@ use(x)
|
||||
|
||||
Generates:
|
||||
|
||||
L0: fork L1
|
||||
L0: fork lab1
|
||||
join L0 # patched.
|
||||
goto Louter
|
||||
L1:
|
||||
lab1:
|
||||
def x
|
||||
join L0
|
||||
Louter:
|
||||
@@ -265,14 +265,14 @@ proc genLabel(c: Con): TPosition =
|
||||
|
||||
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
|
||||
let dist = p.int - c.code.len
|
||||
doAssert(-0x7fff < dist and dist < 0x7fff)
|
||||
doAssert(low(int) div 2 + 1 < dist and dist < high(int) div 2)
|
||||
c.code.add Instr(n: n, kind: goto, dest: dist)
|
||||
|
||||
proc patch(c: var Con, p: TPosition) =
|
||||
# patch with current index
|
||||
let p = p.int
|
||||
let diff = c.code.len - p
|
||||
doAssert(-0x7fff < diff and diff < 0x7fff)
|
||||
doAssert(low(int) div 2 + 1 < diff and diff < high(int) div 2)
|
||||
c.code[p].dest = diff
|
||||
|
||||
proc popBlock(c: var Con; oldLen: int) =
|
||||
@@ -354,24 +354,24 @@ when true:
|
||||
else:
|
||||
|
||||
proc genWhile(c: var Con; n: PNode) =
|
||||
# L1:
|
||||
# lab1:
|
||||
# cond, tmp
|
||||
# fork tmp, L2
|
||||
# fork tmp, lab2
|
||||
# body
|
||||
# jmp L1
|
||||
# L2:
|
||||
# jmp lab1
|
||||
# lab2:
|
||||
let oldForksLen = c.forks.len
|
||||
let L1 = c.genLabel
|
||||
let lab1 = c.genLabel
|
||||
withBlock(nil):
|
||||
if isTrue(n.sons[0]):
|
||||
c.gen(n.sons[1])
|
||||
c.jmpBack(n, L1)
|
||||
c.jmpBack(n, lab1)
|
||||
else:
|
||||
c.gen(n.sons[0])
|
||||
let L2 = c.forkI(n)
|
||||
let lab2 = c.forkI(n)
|
||||
c.gen(n.sons[1])
|
||||
c.jmpBack(n, L1)
|
||||
c.patch(L2)
|
||||
c.jmpBack(n, lab1)
|
||||
c.patch(lab2)
|
||||
setLen(c.forks, oldForksLen)
|
||||
|
||||
proc genBlock(c: var Con; n: PNode) =
|
||||
@@ -383,23 +383,23 @@ proc genJoins(c: var Con; n: PNode) =
|
||||
|
||||
proc genBreak(c: var Con; n: PNode) =
|
||||
genJoins(c, n)
|
||||
let L1 = c.gotoI(n)
|
||||
let lab1 = c.gotoI(n)
|
||||
if n.sons[0].kind == nkSym:
|
||||
#echo cast[int](n.sons[0].sym)
|
||||
for i in countdown(c.blocks.len-1, 0):
|
||||
if c.blocks[i].label == n.sons[0].sym:
|
||||
c.blocks[i].fixups.add L1
|
||||
c.blocks[i].fixups.add lab1
|
||||
return
|
||||
#globalError(n.info, "VM problem: cannot find 'break' target")
|
||||
else:
|
||||
c.blocks[c.blocks.high].fixups.add L1
|
||||
c.blocks[c.blocks.high].fixups.add lab1
|
||||
|
||||
template forkT(n, body) =
|
||||
let oldLen = c.forks.len
|
||||
let L1 = c.forkI(n)
|
||||
let lab1 = c.forkI(n)
|
||||
body
|
||||
c.patch(L1)
|
||||
c.joinI(L1, n)
|
||||
c.patch(lab1)
|
||||
c.joinI(lab1, n)
|
||||
setLen(c.forks, oldLen)
|
||||
|
||||
proc genIf(c: var Con, n: PNode) =
|
||||
@@ -415,15 +415,15 @@ proc genIf(c: var Con, n: PNode) =
|
||||
D
|
||||
|
||||
cond
|
||||
fork L1
|
||||
fork lab1
|
||||
A
|
||||
goto Lend
|
||||
L1:
|
||||
lab1:
|
||||
condB
|
||||
fork L2
|
||||
fork lab2
|
||||
B
|
||||
goto Lend2
|
||||
L2:
|
||||
lab2:
|
||||
condC
|
||||
fork L3
|
||||
C
|
||||
@@ -457,23 +457,23 @@ proc genIf(c: var Con, n: PNode) =
|
||||
|
||||
proc genAndOr(c: var Con; n: PNode) =
|
||||
# asgn dest, a
|
||||
# fork L1
|
||||
# fork lab1
|
||||
# asgn dest, b
|
||||
# L1:
|
||||
# lab1:
|
||||
# join F1
|
||||
c.gen(n.sons[1])
|
||||
forkT(n):
|
||||
c.gen(n.sons[2])
|
||||
|
||||
proc genCase(c: var Con; n: PNode) =
|
||||
# if (!expr1) goto L1;
|
||||
# if (!expr1) goto lab1;
|
||||
# thenPart
|
||||
# goto LEnd
|
||||
# L1:
|
||||
# if (!expr2) goto L2;
|
||||
# lab1:
|
||||
# if (!expr2) goto lab2;
|
||||
# thenPart2
|
||||
# goto LEnd
|
||||
# L2:
|
||||
# lab2:
|
||||
# elsePart
|
||||
# Lend:
|
||||
let isExhaustive = skipTypes(n.sons[0].typ,
|
||||
@@ -619,8 +619,8 @@ proc aliases(obj, field: PNode): bool =
|
||||
break
|
||||
return false
|
||||
|
||||
proc instrTargets*(ins: Instr; loc: PNode): bool =
|
||||
assert ins.kind in {def, use}
|
||||
proc useInstrTargets*(ins: Instr; loc: PNode): bool =
|
||||
assert ins.kind == use
|
||||
if ins.sym != nil and loc.kind == nkSym:
|
||||
result = ins.sym == loc.sym
|
||||
else:
|
||||
@@ -633,6 +633,20 @@ proc instrTargets*(ins: Instr; loc: PNode): bool =
|
||||
# use x; question does it affect 'x.f'? Yes.
|
||||
result = aliases(ins.n, loc) or aliases(loc, ins.n)
|
||||
|
||||
proc defInstrTargets*(ins: Instr; loc: PNode): bool =
|
||||
assert ins.kind == def
|
||||
if ins.sym != nil and loc.kind == nkSym:
|
||||
result = ins.sym == loc.sym
|
||||
else:
|
||||
result = ins.n == loc or sameTrees(ins.n, loc)
|
||||
if not result:
|
||||
# We can come here if loc is 'x.f' and ins.n is 'x' or the other way round.
|
||||
# def x.f; question: does it affect the full 'x'? No.
|
||||
# def x; question: does it affect the 'x.f'? Yes.
|
||||
# use x.f; question: does it affect the full 'x'? No.
|
||||
# use x; question does it affect 'x.f'? Yes.
|
||||
result = aliases(ins.n, loc)
|
||||
|
||||
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
||||
var n = orig
|
||||
while true:
|
||||
@@ -645,7 +659,7 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
||||
# pointer indirection.
|
||||
n = n[0]
|
||||
return n.kind == nkSym and n.sym.owner == owner and (isSinkParam(n.sym) or
|
||||
n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned, tyVar})
|
||||
n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
|
||||
of nkBracketExpr:
|
||||
let x = n[0]
|
||||
if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple:
|
||||
@@ -700,9 +714,9 @@ proc genCall(c: var Con; n: PNode) =
|
||||
# every call can potentially raise:
|
||||
if c.inTryStmt > 0 and canRaise(n[0]):
|
||||
# we generate the instruction sequence:
|
||||
# fork L1
|
||||
# fork lab1
|
||||
# goto exceptionHandler (except or finally)
|
||||
# L1:
|
||||
# lab1:
|
||||
# join F1
|
||||
let endGoto = c.forkI(n)
|
||||
c.tryStmtFixups.add c.gotoI(n)
|
||||
@@ -766,7 +780,7 @@ proc gen(c: var Con; n: PNode) =
|
||||
of nkBreakStmt: genBreak(c, n)
|
||||
of nkTryStmt, nkHiddenTryStmt: genTry(c, n)
|
||||
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
|
||||
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
|
||||
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt:
|
||||
for x in n: gen(c, x)
|
||||
of nkPragmaBlock: gen(c, n.lastSon)
|
||||
of nkDiscardStmt, nkObjDownConv, nkObjUpConv: gen(c, n.sons[0])
|
||||
|
||||
@@ -680,7 +680,9 @@ 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 defaultBranch = if NimPatch mod 2 == 1: "devel" else: "master"
|
||||
let defaultBranch =
|
||||
if NimPatch mod 2 == 1: "devel"
|
||||
else: "version-$1-$2" % [$NimMajor, $NimMinor]
|
||||
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,
|
||||
|
||||
@@ -416,7 +416,7 @@ proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
|
||||
localError(conf, info, "unknown C compiler: '$1'. Available options are: $2" % [ccname, ccList])
|
||||
conf.compileOptions = getConfigVar(conf, conf.cCompiler, ".options.always")
|
||||
conf.linkOptions = ""
|
||||
conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
|
||||
conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path")
|
||||
for i in low(CC) .. high(CC): undefSymbol(conf.symbols, CC[i].name)
|
||||
defineSymbol(conf.symbols, CC[conf.cCompiler].name)
|
||||
|
||||
@@ -443,10 +443,10 @@ proc initVars*(conf: ConfigRef) =
|
||||
defineSymbol(conf.symbols, CC[conf.cCompiler].name)
|
||||
addCompileOption(conf, getConfigVar(conf, conf.cCompiler, ".options.always"))
|
||||
#addLinkOption(getConfigVar(cCompiler, ".options.linker"))
|
||||
if len(conf.ccompilerpath) == 0:
|
||||
conf.ccompilerpath = getConfigVar(conf, conf.cCompiler, ".path")
|
||||
if len(conf.cCompilerPath) == 0:
|
||||
conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path")
|
||||
|
||||
proc completeCFilePath*(conf: ConfigRef; cfile: AbsoluteFile,
|
||||
proc completeCfilePath*(conf: ConfigRef; cfile: AbsoluteFile,
|
||||
createSubDir: bool = true): AbsoluteFile =
|
||||
result = completeGeneratedFilePath(conf, cfile, createSubDir)
|
||||
|
||||
@@ -591,7 +591,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, isMainFile = false): str
|
||||
for includeDir in items(conf.cIncludes):
|
||||
includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
|
||||
|
||||
compilePattern = joinPath(conf.ccompilerpath, exe)
|
||||
compilePattern = joinPath(conf.cCompilerPath, exe)
|
||||
else:
|
||||
includeCmd = ""
|
||||
compilePattern = getCompilerExe(conf, c, cfile.cname)
|
||||
@@ -606,7 +606,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, isMainFile = false): str
|
||||
if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf):
|
||||
toObjFile(conf, cf).string
|
||||
else:
|
||||
completeCFilePath(conf, toObjFile(conf, cf)).string
|
||||
completeCfilePath(conf, toObjFile(conf, cf)).string
|
||||
elif noAbsolutePaths(conf):
|
||||
extractFilename(cfile.obj.string)
|
||||
else:
|
||||
@@ -666,11 +666,11 @@ proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
|
||||
|
||||
proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) =
|
||||
var c = Cfile(nimname: splitFile(filename).name, cname: filename,
|
||||
obj: toObjFile(conf, completeCFilePath(conf, filename, false)),
|
||||
obj: toObjFile(conf, completeCfilePath(conf, filename, false)),
|
||||
flags: {CfileFlag.External})
|
||||
addExternalFileToCompile(conf, c)
|
||||
|
||||
proc compileCFiles(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var TStringSeq,
|
||||
proc compileCFiles(conf: ConfigRef; list: CfileList, script: var Rope, cmds: var TStringSeq,
|
||||
prettyCmds: var TStringSeq) =
|
||||
var currIdx = 0
|
||||
for it in list:
|
||||
@@ -704,7 +704,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||
# bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
|
||||
if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
|
||||
if noAbsolutePaths(conf): result = linkerExe
|
||||
else: result = joinPath(conf.cCompilerpath, linkerExe)
|
||||
else: result = joinPath(conf.cCompilerPath, linkerExe)
|
||||
let buildgui = if optGenGuiApp in conf.globalOptions and conf.target.targetOS == osWindows:
|
||||
CC[conf.cCompiler].buildGui
|
||||
else:
|
||||
@@ -856,7 +856,7 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
|
||||
finally:
|
||||
removeFile(linkerArgs)
|
||||
|
||||
proc getObjFilePath(conf: ConfigRef, f: CFile): string =
|
||||
proc getObjFilePath(conf: ConfigRef, f: Cfile): string =
|
||||
if noAbsolutePaths(conf): f.obj.extractFilename
|
||||
else: f.obj.string
|
||||
|
||||
@@ -1041,7 +1041,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) =
|
||||
echo getCurrentException().getStackTrace()
|
||||
quit "error evaluating JSON file: " & jsonFile.string
|
||||
|
||||
proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
|
||||
proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
|
||||
for it in list:
|
||||
addf(result, "--file:r\"$1\"$N", [rope(it.cname.string)])
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
|
||||
var readSuccessful = false
|
||||
try:
|
||||
var p = startProcess(cmd, workingDir,
|
||||
options={poEvalCommand, poStderrToStdout})
|
||||
options={poEvalCommand, poStdErrToStdOut})
|
||||
if input.len != 0:
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
@@ -49,7 +49,7 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
|
||||
else:
|
||||
try:
|
||||
var p = startProcess(cmd, workingDir,
|
||||
options={poEvalCommand, poStderrToStdout})
|
||||
options={poEvalCommand, poStdErrToStdOut})
|
||||
if input.len != 0:
|
||||
p.inputStream.write(input)
|
||||
p.inputStream.close()
|
||||
|
||||
@@ -981,9 +981,10 @@ proc buildElse(n: PNode; o: Operators): PNode =
|
||||
var s = newNodeIT(nkCurly, n.info, settype(n.sons[0]))
|
||||
for i in 1..n.len-2:
|
||||
let branch = n.sons[i]
|
||||
assert branch.kind == nkOfBranch
|
||||
for j in 0..branch.len-2:
|
||||
s.add(branch.sons[j])
|
||||
assert branch.kind != nkElse
|
||||
if branch.kind == nkOfBranch:
|
||||
for j in 0..branch.len-2:
|
||||
s.add(branch.sons[j])
|
||||
result = newNodeI(nkCall, n.info, 3)
|
||||
result.sons[0] = newSymNode(o.opContains)
|
||||
result.sons[1] = s
|
||||
|
||||
@@ -79,19 +79,19 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
||||
else:
|
||||
when false:
|
||||
if s.kind == skStub: loadStub(s)
|
||||
if s.kind notin ExportableSymKinds:
|
||||
internalError(c.config, n.info, "importSymbol: 2")
|
||||
let multiImport = s.kind notin ExportableSymKinds or s.kind in skProcKinds
|
||||
# for an enumeration we have to add all identifiers
|
||||
case s.kind
|
||||
of skProcKinds:
|
||||
if multiImport:
|
||||
# for a overloadable syms add all overloaded routines
|
||||
var it: TIdentIter
|
||||
var e = initIdentIter(it, fromMod.tab, s.name)
|
||||
while e != nil:
|
||||
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
|
||||
rawImportSymbol(c, e)
|
||||
if s.kind in ExportableSymKinds:
|
||||
rawImportSymbol(c, e)
|
||||
e = nextIdentIter(it, fromMod.tab)
|
||||
else: rawImportSymbol(c, s)
|
||||
else:
|
||||
rawImportSymbol(c, s)
|
||||
suggestSym(c.config, n.info, s, c.graph.usageSym, false)
|
||||
|
||||
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
||||
@@ -139,7 +139,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
|
||||
|
||||
proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym =
|
||||
let f = checkModuleName(c.config, n)
|
||||
if f != InvalidFileIDX:
|
||||
if f != InvalidFileIdx:
|
||||
let L = c.graph.importStack.len
|
||||
let recursion = c.graph.importStack.find(f)
|
||||
c.graph.importStack.add f
|
||||
|
||||
@@ -150,10 +150,11 @@ type
|
||||
graph: ModuleGraph
|
||||
emptyNode: PNode
|
||||
otherRead: PNode
|
||||
inLoop: int
|
||||
uninit: IntSet # set of uninit'ed vars
|
||||
uninitComputed: bool
|
||||
|
||||
const toDebug = ""
|
||||
const toDebug = "" # "server" # "serverNimAsyncContinue"
|
||||
|
||||
template dbg(body) =
|
||||
when toDebug.len > 0:
|
||||
@@ -165,12 +166,12 @@ proc isLastRead(location: PNode; c: var Con; pc, comesFrom: int): int =
|
||||
while pc < c.g.len:
|
||||
case c.g[pc].kind
|
||||
of def:
|
||||
if instrTargets(c.g[pc], location):
|
||||
if defInstrTargets(c.g[pc], location):
|
||||
# the path lead to a redefinition of 's' --> abandon it.
|
||||
return high(int)
|
||||
inc pc
|
||||
of use:
|
||||
if instrTargets(c.g[pc], location):
|
||||
if useInstrTargets(c.g[pc], location):
|
||||
c.otherRead = c.g[pc].n
|
||||
return -1
|
||||
inc pc
|
||||
@@ -310,6 +311,8 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
|
||||
m.add "; try to make "
|
||||
m.add renderTree(ri)
|
||||
m.add " a 'sink' parameter"
|
||||
m.add "; routine: "
|
||||
m.add c.owner.name.s
|
||||
localError(c.graph.config, ri.info, errGenerated, m)
|
||||
|
||||
proc makePtrType(c: Con, baseType: PType): PType =
|
||||
@@ -337,10 +340,23 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
|
||||
addrExp.add(dest)
|
||||
result = newTree(nkCall, newSymNode(op), addrExp)
|
||||
|
||||
when false:
|
||||
proc preventMoveRef(dest, ri: PNode): bool =
|
||||
let lhs = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
var ri = ri
|
||||
if ri.kind in nkCallKinds and ri[0].kind == nkSym and ri[0].sym.magic == mUnown:
|
||||
ri = ri[1]
|
||||
let rhs = ri.typ.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
result = lhs.kind == tyRef and rhs.kind == tyOwned
|
||||
|
||||
proc canBeMoved(t: PType): bool {.inline.} =
|
||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
result = t.kind != tyRef and t.attachedOps[attachedSink] != nil
|
||||
|
||||
proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
let k = if t.attachedOps[attachedSink] != nil: attachedSink
|
||||
else: attachedAsgn
|
||||
else: attachedAsgn
|
||||
if t.attachedOps[k] != nil:
|
||||
result = genOp(c, t, k, dest, ri)
|
||||
else:
|
||||
@@ -349,8 +365,10 @@ proc genSink(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||
# we generate a fast assignment in this case:
|
||||
result = newTree(nkFastAsgn, dest)
|
||||
|
||||
proc genCopy(c: Con; t: PType; dest, ri: PNode): PNode =
|
||||
proc genCopy(c: var Con; t: PType; dest, ri: PNode): PNode =
|
||||
if tfHasOwned in t.flags:
|
||||
# try to improve the error message here:
|
||||
if c.otherRead == nil: discard isLastRead(ri, c)
|
||||
checkForErrorPragma(c, t, ri, "=")
|
||||
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
result = genOp(c, t, attachedAsgn, dest, ri)
|
||||
@@ -410,7 +428,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode =
|
||||
add(v, vpart)
|
||||
|
||||
result.add v
|
||||
result.add genWasMoved(n, c)
|
||||
result.add genWasMoved(skipConv(n), c)
|
||||
result.add tempAsNode
|
||||
|
||||
proc sinkParamIsLastReadCheck(c: var Con, s: PNode) =
|
||||
@@ -448,6 +466,25 @@ proc passCopyToSink(n: PNode; c: var Con): PNode =
|
||||
result.add newTree(nkAsgn, tmp, p(n, c))
|
||||
result.add tmp
|
||||
|
||||
proc isDangerousSeq(t: PType): bool {.inline.} =
|
||||
let t = t.skipTypes(abstractInst)
|
||||
result = t.kind == tySequence and tfHasOwned notin t.sons[0].flags
|
||||
|
||||
proc containsConstSeq(n: PNode): bool =
|
||||
if n.kind == nkBracket and n.len > 0 and n.typ != nil and isDangerousSeq(n.typ):
|
||||
return true
|
||||
result = false
|
||||
case n.kind
|
||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
||||
result = containsConstSeq(n[1])
|
||||
of nkObjConstr, nkClosure:
|
||||
for i in 1 ..< n.len:
|
||||
if containsConstSeq(n[i]): return true
|
||||
of nkCurly, nkBracket, nkPar, nkTupleConstr:
|
||||
for i in 0 ..< n.len:
|
||||
if containsConstSeq(n[i]): return true
|
||||
else: discard
|
||||
|
||||
proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||
template pArgIfTyped(argPart: PNode): PNode =
|
||||
# typ is nil if we are in if/case expr branch with noreturn
|
||||
@@ -464,7 +501,12 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||
result.add arg[0]
|
||||
for i in 1..<arg.len:
|
||||
result.add pArg(arg[i], c, i < L and isSinkTypeForParam(parameters[i]))
|
||||
elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkBracket, nkCharLit..nkTripleStrLit}:
|
||||
elif arg.containsConstSeq:
|
||||
# const sequences are not mutable and so we need to pass a copy to the
|
||||
# sink parameter (bug #11524). Note that the string implemenation is
|
||||
# different and can deal with 'const string sunk into var'.
|
||||
result = passCopyToSink(arg, c)
|
||||
elif arg.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkCharLit..nkTripleStrLit}:
|
||||
discard "object construction to sink parameter: nothing to do"
|
||||
result = arg
|
||||
elif arg.kind == nkSym and isSinkParam(arg.sym):
|
||||
@@ -472,7 +514,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||
# to disable the destructor which we have not elided
|
||||
sinkParamIsLastReadCheck(c, arg)
|
||||
result = destructiveMoveVar(arg, c)
|
||||
elif arg.kind == nkSym and arg.sym.kind in InterestingSyms and isLastRead(arg, c):
|
||||
elif isAnalysableFieldAccess(arg, c.owner) and isLastRead(arg, c):
|
||||
# it is the last read, can be sinked. We need to reset the memory
|
||||
# to disable the destructor which we have not elided
|
||||
result = destructiveMoveVar(arg, c)
|
||||
@@ -500,7 +542,7 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode =
|
||||
result.add p(arg[0], c)
|
||||
for i in 1..<arg.len:
|
||||
var branch: PNode
|
||||
if arg[i].kind == nkOfbranch:
|
||||
if arg[i].kind == nkOfBranch:
|
||||
branch = arg[i] # of branch conditions are constants
|
||||
branch[^1] = pArgIfTyped(arg[i][^1])
|
||||
elif arg[i].kind in {nkElifBranch, nkElifExpr}:
|
||||
@@ -580,7 +622,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
result.add p(ri[0], c)
|
||||
for i in 1..<ri.len:
|
||||
var branch: PNode
|
||||
if ri[i].kind == nkOfbranch:
|
||||
if ri[i].kind == nkOfBranch:
|
||||
branch = ri[i] # of branch conditions are constants
|
||||
branch[^1] = moveOrCopyIfTyped(ri[i][^1])
|
||||
elif ri[i].kind in {nkElifBranch, nkElifExpr}:
|
||||
@@ -593,7 +635,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
result.add branch
|
||||
of nkBracket:
|
||||
# array constructor
|
||||
result = genSink(c, dest.typ, dest, ri)
|
||||
if ri.len > 0 and isDangerousSeq(ri.typ):
|
||||
result = genCopy(c, dest.typ, dest, ri)
|
||||
else:
|
||||
result = genSink(c, dest.typ, dest, ri)
|
||||
let ri2 = copyTree(ri)
|
||||
for i in 0..<ri.len:
|
||||
# everything that is passed to an array constructor is consumed,
|
||||
@@ -611,7 +656,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
of nkTupleConstr, nkClosure:
|
||||
result = genSink(c, dest.typ, dest, ri)
|
||||
let ri2 = copyTree(ri)
|
||||
for i in 0..<ri.len:
|
||||
for i in ord(ri.kind == nkClosure)..<ri.len:
|
||||
# everything that is passed to an tuple constructor is consumed,
|
||||
# so these all act like 'sink' parameters:
|
||||
if ri[i].kind == nkExprColonExpr:
|
||||
@@ -629,7 +674,8 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
var snk = genSink(c, dest.typ, dest, ri)
|
||||
snk.add ri
|
||||
result = newTree(nkStmtList, snk, genWasMoved(ri, c))
|
||||
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and isLastRead(ri, c):
|
||||
elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
|
||||
isLastRead(ri, c) and canBeMoved(dest.typ):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
var snk = genSink(c, dest.typ, dest, ri)
|
||||
snk.add ri
|
||||
@@ -661,7 +707,8 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
|
||||
result = genCopy(c, dest.typ, dest, ri)
|
||||
result.add p(ri, c)
|
||||
else:
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
|
||||
if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
|
||||
canBeMoved(dest.typ):
|
||||
# Rule 3: `=sink`(x, z); wasMoved(z)
|
||||
var snk = genSink(c, dest.typ, dest, ri)
|
||||
snk.add ri
|
||||
@@ -721,7 +768,7 @@ proc p(n: PNode; c: var Con): PNode =
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
let L = it.len
|
||||
let ri = it[L-1]
|
||||
var ri = it[L-1]
|
||||
if it.kind == nkVarTuple and hasDestructor(ri.typ):
|
||||
let x = lowerTupleUnpacking(c.graph, it, c.owner)
|
||||
result.add p(x, c)
|
||||
@@ -735,6 +782,8 @@ proc p(n: PNode; c: var Con): PNode =
|
||||
# make sure it's destroyed at the end of the proc:
|
||||
if not isUnpackedTuple(it[0].sym):
|
||||
c.destroys.add genDestroy(c, v.typ, v)
|
||||
if ri.kind == nkEmpty and c.inLoop > 0:
|
||||
ri = genDefaultCall(v.typ, c, v.info)
|
||||
if ri.kind != nkEmpty:
|
||||
let r = moveOrCopy(v, ri, c)
|
||||
result.add r
|
||||
@@ -757,7 +806,7 @@ proc p(n: PNode; c: var Con): PNode =
|
||||
else:
|
||||
result = n
|
||||
of nkAsgn, nkFastAsgn:
|
||||
if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda, nkClosure}:
|
||||
if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
|
||||
result = moveOrCopy(n[0], n[1], c)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
@@ -777,20 +826,31 @@ proc p(n: PNode; c: var Con): PNode =
|
||||
result[1][0] = p(result[1][0], c)
|
||||
of nkRaiseStmt:
|
||||
if optNimV2 in c.graph.config.globalOptions and n[0].kind != nkEmpty:
|
||||
let t = n[0].typ
|
||||
let tmp = getTemp(c, t, n.info)
|
||||
var m = genCopyNoCheck(c, t, tmp, n[0])
|
||||
if n[0].kind in nkCallKinds:
|
||||
let call = copyNode(n[0])
|
||||
recurse(n[0], call)
|
||||
result = copyNode(n)
|
||||
result.add call
|
||||
else:
|
||||
let t = n[0].typ
|
||||
let tmp = getTemp(c, t, n.info)
|
||||
var m = genCopyNoCheck(c, t, tmp, n[0])
|
||||
|
||||
m.add p(n[0], c)
|
||||
result = newTree(nkStmtList, genWasMoved(tmp, c), m)
|
||||
var toDisarm = n[0]
|
||||
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
|
||||
if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner:
|
||||
result.add genWasMoved(toDisarm, c)
|
||||
result.add newTree(nkRaiseStmt, tmp)
|
||||
m.add p(n[0], c)
|
||||
result = newTree(nkStmtList, genWasMoved(tmp, c), m)
|
||||
var toDisarm = n[0]
|
||||
if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
|
||||
if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner:
|
||||
result.add genWasMoved(toDisarm, c)
|
||||
result.add newTree(nkRaiseStmt, tmp)
|
||||
else:
|
||||
result = copyNode(n)
|
||||
recurse(n, result)
|
||||
of nkForStmt, nkParForStmt, nkWhileStmt:
|
||||
inc c.inLoop
|
||||
result = copyNode(n)
|
||||
recurse(n, result)
|
||||
dec c.inLoop
|
||||
else:
|
||||
result = copyNode(n)
|
||||
recurse(n, result)
|
||||
|
||||
516
compiler/int128.nim
Normal file
516
compiler/int128.nim
Normal file
@@ -0,0 +1,516 @@
|
||||
|
||||
|
||||
type
|
||||
Int128* = object
|
||||
udata: array[4,uint32]
|
||||
|
||||
template sdata(arg: Int128, idx: int): int32 =
|
||||
# udata and sdata was supposed to be in a union, but unions are
|
||||
# handled incorrectly in the VM.
|
||||
cast[ptr int32](arg.udata[idx].unsafeAddr)[]
|
||||
|
||||
# encoding least significant int first (like LittleEndian)
|
||||
|
||||
type
|
||||
InvalidArgument = object of Exception
|
||||
|
||||
template require(cond: bool) =
|
||||
if unlikely(not cond):
|
||||
raise newException(InvalidArgument, "")
|
||||
|
||||
const
|
||||
Zero* = Int128(udata: [0'u32,0,0,0])
|
||||
One* = Int128(udata: [1'u32,0,0,0])
|
||||
Ten* = Int128(udata: [10'u32,0,0,0])
|
||||
Min = Int128(udata: [0'u32,0,0,0x80000000'u32])
|
||||
Max = Int128(udata: [high(uint32),high(uint32),high(uint32),uint32(high(int32))])
|
||||
|
||||
template low*(t: typedesc[Int128]): Int128 = Min
|
||||
template high*(t: typedesc[Int128]): Int128 = Max
|
||||
|
||||
proc `$`*(a: Int128): string
|
||||
|
||||
proc toInt128*[T: SomeInteger](arg: T): Int128 =
|
||||
when T is SomeUnsignedInt:
|
||||
when sizeof(arg) <= 4:
|
||||
result.udata[0] = uint32(arg)
|
||||
else:
|
||||
result.udata[0] = uint32(arg and T(0xffffffff))
|
||||
result.udata[1] = uint32(arg shr 32)
|
||||
else:
|
||||
when sizeof(arg) <= 4:
|
||||
result.sdata(0) = int32(arg)
|
||||
if arg < 0: # sign extend
|
||||
result.sdata(1) = -1
|
||||
result.sdata(2) = -1
|
||||
result.sdata(3) = -1
|
||||
else:
|
||||
let tmp = int64(arg)
|
||||
result.udata[0] = uint32(tmp and 0xffffffff)
|
||||
result.sdata(1) = int32(tmp shr 32)
|
||||
if arg < 0: # sign extend
|
||||
result.sdata(2) = -1
|
||||
result.sdata(3) = -1
|
||||
|
||||
template isNegative(arg: Int128): bool =
|
||||
arg.sdata(3) < 0
|
||||
|
||||
template isNegative(arg: int32): bool =
|
||||
arg < 0
|
||||
|
||||
proc bitconcat(a,b: uint32): uint64 =
|
||||
(uint64(a) shl 32) or uint64(b)
|
||||
|
||||
proc bitsplit(a: uint64): (uint32,uint32) =
|
||||
(cast[uint32](a shr 32), cast[uint32](a))
|
||||
|
||||
proc toInt64*(arg: Int128): int64 =
|
||||
if isNegative(arg):
|
||||
assert(arg.sdata(3) == -1, "out of range")
|
||||
assert(arg.sdata(2) == -1, "out of range")
|
||||
else:
|
||||
assert(arg.sdata(3) == 0, "out of range")
|
||||
assert(arg.sdata(2) == 0, "out of range")
|
||||
|
||||
cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
|
||||
|
||||
proc toUInt64*(arg: Int128): uint64 =
|
||||
assert(arg.udata[3] == 0)
|
||||
assert(arg.udata[2] == 0)
|
||||
bitconcat(arg.udata[1], arg.udata[0])
|
||||
|
||||
proc addToHex(result: var string; arg: uint32) =
|
||||
for i in 0 ..< 8:
|
||||
let idx = (arg shr ((7-i) * 4)) and 0xf
|
||||
result.add "0123456789abcdef"[idx]
|
||||
|
||||
proc addToHex*(result: var string; arg: Int128) =
|
||||
var i = 3
|
||||
while i >= 0:
|
||||
result.addToHex(arg.udata[i])
|
||||
i -= 1
|
||||
|
||||
proc toHex*(arg: Int128): string =
|
||||
result.addToHex(arg)
|
||||
|
||||
proc inc*(a: var Int128, y: uint32 = 1) =
|
||||
let input = a
|
||||
a.udata[0] += y
|
||||
if unlikely(a.udata[0] < y):
|
||||
a.udata[1].inc
|
||||
if unlikely(a.udata[1] == 0):
|
||||
a.udata[2].inc
|
||||
if unlikely(a.udata[2] == 0):
|
||||
a.udata[3].inc
|
||||
doAssert(a.sdata(3) != low(int32), "overflow")
|
||||
|
||||
proc cmp*(a,b: Int128): int =
|
||||
let tmp1 = cmp(a.sdata(3), b.sdata(3))
|
||||
if tmp1 != 0: return tmp1
|
||||
let tmp2 = cmp(a.udata[2], b.udata[2])
|
||||
if tmp2 != 0: return tmp2
|
||||
let tmp3 = cmp(a.udata[1], b.udata[1])
|
||||
if tmp3 != 0: return tmp3
|
||||
let tmp4 = cmp(a.udata[0], b.udata[0])
|
||||
return tmp4
|
||||
|
||||
proc `<`*(a,b: Int128): bool =
|
||||
cmp(a,b) < 0
|
||||
|
||||
proc `<=`*(a,b: Int128): bool =
|
||||
cmp(a,b) <= 0
|
||||
|
||||
proc `==`*(a,b: Int128): bool =
|
||||
if a.udata[0] != b.udata[0]: return false
|
||||
if a.udata[1] != b.udata[1]: return false
|
||||
if a.udata[2] != b.udata[2]: return false
|
||||
if a.udata[3] != b.udata[3]: return false
|
||||
return true
|
||||
|
||||
proc inplaceBitnot(a: var Int128) =
|
||||
a.udata[0] = not a.udata[0]
|
||||
a.udata[1] = not a.udata[1]
|
||||
a.udata[2] = not a.udata[2]
|
||||
a.udata[3] = not a.udata[3]
|
||||
|
||||
proc bitnot*(a: Int128): Int128 =
|
||||
result.udata[0] = not a.udata[0]
|
||||
result.udata[1] = not a.udata[1]
|
||||
result.udata[2] = not a.udata[2]
|
||||
result.udata[3] = not a.udata[3]
|
||||
|
||||
proc bitand*(a,b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] and b.udata[0]
|
||||
result.udata[1] = a.udata[1] and b.udata[1]
|
||||
result.udata[2] = a.udata[2] and b.udata[2]
|
||||
result.udata[3] = a.udata[3] and b.udata[3]
|
||||
|
||||
proc bitor*(a,b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] or b.udata[0]
|
||||
result.udata[1] = a.udata[1] or b.udata[1]
|
||||
result.udata[2] = a.udata[2] or b.udata[2]
|
||||
result.udata[3] = a.udata[3] or b.udata[3]
|
||||
|
||||
proc bitxor*(a,b: Int128): Int128 =
|
||||
result.udata[0] = a.udata[0] xor b.udata[0]
|
||||
result.udata[1] = a.udata[1] xor b.udata[1]
|
||||
result.udata[2] = a.udata[2] xor b.udata[2]
|
||||
result.udata[3] = a.udata[3] xor b.udata[3]
|
||||
|
||||
proc `shr`*(a: Int128, b: int): Int128 =
|
||||
let b = b and 127
|
||||
if b < 32:
|
||||
result.sdata(3) = a.sdata(3) shr b
|
||||
result.udata[2] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr b)
|
||||
result.udata[1] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr b)
|
||||
result.udata[0] = cast[uint32](bitconcat(a.udata[1], a.udata[0]) shr b)
|
||||
elif b < 64:
|
||||
if isNegative(a):
|
||||
result.sdata(3) = -1
|
||||
result.sdata(2) = a.sdata(3) shr (b and 31)
|
||||
result.udata[1] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr (b and 31))
|
||||
result.udata[0] = cast[uint32](bitconcat(a.udata[1], a.udata[0]) shr (b and 31))
|
||||
elif b < 96:
|
||||
if isNegative(a):
|
||||
result.sdata(3) = -1
|
||||
result.sdata(2) = -1
|
||||
result.sdata(1) = a.sdata(3) shr (b and 31)
|
||||
result.udata[0] = cast[uint32](bitconcat(a.udata[1], a.udata[0]) shr (b and 31))
|
||||
else: # b < 128
|
||||
if isNegative(a):
|
||||
result.sdata(3) = -1
|
||||
result.sdata(2) = -1
|
||||
result.sdata(1) = -1
|
||||
result.sdata(0) = a.sdata(3) shr (b and 31)
|
||||
|
||||
proc `shl`*(a: Int128, b: int): Int128 =
|
||||
let b = b and 127
|
||||
if b < 32:
|
||||
result.udata[0] = a.udata[0] shl b
|
||||
result.udata[1] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl b) shr 32)
|
||||
result.udata[2] = cast[uint32]((bitconcat(a.udata[2], a.udata[1]) shl b) shr 32)
|
||||
result.udata[3] = cast[uint32]((bitconcat(a.udata[3], a.udata[2]) shl b) shr 32)
|
||||
elif b < 64:
|
||||
result.udata[0] = 0
|
||||
result.udata[1] = a.udata[0] shl (b and 31)
|
||||
result.udata[2] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl (b and 31)) shr 32)
|
||||
result.udata[3] = cast[uint32]((bitconcat(a.udata[2], a.udata[1]) shl (b and 31)) shr 32)
|
||||
elif b < 96:
|
||||
result.udata[0] = 0
|
||||
result.udata[1] = 0
|
||||
result.udata[2] = a.udata[0] shl (b and 31)
|
||||
result.udata[3] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl (b and 31)) shr 32)
|
||||
else:
|
||||
result.udata[0] = 0
|
||||
result.udata[1] = 0
|
||||
result.udata[2] = 0
|
||||
result.udata[3] = a.udata[0] shl (b and 31)
|
||||
|
||||
|
||||
proc `+`*(a,b: Int128): Int128 =
|
||||
let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0])
|
||||
result.udata[0] = cast[uint32](tmp0)
|
||||
let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32)
|
||||
result.udata[1] = cast[uint32](tmp1)
|
||||
let tmp2 = uint64(a.udata[2]) + uint64(b.udata[2]) + (tmp1 shr 32)
|
||||
result.udata[2] = cast[uint32](tmp2)
|
||||
let tmp3 = uint64(a.udata[3]) + uint64(b.udata[3]) + (tmp2 shr 32)
|
||||
result.udata[3] = cast[uint32](tmp3)
|
||||
|
||||
proc `+=`*(a: var Int128, b: Int128) =
|
||||
a = a + b
|
||||
|
||||
proc `-`*(a: Int128): Int128 =
|
||||
result = bitnot(a)
|
||||
result.inc
|
||||
|
||||
proc `-`*(a,b: Int128): Int128 =
|
||||
a + (-b)
|
||||
|
||||
proc `-=`*(a: var Int128, b: Int128) =
|
||||
a = a - b
|
||||
|
||||
proc abs*(a: Int128): Int128 =
|
||||
if isNegative(a):
|
||||
-a
|
||||
else:
|
||||
a
|
||||
|
||||
proc abs(a: int32): int =
|
||||
if a < 0: -a else: a
|
||||
|
||||
proc `*`(a: Int128, b: uint32): Int128 =
|
||||
let tmp0 = uint64(a.udata[0]) * uint64(b)
|
||||
let tmp1 = uint64(a.udata[1]) * uint64(b)
|
||||
let tmp2 = uint64(a.udata[2]) * uint64(b)
|
||||
let tmp3 = uint64(a.udata[3]) * uint64(b)
|
||||
|
||||
if unlikely(tmp3 > uint64(high(int32))):
|
||||
assert(false, "overflow")
|
||||
|
||||
result.udata[0] = cast[uint32](tmp0)
|
||||
result.udata[1] = cast[uint32](tmp1) + cast[uint32](tmp0 shr 32)
|
||||
result.udata[2] = cast[uint32](tmp2) + cast[uint32](tmp1 shr 32)
|
||||
result.udata[3] = cast[uint32](tmp3) + cast[uint32](tmp2 shr 32)
|
||||
|
||||
proc `*`*(a: Int128, b: int32): Int128 =
|
||||
let isNegative = isNegative(a) xor isNegative(b)
|
||||
result = a * cast[uint32](abs(b))
|
||||
if b < 0:
|
||||
result = -result
|
||||
|
||||
proc `*=`*(a: var Int128, b: int32): Int128 =
|
||||
result = result * b
|
||||
|
||||
proc makeInt128(high,low: uint64): Int128 =
|
||||
result.udata[0] = cast[uint32](low)
|
||||
result.udata[1] = cast[uint32](low shr 32)
|
||||
result.udata[2] = cast[uint32](high)
|
||||
result.udata[3] = cast[uint32](high shr 32)
|
||||
|
||||
proc high64(a: Int128): uint64 =
|
||||
bitconcat(a.udata[3], a.udata[2])
|
||||
|
||||
proc low64(a: Int128): uint64 =
|
||||
bitconcat(a.udata[1], a.udata[0])
|
||||
|
||||
proc `*`*(lhs,rhs: Int128): Int128 =
|
||||
let isNegative = isNegative(lhs) xor isNegative(rhs)
|
||||
|
||||
let
|
||||
a = cast[uint64](lhs.udata[0])
|
||||
b = cast[uint64](lhs.udata[1])
|
||||
c = cast[uint64](lhs.udata[2])
|
||||
d = cast[uint64](lhs.udata[3])
|
||||
|
||||
e = cast[uint64](rhs.udata[0])
|
||||
f = cast[uint64](rhs.udata[1])
|
||||
g = cast[uint64](rhs.udata[2])
|
||||
h = cast[uint64](rhs.udata[3])
|
||||
|
||||
|
||||
let a32 = cast[uint64](lhs.udata[1])
|
||||
let a00 = cast[uint64](lhs.udata[0])
|
||||
let b32 = cast[uint64](rhs.udata[1])
|
||||
let b00 = cast[uint64](rhs.udata[0])
|
||||
|
||||
result = makeInt128(high64(lhs) * low64(rhs) + low64(lhs) * high64(rhs) + a32 * b32, a00 * b00)
|
||||
result = result + toInt128(a32 * b00) shl 32
|
||||
result = result + toInt128(a00 * b32) shl 32
|
||||
|
||||
if isNegative != isNegative(result):
|
||||
echo result
|
||||
assert(false, "overflow")
|
||||
|
||||
proc `*=`*(a: var Int128, b: Int128) =
|
||||
a = a * b
|
||||
|
||||
import bitops
|
||||
|
||||
proc fastLog2*(a: Int128): int =
|
||||
if a.udata[3] != 0:
|
||||
return 96 + fastLog2(a.udata[3])
|
||||
if a.udata[2] != 0:
|
||||
return 64 + fastLog2(a.udata[2])
|
||||
if a.udata[1] != 0:
|
||||
return 32 + fastLog2(a.udata[1])
|
||||
if a.udata[0] != 0:
|
||||
return fastLog2(a.udata[0])
|
||||
|
||||
proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||
assert(divisor != Zero)
|
||||
let isNegative = isNegative(dividend) xor isNegative(divisor)
|
||||
|
||||
var dividend = abs(dividend)
|
||||
let divisor = abs(divisor)
|
||||
|
||||
if divisor > dividend:
|
||||
result.quotient = Zero
|
||||
result.remainder = dividend
|
||||
return
|
||||
|
||||
if divisor == dividend:
|
||||
result.quotient = One
|
||||
result.remainder = Zero
|
||||
return
|
||||
|
||||
var denominator = divisor
|
||||
var quotient = Zero
|
||||
|
||||
# Left aligns the MSB of the denominator and the dividend.
|
||||
let shift = fastLog2(dividend) - fastLog2(denominator)
|
||||
denominator = denominator shl shift
|
||||
|
||||
# Uses shift-subtract algorithm to divide dividend by denominator. The
|
||||
# remainder will be left in dividend.
|
||||
for i in 0 .. shift:
|
||||
quotient = quotient shl 1
|
||||
if dividend >= denominator:
|
||||
dividend = dividend - denominator
|
||||
quotient = bitor(quotient, One)
|
||||
|
||||
denominator = denominator shr 1
|
||||
|
||||
result.quotient = quotient
|
||||
result.remainder = dividend
|
||||
|
||||
proc `div`*(a,b: Int128): Int128 =
|
||||
let (a,b) = divMod(a,b)
|
||||
return a
|
||||
|
||||
proc `mod`*(a,b: Int128): Int128 =
|
||||
let (a,b) = divMod(a,b)
|
||||
return b
|
||||
|
||||
proc `$`*(a: Int128): string =
|
||||
if a == Zero:
|
||||
result = "0"
|
||||
elif a == low(Int128):
|
||||
result = "-170141183460469231731687303715884105728"
|
||||
else:
|
||||
let isNegative = isNegative(a)
|
||||
var a = abs(a)
|
||||
while a > Zero:
|
||||
let (quot, rem) = divMod(a, Ten)
|
||||
result.add "0123456789"[rem.toInt64]
|
||||
a = quot
|
||||
if isNegative:
|
||||
result.add '-'
|
||||
|
||||
var i = 0
|
||||
var j = high(result)
|
||||
while i < j:
|
||||
swap(result[i], result[j])
|
||||
i += 1
|
||||
j -= 1
|
||||
|
||||
proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
||||
assert(pos < arg.len)
|
||||
assert(arg[pos] in {'-','0'..'9'})
|
||||
|
||||
var isNegative = false
|
||||
var pos = pos
|
||||
if arg[pos] == '-':
|
||||
isNegative = true
|
||||
pos += 1
|
||||
|
||||
result = Zero
|
||||
while pos < arg.len and arg[pos] in '0' .. '9':
|
||||
result = result * Ten
|
||||
result.inc(uint32(arg[pos]) - uint32('0'))
|
||||
pos += 1
|
||||
|
||||
if isNegative:
|
||||
result = -result
|
||||
|
||||
# fluff
|
||||
|
||||
proc `<`*(a: Int128, b: BiggestInt): bool =
|
||||
cmp(a,toInt128(b)) < 0
|
||||
|
||||
proc `<`*(a: BiggestInt, b: Int128): bool =
|
||||
cmp(toInt128(a), b) < 0
|
||||
|
||||
proc `<=`*(a: Int128, b: BiggestInt): bool =
|
||||
cmp(a,toInt128(b)) <= 0
|
||||
|
||||
proc `<=`*(a: BiggestInt, b: Int128): bool =
|
||||
cmp(toInt128(a), b) <= 0
|
||||
|
||||
proc `==`*(a: Int128, b: BiggestInt): bool =
|
||||
a == toInt128(b)
|
||||
|
||||
proc `==`*(a: BiggestInt, b: Int128): bool =
|
||||
toInt128(a) == b
|
||||
|
||||
proc `-`*(a: BiggestInt, b: Int128): Int128 =
|
||||
toInt128(a) - b
|
||||
|
||||
proc `-`*(a: Int128, b: BiggestInt): Int128 =
|
||||
a - toInt128(b)
|
||||
|
||||
proc `+`*(a: BiggestInt, b: Int128): Int128 =
|
||||
toInt128(a) + b
|
||||
|
||||
proc `+`*(a: Int128, b: BiggestInt): Int128 =
|
||||
a + toInt128(b)
|
||||
|
||||
|
||||
when isMainModule:
|
||||
let (a,b) = divMod(Ten,Ten)
|
||||
|
||||
doAssert $One == "1"
|
||||
doAssert $Ten == "10"
|
||||
doAssert $Zero == "0"
|
||||
let c = parseDecimalInt128("12345678989876543210123456789")
|
||||
doAssert $c == "12345678989876543210123456789"
|
||||
|
||||
var d : array[39, Int128]
|
||||
d[0] = parseDecimalInt128("1")
|
||||
d[1] = parseDecimalInt128("10")
|
||||
d[2] = parseDecimalInt128("100")
|
||||
d[3] = parseDecimalInt128("1000")
|
||||
d[4] = parseDecimalInt128("10000")
|
||||
d[5] = parseDecimalInt128("100000")
|
||||
d[6] = parseDecimalInt128("1000000")
|
||||
d[7] = parseDecimalInt128("10000000")
|
||||
d[8] = parseDecimalInt128("100000000")
|
||||
d[9] = parseDecimalInt128("1000000000")
|
||||
d[10] = parseDecimalInt128("10000000000")
|
||||
d[11] = parseDecimalInt128("100000000000")
|
||||
d[12] = parseDecimalInt128("1000000000000")
|
||||
d[13] = parseDecimalInt128("10000000000000")
|
||||
d[14] = parseDecimalInt128("100000000000000")
|
||||
d[15] = parseDecimalInt128("1000000000000000")
|
||||
d[16] = parseDecimalInt128("10000000000000000")
|
||||
d[17] = parseDecimalInt128("100000000000000000")
|
||||
d[18] = parseDecimalInt128("1000000000000000000")
|
||||
d[19] = parseDecimalInt128("10000000000000000000")
|
||||
d[20] = parseDecimalInt128("100000000000000000000")
|
||||
d[21] = parseDecimalInt128("1000000000000000000000")
|
||||
d[22] = parseDecimalInt128("10000000000000000000000")
|
||||
d[23] = parseDecimalInt128("100000000000000000000000")
|
||||
d[24] = parseDecimalInt128("1000000000000000000000000")
|
||||
d[25] = parseDecimalInt128("10000000000000000000000000")
|
||||
d[26] = parseDecimalInt128("100000000000000000000000000")
|
||||
d[27] = parseDecimalInt128("1000000000000000000000000000")
|
||||
d[28] = parseDecimalInt128("10000000000000000000000000000")
|
||||
d[29] = parseDecimalInt128("100000000000000000000000000000")
|
||||
d[30] = parseDecimalInt128("1000000000000000000000000000000")
|
||||
d[31] = parseDecimalInt128("10000000000000000000000000000000")
|
||||
d[32] = parseDecimalInt128("100000000000000000000000000000000")
|
||||
d[33] = parseDecimalInt128("1000000000000000000000000000000000")
|
||||
d[34] = parseDecimalInt128("10000000000000000000000000000000000")
|
||||
d[35] = parseDecimalInt128("100000000000000000000000000000000000")
|
||||
d[36] = parseDecimalInt128("1000000000000000000000000000000000000")
|
||||
d[37] = parseDecimalInt128("10000000000000000000000000000000000000")
|
||||
d[38] = parseDecimalInt128("100000000000000000000000000000000000000")
|
||||
|
||||
for i in 0 ..< d.len:
|
||||
for j in 0 ..< d.len:
|
||||
doAssert(cmp(d[i], d[j]) == cmp(i,j))
|
||||
if i + j < d.len:
|
||||
doAssert d[i] * d[j] == d[i+j]
|
||||
if i - j >= 0:
|
||||
doAssert d[i] div d[j] == d[i-j]
|
||||
|
||||
var sum: Int128
|
||||
|
||||
for it in d:
|
||||
sum += it
|
||||
|
||||
doAssert $sum == "111111111111111111111111111111111111111"
|
||||
|
||||
for it in d.mitems:
|
||||
it = -it
|
||||
|
||||
for i in 0 ..< d.len:
|
||||
for j in 0 ..< d.len:
|
||||
doAssert(cmp(d[i], d[j]) == -cmp(i,j))
|
||||
if i + j < d.len:
|
||||
doAssert d[i] * d[j] == -d[i+j]
|
||||
if i - j >= 0:
|
||||
doAssert d[i] div d[j] == -d[i-j]
|
||||
|
||||
doAssert $high(Int128) == "170141183460469231731687303715884105727"
|
||||
doAssert $low(Int128) == "-170141183460469231731687303715884105728"
|
||||
@@ -431,15 +431,6 @@ const # magic checked op; magic unchecked op;
|
||||
["", ""], # UnaryPlusF64
|
||||
["", ""], # UnaryMinusF64
|
||||
["", ""], # AbsF64
|
||||
["Ze8ToI", "Ze8ToI"], # mZe8ToI
|
||||
["Ze8ToI64", "Ze8ToI64"], # mZe8ToI64
|
||||
["Ze16ToI", "Ze16ToI"], # mZe16ToI
|
||||
["Ze16ToI64", "Ze16ToI64"], # mZe16ToI64
|
||||
["Ze32ToI64", "Ze32ToI64"], # mZe32ToI64
|
||||
["ZeIToI64", "ZeIToI64"], # mZeIToI64
|
||||
["toU8", "toU8"], # toU8
|
||||
["toU16", "toU16"], # toU16
|
||||
["toU32", "toU32"], # toU32
|
||||
["", ""], # ToFloat
|
||||
["", ""], # ToBiggestFloat
|
||||
["", ""], # ToInt
|
||||
@@ -491,7 +482,7 @@ template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
|
||||
a, tmp = x.rdLoc
|
||||
b, tmp2 = y.rdLoc
|
||||
when "$3" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
when "$4" in frmt: (a, tmp) = maybeMakeTemp(p, n[1], x)
|
||||
when "$4" in frmt: (b, tmp2) = maybeMakeTemp(p, n[2], y)
|
||||
|
||||
r.res = frmt % [a, b, tmp, tmp2]
|
||||
r.kind = resExpr
|
||||
@@ -582,20 +573,20 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mMinF64: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
|
||||
of mMaxF64: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
|
||||
of mAddU: applyFormat("", "")
|
||||
of msubU: applyFormat("", "")
|
||||
of mmulU: applyFormat("", "")
|
||||
of mdivU: applyFormat("", "")
|
||||
of mmodU: applyFormat("($1 % $2)", "($1 % $2)")
|
||||
of mSubU: applyFormat("", "")
|
||||
of mMulU: applyFormat("", "")
|
||||
of mDivU: applyFormat("", "")
|
||||
of mModU: applyFormat("($1 % $2)", "($1 % $2)")
|
||||
of mEqI: applyFormat("($1 == $2)", "($1 == $2)")
|
||||
of mLeI: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||
of mLtI: applyFormat("($1 < $2)", "($1 < $2)")
|
||||
of mEqF64: applyFormat("($1 == $2)", "($1 == $2)")
|
||||
of mLeF64: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||
of mLtF64: applyFormat("($1 < $2)", "($1 < $2)")
|
||||
of mleU: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||
of mltU: applyFormat("($1 < $2)", "($1 < $2)")
|
||||
of mleU64: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||
of mltU64: applyFormat("($1 < $2)", "($1 < $2)")
|
||||
of mLeU: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||
of mLtU: applyFormat("($1 < $2)", "($1 < $2)")
|
||||
of mLeU64: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||
of mLtU64: applyFormat("($1 < $2)", "($1 < $2)")
|
||||
of mEqEnum: applyFormat("($1 == $2)", "($1 == $2)")
|
||||
of mLeEnum: applyFormat("($1 <= $2)", "($1 <= $2)")
|
||||
of mLtEnum: applyFormat("($1 < $2)", "($1 < $2)")
|
||||
@@ -621,15 +612,6 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
|
||||
of mUnaryPlusF64: applyFormat("+($1)", "+($1)")
|
||||
of mUnaryMinusF64: applyFormat("-($1)", "-($1)")
|
||||
of mAbsF64: applyFormat("Math.abs($1)", "Math.abs($1)")
|
||||
of mZe8ToI: applyFormat("Ze8ToI($1)", "Ze8ToI($1)")
|
||||
of mZe8ToI64: applyFormat("Ze8ToI64($1)", "Ze8ToI64($1)")
|
||||
of mZe16ToI: applyFormat("Ze16ToI($1)", "Ze16ToI($1)")
|
||||
of mZe16ToI64: applyFormat("Ze16ToI64($1)", "Ze16ToI64($1)")
|
||||
of mZe32ToI64: applyFormat("Ze32ToI64($1)", "Ze32ToI64($1)")
|
||||
of mZeIToI64: applyFormat("ZeIToI64($1)", "ZeIToI64($1)")
|
||||
of mtoU8: applyFormat("toU8($1)", "toU8($1)")
|
||||
of mtoU16: applyFormat("toU16($1)", "toU16($1)")
|
||||
of mtoU32: applyFormat("toU32($1)", "toU32($1)")
|
||||
of mToFloat: applyFormat("$1", "$1")
|
||||
of mToBiggestFloat: applyFormat("$1", "$1")
|
||||
of mToInt: applyFormat("Math.trunc($1)", "Math.trunc($1)")
|
||||
@@ -1609,9 +1591,9 @@ proc arrayTypeForElemType(typ: PType): string =
|
||||
of tyInt, tyInt32: "Int32Array"
|
||||
of tyInt16: "Int16Array"
|
||||
of tyInt8: "Int8Array"
|
||||
of tyUint, tyUint32: "Uint32Array"
|
||||
of tyUint16: "Uint16Array"
|
||||
of tyUint8: "Uint8Array"
|
||||
of tyUInt, tyUInt32: "Uint32Array"
|
||||
of tyUInt16: "Uint16Array"
|
||||
of tyUInt8: "Uint8Array"
|
||||
of tyFloat32: "Float32Array"
|
||||
of tyFloat64, tyFloat: "Float64Array"
|
||||
else: ""
|
||||
@@ -2058,8 +2040,14 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of mParseBiggestFloat:
|
||||
useMagic(p, "nimParseBiggestFloat")
|
||||
genCall(p, n, r)
|
||||
of mArray:
|
||||
genCall(p, n, r)
|
||||
of mSlice:
|
||||
# arr.slice([begin[, end]]): 'end' is exclusive
|
||||
var x, y, z: TCompRes
|
||||
gen(p, n.sons[1], x)
|
||||
gen(p, n.sons[2], y)
|
||||
gen(p, n.sons[3], z)
|
||||
r.res = "($1.slice($2, $3+1))" % [x.rdLoc, y.rdLoc, z.rdLoc]
|
||||
r.kind = resExpr
|
||||
else:
|
||||
genCall(p, n, r)
|
||||
#else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);
|
||||
@@ -2264,7 +2252,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
||||
if prc.typ.sons[0] != nil and sfPure notin prc.flags:
|
||||
resultSym = prc.ast.sons[resultPos].sym
|
||||
let mname = mangleName(p.module, resultSym)
|
||||
if not isindirect(resultSym) and
|
||||
if not isIndirect(resultSym) and
|
||||
resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and
|
||||
mapType(p, resultSym.typ) == etyBaseIndex:
|
||||
resultAsgn = p.indentLine(("var $# = null;$n") % [mname])
|
||||
@@ -2278,8 +2266,8 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
|
||||
else:
|
||||
returnStmt = "return $#;$n" % [a.res]
|
||||
|
||||
let transformed_body = transformBody(oldProc.module.graph, prc, cache = false)
|
||||
p.nested: genStmt(p, transformed_body)
|
||||
let transformedBody = transformBody(oldProc.module.graph, prc, cache = false)
|
||||
p.nested: genStmt(p, transformedBody)
|
||||
|
||||
var def: Rope
|
||||
if not prc.constraint.isNil:
|
||||
@@ -2409,7 +2397,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||
of nkFloatLit..nkFloat64Lit:
|
||||
let f = n.floatVal
|
||||
case classify(f)
|
||||
of fcNaN:
|
||||
of fcNan:
|
||||
r.res = rope"NaN"
|
||||
of fcNegZero:
|
||||
r.res = rope"-0.0"
|
||||
@@ -2559,7 +2547,7 @@ proc genModule(p: PProc, n: PNode) =
|
||||
add(p.body, frameCreate(p,
|
||||
makeJSString("module " & p.module.module.name.s),
|
||||
makeJSString(toFilename(p.config, p.module.module.info))))
|
||||
let n_transformed = transformStmt(p.module.graph, p.module.module, n)
|
||||
let transformedN = transformStmt(p.module.graph, p.module.module, n)
|
||||
if p.config.hcrOn and n.kind == nkStmtList:
|
||||
let moduleSym = p.module.module
|
||||
var moduleLoadedVar = rope(moduleSym.name.s) & "_loaded" &
|
||||
@@ -2567,7 +2555,7 @@ proc genModule(p: PProc, n: PNode) =
|
||||
lineF(p, "var $1;$n", [moduleLoadedVar])
|
||||
var inGuardedBlock = false
|
||||
|
||||
addHcrInitGuards(p, n_transformed, moduleLoadedVar, inGuardedBlock)
|
||||
addHcrInitGuards(p, transformedN, moduleLoadedVar, inGuardedBlock)
|
||||
|
||||
if inGuardedBlock:
|
||||
dec p.extraIndent
|
||||
@@ -2575,7 +2563,7 @@ proc genModule(p: PProc, n: PNode) =
|
||||
|
||||
lineF(p, "$1 = true;$n", [moduleLoadedVar])
|
||||
else:
|
||||
genStmt(p, n_transformed)
|
||||
genStmt(p, transformedN)
|
||||
|
||||
if optStackTrace in p.options:
|
||||
add(p.body, frameDestroy(p))
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import
|
||||
intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
|
||||
idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
|
||||
transf
|
||||
transf, liftdestructors
|
||||
|
||||
discard """
|
||||
The basic approach is that captured vars need to be put on the heap and
|
||||
@@ -257,6 +257,8 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
result.add newCall(getSysSym(g, n.info, "internalNew"), env)
|
||||
if optNimV2 in g.config.globalOptions:
|
||||
createTypeBoundOps(g, nil, env.typ, n.info)
|
||||
result.add makeClosure(g, iter, env, n.info)
|
||||
|
||||
proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
|
||||
@@ -324,6 +326,7 @@ proc asOwnedRef(c: DetectionPass; t: PType): PType =
|
||||
if optNimV2 in c.graph.config.globalOptions:
|
||||
assert t.kind == tyRef
|
||||
result = newType(tyOwned, t.owner)
|
||||
result.flags.incl tfHasOwned
|
||||
result.rawAddSon t
|
||||
else:
|
||||
result = t
|
||||
@@ -341,7 +344,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||
# 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:
|
||||
let fieldType = if false: # c.graph.config.selectedGC == gcDestructors:
|
||||
c.getEnvTypeForOwnerUp(dep, info) #getHiddenParam(dep).typ
|
||||
else:
|
||||
c.getEnvTypeForOwner(dep, info)
|
||||
@@ -490,6 +493,7 @@ type
|
||||
processed: IntSet
|
||||
envVars: Table[int, PNode]
|
||||
inContainer: int
|
||||
unownedEnvVars: Table[int, PNode] # only required for --newruntime
|
||||
|
||||
proc initLiftingPass(fn: PSym): LiftingPass =
|
||||
result.processed = initIntSet()
|
||||
@@ -514,9 +518,9 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
||||
localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
|
||||
result = n
|
||||
|
||||
proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType): PNode =
|
||||
var v = newSym(skVar, getIdent(cache, envName), owner, owner.info)
|
||||
incl(v.flags, sfShadowed)
|
||||
proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo): PNode =
|
||||
var v = newSym(skVar, getIdent(cache, envName), owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = typ
|
||||
result = newSymNode(v)
|
||||
when false:
|
||||
@@ -528,16 +532,21 @@ proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType): PNode =
|
||||
result = newSymNode(v)
|
||||
|
||||
proc setupEnvVar(owner: PSym; d: DetectionPass;
|
||||
c: var LiftingPass): PNode =
|
||||
c: var LiftingPass; info: TLineInfo): PNode =
|
||||
if owner.isIterator:
|
||||
return getHiddenParam(d.graph, owner).newSymNode
|
||||
result = c.envvars.getOrDefault(owner.id)
|
||||
result = c.envVars.getOrDefault(owner.id)
|
||||
if result.isNil:
|
||||
let envVarType = d.ownerToType.getOrDefault(owner.id)
|
||||
if envVarType.isNil:
|
||||
localError d.graph.config, owner.info, "internal error: could not determine closure type"
|
||||
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType))
|
||||
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info)
|
||||
c.envVars[owner.id] = result
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = envVarType
|
||||
c.unownedEnvVars[owner.id] = newSymNode(v)
|
||||
|
||||
proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
|
||||
let p = getHiddenParam(g, owner)
|
||||
@@ -550,20 +559,34 @@ proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
|
||||
result = rawIndirectAccess(result, upField, p.info)
|
||||
|
||||
proc rawClosureCreation(owner: PSym;
|
||||
d: DetectionPass; c: var LiftingPass): PNode =
|
||||
d: DetectionPass; c: var LiftingPass;
|
||||
info: TLineInfo): PNode =
|
||||
result = newNodeI(nkStmtList, owner.info)
|
||||
|
||||
var env: PNode
|
||||
if owner.isIterator:
|
||||
env = getHiddenParam(d.graph, owner).newSymNode
|
||||
else:
|
||||
env = setupEnvVar(owner, d, c)
|
||||
env = setupEnvVar(owner, d, c, info)
|
||||
if env.kind == nkSym:
|
||||
var v = newNodeI(nkVarSection, env.info)
|
||||
addVar(v, env)
|
||||
result.add(v)
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
addVar(v, unowned)
|
||||
|
||||
# add 'new' statement:
|
||||
result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
let env2 = copyTree(env)
|
||||
env2.typ = unowned.typ
|
||||
result.add newAsgnStmt(unowned, env2, env.info)
|
||||
createTypeBoundOps(d.graph, nil, unowned.typ, env.info)
|
||||
|
||||
# add assignment statements for captured parameters:
|
||||
for i in 1..<owner.typ.n.len:
|
||||
let local = owner.typ.n[i].sym
|
||||
@@ -583,6 +606,18 @@ proc rawClosureCreation(owner: PSym;
|
||||
# oldenv, env.info))
|
||||
else:
|
||||
localError(d.graph.config, env.info, "internal error: cannot create up reference")
|
||||
# we are not in the sem'check phase anymore! so pass 'nil' for the PContext
|
||||
# and hope for the best:
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
createTypeBoundOps(d.graph, nil, env.typ, owner.info)
|
||||
|
||||
proc finishClosureCreation(owner: PSym; d: DetectionPass; c: LiftingPass;
|
||||
info: TLineInfo; res: PNode) =
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
let nilLit = newNodeIT(nkNilLit, info, unowned.typ)
|
||||
res.add newAsgnStmt(unowned, nilLit, info)
|
||||
|
||||
proc closureCreationForIter(iter: PNode;
|
||||
d: DetectionPass; c: var LiftingPass): PNode =
|
||||
@@ -602,10 +637,12 @@ proc closureCreationForIter(iter: PNode;
|
||||
addVar(vs, vnode)
|
||||
result.add(vs)
|
||||
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
createTypeBoundOps(d.graph, nil, vnode.typ, iter.info)
|
||||
|
||||
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef}).n, getIdent(d.graph.cache, upName))
|
||||
if upField != nil:
|
||||
let u = setupEnvVar(owner, d, c)
|
||||
let u = setupEnvVar(owner, d, c, iter.info)
|
||||
if u.typ.skipTypes({tyOwned, tyRef}) == upField.typ.skipTypes({tyOwned, tyRef}):
|
||||
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
|
||||
u, iter.info))
|
||||
@@ -615,7 +652,9 @@ proc closureCreationForIter(iter: PNode;
|
||||
|
||||
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
|
||||
c: var LiftingPass): PNode =
|
||||
let access = setupEnvVar(owner, d, c)
|
||||
var access = setupEnvVar(owner, d, c, n.info)
|
||||
if optNimV2 in d.graph.config.globalOptions:
|
||||
access = c.unownedEnvVars[owner.id]
|
||||
let obj = access.typ.skipTypes({tyOwned, tyRef})
|
||||
let field = getFieldFromObj(obj, n.sym)
|
||||
if field != nil:
|
||||
@@ -641,7 +680,7 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
|
||||
result = closureCreationForIter(n, d, c)
|
||||
elif s.skipGenericOwner == owner:
|
||||
# direct dependency, so use the outer's env variable:
|
||||
result = makeClosure(d.graph, s, setupEnvVar(owner, d, c), n.info)
|
||||
result = makeClosure(d.graph, s, setupEnvVar(owner, d, c, n.info), n.info)
|
||||
else:
|
||||
let available = getHiddenParam(d.graph, owner)
|
||||
let wanted = getHiddenParam(d.graph, s).typ
|
||||
@@ -671,10 +710,11 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||
c.inContainer = 0
|
||||
var body = transformBody(d.graph, s)
|
||||
body = liftCapturedVars(body, s, d, c)
|
||||
if c.envvars.getOrDefault(s.id).isNil:
|
||||
if c.envVars.getOrDefault(s.id).isNil:
|
||||
s.transformedBody = body
|
||||
else:
|
||||
s.transformedBody = newTree(nkStmtList, rawClosureCreation(s, d, c), body)
|
||||
s.transformedBody = newTree(nkStmtList, rawClosureCreation(s, d, c, n.info), body)
|
||||
finishClosureCreation(s, d, c, n.info, s.transformedBody)
|
||||
c.inContainer = oldInContainer
|
||||
|
||||
if s.typ.callConv == ccClosure:
|
||||
@@ -718,7 +758,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
|
||||
# transform, let's not touch the LHS in order to make the lifting pass
|
||||
# correct when `result` is lifted
|
||||
n[0].sons[1] = liftCapturedVars(n[0].sons[1], owner, d, c)
|
||||
else: assert n[0].kind == nkEmpty
|
||||
else:
|
||||
n.sons[0] = liftCapturedVars(n[0], owner, d, c)
|
||||
else:
|
||||
if owner.isIterator:
|
||||
if nfLL in n.flags:
|
||||
@@ -810,8 +851,9 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
|
||||
var c = initLiftingPass(fn)
|
||||
result = liftCapturedVars(body, fn, d, c)
|
||||
# echo renderTree(result, {renderIds})
|
||||
if c.envvars.getOrDefault(fn.id) != nil:
|
||||
result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
|
||||
if c.envVars.getOrDefault(fn.id) != nil:
|
||||
result = newTree(nkStmtList, rawClosureCreation(fn, d, c, body.info), result)
|
||||
finishClosureCreation(fn, d, c, body.info, result)
|
||||
else:
|
||||
result = body
|
||||
#if fn.name.s == "get2":
|
||||
@@ -879,6 +921,9 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
|
||||
if optNimV2 in g.config.globalOptions:
|
||||
createTypeBoundOps(g, nil, env.typ, body.info)
|
||||
|
||||
elif op.kind == nkStmtListExpr:
|
||||
let closure = op.lastSon
|
||||
if closure.kind == nkClosure:
|
||||
|
||||
@@ -15,7 +15,7 @@ from os import changeFileExt
|
||||
from sequtils import delete
|
||||
|
||||
const
|
||||
MaxLineLen = 80
|
||||
MinLineLen = 15
|
||||
|
||||
type
|
||||
SplitKind = enum
|
||||
@@ -25,7 +25,12 @@ type
|
||||
detectSemicolonKind, useSemicolon, dontTouch
|
||||
|
||||
LayoutToken = enum
|
||||
ltSpaces, ltNewline, ltTab,
|
||||
ltSpaces,
|
||||
ltCrucialNewline, ## a semantically crucial newline (indentation!)
|
||||
ltSplittingNewline, ## newline used for splitting up long
|
||||
## expressions (like after a comma or a binary operator)
|
||||
ltTab,
|
||||
ltOptionalNewline, ## optional newline introduced by nimpretty
|
||||
ltComment, ltLit, ltKeyword, ltExportMarker, ltIdent,
|
||||
ltOther, ltOpr,
|
||||
ltBeginSection, ltEndSection
|
||||
@@ -36,7 +41,7 @@ type
|
||||
lastTok: TTokType
|
||||
inquote, lastTokWasTerse: bool
|
||||
semicolons: SemicolonKind
|
||||
col, lastLineNumber, lineSpan, indentLevel, indWidth*: int
|
||||
col, lastLineNumber, lineSpan, indentLevel, indWidth*, inSection: int
|
||||
keepIndents*: int
|
||||
doIndentMore*: int
|
||||
kinds: seq[LayoutToken]
|
||||
@@ -44,10 +49,11 @@ type
|
||||
indentStack: seq[int]
|
||||
fixedUntil: int # marks where we must not go in the content
|
||||
altSplitPos: array[SplitKind, int] # alternative split positions
|
||||
maxLineLen*: int
|
||||
|
||||
proc openEmitter*(em: var Emitter, cache: IdentCache;
|
||||
config: ConfigRef, fileIdx: FileIndex) =
|
||||
let fullPath = Absolutefile config.toFullPath(fileIdx)
|
||||
let fullPath = AbsoluteFile config.toFullPath(fileIdx)
|
||||
if em.indWidth == 0:
|
||||
em.indWidth = getIndentWidth(fileIdx, llStreamOpen(fullPath, fmRead),
|
||||
cache, config)
|
||||
@@ -63,17 +69,20 @@ proc openEmitter*(em: var Emitter, cache: IdentCache;
|
||||
|
||||
proc computeMax(em: Emitter; pos: int): int =
|
||||
var p = pos
|
||||
var extraSpace = 0
|
||||
result = 0
|
||||
while p < em.tokens.len and em.kinds[p] != ltEndSection:
|
||||
var lhs = 0
|
||||
var lineLen = 0
|
||||
var foundTab = false
|
||||
while p < em.tokens.len and em.kinds[p] != ltEndSection:
|
||||
if em.kinds[p] == ltNewline:
|
||||
if foundTab and lineLen <= MaxLineLen: result = max(result, lhs)
|
||||
if em.kinds[p] in {ltCrucialNewline, ltSplittingNewline}:
|
||||
if foundTab and lineLen <= em.maxLineLen:
|
||||
result = max(result, lhs + extraSpace)
|
||||
inc p
|
||||
break
|
||||
if em.kinds[p] == ltTab:
|
||||
extraSpace = if em.kinds[p-1] == ltSpaces: 0 else: 1
|
||||
foundTab = true
|
||||
else:
|
||||
if not foundTab:
|
||||
@@ -84,46 +93,17 @@ proc computeMax(em: Emitter; pos: int): int =
|
||||
proc computeRhs(em: Emitter; pos: int): int =
|
||||
var p = pos
|
||||
result = 0
|
||||
while p < em.tokens.len and em.kinds[p] != ltNewline:
|
||||
while p < em.tokens.len and em.kinds[p] notin {ltCrucialNewline, ltSplittingNewline}:
|
||||
inc result, em.tokens[p].len
|
||||
inc p
|
||||
|
||||
proc closeEmitter*(em: var Emitter) =
|
||||
let outFile = em.config.absOutFile
|
||||
proc isLongEnough(lineLen, startPos, endPos: int): bool =
|
||||
result = lineLen > MinLineLen and endPos > startPos + 4
|
||||
|
||||
var content = newStringOfCap(16_000)
|
||||
var maxLhs = 0
|
||||
var lineLen = 0
|
||||
var lineBegin = 0
|
||||
for i in 0..em.tokens.high:
|
||||
case em.kinds[i]
|
||||
of ltBeginSection:
|
||||
maxLhs = computeMax(em, lineBegin)
|
||||
of ltEndSection:
|
||||
maxLhs = 0
|
||||
of ltTab:
|
||||
if maxLhs == 0 or computeRhs(em, i)+maxLhs > MaxLineLen:
|
||||
content.add ' '
|
||||
else:
|
||||
let spaces = max(maxLhs - lineLen + 1, 1)
|
||||
for j in 1..spaces: content.add ' '
|
||||
of ltNewline:
|
||||
content.add em.tokens[i]
|
||||
lineLen = 0
|
||||
lineBegin = i+1
|
||||
else:
|
||||
content.add em.tokens[i]
|
||||
inc lineLen, em.tokens[i].len
|
||||
|
||||
if fileExists(outFile) and readFile(outFile.string) == content:
|
||||
discard "do nothing, see #9499"
|
||||
return
|
||||
var f = llStreamOpen(outFile, fmWrite)
|
||||
if f == nil:
|
||||
rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
|
||||
return
|
||||
f.llStreamWrite content
|
||||
llStreamClose(f)
|
||||
proc findNewline(em: Emitter; p, lineLen: var int) =
|
||||
while p < em.tokens.len and em.kinds[p] notin {ltCrucialNewline, ltSplittingNewline}:
|
||||
inc lineLen, em.tokens[p].len
|
||||
inc p
|
||||
|
||||
proc countNewlines(s: string): int =
|
||||
result = 0
|
||||
@@ -137,17 +117,143 @@ proc calcCol(em: var Emitter; s: string) =
|
||||
dec i
|
||||
inc em.col
|
||||
|
||||
proc optionalIsGood(em: var Emitter; pos, currentLen: int): bool =
|
||||
let ourIndent = em.tokens[pos].len
|
||||
var p = pos+1
|
||||
var lineLen = 0
|
||||
em.findNewline(p, lineLen)
|
||||
if p == pos+1: # optionalNewline followed by another newline
|
||||
result = false
|
||||
elif em.kinds[p-1] == ltComment and currentLen+lineLen < em.maxLineLen+MinLineLen:
|
||||
result = false
|
||||
elif p+1 < em.tokens.len and em.kinds[p+1] == ltSpaces and
|
||||
em.kinds[p-1] == ltOptionalNewline:
|
||||
if em.tokens[p+1].len == ourIndent:
|
||||
# concatenate lines with the same indententation
|
||||
var nlPos = p
|
||||
var lineLenTotal = lineLen
|
||||
inc p
|
||||
em.findNewline(p, lineLenTotal)
|
||||
if isLongEnough(lineLenTotal, nlPos, p):
|
||||
em.kinds[nlPos] = ltOptionalNewline
|
||||
if em.kinds[nlPos+1] == ltSpaces:
|
||||
# inhibit extra spaces when concatenating two lines
|
||||
em.tokens[nlPos+1] = if em.tokens[nlPos-2] == ",": " " else: ""
|
||||
result = true
|
||||
elif em.tokens[p+1].len < ourIndent:
|
||||
result = isLongEnough(lineLen, pos, p)
|
||||
elif em.kinds[pos+1] == ltOther: # note: pos+1, not p+1
|
||||
result = false
|
||||
else:
|
||||
result = isLongEnough(lineLen, pos, p)
|
||||
|
||||
proc lenOfNextTokens(em: Emitter; pos: int): int =
|
||||
result = 0
|
||||
for i in 1 ..< em.tokens.len-pos:
|
||||
if em.kinds[pos+i] in {ltCrucialNewline, ltSplittingNewline, ltOptionalNewline}: break
|
||||
inc result, em.tokens[pos+i].len
|
||||
|
||||
proc closeEmitter*(em: var Emitter) =
|
||||
let outFile = em.config.absOutFile
|
||||
|
||||
var content = newStringOfCap(16_000)
|
||||
var maxLhs = 0
|
||||
var lineLen = 0
|
||||
var lineBegin = 0
|
||||
var i = 0
|
||||
while i <= em.tokens.high:
|
||||
when defined(debug):
|
||||
echo (token: em.tokens[i], kind: em.kinds[i])
|
||||
case em.kinds[i]
|
||||
of ltBeginSection:
|
||||
maxLhs = computeMax(em, lineBegin)
|
||||
of ltEndSection:
|
||||
maxLhs = 0
|
||||
lineBegin = i+1
|
||||
of ltTab:
|
||||
if i >= 2 and em.kinds[i-2] in {ltCrucialNewline, ltSplittingNewline} and
|
||||
em.kinds[i-1] in {ltCrucialNewline, ltSplittingNewline, ltSpaces}:
|
||||
# a previous section has ended
|
||||
maxLhs = 0
|
||||
|
||||
if maxLhs == 0:
|
||||
if em.kinds[i-1] != ltSpaces:
|
||||
content.add em.tokens[i]
|
||||
inc lineLen, em.tokens[i].len
|
||||
else:
|
||||
# pick the shorter indentation token:
|
||||
var spaces = maxLhs - lineLen
|
||||
if spaces < em.tokens[i].len or computeRhs(em, i+1)+maxLhs <= em.maxLineLen+MinLineLen:
|
||||
if spaces <= 0 and content[^1] notin {' ', '\L'}: spaces = 1
|
||||
for j in 1..spaces: content.add ' '
|
||||
inc lineLen, spaces
|
||||
else:
|
||||
content.add em.tokens[i]
|
||||
inc lineLen, em.tokens[i].len
|
||||
of ltCrucialNewline, ltSplittingNewline:
|
||||
content.add em.tokens[i]
|
||||
lineLen = 0
|
||||
lineBegin = i+1
|
||||
of ltOptionalNewline:
|
||||
let totalLineLen = lineLen + lenOfNextTokens(em, i)
|
||||
if totalLineLen > em.maxLineLen and optionalIsGood(em, i, lineLen):
|
||||
if i-1 >= 0 and em.kinds[i-1] == ltSpaces:
|
||||
let spaces = em.tokens[i-1].len
|
||||
content.setLen(content.len - spaces)
|
||||
content.add "\L"
|
||||
content.add em.tokens[i]
|
||||
lineLen = em.tokens[i].len
|
||||
lineBegin = i+1
|
||||
if i+1 < em.kinds.len and em.kinds[i+1] == ltSpaces:
|
||||
# inhibit extra spaces at the start of a new line
|
||||
inc i
|
||||
of ltLit:
|
||||
let lineSpan = countNewlines(em.tokens[i])
|
||||
if lineSpan > 0:
|
||||
em.calcCol(em.tokens[i])
|
||||
lineLen = em.col
|
||||
else:
|
||||
inc lineLen, em.tokens[i].len
|
||||
content.add em.tokens[i]
|
||||
else:
|
||||
content.add em.tokens[i]
|
||||
inc lineLen, em.tokens[i].len
|
||||
inc i
|
||||
|
||||
if fileExists(outFile) and readFile(outFile.string) == content:
|
||||
discard "do nothing, see #9499"
|
||||
return
|
||||
var f = llStreamOpen(outFile, fmWrite)
|
||||
if f == nil:
|
||||
rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
|
||||
return
|
||||
f.llStreamWrite content
|
||||
llStreamClose(f)
|
||||
|
||||
proc wr(em: var Emitter; x: string; lt: LayoutToken) =
|
||||
em.tokens.add x
|
||||
em.kinds.add lt
|
||||
inc em.col, x.len
|
||||
assert em.tokens.len == em.kinds.len
|
||||
|
||||
proc wrNewline(em: var Emitter) =
|
||||
proc wrNewline(em: var Emitter; kind = ltCrucialNewline) =
|
||||
em.tokens.add "\L"
|
||||
em.kinds.add ltNewline
|
||||
em.kinds.add kind
|
||||
em.col = 0
|
||||
|
||||
proc newlineWasSplitting*(em: var Emitter) =
|
||||
if em.kinds.len >= 3 and em.kinds[^3] == ltCrucialNewline:
|
||||
em.kinds[^3] = ltSplittingNewline
|
||||
|
||||
#[
|
||||
Splitting newlines can occur:
|
||||
- after commas, semicolon, '[', '('.
|
||||
- after binary operators, '='.
|
||||
- after ':' type
|
||||
|
||||
We only need parser support for the "after type" case.
|
||||
]#
|
||||
|
||||
proc wrSpaces(em: var Emitter; spaces: int) =
|
||||
if spaces > 0:
|
||||
wr(em, strutils.repeat(' ', spaces), ltSpaces)
|
||||
@@ -158,8 +264,19 @@ proc wrSpace(em: var Emitter) =
|
||||
proc wrTab(em: var Emitter) =
|
||||
wr(em, " ", ltTab)
|
||||
|
||||
proc beginSection*(em: var Emitter) = wr(em, "", ltBeginSection)
|
||||
proc endSection*(em: var Emitter) = wr(em, "", ltEndSection)
|
||||
proc beginSection*(em: var Emitter) =
|
||||
let pos = max(0, em.tokens.len-2)
|
||||
em.tokens.insert "", pos
|
||||
em.kinds.insert ltBeginSection, pos
|
||||
inc em.inSection
|
||||
|
||||
#wr(em, "", ltBeginSection)
|
||||
proc endSection*(em: var Emitter) =
|
||||
em.tokens.insert "", em.tokens.len-2
|
||||
em.kinds.insert ltEndSection, em.kinds.len-2
|
||||
dec em.inSection
|
||||
|
||||
#wr(em, "", ltEndSection)
|
||||
|
||||
proc removeSpaces(em: var Emitter) =
|
||||
while em.kinds.len > 0 and em.kinds[^1] == ltSpaces:
|
||||
@@ -168,7 +285,6 @@ proc removeSpaces(em: var Emitter) =
|
||||
setLen(em.kinds, em.kinds.len-1)
|
||||
dec em.col, tokenLen
|
||||
|
||||
template goodCol(col): bool = col in 40..MaxLineLen
|
||||
|
||||
const
|
||||
openPars = {tkParLe, tkParDotLe,
|
||||
@@ -178,77 +294,63 @@ const
|
||||
tkBracketRi, tkCurlyDotRi,
|
||||
tkCurlyRi}
|
||||
|
||||
splitters = openPars + {tkComma, tkSemicolon}
|
||||
splitters = openPars + {tkComma, tkSemiColon} # do not add 'tkColon' here!
|
||||
oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
|
||||
tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor}
|
||||
|
||||
template rememberSplit(kind) =
|
||||
if goodCol(em.col):
|
||||
em.altSplitPos[kind] = em.tokens.len
|
||||
template goodCol(col): bool = col >= em.maxLineLen div 2
|
||||
|
||||
template moreIndent(em): int =
|
||||
(if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth)
|
||||
if em.doIndentMore > 0: em.indWidth*2 else: em.indWidth
|
||||
|
||||
proc softLinebreak(em: var Emitter, lit: string) =
|
||||
# XXX Use an algorithm that is outlined here:
|
||||
# https://llvm.org/devmtg/2013-04/jasper-slides.pdf
|
||||
# +2 because we blindly assume a comma or ' &' might follow
|
||||
if not em.inquote and em.col+lit.len+2 >= MaxLineLen:
|
||||
if em.lastTok in splitters:
|
||||
# bug #10295, check first if even more indentation would help:
|
||||
let spaces = em.indentLevel+moreIndent(em)
|
||||
if spaces < em.col:
|
||||
removeSpaces em
|
||||
wrNewline(em)
|
||||
em.col = 0
|
||||
wrSpaces em, spaces
|
||||
else:
|
||||
# search backwards for a good split position:
|
||||
for a in mitems(em.altSplitPos):
|
||||
if a > em.fixedUntil:
|
||||
var spaces = 0
|
||||
while a+spaces < em.kinds.len and em.kinds[a+spaces] == ltSpaces:
|
||||
inc spaces
|
||||
if spaces > 0:
|
||||
delete(em.tokens, a, a+spaces-1)
|
||||
delete(em.kinds, a, a+spaces-1)
|
||||
em.kinds.insert(ltNewline, a)
|
||||
em.tokens.insert("\L", a)
|
||||
em.kinds.insert(ltSpaces, a+1)
|
||||
em.tokens.insert(repeat(' ', em.indentLevel+moreIndent(em)), a+1)
|
||||
# recompute em.col:
|
||||
var i = em.kinds.len-1
|
||||
em.col = 0
|
||||
while i >= 0 and em.kinds[i] != ltNewline:
|
||||
inc em.col, em.tokens[i].len
|
||||
dec i
|
||||
# mark position as "already split here"
|
||||
a = -1
|
||||
break
|
||||
template rememberSplit(kind) =
|
||||
if goodCol(em.col) and not em.inquote:
|
||||
let spaces = em.indentLevel+moreIndent(em)
|
||||
if spaces < em.col and spaces > 0:
|
||||
wr(em, strutils.repeat(' ', spaces), ltOptionalNewline)
|
||||
#em.altSplitPos[kind] = em.tokens.len
|
||||
|
||||
proc emitMultilineComment(em: var Emitter, lit: string, col: int) =
|
||||
proc emitMultilineComment(em: var Emitter, lit: string, col: int; dontIndent: bool) =
|
||||
# re-align every line in the multi-line comment:
|
||||
var i = 0
|
||||
var lastIndent = em.indentStack[^1]
|
||||
var lastIndent = if em.keepIndents > 0: em.indentLevel else: em.indentStack[^1]
|
||||
var b = 0
|
||||
var dontIndent = dontIndent
|
||||
var hasEmptyLine = false
|
||||
for commentLine in splitLines(lit):
|
||||
let stripped = commentLine.strip()
|
||||
var a = 0
|
||||
while a < commentLine.len and commentLine[a] == ' ': inc a
|
||||
if i == 0:
|
||||
discard
|
||||
elif stripped.len == 0:
|
||||
wrNewline em
|
||||
if i == 0 and (commentLine.endsWith("\\") or commentLine.endsWith("[")):
|
||||
dontIndent = true
|
||||
wr em, commentLine, ltComment
|
||||
elif dontIndent:
|
||||
if i > 0: wrNewline em
|
||||
wr em, commentLine, ltComment
|
||||
else:
|
||||
if a > lastIndent:
|
||||
b += em.indWidth
|
||||
lastIndent = a
|
||||
elif a < lastIndent:
|
||||
b -= em.indWidth
|
||||
lastIndent = a
|
||||
wrNewline em
|
||||
wrSpaces em, col + b
|
||||
wr em, stripped, ltComment
|
||||
let stripped = commentLine.strip()
|
||||
if i == 0:
|
||||
if em.kinds.len > 0 and em.kinds[^1] != ltTab:
|
||||
wr(em, "", ltTab)
|
||||
elif stripped.len == 0:
|
||||
wrNewline em
|
||||
hasEmptyLine = true
|
||||
else:
|
||||
var a = 0
|
||||
while a < commentLine.len and commentLine[a] == ' ': inc a
|
||||
|
||||
if a > lastIndent:
|
||||
b += em.indWidth
|
||||
lastIndent = a
|
||||
elif a < lastIndent:
|
||||
b -= em.indWidth
|
||||
lastIndent = a
|
||||
wrNewline em
|
||||
if not hasEmptyLine or col + b < 15:
|
||||
if col + b > 0:
|
||||
wr(em, repeat(' ', col+b), ltTab)
|
||||
else:
|
||||
wr(em, "", ltTab)
|
||||
else:
|
||||
wr(em, repeat(' ', a), ltSpaces)
|
||||
wr em, stripped, ltComment
|
||||
inc i
|
||||
|
||||
proc lastChar(s: string): char =
|
||||
@@ -257,20 +359,20 @@ proc lastChar(s: string): char =
|
||||
proc endsInWhite(em: Emitter): bool =
|
||||
var i = em.tokens.len-1
|
||||
while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i)
|
||||
result = if i >= 0: em.kinds[i] in {ltSpaces, ltNewline, ltTab} else: true
|
||||
result = if i >= 0: em.kinds[i] in {ltSpaces, ltCrucialNewline, ltSplittingNewline, ltTab} else: true
|
||||
|
||||
proc endsInNewline(em: Emitter): bool =
|
||||
var i = em.tokens.len-1
|
||||
while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection, ltSpaces}: dec(i)
|
||||
result = if i >= 0: em.kinds[i] in {ltNewline, ltTab} else: true
|
||||
result = if i >= 0: em.kinds[i] in {ltCrucialNewline, ltSplittingNewline, ltTab} else: true
|
||||
|
||||
proc endsInAlpha(em: Emitter): bool =
|
||||
var i = em.tokens.len-1
|
||||
while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i)
|
||||
result = if i >= 0: em.tokens[i].lastChar in SymChars+{'_'} else: false
|
||||
|
||||
proc emitComment(em: var Emitter; tok: TToken) =
|
||||
let col = em.col
|
||||
proc emitComment(em: var Emitter; tok: TToken; dontIndent: bool) =
|
||||
var col = em.col
|
||||
let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB)
|
||||
em.lineSpan = countNewlines(lit)
|
||||
if em.lineSpan > 0: calcCol(em, lit)
|
||||
@@ -281,7 +383,8 @@ proc emitComment(em: var Emitter; tok: TToken) =
|
||||
else:
|
||||
if not endsInWhite(em):
|
||||
wrTab em
|
||||
emitMultilineComment(em, lit, col)
|
||||
inc col
|
||||
emitMultilineComment(em, lit, col, dontIndent)
|
||||
|
||||
proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
template wasExportMarker(em): bool =
|
||||
@@ -303,19 +406,21 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
return
|
||||
|
||||
var preventComment = false
|
||||
if tok.tokType == tkComment and tok.line == em.lastLineNumber and tok.indent >= 0:
|
||||
if tok.tokType == tkComment and tok.line == em.lastLineNumber:
|
||||
# we have an inline comment so handle it before the indentation token:
|
||||
emitComment(em, tok)
|
||||
emitComment(em, tok, dontIndent = (em.inSection == 0))
|
||||
preventComment = true
|
||||
em.fixedUntil = em.tokens.high
|
||||
|
||||
elif tok.indent >= 0:
|
||||
var newlineKind = ltCrucialNewline
|
||||
if em.keepIndents > 0:
|
||||
em.indentLevel = tok.indent
|
||||
elif (em.lastTok in (splitters + oprSet) and tok.tokType notin closedPars):
|
||||
# aka: we are in an expression context:
|
||||
let alignment = tok.indent - em.indentStack[^1]
|
||||
let alignment = max(tok.indent - em.indentStack[^1], 0)
|
||||
em.indentLevel = alignment + em.indentStack.high * em.indWidth
|
||||
newlineKind = ltSplittingNewline
|
||||
else:
|
||||
if tok.indent > em.indentStack[^1]:
|
||||
em.indentStack.add tok.indent
|
||||
@@ -335,7 +440,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
]#
|
||||
# remove trailing whitespace:
|
||||
removeSpaces em
|
||||
wrNewline em
|
||||
wrNewline em, newlineKind
|
||||
for i in 2..tok.line - em.lastLineNumber: wrNewline(em)
|
||||
wrSpaces em, em.indentLevel
|
||||
em.fixedUntil = em.tokens.high
|
||||
@@ -346,20 +451,15 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
if endsInAlpha(em):
|
||||
wrSpace em
|
||||
elif not em.inquote and not endsInWhite(em) and
|
||||
em.lastTok notin openPars and not em.lastTokWasTerse:
|
||||
em.lastTok notin (openPars+{tkOpr, tkDotDot}) and not em.lastTokWasTerse:
|
||||
#and tok.tokType in oprSet
|
||||
wrSpace em
|
||||
|
||||
if not em.inquote:
|
||||
wr(em, TokTypeToStr[tok.tokType], ltKeyword)
|
||||
|
||||
case tok.tokType
|
||||
of tkAnd: rememberSplit(splitAnd)
|
||||
of tkOr: rememberSplit(splitOr)
|
||||
of tkIn, tkNotin:
|
||||
if tok.tokType in {tkAnd, tkOr, tkIn, tkNotin}:
|
||||
rememberSplit(splitIn)
|
||||
wrSpace em
|
||||
else: discard
|
||||
else:
|
||||
# keywords in backticks are not normalized:
|
||||
wr(em, tok.ident.s, ltIdent)
|
||||
@@ -367,7 +467,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
of tkColon:
|
||||
wr(em, TokTypeToStr[tok.tokType], ltOther)
|
||||
wrSpace em
|
||||
of tkSemicolon, tkComma:
|
||||
of tkSemiColon, tkComma:
|
||||
wr(em, TokTypeToStr[tok.tokType], ltOther)
|
||||
rememberSplit(splitComma)
|
||||
wrSpace em
|
||||
@@ -392,8 +492,8 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
wr(em, TokTypeToStr[tok.tokType], ltOther)
|
||||
if not em.inquote: wrSpace(em)
|
||||
of tkOpr, tkDotDot:
|
||||
if ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) or em.inquote) and
|
||||
tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]:
|
||||
if em.inquote or ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) and
|
||||
tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
|
||||
# bug #9504: remember to not spacify a keyword:
|
||||
lastTokWasTerse = true
|
||||
# if not surrounded by whitespace, don't produce any whitespace either:
|
||||
@@ -413,10 +513,9 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
em.inquote = not em.inquote
|
||||
of tkComment:
|
||||
if not preventComment:
|
||||
emitComment(em, tok)
|
||||
emitComment(em, tok, dontIndent = false)
|
||||
of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
|
||||
let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
|
||||
softLinebreak(em, lit)
|
||||
if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
|
||||
em.lineSpan = countNewlines(lit)
|
||||
if em.lineSpan > 0: calcCol(em, lit)
|
||||
@@ -424,7 +523,6 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
|
||||
of tkEof: discard
|
||||
else:
|
||||
let lit = if tok.ident != nil: tok.ident.s else: tok.literal
|
||||
softLinebreak(em, lit)
|
||||
if endsInAlpha(em): wrSpace(em)
|
||||
wr em, lit, ltIdent
|
||||
|
||||
@@ -439,6 +537,11 @@ proc endsWith(em: Emitter; k: varargs[string]): bool =
|
||||
if em.tokens[em.tokens.len - k.len + i] != k[i]: return false
|
||||
return true
|
||||
|
||||
proc rfind(em: Emitter, t: string): int =
|
||||
for i in 1 .. 5:
|
||||
if em.tokens[^i] == t:
|
||||
return i
|
||||
|
||||
proc starWasExportMarker*(em: var Emitter) =
|
||||
if em.endsWith(" ", "*", " "):
|
||||
setLen(em.tokens, em.tokens.len-3)
|
||||
@@ -449,11 +552,13 @@ proc starWasExportMarker*(em: var Emitter) =
|
||||
|
||||
proc commaWasSemicolon*(em: var Emitter) =
|
||||
if em.semicolons == detectSemicolonKind:
|
||||
em.semicolons = if em.endsWith(",", " "): dontTouch else: useSemicolon
|
||||
if em.semicolons == useSemicolon and em.endsWith(",", " "):
|
||||
em.tokens[em.tokens.len-2] = ";"
|
||||
em.semicolons = if em.rfind(";") > 0: useSemicolon else: dontTouch
|
||||
if em.semicolons == useSemicolon:
|
||||
let commaPos = em.rfind(",")
|
||||
if commaPos > 0:
|
||||
em.tokens[^commaPos] = ";"
|
||||
|
||||
proc curlyRiWasPragma*(em: var Emitter) =
|
||||
if em.endsWith("}"):
|
||||
em.tokens[em.tokens.len-1] = ".}"
|
||||
em.tokens[^1] = ".}"
|
||||
inc em.col
|
||||
|
||||
@@ -146,10 +146,6 @@ type
|
||||
previousToken: TLineInfo
|
||||
config*: ConfigRef
|
||||
|
||||
when defined(nimpretty):
|
||||
var
|
||||
gIndentationWidth*: int
|
||||
|
||||
proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
|
||||
result = newLineInfo(L.fileIdx, tok.line, tok.col)
|
||||
when defined(nimpretty):
|
||||
@@ -173,7 +169,7 @@ proc isNimIdentifier*(s: string): bool =
|
||||
inc(i)
|
||||
result = true
|
||||
|
||||
proc tokToStr*(tok: TToken): string =
|
||||
proc `$`*(tok: TToken): string =
|
||||
case tok.tokType
|
||||
of tkIntLit..tkInt64Lit: result = $tok.iNumber
|
||||
of tkFloatLit..tkFloat64Lit: result = $tok.fNumber
|
||||
@@ -188,11 +184,11 @@ proc tokToStr*(tok: TToken): string =
|
||||
|
||||
proc prettyTok*(tok: TToken): string =
|
||||
if isKeyword(tok.tokType): result = "keyword " & tok.ident.s
|
||||
else: result = tokToStr(tok)
|
||||
else: result = $tok
|
||||
|
||||
proc printTok*(conf: ConfigRef; tok: TToken) =
|
||||
msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" &
|
||||
TokTypeToStr[tok.tokType] & " " & tokToStr(tok))
|
||||
TokTypeToStr[tok.tokType] & " " & $tok)
|
||||
|
||||
proc initToken*(L: var TToken) =
|
||||
L.tokType = tkInvalid
|
||||
@@ -223,7 +219,7 @@ proc fillToken(L: var TToken) =
|
||||
proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
|
||||
cache: IdentCache; config: ConfigRef) =
|
||||
openBaseLexer(lex, inputstream)
|
||||
lex.fileIdx = fileidx
|
||||
lex.fileIdx = fileIdx
|
||||
lex.indentAhead = -1
|
||||
lex.currLineIndent = 0
|
||||
inc(lex.lineNumber, inputstream.lineOffset)
|
||||
@@ -379,7 +375,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
|
||||
result.literal = ""
|
||||
result.base = base10
|
||||
startpos = L.bufpos
|
||||
tokenBegin(result, startPos)
|
||||
tokenBegin(result, startpos)
|
||||
|
||||
# First stage: find out base, make verifications, build token literal string
|
||||
# {'c', 'C'} is added for deprecation reasons to provide a clear error message
|
||||
@@ -534,13 +530,13 @@ proc getNumber(L: var TLexer, result: var TToken) =
|
||||
|
||||
case result.tokType
|
||||
of tkIntLit, tkInt64Lit: result.iNumber = xi
|
||||
of tkInt8Lit: result.iNumber = BiggestInt(int8(toU8(int(xi))))
|
||||
of tkInt16Lit: result.iNumber = BiggestInt(int16(toU16(int(xi))))
|
||||
of tkInt32Lit: result.iNumber = BiggestInt(int32(toU32(int64(xi))))
|
||||
of tkInt8Lit: result.iNumber = ashr(xi shl 56, 56)
|
||||
of tkInt16Lit: result.iNumber = ashr(xi shl 48, 48)
|
||||
of tkInt32Lit: result.iNumber = ashr(xi shl 32, 32)
|
||||
of tkUIntLit, tkUInt64Lit: result.iNumber = xi
|
||||
of tkUInt8Lit: result.iNumber = BiggestInt(cast[uint8](toU8(int(xi))))
|
||||
of tkUInt16Lit: result.iNumber = BiggestInt(cast[uint16](toU16(int(xi))))
|
||||
of tkUInt32Lit: result.iNumber = BiggestInt(cast[uint32](toU32(int64(xi))))
|
||||
of tkUInt8Lit: result.iNumber = xi and 0xff
|
||||
of tkUInt16Lit: result.iNumber = xi and 0xffff
|
||||
of tkUInt32Lit: result.iNumber = xi and 0xffffffff
|
||||
of tkFloat32Lit:
|
||||
result.fNumber = (cast[PFloat32](addr(xi)))[]
|
||||
# note: this code is endian neutral!
|
||||
@@ -569,7 +565,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
|
||||
case result.tokType
|
||||
of floatTypes:
|
||||
result.fNumber = parseFloat(result.literal)
|
||||
of tkUint64Lit:
|
||||
of tkUInt64Lit:
|
||||
var iNumber: uint64
|
||||
var len: int
|
||||
try:
|
||||
@@ -882,6 +878,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
|
||||
var h: Hash = 0
|
||||
var pos = L.bufpos
|
||||
tokenBegin(tok, pos)
|
||||
var suspicious = false
|
||||
while true:
|
||||
var c = L.buf[pos]
|
||||
case c
|
||||
@@ -892,21 +889,26 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
|
||||
c = chr(ord(c) + (ord('a') - ord('A'))) # toLower()
|
||||
h = h !& ord(c)
|
||||
inc(pos)
|
||||
suspicious = true
|
||||
of '_':
|
||||
if L.buf[pos+1] notin SymChars:
|
||||
lexMessage(L, errGenerated, "invalid token: trailing underscore")
|
||||
break
|
||||
inc(pos)
|
||||
suspicious = true
|
||||
else: break
|
||||
tokenEnd(tok, pos-1)
|
||||
h = !$h
|
||||
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
|
||||
L.bufpos = pos
|
||||
if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
|
||||
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
|
||||
tok.tokType = tkSymbol
|
||||
else:
|
||||
tok.tokType = TTokType(tok.ident.id + ord(tkSymbol))
|
||||
if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}:
|
||||
lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s)
|
||||
L.bufpos = pos
|
||||
|
||||
|
||||
proc endOperator(L: var TLexer, tok: var TToken, pos: int,
|
||||
hash: Hash) {.inline.} =
|
||||
@@ -1119,7 +1121,7 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
inc(pos)
|
||||
inc(tok.strongSpaceA)
|
||||
of '\t':
|
||||
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabulators are not allowed")
|
||||
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
|
||||
inc(pos)
|
||||
of CR, LF:
|
||||
tokenEndPrevious(tok, pos)
|
||||
@@ -1174,8 +1176,6 @@ proc skip(L: var TLexer, tok: var TToken) =
|
||||
tok.commentOffsetB = L.offsetBase + pos - 1
|
||||
tok.tokType = tkComment
|
||||
tok.indent = commentIndent
|
||||
if gIndentationWidth <= 0:
|
||||
gIndentationWidth = tok.indent
|
||||
|
||||
proc rawGetTok*(L: var TLexer, tok: var TToken) =
|
||||
template atTokenEnd() {.dirty.} =
|
||||
@@ -1334,10 +1334,13 @@ proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
|
||||
var tok: TToken
|
||||
initToken(tok)
|
||||
openLexer(lex, fileIdx, inputstream, cache, config)
|
||||
while true:
|
||||
var prevToken = tkEof
|
||||
while tok.tokType != tkEof:
|
||||
rawGetTok(lex, tok)
|
||||
result = tok.indent
|
||||
if result > 0 or tok.tokType == tkEof: break
|
||||
if tok.indent > 0 and prevToken in {tkColon, tkEquals, tkType, tkConst, tkLet, tkVar, tkUsing}:
|
||||
result = tok.indent
|
||||
if result > 0: break
|
||||
prevToken = tok.tokType
|
||||
closeLexer(lex)
|
||||
|
||||
proc getPrecedence*(ident: PIdent): int =
|
||||
|
||||
@@ -10,28 +10,27 @@
|
||||
## This module implements lifting for type-bound operations
|
||||
## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
|
||||
|
||||
# included from sempass2.nim
|
||||
|
||||
# Todo:
|
||||
# - use openArray instead of array to avoid over-specializations
|
||||
|
||||
import sighashes
|
||||
import modulegraphs, lineinfos, idents, ast, astalgo, renderer, semdata,
|
||||
sighashes, lowerings, options, types, msgs, magicsys, tables
|
||||
|
||||
type
|
||||
TLiftCtx = object
|
||||
graph: ModuleGraph
|
||||
g: ModuleGraph
|
||||
info: TLineInfo # for construction
|
||||
kind: TTypeAttachedOp
|
||||
fn: PSym
|
||||
asgnForType: PType
|
||||
recurse: bool
|
||||
c: PContext
|
||||
c: PContext # c can be nil, then we are called from lambdalifting!
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||
proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo): PSym
|
||||
|
||||
proc createTypeBoundOps*(c: PContext; orig: PType; info: TLineInfo)
|
||||
proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo)
|
||||
|
||||
proc at(a, i: PNode, elemType: PType): PNode =
|
||||
result = newNodeI(nkBracketExpr, a.info, 2)
|
||||
@@ -41,7 +40,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||
|
||||
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
for i in 0 ..< t.len:
|
||||
let lit = lowerings.newIntLit(c.graph, x.info, i)
|
||||
let lit = lowerings.newIntLit(c.g, x.info, i)
|
||||
fillBody(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
|
||||
|
||||
proc dotField(x: PNode, f: PSym): PNode =
|
||||
@@ -88,7 +87,12 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
|
||||
of nkRecList:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y)
|
||||
else:
|
||||
illFormedAstLocal(n, c.graph.config)
|
||||
illFormedAstLocal(n, c.g.config)
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
if t.len > 0 and t.sons[0] != nil:
|
||||
fillBodyObjT(c, skipTypes(t.sons[0], abstractPtrs), body, x, y)
|
||||
fillBodyObj(c, t.n, body, x, y)
|
||||
|
||||
proc genAddr(g: ModuleGraph; x: PNode): PNode =
|
||||
if x.kind == nkHiddenDeref:
|
||||
@@ -125,20 +129,28 @@ proc newDeepCopyCall(op: PSym; x, y: PNode): PNode =
|
||||
result = newAsgnStmt(x, newOpCall(op, y))
|
||||
|
||||
proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} =
|
||||
result = optNimV2 in c.graph.config.globalOptions and
|
||||
result = optNimV2 in c.g.config.globalOptions and
|
||||
({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem)
|
||||
|
||||
proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym =
|
||||
if c.c != nil and typeInst != nil:
|
||||
result = c.c.instTypeBoundOp(c.c, op, typeInst, c.info, attachedAsgn, 1)
|
||||
else:
|
||||
localError(c.g.config, c.info,
|
||||
"cannot generate destructor for generic type: " & typeToString(t))
|
||||
result = nil
|
||||
|
||||
proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
field: var PSym): bool =
|
||||
if optNimV2 in c.graph.config.globalOptions:
|
||||
if optNimV2 in c.g.config.globalOptions:
|
||||
let op = field
|
||||
if field != nil and sfOverriden in field.flags:
|
||||
if sfError in op.flags:
|
||||
incl c.fn.flags, sfError
|
||||
else:
|
||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||
#else:
|
||||
# markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
body.add newAsgnCall(c.graph, op, x, y)
|
||||
body.add newAsgnCall(c.g, op, x, y)
|
||||
result = true
|
||||
elif tfHasAsgn in t.flags:
|
||||
var op: PSym
|
||||
@@ -152,38 +164,37 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
else:
|
||||
op = field
|
||||
if op == nil:
|
||||
op = produceSym(c.c, t, c.kind, c.info)
|
||||
op = produceSym(c.g, c.c, t, c.kind, c.info)
|
||||
if sfError in op.flags:
|
||||
incl c.fn.flags, sfError
|
||||
else:
|
||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||
#else:
|
||||
# markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
# We also now do generic instantiations in the destructor lifting pass:
|
||||
if op.ast[genericParamsPos].kind != nkEmpty:
|
||||
assert t.typeInst != nil
|
||||
op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1)
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
field = op
|
||||
#echo "trying to use ", op.ast
|
||||
#echo "for ", op.name.s, " "
|
||||
#debug(t)
|
||||
#return false
|
||||
assert op.ast[genericParamsPos].kind == nkEmpty
|
||||
body.add newAsgnCall(c.graph, op, x, y)
|
||||
body.add newAsgnCall(c.g, op, x, y)
|
||||
result = true
|
||||
|
||||
proc addDestructorCall(c: var TLiftCtx; t: PType; body, x: PNode) =
|
||||
var op = t.destructor
|
||||
if op == nil and useNoGc(c, t):
|
||||
op = produceSym(c.c, t, attachedDestructor, c.info)
|
||||
op = produceSym(c.g, c.c, t, attachedDestructor, c.info)
|
||||
doAssert op != nil
|
||||
doAssert op == t.destructor
|
||||
|
||||
if op != nil:
|
||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
body.add destructorCall(c.graph, op, x)
|
||||
body.add destructorCall(c.g, op, x)
|
||||
elif useNoGc(c, t):
|
||||
internalError(c.graph.config, c.info,
|
||||
internalError(c.g.config, c.info,
|
||||
"type-bound operator could not be resolved")
|
||||
|
||||
proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
@@ -193,14 +204,13 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
|
||||
if op.ast[genericParamsPos].kind != nkEmpty:
|
||||
assert t.typeInst != nil
|
||||
# patch generic destructor:
|
||||
op = c.c.instTypeBoundOp(c.c, op, t.typeInst, c.info, attachedAsgn, 1)
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
t.attachedOps[attachedDestructor] = op
|
||||
|
||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
body.add destructorCall(c.graph, op, x)
|
||||
body.add destructorCall(c.g, op, x)
|
||||
result = true
|
||||
#result = addDestructorCall(c, t, body, x)
|
||||
of attachedAsgn:
|
||||
@@ -210,7 +220,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
of attachedDeepCopy:
|
||||
let op = t.attachedOps[attachedDeepCopy]
|
||||
if op != nil:
|
||||
markUsed(c.graph.config, c.info, op, c.graph.usageSym)
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
body.add newDeepCopyCall(op, x, y)
|
||||
result = true
|
||||
@@ -227,13 +237,13 @@ proc addVar(father, v, value: PNode) =
|
||||
addSon(father, vpart)
|
||||
|
||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.graph.cache, lowerings.genPrefix), c.fn, c.info)
|
||||
temp.typ = getSysType(c.graph, body.info, tyInt)
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.fn, c.info)
|
||||
temp.typ = getSysType(c.g, body.info, tyInt)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
result = newSymNode(temp)
|
||||
v.addVar(result, lowerings.newIntLit(c.graph, body.info, first))
|
||||
v.addVar(result, lowerings.newIntLit(c.g, body.info, first))
|
||||
body.add v
|
||||
|
||||
proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
|
||||
@@ -243,9 +253,9 @@ proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
|
||||
|
||||
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
|
||||
result = newNodeI(nkWhileStmt, c.info, 2)
|
||||
let cmp = genBuiltin(c.graph, mLtI, "<", i)
|
||||
cmp.add genLen(c.graph, dest)
|
||||
cmp.typ = getSysType(c.graph, c.info, tyBool)
|
||||
let cmp = genBuiltin(c.g, mLtI, "<", i)
|
||||
cmp.add genLen(c.g, dest)
|
||||
cmp.typ = getSysType(c.g, c.info, tyBool)
|
||||
result.sons[0] = cmp
|
||||
result.sons[1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
@@ -253,8 +263,8 @@ proc genIf(c: var TLiftCtx; cond, action: PNode): PNode =
|
||||
result = newTree(nkIfStmt, newTree(nkElifBranch, cond, action))
|
||||
|
||||
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
|
||||
let incCall = genBuiltin(c.graph, mInc, "inc", i)
|
||||
incCall.add lowerings.newIntLit(c.graph, c.info, 1)
|
||||
let incCall = genBuiltin(c.g, mInc, "inc", i)
|
||||
incCall.add lowerings.newIntLit(c.g, c.info, 1)
|
||||
body.add incCall
|
||||
|
||||
proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode =
|
||||
@@ -271,18 +281,18 @@ proc setLenStrCall(g: ModuleGraph; x, y: PNode): PNode =
|
||||
result.add lenCall
|
||||
|
||||
proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode =
|
||||
let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.graph, x.info, tyInt)
|
||||
var op = getSysMagic(c.graph, x.info, "setLen", mSetLengthSeq)
|
||||
op = c.c.instTypeBoundOp(c.c, op, t, c.info, attachedAsgn, 1)
|
||||
let lenCall = genBuiltin(c.g, mLengthSeq, "len", y)
|
||||
lenCall.typ = getSysType(c.g, x.info, tyInt)
|
||||
var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq)
|
||||
op = instantiateGeneric(c, op, t, t)
|
||||
result = newTree(nkCall, newSymNode(op, x.info), x, lenCall)
|
||||
|
||||
proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let i = declareCounter(c, body, firstOrd(c.graph.config, t))
|
||||
let i = declareCounter(c, body, firstOrd(c.g.config, t))
|
||||
let whileLoop = genWhileLoop(c, i, x)
|
||||
let elemType = t.lastSon
|
||||
fillBody(c, elemType, whileLoop.sons[1], x.at(i, elemType),
|
||||
y.at(i, elemType))
|
||||
y.at(i, elemType))
|
||||
addIncStmt(c, whileLoop.sons[1], i)
|
||||
body.add whileLoop
|
||||
|
||||
@@ -297,63 +307,69 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedSink:
|
||||
let moveCall = genBuiltin(c.graph, mMove, "move", x)
|
||||
let moveCall = genBuiltin(c.g, mMove, "move", x)
|
||||
moveCall.add y
|
||||
doAssert t.destructor != nil
|
||||
moveCall.add destructorCall(c.graph, t.destructor, x)
|
||||
moveCall.add destructorCall(c.g, t.destructor, x)
|
||||
body.add moveCall
|
||||
of attachedDestructor:
|
||||
# destroy all elements:
|
||||
forallElements(c, t, body, x, y)
|
||||
body.add genBuiltin(c.graph, mDestroy, "destroy", x)
|
||||
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
||||
|
||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
createTypeBoundOps(c.c, t, body.info)
|
||||
createTypeBoundOps(c.g, c.c, t, body.info)
|
||||
case c.kind
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
doAssert t.assignment != nil
|
||||
body.add newAsgnCall(c.graph, t.assignment, x, y)
|
||||
body.add newAsgnCall(c.g, t.assignment, x, y)
|
||||
of attachedSink:
|
||||
# we always inline the move for better performance:
|
||||
let moveCall = genBuiltin(c.graph, mMove, "move", x)
|
||||
let moveCall = genBuiltin(c.g, mMove, "move", x)
|
||||
moveCall.add y
|
||||
doAssert t.destructor != nil
|
||||
moveCall.add destructorCall(c.graph, t.destructor, x)
|
||||
moveCall.add destructorCall(c.g, t.destructor, x)
|
||||
body.add moveCall
|
||||
# alternatively we could do this:
|
||||
when false:
|
||||
doAssert t.asink != nil
|
||||
body.add newAsgnCall(c.graph, t.asink, x, y)
|
||||
body.add newAsgnCall(c.g, t.asink, x, y)
|
||||
of attachedDestructor:
|
||||
doAssert t.destructor != nil
|
||||
body.add destructorCall(c.graph, t.destructor, x)
|
||||
body.add destructorCall(c.g, t.destructor, x)
|
||||
|
||||
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
body.add callCodegenProc(c.graph, "nimAsgnStrV2", c.info, genAddr(c.graph, x), y)
|
||||
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c.g, x), y)
|
||||
of attachedSink:
|
||||
let moveCall = genBuiltin(c.graph, mMove, "move", x)
|
||||
let moveCall = genBuiltin(c.g, mMove, "move", x)
|
||||
moveCall.add y
|
||||
doAssert t.destructor != nil
|
||||
moveCall.add destructorCall(c.graph, t.destructor, x)
|
||||
moveCall.add destructorCall(c.g, t.destructor, x)
|
||||
body.add moveCall
|
||||
of attachedDestructor:
|
||||
body.add genBuiltin(c.graph, mDestroy, "destroy", x)
|
||||
body.add genBuiltin(c.g, mDestroy, "destroy", x)
|
||||
|
||||
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
# we 'nil' y out afterwards so we *need* to take over its reference
|
||||
# count value:
|
||||
body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
|
||||
body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
body.add genIf(c, y, callCodegenProc(c.graph, "nimIncWeakRef", c.info, y))
|
||||
body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
|
||||
body.add genIf(c, y, callCodegenProc(c.g, "nimIncWeakRef", c.info, y))
|
||||
body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDestructor:
|
||||
body.add genIf(c, x, callCodegenProc(c.graph, "nimDecWeakRef", c.info, x))
|
||||
# it's better to prepend the destruction of weak refs in order to
|
||||
# prevent wrong "dangling refs exist" problems:
|
||||
let des = genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
|
||||
if body.len == 0:
|
||||
body.add des
|
||||
else:
|
||||
body.sons.insert(des, 0)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
|
||||
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
@@ -361,14 +377,14 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
|
||||
let elemType = t.lastSon
|
||||
#fillBody(c, elemType, actions, genDeref(x), genDeref(y))
|
||||
#var disposeCall = genBuiltin(c.graph, mDispose, "dispose", x)
|
||||
#var disposeCall = genBuiltin(c.g, mDispose, "dispose", x)
|
||||
|
||||
if isFinal(elemType):
|
||||
addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr))
|
||||
actions.add callCodegenProc(c.graph, "nimRawDispose", c.info, x)
|
||||
actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x)
|
||||
else:
|
||||
addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr))
|
||||
actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, x)
|
||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x)
|
||||
|
||||
case c.kind
|
||||
of attachedSink, attachedAsgn:
|
||||
@@ -384,35 +400,40 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# have to go through some indirection; we delegate this to the codegen:
|
||||
let call = newNodeI(nkCall, c.info, 2)
|
||||
call.typ = t
|
||||
call.sons[0] = newSymNode(createMagic(c.graph, "deepCopy", mDeepCopy))
|
||||
call.sons[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy))
|
||||
call.sons[1] = y
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif optNimV2 in c.graph.config.globalOptions:
|
||||
let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.graph, c.info, tyPointer)
|
||||
elif optNimV2 in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options:
|
||||
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
case c.kind
|
||||
of attachedSink:
|
||||
# we 'nil' y out afterwards so we *need* to take over its reference
|
||||
# count value:
|
||||
body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx))
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedAsgn:
|
||||
let yy = genBuiltin(c.graph, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.graph, c.info, tyPointer)
|
||||
body.add genIf(c, yy, callCodegenProc(c.graph, "nimIncWeakRef", c.info, yy))
|
||||
body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx))
|
||||
let yy = genBuiltin(c.g, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncWeakRef", c.info, yy))
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDestructor:
|
||||
body.add genIf(c, xx, callCodegenProc(c.graph, "nimDecWeakRef", c.info, xx))
|
||||
let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
if body.len == 0:
|
||||
body.add des
|
||||
else:
|
||||
body.sons.insert(des, 0)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
|
||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c.graph, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.graph, c.info, tyPointer)
|
||||
let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
let elemType = t.lastSon
|
||||
#discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx))
|
||||
actions.add callCodegenProc(c.graph, "nimDestroyAndDispose", c.info, xx)
|
||||
actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xx)
|
||||
case c.kind
|
||||
of attachedSink, attachedAsgn:
|
||||
body.add genIf(c, xx, actions)
|
||||
@@ -428,7 +449,8 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
tyPtr, tyOpt, tyUncheckedArray:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyRef:
|
||||
if optNimV2 in c.graph.config.globalOptions:
|
||||
if optNimV2 in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options:
|
||||
weakrefOp(c, t, body, x, y)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
@@ -439,7 +461,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyOwned:
|
||||
let base = t.skipTypes(abstractInstOwned)
|
||||
if optNimV2 in c.graph.config.globalOptions:
|
||||
if optNimV2 in c.g.config.globalOptions:
|
||||
case base.kind
|
||||
of tyRef:
|
||||
ownedRefOp(c, base, body, x, y)
|
||||
@@ -458,13 +480,13 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of tySequence:
|
||||
if useNoGc(c, t):
|
||||
useSeqOrStrOp(c, t, body, x, y)
|
||||
elif c.graph.config.selectedGC == gcDestructors:
|
||||
elif c.g.config.selectedGC == gcDestructors:
|
||||
# note that tfHasAsgn is propagated so we need the check on
|
||||
# 'selectedGC' here to determine if we have the new runtime.
|
||||
discard considerUserDefinedOp(c, t, body, x, y)
|
||||
elif tfHasAsgn in t.flags:
|
||||
if c.kind != attachedDestructor:
|
||||
body.add newSeqCall(c.graph, x, y)
|
||||
body.add newSeqCall(c.g, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
else:
|
||||
defaultOp(c, t, body, x, y)
|
||||
@@ -477,19 +499,19 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyObject:
|
||||
if not considerUserDefinedOp(c, t, body, x, y):
|
||||
fillBodyObj(c, t.n, body, x, y)
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
of tyDistinct:
|
||||
if not considerUserDefinedOp(c, t, body, x, y):
|
||||
fillBody(c, t.sons[0].skipTypes(skipPtrs), body, x, y)
|
||||
of tyTuple:
|
||||
fillBodyTup(c, t, body, x, y)
|
||||
of tyVarargs, tyOpenArray:
|
||||
localError(c.graph.config, c.info, "cannot copy openArray")
|
||||
localError(c.g.config, c.info, "cannot copy openArray")
|
||||
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
|
||||
tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
|
||||
tyTypeDesc, tyGenericInvocation, tyForward:
|
||||
#internalError(c.graph.config, c.info, "assignment requested for type: " & typeToString(t))
|
||||
#internalError(c.g.config, c.info, "assignment requested for type: " & typeToString(t))
|
||||
discard
|
||||
of tyVar, tyLent:
|
||||
if c.kind != attachedDestructor:
|
||||
@@ -498,25 +520,24 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
tyGenericInst, tyStatic, tyAlias, tySink:
|
||||
fillBody(c, lastSon(t), body, x, y)
|
||||
|
||||
proc produceSymDistinctType(c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
|
||||
proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym =
|
||||
assert typ.kind == tyDistinct
|
||||
let baseType = typ[0]
|
||||
if baseType.attachedOps[kind] == nil:
|
||||
discard produceSym(c, baseType, kind, info)
|
||||
discard produceSym(g, c, baseType, kind, info)
|
||||
typ.attachedOps[kind] = baseType.attachedOps[kind]
|
||||
result = typ.attachedOps[kind]
|
||||
|
||||
proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo): PSym =
|
||||
if typ.kind == tyDistinct:
|
||||
return produceSymDistinctType(c, typ, kind, info)
|
||||
return produceSymDistinctType(g, c, typ, kind, info)
|
||||
|
||||
var a: TLiftCtx
|
||||
a.info = info
|
||||
a.graph = c.graph
|
||||
a.g = g
|
||||
a.kind = kind
|
||||
a.c = c
|
||||
let g = c.graph
|
||||
let body = newNodeI(nkStmtList, info)
|
||||
let procname = getIdent(g.cache, AttachedOpToStr[kind])
|
||||
|
||||
@@ -538,7 +559,7 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
typ.attachedOps[kind] = result
|
||||
|
||||
var tk: TTypeKind
|
||||
if optNimV2 in c.graph.config.globalOptions:
|
||||
if optNimV2 in g.config.globalOptions:
|
||||
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
|
||||
else:
|
||||
tk = tyNone # no special casing for strings and seqs
|
||||
@@ -562,33 +583,39 @@ proc produceSym(c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
|
||||
discard "now a nop"
|
||||
|
||||
proc patchBody(c: PContext; n: PNode; info: TLineInfo) =
|
||||
proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo) =
|
||||
if n.kind in nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mDestroy:
|
||||
let t = n[1].typ.skipTypes(abstractVar)
|
||||
if t.destructor == nil:
|
||||
discard produceSym(c, t, attachedDestructor, info)
|
||||
discard produceSym(g, c, t, attachedDestructor, info)
|
||||
|
||||
if t.destructor != nil:
|
||||
if t.destructor.ast[genericParamsPos].kind != nkEmpty:
|
||||
internalError(c.graph.config, info, "resolved destructor is generic")
|
||||
internalError(g.config, info, "resolved destructor is generic")
|
||||
if t.destructor.magic == mDestroy:
|
||||
internalError(c.graph.config, info, "patching mDestroy with mDestroy?")
|
||||
internalError(g.config, info, "patching mDestroy with mDestroy?")
|
||||
n.sons[0] = newSymNode(t.destructor)
|
||||
for x in n: patchBody(c, x, info)
|
||||
for x in n: patchBody(g, c, x, info)
|
||||
|
||||
template inst(field, t) =
|
||||
if field.ast != nil and field.ast[genericParamsPos].kind != nkEmpty:
|
||||
if t.typeInst != nil:
|
||||
field = c.instTypeBoundOp(c, field, t.typeInst, info, attachedAsgn, 1)
|
||||
var a: TLiftCtx
|
||||
a.info = info
|
||||
a.g = g
|
||||
a.kind = k
|
||||
a.c = c
|
||||
|
||||
field = instantiateGeneric(a, field, t, t.typeInst)
|
||||
if field.ast != nil:
|
||||
patchBody(c, field.ast, info)
|
||||
patchBody(g, c, field.ast, info)
|
||||
else:
|
||||
localError(c.graph.config, info, "unresolved generic parameter")
|
||||
localError(g.config, info, "unresolved generic parameter")
|
||||
|
||||
proc isTrival(s: PSym): bool {.inline.} = s == nil or s.ast[bodyPos].len == 0
|
||||
|
||||
proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
|
||||
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo) =
|
||||
## In the semantic pass this is called in strategic places
|
||||
## to ensure we lift assignment, destructors and moves properly.
|
||||
## The later 'injectdestructors' pass depends on it.
|
||||
@@ -596,11 +623,11 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
|
||||
incl orig.flags, tfCheckedForDestructor
|
||||
|
||||
let h = sighashes.hashType(orig, {CoType, CoConsiderOwned, CoDistinct})
|
||||
var canon = c.graph.canonTypes.getOrDefault(h)
|
||||
var canon = g.canonTypes.getOrDefault(h)
|
||||
var overwrite = false
|
||||
if canon == nil:
|
||||
let typ = orig.skipTypes({tyGenericInst, tyAlias})
|
||||
c.graph.canonTypes[h] = typ
|
||||
g.canonTypes[h] = typ
|
||||
canon = typ
|
||||
elif canon != orig:
|
||||
overwrite = true
|
||||
@@ -615,7 +642,7 @@ proc createTypeBoundOps(c: PContext; orig: PType; info: TLineInfo) =
|
||||
# we generate the destructor first so that other operators can depend on it:
|
||||
for k in attachedDestructor..attachedSink:
|
||||
if canon.attachedOps[k] == nil:
|
||||
discard produceSym(c, canon, k, info)
|
||||
discard produceSym(g, c, canon, k, info)
|
||||
else:
|
||||
inst(canon.attachedOps[k], canon)
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ const
|
||||
hintPath: "added path: '$1'",
|
||||
hintConditionAlwaysTrue: "condition is always true: '$1'",
|
||||
hintConditionAlwaysFalse: "condition is always false: '$1'",
|
||||
hintName: "name should be: '$1'",
|
||||
hintName: "$1",
|
||||
hintPattern: "$1",
|
||||
hintExecuting: "$1",
|
||||
hintLinking: "",
|
||||
@@ -193,7 +193,7 @@ type
|
||||
# used for better error messages and
|
||||
# embedding the original source in the
|
||||
# generated code
|
||||
dirtyfile*: AbsoluteFile # the file that is actually read into memory
|
||||
dirtyFile*: AbsoluteFile # the file that is actually read into memory
|
||||
# and parsed; usually "" but is used
|
||||
# for 'nimsuggest'
|
||||
hash*: string # the checksum of the file
|
||||
@@ -229,12 +229,12 @@ proc raiseRecoverableError*(msg: string) {.noinline.} =
|
||||
raise newException(ERecoverableError, msg)
|
||||
|
||||
const
|
||||
InvalidFileIDX* = FileIndex(-1)
|
||||
InvalidFileIdx* = FileIndex(-1)
|
||||
|
||||
proc unknownLineInfo*(): TLineInfo =
|
||||
result.line = uint16(0)
|
||||
result.col = int16(-1)
|
||||
result.fileIndex = InvalidFileIDX
|
||||
result.fileIndex = InvalidFileIdx
|
||||
|
||||
type
|
||||
Severity* {.pure.} = enum ## VS Code only supports these three
|
||||
|
||||
@@ -13,7 +13,7 @@ import
|
||||
strutils, os, intsets, strtabs
|
||||
|
||||
import options, ast, astalgo, msgs, semdata, ropes, idents,
|
||||
lineinfos, pathutils
|
||||
lineinfos, pathutils, wordrecg
|
||||
|
||||
const
|
||||
Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
|
||||
@@ -22,33 +22,6 @@ proc identLen*(line: string, start: int): int =
|
||||
while start+result < line.len and line[start+result] in Letters:
|
||||
inc result
|
||||
|
||||
type
|
||||
StyleCheck* {.pure.} = enum None, Warn, Auto
|
||||
|
||||
var
|
||||
gOverWrite* = true
|
||||
gStyleCheck*: StyleCheck
|
||||
gCheckExtern*, gOnlyMainfile*: bool
|
||||
|
||||
proc overwriteFiles*(conf: ConfigRef) =
|
||||
let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on"
|
||||
for i in 0 .. high(conf.m.fileInfos):
|
||||
if conf.m.fileInfos[i].dirty and
|
||||
(not gOnlyMainfile or FileIndex(i) == conf.projectMainIdx):
|
||||
let newFile = if gOverWrite: conf.m.fileInfos[i].fullpath
|
||||
else: conf.m.fileInfos[i].fullpath.changeFileExt(".pretty.nim")
|
||||
try:
|
||||
var f = open(newFile.string, fmWrite)
|
||||
for line in conf.m.fileInfos[i].lines:
|
||||
if doStrip:
|
||||
f.write line.strip(leading = false, trailing = true)
|
||||
else:
|
||||
f.write line
|
||||
f.write(conf.m.fileInfos[i], "\L")
|
||||
f.close
|
||||
except IOError:
|
||||
rawMessage(conf, errGenerated, "cannot open file: " & newFile.string)
|
||||
|
||||
proc `=~`(s: string, a: openArray[string]): bool =
|
||||
for x in a:
|
||||
if s.startsWith(x): return true
|
||||
@@ -69,7 +42,7 @@ proc beautifyName(s: string, k: TSymKind): string =
|
||||
"pointer", "float", "csize", "cdouble", "cchar", "cschar",
|
||||
"cshort", "cu", "nil", "typedesc", "auto", "any",
|
||||
"range", "openarray", "varargs", "set", "cfloat", "ref", "ptr",
|
||||
"untyped", "typed", "static", "sink", "lent", "type"]:
|
||||
"untyped", "typed", "static", "sink", "lent", "type", "owned"]:
|
||||
result.add s[i]
|
||||
else:
|
||||
result.add toUpperAscii(s[i])
|
||||
@@ -98,47 +71,26 @@ proc beautifyName(s: string, k: TSymKind): string =
|
||||
result.add s[i]
|
||||
inc i
|
||||
|
||||
proc differ*(line: string, a, b: int, x: string): bool =
|
||||
let y = line[a..b]
|
||||
result = cmpIgnoreStyle(y, x) == 0 and y != x
|
||||
proc differ*(line: string, a, b: int, x: string): string =
|
||||
proc substrEq(s: string, pos, last: int, substr: string): bool =
|
||||
var i = 0
|
||||
var length = substr.len
|
||||
while i < length and pos+i <= last and s[pos+i] == substr[i]:
|
||||
inc i
|
||||
return i == length
|
||||
|
||||
proc replaceInFile(conf: ConfigRef; info: TLineInfo; newName: string) =
|
||||
let line = conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1]
|
||||
var first = min(info.col.int, line.len)
|
||||
if first < 0: return
|
||||
#inc first, skipIgnoreCase(line, "proc ", first)
|
||||
while first > 0 and line[first-1] in Letters: dec first
|
||||
if first < 0: return
|
||||
if line[first] == '`': inc first
|
||||
let last = min(b, line.len)
|
||||
|
||||
let last = first+identLen(line, first)-1
|
||||
if differ(line, first, last, newName):
|
||||
# last-first+1 != newName.len or
|
||||
var x = line.substr(0, first-1) & newName & line.substr(last+1)
|
||||
system.shallowCopy(conf.m.fileInfos[info.fileIndex.int].lines[info.line.int-1], x)
|
||||
conf.m.fileInfos[info.fileIndex.int].dirty = true
|
||||
|
||||
proc lintReport(conf: ConfigRef; info: TLineInfo, beau: string) =
|
||||
if optStyleError in conf.globalOptions:
|
||||
localError(conf, info, "name should be: '$1'" % beau)
|
||||
else:
|
||||
message(conf, info, hintName, beau)
|
||||
result = ""
|
||||
if not substrEq(line, a, b, x):
|
||||
let y = line[a..b]
|
||||
if cmpIgnoreStyle(y, x) == 0:
|
||||
result = y
|
||||
|
||||
proc checkStyle(conf: ConfigRef; cache: IdentCache; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
|
||||
let beau = beautifyName(s, k)
|
||||
if s != beau:
|
||||
if gStyleCheck == StyleCheck.Auto:
|
||||
sym.name = getIdent(cache, beau)
|
||||
replaceInFile(conf, info, beau)
|
||||
else:
|
||||
lintReport(conf, info, beau)
|
||||
|
||||
proc styleCheckDefImpl(conf: ConfigRef; cache: IdentCache; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
# operators stay as they are:
|
||||
if k in {skResult, skTemp} or s.name.s[0] notin Letters: return
|
||||
if k in {skType, skGenericParam} and sfAnon in s.flags: return
|
||||
if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
|
||||
checkStyle(conf, cache, info, s.name.s, k, s)
|
||||
lintReport(conf, info, beau, s)
|
||||
|
||||
proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
# operators stay as they are:
|
||||
@@ -146,35 +98,47 @@ proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
if k in {skType, skGenericParam} and sfAnon in s.flags: return
|
||||
if s.typ != nil and s.typ.kind == tyTypeDesc: return
|
||||
if {sfImportc, sfExportc} * s.flags != {}: return
|
||||
if optStyleCheck notin s.options: return
|
||||
let beau = beautifyName(s.name.s, k)
|
||||
if s.name.s != beau:
|
||||
lintReport(conf, info, beau)
|
||||
lintReport(conf, info, beau, s.name.s)
|
||||
|
||||
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
if {optStyleHint, optStyleError} * conf.globalOptions != {}:
|
||||
nep1CheckDefImpl(conf, info, s, k)
|
||||
when defined(nimfix):
|
||||
if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, cache, info, s, k)
|
||||
|
||||
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
styleCheckDef(conf, info, s, s.kind)
|
||||
template styleCheckDef*(conf: ConfigRef; s: PSym) =
|
||||
styleCheckDef(conf, s.info, s, s.kind)
|
||||
|
||||
proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
|
||||
let line = sourceLine(conf, info)
|
||||
var first = min(info.col.int, line.len)
|
||||
if first < 0: return
|
||||
#inc first, skipIgnoreCase(line, "proc ", first)
|
||||
while first > 0 and line[first-1] in Letters: dec first
|
||||
if first < 0: return
|
||||
if first+1 < line.len and line[first] == '`': inc first
|
||||
|
||||
let last = first+identLen(line, first)-1
|
||||
result = differ(line, first, last, newName)
|
||||
|
||||
proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
if info.fileIndex.int < 0: return
|
||||
# we simply convert it to what it looks like in the definition
|
||||
# for consistency
|
||||
|
||||
# operators stay as they are:
|
||||
if s.kind in {skResult, skTemp} or s.name.s[0] notin Letters:
|
||||
if s.kind == skTemp or s.name.s[0] notin Letters or sfAnon in s.flags:
|
||||
return
|
||||
if s.kind in {skType, skGenericParam} and sfAnon in s.flags: return
|
||||
|
||||
let newName = s.name.s
|
||||
let oldName = differs(conf, info, newName)
|
||||
if oldName.len > 0:
|
||||
lintReport(conf, info, newName, oldName)
|
||||
|
||||
replaceInFile(conf, info, newName)
|
||||
#if newName == "File": writeStackTrace()
|
||||
|
||||
template styleCheckUse*(info: TLineInfo; s: PSym) =
|
||||
when defined(nimfix):
|
||||
if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s)
|
||||
proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
|
||||
let wanted = canonPragmaSpelling(w)
|
||||
if pragmaName != wanted:
|
||||
lintReport(conf, info, wanted, pragmaName)
|
||||
|
||||
@@ -43,12 +43,12 @@ proc addVar*(father, v: PNode) =
|
||||
vpart.sons[2] = vpart[1]
|
||||
addSon(father, vpart)
|
||||
|
||||
proc newAsgnStmt(le, ri: PNode): PNode =
|
||||
proc newAsgnStmt*(le, ri: PNode): PNode =
|
||||
result = newNodeI(nkAsgn, le.info, 2)
|
||||
result.sons[0] = le
|
||||
result.sons[1] = ri
|
||||
|
||||
proc newFastAsgnStmt(le, ri: PNode): PNode =
|
||||
proc newFastAsgnStmt*(le, ri: PNode): PNode =
|
||||
result = newNodeI(nkFastAsgn, le.info, 2)
|
||||
result.sons[0] = le
|
||||
result.sons[1] = ri
|
||||
@@ -155,6 +155,7 @@ proc rawAddField*(obj: PType; field: PSym) =
|
||||
assert field.kind == skField
|
||||
field.position = sonsLen(obj.n)
|
||||
addSon(obj.n, newSymNode(field))
|
||||
propagateToOwner(obj, field.typ)
|
||||
|
||||
proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
|
||||
# returns a[].field as a node
|
||||
@@ -205,6 +206,7 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache) =
|
||||
let t = skipIntLit(s.typ)
|
||||
field.typ = t
|
||||
assert t.kind != tyTyped
|
||||
propagateToOwner(obj, t)
|
||||
field.position = sonsLen(obj.n)
|
||||
addSon(obj.n, newSymNode(field))
|
||||
|
||||
@@ -217,11 +219,12 @@ proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache): PSym {.discardable
|
||||
let t = skipIntLit(s.typ)
|
||||
field.typ = t
|
||||
assert t.kind != tyTyped
|
||||
propagateToOwner(obj, t)
|
||||
field.position = sonsLen(obj.n)
|
||||
addSon(obj.n, newSymNode(field))
|
||||
result = field
|
||||
|
||||
proc newDotExpr(obj, b: PSym): PNode =
|
||||
proc newDotExpr*(obj, b: PSym): PNode =
|
||||
result = newNodeI(nkDotExpr, obj.info)
|
||||
let field = lookupInRecord(obj.typ.n, b.id)
|
||||
assert field != nil, b.name.s
|
||||
@@ -252,7 +255,7 @@ proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
|
||||
addSon(result, newSymNode(field))
|
||||
result.typ = field.typ
|
||||
|
||||
proc indirectAccess(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
|
||||
proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
|
||||
# returns a[].b as a node
|
||||
var deref = newNodeI(nkHiddenDeref, info)
|
||||
deref.typ = a.typ.skipTypes(abstractInst).sons[0]
|
||||
@@ -322,237 +325,6 @@ proc callCodegenProc*(g: ModuleGraph; name: string;
|
||||
result.add optionalArgs[i]
|
||||
result.typ = sym.typ.sons[0]
|
||||
|
||||
proc callProc(a: PNode): PNode =
|
||||
result = newNodeI(nkCall, a.info)
|
||||
result.add a
|
||||
result.typ = a.typ.sons[0]
|
||||
|
||||
# we have 4 cases to consider:
|
||||
# - a void proc --> nothing to do
|
||||
# - a proc returning GC'ed memory --> requires a flowVar
|
||||
# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
|
||||
# - not in a parallel environment --> requires a flowVar for memory safety
|
||||
type
|
||||
TSpawnResult* = enum
|
||||
srVoid, srFlowVar, srByVar
|
||||
TFlowVarKind = enum
|
||||
fvInvalid # invalid type T for 'FlowVar[T]'
|
||||
fvGC # FlowVar of a GC'ed type
|
||||
fvBlob # FlowVar of a blob type
|
||||
|
||||
proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
|
||||
if t.isEmptyType: srVoid
|
||||
elif inParallel and not containsGarbageCollectedRef(t): srByVar
|
||||
else: srFlowVar
|
||||
|
||||
proc flowVarKind(t: PType): TFlowVarKind =
|
||||
if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
|
||||
elif containsGarbageCollectedRef(t): fvInvalid
|
||||
else: fvBlob
|
||||
|
||||
proc typeNeedsNoDeepCopy(t: PType): bool =
|
||||
var t = t.skipTypes(abstractInst)
|
||||
# for the tconvexhull example (and others) we're a bit lax here and pretend
|
||||
# seqs and strings are *by value* only and 'shallow' doesn't exist!
|
||||
if t.kind == tyString: return true
|
||||
# note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
|
||||
# for the stricter check and likewise we can skip 'seq' for a less
|
||||
# strict check:
|
||||
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
|
||||
result = not containsGarbageCollectedRef(t)
|
||||
|
||||
proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym =
|
||||
result = newSym(skLet, name, owner, varSection.info, owner.options)
|
||||
result.flags.incl sfHoisted
|
||||
result.typ = expr.typ
|
||||
|
||||
var varDef = newNodeI(nkIdentDefs, varSection.info, 3)
|
||||
varDef.sons[0] = newSymNode(result)
|
||||
varDef.sons[1] = newNodeI(nkEmpty, varSection.info)
|
||||
varDef.sons[2] = expr
|
||||
|
||||
varSection.add varDef
|
||||
|
||||
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
|
||||
v: PNode; useShallowCopy=false): PSym =
|
||||
result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
|
||||
owner.options)
|
||||
result.typ = typ
|
||||
incl(result.flags, sfFromGeneric)
|
||||
|
||||
var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
|
||||
vpart.sons[0] = newSymNode(result)
|
||||
vpart.sons[1] = newNodeI(nkEmpty, varSection.info)
|
||||
vpart.sons[2] = if varInit.isNil: v else: vpart[1]
|
||||
varSection.add vpart
|
||||
if varInit != nil:
|
||||
if useShallowCopy and typeNeedsNoDeepCopy(typ):
|
||||
varInit.add newFastAsgnStmt(newSymNode(result), v)
|
||||
else:
|
||||
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
|
||||
deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
|
||||
deepCopyCall.sons[1] = newSymNode(result)
|
||||
deepCopyCall.sons[2] = v
|
||||
varInit.add deepCopyCall
|
||||
|
||||
discard """
|
||||
We generate roughly this:
|
||||
|
||||
proc f_wrapper(thread, args) =
|
||||
barrierEnter(args.barrier) # for parallel statement
|
||||
var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
|
||||
# depending on whether we're in a 'parallel' statement
|
||||
var b = args.b
|
||||
var fv = args.fv
|
||||
|
||||
fv.owner = thread # optional
|
||||
nimArgsPassingDone() # signal parent that the work is done
|
||||
#
|
||||
args.fv.blob = f(a, b, ...)
|
||||
nimFlowVarSignal(args.fv)
|
||||
|
||||
# - or -
|
||||
f(a, b, ...)
|
||||
barrierLeave(args.barrier) # for parallel statement
|
||||
|
||||
stmtList:
|
||||
var scratchObj
|
||||
scratchObj.a = a
|
||||
scratchObj.b = b
|
||||
|
||||
nimSpawn(f_wrapper, addr scratchObj)
|
||||
scratchObj.fv # optional
|
||||
|
||||
"""
|
||||
|
||||
proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
|
||||
varSection, varInit, call, barrier, fv: PNode;
|
||||
spawnKind: TSpawnResult): PSym =
|
||||
var body = newNodeI(nkStmtList, f.info)
|
||||
body.flags.incl nfTransf # do not transform further
|
||||
|
||||
var threadLocalBarrier: PSym
|
||||
if barrier != nil:
|
||||
var varSection2 = newNodeI(nkVarSection, barrier.info)
|
||||
threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
|
||||
barrier.typ, barrier)
|
||||
body.add varSection2
|
||||
body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,
|
||||
threadLocalBarrier.newSymNode)
|
||||
var threadLocalProm: PSym
|
||||
if spawnKind == srByVar:
|
||||
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
|
||||
elif fv != nil:
|
||||
internalAssert g.config, fv.typ.kind == tyGenericInst
|
||||
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
|
||||
body.add varSection
|
||||
body.add varInit
|
||||
if fv != nil and spawnKind != srByVar:
|
||||
# generate:
|
||||
# fv.owner = threadParam
|
||||
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
|
||||
"owner", fv.info, g.cache), threadParam.newSymNode)
|
||||
|
||||
body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.info,
|
||||
threadParam.newSymNode)
|
||||
if spawnKind == srByVar:
|
||||
body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
|
||||
elif fv != nil:
|
||||
let fk = fv.typ.sons[1].flowVarKind
|
||||
if fk == fvInvalid:
|
||||
localError(g.config, f.info, "cannot create a flowVar of type: " &
|
||||
typeToString(fv.typ.sons[1]))
|
||||
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
|
||||
if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
|
||||
if fk == fvGC:
|
||||
let incRefCall = newNodeI(nkCall, fv.info, 2)
|
||||
incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
|
||||
incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
|
||||
"data", fv.info, g.cache)
|
||||
body.add incRefCall
|
||||
if barrier == nil:
|
||||
# by now 'fv' is shared and thus might have beeen overwritten! we need
|
||||
# to use the thread-local view instead:
|
||||
body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
|
||||
threadLocalProm.newSymNode)
|
||||
else:
|
||||
body.add call
|
||||
if barrier != nil:
|
||||
body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.info,
|
||||
threadLocalBarrier.newSymNode)
|
||||
|
||||
var params = newNodeI(nkFormalParams, f.info)
|
||||
params.add newNodeI(nkEmpty, f.info)
|
||||
params.add threadParam.newSymNode
|
||||
params.add argsParam.newSymNode
|
||||
|
||||
var t = newType(tyProc, threadParam.owner)
|
||||
t.rawAddSon nil
|
||||
t.rawAddSon threadParam.typ
|
||||
t.rawAddSon argsParam.typ
|
||||
t.n = newNodeI(nkFormalParams, f.info)
|
||||
t.n.add newNodeI(nkEffectList, f.info)
|
||||
t.n.add threadParam.newSymNode
|
||||
t.n.add argsParam.newSymNode
|
||||
|
||||
let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
|
||||
result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info,
|
||||
argsParam.options)
|
||||
let emptyNode = newNodeI(nkEmpty, f.info)
|
||||
result.ast = newProcNode(nkProcDef, f.info, body = body,
|
||||
params = params, name = newSymNode(result), pattern = emptyNode,
|
||||
genericParams = emptyNode, pragmas = emptyNode,
|
||||
exceptions = emptyNode)
|
||||
result.typ = t
|
||||
|
||||
proc createCastExpr(argsParam: PSym; objType: PType): PNode =
|
||||
result = newNodeI(nkCast, argsParam.info)
|
||||
result.add newNodeI(nkEmpty, argsParam.info)
|
||||
result.add newSymNode(argsParam)
|
||||
result.typ = newType(tyPtr, objType.owner)
|
||||
result.typ.rawAddSon(objType)
|
||||
|
||||
proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,
|
||||
castExpr, call,
|
||||
varSection, varInit, result: PNode) =
|
||||
let formals = n[0].typ.n
|
||||
let tmpName = getIdent(g.cache, genPrefix)
|
||||
for i in 1 ..< n.len:
|
||||
# we pick n's type here, which hopefully is 'tyArray' and not
|
||||
# 'tyOpenArray':
|
||||
var argType = n[i].typ.skipTypes(abstractInst)
|
||||
if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
|
||||
localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
|
||||
#elif containsTyRef(argType):
|
||||
# localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
|
||||
|
||||
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
|
||||
var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
|
||||
field.typ = argType
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
|
||||
|
||||
let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
|
||||
indirectAccess(castExpr, field, n.info))
|
||||
call.add(newSymNode(temp))
|
||||
|
||||
proc getRoot*(n: PNode): PSym =
|
||||
## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
|
||||
## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
|
||||
## determined as the owner; ``obj`` in the example.
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
|
||||
result = n.sym
|
||||
of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
|
||||
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = getRoot(n.sons[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = getRoot(n.sons[1])
|
||||
of nkCallKinds:
|
||||
if getMagic(n) == mSlice: result = getRoot(n.sons[1])
|
||||
else: discard
|
||||
|
||||
proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
|
||||
result = nkIntLit.newIntNode(value)
|
||||
result.typ = getSysType(g, info, tyInt)
|
||||
@@ -575,196 +347,14 @@ proc genLen*(g: ModuleGraph; n: PNode): PNode =
|
||||
result.sons[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq))
|
||||
result.sons[1] = n
|
||||
|
||||
proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;
|
||||
castExpr, call,
|
||||
varSection, varInit, result: PNode) =
|
||||
let formals = n[0].typ.n
|
||||
let tmpName = getIdent(g.cache, genPrefix)
|
||||
# we need to copy the foreign scratch object fields into local variables
|
||||
# for correctness: These are called 'threadLocal' here.
|
||||
for i in 1 ..< n.len:
|
||||
let n = n[i]
|
||||
let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
|
||||
abstractInst)
|
||||
#if containsTyRef(argType):
|
||||
# localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
|
||||
proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym =
|
||||
result = newSym(skLet, name, owner, varSection.info, owner.options)
|
||||
result.flags.incl sfHoisted
|
||||
result.typ = expr.typ
|
||||
|
||||
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
|
||||
var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
|
||||
var varDef = newNodeI(nkIdentDefs, varSection.info, 3)
|
||||
varDef.sons[0] = newSymNode(result)
|
||||
varDef.sons[1] = newNodeI(nkEmpty, varSection.info)
|
||||
varDef.sons[2] = expr
|
||||
|
||||
if argType.kind in {tyVarargs, tyOpenArray}:
|
||||
# important special case: we always create a zero-copy slice:
|
||||
let slice = newNodeI(nkCall, n.info, 4)
|
||||
slice.typ = n.typ
|
||||
slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))
|
||||
slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
|
||||
var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
|
||||
fieldB.typ = getSysType(g, n.info, tyInt)
|
||||
objType.addField(fieldB, g.cache)
|
||||
|
||||
if getMagic(n) == mSlice:
|
||||
let a = genAddrOf(n[1])
|
||||
field.typ = a.typ
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||
|
||||
var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
|
||||
fieldA.typ = getSysType(g, n.info, tyInt)
|
||||
objType.addField(fieldA, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
|
||||
|
||||
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,
|
||||
indirectAccess(castExpr, fieldA, n.info),
|
||||
useShallowCopy=true)
|
||||
slice.sons[2] = threadLocal.newSymNode
|
||||
else:
|
||||
let a = genAddrOf(n)
|
||||
field.typ = a.typ
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
|
||||
|
||||
slice.sons[2] = newIntLit(g, n.info, 0)
|
||||
# the array itself does not need to go through a thread local variable:
|
||||
slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
|
||||
|
||||
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,
|
||||
indirectAccess(castExpr, fieldB, n.info),
|
||||
useShallowCopy=true)
|
||||
slice.sons[3] = threadLocal.newSymNode
|
||||
call.add slice
|
||||
elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
|
||||
n.getRoot != nil:
|
||||
# it is more efficient to pass a pointer instead:
|
||||
let a = genAddrOf(n)
|
||||
field.typ = a.typ
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
|
||||
indirectAccess(castExpr, field, n.info),
|
||||
useShallowCopy=true)
|
||||
call.add(genDeref(threadLocal.newSymNode))
|
||||
else:
|
||||
# boring case
|
||||
field.typ = argType
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
|
||||
let threadLocal = addLocalVar(g, varSection, varInit,
|
||||
objType.owner, field.typ,
|
||||
indirectAccess(castExpr, field, n.info),
|
||||
useShallowCopy=true)
|
||||
call.add(threadLocal.newSymNode)
|
||||
|
||||
proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
|
||||
barrier, dest: PNode = nil): PNode =
|
||||
# if 'barrier' != nil, then it is in a 'parallel' section and we
|
||||
# generate quite different code
|
||||
let n = spawnExpr[^2]
|
||||
let spawnKind = spawnResult(retType, barrier!=nil)
|
||||
case spawnKind
|
||||
of srVoid:
|
||||
internalAssert g.config, dest == nil
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
of srFlowVar:
|
||||
internalAssert g.config, dest == nil
|
||||
result = newNodeIT(nkStmtListExpr, n.info, retType)
|
||||
of srByVar:
|
||||
if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
if n.kind notin nkCallKinds:
|
||||
localError(g.config, n.info, "'spawn' takes a call expression")
|
||||
return
|
||||
if optThreadAnalysis in g.config.globalOptions:
|
||||
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
|
||||
localError(g.config, n.info, "'spawn' takes a GC safe call expression")
|
||||
var
|
||||
threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options)
|
||||
argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options)
|
||||
block:
|
||||
let ptrType = getSysType(g, n.info, tyPointer)
|
||||
threadParam.typ = ptrType
|
||||
argsParam.typ = ptrType
|
||||
argsParam.position = 1
|
||||
|
||||
var objType = createObj(g, owner, n.info)
|
||||
incl(objType.flags, tfFinal)
|
||||
let castExpr = createCastExpr(argsParam, objType)
|
||||
|
||||
var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)
|
||||
block:
|
||||
scratchObj.typ = objType
|
||||
incl(scratchObj.flags, sfFromGeneric)
|
||||
var varSectionB = newNodeI(nkVarSection, n.info)
|
||||
varSectionB.addVar(scratchObj.newSymNode)
|
||||
result.add varSectionB
|
||||
|
||||
var call = newNodeIT(nkCall, n.info, n.typ)
|
||||
var fn = n.sons[0]
|
||||
# templates and macros are in fact valid here due to the nature of
|
||||
# the transformation:
|
||||
if fn.kind == nkClosure:
|
||||
localError(g.config, n.info, "closure in spawn environment is not allowed")
|
||||
if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
|
||||
skFunc, skMethod, skConverter}):
|
||||
# for indirect calls we pass the function pointer in the scratchObj
|
||||
var argType = n[0].typ.skipTypes(abstractInst)
|
||||
var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)
|
||||
field.typ = argType
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
|
||||
fn = indirectAccess(castExpr, field, n.info)
|
||||
elif fn.kind == nkSym and fn.sym.kind == skIterator:
|
||||
localError(g.config, n.info, "iterator in spawn environment is not allowed")
|
||||
elif fn.typ.callConv == ccClosure:
|
||||
localError(g.config, n.info, "closure in spawn environment is not allowed")
|
||||
|
||||
call.add(fn)
|
||||
var varSection = newNodeI(nkVarSection, n.info)
|
||||
var varInit = newNodeI(nkStmtList, n.info)
|
||||
if barrier.isNil:
|
||||
setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,
|
||||
varSection, varInit, result)
|
||||
else:
|
||||
setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,
|
||||
varSection, varInit, result)
|
||||
|
||||
var barrierAsExpr: PNode = nil
|
||||
if barrier != nil:
|
||||
let typ = newType(tyPtr, owner)
|
||||
typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
|
||||
var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)
|
||||
field.typ = typ
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
|
||||
barrierAsExpr = indirectAccess(castExpr, field, n.info)
|
||||
|
||||
var fvField, fvAsExpr: PNode = nil
|
||||
if spawnKind == srFlowVar:
|
||||
var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
|
||||
field.typ = retType
|
||||
objType.addField(field, g.cache)
|
||||
fvField = newDotExpr(scratchObj, field)
|
||||
fvAsExpr = indirectAccess(castExpr, field, n.info)
|
||||
# create flowVar:
|
||||
result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
|
||||
if barrier == nil:
|
||||
result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info,
|
||||
fvField)
|
||||
|
||||
elif spawnKind == srByVar:
|
||||
var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
|
||||
field.typ = newType(tyPtr, objType.owner)
|
||||
field.typ.rawAddSon(retType)
|
||||
objType.addField(field, g.cache)
|
||||
fvAsExpr = indirectAccess(castExpr, field, n.info)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
|
||||
|
||||
let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
|
||||
varSection, varInit, call,
|
||||
barrierAsExpr, fvAsExpr, spawnKind)
|
||||
result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.info,
|
||||
wrapper.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
|
||||
|
||||
if spawnKind == srFlowVar: result.add fvField
|
||||
varSection.add varDef
|
||||
|
||||
@@ -26,9 +26,6 @@ when not defined(leanCompiler):
|
||||
|
||||
from magicsys import resetSysTypes
|
||||
|
||||
proc codegenPass(g: ModuleGraph) =
|
||||
registerPass g, cgenPass
|
||||
|
||||
proc semanticPasses(g: ModuleGraph) =
|
||||
registerPass g, verbosePass
|
||||
registerPass g, semPass
|
||||
|
||||
@@ -29,7 +29,7 @@ import ast, intsets, tables, options, lineinfos, hashes, idents,
|
||||
incremental, btrees, md5
|
||||
|
||||
type
|
||||
SigHash* = distinct Md5Digest
|
||||
SigHash* = distinct MD5Digest
|
||||
|
||||
ModuleGraph* = ref object
|
||||
modules*: seq[PSym] ## indexed by int32 fileIdx
|
||||
|
||||
@@ -162,6 +162,6 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
|
||||
if doLocalError:
|
||||
let m = if modulename.len > 0: modulename else: $n
|
||||
localError(conf, n.info, "cannot open file: " & m)
|
||||
result = InvalidFileIDX
|
||||
result = InvalidFileIdx
|
||||
else:
|
||||
result = fileInfoIdx(conf, fullPath)
|
||||
|
||||
@@ -133,12 +133,12 @@ proc wantMainModule*(conf: ConfigRef) =
|
||||
"command expects a filename")
|
||||
conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
|
||||
|
||||
proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIDX) =
|
||||
proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
|
||||
connectCallbacks(graph)
|
||||
let conf = graph.config
|
||||
wantMainModule(conf)
|
||||
let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
|
||||
let projectFile = if projectFileIdx == InvalidFileIDX: conf.projectMainIdx else: projectFileIdx
|
||||
let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
|
||||
graph.importStack.add projectFile
|
||||
if projectFile == systemFileIdx:
|
||||
discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
|
||||
|
||||
@@ -40,9 +40,8 @@ proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
|
||||
#shallow(result.fullPath)
|
||||
result.projPath = projPath
|
||||
#shallow(result.projPath)
|
||||
let fileName = fullPath.extractFilename
|
||||
result.shortName = fileName.changeFileExt("")
|
||||
result.quotedName = fileName.makeCString
|
||||
result.shortName = fullPath.extractFilename
|
||||
result.quotedName = result.shortName.makeCString
|
||||
result.quotedFullName = fullPath.string.makeCString
|
||||
result.lines = @[]
|
||||
when defined(nimpretty):
|
||||
@@ -96,14 +95,20 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
|
||||
|
||||
proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
|
||||
result.fileIndex = fileInfoIdx
|
||||
result.line = uint16(line)
|
||||
result.col = int16(col)
|
||||
if line < int high(uint16):
|
||||
result.line = uint16(line)
|
||||
else:
|
||||
result.line = high(uint16)
|
||||
if col < int high(int16):
|
||||
result.col = int16(col)
|
||||
else:
|
||||
result.col = -1
|
||||
|
||||
proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} =
|
||||
result = newLineInfo(fileInfoIdx(conf, filename), line, col)
|
||||
|
||||
|
||||
proc concat(strings: openarray[string]): string =
|
||||
proc concat(strings: openArray[string]): string =
|
||||
var totalLen = 0
|
||||
for s in strings: totalLen += s.len
|
||||
result = newStringOfCap totalLen
|
||||
@@ -159,10 +164,11 @@ template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||
if fileIdx.int32 < 0 or conf == nil:
|
||||
"???"
|
||||
else:
|
||||
if optListFullPaths in conf.globalOptions:
|
||||
conf.m.fileInfos[fileIdx.int32].fullPath.string
|
||||
else:
|
||||
conf.m.fileInfos[fileIdx.int32].projPath.string
|
||||
conf.m.fileInfos[fileIdx.int32].shortName
|
||||
|
||||
proc toProjPath*(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||
if fileIdx.int32 < 0 or conf == nil: "???"
|
||||
else: conf.m.fileInfos[fileIdx.int32].projPath.string
|
||||
|
||||
proc toFullPath*(conf: ConfigRef; fileIdx: FileIndex): string =
|
||||
if fileIdx.int32 < 0 or conf == nil: result = "???"
|
||||
@@ -192,22 +198,28 @@ proc toFullPathConsiderDirty*(conf: ConfigRef; fileIdx: FileIndex): AbsoluteFile
|
||||
template toFilename*(conf: ConfigRef; info: TLineInfo): string =
|
||||
toFilename(conf, info.fileIndex)
|
||||
|
||||
template toProjPath*(conf: ConfigRef; info: TLineInfo): string =
|
||||
toProjPath(conf, info.fileIndex)
|
||||
|
||||
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 = "???"
|
||||
return
|
||||
let absPath = conf.m.fileInfos[info.fileIndex.int32].fullPath.string
|
||||
if optListFullPaths in conf.globalOptions:
|
||||
result = absPath
|
||||
else:
|
||||
let relPath = conf.m.fileInfos[info.fileIndex.int32].projPath.string
|
||||
result = if relPath.count("..") > 2: absPath else: relPath
|
||||
proc toMsgFilename*(conf: ConfigRef; info: FileIndex): string =
|
||||
let
|
||||
absPath = toFullPath(conf, info)
|
||||
relPath = toProjPath(conf, info)
|
||||
result = if (optListFullPaths in conf.globalOptions) or
|
||||
(relPath.len > absPath.len) or
|
||||
(relPath.count("..") > 2):
|
||||
absPath
|
||||
else:
|
||||
relPath
|
||||
|
||||
template toMsgFilename*(conf: ConfigRef; info: TLineInfo): string =
|
||||
toMsgFilename(conf, info.fileIndex)
|
||||
|
||||
proc toLinenumber*(info: TLineInfo): int {.inline.} =
|
||||
result = int info.line
|
||||
@@ -215,12 +227,9 @@ proc toLinenumber*(info: TLineInfo): int {.inline.} =
|
||||
proc toColumn*(info: TLineInfo): int {.inline.} =
|
||||
result = info.col
|
||||
|
||||
proc toFileLine*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
|
||||
result = toFilename(conf, info) & ":" & $info.line
|
||||
|
||||
proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
|
||||
# consider calling `helpers.lineInfoToString` instead
|
||||
result = toFilename(conf, info) & "(" & $info.line & ", " &
|
||||
result = toMsgFilename(conf, info) & "(" & $info.line & ", " &
|
||||
$(info.col + ColOffset) & ")"
|
||||
|
||||
proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
|
||||
@@ -452,7 +461,8 @@ proc sourceLine*(conf: ConfigRef; i: TLineInfo): string =
|
||||
proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) =
|
||||
const indent = " "
|
||||
msgWriteln(conf, indent & $sourceLine(conf, info))
|
||||
msgWriteln(conf, indent & spaces(info.col) & '^')
|
||||
if info.col >= 0:
|
||||
msgWriteln(conf, indent & spaces(info.col) & '^')
|
||||
|
||||
proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string =
|
||||
let title = case msg
|
||||
@@ -532,7 +542,7 @@ proc localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
|
||||
proc localError*(conf: ConfigRef; info: TLineInfo, arg: string) =
|
||||
liMessage(conf, info, errGenerated, arg, doNothing)
|
||||
|
||||
proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openarray[string]) =
|
||||
proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) =
|
||||
localError(conf, info, format % params)
|
||||
|
||||
proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
|
||||
@@ -556,8 +566,9 @@ template internalAssert*(conf: ConfigRef, e: bool) =
|
||||
if not e: internalError(conf, $instantiationInfo())
|
||||
|
||||
proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
|
||||
assert i.fileIndex.int32 >= 0
|
||||
if optExcessiveStackTrace in conf.globalOptions:
|
||||
if i.fileIndex.int32 < 0:
|
||||
result = makeCString "???"
|
||||
elif optExcessiveStackTrace in conf.globalOptions:
|
||||
result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
|
||||
else:
|
||||
result = conf.m.fileInfos[i.fileIndex.int32].quotedName
|
||||
@@ -577,3 +588,10 @@ proc listHints*(conf: ConfigRef) =
|
||||
if hint in conf.notes: "x" else: " ",
|
||||
lineinfos.HintsToStr[ord(hint) - ord(hintMin)]
|
||||
])
|
||||
|
||||
proc lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) =
|
||||
let m = "'$2' should be: '$1'" % [beau, got]
|
||||
if optStyleError in conf.globalOptions:
|
||||
localError(conf, info, m)
|
||||
else:
|
||||
message(conf, info, hintName, m)
|
||||
|
||||
@@ -14,6 +14,10 @@ define:nimcore
|
||||
define:useStdoutAsStdmsg
|
||||
define:nimOldCaseObjects
|
||||
|
||||
@if nimHasStyleChecks:
|
||||
styleCheck:error
|
||||
@end
|
||||
|
||||
#define:useNodeIds
|
||||
#gc:markAndSweep
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
|
||||
parseopt.next(p)
|
||||
case p.kind
|
||||
of cmdEnd: break
|
||||
of cmdLongoption, cmdShortOption:
|
||||
of cmdLongOption, cmdShortOption:
|
||||
if p.key == " ":
|
||||
p.key = "-"
|
||||
if processArgument(pass, p, argsCount, config): break
|
||||
|
||||
@@ -123,31 +123,31 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack
|
||||
of wEnd: doEnd(L, tok, condStack)
|
||||
of wWrite:
|
||||
ppGetTok(L, tok)
|
||||
msgs.msgWriteln(config, strtabs.`%`(tokToStr(tok), config.configVars,
|
||||
msgs.msgWriteln(config, strtabs.`%`($tok, config.configVars,
|
||||
{useEnvironment, useKey}))
|
||||
ppGetTok(L, tok)
|
||||
else:
|
||||
case tok.ident.s.normalize
|
||||
of "putenv":
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
var key = $tok
|
||||
ppGetTok(L, tok)
|
||||
os.putEnv(key, tokToStr(tok))
|
||||
os.putEnv(key, $tok)
|
||||
ppGetTok(L, tok)
|
||||
of "prependenv":
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
var key = $tok
|
||||
ppGetTok(L, tok)
|
||||
os.putEnv(key, tokToStr(tok) & os.getEnv(key))
|
||||
os.putEnv(key, $tok & os.getEnv(key))
|
||||
ppGetTok(L, tok)
|
||||
of "appendenv":
|
||||
ppGetTok(L, tok)
|
||||
var key = tokToStr(tok)
|
||||
var key = $tok
|
||||
ppGetTok(L, tok)
|
||||
os.putEnv(key, os.getEnv(key) & tokToStr(tok))
|
||||
os.putEnv(key, os.getEnv(key) & $tok)
|
||||
ppGetTok(L, tok)
|
||||
else:
|
||||
lexMessage(L, errGenerated, "invalid directive: '$1'" % tokToStr(tok))
|
||||
lexMessage(L, errGenerated, "invalid directive: '$1'" % $tok)
|
||||
|
||||
proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
|
||||
ppGetTok(L, tok)
|
||||
@@ -156,7 +156,7 @@ proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var s
|
||||
|
||||
proc checkSymbol(L: TLexer, tok: TToken) =
|
||||
if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}:
|
||||
lexMessage(L, errGenerated, "expected identifier, but got: " & tokToStr(tok))
|
||||
lexMessage(L, errGenerated, "expected identifier, but got: " & $tok)
|
||||
|
||||
proc parseAssignment(L: var TLexer, tok: var TToken;
|
||||
config: ConfigRef; condStack: var seq[bool]) =
|
||||
@@ -164,21 +164,21 @@ proc parseAssignment(L: var TLexer, tok: var TToken;
|
||||
confTok(L, tok, config, condStack) # skip unnecessary prefix
|
||||
var info = getLineInfo(L, tok) # save for later in case of an error
|
||||
checkSymbol(L, tok)
|
||||
var s = tokToStr(tok)
|
||||
var s = $tok
|
||||
confTok(L, tok, config, condStack) # skip symbol
|
||||
var val = ""
|
||||
while tok.tokType == tkDot:
|
||||
add(s, '.')
|
||||
confTok(L, tok, config, condStack)
|
||||
checkSymbol(L, tok)
|
||||
add(s, tokToStr(tok))
|
||||
add(s, $tok)
|
||||
confTok(L, tok, config, condStack)
|
||||
if tok.tokType == tkBracketLe:
|
||||
# BUGFIX: val, not s!
|
||||
confTok(L, tok, config, condStack)
|
||||
checkSymbol(L, tok)
|
||||
add(val, '[')
|
||||
add(val, tokToStr(tok))
|
||||
add(val, $tok)
|
||||
confTok(L, tok, config, condStack)
|
||||
if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack)
|
||||
else: lexMessage(L, errGenerated, "expected closing ']'")
|
||||
@@ -188,12 +188,12 @@ proc parseAssignment(L: var TLexer, tok: var TToken;
|
||||
if len(val) > 0: add(val, ':')
|
||||
confTok(L, tok, config, condStack) # skip ':' or '=' or '%'
|
||||
checkSymbol(L, tok)
|
||||
add(val, tokToStr(tok))
|
||||
add(val, $tok)
|
||||
confTok(L, tok, config, condStack) # skip symbol
|
||||
while tok.ident != nil and tok.ident.s == "&":
|
||||
confTok(L, tok, config, condStack)
|
||||
checkSymbol(L, tok)
|
||||
add(val, tokToStr(tok))
|
||||
add(val, $tok)
|
||||
confTok(L, tok, config, condStack)
|
||||
if percent:
|
||||
processSwitch(s, strtabs.`%`(val, config.configVars,
|
||||
|
||||
@@ -65,12 +65,12 @@ proc toBitSet*(conf: ConfigRef; s: PNode, b: var TBitSet) =
|
||||
bitSetInit(b, int(getSize(conf, s.typ)))
|
||||
for i in 0 ..< sonsLen(s):
|
||||
if s.sons[i].kind == nkRange:
|
||||
j = getOrdValue(s.sons[i].sons[0])
|
||||
while j <= getOrdValue(s.sons[i].sons[1]):
|
||||
j = getOrdValue(s.sons[i].sons[0], first)
|
||||
while j <= getOrdValue(s.sons[i].sons[1], first):
|
||||
bitSetIncl(b, j - first)
|
||||
inc(j)
|
||||
else:
|
||||
bitSetIncl(b, getOrdValue(s.sons[i]) - first)
|
||||
bitSetIncl(b, getOrdValue(s.sons[i], first) - first)
|
||||
|
||||
proc toTreeSet*(conf: ConfigRef; s: TBitSet, settype: PType, info: TLineInfo): PNode =
|
||||
var
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import os
|
||||
|
||||
proc findNodeJs*(): string =
|
||||
proc findNodeJs*(): string {.inline.} =
|
||||
## Find NodeJS executable and return it as a string.
|
||||
result = findExe("nodejs")
|
||||
if result == "":
|
||||
result = findExe("node")
|
||||
if result == "":
|
||||
result = findExe("iojs")
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ type # please make sure we have under 32 options
|
||||
# (improves code efficiency a lot!)
|
||||
TOption* = enum # **keep binary compatible**
|
||||
optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
|
||||
optOverflowCheck, optNilCheck,
|
||||
optNaNCheck, optInfCheck, optMoveCheck,
|
||||
optOverflowCheck, optNilCheck, optRefCheck,
|
||||
optNaNCheck, optInfCheck, optStyleCheck,
|
||||
optAssert, optLineDir, optWarns, optHints,
|
||||
optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
|
||||
optLineTrace, # line tracing support (includes stack tracing)
|
||||
@@ -78,7 +78,7 @@ type # please make sure we have under 32 options
|
||||
optWholeProject # for 'doc2': output any dependency
|
||||
optDocInternal # generate documentation for non-exported symbols
|
||||
optMixedMode # true if some module triggered C++ codegen
|
||||
optListFullPaths # use full paths in toMsgFilename, toFilename
|
||||
optListFullPaths # use full paths in toMsgFilename
|
||||
optNoNimblePath
|
||||
optHotCodeReloading
|
||||
optDynlibOverrideAll
|
||||
@@ -149,7 +149,7 @@ type
|
||||
Cfile* = object
|
||||
nimname*: string
|
||||
cname*, obj*: AbsoluteFile
|
||||
flags*: set[CFileFlag]
|
||||
flags*: set[CfileFlag]
|
||||
CfileList* = seq[Cfile]
|
||||
|
||||
Suggest* = ref object
|
||||
@@ -246,7 +246,7 @@ type
|
||||
compileOptionsCmd*: seq[string]
|
||||
linkOptions*: string # (*)
|
||||
compileOptions*: string # (*)
|
||||
ccompilerpath*: string
|
||||
cCompilerPath*: string
|
||||
toCompile*: CfileList # (*)
|
||||
suggestionResultHook*: proc (result: Suggest) {.closure.}
|
||||
suggestVersion*: int
|
||||
@@ -270,12 +270,12 @@ const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, par
|
||||
const
|
||||
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
|
||||
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
|
||||
optMoveCheck}
|
||||
optStyleCheck, optRefCheck}
|
||||
|
||||
DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
|
||||
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
|
||||
optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,
|
||||
optHints, optStackTrace, optLineTrace,
|
||||
optTrMacros, optNilCheck, optMoveCheck}
|
||||
optTrMacros, optNilCheck, optStyleCheck}
|
||||
DefaultGlobalOptions* = {optThreadAnalysis,
|
||||
optExcessiveStackTrace, optListFullPaths}
|
||||
|
||||
@@ -498,7 +498,7 @@ proc setDefaultLibpath*(conf: ConfigRef) =
|
||||
# Find out if $nim/../../lib/system.nim exists.
|
||||
let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
|
||||
if not fileExists(conf.libpath.string / "system.nim") and
|
||||
fileExists(parentNimlibPath / "system.nim"):
|
||||
fileExists(parentNimLibPath / "system.nim"):
|
||||
conf.libpath = AbsoluteDir parentNimLibPath
|
||||
|
||||
proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile =
|
||||
@@ -590,7 +590,7 @@ proc rawFindFile2(conf: ConfigRef; f: RelativeFile): AbsoluteFile =
|
||||
result = it / f
|
||||
if fileExists(result):
|
||||
# bring to front
|
||||
for j in countDown(i,1):
|
||||
for j in countdown(i, 1):
|
||||
swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
|
||||
|
||||
return canonicalizePath(conf, result)
|
||||
|
||||
@@ -147,10 +147,17 @@ template withInd(p, body: untyped) =
|
||||
body
|
||||
p.currInd = oldInd
|
||||
|
||||
template newlineWasSplitting(p: var TParser) =
|
||||
when defined(nimpretty):
|
||||
layouter.newlineWasSplitting(p.em)
|
||||
|
||||
template realInd(p): bool = p.tok.indent > p.currInd
|
||||
template sameInd(p): bool = p.tok.indent == p.currInd
|
||||
template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0
|
||||
|
||||
proc validInd(p: var TParser): bool {.inline.} =
|
||||
result = p.tok.indent < 0 or p.tok.indent > p.currInd
|
||||
|
||||
proc rawSkipComment(p: var TParser, node: PNode) =
|
||||
if p.tok.tokType == tkComment:
|
||||
if node != nil:
|
||||
@@ -339,17 +346,17 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode =
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
break
|
||||
of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
|
||||
let lineinfo = parLineinfo(p)
|
||||
let lineinfo = parLineInfo(p)
|
||||
var accm = ""
|
||||
while p.tok.tokType in {tkOpr, tkDot, tkDotDot, tkEquals,
|
||||
tkParLe..tkParDotRi}:
|
||||
accm.add(tokToStr(p.tok))
|
||||
accm.add($p.tok)
|
||||
getTok(p)
|
||||
let node = newNodeI(nkIdent, lineinfo)
|
||||
node.ident = p.lex.cache.getIdent(accm)
|
||||
result.add(node)
|
||||
of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit:
|
||||
result.add(newIdentNodeP(p.lex.cache.getIdent(tokToStr(p.tok)), p))
|
||||
result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p))
|
||||
getTok(p)
|
||||
else:
|
||||
parMessage(p, errIdentifierExpected, p.tok)
|
||||
@@ -367,6 +374,7 @@ proc colonOrEquals(p: var TParser, a: PNode): PNode =
|
||||
if p.tok.tokType == tkColon:
|
||||
result = newNodeP(nkExprColonExpr, p)
|
||||
getTok(p)
|
||||
newlineWasSplitting(p)
|
||||
#optInd(p, result)
|
||||
addSon(result, a)
|
||||
addSon(result, parseExpr(p))
|
||||
@@ -903,6 +911,9 @@ proc parsePragma(p: var TParser): PNode =
|
||||
#| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}')
|
||||
result = newNodeP(nkPragma, p)
|
||||
inc p.inPragma
|
||||
when defined(nimpretty):
|
||||
inc p.em.doIndentMore
|
||||
inc p.em.keepIndents
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
while p.tok.tokType notin {tkCurlyDotRi, tkCurlyRi, tkEof}:
|
||||
@@ -921,6 +932,9 @@ proc parsePragma(p: var TParser): PNode =
|
||||
else:
|
||||
parMessage(p, "expected '.}'")
|
||||
dec p.inPragma
|
||||
when defined(nimpretty):
|
||||
dec p.em.doIndentMore
|
||||
dec p.em.keepIndents
|
||||
|
||||
proc identVis(p: var TParser; allowDot=false): PNode =
|
||||
#| identVis = symbol opr? # postfix position
|
||||
@@ -967,7 +981,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
|
||||
while true:
|
||||
case p.tok.tokType
|
||||
of tkSymbol, tkAccent:
|
||||
if withPragma in flags: a = identWithPragma(p, allowDot=withdot in flags)
|
||||
if withPragma in flags: a = identWithPragma(p, allowDot=withDot in flags)
|
||||
else: a = parseSymbol(p)
|
||||
if a.kind == nkEmpty: return
|
||||
else: break
|
||||
@@ -1076,7 +1090,7 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
result.sons[0] = parseTypeDesc(p)
|
||||
elif not retColon and not hasParle:
|
||||
elif not retColon and not hasParLe:
|
||||
# Mark as "not there" in order to mark for deprecation in the semantic pass:
|
||||
result = p.emptyNode
|
||||
when defined(nimpretty):
|
||||
@@ -1289,6 +1303,7 @@ proc binaryNot(p: var TParser; a: PNode): PNode =
|
||||
|
||||
proc parseTypeDesc(p: var TParser): PNode =
|
||||
#| typeDesc = simpleExpr ('not' expr)?
|
||||
newlineWasSplitting(p)
|
||||
result = simpleExpr(p, pmTypeDesc)
|
||||
result = binaryNot(p, result)
|
||||
|
||||
@@ -1504,6 +1519,7 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
|
||||
elif p.tok.indent >= 0 and p.tok.indent <= p.currInd or not isExprStart(p):
|
||||
# NL terminates:
|
||||
addSon(result, p.emptyNode)
|
||||
# nimpretty here!
|
||||
else:
|
||||
var e = parseExpr(p)
|
||||
e = postExprBlocks(p, e)
|
||||
@@ -1716,9 +1732,6 @@ proc parsePattern(p: var TParser): PNode =
|
||||
result = parseStmt(p)
|
||||
eat(p, tkCurlyRi)
|
||||
|
||||
proc validInd(p: var TParser): bool =
|
||||
result = p.tok.indent < 0 or p.tok.indent > p.currInd
|
||||
|
||||
proc parseRoutine(p: var TParser, kind: TNodeKind): PNode =
|
||||
#| indAndComment = (IND{>} COMMENT)? | COMMENT?
|
||||
#| routine = optInd identVis pattern? genericParamList?
|
||||
@@ -1803,7 +1816,7 @@ proc parseEnum(p: var TParser): PNode =
|
||||
symPragma = newNodeP(nkPragmaExpr, p)
|
||||
addSon(symPragma, a)
|
||||
addSon(symPragma, pragma)
|
||||
|
||||
# nimpretty support here
|
||||
if p.tok.indent >= 0 and p.tok.indent <= p.currInd:
|
||||
add(result, symPragma)
|
||||
break
|
||||
@@ -2213,6 +2226,7 @@ proc parseStmt(p: var TParser): PNode =
|
||||
#| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED)
|
||||
#| / simpleStmt ^+ ';'
|
||||
if p.tok.indent > p.currInd:
|
||||
# nimpretty support here
|
||||
result = newNodeP(nkStmtList, p)
|
||||
withInd(p):
|
||||
while true:
|
||||
@@ -2289,6 +2303,7 @@ proc parseTopLevelStmt(p: var TParser): PNode =
|
||||
result = p.emptyNode
|
||||
# progress guaranteed
|
||||
while true:
|
||||
# nimpretty support here
|
||||
if p.tok.indent != 0:
|
||||
if p.firstTok and p.tok.indent < 0: discard
|
||||
elif p.tok.tokType != tkSemiColon:
|
||||
|
||||
@@ -30,6 +30,6 @@ proc verboseProcess(context: PPassContext, n: PNode): PNode =
|
||||
# system.nim deactivates all hints, for verbosity:3 we want the processing
|
||||
# messages nonetheless, so we activate them again unconditionally:
|
||||
incl(v.config.notes, hintProcessing)
|
||||
message(v.config, n.info, hintProcessing, $idgen.gFrontendId)
|
||||
message(v.config, n.info, hintProcessing, $idgen.gFrontEndId)
|
||||
|
||||
const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)
|
||||
|
||||
@@ -90,7 +90,7 @@ proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray)
|
||||
proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
|
||||
let fullPath = findModule(conf, module, relativeTo)
|
||||
if fullPath.isEmpty:
|
||||
result = InvalidFileIDX
|
||||
result = InvalidFileIdx
|
||||
else:
|
||||
result = fileInfoIdx(conf, fullPath)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ type
|
||||
# conditionals to condsyms (end of module).
|
||||
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
|
||||
osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
|
||||
osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxworks
|
||||
osAmiga, osAtari, osNetware, osMacos, osMacosx, osHaiku, osAndroid, osVxWorks
|
||||
osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch
|
||||
|
||||
type
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import
|
||||
os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
|
||||
wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
|
||||
types, lookups, lineinfos, pathutils
|
||||
types, lookups, lineinfos, pathutils, linter
|
||||
|
||||
const
|
||||
FirstCallConv* = wNimcall
|
||||
@@ -20,38 +20,38 @@ const
|
||||
|
||||
const
|
||||
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
||||
wMagic, wNoSideEffect, wSideEffect, wNoreturn, wDynlib, wHeader,
|
||||
wCompilerProc, wNonReloadable, wCore, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
|
||||
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
|
||||
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl,
|
||||
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
|
||||
wConstructor, wExportNims, wUsed, wLiftLocals, wStacktrace, wLinetrace}
|
||||
converterPragmas* = procPragmas
|
||||
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
|
||||
wConstructor, wExportNims, wUsed, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy}
|
||||
converterPragmas* = procPragmas - {wNoDestroy}
|
||||
methodPragmas* = procPragmas+{wBase}-{wImportCpp, wNoDestroy}
|
||||
templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
|
||||
wDelegator, wExportNims, wUsed, wPragma}
|
||||
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc,
|
||||
wNodecl, wMagic, wNosideeffect, wCompilerProc, wNonReloadable, wCore, wDeprecated, wExtern,
|
||||
wNodecl, wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore, wDeprecated, wExtern,
|
||||
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
|
||||
wExportNims, wUsed}
|
||||
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
|
||||
iteratorPragmas* = {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect,
|
||||
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
|
||||
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
|
||||
wTags, wLocks, wGcSafe, wExportNims, wUsed}
|
||||
exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNosideeffect}
|
||||
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
|
||||
wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions,
|
||||
exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect}
|
||||
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks,
|
||||
wBoundChecks, wOverflowChecks, wNilChecks, wStyleChecks, wAssertions,
|
||||
wWarnings, wHints,
|
||||
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
|
||||
wLineDir, wStackTrace, wLineTrace, wOptimization, wHint, wWarning, wError,
|
||||
wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop,
|
||||
wBreakpoint, wWatchPoint, wPassl, wPassc,
|
||||
wDeadCodeElimUnused, # deprecated, always on
|
||||
wDeprecated,
|
||||
wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||
wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
|
||||
wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto,
|
||||
wInjectStmt, wDeprecated, wExperimental, wThis}
|
||||
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
|
||||
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
|
||||
wNoSideEffect, wSideEffect, wNoreturn, wDynlib, wHeader,
|
||||
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
|
||||
wRaises, wLocks, wTags, wGcSafe, wCodegenDecl}
|
||||
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
|
||||
@@ -69,7 +69,7 @@ const
|
||||
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims,
|
||||
wIntDefine, wStrDefine, wBoolDefine, wUsed, wCompilerProc, wCore}
|
||||
letPragmas* = varPragmas
|
||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
|
||||
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
|
||||
wThread, wRaises, wLocks, wTags, wGcSafe}
|
||||
forVarPragmas* = {wInject, wGensym}
|
||||
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
|
||||
@@ -161,7 +161,7 @@ proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) =
|
||||
incl(s.flags, sfNamedParamCall)
|
||||
excl(s.flags, sfForward)
|
||||
let m = s.getModule()
|
||||
incl(m.flags, sfCompileToObjC)
|
||||
incl(m.flags, sfCompileToObjc)
|
||||
|
||||
proc newEmptyStrNode(c: PContext; n: PNode): PNode {.noinline.} =
|
||||
result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
|
||||
@@ -331,20 +331,20 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
|
||||
of wChecks: ChecksOptions
|
||||
of wObjChecks: {optObjCheck}
|
||||
of wFieldChecks: {optFieldCheck}
|
||||
of wRangechecks: {optRangeCheck}
|
||||
of wBoundchecks: {optBoundsCheck}
|
||||
of wOverflowchecks: {optOverflowCheck}
|
||||
of wNilchecks: {optNilCheck}
|
||||
of wFloatchecks: {optNaNCheck, optInfCheck}
|
||||
of wRangeChecks: {optRangeCheck}
|
||||
of wBoundChecks: {optBoundsCheck}
|
||||
of wOverflowChecks: {optOverflowCheck}
|
||||
of wNilChecks: {optNilCheck}
|
||||
of wFloatChecks: {optNaNCheck, optInfCheck}
|
||||
of wNanChecks: {optNaNCheck}
|
||||
of wInfChecks: {optInfCheck}
|
||||
of wMovechecks: {optMoveCheck}
|
||||
of wStyleChecks: {optStyleCheck}
|
||||
of wAssertions: {optAssert}
|
||||
of wWarnings: {optWarns}
|
||||
of wHints: {optHints}
|
||||
of wLinedir: {optLineDir}
|
||||
of wStacktrace: {optStackTrace}
|
||||
of wLinetrace: {optLineTrace}
|
||||
of wLineDir: {optLineDir}
|
||||
of wStackTrace: {optStackTrace}
|
||||
of wLineTrace: {optLineTrace}
|
||||
of wDebugger: {optEndb}
|
||||
of wProfiler: {optProfiler, optMemTracker}
|
||||
of wMemTracker: {optMemTracker}
|
||||
@@ -434,6 +434,8 @@ proc processPush(c: PContext, n: PNode, start: int) =
|
||||
# If stacktrace is disabled globally we should not enable it
|
||||
if optStackTrace notin c.optionStack[0].options:
|
||||
c.config.options.excl(optStackTrace)
|
||||
when defined(debugOptions):
|
||||
echo c.config $ n.info, " PUSH config is now ", c.config.options
|
||||
|
||||
proc processPop(c: PContext, n: PNode) =
|
||||
if c.optionStack.len <= 1:
|
||||
@@ -443,6 +445,8 @@ proc processPop(c: PContext, n: PNode) =
|
||||
c.config.notes = c.optionStack[^1].notes
|
||||
c.features = c.optionStack[^1].features
|
||||
c.optionStack.setLen(c.optionStack.len - 1)
|
||||
when defined(debugOptions):
|
||||
echo c.config $ n.info, " POP config is now ", c.config.options
|
||||
|
||||
proc processDefine(c: PContext, n: PNode) =
|
||||
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
|
||||
@@ -491,7 +495,7 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
let dest = getStrLit(c, it, 1)
|
||||
var found = parentDir(toFullPath(c.config, n.info)) / s
|
||||
for f in os.walkFiles(found):
|
||||
let obj = completeCFilePath(c.config, AbsoluteFile(dest % extractFilename(f)))
|
||||
let obj = completeCfilePath(c.config, AbsoluteFile(dest % extractFilename(f)))
|
||||
docompile(c, it, AbsoluteFile f, obj)
|
||||
else:
|
||||
let s = expectStrLit(c, n)
|
||||
@@ -501,7 +505,7 @@ proc processCompile(c: PContext, n: PNode) =
|
||||
else:
|
||||
found = findFile(c.config, s)
|
||||
if found.isEmpty: found = AbsoluteFile s
|
||||
let obj = toObjFile(c.config, completeCFilePath(c.config, found, false))
|
||||
let obj = toObjFile(c.config, completeCfilePath(c.config, found, false))
|
||||
docompile(c, it, found, obj)
|
||||
|
||||
proc processLink(c: PContext, n: PNode) =
|
||||
@@ -610,9 +614,9 @@ proc pragmaLine(c: PContext, n: PNode) =
|
||||
|
||||
proc processPragma(c: PContext, n: PNode, i: int) =
|
||||
let it = n[i]
|
||||
if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(c, n)
|
||||
elif it[0].kind != nkIdent: invalidPragma(c, n)
|
||||
elif it[1].kind != nkIdent: invalidPragma(c, n)
|
||||
if it.kind notin nkPragmaCallKinds and it.safeLen == 2: invalidPragma(c, n)
|
||||
elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent:
|
||||
invalidPragma(c, n)
|
||||
|
||||
var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options)
|
||||
userPragma.ast = newNode(nkPragma, n.info, n.sons[i+1..^1])
|
||||
@@ -762,6 +766,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
let ident = considerQuotedIdent(c, key)
|
||||
var userPragma = strTableGet(c.userPragmas, ident)
|
||||
if userPragma != nil:
|
||||
if {optStyleHint, optStyleError} * c.config.globalOptions != {}:
|
||||
styleCheckUse(c.config, key.info, userPragma)
|
||||
|
||||
# number of pragmas increase/decrease with user pragma expansion
|
||||
inc c.instCounter
|
||||
if c.instCounter > 100:
|
||||
@@ -774,6 +781,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
else:
|
||||
let k = whichKeyword(ident)
|
||||
if k in validPragmas:
|
||||
if {optStyleHint, optStyleError} * c.config.globalOptions != {}:
|
||||
checkPragmaUse(c.config, key.info, k, ident.s)
|
||||
case k
|
||||
of wExportc:
|
||||
makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info)
|
||||
@@ -858,12 +867,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
incl(sym.loc.flags, lfNoDecl)
|
||||
# implies nodecl, because otherwise header would not make sense
|
||||
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
|
||||
of wNosideeffect:
|
||||
of wNoSideEffect:
|
||||
noVal(c, it)
|
||||
if sym != nil:
|
||||
incl(sym.flags, sfNoSideEffect)
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
|
||||
of wSideeffect:
|
||||
of wSideEffect:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfSideEffect)
|
||||
of wNoreturn:
|
||||
@@ -874,6 +883,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
if sym.typ[0] != nil:
|
||||
localError(c.config, sym.ast[paramsPos][0].info,
|
||||
".noreturn with return type not allowed")
|
||||
of wNoDestroy:
|
||||
noVal(c, it)
|
||||
incl(sym.flags, sfGeneratedOp)
|
||||
of wDynlib:
|
||||
processDynLib(c, it, sym)
|
||||
of wCompilerProc, wCore:
|
||||
@@ -1003,12 +1015,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||
noVal(c, it)
|
||||
if sym != nil: incl(sym.flags, sfNoInit)
|
||||
of wCodegenDecl: processCodegenDecl(c, it, sym)
|
||||
of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
|
||||
wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
|
||||
wLinedir, wOptimization, wMovechecks, wCallconv, wDebugger, wProfiler,
|
||||
wFloatchecks, wNanChecks, wInfChecks, wPatterns, wTrMacros:
|
||||
of wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks,
|
||||
wOverflowChecks, wNilChecks, wAssertions, wWarnings, wHints,
|
||||
wLineDir, wOptimization, wStyleChecks, wCallconv, wDebugger, wProfiler,
|
||||
wFloatChecks, wNanChecks, wInfChecks, wPatterns, wTrMacros:
|
||||
processOption(c, it, c.config.options)
|
||||
of wStacktrace, wLinetrace:
|
||||
of wStackTrace, wLineTrace:
|
||||
if sym.kind in {skProc, skMethod, skConverter}:
|
||||
processOption(c, it, sym.options)
|
||||
else:
|
||||
|
||||
@@ -64,14 +64,11 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
|
||||
else:
|
||||
result = '`' & x & '`'
|
||||
|
||||
when not defined(nimpretty):
|
||||
const
|
||||
IndentWidth = 2
|
||||
longIndentWid = IndentWidth * 2
|
||||
else:
|
||||
template IndentWidth: untyped = lexer.gIndentationWidth
|
||||
template longIndentWid: untyped = IndentWidth() * 2
|
||||
const
|
||||
IndentWidth = 2
|
||||
longIndentWid = IndentWidth * 2
|
||||
|
||||
when defined(nimpretty):
|
||||
proc minmaxLine(n: PNode): (int, int) =
|
||||
case n.kind
|
||||
of nkTripleStrLit:
|
||||
@@ -898,16 +895,19 @@ proc accentedName(g: var TSrcGen, n: PNode) =
|
||||
gsub(g, n)
|
||||
|
||||
proc infixArgument(g: var TSrcGen, n: PNode, i: int) =
|
||||
if i >= n.len: return
|
||||
|
||||
if i < 1 and i > 2: return
|
||||
var needsParenthesis = false
|
||||
let n_next = n[i].skipHiddenNodes
|
||||
if n_next.kind == nkInfix:
|
||||
if n_next[0].kind in {nkSym, nkIdent} and n[0].kind in {nkSym, nkIdent}:
|
||||
let nextId = if n_next[0].kind == nkSym: n_next[0].sym.name else: n_next[0].ident
|
||||
let nNext = n[i].skipHiddenNodes
|
||||
if nNext.kind == nkInfix:
|
||||
if nNext[0].kind in {nkSym, nkIdent} and n[0].kind in {nkSym, nkIdent}:
|
||||
let nextId = if nNext[0].kind == nkSym: nNext[0].sym.name else: nNext[0].ident
|
||||
let nnId = if n[0].kind == nkSym: n[0].sym.name else: n[0].ident
|
||||
if getPrecedence(nextId) < getPrecedence(nnId):
|
||||
needsParenthesis = true
|
||||
if i == 1:
|
||||
if getPrecedence(nextId) < getPrecedence(nnId):
|
||||
needsParenthesis = true
|
||||
elif i == 2:
|
||||
if getPrecedence(nextId) <= getPrecedence(nnId):
|
||||
needsParenthesis = true
|
||||
if needsParenthesis:
|
||||
put(g, tkParLe, "(")
|
||||
gsub(g, n, i)
|
||||
@@ -1144,10 +1144,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
elif n[0].kind == nkSym: n[0].sym.name
|
||||
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
|
||||
else: nil
|
||||
let n_next = skipHiddenNodes(n[1])
|
||||
if n_next.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
||||
let nNext = skipHiddenNodes(n[1])
|
||||
if nNext.kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
|
||||
put(g, tkSpaces, Space)
|
||||
if n_next.kind == nkInfix:
|
||||
if nNext.kind == nkInfix:
|
||||
put(g, tkParLe, "(")
|
||||
gsub(g, n.sons[1])
|
||||
put(g, tkParRi, ")")
|
||||
|
||||
@@ -151,10 +151,10 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
|
||||
if a.kind == nkIncludeStmt:
|
||||
for i in 0..<a.len:
|
||||
var f = checkModuleName(graph.config, a.sons[i])
|
||||
if f != InvalidFileIDX:
|
||||
if f != InvalidFileIdx:
|
||||
if containsOrIncl(includedFiles, f.int):
|
||||
localError(graph.config, a.info, "recursive dependency: '$1'" %
|
||||
toFilename(graph.config, f))
|
||||
toMsgFilename(graph.config, f))
|
||||
else:
|
||||
let nn = includeModule(graph, module, f)
|
||||
let nnn = expandIncludes(graph, module, nn, modulePath,
|
||||
|
||||
@@ -35,7 +35,7 @@ proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf",
|
||||
|
||||
proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
|
||||
case classify(f)
|
||||
of fcNaN:
|
||||
of fcNan:
|
||||
result = "NAN"
|
||||
of fcNegZero:
|
||||
result = "-0.0" & literalPostfix
|
||||
|
||||
@@ -16,7 +16,7 @@ import
|
||||
procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
|
||||
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
|
||||
evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
|
||||
lowerings, pluginsupport, plugins/active, rod, lineinfos, strtabs
|
||||
lowerings, pluginsupport, plugins/active, rod, lineinfos, strtabs, int128
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward
|
||||
|
||||
@@ -24,7 +24,7 @@ when defined(nimfix):
|
||||
import nimfix/prettybase
|
||||
|
||||
when not defined(leanCompiler):
|
||||
import semparallel
|
||||
import spawn, semparallel
|
||||
|
||||
# implementation
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
|
||||
src.intVal notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp):
|
||||
result = convNotInRange
|
||||
elif src.kind in nkFloatLit..nkFloat64Lit and
|
||||
(classify(src.floatVal) in {fcNaN, fcNegInf, fcInf} or
|
||||
(classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or
|
||||
src.floatVal.int64 notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp)):
|
||||
result = convNotInRange
|
||||
elif targetBaseTyp.kind in tyFloat..tyFloat64:
|
||||
@@ -160,7 +160,7 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
|
||||
not floatRangeCheck(src.floatVal, targetTyp):
|
||||
result = convNotInRange
|
||||
elif src.kind in nkCharLit..nkUInt64Lit and
|
||||
not floatRangeCheck(src.intval.float, targetTyp):
|
||||
not floatRangeCheck(src.intVal.float, targetTyp):
|
||||
result = convNotInRange
|
||||
else:
|
||||
# we use d, s here to speed up that operation a bit:
|
||||
@@ -220,6 +220,10 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
|
||||
closeScope(c)
|
||||
if lifted != nil: t = lifted
|
||||
|
||||
proc isOwnedSym(c: PContext; n: PNode): bool =
|
||||
let s = qualifiedLookUp(c, n, {})
|
||||
result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned"
|
||||
|
||||
proc semConv(c: PContext, n: PNode): PNode =
|
||||
if sonsLen(n) != 2:
|
||||
localError(c.config, n.info, "a type conversion takes exactly one argument")
|
||||
@@ -247,7 +251,7 @@ proc semConv(c: PContext, n: PNode): PNode =
|
||||
|
||||
maybeLiftType(targetType, c, n[0].info)
|
||||
|
||||
if targetType.kind in {tySink, tyLent, tyOwned}:
|
||||
if targetType.kind in {tySink, tyLent} or isOwnedSym(c, n[0]):
|
||||
let baseType = semTypeNode(c, n.sons[1], nil).skipTypes({tyTypeDesc})
|
||||
let t = newTypeS(targetType.kind, c)
|
||||
if targetType.kind == tyOwned:
|
||||
@@ -844,9 +848,10 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
|
||||
analyseIfAddressTakenInCall(c, result)
|
||||
if callee.magic != mNone:
|
||||
result = magicsAfterOverloadResolution(c, result, flags)
|
||||
if result.typ != nil and
|
||||
not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty):
|
||||
liftTypeBoundOps(c, result.typ, n.info)
|
||||
when false:
|
||||
if result.typ != nil and
|
||||
not (result.typ.kind == tySequence and result.typ.sons[0].kind == tyEmpty):
|
||||
liftTypeBoundOps(c, result.typ, n.info)
|
||||
#result = patchResolvedTypeBoundOp(c, result)
|
||||
if c.matchedConcept == nil:
|
||||
result = evalAtCompileTime(c, result)
|
||||
@@ -882,7 +887,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
semOpAux(c, n)
|
||||
var t: PType = nil
|
||||
if n.sons[0].typ != nil:
|
||||
t = skipTypes(n.sons[0].typ, abstractInst+{tyOwned}-{tyTypeDesc})
|
||||
t = skipTypes(n.sons[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
|
||||
if t != nil and t.kind == tyProc:
|
||||
# This is a proc variable, apply normal overload resolution
|
||||
let m = resolveIndirectCall(c, n, nOrig, t)
|
||||
@@ -1600,7 +1605,7 @@ proc borrowCheck(c: PContext, n, le, ri: PNode) =
|
||||
template resultTypeIsInferrable(typ: PType): untyped =
|
||||
typ.isMetaType and typ.kind != tyTypeDesc
|
||||
|
||||
proc goodLineInfo(arg: PNode): TLineinfo =
|
||||
proc goodLineInfo(arg: PNode): TLineInfo =
|
||||
if arg.kind == nkStmtListExpr and arg.len > 0:
|
||||
goodLineInfo(arg[^1])
|
||||
else:
|
||||
@@ -1689,8 +1694,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
|
||||
borrowCheck(c, n, lhs, rhs)
|
||||
|
||||
n.sons[1] = fitNode(c, le, rhs, goodLineInfo(n[1]))
|
||||
liftTypeBoundOps(c, lhs.typ, lhs.info)
|
||||
#liftTypeBoundOps(c, n.sons[0].typ, n.sons[0].info)
|
||||
when false: liftTypeBoundOps(c, lhs.typ, lhs.info)
|
||||
|
||||
fixAbstractType(c, n)
|
||||
asgnToResultVar(c, n, n.sons[0], n.sons[1])
|
||||
@@ -2007,8 +2011,11 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||
let oldErrorCount = c.config.errorCounter
|
||||
let oldErrorMax = c.config.errorMax
|
||||
let oldCompilesId = c.compilesContextId
|
||||
inc c.compilesContextIdGenerator
|
||||
c.compilesContextId = c.compilesContextIdGenerator
|
||||
# if this is a nested 'when compiles', do not increase the ID so that
|
||||
# generic instantations can still be cached for this level.
|
||||
if c.compilesContextId == 0:
|
||||
inc c.compilesContextIdGenerator
|
||||
c.compilesContextId = c.compilesContextIdGenerator
|
||||
# do not halt after first error:
|
||||
c.config.errorMax = high(int)
|
||||
|
||||
@@ -2085,7 +2092,7 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
|
||||
# since it's an instantiation, we unmark it as a compilerproc. Otherwise
|
||||
# codegen would fail:
|
||||
if sfCompilerProc in result.flags:
|
||||
result.flags = result.flags - {sfCompilerProc, sfExportC, sfImportC}
|
||||
result.flags = result.flags - {sfCompilerProc, sfExportc, sfImportc}
|
||||
result.loc.r = nil
|
||||
|
||||
proc setMs(n: PNode, s: PSym): PNode =
|
||||
@@ -2100,14 +2107,7 @@ proc semSizeof(c: PContext, n: PNode): PNode =
|
||||
n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType})
|
||||
#restoreOldStyleType(n.sons[1])
|
||||
n.typ = getSysType(c.graph, n.info, tyInt)
|
||||
|
||||
let size = getSize(c.config, n[1].typ)
|
||||
if size >= 0:
|
||||
result = newIntNode(nkIntLit, size)
|
||||
result.info = n.info
|
||||
result.typ = n.typ
|
||||
else:
|
||||
result = n
|
||||
result = foldSizeOf(c.config, n, n)
|
||||
|
||||
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
# this is a hotspot in the compiler!
|
||||
@@ -2147,18 +2147,22 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
result.sons[1] = semStmt(c, x, {})
|
||||
dec c.inParallelStmt
|
||||
of mSpawn:
|
||||
result = setMs(n, s)
|
||||
for i in 1 ..< n.len:
|
||||
result.sons[i] = semExpr(c, n.sons[i])
|
||||
let typ = result[^1].typ
|
||||
if not typ.isEmptyType:
|
||||
if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar:
|
||||
result.typ = createFlowVar(c, typ, n.info)
|
||||
else:
|
||||
result.typ = typ
|
||||
result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
|
||||
when defined(leanCompiler):
|
||||
localError(c.config, n.info, "compiler was built without 'spawn' support")
|
||||
result = n
|
||||
else:
|
||||
result.add c.graph.emptyNode
|
||||
result = setMs(n, s)
|
||||
for i in 1 ..< n.len:
|
||||
result.sons[i] = semExpr(c, n.sons[i])
|
||||
let typ = result[^1].typ
|
||||
if not typ.isEmptyType:
|
||||
if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar:
|
||||
result.typ = createFlowVar(c, typ, n.info)
|
||||
else:
|
||||
result.typ = typ
|
||||
result.add instantiateCreateFlowVarCall(c, typ, n.info).newSymNode
|
||||
else:
|
||||
result.add c.graph.emptyNode
|
||||
of mProcCall:
|
||||
result = setMs(n, s)
|
||||
result.sons[1] = semExpr(c, n.sons[1])
|
||||
@@ -2196,7 +2200,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||
result = setMs(n, s)
|
||||
else:
|
||||
result = c.graph.emptyNode
|
||||
of mSizeOf: result = semSizeof(c, setMs(n, s))
|
||||
of mSizeOf: result =
|
||||
semSizeof(c, setMs(n, s))
|
||||
else:
|
||||
result = semDirectOp(c, n, flags)
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ proc makeRange(typ: PType, first, last: BiggestInt; g: ModuleGraph): PType =
|
||||
let lowerNode = newIntNode(nkIntLit, minA)
|
||||
if typ.kind == tyInt and minA == maxA:
|
||||
result = getIntLitType(g, lowerNode)
|
||||
elif typ.kind in {tyUint, tyUInt64}:
|
||||
elif typ.kind in {tyUInt, tyUInt64}:
|
||||
# these are not ordinal types, so you get no subrange type for these:
|
||||
result = typ
|
||||
else:
|
||||
@@ -187,7 +187,7 @@ proc fitLiteral(c: ConfigRef, n: PNode): PNode =
|
||||
result = n
|
||||
|
||||
let typ = n.typ.skipTypes(abstractRange)
|
||||
if typ.kind in tyUInt..tyUint32:
|
||||
if typ.kind in tyUInt..tyUInt32:
|
||||
result.intVal = result.intVal and lastOrd(c, typ, fixedUnsigned=true)
|
||||
|
||||
proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
@@ -219,12 +219,6 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n, g)
|
||||
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n, g)
|
||||
of mAbsI: result = foldAbs(getInt(a), n, g)
|
||||
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
|
||||
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
|
||||
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(g.config, a.typ) * 8) - 1), n, g)
|
||||
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n, g)
|
||||
of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n, g)
|
||||
of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n, g)
|
||||
of mUnaryLt: result = doAndFit(foldSub(getOrdValue(a), 1, n, g))
|
||||
of mSucc: result = doAndFit(foldAdd(getOrdValue(a), getInt(b), n, g))
|
||||
of mPred: result = doAndFit(foldSub(getOrdValue(a), getInt(b), n, g))
|
||||
@@ -248,8 +242,23 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
|
||||
result = doAndFit(newIntNodeT(`shl`(getInt(a), getInt(b)), n, g))
|
||||
else: internalError(g.config, n.info, "constant folding for shl")
|
||||
of mShrI:
|
||||
let a = cast[uint64](getInt(a))
|
||||
var a = cast[uint64](getInt(a))
|
||||
let b = cast[uint64](getInt(b))
|
||||
# To support the ``-d:nimOldShiftRight`` flag, we need to mask the
|
||||
# signed integers to cut off the extended sign bit in the internal
|
||||
# representation.
|
||||
if 0'u64 < b: # do not cut off the sign extension, when there is
|
||||
# no bit shifting happening.
|
||||
case skipTypes(n.typ, abstractRange).kind
|
||||
of tyInt8: a = a and 0xff'u64
|
||||
of tyInt16: a = a and 0xffff'u64
|
||||
of tyInt32: a = a and 0xffffffff'u64
|
||||
of tyInt:
|
||||
if g.config.target.intSize == 4:
|
||||
a = a and 0xffffffff'u64
|
||||
else:
|
||||
# unsigned and 64 bit integers don't need masking
|
||||
discard
|
||||
let c = cast[BiggestInt](a shr b)
|
||||
result = newIntNodeT(c, n, g)
|
||||
of mAshrI:
|
||||
@@ -422,7 +431,7 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
|
||||
|
||||
# XXX range checks?
|
||||
case dstTyp.kind
|
||||
of tyInt..tyInt64, tyUint..tyUInt64:
|
||||
of tyInt..tyInt64, tyUInt..tyUInt64:
|
||||
case srcTyp.kind
|
||||
of tyFloat..tyFloat64:
|
||||
result = newIntNodeT(int(getFloat(a)), n, g)
|
||||
@@ -432,7 +441,7 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode =
|
||||
let toSigned = dstTyp.kind in tyInt..tyInt64
|
||||
var val = a.getOrdValue
|
||||
|
||||
if dstTyp.kind in {tyInt, tyInt64, tyUint, tyUInt64}:
|
||||
if dstTyp.kind in {tyInt, tyInt64, tyUInt, tyUInt64}:
|
||||
# No narrowing needed
|
||||
discard
|
||||
elif dstTyp.kind in {tyInt..tyInt64}:
|
||||
@@ -639,13 +648,11 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
|
||||
# This fixes bug #544.
|
||||
result = newIntNodeT(lengthOrd(g.config, n.sons[1].typ), n, g)
|
||||
of mSizeOf:
|
||||
let size = getSize(g.config, n[1].typ)
|
||||
if size >= 0:
|
||||
result = newIntNode(nkIntLit, size)
|
||||
result.info = n.info
|
||||
result.typ = getSysType(g, n.info, tyInt)
|
||||
else:
|
||||
result = nil
|
||||
result = foldSizeOf(g.config, n, nil)
|
||||
of mAlignOf:
|
||||
result = foldAlignOf(g.config, n, nil)
|
||||
of mOffsetOf:
|
||||
result = foldOffsetOf(g.config, n, nil)
|
||||
of mAstToStr:
|
||||
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g)
|
||||
of mConStrStr:
|
||||
|
||||
@@ -497,13 +497,13 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
||||
var ctx: GenericCtx
|
||||
ctx.toMixin = initIntset()
|
||||
ctx.toMixin = initIntSet()
|
||||
result = semGenericStmt(c, n, {}, ctx)
|
||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||
|
||||
proc semConceptBody(c: PContext, n: PNode): PNode =
|
||||
var ctx: GenericCtx
|
||||
ctx.toMixin = initIntset()
|
||||
ctx.toMixin = initIntSet()
|
||||
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ proc sameInstantiation(a, b: TInstantiation): bool =
|
||||
proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
|
||||
id: CompilesId): PSym =
|
||||
for inst in genericSym.procInstCache:
|
||||
if inst.compilesId == id and sameInstantiation(entry, inst[]):
|
||||
if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]):
|
||||
return inst.sym
|
||||
|
||||
when false:
|
||||
|
||||
@@ -355,6 +355,8 @@ proc semUnown(c: PContext; n: PNode): PNode =
|
||||
|
||||
result = copyTree(n[1])
|
||||
result.typ = unownedType(c, result.typ)
|
||||
# little hack for injectdestructors.nim (see bug #11350):
|
||||
#result.sons[0].typ = nil
|
||||
|
||||
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
flags: TExprFlags): PNode =
|
||||
@@ -373,52 +375,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||
of mTypeOf:
|
||||
result = semTypeOf(c, n)
|
||||
of mSizeOf:
|
||||
# TODO there is no proper way to find out if a type cannot be queried for the size.
|
||||
let size = getSize(c.config, n[1].typ)
|
||||
# We just assume here that the type might come from the c backend
|
||||
if size == szUnknownSize:
|
||||
# Forward to the c code generation to emit a `sizeof` in the C code.
|
||||
result = n
|
||||
elif size >= 0:
|
||||
result = newIntNode(nkIntLit, size)
|
||||
result.info = n.info
|
||||
result.typ = n.typ
|
||||
else:
|
||||
localError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely, type: " & n[1].typ.typeToString)
|
||||
result = n
|
||||
result = foldSizeOf(c.config, n, n)
|
||||
of mAlignOf:
|
||||
# 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
|
||||
result = foldAlignOf(c.config, n, n)
|
||||
of mOffsetOf:
|
||||
var dotExpr: PNode
|
||||
|
||||
block findDotExpr:
|
||||
if n[1].kind == nkDotExpr:
|
||||
dotExpr = n[1]
|
||||
elif n[1].kind == nkCheckedFieldExpr:
|
||||
dotExpr = n[1][0]
|
||||
else:
|
||||
illFormedAst(n, c.config)
|
||||
|
||||
assert dotExpr != nil
|
||||
|
||||
let value = dotExpr[0]
|
||||
let member = dotExpr[1]
|
||||
|
||||
discard computeSize(c.config, value.typ)
|
||||
|
||||
result = newIntNode(nkIntLit, member.sym.offset)
|
||||
result.info = n.info
|
||||
result.typ = n.typ
|
||||
result = foldOffsetOf(c.config, n, n)
|
||||
of mArrGet:
|
||||
result = semArrGet(c, n, flags)
|
||||
of mArrPut:
|
||||
|
||||
@@ -200,8 +200,9 @@ proc semConstructFields(c: PContext, recNode: PNode,
|
||||
fieldsPresentInInitExpr(c, fields, initExpr)
|
||||
|
||||
template checkMissingFields(branchNode: PNode) =
|
||||
let fields = branchNode[branchNode.len - 1]
|
||||
checkForMissingFields(c, fields, initExpr)
|
||||
if branchNode != nil:
|
||||
let fields = branchNode[branchNode.len - 1]
|
||||
checkForMissingFields(c, fields, initExpr)
|
||||
|
||||
let discriminator = recNode.sons[0]
|
||||
internalAssert c.config, discriminator.kind == nkSym
|
||||
@@ -352,6 +353,9 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
if optNimV2 in c.config.globalOptions:
|
||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
||||
# we have to watch out, there are also 'owned proc' types that can be used
|
||||
# multiple times as long as they don't have closures.
|
||||
result.typ.flags.incl tfHasOwned
|
||||
if t.kind != tyObject:
|
||||
localError(c.config, n.info, errGenerated, "object constructor needs an object type")
|
||||
return
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
import
|
||||
ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs,
|
||||
renderer, types, modulegraphs, options
|
||||
renderer, types, modulegraphs, options, spawn
|
||||
|
||||
from trees import getMagic
|
||||
from strutils import `%`
|
||||
|
||||
@@ -18,7 +18,7 @@ when not defined(leanCompiler):
|
||||
when defined(useDfa):
|
||||
import dfa
|
||||
|
||||
include liftdestructors
|
||||
import liftdestructors
|
||||
|
||||
#[ Second semantic checking pass over the AST. Necessary because the old
|
||||
way had some inherent problems. Performs:
|
||||
@@ -74,7 +74,7 @@ type
|
||||
init: seq[int] # list of initialized variables
|
||||
guards: TModel # nested guards
|
||||
locked: seq[PNode] # locked locations
|
||||
gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
|
||||
inEnforcedNoSideEffects: bool
|
||||
maxLockLevel, currLockLevel: TLockLevel
|
||||
config: ConfigRef
|
||||
@@ -263,7 +263,7 @@ proc useVar(a: PEffects, n: PNode) =
|
||||
# prevent superfluous warnings about the same variable:
|
||||
a.init.add s.id
|
||||
if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and
|
||||
s.magic != mNimVm:
|
||||
s.magic != mNimvm:
|
||||
if s.guard != nil: guardGlobal(a, n, s.guard)
|
||||
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and
|
||||
(tfHasGCedMem in s.typ.flags or s.typ.isGCedMem):
|
||||
@@ -476,7 +476,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||
markSideEffect(tracked, s)
|
||||
mergeLockLevels(tracked, n, s.getLockLevel)
|
||||
|
||||
proc procVarcheck(n: PNode; conf: ConfigRef) =
|
||||
proc procVarCheck(n: PNode; conf: ConfigRef) =
|
||||
if n.kind in nkSymChoices:
|
||||
for x in n: procVarCheck(x, conf)
|
||||
elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
|
||||
@@ -485,7 +485,7 @@ proc procVarcheck(n: PNode; conf: ConfigRef) =
|
||||
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||
let n = n.skipConv
|
||||
if paramType.isNil or paramType.kind != tyTypeDesc:
|
||||
procVarcheck skipConvAndClosure(n), tracked.config
|
||||
procVarCheck skipConvAndClosure(n), tracked.config
|
||||
#elif n.kind in nkSymChoices:
|
||||
# echo "came here"
|
||||
let paramType = paramType.skipTypesOrNil(abstractInst)
|
||||
@@ -696,6 +696,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
addEffect(tracked, n.sons[0], useLineInfo=false)
|
||||
for i in 0 ..< safeLen(n):
|
||||
track(tracked, n.sons[i])
|
||||
createTypeBoundOps(tracked.graph, tracked.c, n[0].typ, n.info)
|
||||
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`.
|
||||
@@ -709,7 +710,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
return
|
||||
if n.typ != nil:
|
||||
if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
|
||||
createTypeBoundOps(tracked.c, n.typ, n.info)
|
||||
createTypeBoundOps(tracked.graph, tracked.c, n.typ, n.info)
|
||||
if a.kind == nkCast and a[1].typ.kind == tyProc:
|
||||
a = a[1]
|
||||
# XXX: in rare situations, templates and macros will reach here after
|
||||
@@ -770,18 +771,18 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
notNilCheck(tracked, n.sons[1], n.sons[0].typ)
|
||||
when false: cstringCheck(tracked, n)
|
||||
if tracked.owner.kind != skMacro:
|
||||
createTypeBoundOps(tracked.c, n[0].typ, n.info)
|
||||
createTypeBoundOps(tracked.graph, tracked.c, n[0].typ, n.info)
|
||||
of nkVarSection, nkLetSection:
|
||||
for child in n:
|
||||
let last = lastSon(child)
|
||||
if last.kind != nkEmpty: track(tracked, last)
|
||||
if tracked.owner.kind != skMacro:
|
||||
if child.kind == nkVarTuple:
|
||||
createTypeBoundOps(tracked.c, child[^1].typ, child.info)
|
||||
createTypeBoundOps(tracked.graph, tracked.c, child[^1].typ, child.info)
|
||||
for i in 0..child.len-3:
|
||||
createTypeBoundOps(tracked.c, child[i].typ, child.info)
|
||||
createTypeBoundOps(tracked.graph, tracked.c, child[i].typ, child.info)
|
||||
else:
|
||||
createTypeBoundOps(tracked.c, child[0].typ, child.info)
|
||||
createTypeBoundOps(tracked.graph, tracked.c, child[0].typ, child.info)
|
||||
if child.kind == nkIdentDefs and last.kind != nkEmpty:
|
||||
for i in 0 .. child.len-3:
|
||||
initVar(tracked, child.sons[i], volatileCheck=false)
|
||||
@@ -827,15 +828,15 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
if tracked.owner.kind != skMacro:
|
||||
if it.kind == nkVarTuple:
|
||||
for x in it:
|
||||
createTypeBoundOps(tracked.c, x.typ, x.info)
|
||||
createTypeBoundOps(tracked.graph, tracked.c, x.typ, x.info)
|
||||
else:
|
||||
createTypeBoundOps(tracked.c, it.typ, it.info)
|
||||
createTypeBoundOps(tracked.graph, tracked.c, it.typ, it.info)
|
||||
let iterCall = n[n.len-2]
|
||||
let loopBody = n[n.len-1]
|
||||
if tracked.owner.kind != skMacro and iterCall.safelen > 1:
|
||||
if tracked.owner.kind != skMacro and iterCall.safeLen > 1:
|
||||
# XXX this is a bit hacky:
|
||||
if iterCall[1].typ != nil and iterCall[1].typ.skipTypes(abstractVar).kind notin {tyVarargs, tyOpenArray}:
|
||||
createTypeBoundOps(tracked.c, iterCall[1].typ, iterCall[1].info)
|
||||
createTypeBoundOps(tracked.graph, tracked.c, iterCall[1].typ, iterCall[1].info)
|
||||
track(tracked, iterCall)
|
||||
track(tracked, loopBody)
|
||||
setLen(tracked.init, oldState)
|
||||
@@ -860,7 +861,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
lockLocations(tracked, pragmaList.sons[i])
|
||||
elif pragma == wGcSafe:
|
||||
enforcedGcSafety = true
|
||||
elif pragma == wNosideeffect:
|
||||
elif pragma == wNoSideEffect:
|
||||
enforceNoSideEffects = true
|
||||
if enforcedGcSafety: tracked.inEnforcedGcSafe = true
|
||||
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
|
||||
@@ -1034,5 +1035,5 @@ proc trackTopLevelStmt*(c: PContext; module: PSym; n: PNode) =
|
||||
var effects = newNode(nkEffectList, n.info)
|
||||
var t: TEffects
|
||||
initEffects(g, effects, module, t, c)
|
||||
t.isToplevel = true
|
||||
t.isTopLevel = true
|
||||
track(t, n)
|
||||
|
||||
@@ -189,16 +189,16 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
template semExceptBranchType(typeNode: PNode): bool =
|
||||
# returns true if exception type is imported type
|
||||
let typ = semTypeNode(c, typeNode, nil).toObject()
|
||||
var is_imported = false
|
||||
var isImported = false
|
||||
if isImportedException(typ, c.config):
|
||||
is_imported = true
|
||||
isImported = true
|
||||
elif not isException(typ):
|
||||
localError(c.config, typeNode.info, errExprCannotBeRaised)
|
||||
|
||||
if containsOrIncl(check, typ.id):
|
||||
localError(c.config, typeNode.info, errExceptionAlreadyHandled)
|
||||
typeNode = newNodeIT(nkType, typeNode.info, typ)
|
||||
is_imported
|
||||
isImported
|
||||
|
||||
result = n
|
||||
inc c.p.inTryStmt
|
||||
@@ -223,9 +223,9 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
|
||||
if a.len == 2 and a[0].isInfixAs():
|
||||
# support ``except Exception as ex: body``
|
||||
let is_imported = semExceptBranchType(a[0][1])
|
||||
let isImported = semExceptBranchType(a[0][1])
|
||||
let symbol = newSymG(skLet, a[0][2], c)
|
||||
symbol.typ = if is_imported: a[0][1].typ
|
||||
symbol.typ = if isImported: a[0][1].typ
|
||||
else: a[0][1].typ.toRef()
|
||||
addDecl(c, symbol)
|
||||
# Overwrite symbol in AST with the symbol in the symbol table.
|
||||
@@ -241,13 +241,13 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
# if ``except: body`` already encountered,
|
||||
# cannot be followed by a ``except KeyError, ... : body`` block
|
||||
inc catchAllExcepts
|
||||
var is_native, is_imported: bool
|
||||
var isNative, isImported: bool
|
||||
for j in 0..a.len-2:
|
||||
let tmp = semExceptBranchType(a[j])
|
||||
if tmp: is_imported = true
|
||||
else: is_native = true
|
||||
if tmp: isImported = true
|
||||
else: isNative = true
|
||||
|
||||
if is_native and is_imported:
|
||||
if isNative and isImported:
|
||||
localError(c.config, a[0].info, "Mix of imported and native exception types is not allowed in one except branch")
|
||||
|
||||
elif a.kind == nkFinally:
|
||||
@@ -320,6 +320,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
|
||||
result = semIdentWithPragma(c, kind, n, {})
|
||||
if result.owner.kind == skModule:
|
||||
incl(result.flags, sfGlobal)
|
||||
result.options = c.config.options
|
||||
|
||||
proc getLineInfo(n: PNode): TLineInfo =
|
||||
case n.kind
|
||||
@@ -333,7 +334,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym =
|
||||
suggestSym(c.config, info, result, c.graph.usageSym)
|
||||
|
||||
proc checkNilable(c: PContext; v: PSym) =
|
||||
if {sfGlobal, sfImportC} * v.flags == {sfGlobal} and
|
||||
if {sfGlobal, sfImportc} * v.flags == {sfGlobal} and
|
||||
{tfNotNil, tfNeedsInit} * v.typ.flags != {}:
|
||||
if v.astdef.isNil:
|
||||
message(c.config, v.info, warnProveInit, v.name.s)
|
||||
@@ -427,8 +428,8 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
|
||||
proc setVarType(c: PContext; v: PSym, typ: PType) =
|
||||
if v.typ != nil and not sameTypeOrNil(v.typ, typ):
|
||||
localError(c.config, v.info, "inconsistent typing for reintroduced symbol '" &
|
||||
v.name.s & "': previous type was: " & typeToString(v.typ) &
|
||||
"; new type is: " & typeToString(typ))
|
||||
v.name.s & "': previous type was: " & typeToString(v.typ, preferDesc) &
|
||||
"; new type is: " & typeToString(typ, preferDesc))
|
||||
v.typ = typ
|
||||
|
||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
@@ -493,7 +494,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||
# this can only happen for errornous var statements:
|
||||
if typ == nil: continue
|
||||
typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {})
|
||||
liftTypeBoundOps(c, typ, a.info)
|
||||
when false: liftTypeBoundOps(c, typ, a.info)
|
||||
instAllTypeBoundOp(c, a.info)
|
||||
var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
|
||||
if a.kind == nkVarTuple:
|
||||
@@ -877,7 +878,7 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
pushCaseContext(c, n)
|
||||
n.sons[0] = semExprWithType(c, n.sons[0])
|
||||
var chckCovered = false
|
||||
var covered: BiggestInt = 0
|
||||
var covered: Int128 = toInt128(0)
|
||||
var typ = commonTypeBegin
|
||||
var hasElse = false
|
||||
let caseTyp = skipTypes(n.sons[0].typ, abstractVar-{tyTypeDesc})
|
||||
@@ -891,10 +892,12 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||
of tyFloat..tyFloat128, tyString, tyError:
|
||||
discard
|
||||
else:
|
||||
popCaseContext(c)
|
||||
closeScope(c)
|
||||
if caseStmtMacros in c.features:
|
||||
result = handleCaseStmtMacro(c, n)
|
||||
if result != nil: return result
|
||||
|
||||
if result != nil:
|
||||
return result
|
||||
localError(c.config, n.sons[0].info, errSelectorMustBeOfCertainTypes)
|
||||
return
|
||||
for i in 1 ..< sonsLen(n):
|
||||
@@ -1007,7 +1010,6 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
||||
let typsym = pkg.tab.strTableGet(typName)
|
||||
if typsym.isNil:
|
||||
s = semIdentDef(c, name[1], skType)
|
||||
styleCheckDef(c.config, s)
|
||||
onDef(name[1].info, s)
|
||||
s.typ = newTypeS(tyObject, c)
|
||||
s.typ.sym = s
|
||||
@@ -1022,7 +1024,6 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
||||
s = typsym
|
||||
else:
|
||||
s = semIdentDef(c, name, skType)
|
||||
styleCheckDef(c.config, s)
|
||||
onDef(name.info, s)
|
||||
s.typ = newTypeS(tyForward, c)
|
||||
s.typ.sym = s # process pragmas:
|
||||
@@ -1251,6 +1252,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
|
||||
if a.kind == nkCommentStmt: continue
|
||||
let name = typeSectionTypeName(c, a.sons[0])
|
||||
var s = name.sym
|
||||
# check the style here after the pragmas have been processed:
|
||||
styleCheckDef(c.config, s)
|
||||
# compute the type's size and check for illegal recursions:
|
||||
if a.sons[1].kind == nkEmpty:
|
||||
var x = a[2]
|
||||
@@ -1284,9 +1287,9 @@ proc semAllTypeSections(c: PContext; n: PNode): PNode =
|
||||
of nkIncludeStmt:
|
||||
for i in 0..<n.len:
|
||||
var f = checkModuleName(c.config, n.sons[i])
|
||||
if f != InvalidFileIDX:
|
||||
if f != InvalidFileIdx:
|
||||
if containsOrIncl(c.includedFiles, f.int):
|
||||
localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
|
||||
localError(c.config, n.info, errRecursiveDependencyX % toMsgFilename(c.config, f))
|
||||
else:
|
||||
let code = c.graph.includeFileCallback(c.graph, c.module, f)
|
||||
gatherStmts c, code, result
|
||||
@@ -1861,7 +1864,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
" operator has to be enabled with {.experimental: \"callOperator\".}")
|
||||
|
||||
if n.sons[bodyPos].kind != nkEmpty and sfError notin s.flags:
|
||||
# for DLL generation it is annoying to check for sfImportc!
|
||||
# for DLL generation we allow sfImportc to have a body, for use in VM
|
||||
if sfBorrow in s.flags:
|
||||
localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s)
|
||||
let usePseudoGenerics = kind in {skMacro, skTemplate}
|
||||
@@ -1879,12 +1882,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
|
||||
c.p.wasForwarded = proto != nil
|
||||
maybeAddResult(c, s, n)
|
||||
if lfDynamicLib notin s.loc.flags:
|
||||
# no semantic checking for importc:
|
||||
s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||
# unfortunately we cannot skip this step when in 'system.compiles'
|
||||
# context as it may even be evaluated in 'system.compiles':
|
||||
trackProc(c, s, s.ast[bodyPos])
|
||||
# semantic checking also needed with importc in case used in VM
|
||||
s.ast[bodyPos] = hloBody(c, semProcBody(c, n.sons[bodyPos]))
|
||||
# unfortunately we cannot skip this step when in 'system.compiles'
|
||||
# context as it may even be evaluated in 'system.compiles':
|
||||
trackProc(c, s, s.ast[bodyPos])
|
||||
if s.kind == skMethod: semMethodPrototype(c, s, n)
|
||||
else:
|
||||
if (s.typ.sons[0] != nil and kind != skIterator) or kind == skMacro:
|
||||
@@ -1897,8 +1899,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
|
||||
fixupInstantiatedSymbols(c, s)
|
||||
if s.kind == skMethod: semMethodPrototype(c, s, n)
|
||||
if sfImportc in s.flags:
|
||||
# so we just ignore the body after semantic checking for importc:
|
||||
n.sons[bodyPos] = c.graph.emptyNode
|
||||
# don't ignore the body in case used in VM
|
||||
# n.sons[bodyPos] = c.graph.emptyNode
|
||||
discard
|
||||
popProcCon(c)
|
||||
else:
|
||||
if s.kind == skMethod: semMethodPrototype(c, s, n)
|
||||
@@ -2019,9 +2022,9 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
|
||||
|
||||
proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
|
||||
var f = checkModuleName(c.config, it)
|
||||
if f != InvalidFileIDX:
|
||||
if f != InvalidFileIdx:
|
||||
if containsOrIncl(c.includedFiles, f.int):
|
||||
localError(c.config, n.info, errRecursiveDependencyX % toFilename(c.config, f))
|
||||
localError(c.config, n.info, errRecursiveDependencyX % toMsgFilename(c.config, f))
|
||||
else:
|
||||
addSon(includeStmtResult, semStmt(c, c.graph.includeFileCallback(c.graph, c.module, f), {}))
|
||||
excl(c.includedFiles, f.int)
|
||||
|
||||
@@ -496,7 +496,7 @@ proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
|
||||
if overlap(t.sons[i].sons[j].skipConv, ex):
|
||||
localError(c.config, ex.info, errDuplicateCaseLabel)
|
||||
|
||||
proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode =
|
||||
proc semBranchRange(c: PContext, t, a, b: PNode, covered: var Int128): PNode =
|
||||
checkMinSonsLen(t, 1, c.config)
|
||||
let ac = semConstExpr(c, a)
|
||||
let bc = semConstExpr(c, b)
|
||||
@@ -510,12 +510,12 @@ proc semBranchRange(c: PContext, t, a, b: PNode, covered: var BiggestInt): PNode
|
||||
else: covered = covered + getOrdValue(bc) - getOrdValue(ac) + 1
|
||||
|
||||
proc semCaseBranchRange(c: PContext, t, b: PNode,
|
||||
covered: var BiggestInt): PNode =
|
||||
covered: var Int128): PNode =
|
||||
checkSonsLen(b, 3, c.config)
|
||||
result = semBranchRange(c, t, b.sons[1], b.sons[2], covered)
|
||||
|
||||
proc semCaseBranchSetElem(c: PContext, t, b: PNode,
|
||||
covered: var BiggestInt): PNode =
|
||||
covered: var Int128): PNode =
|
||||
if isRange(b):
|
||||
checkSonsLen(b, 3, c.config)
|
||||
result = semBranchRange(c, t, b.sons[1], b.sons[2], covered)
|
||||
@@ -527,7 +527,7 @@ proc semCaseBranchSetElem(c: PContext, t, b: PNode,
|
||||
inc(covered)
|
||||
|
||||
proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
|
||||
covered: var BiggestInt) =
|
||||
covered: var Int128) =
|
||||
let lastIndex = sonsLen(branch) - 2
|
||||
for i in 0..lastIndex:
|
||||
var b = branch.sons[i]
|
||||
@@ -567,12 +567,22 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
|
||||
for i in lastIndex.succ..(sonsLen(branch) - 2):
|
||||
checkForOverlap(c, t, i, branchIndex)
|
||||
|
||||
proc toCover(c: PContext, t: PType): BiggestInt =
|
||||
proc toCover(c: PContext, t: PType): Int128 =
|
||||
let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
|
||||
if t2.kind == tyEnum and enumHasHoles(t2):
|
||||
result = sonsLen(t2.n)
|
||||
result = toInt128(sonsLen(t2.n))
|
||||
else:
|
||||
result = lengthOrd(c.config, skipTypes(t, abstractVar-{tyTypeDesc}))
|
||||
# <----
|
||||
let t = skipTypes(t, abstractVar-{tyTypeDesc})
|
||||
# XXX: hack incoming. lengthOrd is incorrect for 64bit integer
|
||||
# types because it doesn't uset Int128 yet. This entire branching
|
||||
# should be removed as soon as lengthOrd uses int128.
|
||||
if t.kind in {tyInt64, tyUInt64}:
|
||||
result = toInt128(1) shl 64
|
||||
elif t.kind in {tyInt, tyUInt}:
|
||||
result = toInt128(1) shl (c.config.target.intSize * 8)
|
||||
else:
|
||||
result = toInt128(lengthOrd(c.config, t))
|
||||
|
||||
proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
father: PNode, rectype: PType, hasCaseFields = false)
|
||||
@@ -603,7 +613,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
internalError(c.config, "semRecordCase: discriminant is no symbol")
|
||||
return
|
||||
incl(a.sons[0].sym.flags, sfDiscriminant)
|
||||
var covered: BiggestInt = 0
|
||||
var covered: Int128 = toInt128(0)
|
||||
var chckCovered = false
|
||||
var typ = skipTypes(a.sons[0].typ, abstractVar-{tyTypeDesc})
|
||||
const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt32, tyBool}
|
||||
@@ -710,6 +720,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
|
||||
suggestSym(c.config, n.sons[i].info, f, c.graph.usageSym)
|
||||
f.typ = typ
|
||||
f.position = pos
|
||||
f.options = c.config.options
|
||||
if fieldOwner != nil and
|
||||
{sfImportc, sfExportc} * fieldOwner.flags != {} and
|
||||
not hasCaseFields and f.loc.r == nil:
|
||||
@@ -1368,8 +1379,11 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
if tx.isNil or isTupleRecursive(tx):
|
||||
localError(c.config, n.info, "illegal recursion in type '$1'" % typeToString(result[0]))
|
||||
return errorType(c)
|
||||
if tx != result and tx.kind == tyObject and tx.sons[0] != nil:
|
||||
semObjectTypeForInheritedGenericInst(c, n, tx)
|
||||
if tx != result and tx.kind == tyObject:
|
||||
if tx.sons[0] != nil:
|
||||
semObjectTypeForInheritedGenericInst(c, n, tx)
|
||||
var position = 0
|
||||
recomputeFieldPositions(tx, tx.n, position)
|
||||
|
||||
proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
|
||||
if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward} and prev != nil:
|
||||
@@ -1604,8 +1618,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
elif op.id == ord(wType):
|
||||
checkSonsLen(n, 2, c.config)
|
||||
result = semTypeof(c, n[1], prev)
|
||||
elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeof:
|
||||
result = semTypeOf2(c, n, prev)
|
||||
elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
|
||||
result = semTypeof2(c, n, prev)
|
||||
elif op.s == "owned" and optNimV2 notin c.config.globalOptions and n.len == 2:
|
||||
result = semTypeExpr(c, n[1], prev)
|
||||
else:
|
||||
if c.inGenericContext > 0 and n.kind == nkCall:
|
||||
result = makeTypeFromExpr(c, n.copyTree)
|
||||
@@ -1628,7 +1644,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
of mSet: result = semSet(c, n, prev)
|
||||
of mOrdinal: result = semOrdinal(c, n, prev)
|
||||
of mSeq:
|
||||
if c.config.selectedGc == gcDestructors and optNimV2 notin c.config.globalOptions:
|
||||
if c.config.selectedGC == gcDestructors and optNimV2 notin c.config.globalOptions:
|
||||
let s = c.graph.sysTypes[tySequence]
|
||||
assert s != nil
|
||||
assert prev == nil
|
||||
@@ -1646,7 +1662,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
c.typesWithOps.add((result, result))
|
||||
else:
|
||||
result = semContainer(c, n, tySequence, "seq", prev)
|
||||
if c.config.selectedGc == gcDestructors:
|
||||
if c.config.selectedGC == gcDestructors:
|
||||
incl result.flags, tfHasAsgn
|
||||
of mOpt: result = semContainer(c, n, tyOpt, "opt", prev)
|
||||
of mVarargs: result = semVarargs(c, n, prev)
|
||||
@@ -1738,7 +1754,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
markUsed(c.config, n.info, n.sym, c.graph.usageSym)
|
||||
onUse(n.info, n.sym)
|
||||
else:
|
||||
if s.kind != skError: localError(c.config, n.info, errTypeExpected)
|
||||
if s.kind != skError:
|
||||
localError(c.config, n.info, "type expected, but got symbol '$1' of kind '$2'" %
|
||||
[s.name.s, substr($s.kind, 2)])
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
of nkObjectTy: result = semObjectNode(c, n, prev, isInheritable=false)
|
||||
of nkTupleTy: result = semTuple(c, n, prev)
|
||||
@@ -1828,7 +1846,7 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
of mString:
|
||||
setMagicType(c.config, m, tyString, szUncomputedSize)
|
||||
rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar))
|
||||
if c.config.selectedGc == gcDestructors:
|
||||
if c.config.selectedGC == gcDestructors:
|
||||
incl m.typ.flags, tfHasAsgn
|
||||
of mCstring:
|
||||
setMagicIntegral(c.config, m, tyCString, c.config.target.ptrSize)
|
||||
@@ -1869,7 +1887,7 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
setMagicIntegral(c.config, m, tyUncheckedArray, szUncomputedSize)
|
||||
of mSeq:
|
||||
setMagicType(c.config, m, tySequence, szUncomputedSize)
|
||||
if c.config.selectedGc == gcDestructors:
|
||||
if c.config.selectedGC == gcDestructors:
|
||||
incl m.typ.flags, tfHasAsgn
|
||||
assert c.graph.sysTypes[tySequence] == nil
|
||||
c.graph.sysTypes[tySequence] = m.typ
|
||||
|
||||
@@ -539,7 +539,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
let lookup = cl.typeMap.lookup(t)
|
||||
if lookup != nil:
|
||||
result = lookup
|
||||
if tfUnresolved in t.flags or cl.skipTypedesc: result = result.base
|
||||
if result.kind != tyTypeDesc:
|
||||
result = makeTypeDesc(cl.c, result)
|
||||
elif tfUnresolved in t.flags or cl.skipTypedesc:
|
||||
result = result.base
|
||||
elif t.sons[0].kind != tyNone:
|
||||
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
|
||||
|
||||
@@ -595,7 +598,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||
skipIntLiteralParams(result)
|
||||
|
||||
of tySequence:
|
||||
if cl.isReturnType and cl.c.config.selectedGc == gcDestructors and
|
||||
if cl.isReturnType and cl.c.config.selectedGC == gcDestructors and
|
||||
result.attachedOps[attachedDestructor].isNil and
|
||||
result[0].kind != tyEmpty and optNimV2 notin cl.c.config.globalOptions:
|
||||
let s = cl.c.graph.sysTypes[tySequence]
|
||||
@@ -664,6 +667,22 @@ proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
|
||||
result = replaceTypeVarsN(cl, n)
|
||||
popInfoContext(p.config)
|
||||
|
||||
proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) =
|
||||
if t != nil and t.len > 0 and t.sons[0] != nil:
|
||||
let b = skipTypes(t.sons[0], skipPtrs)
|
||||
recomputeFieldPositions(b, b.n, currPosition)
|
||||
case obj.kind
|
||||
of nkRecList:
|
||||
for i in 0 ..< sonsLen(obj): recomputeFieldPositions(nil, obj.sons[i], currPosition)
|
||||
of nkRecCase:
|
||||
recomputeFieldPositions(nil, obj.sons[0], currPosition)
|
||||
for i in 1 ..< sonsLen(obj):
|
||||
recomputeFieldPositions(nil, lastSon(obj.sons[i]), currPosition)
|
||||
of nkSym:
|
||||
obj.sym.position = currPosition
|
||||
inc currPosition
|
||||
else: discard "cannot happen"
|
||||
|
||||
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
|
||||
t: PType): PType =
|
||||
var typeMap = initLayeredTypeMap(pt)
|
||||
@@ -671,6 +690,10 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
|
||||
pushInfoContext(p.config, info)
|
||||
result = replaceTypeVarsT(cl, t)
|
||||
popInfoContext(p.config)
|
||||
let objType = result.skipTypes(abstractInst)
|
||||
if objType.kind == tyObject:
|
||||
var position = 0
|
||||
recomputeFieldPositions(objType, objType.n, position)
|
||||
|
||||
proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
|
||||
t: PType): PType =
|
||||
|
||||
@@ -249,7 +249,7 @@ proc hashType*(t: PType; flags: set[ConsiderFlag] = {CoType}): SigHash =
|
||||
var c: MD5Context
|
||||
md5Init c
|
||||
hashType c, t, flags+{CoOwnerSig}
|
||||
md5Final c, result.Md5Digest
|
||||
md5Final c, result.MD5Digest
|
||||
when defined(debugSigHashes):
|
||||
db.exec(sql"INSERT OR IGNORE INTO sighashes(type, hash) VALUES (?, ?)",
|
||||
typeToString(t), $result)
|
||||
@@ -272,7 +272,7 @@ proc hashProc*(s: PSym): SigHash =
|
||||
# hash, we also hash the line information. This is pretty bad, but the best
|
||||
# solution for now:
|
||||
#c &= s.info.line
|
||||
md5Final c, result.Md5Digest
|
||||
md5Final c, result.MD5Digest
|
||||
|
||||
proc hashNonProc*(s: PSym): SigHash =
|
||||
var c: MD5Context
|
||||
@@ -288,7 +288,7 @@ proc hashNonProc*(s: PSym): SigHash =
|
||||
# might cause:
|
||||
if s.kind == skParam:
|
||||
c &= s.position
|
||||
md5Final c, result.Md5Digest
|
||||
md5Final c, result.MD5Digest
|
||||
|
||||
proc hashOwner*(s: PSym): SigHash =
|
||||
var c: MD5Context
|
||||
@@ -301,7 +301,7 @@ proc hashOwner*(s: PSym): SigHash =
|
||||
c &= "."
|
||||
c &= m.name.s
|
||||
|
||||
md5Final c, result.Md5Digest
|
||||
md5Final c, result.MD5Digest
|
||||
|
||||
proc sigHash*(s: PSym): SigHash =
|
||||
if s.kind in routineKinds and s.typ != nil:
|
||||
@@ -368,14 +368,14 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
|
||||
md5Init(c)
|
||||
c.hashType(sym.typ, {CoProc})
|
||||
c &= char(sym.kind)
|
||||
c.md5Final(result.Md5Digest)
|
||||
c.md5Final(result.MD5Digest)
|
||||
graph.symBodyHashes[sym.id] = result # protect from recursion in the body
|
||||
|
||||
if sym.ast != nil:
|
||||
md5Init(c)
|
||||
c.md5Update(cast[cstring](result.addr), sizeof(result))
|
||||
hashBodyTree(graph, c, sym.ast[bodyPos])
|
||||
c.md5Final(result.Md5Digest)
|
||||
c.md5Final(result.MD5Digest)
|
||||
graph.symBodyHashes[sym.id] = result
|
||||
|
||||
proc idOrSig*(s: PSym, currentModule: string,
|
||||
|
||||
@@ -1293,6 +1293,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
inc(c.inheritancePenalty, depth)
|
||||
result = isSubtype
|
||||
of tyDistinct:
|
||||
a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
|
||||
if a.kind == tyDistinct:
|
||||
if sameDistinctTypes(f, a): result = isEqual
|
||||
#elif f.base.kind == tyAnything: result = isGeneric # issue 4435
|
||||
@@ -1467,10 +1468,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
|
||||
of tyGenericInvocation:
|
||||
var x = a.skipGenericAlias
|
||||
var preventHack = false
|
||||
if x.kind == tyOwned and f.sons[0].kind != tyOwned:
|
||||
preventHack = true
|
||||
x = x.lastSon
|
||||
# XXX: This is very hacky. It should be moved back into liftTypeParam
|
||||
if x.kind in {tyGenericInst, tyArray} and
|
||||
c.calleeSym != nil and
|
||||
c.calleeSym.kind in {skProc, skFunc} and c.call != nil:
|
||||
c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack:
|
||||
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
|
||||
#echo "inferred ", typeToString(inst), " for ", f
|
||||
return typeRel(c, inst, a)
|
||||
@@ -1489,7 +1494,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# Workaround for regression #4589
|
||||
if f.sons[i].kind != tyTypeDesc: return
|
||||
result = isGeneric
|
||||
elif x.kind == tyGenericInst and isGenericSubType(c, x, f, depth, f) and
|
||||
elif x.kind == tyGenericInst and isGenericSubtype(c, x, f, depth, f) and
|
||||
(sonsLen(x) - 1 == sonsLen(f)):
|
||||
# do not recurse here in order to not K bind twice for this code:
|
||||
#
|
||||
@@ -1540,7 +1545,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
|
||||
# Here object inheriting from generic/specialized generic object
|
||||
# crossing path with metatypes/aliases, so we need to separate them
|
||||
# by checking sym.id
|
||||
let genericSubtype = isGenericSubType(c, x, f, depth, f)
|
||||
let genericSubtype = isGenericSubtype(c, x, f, depth, f)
|
||||
if not (genericSubtype and aobj.sym.id != fobj.sym.id) and aOrig.kind != tyGenericBody:
|
||||
depth = -1
|
||||
|
||||
@@ -2456,7 +2461,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||
# we end up here if the argument can be converted into the varargs
|
||||
# formal (eg. seq[T] -> varargs[T]) but we have already instantiated
|
||||
# a container
|
||||
assert arg.kind == nkHiddenStdConv
|
||||
#assert arg.kind == nkHiddenStdConv # for 'nim check'
|
||||
# this assertion can be off
|
||||
localError(c.config, n.sons[a].info, "cannot convert $1 to $2" % [
|
||||
typeToString(n.sons[a].typ), typeToString(formal.typ) ])
|
||||
m.state = csNoMatch
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
proc align(address, alignment: BiggestInt): BiggestInt =
|
||||
result = (address + (alignment - 1)) and not (alignment - 1)
|
||||
|
||||
proc align(address, alignment: int): int =
|
||||
result = (address + (alignment - 1)) and not (alignment - 1)
|
||||
|
||||
|
||||
const
|
||||
## a size is concidered "unknown" when it is an imported type from C
|
||||
## or C++.
|
||||
@@ -18,6 +22,38 @@ const
|
||||
szIllegalRecursion* = -2
|
||||
szUncomputedSize* = -1
|
||||
|
||||
type IllegalTypeRecursionError = object of Exception
|
||||
|
||||
proc raiseIllegalTypeRecursion() =
|
||||
raise newException(IllegalTypeRecursionError, "illegal type recursion")
|
||||
|
||||
type
|
||||
OffsetAccum = object
|
||||
maxAlign: int
|
||||
offset: int
|
||||
|
||||
proc inc(arg: var OffsetAccum; value: int) =
|
||||
if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
|
||||
if value == szUnknownSize or arg.offset == szUnknownSize:
|
||||
arg.offset = szUnknownSize
|
||||
else:
|
||||
arg.offset += value
|
||||
|
||||
proc align(arg: var OffsetAccum; value: int) =
|
||||
if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
|
||||
if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
|
||||
arg.maxAlign = szUnknownSize
|
||||
arg.offset = szUnknownSize
|
||||
else:
|
||||
arg.maxAlign = max(value, arg.maxAlign)
|
||||
arg.offset = align(arg.offset, value)
|
||||
|
||||
proc finish(arg: var OffsetAccum) =
|
||||
if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
|
||||
arg.offset = szUnknownSize
|
||||
else:
|
||||
arg.offset = align(arg.offset, arg.maxAlign)
|
||||
|
||||
proc computeSizeAlign(conf: ConfigRef; typ: PType)
|
||||
|
||||
proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
|
||||
@@ -49,6 +85,14 @@ proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
|
||||
else:
|
||||
result = 1
|
||||
|
||||
|
||||
proc setOffsetsToUnknown(n: PNode) =
|
||||
if n.kind == nkSym and n.sym.kind == skField:
|
||||
n.sym.offset = szUnknownSize
|
||||
else:
|
||||
for i in 0 ..< safeLen(n):
|
||||
setOffsetsToUnknown(n[i])
|
||||
|
||||
proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
|
||||
initialOffset: BiggestInt): tuple[offset, align: BiggestInt] =
|
||||
## ``offset`` is the offset within the object, after the node has been written, no padding bytes added
|
||||
@@ -65,7 +109,7 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
|
||||
assert(n.sons[0].kind == nkSym)
|
||||
let (kindOffset, kindAlign) = computeObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset)
|
||||
|
||||
var maxChildAlign: BiggestInt = 0
|
||||
var maxChildAlign: BiggestInt = if initialOffset == szUnknownSize: szUnknownSize else: 0
|
||||
for i in 1 ..< sonsLen(n):
|
||||
let child = n.sons[i]
|
||||
case child.kind
|
||||
@@ -83,6 +127,7 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
|
||||
else:
|
||||
internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
|
||||
if maxChildAlign == szUnknownSize:
|
||||
setOffsetsToUnknown(n)
|
||||
result.align = szUnknownSize
|
||||
result.offset = szUnknownSize
|
||||
else:
|
||||
@@ -98,17 +143,17 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode,
|
||||
result.align = 1 # maximum of all member alignments
|
||||
var offset = initialOffset
|
||||
for i, child in n.sons:
|
||||
let (new_offset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
|
||||
if new_offset == szIllegalRecursion:
|
||||
let (newOffset, align) = computeObjectOffsetsFoldFunction(conf, child, offset)
|
||||
if newOffset == szIllegalRecursion:
|
||||
result.offset = szIllegalRecursion
|
||||
result.align = szIllegalRecursion
|
||||
return
|
||||
elif new_offset == szUnknownSize or offset == szUnknownSize:
|
||||
elif newOffset == szUnknownSize or offset == szUnknownSize:
|
||||
# if anything is unknown, the rest becomes unknown as well
|
||||
offset = szUnknownSize
|
||||
result.align = szUnknownSize
|
||||
else:
|
||||
offset = new_offset
|
||||
offset = newOffset
|
||||
result.align = max(result.align, align)
|
||||
# final alignment
|
||||
if offset == szUnknownSize:
|
||||
@@ -145,10 +190,12 @@ proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOf
|
||||
var maxChildOffset: BiggestInt = kindUnionOffset
|
||||
for i in 1 ..< sonsLen(n):
|
||||
let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug)
|
||||
if offset < 0:
|
||||
result = offset
|
||||
break
|
||||
maxChildOffset = max(maxChildOffset, offset)
|
||||
if offset == szIllegalRecursion:
|
||||
return szIllegalRecursion
|
||||
if offset == szUnknownSize or maxChildOffset == szUnknownSize:
|
||||
maxChildOffset = szUnknownSize
|
||||
else:
|
||||
maxChildOffset = max(maxChildOffset, offset)
|
||||
result = maxChildOffset
|
||||
of nkRecList:
|
||||
result = initialOffset
|
||||
@@ -291,7 +338,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
of tyUncheckedArray:
|
||||
let base = typ.lastSon
|
||||
computeSizeAlign(conf, base)
|
||||
typ.size = szUnknownSize
|
||||
typ.size = 0
|
||||
typ.align = base.align
|
||||
of tyEnum:
|
||||
if firstOrd(conf, typ) < 0:
|
||||
@@ -335,19 +382,22 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
typ.size = typ.sons[0].size
|
||||
typ.align = typ.sons[0].align
|
||||
of tyTuple:
|
||||
maxAlign = 1
|
||||
sizeAccum = 0
|
||||
for i in 0 ..< sonsLen(typ):
|
||||
let child = typ.sons[i]
|
||||
computeSizeAlign(conf, child)
|
||||
if child.size < 0:
|
||||
typ.size = child.size
|
||||
typ.align = child.align
|
||||
return
|
||||
maxAlign = max(maxAlign, child.align)
|
||||
sizeAccum = align(sizeAccum, child.align) + child.size
|
||||
typ.size = align(sizeAccum, maxAlign)
|
||||
typ.align = int16(maxAlign)
|
||||
try:
|
||||
var accum = OffsetAccum(maxAlign: 1)
|
||||
for i in 0 ..< sonsLen(typ):
|
||||
let child = typ.sons[i]
|
||||
computeSizeAlign(conf, child)
|
||||
accum.align(child.align)
|
||||
if typ.n != nil: # is named tuple (has field symbols)?
|
||||
let sym = typ.n[i].sym
|
||||
sym.offset = accum.offset
|
||||
accum.inc(int(child.size))
|
||||
accum.finish
|
||||
typ.size = accum.offset
|
||||
typ.align = int16(accum.maxAlign)
|
||||
except IllegalTypeRecursionError:
|
||||
typ.size = szIllegalRecursion
|
||||
typ.align = szIllegalRecursion
|
||||
of tyObject:
|
||||
var headerSize: BiggestInt
|
||||
var headerAlign: int16
|
||||
@@ -448,3 +498,58 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
|
||||
else:
|
||||
typ.size = szUncomputedSize
|
||||
typ.align = szUncomputedSize
|
||||
|
||||
template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
|
||||
let config = conf
|
||||
let node = n
|
||||
let typ = node[1].typ
|
||||
computeSizeAlign(config, typ)
|
||||
let size = typ.size
|
||||
if size >= 0:
|
||||
let res = newIntNode(nkIntLit, size)
|
||||
res.info = node.info
|
||||
res.typ = node.typ
|
||||
res
|
||||
else:
|
||||
fallback
|
||||
|
||||
template foldAlignOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
|
||||
let config = conf
|
||||
let node = n
|
||||
let typ = node[1].typ
|
||||
computeSizeAlign(config, typ)
|
||||
let align = typ.align
|
||||
if align >= 0:
|
||||
let res = newIntNode(nkIntLit, align)
|
||||
res.info = node.info
|
||||
res.typ = node.typ
|
||||
res
|
||||
else:
|
||||
fallback
|
||||
|
||||
template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
|
||||
## Returns an int literal node of the given offsetof expression in `n`.
|
||||
## Falls back to `fallback`, if the `offsetof` expression can't be processed.
|
||||
let config = conf
|
||||
let node : PNode = n
|
||||
var dotExpr: PNode
|
||||
block findDotExpr:
|
||||
if node[1].kind == nkDotExpr:
|
||||
dotExpr = node[1]
|
||||
elif node[1].kind == nkCheckedFieldExpr:
|
||||
dotExpr = node[1][0]
|
||||
else:
|
||||
localError(config, node.info, "can't compute offsetof on this ast")
|
||||
|
||||
assert dotExpr != nil
|
||||
let value = dotExpr[0]
|
||||
let member = dotExpr[1]
|
||||
computeSizeAlign(config, value.typ)
|
||||
let offset = member.sym.offset
|
||||
if offset >= 0:
|
||||
let tmp = newIntNode(nkIntLit, offset)
|
||||
tmp.info = node.info
|
||||
tmp.typ = node.typ
|
||||
tmp
|
||||
else:
|
||||
fallback
|
||||
|
||||
428
compiler/spawn.nim
Normal file
428
compiler/spawn.nim
Normal file
@@ -0,0 +1,428 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements threadpool's ``spawn``.
|
||||
|
||||
import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
|
||||
lineinfos, lowerings
|
||||
from trees import getMagic
|
||||
|
||||
proc callProc(a: PNode): PNode =
|
||||
result = newNodeI(nkCall, a.info)
|
||||
result.add a
|
||||
result.typ = a.typ.sons[0]
|
||||
|
||||
# we have 4 cases to consider:
|
||||
# - a void proc --> nothing to do
|
||||
# - a proc returning GC'ed memory --> requires a flowVar
|
||||
# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
|
||||
# - not in a parallel environment --> requires a flowVar for memory safety
|
||||
type
|
||||
TSpawnResult* = enum
|
||||
srVoid, srFlowVar, srByVar
|
||||
TFlowVarKind = enum
|
||||
fvInvalid # invalid type T for 'FlowVar[T]'
|
||||
fvGC # FlowVar of a GC'ed type
|
||||
fvBlob # FlowVar of a blob type
|
||||
|
||||
proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
|
||||
if t.isEmptyType: srVoid
|
||||
elif inParallel and not containsGarbageCollectedRef(t): srByVar
|
||||
else: srFlowVar
|
||||
|
||||
proc flowVarKind(t: PType): TFlowVarKind =
|
||||
if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
|
||||
elif containsGarbageCollectedRef(t): fvInvalid
|
||||
else: fvBlob
|
||||
|
||||
proc typeNeedsNoDeepCopy(t: PType): bool =
|
||||
var t = t.skipTypes(abstractInst)
|
||||
# for the tconvexhull example (and others) we're a bit lax here and pretend
|
||||
# seqs and strings are *by value* only and 'shallow' doesn't exist!
|
||||
if t.kind == tyString: return true
|
||||
# note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
|
||||
# for the stricter check and likewise we can skip 'seq' for a less
|
||||
# strict check:
|
||||
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
|
||||
result = not containsGarbageCollectedRef(t)
|
||||
|
||||
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
|
||||
v: PNode; useShallowCopy=false): PSym =
|
||||
result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
|
||||
owner.options)
|
||||
result.typ = typ
|
||||
incl(result.flags, sfFromGeneric)
|
||||
|
||||
var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
|
||||
vpart.sons[0] = newSymNode(result)
|
||||
vpart.sons[1] = newNodeI(nkEmpty, varSection.info)
|
||||
vpart.sons[2] = if varInit.isNil: v else: vpart[1]
|
||||
varSection.add vpart
|
||||
if varInit != nil:
|
||||
if useShallowCopy and typeNeedsNoDeepCopy(typ):
|
||||
varInit.add newFastAsgnStmt(newSymNode(result), v)
|
||||
else:
|
||||
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
|
||||
deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
|
||||
deepCopyCall.sons[1] = newSymNode(result)
|
||||
deepCopyCall.sons[2] = v
|
||||
varInit.add deepCopyCall
|
||||
|
||||
discard """
|
||||
We generate roughly this:
|
||||
|
||||
proc f_wrapper(thread, args) =
|
||||
barrierEnter(args.barrier) # for parallel statement
|
||||
var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
|
||||
# depending on whether we're in a 'parallel' statement
|
||||
var b = args.b
|
||||
var fv = args.fv
|
||||
|
||||
fv.owner = thread # optional
|
||||
nimArgsPassingDone() # signal parent that the work is done
|
||||
#
|
||||
args.fv.blob = f(a, b, ...)
|
||||
nimFlowVarSignal(args.fv)
|
||||
|
||||
# - or -
|
||||
f(a, b, ...)
|
||||
barrierLeave(args.barrier) # for parallel statement
|
||||
|
||||
stmtList:
|
||||
var scratchObj
|
||||
scratchObj.a = a
|
||||
scratchObj.b = b
|
||||
|
||||
nimSpawn(f_wrapper, addr scratchObj)
|
||||
scratchObj.fv # optional
|
||||
|
||||
"""
|
||||
|
||||
proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
|
||||
varSection, varInit, call, barrier, fv: PNode;
|
||||
spawnKind: TSpawnResult): PSym =
|
||||
var body = newNodeI(nkStmtList, f.info)
|
||||
body.flags.incl nfTransf # do not transform further
|
||||
|
||||
var threadLocalBarrier: PSym
|
||||
if barrier != nil:
|
||||
var varSection2 = newNodeI(nkVarSection, barrier.info)
|
||||
threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
|
||||
barrier.typ, barrier)
|
||||
body.add varSection2
|
||||
body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,
|
||||
threadLocalBarrier.newSymNode)
|
||||
var threadLocalProm: PSym
|
||||
if spawnKind == srByVar:
|
||||
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
|
||||
elif fv != nil:
|
||||
internalAssert g.config, fv.typ.kind == tyGenericInst
|
||||
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
|
||||
body.add varSection
|
||||
body.add varInit
|
||||
if fv != nil and spawnKind != srByVar:
|
||||
# generate:
|
||||
# fv.owner = threadParam
|
||||
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
|
||||
"owner", fv.info, g.cache), threadParam.newSymNode)
|
||||
|
||||
body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.info,
|
||||
threadParam.newSymNode)
|
||||
if spawnKind == srByVar:
|
||||
body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
|
||||
elif fv != nil:
|
||||
let fk = fv.typ.sons[1].flowVarKind
|
||||
if fk == fvInvalid:
|
||||
localError(g.config, f.info, "cannot create a flowVar of type: " &
|
||||
typeToString(fv.typ.sons[1]))
|
||||
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
|
||||
if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
|
||||
if fk == fvGC:
|
||||
let incRefCall = newNodeI(nkCall, fv.info, 2)
|
||||
incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
|
||||
incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
|
||||
"data", fv.info, g.cache)
|
||||
body.add incRefCall
|
||||
if barrier == nil:
|
||||
# by now 'fv' is shared and thus might have beeen overwritten! we need
|
||||
# to use the thread-local view instead:
|
||||
body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
|
||||
threadLocalProm.newSymNode)
|
||||
else:
|
||||
body.add call
|
||||
if barrier != nil:
|
||||
body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.info,
|
||||
threadLocalBarrier.newSymNode)
|
||||
|
||||
var params = newNodeI(nkFormalParams, f.info)
|
||||
params.add newNodeI(nkEmpty, f.info)
|
||||
params.add threadParam.newSymNode
|
||||
params.add argsParam.newSymNode
|
||||
|
||||
var t = newType(tyProc, threadParam.owner)
|
||||
t.rawAddSon nil
|
||||
t.rawAddSon threadParam.typ
|
||||
t.rawAddSon argsParam.typ
|
||||
t.n = newNodeI(nkFormalParams, f.info)
|
||||
t.n.add newNodeI(nkEffectList, f.info)
|
||||
t.n.add threadParam.newSymNode
|
||||
t.n.add argsParam.newSymNode
|
||||
|
||||
let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
|
||||
result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info,
|
||||
argsParam.options)
|
||||
let emptyNode = newNodeI(nkEmpty, f.info)
|
||||
result.ast = newProcNode(nkProcDef, f.info, body = body,
|
||||
params = params, name = newSymNode(result), pattern = emptyNode,
|
||||
genericParams = emptyNode, pragmas = emptyNode,
|
||||
exceptions = emptyNode)
|
||||
result.typ = t
|
||||
|
||||
proc createCastExpr(argsParam: PSym; objType: PType): PNode =
|
||||
result = newNodeI(nkCast, argsParam.info)
|
||||
result.add newNodeI(nkEmpty, argsParam.info)
|
||||
result.add newSymNode(argsParam)
|
||||
result.typ = newType(tyPtr, objType.owner)
|
||||
result.typ.rawAddSon(objType)
|
||||
|
||||
proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,
|
||||
castExpr, call,
|
||||
varSection, varInit, result: PNode) =
|
||||
let formals = n[0].typ.n
|
||||
let tmpName = getIdent(g.cache, genPrefix)
|
||||
for i in 1 ..< n.len:
|
||||
# we pick n's type here, which hopefully is 'tyArray' and not
|
||||
# 'tyOpenArray':
|
||||
var argType = n[i].typ.skipTypes(abstractInst)
|
||||
if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
|
||||
localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
|
||||
#elif containsTyRef(argType):
|
||||
# localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
|
||||
|
||||
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
|
||||
var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
|
||||
field.typ = argType
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
|
||||
|
||||
let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
|
||||
indirectAccess(castExpr, field, n.info))
|
||||
call.add(newSymNode(temp))
|
||||
|
||||
proc getRoot*(n: PNode): PSym =
|
||||
## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
|
||||
## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
|
||||
## determined as the owner; ``obj`` in the example.
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
|
||||
result = n.sym
|
||||
of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
|
||||
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = getRoot(n.sons[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = getRoot(n.sons[1])
|
||||
of nkCallKinds:
|
||||
if getMagic(n) == mSlice: result = getRoot(n.sons[1])
|
||||
else: discard
|
||||
|
||||
proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;
|
||||
castExpr, call,
|
||||
varSection, varInit, result: PNode) =
|
||||
let formals = n[0].typ.n
|
||||
let tmpName = getIdent(g.cache, genPrefix)
|
||||
# we need to copy the foreign scratch object fields into local variables
|
||||
# for correctness: These are called 'threadLocal' here.
|
||||
for i in 1 ..< n.len:
|
||||
let n = n[i]
|
||||
let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
|
||||
abstractInst)
|
||||
#if containsTyRef(argType):
|
||||
# localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
|
||||
|
||||
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
|
||||
var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
|
||||
|
||||
if argType.kind in {tyVarargs, tyOpenArray}:
|
||||
# important special case: we always create a zero-copy slice:
|
||||
let slice = newNodeI(nkCall, n.info, 4)
|
||||
slice.typ = n.typ
|
||||
slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))
|
||||
slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
|
||||
var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
|
||||
fieldB.typ = getSysType(g, n.info, tyInt)
|
||||
objType.addField(fieldB, g.cache)
|
||||
|
||||
if getMagic(n) == mSlice:
|
||||
let a = genAddrOf(n[1])
|
||||
field.typ = a.typ
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||
|
||||
var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
|
||||
fieldA.typ = getSysType(g, n.info, tyInt)
|
||||
objType.addField(fieldA, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
|
||||
|
||||
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,
|
||||
indirectAccess(castExpr, fieldA, n.info),
|
||||
useShallowCopy=true)
|
||||
slice.sons[2] = threadLocal.newSymNode
|
||||
else:
|
||||
let a = genAddrOf(n)
|
||||
field.typ = a.typ
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
|
||||
|
||||
slice.sons[2] = newIntLit(g, n.info, 0)
|
||||
# the array itself does not need to go through a thread local variable:
|
||||
slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
|
||||
|
||||
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,
|
||||
indirectAccess(castExpr, fieldB, n.info),
|
||||
useShallowCopy=true)
|
||||
slice.sons[3] = threadLocal.newSymNode
|
||||
call.add slice
|
||||
elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
|
||||
n.getRoot != nil:
|
||||
# it is more efficient to pass a pointer instead:
|
||||
let a = genAddrOf(n)
|
||||
field.typ = a.typ
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
|
||||
indirectAccess(castExpr, field, n.info),
|
||||
useShallowCopy=true)
|
||||
call.add(genDeref(threadLocal.newSymNode))
|
||||
else:
|
||||
# boring case
|
||||
field.typ = argType
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
|
||||
let threadLocal = addLocalVar(g, varSection, varInit,
|
||||
objType.owner, field.typ,
|
||||
indirectAccess(castExpr, field, n.info),
|
||||
useShallowCopy=true)
|
||||
call.add(threadLocal.newSymNode)
|
||||
|
||||
proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
|
||||
barrier, dest: PNode = nil): PNode =
|
||||
# if 'barrier' != nil, then it is in a 'parallel' section and we
|
||||
# generate quite different code
|
||||
let n = spawnExpr[^2]
|
||||
let spawnKind = spawnResult(retType, barrier!=nil)
|
||||
case spawnKind
|
||||
of srVoid:
|
||||
internalAssert g.config, dest == nil
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
of srFlowVar:
|
||||
internalAssert g.config, dest == nil
|
||||
result = newNodeIT(nkStmtListExpr, n.info, retType)
|
||||
of srByVar:
|
||||
if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
if n.kind notin nkCallKinds:
|
||||
localError(g.config, n.info, "'spawn' takes a call expression")
|
||||
return
|
||||
if optThreadAnalysis in g.config.globalOptions:
|
||||
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
|
||||
localError(g.config, n.info, "'spawn' takes a GC safe call expression")
|
||||
var
|
||||
threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options)
|
||||
argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options)
|
||||
block:
|
||||
let ptrType = getSysType(g, n.info, tyPointer)
|
||||
threadParam.typ = ptrType
|
||||
argsParam.typ = ptrType
|
||||
argsParam.position = 1
|
||||
|
||||
var objType = createObj(g, owner, n.info)
|
||||
incl(objType.flags, tfFinal)
|
||||
let castExpr = createCastExpr(argsParam, objType)
|
||||
|
||||
var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)
|
||||
block:
|
||||
scratchObj.typ = objType
|
||||
incl(scratchObj.flags, sfFromGeneric)
|
||||
var varSectionB = newNodeI(nkVarSection, n.info)
|
||||
varSectionB.addVar(scratchObj.newSymNode)
|
||||
result.add varSectionB
|
||||
|
||||
var call = newNodeIT(nkCall, n.info, n.typ)
|
||||
var fn = n.sons[0]
|
||||
# templates and macros are in fact valid here due to the nature of
|
||||
# the transformation:
|
||||
if fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure):
|
||||
localError(g.config, n.info, "closure in spawn environment is not allowed")
|
||||
if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
|
||||
skFunc, skMethod, skConverter}):
|
||||
# for indirect calls we pass the function pointer in the scratchObj
|
||||
var argType = n[0].typ.skipTypes(abstractInst)
|
||||
var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)
|
||||
field.typ = argType
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
|
||||
fn = indirectAccess(castExpr, field, n.info)
|
||||
elif fn.kind == nkSym and fn.sym.kind == skIterator:
|
||||
localError(g.config, n.info, "iterator in spawn environment is not allowed")
|
||||
elif fn.typ.callConv == ccClosure:
|
||||
localError(g.config, n.info, "closure in spawn environment is not allowed")
|
||||
|
||||
call.add(fn)
|
||||
var varSection = newNodeI(nkVarSection, n.info)
|
||||
var varInit = newNodeI(nkStmtList, n.info)
|
||||
if barrier.isNil:
|
||||
setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,
|
||||
varSection, varInit, result)
|
||||
else:
|
||||
setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,
|
||||
varSection, varInit, result)
|
||||
|
||||
var barrierAsExpr: PNode = nil
|
||||
if barrier != nil:
|
||||
let typ = newType(tyPtr, owner)
|
||||
typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
|
||||
var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)
|
||||
field.typ = typ
|
||||
objType.addField(field, g.cache)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
|
||||
barrierAsExpr = indirectAccess(castExpr, field, n.info)
|
||||
|
||||
var fvField, fvAsExpr: PNode = nil
|
||||
if spawnKind == srFlowVar:
|
||||
var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
|
||||
field.typ = retType
|
||||
objType.addField(field, g.cache)
|
||||
fvField = newDotExpr(scratchObj, field)
|
||||
fvAsExpr = indirectAccess(castExpr, field, n.info)
|
||||
# create flowVar:
|
||||
result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
|
||||
if barrier == nil:
|
||||
result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info,
|
||||
fvField)
|
||||
|
||||
elif spawnKind == srByVar:
|
||||
var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
|
||||
field.typ = newType(tyPtr, objType.owner)
|
||||
field.typ.rawAddSon(retType)
|
||||
objType.addField(field, g.cache)
|
||||
fvAsExpr = indirectAccess(castExpr, field, n.info)
|
||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
|
||||
|
||||
let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
|
||||
varSection, varInit, call,
|
||||
barrierAsExpr, fvAsExpr, spawnKind)
|
||||
result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.info,
|
||||
wrapper.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
|
||||
|
||||
if spawnKind == srFlowVar: result.add fvField
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
# included from sigmatch.nim
|
||||
|
||||
import algorithm, prefixmatches, lineinfos, pathutils, parseutils
|
||||
import algorithm, prefixmatches, lineinfos, pathutils, parseutils, linter
|
||||
from wordrecg import wDeprecated, wError, wAddr, wYield, specialWords
|
||||
|
||||
when defined(nimsuggest):
|
||||
@@ -362,7 +362,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
|
||||
# error: no known module name:
|
||||
typ = nil
|
||||
else:
|
||||
let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullpath))
|
||||
let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullPath))
|
||||
if m == nil: typ = nil
|
||||
else:
|
||||
for it in items(n.sym.tab):
|
||||
@@ -539,6 +539,8 @@ proc markUsed(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) =
|
||||
if sfError in s.flags: userError(conf, info, s)
|
||||
when defined(nimsuggest):
|
||||
suggestSym(conf, info, s, usageSym, false)
|
||||
if {optStyleHint, optStyleError} * conf.globalOptions != {}:
|
||||
styleCheckUse(conf, info, s)
|
||||
|
||||
proc safeSemExpr*(c: PContext, n: PNode): PNode =
|
||||
# use only for idetools support!
|
||||
|
||||
@@ -72,7 +72,7 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache
|
||||
i = 0
|
||||
inc linenumber
|
||||
if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
|
||||
when defined(nimpretty2):
|
||||
when defined(nimpretty):
|
||||
# XXX this is a bit hacky, but oh well...
|
||||
quit "can't nimpretty a source code filter"
|
||||
else:
|
||||
|
||||
@@ -555,17 +555,22 @@ proc transformConv(c: PTransf, n: PNode): PTransNode =
|
||||
|
||||
type
|
||||
TPutArgInto = enum
|
||||
paDirectMapping, paFastAsgn, paVarAsgn, paComplexOpenarray
|
||||
paDirectMapping, paFastAsgn, paFastAsgnTakeTypeFromArg
|
||||
paVarAsgn, paComplexOpenarray
|
||||
|
||||
proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
|
||||
# This analyses how to treat the mapping "formal <-> arg" in an
|
||||
# inline context.
|
||||
if formal.kind == tyTypeDesc: return paDirectMapping
|
||||
if skipTypes(formal, abstractInst).kind in {tyOpenArray, tyVarargs}:
|
||||
if arg.kind == nkStmtListExpr:
|
||||
case arg.kind
|
||||
of nkStmtListExpr:
|
||||
return paComplexOpenarray
|
||||
return paDirectMapping # XXX really correct?
|
||||
# what if ``arg`` has side-effects?
|
||||
of nkBracket:
|
||||
return paFastAsgnTakeTypeFromArg
|
||||
else:
|
||||
return paDirectMapping # XXX really correct?
|
||||
# what if ``arg`` has side-effects?
|
||||
case arg.kind
|
||||
of nkEmpty..nkNilLit:
|
||||
result = paDirectMapping
|
||||
@@ -645,12 +650,15 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
|
||||
# can happen for 'nim check':
|
||||
if i >= ff.n.len: return result
|
||||
var formal = ff.n.sons[i].sym
|
||||
case putArgInto(arg, formal.typ)
|
||||
let pa = putArgInto(arg, formal.typ)
|
||||
case pa
|
||||
of paDirectMapping:
|
||||
idNodeTablePut(newC.mapping, formal, arg)
|
||||
of paFastAsgn:
|
||||
of paFastAsgn, paFastAsgnTakeTypeFromArg:
|
||||
var t = formal.typ
|
||||
if formal.ast != nil and formal.ast.typ.destructor != nil and t.destructor == nil:
|
||||
if pa == paFastAsgnTakeTypeFromArg:
|
||||
t = arg.typ
|
||||
elif formal.ast != nil and formal.ast.typ.destructor != nil and t.destructor == nil:
|
||||
t = formal.ast.typ # better use the type that actually has a destructor.
|
||||
elif t.destructor == nil and arg.typ.destructor != nil:
|
||||
t = arg.typ
|
||||
@@ -795,7 +803,6 @@ proc transformCall(c: PTransf, n: PNode): PTransNode =
|
||||
result = s.PTransNode
|
||||
|
||||
proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
|
||||
result = transformSons(c, n)
|
||||
if n[0].isInfixAs() and not isImportedException(n[0][1].typ, c.graph.config):
|
||||
let excTypeNode = n[0][1]
|
||||
let actions = newTransNode(nkStmtListExpr, n[1], 2)
|
||||
@@ -817,12 +824,14 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode =
|
||||
letSection[0] = identDefs
|
||||
# Place the let statement and body of the 'except' branch into new stmtList.
|
||||
actions[0] = letSection
|
||||
actions[1] = transformSons(c, n[1])
|
||||
actions[1] = transform(c, n[1])
|
||||
# Overwrite 'except' branch body with our stmtList.
|
||||
result[1] = actions
|
||||
|
||||
result = newTransNode(nkExceptBranch, n[1].info, 2)
|
||||
# Replace the `Exception as foobar` with just `Exception`.
|
||||
result[0] = result[0][1]
|
||||
result[0] = transform(c, n[0][1])
|
||||
result[1] = actions
|
||||
else:
|
||||
result = transformSons(c, n)
|
||||
|
||||
proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} =
|
||||
# symbols that expand to a complex constant (array, etc.) should not be
|
||||
|
||||
@@ -77,12 +77,12 @@ proc isPureObject*(typ: PType): bool =
|
||||
t = t.sons[0].skipTypes(skipPtrs)
|
||||
result = t.sym != nil and sfPure in t.sym.flags
|
||||
|
||||
proc getOrdValue*(n: PNode): BiggestInt =
|
||||
proc getOrdValue*(n: PNode; onError = high(BiggestInt)): BiggestInt =
|
||||
case n.kind
|
||||
of nkCharLit..nkUInt64Lit: n.intVal
|
||||
of nkNilLit: 0
|
||||
of nkHiddenStdConv: getOrdValue(n.sons[1])
|
||||
else: high(BiggestInt)
|
||||
of nkHiddenStdConv: getOrdValue(n.sons[1], onError)
|
||||
else: onError
|
||||
|
||||
proc getFloatValue*(n: PNode): BiggestFloat =
|
||||
case n.kind
|
||||
@@ -168,7 +168,7 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
|
||||
for i in 0 ..< sonsLen(t):
|
||||
result = iterOverTypeAux(marker, t.sons[i], iter, closure)
|
||||
if result: return
|
||||
if t.n != nil: result = iterOverNode(marker, t.n, iter, closure)
|
||||
if t.n != nil and t.kind != tyProc: result = iterOverNode(marker, t.n, iter, closure)
|
||||
|
||||
proc iterOverType(t: PType, iter: TTypeIter, closure: RootRef): bool =
|
||||
var marker = initIntSet()
|
||||
@@ -760,7 +760,9 @@ proc floatRangeCheck*(x: BiggestFloat, t: PType): bool =
|
||||
|
||||
proc lengthOrd*(conf: ConfigRef; t: PType): BiggestInt =
|
||||
case t.skipTypes(tyUserTypeClasses).kind
|
||||
of tyInt64, tyInt32, tyInt: result = lastOrd(conf, t)
|
||||
of tyInt64, tyInt32, tyInt:
|
||||
# XXX: this is just wrong
|
||||
result = lastOrd(conf, t)
|
||||
of tyDistinct: result = lengthOrd(conf, t.sons[0])
|
||||
else:
|
||||
let last = lastOrd(conf, t)
|
||||
@@ -1204,30 +1206,35 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
# if we have already checked the type, return true, because we stop the
|
||||
# evaluation if something is wrong:
|
||||
result = nil
|
||||
if typ == nil: return
|
||||
if containsOrIncl(marker, typ.id): return
|
||||
if typ == nil: return nil
|
||||
if containsOrIncl(marker, typ.id): return nil
|
||||
var t = skipTypes(typ, abstractInst-{tyTypeDesc})
|
||||
case t.kind
|
||||
of tyVar, tyLent:
|
||||
if kind in {skProc, skFunc, skConst}: return t
|
||||
elif t.kind == tyLent and kind != skResult: return t
|
||||
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
|
||||
case t2.kind
|
||||
of tyVar, tyLent:
|
||||
if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
|
||||
of tyOpenArray, tyUncheckedArray:
|
||||
if kind != skParam: result = t
|
||||
else: result = typeAllowedAux(marker, t2.sons[0], skParam, flags)
|
||||
if kind in {skProc, skFunc, skConst}:
|
||||
result = t
|
||||
elif t.kind == tyLent and kind != skResult:
|
||||
result = t
|
||||
else:
|
||||
if kind notin {skParam, skResult}: result = t
|
||||
else: result = typeAllowedAux(marker, t2, kind, flags)
|
||||
var t2 = skipTypes(t.sons[0], abstractInst-{tyTypeDesc})
|
||||
case t2.kind
|
||||
of tyVar, tyLent:
|
||||
if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
|
||||
of tyOpenArray, tyUncheckedArray:
|
||||
if kind != skParam: result = t
|
||||
else: result = typeAllowedAux(marker, t2.sons[0], skParam, flags)
|
||||
else:
|
||||
if kind notin {skParam, skResult}: result = t
|
||||
else: result = typeAllowedAux(marker, t2, kind, flags)
|
||||
of tyProc:
|
||||
if kind == skConst and t.callConv == ccClosure: return t
|
||||
for i in 1 ..< sonsLen(t):
|
||||
result = typeAllowedAux(marker, t.sons[i], skParam, flags)
|
||||
if result != nil: break
|
||||
if result.isNil and t.sons[0] != nil:
|
||||
result = typeAllowedAux(marker, t.sons[0], skResult, flags)
|
||||
if kind == skConst and t.callConv == ccClosure:
|
||||
result = t
|
||||
else:
|
||||
for i in 1 ..< sonsLen(t):
|
||||
result = typeAllowedAux(marker, t.sons[i], skParam, flags)
|
||||
if result != nil: break
|
||||
if result.isNil and t.sons[0] != nil:
|
||||
result = typeAllowedAux(marker, t.sons[0], skResult, flags)
|
||||
of tyTypeDesc:
|
||||
# XXX: This is still a horrible idea...
|
||||
result = nil
|
||||
@@ -1287,13 +1294,15 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
|
||||
if result != nil: break
|
||||
of tyObject, tyTuple:
|
||||
if kind in {skProc, skFunc, skConst} and
|
||||
t.kind == tyObject and t.sons[0] != nil: return t
|
||||
let flags = flags+{taField}
|
||||
for i in 0 ..< sonsLen(t):
|
||||
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
||||
if result != nil: break
|
||||
if result.isNil and t.n != nil:
|
||||
result = typeAllowedNode(marker, t.n, kind, flags)
|
||||
t.kind == tyObject and t.sons[0] != nil:
|
||||
result = t
|
||||
else:
|
||||
let flags = flags+{taField}
|
||||
for i in 0 ..< sonsLen(t):
|
||||
result = typeAllowedAux(marker, t.sons[i], kind, flags)
|
||||
if result != nil: break
|
||||
if result.isNil and t.n != nil:
|
||||
result = typeAllowedNode(marker, t.n, kind, flags)
|
||||
of tyEmpty:
|
||||
if kind in {skVar, skLet}: result = t
|
||||
of tyProxy:
|
||||
|
||||
@@ -1082,12 +1082,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs),
|
||||
currentException: c.currentExceptionA,
|
||||
currentLineInfo: c.debug[pc]))
|
||||
elif sfImportc in prc.flags:
|
||||
elif importcCond(prc):
|
||||
if compiletimeFFI notin c.config.features:
|
||||
globalError(c.config, c.debug[pc], "VM not allowed to do FFI, see `compiletimeFFI`")
|
||||
# we pass 'tos.slots' instead of 'regs' so that the compiler can keep
|
||||
# 'regs' in a register:
|
||||
when hasFFI:
|
||||
if prc.position - 1 < 0:
|
||||
globalError(c.config, c.debug[pc],
|
||||
"VM call invalid: prc.position: " & $prc.position)
|
||||
let prcValue = c.globals.sons[prc.position-1]
|
||||
if prcValue.kind == nkEmpty:
|
||||
globalError(c.config, c.debug[pc], "cannot run " & prc.name.s)
|
||||
@@ -1905,16 +1908,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||
when not defined(nimNoNilSeqs):
|
||||
if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000)
|
||||
storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config)
|
||||
of opcToNarrowInt:
|
||||
decodeBC(rkInt)
|
||||
let mask = (1'i64 shl rc) - 1 # 0xFF
|
||||
let signbit = 1'i64 shl (rc - 1) # 0x80
|
||||
let toggle = mask - signbit # 0x7F
|
||||
# algorithm: -((i8 and 0xFF) xor 0x7F) + 0x7F
|
||||
# mask off higher bits.
|
||||
# uses two's complement to sign-extend integer.
|
||||
# reajust integer into desired range.
|
||||
regs[ra].intVal = -((regs[rb].intVal and mask) xor toggle) + toggle
|
||||
|
||||
inc pc
|
||||
|
||||
@@ -2068,13 +2061,21 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
|
||||
for i in 0 ..< gp.len:
|
||||
let genericParam = gp[i].sym
|
||||
let posInCall = macroSym.typ.len + i
|
||||
yield (genericParam, call[posInCall])
|
||||
if posInCall < call.len:
|
||||
yield (genericParam, call[posInCall])
|
||||
|
||||
# to prevent endless recursion in macro instantiation
|
||||
const evalMacroLimit = 1000
|
||||
|
||||
proc errorNode(owner: PSym, n: PNode): PNode =
|
||||
result = newNodeI(nkEmpty, n.info)
|
||||
result.typ = newType(tyError, owner)
|
||||
result.typ.flags.incl tfCheckedForDestructor
|
||||
|
||||
proc evalMacroCall*(module: PSym; g: ModuleGraph;
|
||||
n, nOrig: PNode, sym: PSym): PNode =
|
||||
if g.config.errorCounter > 0: return errorNode(module, n)
|
||||
|
||||
# XXX globalError() is ugly here, but I don't know a better solution for now
|
||||
inc(g.config.evalMacroCounter)
|
||||
if g.config.evalMacroCounter > evalMacroLimit:
|
||||
|
||||
@@ -149,7 +149,6 @@ type
|
||||
opcSetType, # dest.typ = types[Bx]
|
||||
opcTypeTrait,
|
||||
opcMarshalLoad, opcMarshalStore,
|
||||
opcToNarrowInt,
|
||||
opcSymOwner,
|
||||
opcSymIsInstantiationOf
|
||||
|
||||
|
||||
@@ -253,7 +253,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
result.add t.n.sons[1].copyTree
|
||||
of tyPointer: result = atomicType("pointer", mPointer)
|
||||
of tyString: result = atomicType("string", mString)
|
||||
of tyCString: result = atomicType("cstring", mCString)
|
||||
of tyCString: result = atomicType("cstring", mCstring)
|
||||
of tyInt: result = atomicType("int", mInt)
|
||||
of tyInt8: result = atomicType("int8", mInt8)
|
||||
of tyInt16: result = atomicType("int16", mInt16)
|
||||
@@ -263,11 +263,11 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
|
||||
of tyFloat32: result = atomicType("float32", mFloat32)
|
||||
of tyFloat64: result = atomicType("float64", mFloat64)
|
||||
of tyFloat128: result = atomicType("float128", mFloat128)
|
||||
of tyUInt: result = atomicType("uint", mUint)
|
||||
of tyUInt8: result = atomicType("uint8", mUint8)
|
||||
of tyUInt16: result = atomicType("uint16", mUint16)
|
||||
of tyUInt32: result = atomicType("uint32", mUint32)
|
||||
of tyUInt64: result = atomicType("uint64", mUint64)
|
||||
of tyUInt: result = atomicType("uint", mUInt)
|
||||
of tyUInt8: result = atomicType("uint8", mUInt8)
|
||||
of tyUInt16: result = atomicType("uint16", mUInt16)
|
||||
of tyUInt32: result = atomicType("uint32", mUInt32)
|
||||
of tyUInt64: result = atomicType("uint64", mUInt64)
|
||||
of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info)
|
||||
of tyProxy: result = atomicType("error", mNone)
|
||||
of tyBuiltInTypeClass:
|
||||
|
||||
@@ -304,31 +304,31 @@ proc isTrue(n: PNode): bool =
|
||||
n.kind == nkIntLit and n.intVal != 0
|
||||
|
||||
proc genWhile(c: PCtx; n: PNode) =
|
||||
# L1:
|
||||
# lab1:
|
||||
# cond, tmp
|
||||
# fjmp tmp, L2
|
||||
# fjmp tmp, lab2
|
||||
# body
|
||||
# jmp L1
|
||||
# L2:
|
||||
let L1 = c.genLabel
|
||||
# jmp lab1
|
||||
# lab2:
|
||||
let lab1 = c.genLabel
|
||||
withBlock(nil):
|
||||
if isTrue(n.sons[0]):
|
||||
c.gen(n.sons[1])
|
||||
c.jmpBack(n, L1)
|
||||
c.jmpBack(n, lab1)
|
||||
elif isNotOpr(n.sons[0]):
|
||||
var tmp = c.genx(n.sons[0].sons[1])
|
||||
let L2 = c.xjmp(n, opcTJmp, tmp)
|
||||
let lab2 = c.xjmp(n, opcTJmp, tmp)
|
||||
c.freeTemp(tmp)
|
||||
c.gen(n.sons[1])
|
||||
c.jmpBack(n, L1)
|
||||
c.patch(L2)
|
||||
c.jmpBack(n, lab1)
|
||||
c.patch(lab2)
|
||||
else:
|
||||
var tmp = c.genx(n.sons[0])
|
||||
let L2 = c.xjmp(n, opcFJmp, tmp)
|
||||
let lab2 = c.xjmp(n, opcFJmp, tmp)
|
||||
c.freeTemp(tmp)
|
||||
c.gen(n.sons[1])
|
||||
c.jmpBack(n, L1)
|
||||
c.patch(L2)
|
||||
c.jmpBack(n, lab1)
|
||||
c.patch(lab2)
|
||||
|
||||
proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
||||
let oldRegisterCount = c.prc.maxSlots
|
||||
@@ -342,26 +342,26 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
|
||||
c.clearDest(n, dest)
|
||||
|
||||
proc genBreak(c: PCtx; n: PNode) =
|
||||
let L1 = c.xjmp(n, opcJmp)
|
||||
let lab1 = c.xjmp(n, opcJmp)
|
||||
if n.sons[0].kind == nkSym:
|
||||
#echo cast[int](n.sons[0].sym)
|
||||
for i in countdown(c.prc.blocks.len-1, 0):
|
||||
if c.prc.blocks[i].label == n.sons[0].sym:
|
||||
c.prc.blocks[i].fixups.add L1
|
||||
c.prc.blocks[i].fixups.add lab1
|
||||
return
|
||||
globalError(c.config, n.info, "VM problem: cannot find 'break' target")
|
||||
else:
|
||||
c.prc.blocks[c.prc.blocks.high].fixups.add L1
|
||||
c.prc.blocks[c.prc.blocks.high].fixups.add lab1
|
||||
|
||||
proc genIf(c: PCtx, n: PNode; dest: var TDest) =
|
||||
# if (!expr1) goto L1;
|
||||
# if (!expr1) goto lab1;
|
||||
# thenPart
|
||||
# goto LEnd
|
||||
# L1:
|
||||
# if (!expr2) goto L2;
|
||||
# lab1:
|
||||
# if (!expr2) goto lab2;
|
||||
# thenPart2
|
||||
# goto LEnd
|
||||
# L2:
|
||||
# lab2:
|
||||
# elsePart
|
||||
# Lend:
|
||||
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
|
||||
@@ -393,18 +393,18 @@ proc isTemp(c: PCtx; dest: TDest): bool =
|
||||
|
||||
proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) =
|
||||
# asgn dest, a
|
||||
# tjmp|fjmp L1
|
||||
# tjmp|fjmp lab1
|
||||
# asgn dest, b
|
||||
# L1:
|
||||
# lab1:
|
||||
let copyBack = dest < 0 or not isTemp(c, dest)
|
||||
let tmp = if copyBack:
|
||||
getTemp(c, n.typ)
|
||||
else:
|
||||
TRegister dest
|
||||
c.gen(n.sons[1], tmp)
|
||||
let L1 = c.xjmp(n, opc, tmp)
|
||||
let lab1 = c.xjmp(n, opc, tmp)
|
||||
c.gen(n.sons[2], tmp)
|
||||
c.patch(L1)
|
||||
c.patch(lab1)
|
||||
if dest < 0:
|
||||
dest = tmp
|
||||
elif copyBack:
|
||||
@@ -452,14 +452,14 @@ proc unused(c: PCtx; n: PNode; x: TDest) {.inline.} =
|
||||
globalError(c.config, n.info, "not unused")
|
||||
|
||||
proc genCase(c: PCtx; n: PNode; dest: var TDest) =
|
||||
# if (!expr1) goto L1;
|
||||
# if (!expr1) goto lab1;
|
||||
# thenPart
|
||||
# goto LEnd
|
||||
# L1:
|
||||
# if (!expr2) goto L2;
|
||||
# lab1:
|
||||
# if (!expr2) goto lab2;
|
||||
# thenPart2
|
||||
# goto LEnd
|
||||
# L2:
|
||||
# lab2:
|
||||
# elsePart
|
||||
# Lend:
|
||||
if not isEmptyType(n.typ):
|
||||
@@ -501,7 +501,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
|
||||
c.gen(n.sons[0], dest)
|
||||
c.clearDest(n, dest)
|
||||
# Add a jump past the exception handling code
|
||||
endings.add(c.xjmp(n, opcJmp, 0))
|
||||
let jumpToFinally = 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:
|
||||
@@ -525,6 +525,7 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
|
||||
let fin = lastSon(n)
|
||||
# we always generate an 'opcFinally' as that pops the safepoint
|
||||
# from the stack if no exception is raised in the body.
|
||||
c.patch(jumpToFinally)
|
||||
c.gABx(fin, opcFinally, 0, 0)
|
||||
for endPos in endings: c.patch(endPos)
|
||||
if fin.kind == nkFinally:
|
||||
@@ -558,6 +559,8 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
|
||||
#if n.typ != nil and n.typ.sym != nil and n.typ.sym.magic == mPNimrodNode:
|
||||
# genLit(c, n, dest)
|
||||
# return
|
||||
# bug #10901: do not produce code for wrong call expressions:
|
||||
if n.len == 0 or n[0].typ.isNil: return
|
||||
if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
|
||||
let x = c.getTempRange(n.len, slotTempUnknown)
|
||||
# varargs need 'opcSetType' for the FFI support:
|
||||
@@ -813,29 +816,45 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
|
||||
|
||||
proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
|
||||
const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar}
|
||||
var signedIntegers = {tyInt..tyInt64}
|
||||
var unsignedIntegers = {tyUInt..tyUInt64, tyChar}
|
||||
var signedIntegers = {tyInt8..tyInt32}
|
||||
var unsignedIntegers = {tyUInt8..tyUInt32, tyChar}
|
||||
let src = n.sons[1].typ.skipTypes(abstractRange)#.kind
|
||||
let dst = n.sons[0].typ.skipTypes(abstractRange)#.kind
|
||||
let src_size = getSize(c.config, src)
|
||||
let dst_size = getSize(c.config, dst)
|
||||
if src.kind in allowedIntegers and dst.kind in allowedIntegers:
|
||||
let srcSize = getSize(c.config, src)
|
||||
let dstSize = getSize(c.config, dst)
|
||||
if c.config.target.intSize < 8:
|
||||
signedIntegers.incl(tyInt)
|
||||
unsignedIntegers.incl(tyUInt)
|
||||
if srcSize == dstSize and src.kind in allowedIntegers and
|
||||
dst.kind in allowedIntegers:
|
||||
let tmp = c.genx(n.sons[1])
|
||||
var tmp2 = c.getTemp(n.sons[1].typ)
|
||||
let tmp3 = c.getTemp(n.sons[1].typ)
|
||||
if dest < 0: dest = c.getTemp(n[0].typ)
|
||||
c.gABC(n, opcAsgnInt, dest, tmp)
|
||||
if dst_size != sizeof(BiggestInt): # don't do anything on biggest int types
|
||||
if dst.kind in signedIntegers: # we need to do sign extensions
|
||||
if dst_size <= src_size:
|
||||
# Sign extension can be omitted when the size increases.
|
||||
c.gABC(n, opcSignExtend, dest, TRegister(dst_size*8))
|
||||
elif dst.kind in unsignedIntegers:
|
||||
if src.kind in signedIntegers or dst_size < src_size:
|
||||
# Cast from signed to unsigned always needs narrowing. Cast
|
||||
# from unsigned to unsigned only needs narrowing when target
|
||||
# is smaller than source.
|
||||
c.gABC(n, opcNarrowU, dest, TRegister(dst_size*8))
|
||||
proc mkIntLit(ival: int): int =
|
||||
result = genLiteral(c, newIntTypeNode(nkIntLit, ival, getSysType(c.graph, n.info, tyInt)))
|
||||
if src.kind in unsignedIntegers and dst.kind in signedIntegers:
|
||||
# cast unsigned to signed integer of same size
|
||||
# signedVal = (unsignedVal xor offset) -% offset
|
||||
let offset = 1 shl (srcSize * 8 - 1)
|
||||
c.gABx(n, opcLdConst, tmp2, mkIntLit(offset))
|
||||
c.gABC(n, opcBitxorInt, tmp3, tmp, tmp2)
|
||||
c.gABC(n, opcSubInt, dest, tmp3, tmp2)
|
||||
elif src.kind in signedIntegers and dst.kind in unsignedIntegers:
|
||||
# cast signed to unsigned integer of same size
|
||||
# unsignedVal = (offset +% signedVal +% 1) and offset
|
||||
let offset = (1 shl (srcSize * 8)) - 1
|
||||
c.gABx(n, opcLdConst, tmp2, mkIntLit(offset))
|
||||
c.gABx(n, opcLdConst, dest, mkIntLit(offset+1))
|
||||
c.gABC(n, opcAddu, tmp3, tmp, dest)
|
||||
c.gABC(n, opcNarrowU, tmp3, TRegister(srcSize*8))
|
||||
c.gABC(n, opcBitandInt, dest, tmp3, tmp2)
|
||||
else:
|
||||
c.gABC(n, opcAsgnInt, dest, tmp)
|
||||
c.freeTemp(tmp)
|
||||
elif src_size == dst_size and src.kind in allowedIntegers and
|
||||
c.freeTemp(tmp2)
|
||||
c.freeTemp(tmp3)
|
||||
elif srcSize == dstSize and src.kind in allowedIntegers and
|
||||
dst.kind in {tyFloat, tyFloat32, tyFloat64}:
|
||||
let tmp = c.genx(n[1])
|
||||
if dest < 0: dest = c.getTemp(n[0].typ)
|
||||
@@ -845,7 +864,7 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
|
||||
c.gABC(n, opcAsgnFloat64FromInt, dest, tmp)
|
||||
c.freeTemp(tmp)
|
||||
|
||||
elif src_size == dst_size and src.kind in {tyFloat, tyFloat32, tyFloat64} and
|
||||
elif srcSize == dstSize and src.kind in {tyFloat, tyFloat32, tyFloat64} and
|
||||
dst.kind in allowedIntegers:
|
||||
let tmp = c.genx(n[1])
|
||||
if dest < 0: dest = c.getTemp(n[0].typ)
|
||||
@@ -1061,21 +1080,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr,
|
||||
mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
|
||||
genConv(c, n, n.sons[1], dest)
|
||||
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
|
||||
#genNarrowU modified
|
||||
let t = skipTypes(n.sons[1].typ, abstractVar-{tyTypeDesc})
|
||||
let tmp = c.genx(n.sons[1])
|
||||
c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8))
|
||||
# assign result to dest register
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcAsgnInt, dest, tmp)
|
||||
c.freeTemp(tmp)
|
||||
of mToU8, mToU16, mToU32:
|
||||
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
|
||||
var tmp = c.genx(n.sons[1])
|
||||
if dest < 0: dest = c.getTemp(n.typ)
|
||||
c.gABC(n, opcToNarrowInt, dest, tmp, TRegister(t.size*8))
|
||||
c.freeTemp(tmp)
|
||||
of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr)
|
||||
of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
|
||||
of mLtStr: genBinaryABC(c, n, dest, opcLtStr)
|
||||
@@ -1324,8 +1328,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||
# produces a value
|
||||
else:
|
||||
globalError(c.config, n.info, "expandToAst requires a call expression")
|
||||
of mSizeOf, mAlignOf:
|
||||
globalError(c.config, n.info, "cannot evaluate 'sizeof/alignof' because its type is not defined completely")
|
||||
of mSizeOf:
|
||||
globalError(c.config, n.info, "cannot evaluate 'sizeof' because its type is not defined completely")
|
||||
of mAlignOf:
|
||||
globalError(c.config, n.info, "cannot evaluate 'alignof' because its type is not defined completely")
|
||||
of mOffsetOf:
|
||||
globalError(c.config, n.info, "cannot evaluate 'offsetof' because its type is not defined completely")
|
||||
of mRunnableExamples:
|
||||
discard "just ignore any call to runnableExamples"
|
||||
of mDestroy: discard "ignore calls to the default destructor"
|
||||
@@ -1559,8 +1567,11 @@ proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
|
||||
n.typ = t
|
||||
genLit(c, n, dest)
|
||||
|
||||
proc importcCond(s: PSym): bool {.inline.} =
|
||||
sfImportc in s.flags and (lfDynamicLib notin s.loc.flags or s.ast == nil)
|
||||
proc importcCond*(s: PSym): bool {.inline.} =
|
||||
## return true to importc `s`, false to execute its body instead (refs #8405)
|
||||
if sfImportc in s.flags:
|
||||
if s.kind in routineKinds:
|
||||
return s.ast.sons[bodyPos].kind == nkEmpty
|
||||
|
||||
proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
|
||||
when hasFFI:
|
||||
@@ -1568,7 +1579,8 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
|
||||
c.globals.add(importcSymbol(c.config, s))
|
||||
s.position = c.globals.len
|
||||
else:
|
||||
localError(c.config, info, "VM is not allowed to 'importc'")
|
||||
localError(c.config, info,
|
||||
"VM is not allowed to 'importc' without --experimental:compiletimeFFI")
|
||||
else:
|
||||
localError(c.config, info,
|
||||
"cannot 'importc' variable at compile time; " & s.name.s)
|
||||
@@ -1686,11 +1698,11 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
|
||||
c.gABC(n, opcContainsSet, rs, setLit, discVal)
|
||||
c.freeTemp(setLit)
|
||||
# If the check fails let the user know
|
||||
let L1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs)
|
||||
let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs)
|
||||
c.freeTemp(rs)
|
||||
# Not ideal but will do for the moment
|
||||
c.gABC(n, opcQuit)
|
||||
c.patch(L1)
|
||||
c.patch(lab1)
|
||||
|
||||
proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
var objR: TDest = -1
|
||||
@@ -1723,19 +1735,24 @@ proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||
else:
|
||||
genArrAccess2(c, n, dest, opcLdArr, flags)
|
||||
|
||||
proc getNullValueAux(obj: PNode, result: PNode; conf: ConfigRef) =
|
||||
proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
|
||||
if t != nil and t.len > 0 and t.sons[0] != nil:
|
||||
let b = skipTypes(t.sons[0], skipPtrs)
|
||||
getNullValueAux(b, b.n, result, conf, currPosition)
|
||||
case obj.kind
|
||||
of nkRecList:
|
||||
for i in 0 ..< sonsLen(obj): getNullValueAux(obj.sons[i], result, conf)
|
||||
for i in 0 ..< sonsLen(obj): getNullValueAux(nil, obj.sons[i], result, conf, currPosition)
|
||||
of nkRecCase:
|
||||
getNullValueAux(obj.sons[0], result, conf)
|
||||
getNullValueAux(nil, obj.sons[0], result, conf, currPosition)
|
||||
for i in 1 ..< sonsLen(obj):
|
||||
getNullValueAux(lastSon(obj.sons[i]), result, conf)
|
||||
getNullValueAux(nil, lastSon(obj.sons[i]), result, conf, currPosition)
|
||||
of nkSym:
|
||||
let field = newNodeI(nkExprColonExpr, result.info)
|
||||
field.add(obj)
|
||||
field.add(getNullValue(obj.sym.typ, result.info, conf))
|
||||
addSon(result, field)
|
||||
doAssert obj.sym.position == currPosition
|
||||
inc currPosition
|
||||
else: globalError(conf, result.info, "cannot create null element for: " & $obj)
|
||||
|
||||
proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
|
||||
@@ -1763,13 +1780,9 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
|
||||
of tyObject:
|
||||
result = newNodeIT(nkObjConstr, info, t)
|
||||
result.add(newNodeIT(nkEmpty, info, t))
|
||||
# initialize inherited fields:
|
||||
var base = t.sons[0]
|
||||
while base != nil:
|
||||
let b = skipTypes(base, skipPtrs)
|
||||
getNullValueAux(b.n, result, conf)
|
||||
base = b.sons[0]
|
||||
getNullValueAux(t.n, result, conf)
|
||||
# initialize inherited fields, and all in the correct order:
|
||||
var currPosition = 0
|
||||
getNullValueAux(t, t.n, result, conf, currPosition)
|
||||
of tyArray:
|
||||
result = newNodeIT(nkBracket, info, t)
|
||||
for i in 0 ..< int(lengthOrd(conf, t)):
|
||||
@@ -2241,9 +2254,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 == "fun1":
|
||||
# echo renderTree(body)
|
||||
# c.echoCode(result)
|
||||
#if s.name.s == "main":
|
||||
# echo renderTree(body)
|
||||
# c.echoCode(result)
|
||||
c.prc = oldPrc
|
||||
else:
|
||||
c.prc.maxSlots = s.offset
|
||||
|
||||
@@ -43,21 +43,21 @@ type
|
||||
wImportCpp, wImportObjC,
|
||||
wImportCompilerProc,
|
||||
wImportc, wExportc, wExportNims, wIncompleteStruct, wRequiresInit,
|
||||
wAlign, wNodecl, wPure, wSideeffect, wHeader,
|
||||
wNosideeffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib,
|
||||
wCompilerproc, wCore, wProcVar, wBase, wUsed,
|
||||
wAlign, wNodecl, wPure, wSideEffect, wHeader,
|
||||
wNoSideEffect, wGcSafe, wNoreturn, wMerge, wLib, wDynlib,
|
||||
wCompilerProc, wCore, wProcVar, wBase, wUsed,
|
||||
wFatal, wError, wWarning, wHint, wLine, wPush, wPop, wDefine, wUndef,
|
||||
wLinedir, wStacktrace, wLinetrace, wLink, wCompile,
|
||||
wLineDir, wStackTrace, wLineTrace, wLink, wCompile,
|
||||
wLinksys, wDeprecated, wVarargs, wCallconv, wBreakpoint, wDebugger,
|
||||
wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline,
|
||||
wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangechecks,
|
||||
wBoundchecks, wOverflowchecks, wNilchecks,
|
||||
wFloatchecks, wNanChecks, wInfChecks, wMoveChecks,
|
||||
wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangeChecks,
|
||||
wBoundChecks, wOverflowChecks, wNilChecks,
|
||||
wFloatChecks, wNanChecks, wInfChecks, wStyleChecks,
|
||||
wNonReloadable, wExecuteOnReload,
|
||||
wAssertions, wPatterns, wTrMacros, wWarnings,
|
||||
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
|
||||
wDeadCodeElimUnused, # deprecated, dead code elim always happens
|
||||
wSafecode, wPackage, wNoForward, wReorder, wNoRewrite,
|
||||
wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, wNoDestroy,
|
||||
wPragma,
|
||||
wCompileTime, wNoInit,
|
||||
wPassc, wPassl, wBorrow, wDiscardable,
|
||||
@@ -142,13 +142,13 @@ const
|
||||
"cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
|
||||
"noconv", "on", "off", "checks", "rangechecks", "boundchecks",
|
||||
"overflowchecks", "nilchecks",
|
||||
"floatchecks", "nanchecks", "infchecks", "movechecks",
|
||||
"floatchecks", "nanchecks", "infchecks", "stylechecks",
|
||||
"nonreloadable", "executeonreload",
|
||||
|
||||
"assertions", "patterns", "trmacros", "warnings", "hints",
|
||||
"optimization", "raises", "writes", "reads", "size", "effects", "tags",
|
||||
"deadcodeelim", # deprecated, dead code elim always happens
|
||||
"safecode", "package", "noforward", "reorder", "norewrite",
|
||||
"safecode", "package", "noforward", "reorder", "norewrite", "nodestroy",
|
||||
"pragma",
|
||||
"compiletime", "noinit",
|
||||
"passc", "passl", "borrow", "discardable", "fieldchecks",
|
||||
@@ -184,3 +184,35 @@ proc findStr*(a: openArray[string], s: string): int =
|
||||
if cmpIgnoreStyle(a[i], s) == 0:
|
||||
return i
|
||||
result = - 1
|
||||
|
||||
proc canonPragmaSpelling*(w: TSpecialWord): string =
|
||||
case w
|
||||
of wNoSideEffect: "noSideEffect"
|
||||
of wImportCompilerProc: "importCompilerProc"
|
||||
of wIncompleteStruct: "incompleteStruct"
|
||||
of wRequiresInit: "requiresInit"
|
||||
of wSideEffect: "sideEffect"
|
||||
of wLineDir: "lineDir"
|
||||
of wStackTrace: "stackTrace"
|
||||
of wLineTrace: "lineTrace"
|
||||
of wRangeChecks: "rangeChecks"
|
||||
of wBoundChecks: "boundChecks"
|
||||
of wOverflowChecks: "overflowChecks"
|
||||
of wNilChecks: "nilChecks"
|
||||
of wFloatChecks: "floatChecks"
|
||||
of wNanChecks: "nanChecks"
|
||||
of wInfChecks: "infChecks"
|
||||
of wStyleChecks: "styleChecks"
|
||||
of wNonReloadable: "nonReloadable"
|
||||
of wExecuteOnReload: "executeOnReload"
|
||||
of wDeadCodeElimUnused: "deadCodeElim"
|
||||
of wCompileTime: "compileTime"
|
||||
of wFieldChecks: "fieldChecks"
|
||||
of wLinearScanEnd: "linearScanEnd"
|
||||
of wComputedGoto: "computedGoto"
|
||||
of wInjectStmt: "injectStmt"
|
||||
of wAsmNoStackFrame: "asmNoStackframe"
|
||||
of wImplicitStatic: "implicitStatic"
|
||||
of wCodegenDecl: "codegenDecl"
|
||||
of wLiftLocals: "liftLocals"
|
||||
else: specialWords[w]
|
||||
|
||||
@@ -71,6 +71,7 @@ path="$lib/pure"
|
||||
|
||||
@if release or danger:
|
||||
stacktrace:off
|
||||
excessiveStackTrace:off
|
||||
linetrace:off
|
||||
debugger:off
|
||||
line_dir:off
|
||||
|
||||
@@ -297,6 +297,23 @@ input#searchInput {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some custom formatting for input forms.
|
||||
* This also fixes input form colors on Firefox with a dark system theme on Linux.
|
||||
*/
|
||||
input {
|
||||
-moz-appearance: none;
|
||||
color: #333;
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #aaa;
|
||||
font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
|
||||
font-size: 0.9em;
|
||||
padding: 6px;
|
||||
}
|
||||
input:focus {
|
||||
border: 1px solid #1fa0eb;
|
||||
box-shadow: 0 0 2px #1fa0eb;
|
||||
}
|
||||
|
||||
/* Docgen styles */
|
||||
/* Links */
|
||||
|
||||
@@ -9,7 +9,6 @@ Advanced commands:
|
||||
//jsondoc extract the documentation to a json file
|
||||
//ctags create a tags file
|
||||
//buildIndex build an index for the whole documentation
|
||||
//run run the project (with Tiny C backend; buggy!)
|
||||
//genDepend generate a DOT file containing the
|
||||
module dependency graph
|
||||
//dump dump all defined conditionals and search paths
|
||||
@@ -26,6 +25,7 @@ Runtime checks (see -x):
|
||||
--nanChecks:on|off turn NaN checks on|off
|
||||
--infChecks:on|off turn Inf checks on|off
|
||||
--nilChecks:on|off turn nil checks on|off
|
||||
--refChecks:on|off turn ref checks on|off (only for --newruntime)
|
||||
|
||||
Advanced options:
|
||||
-o:FILE, --out:FILE set the output filename
|
||||
@@ -120,6 +120,8 @@ Advanced options:
|
||||
--errorMax:N stop compilation after N errors; 0 means unlimited
|
||||
--experimental:$1
|
||||
enable experimental language feature
|
||||
--profiler:on|off Enable profiling; requires `import nimprof`, and
|
||||
--newruntime use an alternative runtime that uses destructors
|
||||
and that uses a shared heap via -d:useMalloc
|
||||
--profiler:on|off enable profiling; requires `import nimprof`, and
|
||||
works better with `--stackTrace:on`
|
||||
see also https://nim-lang.github.io/Nim/estp.html
|
||||
|
||||
@@ -26,7 +26,7 @@ There are 3 types of tests:
|
||||
2. tests in ``when isMainModule:`` block, ran by ``nim c mymod.nim``
|
||||
``nimble test`` also typially runs these in external nimble packages.
|
||||
|
||||
3. testament tests, eg: ``tests/stdlib/tos.nim`` (only used for Nim repo).
|
||||
3. testament tests, e.g.: ``tests/stdlib/tos.nim`` (only used for Nim repo).
|
||||
|
||||
Not all the tests follow the convention here, feel free to change the ones
|
||||
that don't. Always leave the code cleaner than you found it.
|
||||
@@ -34,7 +34,7 @@ that don't. Always leave the code cleaner than you found it.
|
||||
Stdlib
|
||||
------
|
||||
|
||||
If you change the stdlib (anything under ``lib/``, eg ``lib/pure/os.nim``),
|
||||
If you change the stdlib (anything under ``lib/``, e.g. ``lib/pure/os.nim``),
|
||||
put a test in the file you changed. Add the tests under a ``when isMainModule:``
|
||||
condition so they only get executed when the tester is building the
|
||||
file. Each test should be in a separate ``block:`` statement, such that
|
||||
@@ -57,7 +57,7 @@ Sample test:
|
||||
doAssert not (1 == 2)
|
||||
|
||||
Newer tests tend to be run via ``testament`` rather than via ``when isMainModule:``,
|
||||
eg ``tests/stdlib/tos.nim``; this allows additional features such as custom
|
||||
e.g. ``tests/stdlib/tos.nim``; this allows additional features such as custom
|
||||
compiler flags; for more details see below.
|
||||
|
||||
Compiler
|
||||
@@ -96,6 +96,7 @@ An example for a test:
|
||||
var buf: PTest
|
||||
buf.test()
|
||||
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
@@ -125,16 +126,17 @@ To run a single test:
|
||||
|
||||
::
|
||||
|
||||
./koch test run <category>/<name> # eg: tuples/ttuples_issues
|
||||
./koch test run <category>/<name> # e.g.: tuples/ttuples_issues
|
||||
./koch test run tests/stdlib/tos.nim # can also provide relative path
|
||||
|
||||
For reproducible tests (to reproduce an environment more similar to the one
|
||||
run by Continuous Integration on travis/appveyor), you may want to disable your
|
||||
local configuration (eg in ``~/.config/nim/nim.cfg``) which may affect some
|
||||
local configuration (e.g. in ``~/.config/nim/nim.cfg``) which may affect some
|
||||
tests; this can also be achieved by using
|
||||
``export XDG_CONFIG_HOME=pathtoAlternateConfig`` before running ``./koch``
|
||||
commands.
|
||||
|
||||
|
||||
Comparing tests
|
||||
===============
|
||||
|
||||
@@ -177,7 +179,8 @@ the old name and introduce a new name:
|
||||
.. code-block:: nim
|
||||
|
||||
# for routines (proc/template/macro/iterator) and types:
|
||||
proc oldProc() {.deprecated: "use `newImpl: string -> int` instead".} = discard
|
||||
proc oldProc(a: int, b: float): bool {.deprecated:
|
||||
"deprecated since v1.2.3; use `newImpl: string -> int` instead".} = discard
|
||||
|
||||
# for (const/var/let/fields) the msg is not yet supported:
|
||||
const Foo {.deprecated.} = 1
|
||||
@@ -263,6 +266,7 @@ or
|
||||
|
||||
the first is preferred.
|
||||
|
||||
|
||||
Best practices
|
||||
=============
|
||||
|
||||
@@ -309,7 +313,7 @@ which for which ``nim doc`` ignores ``-d:release``).
|
||||
|
||||
.. _delegate_printing:
|
||||
Delegate printing to caller: return ``string`` instead of calling ``echo``
|
||||
rationale: it's more flexible (eg allows caller to call custom printing,
|
||||
rationale: it's more flexible (e.g. allows caller to call custom printing,
|
||||
including prepending location info, writing to log files, etc).
|
||||
|
||||
.. code-block:: nim
|
||||
@@ -319,7 +323,7 @@ including prepending location info, writing to log files, etc).
|
||||
|
||||
.. _use_Option:
|
||||
[Ongoing debate] Consider using Option instead of return bool + var argument,
|
||||
unless stack allocation is needed (eg for efficiency).
|
||||
unless stack allocation is needed (e.g. for efficiency).
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
@@ -337,6 +341,7 @@ https://github.com/nim-lang/Nim/pull/9335 and https://forum.nim-lang.org/t/4089
|
||||
echo foo() # adds a line for testament in `output:` block inside `discard`.
|
||||
doAssert foo() == [1, 2] # preferred, except when not possible to do so.
|
||||
|
||||
|
||||
The Git stuff
|
||||
=============
|
||||
|
||||
@@ -344,11 +349,20 @@ General commit rules
|
||||
--------------------
|
||||
|
||||
1. The commit message should contain either ``[bugfix]`` or ``[feature]``
|
||||
or ``[refactoring]`` or ``[other]``. Refactorings and bugfixes and "other"
|
||||
are backported to the latest stable release branch (currently 0.20.x).
|
||||
or ``[refactoring]`` or ``[other]``. In practice however this is very
|
||||
often forgotten and a commit message like ``fixes #xyz`` is good enough.
|
||||
|
||||
Every commit is backported unless
|
||||
tagged with either ``[feature]`` or with ``[nobackport]``. They are
|
||||
backported to the latest stable release branch (currently 0.20.x).
|
||||
|
||||
Refactorings are backported because they often enable further bugfixes.
|
||||
|
||||
2. All changes introduced by the commit (diff lines) must be related to the
|
||||
2. If you introduce changes which affect backwards compatibility,
|
||||
make breaking changes, or have PR which is tagged as ``[feature]``,
|
||||
the changes should be mentioned in `<changelog.md>`_.
|
||||
|
||||
3. All changes introduced by the commit (diff lines) must be related to the
|
||||
subject of the commit.
|
||||
|
||||
If you change something unrelated to the subject parts of the file, because
|
||||
@@ -358,7 +372,7 @@ General commit rules
|
||||
*Tip:* Never commit everything as is using ``git commit -a``, but review
|
||||
carefully your changes with ``git add -p``.
|
||||
|
||||
3. Changes should not introduce any trailing whitespace.
|
||||
4. Changes should not introduce any trailing whitespace.
|
||||
|
||||
Always check your changes for whitespace errors using ``git diff --check``
|
||||
or add following ``pre-commit`` hook:
|
||||
@@ -368,26 +382,26 @@ General commit rules
|
||||
#!/bin/sh
|
||||
git diff --check --cached || exit $?
|
||||
|
||||
4. Describe your commit and use your common sense.
|
||||
5. Describe your commit and use your common sense.
|
||||
Example commit message:
|
||||
|
||||
Example Commit messages: ``Fixes #123; refs #124``
|
||||
``Fixes #123; refs #124``
|
||||
|
||||
indicates that issue ``#123`` is completely fixed (github may automatically
|
||||
close it when the PR is committed), wheres issue ``#124`` is referenced
|
||||
(eg: partially fixed) and won't close the issue when committed.
|
||||
(e.g.: partially fixed) and won't close the issue when committed.
|
||||
|
||||
5. Commits should be always be rebased against devel (so a fast forward
|
||||
6. Commits should be always be rebased against devel (so a fast forward
|
||||
merge can happen)
|
||||
|
||||
eg: use ``git pull --rebase origin devel``. This is to avoid messing up
|
||||
git history, see `#8664 <https://github.com/nim-lang/Nim/issues/8664>`_ .
|
||||
e.g.: use ``git pull --rebase origin devel``. This is to avoid messing up
|
||||
git history.
|
||||
Exceptions should be very rare: when rebase gives too many conflicts, simply
|
||||
squash all commits using the script shown in
|
||||
https://github.com/nim-lang/Nim/pull/9356
|
||||
|
||||
|
||||
6. Do not mix pure formatting changes (eg whitespace changes, nimpretty) or
|
||||
automated changes (eg nimfix) with other code changes: these should be in
|
||||
7. Do not mix pure formatting changes (e.g. whitespace changes, nimpretty) or
|
||||
automated changes (e.g. nimfix) with other code changes: these should be in
|
||||
separate commits (and the merge on github should not squash these into 1).
|
||||
|
||||
|
||||
@@ -395,11 +409,11 @@ Continuous Integration (CI)
|
||||
---------------------------
|
||||
|
||||
1. Continuous Integration is by default run on every push in a PR; this clogs
|
||||
the CI pipeline and affects other PR's; if you don't need it (eg for WIP or
|
||||
the CI pipeline and affects other PR's; if you don't need it (e.g. for WIP or
|
||||
documentation only changes), add ``[ci skip]`` to your commit message title.
|
||||
This convention is supported by `Appveyor <https://www.appveyor.com/docs/how-to/filtering-commits/#skip-directive-in-commit-message>`_
|
||||
and `Travis <https://docs.travis-ci.com/user/customizing-the-build/#skipping-a-build>`_
|
||||
|
||||
This convention is supported by `Appveyor
|
||||
<https://www.appveyor.com/docs/how-to/filtering-commits/#skip-directive-in-commit-message>`_
|
||||
and `Travis <https://docs.travis-ci.com/user/customizing-the-build/#skipping-a-build>`_.
|
||||
|
||||
2. Consider enabling CI (travis and appveyor) in your own Nim fork, and
|
||||
waiting for CI to be green in that fork (fixing bugs as needed) before
|
||||
@@ -424,7 +438,7 @@ Code reviews
|
||||
git show --color-moved-ws=allow-indentation-change --color-moved=blocks HEAD^
|
||||
|
||||
3. In addition, you can view github-like diffs locally to identify what was changed
|
||||
within a code block using `diff-highlight` or `diff-so-fancy`, eg:
|
||||
within a code block using `diff-highlight` or `diff-so-fancy`, e.g.:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
|
||||
563
doc/destructors.rst
Normal file
563
doc/destructors.rst
Normal file
@@ -0,0 +1,563 @@
|
||||
==================================
|
||||
Nim Destructors and Move Semantics
|
||||
==================================
|
||||
|
||||
:Authors: Andreas Rumpf
|
||||
:Version: |nimversion|
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
About this document
|
||||
===================
|
||||
|
||||
This document describes the upcoming Nim runtime which does
|
||||
not use classical GC algorithms anymore but is based on destructors and
|
||||
move semantics. The new runtime's advantages are that Nim programs become
|
||||
oblivious to the involved heap sizes and programs are easier to write to make
|
||||
effective use of multi-core machines. As a nice bonus, files and sockets and
|
||||
the like will not require manual ``close`` calls anymore.
|
||||
|
||||
This document aims to be a precise specification about how
|
||||
move semantics and destructors work in Nim.
|
||||
|
||||
|
||||
Motivating example
|
||||
==================
|
||||
|
||||
With the language mechanisms described here a custom seq could be
|
||||
written as:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
myseq*[T] = object
|
||||
len, cap: int
|
||||
data: ptr UncheckedArray[T]
|
||||
|
||||
proc `=destroy`*[T](x: var myseq[T]) =
|
||||
if x.data != nil:
|
||||
for i in 0..<x.len: `=destroy`(x[i])
|
||||
dealloc(x.data)
|
||||
x.data = nil
|
||||
|
||||
proc `=`*[T](a: var myseq[T]; b: myseq[T]) =
|
||||
# do nothing for self-assignments:
|
||||
if a.data == b.data: return
|
||||
`=destroy`(a)
|
||||
a.len = b.len
|
||||
a.cap = b.cap
|
||||
if b.data != nil:
|
||||
a.data = cast[type(a.data)](alloc(a.cap * sizeof(T)))
|
||||
for i in 0..<a.len:
|
||||
a.data[i] = b.data[i]
|
||||
|
||||
proc `=move`*[T](a, b: var myseq[T]) =
|
||||
# do nothing for self-assignments:
|
||||
if a.data == b.data: return
|
||||
`=destroy`(a)
|
||||
a.len = b.len
|
||||
a.cap = b.cap
|
||||
a.data = b.data
|
||||
# b's elements have been stolen so ensure that the
|
||||
# destructor for b does nothing:
|
||||
b.data = nil
|
||||
b.len = 0
|
||||
|
||||
proc add*[T](x: var myseq[T]; y: sink T) =
|
||||
if x.len >= x.cap: resize(x)
|
||||
x.data[x.len] = y
|
||||
inc x.len
|
||||
|
||||
proc `[]`*[T](x: myseq[T]; i: Natural): lent T =
|
||||
assert i < x.len
|
||||
x.data[i]
|
||||
|
||||
proc `[]=`*[T](x: myseq[T]; i: Natural; y: sink T) =
|
||||
assert i < x.len
|
||||
x.data[i] = y
|
||||
|
||||
proc createSeq*[T](elems: varargs[T]): myseq[T] =
|
||||
result.cap = elems.len
|
||||
result.len = elems.len
|
||||
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
|
||||
for i in 0..<result.len: result.data[i] = elems[i]
|
||||
|
||||
proc len*[T](x: myseq[T]): int {.inline.} = x.len
|
||||
|
||||
|
||||
|
||||
Lifetime-tracking hooks
|
||||
=======================
|
||||
|
||||
The memory management for Nim's standard ``string`` and ``seq`` types as
|
||||
well as other standard collections is performed via so called
|
||||
"Lifetime-tracking hooks" or "type-bound operators". There are 3 different
|
||||
hooks for each (generic or concrete) object type ``T`` (``T`` can also be a
|
||||
``distinct`` type) that are called implicitly by the compiler.
|
||||
|
||||
(Note: The word "hook" here does not imply any kind of dynamic binding
|
||||
or runtime indirections, the implicit calls are statically bound and
|
||||
potentially inlined.)
|
||||
|
||||
|
||||
`=destroy` hook
|
||||
---------------
|
||||
|
||||
A `=destroy` hook frees the object's associated memory and releases
|
||||
other associated resources. Variables are destroyed via this hook when
|
||||
they go out of scope or when the routine they were declared in is about
|
||||
to return.
|
||||
|
||||
The prototype of this hook for a type ``T`` needs to be:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=destroy`(x: var T)
|
||||
|
||||
|
||||
The general pattern in ``=destroy`` looks like:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=destroy`(x: var T) =
|
||||
# first check if 'x' was moved to somewhere else:
|
||||
if x.field != nil:
|
||||
freeResource(x.field)
|
||||
x.field = nil
|
||||
|
||||
|
||||
|
||||
`=move` hook
|
||||
------------
|
||||
|
||||
A `=move` hook moves an object around, the resources are stolen from the source
|
||||
and passed to the destination. It must be ensured that source's destructor does
|
||||
not free the resources afterwards.
|
||||
|
||||
The prototype of this hook for a type ``T`` needs to be:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=move`(dest, source: var T)
|
||||
|
||||
|
||||
The general pattern in ``=move`` looks like:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=move`(dest, source: var T) =
|
||||
# protect against self-assignments:
|
||||
if dest.field != source.field:
|
||||
`=destroy`(dest)
|
||||
dest.field = source.field
|
||||
source.field = nil
|
||||
|
||||
|
||||
|
||||
`=` (copy) hook
|
||||
---------------
|
||||
|
||||
The ordinary assignment in Nim conceptually copies the values. The ``=`` hook
|
||||
is called for assignments that couldn't be transformed into moves.
|
||||
|
||||
The prototype of this hook for a type ``T`` needs to be:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=`(dest: var T; source: T)
|
||||
|
||||
|
||||
The general pattern in ``=`` looks like:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=`(dest: var T; source: T) =
|
||||
# protect against self-assignments:
|
||||
if dest.field != source.field:
|
||||
`=destroy`(dest)
|
||||
dest.field = duplicateResource(source.field)
|
||||
|
||||
|
||||
The ``=`` proc can be marked with the ``{.error.}`` pragma. Then any assignment
|
||||
that otherwise would lead to a copy is prevented at compile-time.
|
||||
|
||||
|
||||
Move semantics
|
||||
==============
|
||||
|
||||
A "move" can be regarded as an optimized copy operation. If the source of the
|
||||
copy operation is not used afterwards, the copy can be replaced by a move. This
|
||||
document uses the notation ``lastReadOf(x)`` to describe that ``x`` is not
|
||||
used afterwards. This property is computed by a static control flow analysis
|
||||
but can also be enforced by using ``system.move`` explicitly.
|
||||
|
||||
|
||||
Swap
|
||||
====
|
||||
|
||||
The need to check for self-assignments and also the need to destroy previous
|
||||
objects inside ``=`` and ``=move`` is a strong indicator to treat ``system.swap``
|
||||
as a builtin primitive of its own that simply swaps every field in the involved
|
||||
objects via ``copyMem`` or a comparable mechanism.
|
||||
In other words, ``swap(a, b)`` is **not** implemented
|
||||
as ``let tmp = move(a); b = move(a); a = move(tmp)``!
|
||||
|
||||
This has further consequences:
|
||||
|
||||
* Objects that contain pointers that point to the same object are not supported
|
||||
by Nim's model. Otherwise swapped objects would end up in an inconsistent state.
|
||||
* Seqs can use ``realloc`` in the implementation.
|
||||
|
||||
|
||||
Sink parameters
|
||||
===============
|
||||
|
||||
To move a variable into a collection usually ``sink`` parameters are involved.
|
||||
A location that is passed to a ``sink`` parameters should not be used afterwards.
|
||||
This is ensured by a static analysis over a control flow graph. A sink parameter
|
||||
*may* be consumed once in the proc's body but doesn't have to be consumed at all.
|
||||
The reason for this is that signatures
|
||||
like ``proc put(t: var Table; k: sink Key, v: sink Value)`` should be possible
|
||||
without any further overloads and ``put`` might not take owership of ``k`` if
|
||||
``k`` already exists in the table. Sink parameters enable an affine type system,
|
||||
not a linear type system.
|
||||
|
||||
The employed static analysis is limited and only concerned with local variables;
|
||||
however object and tuple fields are treated as separate entities:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc consume(x: sink Obj) = discard "no implementation"
|
||||
|
||||
proc main =
|
||||
let tup = (Obj(), Obj())
|
||||
consume tup[0]
|
||||
# ok, only tup[0] was consumed, tup[1] is still alive:
|
||||
echo tup[1]
|
||||
|
||||
|
||||
Sometimes it is required to explicitly ``move`` a value into its final position:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc main =
|
||||
var dest, src: array[10, string]
|
||||
# ...
|
||||
for i in 0..high(dest): dest[i] = move(src[i])
|
||||
|
||||
An implementation is allowed, but not required to implement even more move
|
||||
optimizations (and the current implementation does not).
|
||||
|
||||
|
||||
Self assignments
|
||||
================
|
||||
|
||||
Unfortunately this document departs significantly from
|
||||
the older design as specified here, https://github.com/nim-lang/Nim/wiki/Destructors.
|
||||
The reason is that under the old design so called "self assignments" could not work.
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc select(cond: bool; a, b: sink string): string =
|
||||
if cond:
|
||||
result = a # moves a into result
|
||||
else:
|
||||
result = b # moves b into result
|
||||
|
||||
proc main =
|
||||
var x = "abc"
|
||||
var y = "xyz"
|
||||
|
||||
# possible self-assignment:
|
||||
x = select(rand() < 0.5, x, y)
|
||||
# 'select' must communicate what parameter has been
|
||||
# consumed. We cannot simply generate:
|
||||
# (select(...); wasMoved(x); wasMoved(y))
|
||||
|
||||
Consequence: ``sink`` parameters for objects that have a non-trivial destructor
|
||||
must be passed as by-pointer under the hood. A further advantage is that parameters
|
||||
are never destroyed, only variables are. The caller's location passed to
|
||||
a ``sink`` parameter has to be destroyed by the caller and does not burden
|
||||
the callee.
|
||||
|
||||
|
||||
Const temporaries
|
||||
=================
|
||||
|
||||
Constant literals like ``nil`` cannot be easily be ``=moved``'d. The solution
|
||||
is to pass a temporary location that contains ``nil`` to the sink location.
|
||||
In other words, ``var T`` can only bind to locations, but ``sink T`` can bind
|
||||
to values.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
var x: owned ref T = nil
|
||||
# gets turned into by the compiler:
|
||||
var tmp = nil
|
||||
move(x, tmp)
|
||||
|
||||
|
||||
Rewrite rules
|
||||
=============
|
||||
|
||||
**Note**: A function call ``f()`` is always the "last read" of the involved
|
||||
temporary location and so covered under the more general rewrite rules.
|
||||
|
||||
**Note**: There are two different allowed implementation strategies:
|
||||
|
||||
1. The produced ``finally`` section can be a single section that is wrapped
|
||||
around the complete routine body.
|
||||
2. The produced ``finally`` section is wrapped around the enclosing scope.
|
||||
|
||||
The current implementation follows strategy (1). This means that resources are
|
||||
not destroyed at the scope exit, but at the proc exit.
|
||||
|
||||
::
|
||||
|
||||
var x: T; stmts
|
||||
--------------- (destroy-var)
|
||||
var x: T; try stmts
|
||||
finally: `=destroy`(x)
|
||||
|
||||
|
||||
f(...)
|
||||
------------------------ (function-call)
|
||||
(let tmp = f(...); tmp)
|
||||
finally: `=destroy`(tmp)
|
||||
|
||||
|
||||
x = lastReadOf z
|
||||
------------------ (move-optimization)
|
||||
`=move`(x, z)
|
||||
|
||||
|
||||
x = y
|
||||
------------------ (copy)
|
||||
`=`(x, y)
|
||||
|
||||
|
||||
x = move y
|
||||
------------------ (enforced-move)
|
||||
`=move`(x, y)
|
||||
|
||||
|
||||
f_sink(notLastReadOf y)
|
||||
----------------------- (copy-to-sink)
|
||||
(let tmp; `=`(tmp, y); f_sink(tmp))
|
||||
finally: `=destroy`(tmp)
|
||||
|
||||
|
||||
f_sink(move y)
|
||||
----------------------- (enforced-move-to-sink)
|
||||
(let tmp; `=move`(tmp, y); f_sink(tmp))
|
||||
finally: `=destroy`(tmp)
|
||||
|
||||
|
||||
|
||||
Cursor variables
|
||||
================
|
||||
|
||||
There is an additional rewrite rule for so called "cursor" variables.
|
||||
A cursor variable is a variable that is only used for navigation inside
|
||||
a data structure. The otherwise implied copies (or moves) and destructions
|
||||
can be avoided altogether for cursor variables:
|
||||
|
||||
::
|
||||
|
||||
var x {.cursor.}: T
|
||||
x = path(z)
|
||||
stmts
|
||||
-------------------------- (cursor-var)
|
||||
x = bitwiseCopy(path z)
|
||||
stmts
|
||||
# x is not destroyed.
|
||||
|
||||
|
||||
``stmts`` must not mutate ``z`` nor ``x``. All assignments to ``x`` must be
|
||||
of the form ``path(z)`` but the ``z`` can differ. Neither ``z`` nor ``x``
|
||||
can be aliased; this implies the addresses of these locations must not be
|
||||
used explicitly.
|
||||
|
||||
The current implementation does not compute cursor variables but supports
|
||||
the ``.cursor`` pragma annotation. Cursor variables are respected and
|
||||
simply trusted: No checking is performed that no mutations or aliasing
|
||||
occurs.
|
||||
|
||||
Cursor variables are commonly used in ``iterator`` implementations:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
iterator nonEmptyItems(x: seq[string]): string =
|
||||
for i in 0..high(x):
|
||||
let it {.cursor.} = x[i] # no string copies, no destruction of 'it'
|
||||
if it.len > 0:
|
||||
yield it
|
||||
|
||||
|
||||
Lent type
|
||||
=========
|
||||
|
||||
``proc p(x: sink T)`` means that the proc ``p`` takes ownership of ``x``.
|
||||
To eliminate even more creation/copy <-> destruction pairs, a proc's return
|
||||
type can be annotated as ``lent T``. This is useful for "getter" accessors
|
||||
that seek to allow an immutable view into a container.
|
||||
|
||||
The ``sink`` and ``lent`` annotations allow us to remove most (if not all)
|
||||
superfluous copies and destructions.
|
||||
|
||||
``lent T`` is like ``var T`` a hidden pointer. It is proven by the compiler
|
||||
that the pointer does not outlive its origin. No destructor call is injected
|
||||
for expressions of type ``lent T`` or of type ``var T``.
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Tree = object
|
||||
kids: seq[Tree]
|
||||
|
||||
proc construct(kids: sink seq[Tree]): Tree =
|
||||
result = Tree(kids: kids)
|
||||
# converted into:
|
||||
`=move`(result.kids, kids)
|
||||
|
||||
proc `[]`*(x: Tree; i: int): lent Tree =
|
||||
result = x.kids[i]
|
||||
# borrows from 'x', this is transformed into:
|
||||
result = addr x.kids[i]
|
||||
# This means 'lent' is like 'var T' a hidden pointer.
|
||||
# Unlike 'var' this cannot be used to mutate the object.
|
||||
|
||||
iterator children*(t: Tree): lent Tree =
|
||||
for x in t.kids: yield x
|
||||
|
||||
proc main =
|
||||
# everything turned into moves:
|
||||
let t = construct(@[construct(@[]), construct(@[])])
|
||||
echo t[0] # accessor does not copy the element!
|
||||
|
||||
|
||||
|
||||
Owned refs
|
||||
==========
|
||||
|
||||
Let ``W`` be an ``owned ref`` type. Conceptually its hooks look like:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=destroy`(x: var W) =
|
||||
if x != nil:
|
||||
assert x.refcount == 0, "dangling unowned pointers exist!"
|
||||
`=destroy`(x[])
|
||||
x = nil
|
||||
|
||||
proc `=`(x: var W; y: W) {.error: "owned refs can only be moved".}
|
||||
|
||||
proc `=move`(x, y: var W) =
|
||||
if x != y:
|
||||
`=destroy`(x)
|
||||
bitwiseCopy x, y # raw pointer copy
|
||||
y = nil
|
||||
|
||||
Let ``U`` be an unowned ``ref`` type. Conceptually its hooks look like:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `=destroy`(x: var U) =
|
||||
if x != nil:
|
||||
dec x.refcount
|
||||
|
||||
proc `=`(x: var U; y: U) =
|
||||
# Note: No need to check for self-assignments here.
|
||||
if y != nil: inc y.refcount
|
||||
if x != nil: dec x.refcount
|
||||
bitwiseCopy x, y # raw pointer copy
|
||||
|
||||
proc `=move`(x, y: var U) =
|
||||
# Note: Moves are the same as assignments.
|
||||
`=`(x, y)
|
||||
|
||||
|
||||
Hook lifting
|
||||
============
|
||||
|
||||
The hooks of a tuple type ``(A, B, ...)`` are generated by lifting the
|
||||
hooks of the involved types ``A``, ``B``, ... to the tuple type. In
|
||||
other words, a copy ``x = y`` is implemented
|
||||
as ``x[0] = y[0]; x[1] = y[1]; ...``, likewise for ``=move`` and ``=destroy``.
|
||||
|
||||
Other value-based compound types like ``object`` and ``array`` are handled
|
||||
correspondingly. For ``object`` however, the compiler generated hooks
|
||||
can be overridden. This can also be important to use an alternative traversal
|
||||
of the involved datastructure that is more efficient or in order to avoid
|
||||
deep recursions.
|
||||
|
||||
|
||||
|
||||
Hook generation
|
||||
===============
|
||||
|
||||
The ability to override a hook leads to a phase ordering problem:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Foo[T] = object
|
||||
|
||||
proc main =
|
||||
var f: Foo[int]
|
||||
# error: destructor for 'f' called here before
|
||||
# it was seen in this module.
|
||||
|
||||
proc `=destroy`[T](f: var Foo[T]) =
|
||||
discard
|
||||
|
||||
|
||||
The solution is to define ``proc `=destroy`[T](f: var Foo[T])`` before
|
||||
it is used. The compiler generates implicit
|
||||
hooks for all types in *strategic places* so that an explicitly provided
|
||||
hook that comes too "late" can be detected reliably. These *strategic places*
|
||||
have been derived from the rewrite rules and are as follows:
|
||||
|
||||
- In the construct ``let/var x = ...`` (var/let binding)
|
||||
hooks are generated for ``typeof(x)``.
|
||||
- In ``x = ...`` (assignment) hooks are generated for ``typeof(x)``.
|
||||
- In ``f(...)`` (function call) hooks are generated for ``typeof(f(...))``.
|
||||
|
||||
|
||||
nodestroy pragma
|
||||
================
|
||||
|
||||
The experimental `nodestroy`:idx: pragma inhibits hook injections. This can be
|
||||
used to specialize the object traversal in order to avoid deep recursions:
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
type Node = ref object
|
||||
x, y: int32
|
||||
left, right: owned Node
|
||||
|
||||
type Tree = object
|
||||
root: owned Node
|
||||
|
||||
proc `=destroy`(t: var Tree) {.nodestroy.} =
|
||||
# use an explicit stack so that we do not get stack overflows:
|
||||
var s: seq[owned Node] = @[t.root]
|
||||
while s.len > 0:
|
||||
let x = s.pop
|
||||
if x.left != nil: s.add(x.left)
|
||||
if x.right != nil: s.add(x.right)
|
||||
# free the memory explicit:
|
||||
dispose(x)
|
||||
# notice how even the destructor for 's' is not called implicitly
|
||||
# anymore thanks to .nodestroy, so we have to call it on our own:
|
||||
`=destroy`(s)
|
||||
|
||||
|
||||
As can be seen from the example, this solution is hardly sufficient and
|
||||
should eventually be replaced by a better solution.
|
||||
@@ -220,7 +220,7 @@ Source URLs are generated as `href="${url}/tree/${commit}/${path}#L${line}"` by
|
||||
You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value with a hyperlink to your own code repository.
|
||||
|
||||
In the case of Nim's own documentation, the ``commit`` value is just a commit
|
||||
hash to append to a formatted URL to https://github.com/Araq/Nim. The
|
||||
hash to append to a formatted URL to https://github.com/nim-lang/Nim. The
|
||||
``tools/nimweb.nim`` helper queries the current git commit hash during doc
|
||||
generation, but since you might be working on an unpublished repository, it
|
||||
also allows specifying a ``githash`` value in ``web/website.ini`` to force a
|
||||
|
||||
@@ -37,8 +37,8 @@ pragmas semantic checking of pragmas
|
||||
|
||||
idents implements a general mapping from identifiers to an internal
|
||||
representation (``PIdent``) that is used so that a simple
|
||||
id-comparison suffices to say whether two Nim identifiers
|
||||
are equivalent
|
||||
id-comparison suffices to establish whether two Nim
|
||||
identifiers are equivalent
|
||||
ropes implements long strings represented as trees for
|
||||
lazy evaluation; used mainly by the code generators
|
||||
|
||||
|
||||
216
doc/hcr.rst
Normal file
216
doc/hcr.rst
Normal file
@@ -0,0 +1,216 @@
|
||||
===================================
|
||||
Hot code reloading
|
||||
===================================
|
||||
|
||||
The `hotCodeReloading`:idx: option enables special compilation mode where
|
||||
changes in the code can be applied automatically to a running program.
|
||||
The code reloading happens at the granularity of an individual module.
|
||||
When a module is reloaded, any newly added global variables will be
|
||||
initialized, but all other top-level code appearing in the module won't
|
||||
be re-executed and the state of all existing global variables will be
|
||||
preserved.
|
||||
|
||||
|
||||
Basic workflow
|
||||
==============
|
||||
|
||||
Currently hot code reloading does not work for the main module itself,
|
||||
so we have to use a helper module where the major logic we want to change
|
||||
during development resides.
|
||||
|
||||
In this example we use SDL2 to create a window and we reload the logic
|
||||
code when ``F9`` is pressed. The important lines are marked with ``#***``.
|
||||
To install SDL2 you can use ``nimble install sdl2``.
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
# logic.nim
|
||||
import sdl2/sdl
|
||||
|
||||
#*** import the hotcodereloading stdlib module ***
|
||||
import hotcodereloading
|
||||
|
||||
var runGame*: bool = true
|
||||
var window: Window
|
||||
var renderer: Renderer
|
||||
|
||||
proc init*() =
|
||||
discard sdl.init(INIT_EVERYTHING)
|
||||
window = createWindow("testing", WINDOWPOS_UNDEFINED.cint, WINDOWPOS_UNDEFINED.cint, 640, 480, 0'u32)
|
||||
assert(window != nil, $sdl.getError())
|
||||
renderer = createRenderer(window, -1, RENDERER_SOFTWARE)
|
||||
assert(renderer != nil, $sdl.getError())
|
||||
|
||||
proc destroy*() =
|
||||
destroyRenderer(renderer)
|
||||
destroyWindow(window)
|
||||
|
||||
var posX = 1
|
||||
var posY = 0
|
||||
var dX = 1
|
||||
var dY = 1
|
||||
|
||||
proc update*() =
|
||||
for evt in events():
|
||||
if evt.kind == QUIT:
|
||||
runGame = false
|
||||
break
|
||||
if evt.kind == KEY_DOWN:
|
||||
if evt.key.keysym.scancode == SCANCODE_ESCAPE: runGame = false
|
||||
elif evt.key.keysym.scancode == SCANCODE_F9:
|
||||
#*** reload this logic.nim module on the F9 keypress ***
|
||||
performCodeReload()
|
||||
|
||||
# draw a bouncing rectangle:
|
||||
posX += dX
|
||||
posY += dY
|
||||
|
||||
if posX >= 640: dX = -2
|
||||
if posX <= 0: dX = +2
|
||||
if posY >= 480: dY = -2
|
||||
if posY <= 0: dY = +2
|
||||
|
||||
discard renderer.setRenderDrawColor(0, 0, 255, 255)
|
||||
discard renderer.renderClear()
|
||||
discard renderer.setRenderDrawColor(255, 128, 128, 0)
|
||||
|
||||
var rect: Rect(x: posX - 25, y: posY - 25, w: 50, h: 50)
|
||||
discard renderer.renderFillRect(rect.addr)
|
||||
delay(16)
|
||||
renderer.renderPresent()
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
# mymain.nim
|
||||
import logic
|
||||
|
||||
proc main() =
|
||||
init()
|
||||
while runGame:
|
||||
update()
|
||||
destroy()
|
||||
|
||||
main()
|
||||
|
||||
|
||||
Compile this example via::
|
||||
|
||||
nim c --hotcodereloading:on mymain.nim
|
||||
|
||||
Now start the program and KEEP it running!
|
||||
|
||||
::
|
||||
|
||||
# Unix:
|
||||
mymain &
|
||||
# or Windows (click on the .exe)
|
||||
mymain.exe
|
||||
# edit
|
||||
|
||||
For example, change the line::
|
||||
|
||||
discard renderer.setRenderDrawColor(255, 128, 128, 0)
|
||||
|
||||
into::
|
||||
|
||||
discard renderer.setRenderDrawColor(255, 255, 128, 0)
|
||||
|
||||
(This will change the color of the rectangle.)
|
||||
|
||||
Then recompile the project, but do not restart or quit the mymain.exe program!
|
||||
::
|
||||
|
||||
nim c --hotcodereloading:on mymain.nim
|
||||
|
||||
Now give the ``mymain`` SDL window the focus, press F9 and watch the
|
||||
updated version of the program.
|
||||
|
||||
|
||||
|
||||
Reloading API
|
||||
=============
|
||||
|
||||
One can use the special event handlers ``beforeCodeReload`` and
|
||||
``afterCodeReload`` to reset the state of a particular variable or to force
|
||||
the execution of certain statements:
|
||||
|
||||
.. code-block:: Nim
|
||||
var
|
||||
settings = initTable[string, string]()
|
||||
lastReload: Time
|
||||
|
||||
for k, v in loadSettings():
|
||||
settings[k] = v
|
||||
|
||||
initProgram()
|
||||
|
||||
afterCodeReload:
|
||||
lastReload = now()
|
||||
resetProgramState()
|
||||
|
||||
On each code reload, Nim will first execute all `beforeCodeReload`:idx:
|
||||
handlers registered in the previous version of the program and then all
|
||||
`afterCodeReload`:idx: handlers appearing in the newly loaded code. Please note
|
||||
that any handlers appearing in modules that weren't reloaded will also be
|
||||
executed. To prevent this behavior, one can guard the code with the
|
||||
`hasModuleChanged()`:idx: API:
|
||||
|
||||
.. code-block:: Nim
|
||||
import mydb
|
||||
|
||||
var myCache = initTable[Key, Value]()
|
||||
|
||||
afterCodeReload:
|
||||
if hasModuleChanged(mydb):
|
||||
resetCache(myCache)
|
||||
|
||||
The hot code reloading is based on dynamic library hot swapping in the native
|
||||
targets and direct manipulation of the global namespace in the JavaScript
|
||||
target. The Nim compiler does not specify the mechanism for detecting the
|
||||
conditions when the code must be reloaded. Instead, the program code is
|
||||
expected to call `performCodeReload()`:idx: every time it wishes to reload
|
||||
its code.
|
||||
|
||||
It's expected that most projects will implement the reloading with a suitable
|
||||
build-system triggered IPC notification mechanism, but a polling solution is
|
||||
also possible through the provided `hasAnyModuleChanged()`:idx: API.
|
||||
|
||||
In order to access ``beforeCodeReload``, ``afterCodeReload``, ``hasModuleChanged``
|
||||
or ``hasAnyModuleChanged`` one must import the `hotcodereloading`:idx: module.
|
||||
|
||||
|
||||
Native code targets
|
||||
===================
|
||||
|
||||
Native projects using the hot code reloading option will be implicitly
|
||||
compiled with the `-d:useNimRtl` option and they will depend on both
|
||||
the ``nimrtl`` library and the ``nimhcr`` library which implements the
|
||||
hot code reloading run-time.
|
||||
|
||||
All modules of the project will be compiled to separate dynamic link
|
||||
libraries placed in the ``nimcache`` directory. Please note that during
|
||||
the execution of the program, the hot code reloading run-time will load
|
||||
only copies of these libraries in order to not interfere with any newly
|
||||
issued build commands.
|
||||
|
||||
The main module of the program is considered non-reloadable. Please note
|
||||
that procs from reloadable modules should not appear in the call stack of
|
||||
program while ``performCodeReload`` is being called. Thus, the main module
|
||||
is a suitable place for implementing a program loop capable of calling
|
||||
``performCodeReload``.
|
||||
|
||||
Please note that reloading won't be possible when any of the type definitions
|
||||
in the program has been changed. When closure iterators are used (directly or
|
||||
through async code), the reloaded refinitions will affect only newly created
|
||||
instances. Existing iterator instancess will execute their original code to
|
||||
completion.
|
||||
|
||||
JavaScript target
|
||||
=================
|
||||
|
||||
Once your code is compiled for hot reloading, the ``nim-livereload`` NPM
|
||||
package provides a convenient solution for implementing the actual reloading
|
||||
in the browser using a framework such as [LiveReload](http://livereload.com/)
|
||||
or [BrowserSync](https://browsersync.io/).
|
||||
@@ -184,7 +184,7 @@ How the RTL is compiled
|
||||
|
||||
The ``system`` module contains the part of the RTL which needs support by
|
||||
compiler magic (and the stuff that needs to be in it because the spec
|
||||
says so). The C code generator generates the C code for it just like any other
|
||||
says so). The C code generator generates the C code for it, just like any other
|
||||
module. However, calls to some procedures like ``addInt`` are inserted by
|
||||
the CCG. Therefore the module ``magicsys`` contains a table (``compilerprocs``)
|
||||
with all symbols that are marked as ``compilerproc``. ``compilerprocs`` are
|
||||
@@ -246,14 +246,14 @@ The symbol's ``ast`` field is loaded lazily, on demand. This is where most
|
||||
savings come from, only the shallow outer AST is reconstructed immediately.
|
||||
|
||||
It is also important that the replay involves the ``import`` statement so
|
||||
that the dependencies are resolved properly.
|
||||
that dependencies are resolved properly.
|
||||
|
||||
|
||||
Shared global compiletime state
|
||||
-------------------------------
|
||||
|
||||
Nim allows ``.global, compiletime`` variables that can be filled by macro
|
||||
invokations across different modules. This feature breaks modularity in a
|
||||
invocations across different modules. This feature breaks modularity in a
|
||||
severe way. Plenty of different solutions have been proposed:
|
||||
|
||||
- Restrict the types of global compiletime variables to ``Set[T]`` or
|
||||
@@ -286,7 +286,7 @@ We only know the root is ``someGlobal`` but the concrete path to the data
|
||||
is unknown as is the value that is added. We could compute a "diff" between
|
||||
the global states and use that to compute a symbol patchset, but this is
|
||||
quite some work, expensive to do at runtime (it would need to run after
|
||||
every module has been compiled) and also would break for hash tables.
|
||||
every module has been compiled) and would also break for hash tables.
|
||||
|
||||
We need an API that hides the complex aliasing problems by not relying
|
||||
on Nim's global variables. The obvious solution is to use string keys
|
||||
|
||||
165
doc/manual.rst
165
doc/manual.rst
@@ -1283,6 +1283,39 @@ Arrays are always bounds checked (statically or at runtime). These
|
||||
checks can be disabled via pragmas or invoking the compiler with the
|
||||
``--boundChecks:off`` command line switch.
|
||||
|
||||
An array constructor can have explicit indexes for readability:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Values = enum
|
||||
valA, valB, valC
|
||||
|
||||
const
|
||||
lookupTable = [
|
||||
valA: "A",
|
||||
valB: "B",
|
||||
valC: "C"
|
||||
]
|
||||
|
||||
If an index is left out, ``succ(lastIndex)`` is used as the index
|
||||
value:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
type
|
||||
Values = enum
|
||||
valA, valB, valC, valD, valE
|
||||
|
||||
const
|
||||
lookupTable = [
|
||||
valA: "A",
|
||||
"B",
|
||||
valC: "C",
|
||||
"D", "e"
|
||||
]
|
||||
|
||||
|
||||
|
||||
Open arrays
|
||||
-----------
|
||||
@@ -1667,7 +1700,39 @@ To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and
|
||||
``realloc`` can be used. The documentation of the system module contains
|
||||
further information.
|
||||
|
||||
If a reference points to *nothing*, it has the value ``nil``.
|
||||
|
||||
Nil
|
||||
---
|
||||
|
||||
If a reference points to *nothing*, it has the value ``nil``. ``nil`` is also
|
||||
the default value for all ``ref`` and ``ptr`` types. Dereferencing ``nil``
|
||||
is an unrecoverable fatal runtime error. A dereferencing operation ``p[]``
|
||||
implies that ``p`` is not nil. This can be exploited by the implementation to
|
||||
optimize code like:
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
p[].field = 3
|
||||
if p != nil:
|
||||
# if p were nil, ``p[]`` would have caused a crash already,
|
||||
# so we know ``p`` is always not nil here.
|
||||
action()
|
||||
|
||||
Into:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
p[].field = 3
|
||||
action()
|
||||
|
||||
|
||||
*Note*: This is not comparable to C's "undefined behavior" for
|
||||
dereferencing NULL pointers.
|
||||
|
||||
|
||||
Mixing GC'ed memory with ``ptr``
|
||||
--------------------------------
|
||||
|
||||
Special care has to be taken if an untraced object contains traced objects like
|
||||
traced references, strings or sequences: in order to free everything properly,
|
||||
@@ -3023,6 +3088,21 @@ As seen in the above example, the case expression can also introduce side
|
||||
effects. When multiple statements are given for a branch, Nim will use
|
||||
the last expression as the result value.
|
||||
|
||||
Block expression
|
||||
----------------
|
||||
|
||||
A `block expression` is almost like a block statement, but it is an expression
|
||||
that uses last expression under the block as the value.
|
||||
It is similar to the statement list expression, but the statement list expression
|
||||
does not open new block scope.
|
||||
|
||||
.. code-block:: nim
|
||||
let a = block:
|
||||
var fib = @[0, 1]
|
||||
for i in 0..10:
|
||||
fib.add fib[^1] + fib[^2]
|
||||
fib
|
||||
|
||||
Table constructor
|
||||
-----------------
|
||||
|
||||
@@ -3062,6 +3142,16 @@ Ordinary procs are often preferred over type conversions in Nim: For instance,
|
||||
``$`` is the ``toString`` operator by convention and ``toFloat`` and ``toInt``
|
||||
can be used to convert from floating point to integer or vice versa.
|
||||
|
||||
A type conversion can also be used to disambiguate overloaded routines:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc p(x: int) = echo "int"
|
||||
proc p(x: string) = echo "string"
|
||||
|
||||
let procVar = (proc(x: string))(p)
|
||||
procVar("a")
|
||||
|
||||
|
||||
Type casts
|
||||
----------
|
||||
@@ -3298,6 +3388,25 @@ different; for this a special setter syntax is needed:
|
||||
new s
|
||||
s.host = 34 # same as `host=`(s, 34)
|
||||
|
||||
A proc defined as ``f=`` (with the trailing ``=``) is called
|
||||
a `setter`:idx:. A setter can be called explicitly via the common
|
||||
backticks notation:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc `f=`(x: MyObject; value: string) =
|
||||
discard
|
||||
|
||||
`f=`(myObject, "value")
|
||||
|
||||
|
||||
``f=`` can be called implicitly in the pattern
|
||||
``x.f = value`` if and only if the type of ``x`` does not have a field
|
||||
named ``f`` or if ``f`` is not visible in the current module. These rules
|
||||
ensure that object fields and accessors can have the same name. Within the
|
||||
module ``x.f`` is then always interpreted as field access and outside the
|
||||
module it is interpreted as an accessor proc call.
|
||||
|
||||
|
||||
Command invocation syntax
|
||||
-------------------------
|
||||
@@ -5040,57 +5149,6 @@ Currently for loop macros must be enabled explicitly
|
||||
via ``{.experimental: "forLoopMacros".}``.
|
||||
|
||||
|
||||
Case statement macros
|
||||
---------------------
|
||||
|
||||
A macro that needs to be called `match`:idx: can be used to rewrite
|
||||
``case`` statements in order to implement `pattern matching`:idx: for
|
||||
certain types. The following example implements a simplistic form of
|
||||
pattern matching for tuples, leveraging the existing equality operator
|
||||
for tuples (as provided in ``system.==``):
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
{.experimental: "caseStmtMacros".}
|
||||
|
||||
import macros
|
||||
|
||||
macro match(n: tuple): untyped =
|
||||
result = newTree(nnkIfStmt)
|
||||
let selector = n[0]
|
||||
for i in 1 ..< n.len:
|
||||
let it = n[i]
|
||||
case it.kind
|
||||
of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr:
|
||||
result.add it
|
||||
of nnkOfBranch:
|
||||
for j in 0..it.len-2:
|
||||
let cond = newCall("==", selector, it[j])
|
||||
result.add newTree(nnkElifBranch, cond, it[^1])
|
||||
else:
|
||||
error "'match' cannot handle this node", it
|
||||
echo repr result
|
||||
|
||||
case ("foo", 78)
|
||||
of ("foo", 78): echo "yes"
|
||||
of ("bar", 88): echo "no"
|
||||
else: discard
|
||||
|
||||
|
||||
Currently case statement macros must be enabled explicitly
|
||||
via ``{.experimental: "caseStmtMacros".}``.
|
||||
|
||||
``match`` macros are subject to overload resolution. First the
|
||||
``case``'s selector expression is used to determine which ``match``
|
||||
macro to call. To this macro is then passed the complete ``case``
|
||||
statement body and the macro is evaluated.
|
||||
|
||||
In other words, the macro needs to transform the full ``case`` statement
|
||||
but only the statement's selector expression is used to determine which
|
||||
macro to call.
|
||||
|
||||
|
||||
Special Types
|
||||
=============
|
||||
|
||||
@@ -6020,7 +6078,6 @@ In the example a new pragma named ``rtl`` is introduced that either imports
|
||||
a symbol from a dynamic library or exports the symbol for dynamic library
|
||||
generation.
|
||||
|
||||
|
||||
Disabling certain messages
|
||||
--------------------------
|
||||
Nim generates some warnings and hints ("line too long") that may annoy the
|
||||
@@ -6291,7 +6348,7 @@ prefixes ``/*TYPESECTION*/`` or ``/*VARSECTION*/`` or ``/*INCLUDESECTION*/``:
|
||||
ImportCpp pragma
|
||||
----------------
|
||||
|
||||
**Note**: `c2nim <https://nim-lang.org/docs/c2nim.html>`_ can parse a large subset of C++ and knows
|
||||
**Note**: `c2nim <https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst>`_ can parse a large subset of C++ and knows
|
||||
about the ``importcpp`` pragma pattern language. It is not necessary
|
||||
to know all the details described here.
|
||||
|
||||
|
||||
@@ -886,6 +886,57 @@ The builtin ``deepCopy`` can even clone closures and their environments. See
|
||||
the documentation of `spawn`_ for details.
|
||||
|
||||
|
||||
Case statement macros
|
||||
=====================
|
||||
|
||||
A macro that needs to be called `match`:idx: can be used to rewrite
|
||||
``case`` statements in order to implement `pattern matching`:idx: for
|
||||
certain types. The following example implements a simplistic form of
|
||||
pattern matching for tuples, leveraging the existing equality operator
|
||||
for tuples (as provided in ``system.==``):
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
{.experimental: "caseStmtMacros".}
|
||||
|
||||
import macros
|
||||
|
||||
macro match(n: tuple): untyped =
|
||||
result = newTree(nnkIfStmt)
|
||||
let selector = n[0]
|
||||
for i in 1 ..< n.len:
|
||||
let it = n[i]
|
||||
case it.kind
|
||||
of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr:
|
||||
result.add it
|
||||
of nnkOfBranch:
|
||||
for j in 0..it.len-2:
|
||||
let cond = newCall("==", selector, it[j])
|
||||
result.add newTree(nnkElifBranch, cond, it[^1])
|
||||
else:
|
||||
error "'match' cannot handle this node", it
|
||||
echo repr result
|
||||
|
||||
case ("foo", 78)
|
||||
of ("foo", 78): echo "yes"
|
||||
of ("bar", 88): echo "no"
|
||||
else: discard
|
||||
|
||||
|
||||
Currently case statement macros must be enabled explicitly
|
||||
via ``{.experimental: "caseStmtMacros".}``.
|
||||
|
||||
``match`` macros are subject to overload resolution. First the
|
||||
``case``'s selector expression is used to determine which ``match``
|
||||
macro to call. To this macro is then passed the complete ``case``
|
||||
statement body and the macro is evaluated.
|
||||
|
||||
In other words, the macro needs to transform the full ``case`` statement
|
||||
but only the statement's selector expression is used to determine which
|
||||
macro to call.
|
||||
|
||||
|
||||
Term rewriting macros
|
||||
=====================
|
||||
|
||||
@@ -1589,6 +1640,26 @@ having unknown lock level as well:
|
||||
g.memberProc()
|
||||
|
||||
|
||||
noRewrite pragma
|
||||
----------------
|
||||
|
||||
Term rewriting macros and templates are currently greedy and
|
||||
they will rewrite as long as there is a match.
|
||||
There was no way to ensure some rewrite happens only once,
|
||||
eg. when rewriting term to same term plus extra content.
|
||||
|
||||
``noRewrite`` pragma can actually prevent further rewriting on marked code,
|
||||
e.g. with given example ``echo("ab")`` will be rewritten just once:
|
||||
|
||||
.. code-block:: nim
|
||||
template pwnEcho{echo(x)}(x: expr) =
|
||||
{.noRewrite.}: echo("pwned!")
|
||||
|
||||
echo "ab"
|
||||
|
||||
``noRewrite`` pragma can be useful to control term-rewriting macros recursion.
|
||||
|
||||
|
||||
Taint mode
|
||||
==========
|
||||
|
||||
|
||||
97
doc/nimc.rst
97
doc/nimc.rst
@@ -144,6 +144,9 @@ to override symbols during build time.
|
||||
Compile time symbols are completely **case insensitive** and underscores are
|
||||
ignored too. ``--define:FOO`` and ``--define:foo`` are identical.
|
||||
|
||||
Compile time symbols starting with the ``nim`` prefix are reserved for the
|
||||
implementation and should not be used elsewhere.
|
||||
|
||||
|
||||
Configuration files
|
||||
-------------------
|
||||
@@ -266,11 +269,11 @@ configuration file should contain something like::
|
||||
Cross compilation for Windows
|
||||
=============================
|
||||
|
||||
To cross compile for Windows from Linux or OSX using the MinGW-w64 toolchain::
|
||||
To cross compile for Windows from Linux or macOS using the MinGW-w64 toolchain::
|
||||
|
||||
nim c -d:mingw myproject.nim
|
||||
|
||||
Use ``--cpu:i386`` or ``--cpu:amd64`` to switch the cpu arch.
|
||||
Use ``--cpu:i386`` or ``--cpu:amd64`` to switch the CPU architecture.
|
||||
|
||||
The MinGW-w64 toolchain can be installed as follows::
|
||||
|
||||
@@ -414,94 +417,6 @@ the generated C contains code to ensure that proper stack traces with line
|
||||
number information are given if the program crashes or an uncaught exception
|
||||
is raised.
|
||||
|
||||
Hot code reloading
|
||||
------------------
|
||||
|
||||
The `hotCodeReloading`:idx: option enables special compilation mode where
|
||||
changes in the code can be applied automatically to a running program.
|
||||
The code reloading happens at the granularity of an individual module.
|
||||
When a module is reloaded, any newly added global variables will be
|
||||
initialized, but all other top-level code appearing in the module won't
|
||||
be re-executed and the state of all existing global variables will be
|
||||
preserved. One can use the special event handlers ``beforeCodeReload`` and
|
||||
``afterCodeReload`` to reset the state of a particular variable or to force
|
||||
the execution of certain statements:
|
||||
|
||||
.. code-block:: Nim
|
||||
var
|
||||
settings = initTable[string, string]()
|
||||
lastReload: Time
|
||||
|
||||
for k, v in loadSettings():
|
||||
settings[k] = v
|
||||
|
||||
initProgram()
|
||||
|
||||
afterCodeReload:
|
||||
lastReload = now()
|
||||
resetProgramState()
|
||||
|
||||
On each code reload, Nim will first execute all `beforeCodeReload`:idx:
|
||||
handlers registered in the previous version of the program and then all
|
||||
`afterCodeReload`:idx handlers appearing in the newly loaded code. Please note
|
||||
that any handlers appearing in modules that weren't reloaded will also be
|
||||
executed. To prevent this behavior, one can guard the code with the
|
||||
`hasModuleChanged()`:idx: API:
|
||||
|
||||
.. code-block:: Nim
|
||||
import mydb
|
||||
|
||||
var myCache = initTable[Key, Value]()
|
||||
|
||||
afterCodeReload:
|
||||
if hasModuleChanged(mydb):
|
||||
resetCache(myCache)
|
||||
|
||||
The hot code reloading is based on dynamic library hot swapping in the native
|
||||
targets and direct manipulation of the global namespace in the JavaScript
|
||||
target. The Nim compiler does not specify the mechanism for detecting the
|
||||
conditions when the code must be reloaded. Instead, the program code is
|
||||
expected to call `performCodeReload()`:idx every time it wishes to reload
|
||||
its code.
|
||||
|
||||
It's expected that most projects will implement the reloading with a suitable
|
||||
build-system triggered IPC notification mechanism, but a polling solution is
|
||||
also possible through the provided `hasAnyModuleChanged()`:idx API.
|
||||
|
||||
In order to access ``beforeCodeReload``, ``afterCodeReload``, ``hasModuleChanged``
|
||||
or ``hasAnyModuleChanged`` one must import the `hotcodereloading`:idx module.
|
||||
|
||||
**Usage in Native projects:**
|
||||
|
||||
Native projects using the hot code reloading option will be implicitly
|
||||
compiled with the `-d:useNimRtl` option and they will depend on both
|
||||
the ``nimrtl`` library and the ``nimhcr`` library which implements the
|
||||
hot code reloading run-time.
|
||||
|
||||
All modules of the project will be compiled to separate dynamic link
|
||||
libraries placed in the ``nimcache`` directory. Please note that during
|
||||
the execution of the program, the hot code reloading run-time will load
|
||||
only copies of these libraries in order to not interfere with any newly
|
||||
issued build commands.
|
||||
|
||||
The main module of the program is considered non-reloadable. Please note
|
||||
that procs from reloadable modules should not appear in the call stack of
|
||||
program while ``performCodeReload`` is being called. Thus, the main module
|
||||
is a suitable place for implementing a program loop capable of calling
|
||||
``performCodeReload``.
|
||||
|
||||
Please note that reloading won't be possible when any of the type definitions
|
||||
in the program has been changed. When closure iterators are used (directly or
|
||||
through async code), the reloaded refinitions will affect only newly created
|
||||
instances. Existing iterator instancess will execute their original code to
|
||||
completion.
|
||||
|
||||
**Usage in JavaScript projects:**
|
||||
|
||||
Once your code is compiled for hot reloading, the ``nim-livereload`` NPM
|
||||
package provides a convenient solution for implementing the actual reloading
|
||||
in the browser using a framework such as [LiveReload](http://livereload.com/)
|
||||
or [BrowserSync](https://browsersync.io/).
|
||||
|
||||
|
||||
DynlibOverride
|
||||
@@ -531,7 +446,7 @@ Backend language options
|
||||
The typical compiler usage involves using the ``compile`` or ``c`` command to
|
||||
transform a ``.nim`` file into one or more ``.c`` files which are then
|
||||
compiled with the platform's C compiler into a static binary. However there
|
||||
are other commands to compile to C++, Objective-C or Javascript. More details
|
||||
are other commands to compile to C++, Objective-C or JavaScript. More details
|
||||
can be read in the `Nim Backend Integration document <backends.html>`_.
|
||||
|
||||
|
||||
|
||||
178
doc/nims.rst
178
doc/nims.rst
@@ -28,8 +28,9 @@ previous settings):
|
||||
|
||||
For available procs and implementation details see `nimscript <nimscript.html>`_.
|
||||
|
||||
|
||||
Limitations
|
||||
=================================
|
||||
===========
|
||||
|
||||
NimScript is subject to some limitations caused by the implementation of the VM
|
||||
(virtual machine):
|
||||
@@ -47,18 +48,50 @@ NimScript is subject to some limitations caused by the implementation of the VM
|
||||
* More than one level of `ref` is generally not supported (for example, the type
|
||||
`ref ref int`).
|
||||
|
||||
* multimethods are not available.
|
||||
* Multimethods are not available.
|
||||
|
||||
Given the above restrictions, at least the following modules are available:
|
||||
* ``random.randomize()`` requires an ``int64`` explicitly passed as argument, you *must* pass a Seed integer.
|
||||
|
||||
* ``unicode`` can be imported, but not ``unidecode``.
|
||||
|
||||
|
||||
Standard library modules
|
||||
========================
|
||||
|
||||
At least the following standard library modules are available:
|
||||
|
||||
* `macros <macros.html>`_
|
||||
* `os <os.html>`_
|
||||
* `strutils <strutils.html>`_
|
||||
* `math <math.html>`_
|
||||
* `distros <distros.html>`_
|
||||
* `sugar <sugar.html>`_
|
||||
* `algorithm <algorithm.html>`_
|
||||
* `base64 <base64.html>`_
|
||||
* `bitops <bitops.html>`_
|
||||
* `chains <chains.html>`_
|
||||
* `colors <colors.html>`_
|
||||
* `complex <complex.html>`_
|
||||
* `htmlgen <htmlgen.html>`_
|
||||
* `httpcore <httpcore.html>`_
|
||||
* `lenientops <lenientops.html>`_
|
||||
* `mersenne <mersenne.html>`_
|
||||
* `options <options.html>`_
|
||||
* `parseutils <parseutils.html>`_
|
||||
* `punycode <punycode.html>`_
|
||||
* `random <punycode.html>`_
|
||||
* `stats <stats.html>`_
|
||||
* `strformat <strformat.html>`_
|
||||
* `strmisc <strmisc.html>`_
|
||||
* `strscans <strscans.html>`_
|
||||
* `unicode <unicode.html>`_
|
||||
* `uri <uri.html>`_
|
||||
* `std/editdistance <editdistance.html>`_
|
||||
* `std/wordwrap <wordwrap.html>`_
|
||||
* `std/sums <sums.html>`_
|
||||
|
||||
In addition to the standard Nim syntax (`system <system.html>`_
|
||||
module), NimScripts support the procs and templates defined in the
|
||||
In addition to the standard Nim syntax (`system <system.html>`_ module),
|
||||
NimScripts support the procs and templates defined in the
|
||||
`nimscript <nimscript.html>`_ module too.
|
||||
|
||||
|
||||
@@ -130,8 +163,6 @@ See the `Nimble readme <https://github.com/nim-lang/nimble#readme>`_
|
||||
for more information.
|
||||
|
||||
|
||||
|
||||
|
||||
Standalone NimScript
|
||||
====================
|
||||
|
||||
@@ -165,3 +196,136 @@ ends with ``.nims``:
|
||||
echo "hello world"
|
||||
|
||||
Use ``#!/usr/bin/env -S nim --hints:off`` to disable hints.
|
||||
|
||||
|
||||
Benefits
|
||||
========
|
||||
|
||||
Cross-Platform
|
||||
--------------
|
||||
|
||||
It is a cross-platform scripting language that can run where Nim can run,
|
||||
e.g. you can not run Batch or PowerShell on Linux or Mac,
|
||||
the Bash for Linux might not run on Mac,
|
||||
there are no unit tests tools for Batch, etc.
|
||||
|
||||
NimScript can detect on which platform, operating system,
|
||||
architecture, and even which Linux distribution is running on,
|
||||
allowing the same script to support a lot of systems.
|
||||
|
||||
See the following (incomplete) example:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
import distros
|
||||
|
||||
# Architectures.
|
||||
if defined(amd64):
|
||||
echo "Architecture is x86 64Bits"
|
||||
elif defined(i386):
|
||||
echo "Architecture is x86 32Bits"
|
||||
elif defined(arm):
|
||||
echo "Architecture is ARM"
|
||||
|
||||
# Operating Systems.
|
||||
if defined(linux):
|
||||
echo "Operating System is GNU Linux"
|
||||
elif defined(windows):
|
||||
echo "Operating System is Microsoft Windows"
|
||||
elif defined(macosx):
|
||||
echo "Operating System is Apple OS X"
|
||||
|
||||
# Distros.
|
||||
if detectOs(Ubuntu):
|
||||
echo "Distro is Ubuntu"
|
||||
elif detectOs(ArchLinux):
|
||||
echo "Distro is ArchLinux"
|
||||
elif detectOs(Debian):
|
||||
echo "Distro is Debian"
|
||||
|
||||
|
||||
Uniform Syntax
|
||||
--------------
|
||||
|
||||
The syntax, style, and rest of the ecosystem is the same as for compiled Nim,
|
||||
that means there is nothing new to learn, no context switch for developers.
|
||||
|
||||
|
||||
Powerful Metaprogramming
|
||||
------------------------
|
||||
|
||||
NimScript can use Nim's templates, macros, types, concepts, effect tracking system, and more,
|
||||
you can create modules that work on compiled Nim and also on interpreted NimScript.
|
||||
|
||||
``func`` will still check for side effects, ``debugEcho`` also works as expected,
|
||||
making it ideal for functional scripting metaprogramming.
|
||||
|
||||
This is an example of a third party module that uses macros and templates to
|
||||
translate text strings on unmodified NimScript:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
import nimterlingua
|
||||
nimterlingua("translations.cfg")
|
||||
echo "cat" # Run with -d:RU becomes "kot", -d:ES becomes "gato", ...
|
||||
|
||||
translations.cfg
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[cat]
|
||||
ES = gato
|
||||
PT = minino
|
||||
RU = kot
|
||||
FR = chat
|
||||
|
||||
|
||||
* `Nimterlingua <https://nimble.directory/pkg/nimterlingua>`_
|
||||
|
||||
|
||||
Graceful Fallback
|
||||
-----------------
|
||||
|
||||
Some features of compiled Nim may not work on NimScript,
|
||||
but often a graceful and seamless fallback degradation is used.
|
||||
|
||||
See the following NimScript:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
if likely(true):
|
||||
discard
|
||||
elif unlikely(false):
|
||||
discard
|
||||
|
||||
proc foo() {.compiletime.} = echo NimVersion
|
||||
|
||||
static:
|
||||
echo CompileDate
|
||||
|
||||
|
||||
``likely()``, ``unlikely()``, ``static:`` and ``{.compiletime.}``
|
||||
will produce no code at all when run on NimScript,
|
||||
but still no error nor warning is produced and the code just works.
|
||||
|
||||
Evolving Scripting language
|
||||
---------------------------
|
||||
|
||||
NimScript evolves together with Nim,
|
||||
`occasionally new features might become available on NimScript <https://github.com/nim-lang/Nim/pulls?utf8=%E2%9C%93&q=nimscript>`_ ,
|
||||
adapted from compiled Nim or added as new features on both.
|
||||
|
||||
Scripting Language with a Package Manager
|
||||
-----------------------------------------
|
||||
|
||||
You can create your own modules to be compatible with NimScript,
|
||||
and check `Nimble <https://nimble.directory>`_
|
||||
to search for third party modules that may work on NimScript.
|
||||
|
||||
DevOps Scripting
|
||||
----------------
|
||||
|
||||
You can use NimScript to deploy to production, run tests, build projects, do benchmarks,
|
||||
generate documentation, and all kinds of DevOps/SysAdmin specific tasks.
|
||||
|
||||
* `An example of a third party NimScript that can be used as a project-agnostic tool. <https://github.com/kaushalmodi/nim_config#list-available-tasks>`_
|
||||
|
||||
@@ -4,6 +4,10 @@ Tools available with Nim
|
||||
|
||||
The standard distribution ships with the following tools:
|
||||
|
||||
- | `Hot code reloading <hcr.html>`_
|
||||
| The "Hot code reloading" feature is built into the compiler but has its own
|
||||
document explaining how it works.
|
||||
|
||||
- | `Documentation generator <docgen.html>`_
|
||||
| The builtin document generator ``nim doc`` generates HTML documentation
|
||||
from ``.nim`` source files.
|
||||
@@ -16,5 +20,12 @@ The standard distribution ships with the following tools:
|
||||
- | `C2nim <https://github.com/nim-lang/c2nim/blob/master/doc/c2nim.rst>`_
|
||||
| C to Nim source converter. Translates C header files to Nim.
|
||||
|
||||
- | `niminst <https://nim-lang.org/docs/niminst.html>`_
|
||||
| niminst is a tool to generate an installer for a Nim program.
|
||||
|
||||
- | `nimgrep <nimgrep.html>`_
|
||||
| Nim search and replace utility.
|
||||
|
||||
- | nimpretty
|
||||
| ``nimpretty`` is a Nim source code beautifier,
|
||||
to format code according to the official style guide.
|
||||
|
||||
@@ -56,7 +56,7 @@ To compile a release version use::
|
||||
nim c -d:release greetings.nim
|
||||
|
||||
By default the Nim compiler generates a large amount of runtime checks
|
||||
aiming for your debugging pleasure. With ``-d:release`` these checks are
|
||||
aiming for your debugging pleasure. With ``-d:release`` some checks are
|
||||
`turned off and optimizations are turned on
|
||||
<nimc.html#compiler-usage-compile-time-symbols>`_.
|
||||
|
||||
|
||||
@@ -605,7 +605,7 @@ performed before the expression is passed to the template.
|
||||
If the template has no explicit return type,
|
||||
``void`` is used for consistency with procs and methods.
|
||||
|
||||
To pass a block of statements to a template, use 'untyped' for the last parameter:
|
||||
To pass a block of statements to a template, use ``untyped`` for the last parameter:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
89
koch.nim
89
koch.nim
@@ -121,12 +121,13 @@ proc csource(args: string) =
|
||||
"--main:compiler/nim.nim compiler/installer.ini $1") %
|
||||
[args, VersionAsString, compileNimInst])
|
||||
|
||||
proc bundleC2nim() =
|
||||
proc bundleC2nim(args: string) =
|
||||
if not dirExists("dist/c2nim/.git"):
|
||||
exec("git clone https://github.com/nim-lang/c2nim.git dist/c2nim")
|
||||
nimCompile("dist/c2nim/c2nim", options = "--noNimblePath --path:.")
|
||||
nimCompile("dist/c2nim/c2nim",
|
||||
options = "--noNimblePath --path:. " & args)
|
||||
|
||||
proc bundleNimbleExe(latest: bool) =
|
||||
proc bundleNimbleExe(latest: bool, args: string) =
|
||||
if not dirExists("dist/nimble/.git"):
|
||||
exec("git clone https://github.com/nim-lang/nimble.git dist/nimble")
|
||||
if not latest:
|
||||
@@ -134,9 +135,10 @@ proc bundleNimbleExe(latest: bool) =
|
||||
exec("git fetch")
|
||||
exec("git checkout " & NimbleStableCommit)
|
||||
# installer.ini expects it under $nim/bin
|
||||
nimCompile("dist/nimble/src/nimble.nim", options = "-d:release --nilseqs:on")
|
||||
nimCompile("dist/nimble/src/nimble.nim",
|
||||
options = "-d:release --nilseqs:on " & args)
|
||||
|
||||
proc buildNimble(latest: bool) =
|
||||
proc buildNimble(latest: bool, args: string) =
|
||||
# if koch is used for a tar.xz, build the dist/nimble we shipped
|
||||
# with the tarball:
|
||||
var installDir = "dist/nimble"
|
||||
@@ -157,29 +159,32 @@ proc buildNimble(latest: bool) =
|
||||
else:
|
||||
exec("git fetch")
|
||||
exec("git checkout " & NimbleStableCommit)
|
||||
nimCompile(installDir / "src/nimble.nim", options = "--noNimblePath --nilseqs:on -d:release")
|
||||
nimCompile(installDir / "src/nimble.nim",
|
||||
options = "--noNimblePath --nilseqs:on -d:release " & args)
|
||||
|
||||
proc bundleNimsuggest() =
|
||||
nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim", options = "-d:release -d:danger")
|
||||
proc bundleNimsuggest(args: string) =
|
||||
nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim",
|
||||
options = "-d:release -d:danger " & args)
|
||||
|
||||
proc buildVccTool() =
|
||||
nimCompileFold("Compile Vcc", "tools/vccexe/vccexe.nim")
|
||||
proc buildVccTool(args: string) =
|
||||
nimCompileFold("Compile Vcc", "tools/vccexe/vccexe.nim ", options = args)
|
||||
|
||||
proc bundleWinTools() =
|
||||
nimCompile("tools/finish.nim", outputDir = "")
|
||||
proc bundleWinTools(args: string) =
|
||||
nimCompile("tools/finish.nim", outputDir = "", options = args)
|
||||
|
||||
buildVccTool()
|
||||
nimCompile("tools/nimgrab.nim", options = "-d:ssl")
|
||||
nimCompile("tools/nimgrep.nim")
|
||||
bundleC2nim()
|
||||
buildVccTool(args)
|
||||
nimCompile("tools/nimgrab.nim", options = "-d:ssl " & args)
|
||||
nimCompile("tools/nimgrep.nim", options = args)
|
||||
bundleC2nim(args)
|
||||
when false:
|
||||
# not yet a tool worth including
|
||||
nimCompile(r"tools\downloader.nim", options = r"--cc:vcc --app:gui -d:ssl --noNimblePath --path:..\ui")
|
||||
nimCompile(r"tools\downloader.nim",
|
||||
options = r"--cc:vcc --app:gui -d:ssl --noNimblePath --path:..\ui " & args)
|
||||
|
||||
proc zip(latest: bool; args: string) =
|
||||
bundleNimbleExe(latest)
|
||||
bundleNimsuggest()
|
||||
bundleWinTools()
|
||||
bundleNimbleExe(latest, args)
|
||||
bundleNimsuggest(args)
|
||||
bundleWinTools(args)
|
||||
nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" %
|
||||
[VersionAsString, compileNimInst])
|
||||
exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" %
|
||||
@@ -203,17 +208,20 @@ proc buildTool(toolname, args: string) =
|
||||
nimexec("cc $# $#" % [args, toolname])
|
||||
copyFile(dest="bin" / splitFile(toolname).name.exe, source=toolname.exe)
|
||||
|
||||
proc buildTools() =
|
||||
bundleNimsuggest()
|
||||
nimCompileFold("Compile nimgrep", "tools/nimgrep.nim", options = "-d:release")
|
||||
when defined(windows): buildVccTool()
|
||||
nimCompileFold("Compile nimpretty", "nimpretty/nimpretty.nim", options = "-d:release")
|
||||
nimCompileFold("Compile nimfind", "tools/nimfind.nim", options = "-d:release")
|
||||
proc buildTools(args: string = "") =
|
||||
bundleNimsuggest(args)
|
||||
nimCompileFold("Compile nimgrep", "tools/nimgrep.nim",
|
||||
options = "-d:release " & args)
|
||||
when defined(windows): buildVccTool(args)
|
||||
nimCompileFold("Compile nimpretty", "nimpretty/nimpretty.nim",
|
||||
options = "-d:release " & args)
|
||||
nimCompileFold("Compile nimfind", "tools/nimfind.nim",
|
||||
options = "-d:release " & args)
|
||||
|
||||
proc nsis(latest: bool; args: string) =
|
||||
bundleNimbleExe(latest)
|
||||
bundleNimsuggest()
|
||||
bundleWinTools()
|
||||
bundleNimbleExe(latest, args)
|
||||
bundleNimsuggest(args)
|
||||
bundleWinTools(args)
|
||||
# make sure we have generated the niminst executables:
|
||||
buildTool("tools/niminst/niminst", args)
|
||||
#buildTool("tools/nimgrep", args)
|
||||
@@ -279,7 +287,7 @@ proc boot(args: string) =
|
||||
# default to use the 'c' command:
|
||||
let useCpp = getEnv("NIM_COMPILE_TO_CPP", "false") == "true"
|
||||
let smartNimcache = (if "release" in args or "danger" in args: "nimcache/r_" else: "nimcache/d_") &
|
||||
hostOs & "_" & hostCpu
|
||||
hostOS & "_" & hostCPU
|
||||
|
||||
let nimStart = findStartNim()
|
||||
for i in 0..2:
|
||||
@@ -427,9 +435,10 @@ proc temp(args: string) =
|
||||
let d = getAppDir()
|
||||
var output = d / "compiler" / "nim".exe
|
||||
var finalDest = d / "bin" / "nim_temp".exe
|
||||
# 125 is the magic number to tell git bisect to skip the current
|
||||
# commit.
|
||||
let (bootArgs, programArgs) = splitArgs(args)
|
||||
# 125 is the magic number to tell git bisect to skip the current commit.
|
||||
var (bootArgs, programArgs) = splitArgs(args)
|
||||
if "doc" notin programArgs and "threads" notin programArgs and "js" notin programArgs:
|
||||
bootArgs.add " -d:leanCompiler"
|
||||
let nimexec = findNim()
|
||||
exec(nimexec & " c -d:debug --debugger:native " & bootArgs & " " & (d / "compiler" / "nim"), 125)
|
||||
copyExe(output, finalDest)
|
||||
@@ -603,17 +612,17 @@ when isMainModule:
|
||||
of "test", "tests": tests(op.cmdLineRest)
|
||||
of "temp": temp(op.cmdLineRest)
|
||||
of "xtemp": xtemp(op.cmdLineRest)
|
||||
of "wintools": bundleWinTools()
|
||||
of "nimble": buildNimble(latest)
|
||||
of "nimsuggest": bundleNimsuggest()
|
||||
of "wintools": bundleWinTools(op.cmdLineRest)
|
||||
of "nimble": buildNimble(latest, op.cmdLineRest)
|
||||
of "nimsuggest": bundleNimsuggest(op.cmdLineRest)
|
||||
of "toolsnonimble":
|
||||
buildTools()
|
||||
buildTools(op.cmdLineRest)
|
||||
of "tools":
|
||||
buildTools()
|
||||
buildNimble(latest)
|
||||
buildTools(op.cmdLineRest)
|
||||
buildNimble(latest, op.cmdLineRest)
|
||||
of "pushcsource", "pushcsources": pushCsources()
|
||||
of "valgrind": valgrind(op.cmdLineRest)
|
||||
of "c2nim": bundleC2nim()
|
||||
of "c2nim": bundleC2nim(op.cmdLineRest)
|
||||
else: showHelp()
|
||||
break
|
||||
of cmdEnd: break
|
||||
|
||||
@@ -16,7 +16,10 @@ when defined(hotcodereloading):
|
||||
proc hasAnyModuleChanged*(): bool = hcrReloadNeeded()
|
||||
|
||||
when not defined(JS):
|
||||
template performCodeReload* = hcrPerformCodeReload()
|
||||
template performCodeReload* =
|
||||
when isMainModule:
|
||||
{.warning: "Code residing in the main module will not be changed from calling a code-reload".}
|
||||
hcrPerformCodeReload()
|
||||
else:
|
||||
template performCodeReload* = discard
|
||||
else:
|
||||
|
||||
@@ -499,7 +499,7 @@ type
|
||||
filename*: string
|
||||
line*,column*: int
|
||||
|
||||
proc `$`*(arg: Lineinfo): string =
|
||||
proc `$`*(arg: LineInfo): string =
|
||||
# BUG: without `result = `, gives compile error
|
||||
result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")"
|
||||
|
||||
@@ -748,17 +748,17 @@ proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} =
|
||||
result.add newLit(x)
|
||||
|
||||
proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
|
||||
var bracket = nnkBracket.newTree
|
||||
let bracket = nnkBracket.newTree
|
||||
for x in arg:
|
||||
bracket.add newLit(x)
|
||||
|
||||
result = nnkCall.newTree(
|
||||
nnkBracketExpr.newTree(
|
||||
nnkAccQuoted.newTree( bindSym"@" ),
|
||||
getTypeInst( bindSym"T" )
|
||||
),
|
||||
result = nnkPrefix.newTree(
|
||||
bindSym"@",
|
||||
bracket
|
||||
)
|
||||
if arg.len == 0:
|
||||
# add type cast for empty seq
|
||||
var typ = getTypeInst(typeof(arg))[1]
|
||||
result = newCall(typ,result)
|
||||
|
||||
proc newLit*[T](s: set[T]): NimNode {.compileTime.} =
|
||||
result = nnkCurly.newTree
|
||||
@@ -988,16 +988,16 @@ proc newBlockStmt*(label, body: NimNode): NimNode {.compileTime.} =
|
||||
## Create a new block statement with label
|
||||
return newNimNode(nnkBlockStmt).add(label, body)
|
||||
|
||||
proc newBlockStmt*(body: NimNode): NimNode {.compiletime.} =
|
||||
proc newBlockStmt*(body: NimNode): NimNode {.compileTime.} =
|
||||
## Create a new block: stmt
|
||||
return newNimNode(nnkBlockStmt).add(newEmptyNode(), body)
|
||||
|
||||
proc newVarStmt*(name, value: NimNode): NimNode {.compiletime.} =
|
||||
proc newVarStmt*(name, value: NimNode): NimNode {.compileTime.} =
|
||||
## Create a new var stmt
|
||||
return newNimNode(nnkVarSection).add(
|
||||
newNimNode(nnkIdentDefs).add(name, newNimNode(nnkEmpty), value))
|
||||
|
||||
proc newLetStmt*(name, value: NimNode): NimNode {.compiletime.} =
|
||||
proc newLetStmt*(name, value: NimNode): NimNode {.compileTime.} =
|
||||
## Create a new let stmt
|
||||
return newNimNode(nnkLetSection).add(
|
||||
newNimNode(nnkIdentDefs).add(name, newNimNode(nnkEmpty), value))
|
||||
@@ -1095,7 +1095,7 @@ proc newProc*(name = newEmptyNode();
|
||||
body)
|
||||
|
||||
proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]):
|
||||
NimNode {.compiletime.} =
|
||||
NimNode {.compileTime.} =
|
||||
## Constructor for ``if`` statements.
|
||||
##
|
||||
## .. code-block:: nim
|
||||
@@ -1233,7 +1233,7 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
|
||||
else:
|
||||
badNodeKind someProc, "body="
|
||||
|
||||
proc basename*(a: NimNode): NimNode {.compiletime, benign.}
|
||||
proc basename*(a: NimNode): NimNode {.compileTime, benign.}
|
||||
|
||||
proc `$`*(node: NimNode): string {.compileTime.} =
|
||||
## Get the string of an identifier node
|
||||
@@ -1302,7 +1302,8 @@ proc basename*(a: NimNode): NimNode =
|
||||
of nnkIdent: return a
|
||||
of nnkPostfix, nnkPrefix: return a[1]
|
||||
else:
|
||||
quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
|
||||
error("Do not know how to get basename of (" & treeRepr(a) & ")\n" &
|
||||
repr(a), a)
|
||||
|
||||
proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
|
||||
case a.kind
|
||||
@@ -1311,7 +1312,8 @@ proc `basename=`*(a: NimNode; val: string) {.compileTime.}=
|
||||
of nnkPostfix, nnkPrefix:
|
||||
a[1] = ident(val)
|
||||
else:
|
||||
quit "Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a)
|
||||
error("Do not know how to get basename of (" & treeRepr(a) & ")\n" &
|
||||
repr(a), a)
|
||||
|
||||
proc postfix*(node: NimNode; op: string): NimNode {.compileTime.} =
|
||||
newNimNode(nnkPostfix).add(ident(op), node)
|
||||
@@ -1394,7 +1396,7 @@ else:
|
||||
else:
|
||||
result = false
|
||||
|
||||
proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
|
||||
proc hasArgOfName*(params: NimNode; name: string): bool {.compileTime.}=
|
||||
## Search nnkFormalParams for an argument.
|
||||
expectKind(params, nnkFormalParams)
|
||||
for i in 1 ..< params.len:
|
||||
@@ -1402,7 +1404,7 @@ proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}=
|
||||
if name.eqIdent( $ node[0]):
|
||||
return true
|
||||
|
||||
proc addIdentIfAbsent*(dest: NimNode, ident: string) {.compiletime.} =
|
||||
proc addIdentIfAbsent*(dest: NimNode, ident: string) {.compileTime.} =
|
||||
## Add ident to dest if it is not present. This is intended for use
|
||||
## with pragmas.
|
||||
for node in dest.children:
|
||||
|
||||
@@ -52,17 +52,30 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} =
|
||||
result = orig +! sizeof(RefHeader)
|
||||
else:
|
||||
result = alloc0(s) +! sizeof(RefHeader)
|
||||
inc allocs
|
||||
when hasThreadSupport:
|
||||
atomicInc allocs
|
||||
else:
|
||||
inc allocs
|
||||
|
||||
proc nimDecWeakRef(p: pointer) {.compilerRtl.} =
|
||||
dec head(p).rc
|
||||
when hasThreadSupport:
|
||||
atomicDec head(p).rc
|
||||
else:
|
||||
dec head(p).rc
|
||||
|
||||
proc nimIncWeakRef(p: pointer) {.compilerRtl.} =
|
||||
inc head(p).rc
|
||||
when hasThreadSupport:
|
||||
atomicInc head(p).rc
|
||||
else:
|
||||
inc head(p).rc
|
||||
|
||||
proc nimRawDispose(p: pointer) {.compilerRtl.} =
|
||||
when not defined(nimscript):
|
||||
if head(p).rc != 0:
|
||||
when hasThreadSupport:
|
||||
let hasDanglingRefs = atomicLoadN(addr head(p).rc, ATOMIC_RELAXED) != 0
|
||||
else:
|
||||
let hasDanglingRefs = head(p).rc != 0
|
||||
if hasDanglingRefs:
|
||||
cstderr.rawWrite "[FATAL] dangling references exist\n"
|
||||
quit 1
|
||||
when defined(useMalloc):
|
||||
@@ -70,14 +83,27 @@ proc nimRawDispose(p: pointer) {.compilerRtl.} =
|
||||
else:
|
||||
dealloc(p -! sizeof(RefHeader))
|
||||
if allocs > 0:
|
||||
dec allocs
|
||||
when hasThreadSupport:
|
||||
atomicDec allocs
|
||||
else:
|
||||
dec allocs
|
||||
else:
|
||||
cstderr.rawWrite "[FATAL] unpaired dealloc\n"
|
||||
quit 1
|
||||
|
||||
template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x))
|
||||
#proc dispose*(x: pointer) = nimRawDispose(x)
|
||||
|
||||
proc nimDestroyAndDispose(p: pointer) {.compilerRtl.} =
|
||||
let d = cast[ptr PNimType](p)[].destructor
|
||||
if d != nil: cast[DestructorProc](d)(p)
|
||||
when false:
|
||||
cstderr.rawWrite cast[ptr PNimType](p)[].name
|
||||
cstderr.rawWrite "\n"
|
||||
if d == nil:
|
||||
cstderr.rawWrite "bah, nil\n"
|
||||
else:
|
||||
cstderr.rawWrite "has destructor!\n"
|
||||
nimRawDispose(p)
|
||||
|
||||
proc isObj(obj: PNimType, subclass: cstring): bool {.compilerproc.} =
|
||||
|
||||
@@ -170,9 +170,14 @@ proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffec
|
||||
proc setLen[T](s: var seq[T], newlen: Natural) =
|
||||
{.noSideEffect.}:
|
||||
if newlen < s.len:
|
||||
shrink(s, newLen)
|
||||
shrink(s, newlen)
|
||||
else:
|
||||
grow(s, newLen, default(T))
|
||||
let oldLen = s.len
|
||||
if newlen <= oldLen: return
|
||||
var xu = cast[ptr NimSeqV2[T]](addr s)
|
||||
if xu.p == nil or xu.p.cap < newlen:
|
||||
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T)))
|
||||
xu.len = newlen
|
||||
|
||||
when false:
|
||||
proc resize[T](s: var NimSeqV2[T]) =
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
when false:
|
||||
# these are to be implemented or changed in the code generator.
|
||||
|
||||
#proc rawNewStringNoInit(space: int): NimString {.compilerProc.}
|
||||
#proc rawNewStringNoInit(space: int): NimString {.compilerproc.}
|
||||
# seems to be unused.
|
||||
proc copyDeepString(src: NimString): NimString {.inline.}
|
||||
# ----------------- sequences ----------------------------------------------
|
||||
|
||||
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.}
|
||||
proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.}
|
||||
proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
|
||||
compilerRtl.}
|
||||
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.}
|
||||
@@ -109,7 +109,7 @@ proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} =
|
||||
s.p.data[s.len+1] = '\0'
|
||||
inc s.len
|
||||
|
||||
proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} =
|
||||
proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} =
|
||||
if len <= 0:
|
||||
result = NimStringV2(len: 0, p: nil)
|
||||
else:
|
||||
@@ -126,7 +126,7 @@ proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
|
||||
if str == nil: toNimStr(str, 0)
|
||||
else: toNimStr(str, str.len)
|
||||
|
||||
proc nimToCStringConv(s: NimStringV2): cstring {.compilerProc, nonReloadable, inline.} =
|
||||
proc nimToCStringConv(s: NimStringV2): cstring {.compilerproc, nonReloadable, inline.} =
|
||||
if s.len == 0: result = cstring""
|
||||
else: result = cstring(unsafeAddr s.p.data)
|
||||
|
||||
@@ -141,7 +141,7 @@ proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
|
||||
dest.p.data[dest.len+1] = '\0'
|
||||
inc dest.len
|
||||
|
||||
proc rawNewString(space: int): NimStringV2 {.compilerProc.} =
|
||||
proc rawNewString(space: int): NimStringV2 {.compilerproc.} =
|
||||
# this is also 'system.newStringOfCap'.
|
||||
if space <= 0:
|
||||
result = NimStringV2(len: 0, p: nil)
|
||||
@@ -152,7 +152,7 @@ proc rawNewString(space: int): NimStringV2 {.compilerProc.} =
|
||||
p.cap = space
|
||||
result = NimStringV2(len: 0, p: p)
|
||||
|
||||
proc mnewString(len: int): NimStringV2 {.compilerProc.} =
|
||||
proc mnewString(len: int): NimStringV2 {.compilerproc.} =
|
||||
if len <= 0:
|
||||
result = NimStringV2(len: 0, p: nil)
|
||||
else:
|
||||
|
||||
@@ -9,6 +9,28 @@
|
||||
|
||||
## This module implements an algorithm to compute the
|
||||
## `diff`:idx: between two sequences of lines.
|
||||
##
|
||||
## A basic example of ``diffInt`` on 2 arrays of integers:
|
||||
##
|
||||
## .. code::nim
|
||||
##
|
||||
## import experimental/diff
|
||||
## echo diffInt([0, 1, 2, 3, 4, 5, 6, 7, 8], [-1, 1, 2, 3, 4, 5, 666, 7, 42])
|
||||
##
|
||||
## Another short example of ``diffText`` to diff strings:
|
||||
##
|
||||
## .. code::nim
|
||||
##
|
||||
## import experimental/diff
|
||||
## # 2 samples of text for testing (from "The Call of Cthulhu" by Lovecraft)
|
||||
## let txt0 = """I have looked upon all the universe has to hold of horror,
|
||||
## even skies of spring and flowers of summer must ever be poison to me."""
|
||||
## let txt1 = """I have looked upon all your code has to hold of bugs,
|
||||
## even skies of spring and flowers of summer must ever be poison to me."""
|
||||
##
|
||||
## echo diffText(txt0, txt1)
|
||||
##
|
||||
## - To learn more see `Diff on Wikipedia. <http://wikipedia.org/wiki/Diff>`_
|
||||
|
||||
# code owner: Arne Döring
|
||||
#
|
||||
@@ -257,7 +279,7 @@ proc diffInt*(arrayA, arrayB: openArray[int]): seq[Item] =
|
||||
##
|
||||
## ``arrayB`` B-version of the numbers (usualy the new one)
|
||||
##
|
||||
## Returns a array of Items that describe the differences.
|
||||
## Returns a sequence of Items that describe the differences.
|
||||
|
||||
# The A-Version of the data (original data) to be compared.
|
||||
var dataA = newDiffData(@arrayA, arrayA.len)
|
||||
|
||||
@@ -70,7 +70,7 @@ proc newMapSlab(): ptr MapSlab =
|
||||
|
||||
iterator items(s: ptr MapSlab): ptr Map =
|
||||
let mapCount = (SlabBackendSize - sizeof(SlabMeta)) div sizeof(Map)
|
||||
for i in 0 .. <mapCount:
|
||||
for i in 0 ..< mapCount:
|
||||
yield s.maps[i].addr
|
||||
|
||||
var slabs: ptr MapSlab
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user