Compare commits

...

224 Commits

Author SHA1 Message Date
narimiran
88a0edba4b bump version to 0.20.2 2019-07-16 21:24:02 +02:00
Kaushal Modi
5a12553394 [bugfix] critbits styleCheck fix: consistent var naming (#11752)
(cherry picked from commit 44d80dd863)
2019-07-16 21:21:59 +02:00
Kaushal Modi
7469f5fa78 [bugfix] openssl styleCheck fix: consistent var naming (#11750)
(cherry picked from commit 3f7142fcfd)
2019-07-16 21:21:59 +02:00
Kaushal Modi
6bd301ca9c styleCheck fix: type naming: s/outType/OutType/ (#11749)
(cherry picked from commit 0b511b15ea)
2019-07-16 21:21:59 +02:00
Tristano Ajmone
0047f7cf4f Fix Typos in Internal Docs (#11735) [ci skip]
(cherry picked from commit 8550a8127c)
2019-07-16 21:21:47 +02:00
Araq
8be734bf46 fixes #11723
(cherry picked from commit 76f9ddb6ab)
2019-07-16 21:21:47 +02:00
narimiran
9b0cb52d74 styleCheck fix 2019-07-16 10:17:00 +02:00
Kartik Saranathan
1aefa74e46 fix broken link (#11736) [ci skip]
(cherry picked from commit ec9d0ee86d)
2019-07-15 16:18:34 +02:00
Araq
c0085bc2b7 styleCheck fixes [bugfix]
(cherry picked from commit dd7dd1b6dc)
2019-07-15 16:18:17 +02:00
Araq
43cba52ed8 styleCheck fixes [bugfix]
(cherry picked from commit ad67a46e5d)
2019-07-15 16:18:17 +02:00
Araq
d6d5dd30e9 styleCheck fixes [bugfix]
(cherry picked from commit 52cbd0c558)
2019-07-15 16:18:17 +02:00
Araq
073651237a newruntime: progress to make 'async' work with --newruntime
(cherry picked from commit edbe02b4e1)
2019-07-15 16:18:17 +02:00
Andreas Rumpf
f23ca7f539 makes devel green again
(cherry picked from commit df53c68a36)
2019-07-15 16:18:17 +02:00
Araq
2bf1985f1a 'nim check' stability improvements
(cherry picked from commit 58e0dad371)
2019-07-15 16:18:17 +02:00
Araq
abebc395d3 makes --styleCheck:error the default for the Nim compiler itself
(cherry picked from commit e06046ab51)
2019-07-15 16:18:17 +02:00
Araq
ac41aec348 minor update for the genode target
(cherry picked from commit fab70e4fe3)
2019-07-15 16:18:17 +02:00
Araq
575f688545 make 'koch temp' work again
(cherry picked from commit 44c12d9d20)
2019-07-15 16:18:17 +02:00
Araq
95fd0d0a6f [refactoring] moves transformation for 'spawn' into its own spawn.nim implementation
(cherry picked from commit 9db369063c)
2019-07-15 16:18:17 +02:00
alaviss
c4cbc5f449 os: confine osErrorMsg example to Linux (#11725)
POSIX does **not** define the exact number for each errors. This caused
a failure in docs building on Haiku.

(cherry picked from commit a59fb5b352)
2019-07-15 16:18:17 +02:00
Jacek Sieka
d5d661b70e times: use posix constant instead of import (#11692)
* times: use posix constant instead of import

* simplify clock id usage

(cherry picked from commit cf36e4a44a)
2019-07-15 16:18:17 +02:00
Andreas Rumpf
46cb304770 fixes #11715 (#11721)
(cherry picked from commit de1ede77fe)
2019-07-15 16:18:17 +02:00
narimiran
b4742cc496 fix failing tests 2019-07-15 09:39:35 +02:00
Araq
abd4330dd0 newruntime: refchecks:on|off switch
(cherry picked from commit 423e8cca91)
2019-07-15 08:06:47 +02:00
Araq
876d0aaade made --newruntime ready for multi-threading
(cherry picked from commit 416ba133ad)
2019-07-15 08:06:47 +02:00
Araq
8b6ea5d451 atomics.nim: minor improvements
(cherry picked from commit bb5ca0772e)
2019-07-15 08:06:47 +02:00
Araq
4771b08ab5 documented --newruntime switch
(cherry picked from commit f45a1b927a)
2019-07-15 08:06:47 +02:00
Araq
a59efc53f2 preparations to be able to use atomics in runtime_v2.nim
(cherry picked from commit 03ded27021)
2019-07-15 08:06:47 +02:00
Andreas Rumpf
ae04734031 fixes tcompiletimerange [bugfix] (#11720)
(cherry picked from commit 2895ad70c8)
2019-07-15 08:06:47 +02:00
Ico Doornekamp
75f0a7a7af Moved 'Case statement macros' documentation out of manual into manual_experimental (#11716)
(cherry picked from commit 38b836b49e)
2019-07-15 08:06:47 +02:00
Araq
d91b4b1bb7 [bugfix] VM: finally do inheritance properly
(cherry picked from commit 7606efc0bf)
2019-07-15 08:06:46 +02:00
Araq
5a11ae85b1 document 'nim' defines prefix
(cherry picked from commit 13253f4154)
2019-07-15 08:03:53 +02:00
Araq
8eb66f6c6b nimpretty: implement a --maxLineLen command line option
(cherry picked from commit 5bf3734984)
2019-07-15 08:03:53 +02:00
Araq
6f214131a2 JS codegen: supports toOpenArray [bugfix]
(cherry picked from commit eaf4b42ff9)
2019-07-15 08:03:53 +02:00
Araq
d2f9a40b3c koch temp bugfix
(cherry picked from commit 511b6ae27c)
2019-07-15 08:03:53 +02:00
Miran
622eadff52 nimpretty: fix #11700, no extra newlines (#11714)
(cherry picked from commit 2f0a50fa51)
2019-07-15 08:03:53 +02:00
Dominik Picheta
93b58ebe1b Give NilAccessError a message (#11706)
(cherry picked from commit b8be1ccb85)
2019-07-15 08:03:16 +02:00
Andreas Rumpf
f7b061f597 make async tests green again
(cherry picked from commit ac458448d8)
2019-07-15 08:03:16 +02:00
Araq
e34a318b9f minor style changes
(cherry picked from commit cb3c3c306c)
2019-07-15 08:03:16 +02:00
Araq
641d1e1175 minor style changes
(cherry picked from commit 38bdf1cd7f)
2019-07-15 08:03:16 +02:00
Araq
07505d87e2 make tests green again
(cherry picked from commit b2f9447896)
2019-07-15 08:03:16 +02:00
Araq
6ffe82a5aa there is only one style -- my style
(cherry picked from commit 87a2ced1ba)
2019-07-15 08:02:24 +02:00
Araq
2b4a7ff6f6 fixes linter regressions
(cherry picked from commit 73cc029fec)
2019-07-15 07:59:14 +02:00
Araq
3d33e96388 make editdistance work with --styleCheck:error
(cherry picked from commit dc0bbba3fa)
2019-07-15 07:59:14 +02:00
Araq
19225b71ab fixes #10901
(cherry picked from commit 78174857f8)
2019-07-15 07:59:14 +02:00
Araq
e5e2b067f4 fixes #11683
(cherry picked from commit b03421d087)
2019-07-15 07:59:14 +02:00
Araq
3866e3302a make more parts of the stdlib compile with --styleCheck:error
(cherry picked from commit bc7733827d)
2019-07-15 07:59:14 +02:00
Araq
607ce16acf fixes #11691
(cherry picked from commit 6663dbbe57)
2019-07-15 07:59:14 +02:00
Araq
694d57c899 styleCheck: make the compiler and large parts of the stdlib compatible with --styleCheck:error
(cherry picked from commit c94647aeca)
2019-07-15 07:58:45 +02:00
Araq
ed627cff3a linter: refactorings
(cherry picked from commit 18182e4bfd)
2019-07-15 07:58:27 +02:00
Araq
8a5860d196 style changes
(cherry picked from commit ff4a9a226f)
2019-07-15 07:58:27 +02:00
Araq
b5ab4dabe2 nim styleChecker: implemented all the missing features (bugfix)
(cherry picked from commit bd689849f2)
2019-07-15 07:58:27 +02:00
Araq
b3b10ec849 minor system.nim style changes
(cherry picked from commit c6c9e30379)
2019-07-15 07:58:27 +02:00
Araq
6ec566cdd8 koch temp: use -d:leanCompiler if possible
(cherry picked from commit 7a39eb13a5)
2019-07-15 07:58:27 +02:00
Miran
c4e26f7400 [other] prettify collections (#11695)
(cherry picked from commit 2255d8795b)
2019-07-15 07:58:27 +02:00
Araq
ab98141c75 newruntime: async progress
(cherry picked from commit 55e8aefbea)
2019-07-15 07:58:27 +02:00
Araq
14c6e3a6ab control flow graph: don't ignore yield statements
(cherry picked from commit 73c570d1d9)
2019-07-15 07:58:27 +02:00
Araq
808746e864 newruntime: map system.reset to =destroy
(cherry picked from commit ad58ed344a)
2019-07-15 07:58:27 +02:00
Arne Döring
386f126dd7 closes #11551 (#11693)
(cherry picked from commit eb059fa848)
2019-07-15 07:58:26 +02:00
Araq
ce9eaee303 newruntime: 'owned distinct' is a valid construct (required by async)
(cherry picked from commit a85e20c125)
2019-07-15 07:58:26 +02:00
Araq
7c7bab0626 make tests green again
(cherry picked from commit fa982b43f0)
2019-07-15 07:58:26 +02:00
narimiran
da2a149522 nimpretty: more comment indentation fixes
(cherry picked from commit 96d49608c0)
2019-07-15 07:58:26 +02:00
Araq
8afe92a1ab nimpretty: more improvements
(cherry picked from commit eefcea8892)
2019-07-15 07:58:26 +02:00
Araq
2a0c9dc063 nimpretty: code cleanups
(cherry picked from commit fc34193483)
2019-07-15 07:58:26 +02:00
Araq
e0b4d034bc nimpretty: yet another improvement
(cherry picked from commit 0193c8ad2e)
2019-07-15 07:58:26 +02:00
Araq
12baecb42d async: more 'owned' annotations
(cherry picked from commit 7993d61d00)
2019-07-15 07:58:26 +02:00
Araq
a144bc96b9 asyncmacro produces 'owned' types
(cherry picked from commit 2c2f2de614)
2019-07-15 07:58:26 +02:00
Araq
e0991c4bcf bugfix: allow type conversions between 'owned ref' types
(cherry picked from commit f64179e7fe)
2019-07-15 07:58:26 +02:00
Araq
40c2f504fb better error message for invalid types
(cherry picked from commit 7c62de0cb7)
2019-07-15 07:58:26 +02:00
Arne Döring
80e51bb407 Offsetof fixes (#11690)
* first fixes

* more tests and fixes

* code normalization

(cherry picked from commit 11dad688fe)
2019-07-15 07:58:26 +02:00
Miran
9b217c10e6 [other] test more packages (#11687)
(cherry picked from commit b50ae6817a)
2019-07-15 07:58:26 +02:00
Andreas Rumpf
78b665cc7f fixes #11530
(cherry picked from commit 124a584eb2)
2019-07-15 07:58:26 +02:00
Timothee Cour
092154de02 [refactoring] nimbase.h: remove dead code which was confusing (#11682)
(cherry picked from commit 655cb08461)
2019-07-08 11:33:20 +02:00
Jacek Sieka
0646d6a720 dynlib: use posix module (#11623)
(cherry picked from commit 9e7d885f35)
2019-07-08 11:33:20 +02:00
Miran
585a08548a nimpretty: don't introduce extra spaces after a colon or a comma (#11672)
(cherry picked from commit d0616ed115)
2019-07-08 11:33:20 +02:00
Araq
f77bc2522f fixes #11350
(cherry picked from commit 085a21ebf4)
2019-07-08 11:33:20 +02:00
Andreas Rumpf
5cc49aad44 fixes #11660 (#11677)
(cherry picked from commit bab1f67971)
2019-07-08 11:33:20 +02:00
awr1
e3c6f4d21b [HCR] Added warning for using performCodeReload() in main module (#11667)
* [HCR] Added error for using performCodeReload() in main module

* [HCR] changed this to a warning, clarified usage

* [HCR] fixed typo

(cherry picked from commit 6a7f8e8ab5)
2019-07-08 11:33:20 +02:00
Andreas Rumpf
615be176e6 fixes #11671
(cherry picked from commit 3258c1e2a3)
2019-07-08 11:33:20 +02:00
Andreas Rumpf
5dfec61597 improved the error message for #11494; closes #11494
(cherry picked from commit 465815e894)
2019-07-08 11:33:20 +02:00
Andreas Rumpf
df32d891a9 fixes a finish.exe regression
(cherry picked from commit 6ce6bce12e)
2019-07-08 11:33:20 +02:00
Andreas Rumpf
c96e6682a0 fixes #11447
(cherry picked from commit d2684cdfd2)
2019-07-08 11:33:19 +02:00
Andreas Rumpf
753f8a05d2 fixes #3333
(cherry picked from commit cab0c3e544)
2019-07-08 11:33:19 +02:00
Andreas Rumpf
00ccdec238 fixes #8316 (#11673)
(cherry picked from commit 64e1408920)
2019-07-08 11:33:00 +02:00
Andreas Rumpf
49de021b1a fixes #7057
(cherry picked from commit 2678fa679a)
2019-07-08 11:28:42 +02:00
Andreas Rumpf
5ecfbb5a4a ABI fixes for OSX/BSD; fixes #6860 (#11666)
(cherry picked from commit 02b9af2f75)
2019-07-08 11:28:42 +02:00
Juan Carlos
1b10f926c8 Documentation Diff, Typo (#11566)
* Documentation for Diff, add Examples and runnableExamples

(cherry picked from commit 25cd8a5490)
2019-07-08 11:28:42 +02:00
Miran
a31b3ef3cf nimpretty: improved detection of commas and semicolons (#11661)
* nimpretty: improved detection of commas and semicolons

* address the comments

(cherry picked from commit 34c09a98c7)
2019-07-08 11:28:42 +02:00
Arne Döring
8501971a60 introduce internal Int128 type, use it to fix case stmt checks (#11652)
* initial version of int128
* use int128 in case stmt
* fixes #11552

(cherry picked from commit 7d5d9f7703)
2019-07-08 11:28:42 +02:00
Miran
5863461d47 nimpretty: don't touch formatted multiline comments (#11663)
(cherry picked from commit 176eaf5c90)
2019-07-08 11:28:42 +02:00
cooldome
cb3a920097 Fixes #11662: render ops priority (#11664)
(cherry picked from commit d1f6c820dd)
2019-07-08 11:28:42 +02:00
narimiran
88896194bd nimpretty: nicer debugging [ci skip]
(cherry picked from commit 2c0c0e772c)
2019-07-08 11:28:42 +02:00
Miran
5689003410 nimpretty: relax line length rules when dealing with (inline) comments (#11657)
(cherry picked from commit 96a19ea1b4)
2019-07-08 11:28:42 +02:00
Timothee Cour
789c8e0065 fix #11651 exportc symbol not exported, leading to link error (#11658)
(cherry picked from commit 95f2f2396f)
2019-07-08 11:28:41 +02:00
Andreas Rumpf
e74e85a41e fixes #11610 (#11659)
(cherry picked from commit 28c9c062a4)
2019-07-08 11:28:41 +02:00
Tomohiro
2ee6d209c2 manual: document block expression (#11638)
(cherry picked from commit b64dd795b1)
2019-07-08 11:28:41 +02:00
Andreas Rumpf
3e8c4519d4 newruntime for async (#11650)
* fixes overloading resolution for passing owned(Future[string]) to Future[T]
* WIP: make --newruntime work with .async
* memtracker: make it compile again
* make Nimble compile again

(cherry picked from commit 5f515410af)
2019-07-08 11:28:41 +02:00
Akito13
cbdba3ceb3 Corrected example shown in documentation (#11654) [ci skip]
Used template within example was added to imports.

(cherry picked from commit 0a501932c2)
2019-07-08 11:27:15 +02:00
Graeme Cross
a9322ed873 Fix the broken link in math.nim (#11653) [ci skip]
(cherry picked from commit dad3c40f55)
2019-07-08 11:27:15 +02:00
narimiran
66660eca32 nimpretty: fix out of bounds error [ci skip]
(cherry picked from commit 4450fca6fd)
2019-07-08 11:27:15 +02:00
Araq
d13c75eb2f nimpretty: nimpretty now understands splitting newlines
(cherry picked from commit a59f0adc50)
2019-07-08 11:27:15 +02:00
Araq
f70a0c90f1 nimpretty: distinguish between the different kinds of newlines in Nim
(cherry picked from commit 7d29f8b1a4)
2019-07-08 11:27:15 +02:00
Timothee Cour
6a23772e23 fixes #8405: -d:useNimRtl now works even when {.rtl.} procs are used at compile time; CTFFI now works with {dynlib} (#11635)
(cherry picked from commit 64168d4aea)
2019-07-08 11:27:15 +02:00
Miran
da3a7b852d [bugfix] fix wrong nimpretty unidents, refs #11616 (#11645)
(cherry picked from commit 0718d6c2cd)
2019-07-08 11:27:15 +02:00
Andreas Rumpf
7ab44997aa fixes #11585
(cherry picked from commit 045eeab6b4)
2019-07-08 11:27:15 +02:00
Timothee Cour
b94e7b65a0 [bugfix] fix mach_absolute_time header; refs #11591 (#11593)
(cherry picked from commit 0ca71dd92c)
2019-07-08 11:27:15 +02:00
Miran
20f6acc0da [bugfix] fix #11484, acknowledge 'danger' flag (#11486)
(cherry picked from commit 1e9316e23c)
2019-07-08 11:27:15 +02:00
Araq
b0e86d0f05 fixes #11510
(cherry picked from commit c0db1705dc)
2019-07-08 11:27:15 +02:00
Araq
6a6a9423e4 fixes #11636
(cherry picked from commit 476b4ff372)
2019-07-08 11:27:15 +02:00
Arne Döring
191b7e0bde [backport] fix #11320 (#11538)
* [backport] fix #11320
* fix test for 32 bit test

(cherry picked from commit 20d0ef8afb)
2019-07-08 11:26:20 +02:00
Hugo Locurcio
1e48169bbc Implement some custom formatting for input fields in the documentation (#11632)
* Implement some custom formatting for input fields in the documentation

Aside of resulting in more consistent appearance across browsers,
this also fixes input form rendering when using Firefox with a
dark system theme on Linux.

* change the expected html in the tests

(cherry picked from commit 0a1cb631a2)
2019-07-08 11:26:20 +02:00
Graeme Cross
bd35d44808 Documentation improvements to the times module (#11639)
* Typo and grammar fixes for the times module
* Minor typo fixes to the times module

(cherry picked from commit ab6dec6e90)
2019-07-08 11:26:20 +02:00
Andreas Rumpf
d1de6b9fe4 fixes #11479 (#11641)
(cherry picked from commit 71b7383f12)
2019-07-08 11:26:20 +02:00
Miran
466d5ba060 [bugfix] nimpretty indents multiline comments correctly (#11607)
(cherry picked from commit c522a455df)
2019-07-02 13:57:12 +02:00
Andreas Rumpf
7938947907 fixes #11633
(cherry picked from commit cce8a739da)
2019-07-02 13:57:12 +02:00
Andreas Rumpf
476b3b1c13 fixes #11515 (#11624)
* fixes #11515

* better fix for #11515; make tests green

(cherry picked from commit 766960dc15)
2019-07-02 13:57:12 +02:00
Andreas Rumpf
59fbd261a6 fixes #11618 (#11631)
(cherry picked from commit 9b949855c7)
2019-07-02 13:57:12 +02:00
alaviss
eb295bbb7d assertions: fixes #11545 (#11605)
* assertions: properly fix #11545

* tests/assert: enable excessiveStackTrace

* tests/assert: add test case for #11545

* tfailedassert_stacktrace: disable excessiveStackTrace

* assertions: weird workaround for failing tests

This fixes megatest on *nix, but have no idea why

(cherry picked from commit 2c10b246ec)
2019-07-02 13:56:55 +02:00
Araq
e6c963259c fixes #11617
(cherry picked from commit bd55c862a6)
2019-07-02 13:56:55 +02:00
Araq
c454cdac49 fixes #11628
(cherry picked from commit c352892232)
2019-07-02 13:56:55 +02:00
Araq
09d43c1d8f fixes #11614
(cherry picked from commit 413dff03a0)
2019-07-02 13:56:55 +02:00
Ico Doornekamp
281baadb33 Make repr use 'ptr' for ptr types instead of 'ref' (#11626)
(cherry picked from commit a5686961bd)
2019-07-02 13:56:55 +02:00
Juan Carlos
d7ea8588c5 Remove IO.js compat (long dead vanished project,superseed by NodeJS) (#11627)
(cherry picked from commit d7a7662a7f)
2019-07-02 13:56:55 +02:00
Araq
10fa252426 [bugfix] make -d:nimSetUtf8CodePage switch work again
(cherry picked from commit dd39b11b13)
2019-07-02 13:56:55 +02:00
Andreas Rumpf
b228b741a3 added destructors.rst document
(cherry picked from commit d37c4e44d3)
2019-07-02 13:56:55 +02:00
alaviss
c2d0e0f7d5 msgs, ccgstmts: fixes #11572 (#11621)
* [refactor] msgs: toFilename now return just the filename

The C codegen uses just the file name for stacktrace when
excessiveStackTrace is off (see quotedName),
so there aren't any reason for other codegen to not do the same.

The file name is now cached in TFileInfo.shortName, which was introduced
for nimsuggest, and went unused after several refactoring of the
compiler.

A toProjPath() proc has been added for the previous behavior of
toFilename().

* ccgstmt: use quotedFilename() for raiseExceptionEx

This is the same proc used for stacktrace when --stacktrace:on

Fixes #11572

* msgs: handle case where file name is not available

(cherry picked from commit e259f80fc7)
2019-07-02 13:56:27 +02:00
Andreas Rumpf
95df6af527 [other] additions to the manual
(cherry picked from commit 7113c1e931)
2019-07-02 13:56:27 +02:00
Araq
199aa1f3a1 fixes #11606
(cherry picked from commit 3606e035f5)
2019-07-02 13:56:27 +02:00
Araq
57df2c9c3a fixes #11611
(cherry picked from commit 7ddb31262e)
2019-07-02 13:56:27 +02:00
Ivan Bobev
17cac707d0 Pass additional koch options to the nim compiler (#11615)
Add possibility when "koch tools" command is used, nim compiler options
to be passed. The current options are kept and the new user specified
options will be added to the end of the command line if present.

Example:
> koch --latest tools -d:danger --debugInfo --lineDir:on
bin\nim.exe c -o:bin\nimsuggest.exe -d:release -d:danger -d:danger
 --debugInfo --lineDir:on nimsuggest/nimsuggest.nim
...
bin\nim.exe c -o:bin\nimgrep.exe -d:release -d:danger --debugInfo
 --lineDir:on tools/nimgrep.nim
...
bin\nim.exe c -o:bin\vccexe.exe -d:danger --debugInfo --lineDir:on
 tools/vccexe/vccexe.nim
...
bin\nim.exe c -o:bin\nimpretty.exe -d:release -d:danger --debugInfo
 --lineDir:on nimpretty/nimpretty.nim
...
bin\nim.exe c -o:bin\nimfind.exe -d:release -d:danger --debugInfo
 --lineDir:on tools/nimfind.nim
...
bin\nim.exe c -o:bin\nimble.exe --noNimblePath --nilseqs:on -d:release
 -d:danger --debugInfo --lineDir:on dist\nimble\src\nimble.nim

(cherry picked from commit 5be5bf0222)
2019-07-02 13:56:15 +02:00
Araq
970c7616bc nimpretty: bugfix [bugfix]
(cherry picked from commit 284a8cb58a)
2019-07-02 13:55:37 +02:00
Andreas Rumpf
1503970c3b contributing.rst guide: Make the rules reflect the reality.
(cherry picked from commit 765ff4b67b)
2019-07-02 13:54:42 +02:00
Andreas Rumpf
922a4ed195 manual: typo
(cherry picked from commit 17bbce7c8a)
2019-07-02 13:54:42 +02:00
Andreas Rumpf
c39a799aec manual: document how accessors are resolved; fixes #11514
(cherry picked from commit d9604d7d0b)
2019-07-02 13:54:42 +02:00
Zestyr
62691c5d73 Fix typo in xmltree docs [ci skip]
(cherry picked from commit bab83c4d8b)
2019-07-02 13:54:42 +02:00
Araq
ab5bd9e6ff make tests green again
(cherry picked from commit f36a61e6d4)
2019-07-02 13:54:42 +02:00
Andreas Rumpf
3c3f07cfde fixes #11445
(cherry picked from commit fb878330a9)
2019-07-02 13:54:41 +02:00
Andreas Rumpf
843ae0073b fixes #11523
(cherry picked from commit 135fdde6a9)
2019-07-02 13:54:41 +02:00
Andreas Rumpf
d206e881e9 fixes #11525
(cherry picked from commit 19b142401c)
2019-07-02 13:52:20 +02:00
Miran
712d9ce1b9 [bugfix] fix #11532, nimpretty should ignore source code filters (#11603)
(cherry picked from commit f65eaa51f1)
2019-06-27 20:59:46 +02:00
Andreas Rumpf
ba6eb0151c fixes #11098
(cherry picked from commit e083a1533b)
2019-06-27 20:59:46 +02:00
narimiran
28708e0b8e [bugfix] fix #11588, don't check if SharedTable is initialized
(cherry picked from commit 326e3ad09d)
2019-06-27 20:59:46 +02:00
Araq
e08cf5a947 fixes #11600
(cherry picked from commit b6fb47df77)
2019-06-27 20:59:46 +02:00
Miran
38aa7f6d20 [bugfix] fix #11469, new rules for a newline in nimpretty (#11512)
* [bugfix] fix #11469, new rules for a newline in nimpretty
* concatenate two lines if they have the same indentation level

(cherry picked from commit f288e1b11a)
2019-06-27 20:59:46 +02:00
Araq
763357089e fixes #11577
(cherry picked from commit 0a81b038fd)
2019-06-27 20:59:46 +02:00
alaviss
6576c1ab5c [refactor] compiler/[msgs, reorder, semstmts]: use toMsgFilename where appropriate (#11595)
* compiler/msgs: toMsgFilename now operates on FileIndex
* compiler/reorder: use toMsgFilename for compiler messages
* compiler/semstmts: respect listFullPaths for recursive deps error

(cherry picked from commit 993b3909a8)
2019-06-27 20:59:45 +02:00
alaviss
8a16fb972b compiler/[msgs, options]: confine --listFullPaths to compiler messages (#11583)
* compiler/[msgs, options]: make toFilename independent of listFullPaths

toFilename is used mainly in codegen, as such it should not follow
--listFullPaths

* compiler/msgs: use toMsgFilename for toFileLineCol

This proc is mainly used for compiler messages, so it should follow
--listFullPaths

* compiler/msgs: remove toFileLine

unused proc

* compiler/msgs: cleanup toMsgFilename

Also improved path choosing logic, /home should now be preferred over
../home on *nix

(cherry picked from commit 2f1a1b7106)
2019-06-27 20:59:45 +02:00
Toshiyuki-Tega
f1b2182035 VCC discovery using vswhere (#6540) (#11559)
(cherry picked from commit b7f8031e98)
2019-06-27 20:59:45 +02:00
Juan Carlos
50358e6d4a Documentation highlite (#11596)
* Add Documentation with examples for highlite

* Add Documentation with examples for highlite

(cherry picked from commit 206f2478b8)
2019-06-27 20:59:45 +02:00
Andreas Rumpf
48770b04bd fixes #11563 (#11594)
(cherry picked from commit 0f868b587b)
2019-06-27 20:59:45 +02:00
cooldome
7e702d95c1 increase dfa instruction limit (#11579)
(cherry picked from commit 0d50b0c8a7)
2019-06-27 20:59:45 +02:00
Jasper Jenkins
8396ddb3a4 [bugfix] remove dangling ref (#11576)
(cherry picked from commit 91f0626dcb)
2019-06-27 20:59:45 +02:00
Gérôme Fournier
8ab0be4530 Fix out of bound access in cgi module (#11578)
When an HTTP request with a zero CONTENT_LENGTH is made,
attempting to access addr(result[0]) raise an exception as the 0 index
is out of bound

(cherry picked from commit 181350f6c9)
2019-06-27 20:59:45 +02:00
WhiteDuke
30d7cf02ad Fix typo (#11573)
(cherry picked from commit 2a7cf71db3)
2019-06-27 20:59:45 +02:00
alaviss
98b9ffa3a4 assertions: make assert follow excessiveStackTrace (#11574)
(cherry picked from commit 403fd3ac43)
2019-06-27 20:59:45 +02:00
Arne Döring
a5a62b562f [refactoring] remove zeroExtend and friends from the compiler builtins. (#11531)
* remove zeroExtend and friends from the compiler builtins.
* fix jssys

(cherry picked from commit e90d91f0e4)
2019-06-27 20:59:45 +02:00
alaviss
06904a212a nim.cfg: disable excessiveStackTrace on release (#11575)
(cherry picked from commit e9a23cee0a)
2019-06-27 20:59:45 +02:00
narimiran
a74104eda5 [documentation] fix #4630, document unsupported functions for JS
(cherry picked from commit 436f570651)
2019-06-27 18:16:54 +02:00
Simon Krauter
f8087a2815 Spelling fixes in nimc.rst (#11569)
(cherry picked from commit 800bc661b6)
2019-06-27 18:15:53 +02:00
Juan Carlos
6a7b490046 [other] Documentation NimScript (#11548)
* Documentation of NimScript, update and expand, mention the benefits aside of its limitations, add examples, update the list of usable std lib modules

(cherry picked from commit bb9b60604b)
2019-06-27 18:15:46 +02:00
Miran
ceafb7f771 [bugfix] fix OrderedTable iterators (#11562)
(cherry picked from commit 3d50f7e2a1)
2019-06-27 18:14:36 +02:00
Andreas Rumpf
433dfaf288 [bugfix] fixes #11524
(cherry picked from commit 79c721d418)
2019-06-27 18:14:36 +02:00
Andreas Rumpf
4d18ee498a closes #11533
(cherry picked from commit 0db3c670ac)
2019-06-27 18:14:36 +02:00
Andreas Rumpf
ef018d7a63 [bugfix] fixes #11556
(cherry picked from commit 35df59b3ce)
2019-06-27 18:14:35 +02:00
narimiran
2a36cb7fc9 [documentation] fix wrong link [ci skip]
(cherry picked from commit 12eebf48b6)
2019-06-27 18:14:35 +02:00
Kaushal Modi
810166ca95 [other] Improve the "tabs are not allowed" error message (#11554)
(cherry picked from commit a4c613c110)
2019-06-27 18:14:35 +02:00
Miran
10407fefec [bugfix] fix delete in strutils and sequtils (#11535)
(cherry picked from commit 23e95dc629)
2019-06-27 18:14:35 +02:00
Miran
d5ea497cff [bugfix] correct 'source' for documentation (#11540)
(cherry picked from commit d6b0e46ee4)
2019-06-27 18:13:55 +02:00
Miran
c6ab69b84e [bugfix] fix OrderedTable default initialization (#11549)
(cherry picked from commit 45bbf1ef5b)
2019-06-27 18:13:55 +02:00
Andreas Rumpf
edc151fa64 [other] new experimental pragma .nodestroy for easier custom destructors
(cherry picked from commit 55da84e141)
2019-06-27 18:13:55 +02:00
Andreas Rumpf
6d977e66cb [other] added system.dispose for owned refs
(cherry picked from commit f310cf5250)
2019-06-27 18:13:55 +02:00
Andreas Rumpf
f3ae2dcfa3 [bugfix] system.nim: make pop work with --newruntime
(cherry picked from commit 280193cc07)
2019-06-27 18:13:55 +02:00
Andreas Rumpf
cd49bba7b3 [bugfix] proper destruction for strings/seqs for --newruntime
(cherry picked from commit 7e4748beee)
2019-06-27 18:13:55 +02:00
Andreas Rumpf
194eebd4b8 [bugfix] system.nim: make pop work with --newruntime
(cherry picked from commit bc9fb4885b)
2019-06-27 18:13:55 +02:00
Araq
b41d31e887 [other] newruntime: add a simple closure iterator test
(cherry picked from commit 678beb8ef9)
2019-06-20 11:59:50 +02:00
Araq
cb4bb4d6c7 [bugfix] newruntime: trivial closure iterator works
(cherry picked from commit 6adcc09877)
2019-06-20 11:59:50 +02:00
Araq
cd35f9664c [refactoring] closureiters.nim: style change
(cherry picked from commit c83914b435)
2019-06-20 11:59:50 +02:00
Araq
0ba1624325 wip
(cherry picked from commit 50afafdb38)
2019-06-20 11:59:50 +02:00
Andreas Rumpf
6ad45a1c97 WIP: closures for the newruntime [ci skip]
(cherry picked from commit af10788dcc)
2019-06-20 11:59:50 +02:00
narimiran
5cf9cbe9a5 [bugfix] fix #11546, correct link for sequtils
(cherry picked from commit 16d81fa205)
2019-06-20 11:59:50 +02:00
narimiran
9789146899 [bugfix] fix #11543, add std/sums to theindex.html
(cherry picked from commit 96bed814d2)
2019-06-20 11:59:50 +02:00
Andreas Rumpf
905fc43abe newruntime: added yet another test
(cherry picked from commit 0adb1c6223)
2019-06-20 11:59:50 +02:00
Andreas Rumpf
aacc97e854 [bugfix] owned closures (#11544)
(cherry picked from commit c65a5d754b)
2019-06-20 11:59:50 +02:00
Arne Döring
e0b41736b3 change newLit on Seq (#11542)
(cherry picked from commit 1a074fffa4)
2019-06-20 11:59:50 +02:00
Araq
8d46524b1b [bugfix] fixes #11517
(cherry picked from commit ab36ffbeb1)
2019-06-20 11:55:37 +02:00
Araq
3aa034f40d libffi: style changes
(cherry picked from commit ff89f7e33a)
2019-06-20 11:55:24 +02:00
Miran
eb9ac127b9 [other] update homebrew, fix travis fails on OSX (#11541)
(cherry picked from commit ae5a4cea98)
2019-06-20 11:54:46 +02:00
narimiran
22c515b067 [other] documentation: deprecation comments are now auto-generated
(cherry picked from commit cf47c2c60c)
2019-06-20 11:54:46 +02:00
alaviss
4c7dec34d5 [bugfix] gc_common: export foreign thread gc docs (#11536)
(cherry picked from commit 24ac13f653)
2019-06-20 11:54:46 +02:00
Araq
81f15184e5 fixes #11436
(cherry picked from commit 296c48de5e)
2019-06-20 11:53:37 +02:00
Phạm Ngọc Quang Nam
368e12ed36 [bugfix] Fix smtp.nim unhandled exception (#11498)
(cherry picked from commit 7c637c2faf)
2019-06-20 11:53:08 +02:00
Araq
c57485e67b add HCR document to the tools.rst list
(cherry picked from commit c99ce5051e)
2019-06-20 11:52:51 +02:00
Araq
2d606e38ea HCR: better documentation
(cherry picked from commit 38f05bb9a3)
2019-06-20 11:52:44 +02:00
Benjamin Summerton
c1097ea949 Add cbrt() bindings for the JS Target (#11528)
`Math.cbrt()` exists:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt

The binding was missing for the JavaScript backend.

(cherry picked from commit a009cbdc2f)
2019-06-20 11:52:37 +02:00
Miran
4144893ba2 [bugfix] fix #11450, random.initRand crashes in JS backend (#11522)
(cherry picked from commit ac3449b93b)
2019-06-20 11:49:17 +02:00
Miran
dacd1d071a [bugfix] fix #11482, fix nimsuggest range error (#11519)
The default starting parameters for `line` and `col` were switched.

See line 141: "line starts at 1, column at 0, dirtyfile is optional".
Also, in `TLineInfo` object, `line` is uint16, `col` is int16.

(cherry picked from commit 6cad5c7861)
2019-06-20 11:49:07 +02:00
narimiran
1348cb525b [other] introduce a separate changelog for backports, update
contributing guidelines

(cherry picked from commit b70fd0400e)
2019-06-20 11:48:32 +02:00
Juan Carlos
77166176b2 [bugfix] Fixes #11434 , Update Tools Documentation (#11449)
(cherry picked from commit eb087b3546)
2019-06-20 11:46:12 +02:00
Juan Carlos
e15ce6bdeb [bugfix] Fixes #11495 , Remove TinyC from help (#11508)
(cherry picked from commit aa7f23e1ee)
2019-06-20 11:46:12 +02:00
Charles Blake
464142683f [bugfix] fix #11430, strutils rfind (#11487) 2019-06-16 17:44:06 +02:00
Kaushal Modi
7095a3dfa4 [bugfix] Fix the link to "For Loop Macro" section (#11505)
(cherry picked from commit d1ce86fdc6)
2019-06-16 17:19:43 +02:00
SolitudeSF
7e235779f0 [bugfix] Add .47 as supported libssl version
(cherry picked from commit e96afd6324)
2019-06-16 17:19:43 +02:00
Kaushal Modi
b68ee27dd5 [bugfix] Minor formatting fix in tut2 [ci skip] (#11501)
Monospace `untyped` like elsewhere in the tutorial.

(cherry picked from commit c656714623)
2019-06-16 17:19:43 +02:00
Araq
63116d94ae [bugfix] nimpretty: better alignment; fixes #11467
(cherry picked from commit 57a83df745)
2019-06-16 17:19:43 +02:00
Juan Carlos
01fcde2146 [bugfix] document the norewrite pragma (#11453)
* fixes #9454
* Peer review feedback, wording, move to experimental manual

(cherry picked from commit 80320c72d3)
2019-06-16 17:19:43 +02:00
Jasper Jenkins
ea4d72a379 [refactoring] error instead of quit for basename (#11491)
(cherry picked from commit bda9fd149c)
2019-06-16 17:19:43 +02:00
Jasper Jenkins
78c6f01b90 [bugfix] fix quit errormsg in nimvm (#11490)
(cherry picked from commit 06ef56ba72)
2019-06-16 17:18:58 +02:00
Simon Krauter
41e0fc593c Update tut1: -d:release does not turn off *allruntime checks off anymore (#11492)
According to https://nim-lang.org/blog/2019/06/06/version-0200-released.html
"The -d:release switch now does not disable runtime checks anymore. "

(cherry picked from commit e2f99a2026)
2019-06-16 17:18:58 +02:00
Araq
21910d6eb1 [bugfix] nimpretty: fixes #11470
(cherry picked from commit 165ea46284)
2019-06-16 17:18:58 +02:00
Araq
7bfde5fa40 [bugfix] nimpretty: fixes #11468
(cherry picked from commit af39f26a3a)
2019-06-16 17:18:58 +02:00
Araq
9e28b3ccc9 [other] nimsuggest: adhere to our style guide
(cherry picked from commit b70295c768)
2019-06-16 17:18:58 +02:00
alaviss
b4464f1dfa [other] appveyor: use official dlls on nim-lang.org (#11473)
(cherry picked from commit 08fdffc717)
2019-06-16 17:18:58 +02:00
Dominik Picheta
4f69f7b62d [bugfix] Fixes async IO operations stalling even after socket is closed. (#11232)
(cherry picked from commit 73c44c6e9b)
2019-06-16 17:18:58 +02:00
Jasper Jenkins
6530a4100a [bugfix] fix nimpretty for absolute paths (#11480)
(cherry picked from commit da035e9c83)
2019-06-16 17:17:42 +02:00
Araq
9797da3615 [refactoring] liftdestructors.nim is callable from lambdalifting; refs #11217
(cherry picked from commit b056d32a79)
2019-06-16 17:17:41 +02:00
Arne Döring
0af9085e78 fix regression in semfold for old right shift (#11477)
(cherry picked from commit e4d0f4fee0)
2019-06-16 17:17:41 +02:00
Araq
e1c29d7a19 [refactoring] liftdestructors is now a module of its own
(cherry picked from commit c07973e313)
2019-06-16 17:17:41 +02:00
Araq
32cecf3a42 [other] make typeAllowed easier to debug by using structured programming
(cherry picked from commit 549d8cc0c6)
2019-06-16 17:17:41 +02:00
Andreas Rumpf
05d647398a [other] preparations for --styleCheck:error for the Nim compiler (#11478)
(cherry picked from commit 572b7c37a5)
2019-06-16 17:17:41 +02:00
Arne Döring
4c8a02165e [refactoring] refactor the compiler and stdlib to deprecation warnings (#11419)
(cherry picked from commit c7e1c665a1)
2019-06-16 17:17:41 +02:00
Juan Carlos
7e6c3f3684 [bugfix] Add .nimf to mimetypes (#11417)
(cherry picked from commit 3d13103443)
2019-06-16 17:17:41 +02:00
Miran
65a2b3bb6f [other] better error message for IndexError for empty containers (#11476)
(cherry picked from commit b3d2cd738a)
2019-06-16 17:16:49 +02:00
narimiran
8dfebb3282 Revert "VM can now cast integer type arbitrarily. (#11459) [feature]"
This is a new feature, which shouldn't be backported.
2019-06-16 17:15:23 +02:00
narimiran
de2c271293 Merge remote-tracking branch 'upstream/devel' into version-0-20 2019-06-11 12:13:57 +02:00
narimiran
022cd2c144 bump version to 0.20.1 2019-06-11 12:13:35 +02:00
285 changed files with 8577 additions and 3948 deletions

View File

@@ -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

View File

@@ -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:

View File

@@ -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
View 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

View File

@@ -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])

View File

@@ -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,

View File

@@ -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])

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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(),

View File

@@ -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)

View File

@@ -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")

View File

@@ -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])

View File

@@ -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,

View File

@@ -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)])

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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"

View File

@@ -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))

View File

@@ -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:

View File

@@ -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

View File

@@ -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 =

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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})

View File

@@ -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)

View File

@@ -14,6 +14,10 @@ define:nimcore
define:useStdoutAsStdmsg
define:nimOldCaseObjects
@if nimHasStyleChecks:
styleCheck:error
@end
#define:useNodeIds
#gc:markAndSweep

View File

@@ -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

View File

@@ -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,

View File

@@ -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

View File

@@ -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")

View File

@@ -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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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, ")")

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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 `%`

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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 =

View File

@@ -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,

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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!

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -149,7 +149,6 @@ type
opcSetType, # dest.typ = types[Bx]
opcTypeTrait,
opcMarshalLoad, opcMarshalStore,
opcToNarrowInt,
opcSymOwner,
opcSymIsInstantiationOf

View File

@@ -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:

View File

@@ -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

View File

@@ -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]

View File

@@ -71,6 +71,7 @@ path="$lib/pure"
@if release or danger:
stacktrace:off
excessiveStackTrace:off
linetrace:off
debugger:off
line_dir:off

View File

@@ -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 */

View File

@@ -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

View File

@@ -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
View 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.

View File

@@ -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

View File

@@ -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
View 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/).

View File

@@ -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

View File

@@ -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.

View File

@@ -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
==========

View File

@@ -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>`_.

View File

@@ -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>`_

View File

@@ -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.

View File

@@ -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>`_.

View File

@@ -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"

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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.} =

View File

@@ -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]) =

View File

@@ -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:

View File

@@ -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)

View File

@@ -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